Commit dc05b25f authored by FE-安焕焕's avatar FE-安焕焕 👣

下订单逻辑

parent f31000f9
import config from '@/config';
import http from '@/service/httpDecorator';
import { encryption } from '@/service/encrypt';
const { talosHost } = config;
export default {
......@@ -22,6 +23,6 @@ export default {
},
// 订单创建
orderCreate(data) {
return http.get(`${talosHost}/api/kdsp/order-info/e/vmSubmit`, data);
return http.post(`${talosHost}/api/kdsp/order-info/e/vmSubmit`, { data: encryption(data) });
}
};
import request from '@/service/httpDecorator';
import config from '@/config';
import { encryption } from '@/service/encrypt';
const { talosHost } = config;
const queryPayInfo = function(data) {
......@@ -11,9 +12,13 @@ const prepay = function(data) {
};
const pay = function(data) {
return request.post(`${talosHost}/open/checkout/pay`, data, {
needScDeviceId: true
});
return request.post(
`${talosHost}/open/checkout/pay`,
{ data: encryption(data) },
{
needScDeviceId: true
}
);
};
const queryPayStatus = function(data) {
......@@ -40,14 +45,22 @@ const ocrFaceId = function(params) {
return request.post(`${talosHost}open/checkout/ocr_faceId`, params);
};
const getGoodsList = function(data) {
return request.get(`${talosHost}/vcc/xyqb/recommend/goods-list`, data, {
needScDeviceId: true,
hideLoading: true
});
};
export {
pay,
prepay,
sendSms,
queryPayInfo,
queryPayStatus,
desSalt,
h5AppyUrl,
getCoupon,
ocrFaceId
ocrFaceId,
queryPayInfo,
getGoodsList,
queryPayStatus
};
<template>
<div class="reco">
<cr-list
v-model="loading"
:immediate-check="false"
:finished="finished"
finished-text="没有更多了"
@load="onLoad"
>
<div class="list">
<div class="left">
<items :data="leftList" />
</div>
<div class="right">
<items :data="rightList" />
</div>
</div>
</cr-list>
</div>
</template>
<script>
import Items from './RecoGoodsItem';
import { getGoodsList } from '@/api/pay.api';
export default {
components: {
Items
},
// props: {
// list: Array
// },
data() {
return {
finished: true,
loading: false,
list: []
};
},
computed: {
leftList: function() {
return this.list.filter((item, index) => {
if (index % 2 === 0) {
return item;
}
});
},
rightList: function() {
return this.list.filter((item, index) => index % 2);
}
},
methods: {
async onLoad() {
this.loading = true;
const [data = {}] = await getGoodsList({ pageNo: 1, pageSize: 10 });
this.list = [...this.list, ...data?.goodsList];
this.loading = false;
}
}
};
</script>
<style lang="less" scoped>
.reco {
width: 100%;
// height: 100%;
overflow: auto;
margin: 12px 0 70px 0;
background-color: #f7f7f7;
padding-bottom: 10px;
.list {
height: 100%;
overflow: auto;
clear: both;
}
.left {
float: left;
width: 50%;
height: 100%;
display: flex;
flex-direction: column;
padding: 0 5px 0 10px;
box-sizing: border-box;
}
.right {
float: right;
width: 50%;
height: 100%;
display: flex;
flex-direction: column;
padding-right: 10px;
box-sizing: border-box;
padding: 0 10px 0 5px;
}
@{deep} .cr-list__finished-text {
font-size: 14px;
}
}
</style>
<template>
<div class="goods">
<template v-for="item in data">
<div
v-if="item.goods"
:key="item.goodsId"
:class="['card']"
@click="toDetail(item.goods.jumpUrl)"
>
<div class="card__top">
<div class="card__head">
<cr-image
height="190px"
width="190px"
class="card__head__image"
object-fit="contain"
:src="item.goods.goodsImage"
/>
</div>
<p class="card__name">
<cr-image
v-if="item.goods.goodsTypeImage"
width="0.64rem"
height="auto"
class="add-title-label"
:src="item.goods.goodsTypeImage"
/>
{{ item.goods.goodsName }}
</p>
<p v-if="item.goods.serviceTypeList" class="card__service">
"{{ item.goods.serviceTypeList[0] }}"
</p>
<div class="card__tag">
<div v-for="(tag, index) in item.goods.tagList" :key="index" class="card__tag-wrap">
<span v-if="tag.type == 2" :key="index" class="card__tag-normal">{{ tag.name }}</span>
<img v-if="tag.type == 1" class="card__tag-icon" :src="tag.icon" alt="" />
</div>
</div>
</div>
<div class="card__bottom">
<div class="price">
<p>
<span class="price__icon">¥</span>
<span class="price__text">{{
item.goods.goodsSalePrice && item.goods.goodsSalePrice.replace('', '')
}}</span>
</p>
<p class="sale">已售{{ item.goods.saleCount }}</p>
</div>
</div>
</div>
</template>
</div>
</template>
<script>
export default {
props: {
data: [Object, Array]
},
methods: {
toDetail(url) {
const data = {
event: 'openNewUrl',
data: {
newUrl: url // 需要打开的新链接
}
};
this.util.openNewUrl(data);
}
}
};
</script>
<style lang="less" scoped>
.add-title-label {
vertical-align: middle;
width: 24px;
margin-top: -2px;
}
.goods {
height: 100%;
}
.card {
font-size: 0;
background-color: #fff;
border-radius: 6px;
margin-bottom: 10px;
display: flex;
flex-direction: column;
position: relative;
justify-content: space-between;
&__head {
height: 173px;
border-radius: 6px;
overflow: hidden;
&__image {
height: 100%;
width: 100%;
}
}
&__name {
font-size: 13px;
line-height: 18px;
display: -webkit-box;
overflow: hidden;
-webkit-line-clamp: 2;
/*! autoprefixer: off */
-webkit-box-orient: vertical;
/*! autoprefixer: on */
margin: 8px 5px 0px 14px;
text-align: left;
.shop-tag {
vertical-align: middle;
margin-top: -2px;
}
}
&__service {
font-size: 13px;
color: #e1a069;
margin: 5px 14px;
}
&__tag {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin: 3px 5px 0 14px;
&-wrap {
margin-bottom: 5px;
}
&-text {
color: #e1a069;
font-size: 13px;
}
&-normal {
display: inline-block;
font-size: 10px;
box-sizing: border-box;
border: 1px solid #ff5a4b;
color: #ff5a4b;
padding: 0px 4px;
border-radius: 3px;
margin-right: 5px;
line-height: 14px;
}
&-icon {
height: 15px;
margin-right: 5px;
}
&-warn {
color: #ff4b00;
border: 1px solid #ff4b00;
}
}
&__bottom {
padding: 5px 5px 6px 14px;
.activity {
display: flex;
align-items: bottom;
&__free {
height: 14px;
margin-right: 3px;
}
}
.price__raw {
float: left;
color: #999;
font-size: 10px;
line-height: 14px;
text-decoration: line-through;
}
.sale {
color: #999;
font-size: 10px;
height: 20px;
display: flex;
align-items: flex-end;
margin-left: 5px;
}
.price {
display: flex;
align-items: center;
&__icon {
font-size: 12px;
color: #ec3333;
}
&__text {
font-size: 18px;
line-height: 25px;
color: #ec3333;
}
}
}
&::after {
clear: both;
}
}
</style>
export const APP_ID = '102';
export const PUBLIC_KEY = `
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCGf4wnNJXHXM54wsmxTwhpiPaAp27zM
3QsrWu1+GOMoCDmAEOb2jYjyNhwBIeV9eY0fwoK+MfWBywbonypyXs1j5l/mTmFRQ8IZY
+xZF0t01cornoMpKJncoNiwqc9OnsiwGPqzIs/iLalBonppqhWSn0g99vFS2qgr0WnOWl
qLQIDAQAB
-----END PUBLIC KEY-----
`;
export const PRIVATE_KEY = `
-----BEGIN RSA PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAIC46noXi7VhxDMCQNK3x
k7uVQICaIwhQ6Ye5c5XYaM9IgUhFUioQZ3llelM3oAnYqLuAZMU0POcNqm8qmr7I3fc8A
SZZKm5XUm6i/PI++xT+5c9zhHHsHumoSoTJ/bpS8xNhDFz/X2JQTIn6izFtBBnsMJRx5K
aJWXYebk1RyCVAgMBAAECgYA27bGxE+ccKXINykJbKOzItc80ok52raMuejTaTlNt0yJ3
SdzJOnN1q4jDG3g++4+Nsz6cwt8/dUOmPsoCCNTjMDUg40Cl8NopPMXvo/INwd2vJSEFr
JyC629pl3N7E18Iqjdt8jMy/zEMSDMPh7+NDtSDQqfQpeT0w31/Q1qdwQJBALtk/xIIPN
/zyCZcAIKtXvp0FrHslEL55q01kV6bwhyfAnyz+GcGJwxPWOKJ1FRCnzjOZRc3ygekMZU
WGLjszDkCQQCv2Qo/q97pjw4QwxsTwuuQKlLhAuBr2W0eTsnou9tk3i7PJX/xQXlccp0C
FRWAe3xdpYtapUiMJ8zpKkw+PC89AkBdn9UMRk9buKmL+LVMlJ/6U5uvIzrjx4UnjrblZ
L5znEIa2bPGjT1fGhmXfTM3Md3o/L1m/zmR3cfj65lIcw6JAkB0uf6qmz0CDnrIt6pOqN
HTRbT0NgOgs5hSSPyQJ7sPrsilqn/ONqcHrfD5A+PdAJtiUlQ5nIOWvYiwseogWbUFAkB
y6/cB1HblHJzuyJwaAW9vLNmbqeKW5DhU2QG7Jp1uUgk4GcopDAgFWlAjbQcBCGDyStHH
YxINn+qpKviRkeFf
-----END RSA PRIVATE KEY-----
`;
......@@ -4,13 +4,12 @@
* @LastEditors: gzw
* @LastEditTime: 2021-01-29 19:06:27
*/
// perf forge.js较大,后期需要替换为其他库,以减小体积
// const forge = require('../utils/forge.min');
import { cipher as AES, util as UTIL, pki as PKI, md as SHA1 } from 'node-forge';
import uuidv1 from 'uuid/v1';
import { parseTime } from './utils.service';
import { APP_ID, PUBLIC_KEY, PRIVATE_KEY } from './encrypt.config';
const CryptoJS = require('./crypto.min');
import { desSalt } from '@/api/pay.api';
// import uuidv1 from 'uuid/v1';
// import { parseTime } from './index';
// import { APP_ID, PUBLIC_KEY, PRIVATE_KEY } from '@/config/encrypt.config';
/**
* @description: 数据加密
......@@ -18,21 +17,21 @@ import { desSalt } from '@/api/pay.api';
* @return {String} 加密后的数据base64
*/
// export function encryption(data = '') {
// if (!data) return null;
// const key = generateRandomStr(16);
// const iv = key; // 后台约定iv与key一致
// const plaintext = typeof data === 'object' ? JSON.stringify(data) : data;
// const body = encryptDataByAes(plaintext, key, iv); // AES加密数据
// const encryptKey = encryptDataByPb(key); // RSA公钥加密AES密钥
// const signData = generateSign(plaintext);
// return {
// appId: APP_ID,
// body,
// encryptKey,
// ...signData
// };
// }
export function encryption(data = '') {
if (!data) return null;
const key = generateRandomStr(16);
const iv = key; // 后台约定iv与key一致
const plaintext = typeof data === 'object' ? JSON.stringify(data) : data;
const body = encryptDataByAes(plaintext, key, iv); // AES加密数据
const encryptKey = encryptDataByPb(key); // RSA公钥加密AES密钥
const signData = generateSign(plaintext);
return {
appId: APP_ID,
body,
encryptKey,
...signData
};
}
/**
* @description: 数据加密
......@@ -56,25 +55,25 @@ export async function encryptByDESModeEBC(message) {
* @param {String} iv 初始化向量
* @return {String} 加密后的数据base64
*/
// function encryptDataByAes(txt, key, iv) {
// const cipherInstance = forge.cipher.createCipher('AES-CBC', key);
// cipherInstance.start({ iv });
// cipherInstance.update(forge.util.createBuffer(txt, 'utf8'));
// cipherInstance.finish();
// const ciphertext = cipherInstance.output.getBytes();
// return buffer2Base64(ciphertext);
// }
function encryptDataByAes(txt, key, iv) {
const cipher = AES.createCipher('AES-CBC', key);
cipher.start({ iv });
cipher.update(UTIL.createBuffer(txt, 'utf8'));
cipher.finish();
const ciphertext = cipher.output.getBytes();
return buffer2Base64(ciphertext);
}
/**
* @description: 使用RSA公钥加密数据
* @param {String} txt 数据源
* @return {String} 加密后的数据base64
*/
// function encryptDataByPb(txt) {
// const publicKey = forge.pki.publicKeyFromPem(PUBLIC_KEY);
// const pbData = publicKey.encrypt(txt);
// return buffer2Base64(pbData);
// }
function encryptDataByPb(txt) {
const publicKey = PKI.publicKeyFromPem(PUBLIC_KEY);
const pbData = publicKey.encrypt(txt);
return buffer2Base64(pbData);
}
/**
* @description: RSA私钥+SHA1生成签名
......@@ -82,51 +81,51 @@ export async function encryptByDESModeEBC(message) {
* @param {String} txt 数据源
* @return {Object} 生成的sign数据,时间戳、Nonce
*/
// function generateSign(txt) {
// const timestamp = parseTime('');
// const nonce = generateNonce();
// const privateKey = forge.pki.privateKeyFromPem(PRIVATE_KEY);
// const md = forge.md.sha1.create();
// md.update(nonce + APP_ID + timestamp + txt, 'utf8');
// let sign = privateKey.sign(md);
// sign = buffer2Base64(sign);
// return {
// timestamp,
// nonce,
// sign
// };
// }
function generateSign(txt) {
const timestamp = parseTime('');
const nonce = generateNonce();
const privateKey = PKI.privateKeyFromPem(PRIVATE_KEY);
const md = SHA1.sha1.create();
md.update(nonce + APP_ID + timestamp + txt, 'utf8');
let sign = privateKey.sign(md);
sign = buffer2Base64(sign);
return {
timestamp,
nonce,
sign
};
}
/**
* @description: buffer转base64
* @param {Buffer} buf buffer源数据
* @return {String} base64 字符串
*/
// function buffer2Base64(buf) {
// return forge.util.encode64(buf);
// }
function buffer2Base64(buf) {
return UTIL.encode64(buf);
}
/**
* @description: 生成nonce(uuid)
* 规则:以当前时间的uuid作为name,以随机生成的uuid作为namespace,生成最终的uuid
* @return {String} 生成的uuid
*/
// function generateNonce() {
// return uuidv1();
// }
function generateNonce() {
return uuidv1();
}
/**
* @description: 生成随机字符串
* @param {Number} n 位数
* @return {String} 生成的字符串
*/
// function generateRandomStr(n) {
// const len = n || 32;
// const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz0123456789';
// const maxPos = chars.length;
// let pwd = '';
// for (let i = 0; i < len; i++) {
// pwd += chars.charAt(Math.floor(Math.random() * maxPos));
// }
// return pwd;
// }
function generateRandomStr(n) {
const len = n || 32;
const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz0123456789';
const maxPos = chars.length;
let pwd = '';
for (let i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
import HttpRequest from '@qg/ui-request';
import { Toast } from '@qg/cherry-ui';
import store from '@/store';
console.log(HttpRequest);
const http = new HttpRequest(
{},
{},
......
......@@ -328,9 +328,10 @@ export function phoneFormat(mobile) {
return value;
}
export function checkPhoneFormat(mobile) {
console.log(mobile);
/* 校验手机号格式 */
if (!mobile) return;
const reg = /^1(3[0-9]|4[5,7]|5[0,1,2,3,5,6,7,8,9]|6[2,5,6,7]|7[0,1,7,8]|8[0-9]|9[1,8,9])\d{8}$/;
const reg = /^1([3-9][0-9]|4[5,7]|5[0,1,2,3,5,6,7,8,9]|6[2,5,6,7]|7[0,1,7,8]|8[0-9]|9[1,8,9])\d{8}$/;
return reg.test(mobile);
}
......
<template>
<p class="center-phone">
<span v-if="rechargeTel" class="center-phone-location"
>{{ isDefaultphone ? '默认号码' : '未知号码'
}}{{ phoneNoHome ? `(${phoneNoHome})` : '' }}</span
>
<cr-field
v-model="rechargeTel"
type="tel"
class="center-phone-field"
placeholder="请输入手机号码"
@input="changeTelFormat"
@blur="searchPhoneNoHome"
/>
</p>
</template>
<script>
import api from '@/api/recharge.api';
import { phoneFormat, phoneTrim, checkPhoneFormat } from '@/service/utils.service';
const { getPhoneHome } = api;
export default {
props: {
userPhoneInfo: Object
},
data() {
return {
phoneNoHome: null,
rechargeTel: null
};
},
computed: {
isDefaultphone: function() {
return this.phoneNo === this.userPhoneInfo?.phoneNo;
},
phoneNo: function() {
return phoneTrim(this.rechargeTel);
}
},
watch: {
userPhoneInfo: function(val) {
this.phoneNoHome = val?.phoneNoHome;
this.changeTelFormat(val?.phoneNo);
}
},
created() {},
methods: {
changeTelFormat(phone) {
this.phoneNoHome = '';
this.rechargeTel = phoneFormat(phone);
},
async searchPhoneNoHome() {
if (!this.phoneNo) return;
if (!checkPhoneFormat(this.phoneNo)) {
this.$toast.fail('请您输入正确的手机号');
this.$emit('input', { phoneNo: '', phoneNoHome: this.phoneNoHome });
return;
}
const [data] = await getPhoneHome(this.phoneNo);
this.phoneNoHome = data?.phoneHome;
this.$emit('input', { phoneNo: this.phoneNo, phoneNoHome: this.phoneNoHome });
}
}
};
</script>
<style lang="less">
.center {
&-phone {
border-bottom: @border-width-base solid @gray-2;
.cr-cell__title {
display: none;
}
.cr-cell {
.text-24();
color: @black;
padding: @padding-xs 0;
}
&-location {
.text-12();
color: @gray-5;
}
&-field {
text-indent: -2px;
}
}
}
</style>
<template>
<div class="center">
<cr-tabs
v-model="rechargeType"
background="#ffffff"
class="center-recharge"
@change="changeRechargeType"
>
<cr-tab title="话费充值" :title-style="tabItemStyle" name="recharge">
<PhoneRechargeList
:list="rechargeList.recharge"
:info="selectedRechargeInfo"
:three-col="true"
/>
</cr-tab>
<cr-tab title="话费慢冲" :title-style="tabItemStyle" name="slowRecharge">
<PhoneRechargeList
:list="rechargeList.slowRecharge"
:info="selectedRechargeInfo"
:three-col="true"
/></cr-tab>
</cr-tabs>
</div>
<cr-tabs
v-model="rechargeType"
background="#ffffff"
class="center-recharge"
@change="changeRechargeType"
>
<cr-tab title="话费充值" :title-style="tabItemStyle" name="recharge">
<PhoneRechargeList
:list="rechargeList.recharge"
:info="selectedRechargeInfo"
:three-col="true"
/>
</cr-tab>
<cr-tab title="话费慢冲" :title-style="tabItemStyle" name="slowRecharge">
<PhoneRechargeList
:list="rechargeList.slowRecharge"
:info="selectedRechargeInfo"
:three-col="true"
/></cr-tab>
</cr-tabs>
</template>
<script>
import api from '@/api/recharge.api';
import PhoneRechargeList from '../vipLife/components/SkuList.vue';
const { getSpuList, getSkuList } = api;
import PhoneRechargeList from '../../vipLife/components/SkuList.vue';
const { getSkuList } = api;
export default {
components: {
PhoneRechargeList
},
props: { info: Object },
props: { phoneRecharge: Object },
data() {
return {
rechargeType: 'recharge',
tabItemStyle: {
width: '75px',
flex: 'none'
},
selectedRechargeInfo: {},
rechargeList: {
recharge: [],
slowRecharge: []
},
rechargeTel: 'recharge',
selectedRechargeInfo: {}
}
};
},
async mounted() {
const [data] = await getSpuList();
if (data) {
this.vipLife = data.vipLife || [];
this.phoneRecharge = data.phoneRecharge || {};
this.userPhoneInfo = data.userPhoneInfo || {};
this.changeTelFormat(this.userPhoneInfo.phoneNo);
this.phoneNoHome = this.userPhoneInfo.phoneNoHome;
watch: {
phoneRecharge: function() {
this.changeRechargeType('recharge');
}
},
......@@ -62,15 +54,19 @@ export default {
this.phoneRecharge[`${name}SpuNo`],
this.phoneRecharge[`${name}Type`]
);
this.rechargeList[name] = data.rechargeList || [];
this.rechargeList[name] = data?.rechargeList || [];
},
handleSkuSelected(item) {
this.selectedRechargeInfo = item;
this.$emit('selectedRecherge', item);
this.$emit('selectedRecharge', item);
}
}
};
</script>
<style lang="less">
@import './index';
.center-recharge {
height: 300px;
margin-top: @padding-xs;
margin-bottom: @padding-xs;
}
</style>
<template>
<div class="top">
<div class="top-link">
<a href="#" class="top-link-title top-link-title-first" @click="goOrderList">充值订单</a>
<a
href="https://www.sobot.com/chat/h5/v2/index.html?sysnum=84ed0ad93caa47b0a9d1600824546b35&source=2"
class="top-link-title"
>联系客服</a
>
</div>
<div class="top-tabs">
<p class="top-tabs-title">娱乐生活</p>
<div class="top-tabs-recharge">
<div v-for="life in vipLife" :key="life.type" class="recharge" @click="goVipLife(life)">
<cr-image width="34px" height="34px" :src="life.icon" />
<p class="recharge-name">{{ life.name }}</p>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
value: Array
},
data() {
return {
vipLife: this.value
};
},
watch: {
value: function(val) {
this.vipLife = val;
}
},
methods: {
goVipLife(spu) {
this.$router.push({
name: 'vipLife',
query: {
spuType: spu.type
}
});
},
goOrderList() {
this.$router.push({ name: 'orderList' });
}
}
};
</script>
<style lang="less">
.top {
width: 100%;
height: 132px;
padding: 10px;
background-size: 100%;
box-sizing: border-box;
background-repeat: no-repeat;
background: url('~@/assets/images/ellipse.png');
.recharge {
width: 55px;
height: 55px;
text-align: center;
&-name {
.text-12();
color: @gray-5;
}
}
&-link {
text-align: right;
&-title {
.text-14();
width: 64px;
color: @white;
padding: 0 @padding-xs;
&-first {
border-right: 1px solid @white;
}
}
}
&-tabs {
height: 108px;
width: 351px;
margin: auto;
overflow: hidden;
position: relative;
background: @white;
box-sizing: border-box;
border-radius: @border-radius-md;
padding: @padding-xs @padding-sm;
top: @padding-md;
&-title {
.text-14();
color: @black;
font-weight: bold;
}
&-recharge {
display: flex;
overflow-x: auto;
overflow-y: hidden;
margin-top: @padding-sm;
justify-content: space-between;
}
}
}
</style>
.center {
.home {
height: 100%;
.top {
width: 100%;
height: 132px;
padding: 10px;
box-sizing: border-box;
background: url('../../assets/images/ellipse.png');
background-size: 100%;
background-repeat: no-repeat;
&-link {
text-align: right;
&-title {
.text-14();
width: 64px;
padding: 0 @padding-xs;
color: @white;
&-first {
border-right: 1px solid @white;
}
}
}
&-tabs {
position: relative;
height: 108px;
width: 351px;
box-sizing: border-box;
overflow: hidden;
margin: auto;
border-radius: 8px;
background: @white;
padding: @padding-xs @padding-sm;
top: 16px;
&-title {
.text-14();
font-weight: bold;
color: @black;
}
&-recharge {
display: flex;
margin-top: 10px;
overflow-x: auto;
overflow-y: hidden;
justify-content: space-between;
}
}
}
.recharge {
width: 55px;
height: 55px;
text-align: center;
&-name {
.text-12();
color: @gray-5;
}
}
.center {
width: 351px;
height: 451px;
......@@ -63,26 +9,6 @@
padding: 14px;
margin: auto;
margin-top: 50px;
&-phone {
border-bottom: 1px solid #dcdcdc;
.cr-cell__title {
display: none;
}
.cr-cell {
padding: @padding-xs 0;
font-size: 24px;
color: @black;
}
&-location {
.text-12();
color: @gray-5;
}
}
&-recharge {
height: 300px;
margin-top: @padding-xs;
margin-bottom: @padding-xs;
}
.button{
.text-18();
}
......
<template>
<div class="center">
<div class="top">
<div class="top-link">
<a href="#" class="top-link-title top-link-title-first" @click="goOrderList">充值订单</a>
<a href="#" class="top-link-title" @click="goHelper">联系客服</a>
</div>
<div class="top-tabs">
<p class="top-tabs-title">娱乐生活</p>
<div class="top-tabs-recharge">
<div v-for="life in vipLife" :key="life.type" class="recharge" @click="goVipLife(life)">
<cr-image width="34px" height="34px" :src="life.icon" />
<p class="recharge-name">{{ life.name }}</p>
</div>
</div>
</div>
</div>
<div class="home">
<RechargeTop v-model="vipLife" />
<div class="center">
<p class="center-phone">
<span v-if="rechargeTel" class="center-phone-location"
>{{ isDefaultphone ? '默认号码' : '未知号码'
}}{{ phoneNoHome ? `(${phoneNoHome})` : '' }}</span
>
<cr-field
v-model="rechargeTel"
type="tel"
@input="changeTelFormat"
@blur="searchPhoneNoHome"
/>
</p>
<!-- <PhoneRecharge :info="phoneRecharge" /> -->
<cr-tabs
v-model="rechargeType"
background="#ffffff"
class="center-recharge"
@change="changeRechargeType"
>
<cr-tab title="话费充值" :title-style="tabItemStyle" name="recharge">
<PhoneRechargeList
:list="rechargeList.recharge"
:info="selectedRechargeInfo"
:three-col="true"
/>
</cr-tab>
<cr-tab title="话费慢冲" :title-style="tabItemStyle" name="slowRecharge">
<PhoneRechargeList
:list="rechargeList.slowRecharge"
:info="selectedRechargeInfo"
:three-col="true"
/></cr-tab>
</cr-tabs>
<RechargeInput v-model="rechargePhoneInfo" :user-phone-info="userPhoneInfo" />
<RechargeList :phone-recharge="phoneRecharge" @selectedRecharge="selectedRecharge" />
<cr-button
type="primary"
block
type="primary"
shape="circle"
:disabled="!disabled"
class="button"
:disabled="!disabled"
@click="goOrder"
>
{{ selectedRechargeInfo.salePrice ? `¥${selectedRechargeInfo.salePrice}` : '' }}立即充值
......@@ -64,50 +18,33 @@
</div>
</template>
<script>
import { phoneFormat, phoneTrim, checkPhoneFormat } from '@/service/utils.service';
import api from '@/api/recharge.api';
import PhoneRechargeList from '../vipLife/components/SkuList.vue';
const { getSpuList, getPhoneHome, getSkuList } = api;
// 组件抽离
// 下订单
import orderApi from '@/api/order.api';
import RechargeTop from './components/RechargeTop.vue';
import RechargeList from './components/RechargeList.vue';
import RechargeInput from './components/RechargeInput.vue';
import localStorage from '@/service/localStorage.service';
const { getSpuList } = api;
export default {
components: {
PhoneRechargeList
RechargeTop,
RechargeList,
RechargeInput
},
data() {
return {
vipLife: [],
phoneNoHome: '',
rechargeType: null,
rechargeAmount: 0,
tabItemStyle: {
width: '75px',
flex: 'none'
},
phoneRecharge: {},
userPhoneInfo: {},
rechargeList: {
recharge: [],
slowRecharge: []
},
rechargeTel: 'recharge',
rechargeType: null,
rechargePhoneInfo: {},
selectedRechargeInfo: {}
};
},
computed: {
disabled: function() {
return (
this.rechargeTel &&
phoneTrim(this.rechargeTel).length === 11 &&
checkPhoneFormat(this.phoneNo) &&
this.selectedRechargeInfo.salePrice
);
},
isDefaultphone: function() {
return phoneTrim(this.rechargeTel) === this.userPhoneInfo.phoneNo;
},
phoneNo: function() {
return phoneTrim(this.rechargeTel);
return this.rechargePhoneInfo.phoneNo && this.selectedRechargeInfo.salePrice;
}
},
created() {},
......@@ -117,45 +54,28 @@ export default {
this.vipLife = data.vipLife || [];
this.phoneRecharge = data.phoneRecharge || {};
this.userPhoneInfo = data.userPhoneInfo || {};
this.changeTelFormat(this.userPhoneInfo.phoneNo);
this.phoneNoHome = this.userPhoneInfo.phoneNoHome;
this.changeRechargeType('recharge');
this.rechargePhoneInfo = data.userPhoneInfo || {};
localStorage.set('phoneNo', data.userPhoneInfo.phoneNo);
}
},
methods: {
changeTelFormat(phone) {
this.rechargeTel = phoneFormat(phone);
},
async searchPhoneNoHome() {
if (!this.phoneNo) return;
if (!checkPhoneFormat(this.phoneNo)) {
this.$toast.fail('请您输入正确的手机号');
}
const [data] = await getPhoneHome(this.phoneNo);
this.phoneNoHome = data.phoneNoHome;
},
async changeRechargeType(name) {
const [data] = await getSkuList(
this.phoneRecharge[`${name}SpuNo`],
this.phoneRecharge[`${name}Type`]
);
this.rechargeList[name] = data.rechargeList || [];
},
handleSkuSelected(item) {
selectedRecharge(item) {
this.selectedRechargeInfo = item;
},
goOrder() {},
goOrderList() {
this.$router.push({ name: 'orderList' });
},
goHelper() {},
goVipLife(spu) {
this.$router.push({
name: 'vipLife',
query: {
spuType: spu.type
}
async goOrder() {
const { skuNo, salePrice } = this.selectedRechargeInfo;
const [res] = await orderApi.orderCreate({
totalFee: salePrice,
registrationLocation: this.rechargePhoneInfo.phoneNoHome,
virtualRechargeType: this.phoneRecharge[`${this.rechargeType}Type`],
skuList: [
{
skuNo,
count: 1
}
]
});
res && this.$router.push({ path: '/pay', query: { orderNo: res.orderNo } });
}
}
};
......
......@@ -5,7 +5,7 @@
<p class="sms-icon"><cr-icon type="cross" color="#999999" @click="closeModal" /></p>
<p class="sms-title">请输入短信验证码</p>
<p class="sms-des">为保证您账户安全,此笔交易需要短信验证</p>
<p class="sms-des phone">已发送至 {{ getPhone() }}17611682272</p>
<p class="phone">已发送至 {{ getPhone() }}17611682272</p>
<cr-authcode-field
span-size="20px"
type="number"
......@@ -30,6 +30,7 @@
<script>
// import { registeredEvents } from '@/utils/sa';
import localStorage from '@/service/localStorage.service';
import { sendSms as sendSmsApi } from '@/api/pay.api';
export default {
props: {
......@@ -85,9 +86,9 @@ export default {
}
},
getPhone() {
// const phone = uni.getStorageSync('phone');
// const phoneS = phone.replace(/(\d{3})\d*(\d{4})/, '+86 $1 **** $2');
// return phoneS;
const phone = localStorage.get('phoneNo');
const phoneS = phone.replace(/(\d{3})\d*(\d{4})/, '+86 $1 **** $2');
return phoneS;
},
sendSa() {
// if (!this.numberArr.length && !this.isDel) {
......@@ -129,7 +130,7 @@ export default {
font-size: 16px;
}
&-des {
padding: 10px 0 0 8px;
padding: 17px 0 0 8px;
display: flex;
justify-content: flex-start;
font-size: 12px;
......@@ -139,6 +140,7 @@ export default {
margin: auto;
}
.phone {
padding-left: 8px;
margin-top: 8px;
color: #323233;
font-size: 14px;
......
.app {
.pay {
// width: 100%;
padding: @padding-xs;
}
......
<template>
<div class="app">
<div class="pay">
<!-- 支付倒计时 -->
<div class="price-box">
<p class="price">{{ displayInfo.orderAmt }}100</p>
......
<template>
<div v-if="isSuccess" class="card">
<div class="info">
<cr-image
width="100px"
height="100px"
class="info__image"
src="https://img.lkbang.net/xcx/pay-success.png"
/>
<div class="info__desc">
<p class="info__text">订单支付成功!</p>
<p class="info__text info__money">实付¥{{ money }}</p>
<p v-if="freeAmount > 0" class="info__text info__free">已优惠¥{{ freeAmount }}</p>
<div>
<div v-if="isSuccess" class="card">
<div class="info">
<cr-image
width="100px"
height="100px"
class="info__image"
src="https://img.lkbang.net/xcx/pay-success.png"
/>
<div class="info__desc">
<p class="info__text">订单支付成功!</p>
<p class="info__text info__money">实付¥{{ money }}</p>
<p v-if="freeAmount > 0" class="info__text info__free">已优惠¥{{ freeAmount }}</p>
</div>
</div>
<p class="tips">
您可在“我的-我的订单”中查看详情
</p>
<div class="actions">
<cr-button shape="circle" class="actions__back" :plain="true" type="primary" @click="goHome"
>返回首页</cr-button
>
<cr-button shape="circle" type="primary" @click="goOrderDetail">查看订单</cr-button>
</div>
</div>
<p class="tips">
您可在“我的-我的订单”中查看详情
</p>
<div class="actions">
<cr-button shape="circle" class="actions__back" :plain="true" type="primary" @click="goHome"
>返回首页</cr-button
<div v-else class="card">
<div class="info">
<cr-image
width="100px"
height="100px"
class="info__image"
src="https://img.lkbang.net/xcx/pay-fail.png"
/>
<p class="info__text">订单支付失败!</p>
</div>
<p class="tips">
{{ reason }}
</p>
<cr-button type="primary" shape="circle" class="info_button" @click="goPay"
>重新支付</cr-button
>
<cr-button shape="circle" type="primary" @click="goOrderDetail">查看订单</cr-button>
</div>
</div>
<div v-else class="card">
<div class="info">
<cr-image
width="100px"
height="100px"
class="info__image"
src="https://img.lkbang.net/xcx/pay-fail.png"
/>
<p class="info__text">订单支付失败!</p>
</div>
<p class="tips">
{{ reason }}
</p>
<cr-button type="primary" shape="circle" class="info_button" @click="goPay">重新支付</cr-button>
<RecoGoods />
</div>
</template>
<script>
import { registeredEvents } from '@/service/sa.service';
import RecoGoods from '@/components/RecoGoods.vue';
export default {
components: {},
components: { RecoGoods },
data() {
return {
money: '00.00',
......
......@@ -66,8 +66,9 @@ export default {
}
}
&-item {
width: 162px;
width: 159px;
height: 72px;
box-sizing: border-box;
border-radius: @border-radius-sm - 2;
border: 1px solid @grey-border;
display: flex;
......
......@@ -58,7 +58,6 @@ export default {
},
mounted() {
this.currentTab = +this.$route.query.spuType || 0;
console.log(this.currentTab);
this.$nextTick(() => {
this.getList();
});
......@@ -103,6 +102,7 @@ export default {
totalFee: salePrice,
orderCouponIds: '',
freightCouponIds: '',
virtualRechargeType: this.spuInfo.rechargeAccountType,
skuList: [
{
skuNo,
......
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