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

下订单逻辑

parent f31000f9
import config from '@/config'; import config from '@/config';
import http from '@/service/httpDecorator'; import http from '@/service/httpDecorator';
import { encryption } from '@/service/encrypt';
const { talosHost } = config; const { talosHost } = config;
export default { export default {
...@@ -22,6 +23,6 @@ export default { ...@@ -22,6 +23,6 @@ export default {
}, },
// 订单创建 // 订单创建
orderCreate(data) { 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 request from '@/service/httpDecorator';
import config from '@/config'; import config from '@/config';
import { encryption } from '@/service/encrypt';
const { talosHost } = config; const { talosHost } = config;
const queryPayInfo = function(data) { const queryPayInfo = function(data) {
...@@ -11,9 +12,13 @@ const prepay = function(data) { ...@@ -11,9 +12,13 @@ const prepay = function(data) {
}; };
const pay = function(data) { const pay = function(data) {
return request.post(`${talosHost}/open/checkout/pay`, data, { return request.post(
`${talosHost}/open/checkout/pay`,
{ data: encryption(data) },
{
needScDeviceId: true needScDeviceId: true
}); }
);
}; };
const queryPayStatus = function(data) { const queryPayStatus = function(data) {
...@@ -40,14 +45,22 @@ const ocrFaceId = function(params) { ...@@ -40,14 +45,22 @@ const ocrFaceId = function(params) {
return request.post(`${talosHost}open/checkout/ocr_faceId`, 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 { export {
pay, pay,
prepay, prepay,
sendSms, sendSms,
queryPayInfo,
queryPayStatus,
desSalt, desSalt,
h5AppyUrl, h5AppyUrl,
getCoupon, 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 @@ ...@@ -4,13 +4,12 @@
* @LastEditors: gzw * @LastEditors: gzw
* @LastEditTime: 2021-01-29 19:06:27 * @LastEditTime: 2021-01-29 19:06:27
*/ */
// perf forge.js较大,后期需要替换为其他库,以减小体积 import { cipher as AES, util as UTIL, pki as PKI, md as SHA1 } from 'node-forge';
// const forge = require('../utils/forge.min'); 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'); const CryptoJS = require('./crypto.min');
import { desSalt } from '@/api/pay.api'; 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: 数据加密 * @description: 数据加密
...@@ -18,21 +17,21 @@ import { desSalt } from '@/api/pay.api'; ...@@ -18,21 +17,21 @@ import { desSalt } from '@/api/pay.api';
* @return {String} 加密后的数据base64 * @return {String} 加密后的数据base64
*/ */
// export function encryption(data = '') { export function encryption(data = '') {
// if (!data) return null; if (!data) return null;
// const key = generateRandomStr(16); const key = generateRandomStr(16);
// const iv = key; // 后台约定iv与key一致 const iv = key; // 后台约定iv与key一致
// const plaintext = typeof data === 'object' ? JSON.stringify(data) : data; const plaintext = typeof data === 'object' ? JSON.stringify(data) : data;
// const body = encryptDataByAes(plaintext, key, iv); // AES加密数据 const body = encryptDataByAes(plaintext, key, iv); // AES加密数据
// const encryptKey = encryptDataByPb(key); // RSA公钥加密AES密钥 const encryptKey = encryptDataByPb(key); // RSA公钥加密AES密钥
// const signData = generateSign(plaintext); const signData = generateSign(plaintext);
// return { return {
// appId: APP_ID, appId: APP_ID,
// body, body,
// encryptKey, encryptKey,
// ...signData ...signData
// }; };
// } }
/** /**
* @description: 数据加密 * @description: 数据加密
...@@ -56,25 +55,25 @@ export async function encryptByDESModeEBC(message) { ...@@ -56,25 +55,25 @@ export async function encryptByDESModeEBC(message) {
* @param {String} iv 初始化向量 * @param {String} iv 初始化向量
* @return {String} 加密后的数据base64 * @return {String} 加密后的数据base64
*/ */
// function encryptDataByAes(txt, key, iv) { function encryptDataByAes(txt, key, iv) {
// const cipherInstance = forge.cipher.createCipher('AES-CBC', key); const cipher = AES.createCipher('AES-CBC', key);
// cipherInstance.start({ iv }); cipher.start({ iv });
// cipherInstance.update(forge.util.createBuffer(txt, 'utf8')); cipher.update(UTIL.createBuffer(txt, 'utf8'));
// cipherInstance.finish(); cipher.finish();
// const ciphertext = cipherInstance.output.getBytes(); const ciphertext = cipher.output.getBytes();
// return buffer2Base64(ciphertext); return buffer2Base64(ciphertext);
// } }
/** /**
* @description: 使用RSA公钥加密数据 * @description: 使用RSA公钥加密数据
* @param {String} txt 数据源 * @param {String} txt 数据源
* @return {String} 加密后的数据base64 * @return {String} 加密后的数据base64
*/ */
// function encryptDataByPb(txt) { function encryptDataByPb(txt) {
// const publicKey = forge.pki.publicKeyFromPem(PUBLIC_KEY); const publicKey = PKI.publicKeyFromPem(PUBLIC_KEY);
// const pbData = publicKey.encrypt(txt); const pbData = publicKey.encrypt(txt);
// return buffer2Base64(pbData); return buffer2Base64(pbData);
// } }
/** /**
* @description: RSA私钥+SHA1生成签名 * @description: RSA私钥+SHA1生成签名
...@@ -82,51 +81,51 @@ export async function encryptByDESModeEBC(message) { ...@@ -82,51 +81,51 @@ export async function encryptByDESModeEBC(message) {
* @param {String} txt 数据源 * @param {String} txt 数据源
* @return {Object} 生成的sign数据,时间戳、Nonce * @return {Object} 生成的sign数据,时间戳、Nonce
*/ */
// function generateSign(txt) { function generateSign(txt) {
// const timestamp = parseTime(''); const timestamp = parseTime('');
// const nonce = generateNonce(); const nonce = generateNonce();
// const privateKey = forge.pki.privateKeyFromPem(PRIVATE_KEY); const privateKey = PKI.privateKeyFromPem(PRIVATE_KEY);
// const md = forge.md.sha1.create(); const md = SHA1.sha1.create();
// md.update(nonce + APP_ID + timestamp + txt, 'utf8'); md.update(nonce + APP_ID + timestamp + txt, 'utf8');
// let sign = privateKey.sign(md); let sign = privateKey.sign(md);
// sign = buffer2Base64(sign); sign = buffer2Base64(sign);
// return { return {
// timestamp, timestamp,
// nonce, nonce,
// sign sign
// }; };
// } }
/** /**
* @description: buffer转base64 * @description: buffer转base64
* @param {Buffer} buf buffer源数据 * @param {Buffer} buf buffer源数据
* @return {String} base64 字符串 * @return {String} base64 字符串
*/ */
// function buffer2Base64(buf) { function buffer2Base64(buf) {
// return forge.util.encode64(buf); return UTIL.encode64(buf);
// } }
/** /**
* @description: 生成nonce(uuid) * @description: 生成nonce(uuid)
* 规则:以当前时间的uuid作为name,以随机生成的uuid作为namespace,生成最终的uuid * 规则:以当前时间的uuid作为name,以随机生成的uuid作为namespace,生成最终的uuid
* @return {String} 生成的uuid * @return {String} 生成的uuid
*/ */
// function generateNonce() { function generateNonce() {
// return uuidv1(); return uuidv1();
// } }
/** /**
* @description: 生成随机字符串 * @description: 生成随机字符串
* @param {Number} n 位数 * @param {Number} n 位数
* @return {String} 生成的字符串 * @return {String} 生成的字符串
*/ */
// function generateRandomStr(n) { function generateRandomStr(n) {
// const len = n || 32; const len = n || 32;
// const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz0123456789'; const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz0123456789';
// const maxPos = chars.length; const maxPos = chars.length;
// let pwd = ''; let pwd = '';
// for (let i = 0; i < len; i++) { for (let i = 0; i < len; i++) {
// pwd += chars.charAt(Math.floor(Math.random() * maxPos)); pwd += chars.charAt(Math.floor(Math.random() * maxPos));
// } }
// return pwd; return pwd;
// } }
import HttpRequest from '@qg/ui-request'; import HttpRequest from '@qg/ui-request';
import { Toast } from '@qg/cherry-ui'; import { Toast } from '@qg/cherry-ui';
import store from '@/store'; import store from '@/store';
console.log(HttpRequest);
const http = new HttpRequest( const http = new HttpRequest(
{}, {},
{}, {},
......
...@@ -328,9 +328,10 @@ export function phoneFormat(mobile) { ...@@ -328,9 +328,10 @@ export function phoneFormat(mobile) {
return value; return value;
} }
export function checkPhoneFormat(mobile) { export function checkPhoneFormat(mobile) {
console.log(mobile);
/* 校验手机号格式 */ /* 校验手机号格式 */
if (!mobile) return; 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); 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> <template>
<div class="center">
<cr-tabs <cr-tabs
v-model="rechargeType" v-model="rechargeType"
background="#ffffff" background="#ffffff"
...@@ -20,39 +19,32 @@ ...@@ -20,39 +19,32 @@
:three-col="true" :three-col="true"
/></cr-tab> /></cr-tab>
</cr-tabs> </cr-tabs>
</div>
</template> </template>
<script> <script>
import api from '@/api/recharge.api'; import api from '@/api/recharge.api';
import PhoneRechargeList from '../vipLife/components/SkuList.vue'; import PhoneRechargeList from '../../vipLife/components/SkuList.vue';
const { getSpuList, getSkuList } = api; const { getSkuList } = api;
export default { export default {
components: { components: {
PhoneRechargeList PhoneRechargeList
}, },
props: { info: Object }, props: { phoneRecharge: Object },
data() { data() {
return { return {
rechargeType: 'recharge',
tabItemStyle: { tabItemStyle: {
width: '75px', width: '75px',
flex: 'none' flex: 'none'
}, },
selectedRechargeInfo: {},
rechargeList: { rechargeList: {
recharge: [], recharge: [],
slowRecharge: [] slowRecharge: []
}, }
rechargeTel: 'recharge',
selectedRechargeInfo: {}
}; };
}, },
async mounted() { watch: {
const [data] = await getSpuList(); phoneRecharge: function() {
if (data) {
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.changeRechargeType('recharge');
} }
}, },
...@@ -62,15 +54,19 @@ export default { ...@@ -62,15 +54,19 @@ export default {
this.phoneRecharge[`${name}SpuNo`], this.phoneRecharge[`${name}SpuNo`],
this.phoneRecharge[`${name}Type`] this.phoneRecharge[`${name}Type`]
); );
this.rechargeList[name] = data.rechargeList || []; this.rechargeList[name] = data?.rechargeList || [];
}, },
handleSkuSelected(item) { handleSkuSelected(item) {
this.selectedRechargeInfo = item; this.selectedRechargeInfo = item;
this.$emit('selectedRecherge', item); this.$emit('selectedRecharge', item);
} }
} }
}; };
</script> </script>
<style lang="less"> <style lang="less">
@import './index'; .center-recharge {
height: 300px;
margin-top: @padding-xs;
margin-bottom: @padding-xs;
}
</style> </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%; 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 { .center {
width: 351px; width: 351px;
height: 451px; height: 451px;
...@@ -63,26 +9,6 @@ ...@@ -63,26 +9,6 @@
padding: 14px; padding: 14px;
margin: auto; margin: auto;
margin-top: 50px; 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{ .button{
.text-18(); .text-18();
} }
......
<template> <template>
<div class="home">
<RechargeTop v-model="vipLife" />
<div class="center"> <div class="center">
<div class="top"> <RechargeInput v-model="rechargePhoneInfo" :user-phone-info="userPhoneInfo" />
<div class="top-link"> <RechargeList :phone-recharge="phoneRecharge" @selectedRecharge="selectedRecharge" />
<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="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>
<cr-button <cr-button
type="primary"
block block
type="primary"
shape="circle" shape="circle"
:disabled="!disabled"
class="button" class="button"
:disabled="!disabled"
@click="goOrder" @click="goOrder"
> >
{{ selectedRechargeInfo.salePrice ? `¥${selectedRechargeInfo.salePrice}` : '' }}立即充值 {{ selectedRechargeInfo.salePrice ? `¥${selectedRechargeInfo.salePrice}` : '' }}立即充值
...@@ -64,50 +18,33 @@ ...@@ -64,50 +18,33 @@
</div> </div>
</template> </template>
<script> <script>
import { phoneFormat, phoneTrim, checkPhoneFormat } from '@/service/utils.service';
import api from '@/api/recharge.api'; import api from '@/api/recharge.api';
import PhoneRechargeList from '../vipLife/components/SkuList.vue'; import orderApi from '@/api/order.api';
const { getSpuList, getPhoneHome, getSkuList } = 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 { export default {
components: { components: {
PhoneRechargeList RechargeTop,
RechargeList,
RechargeInput
}, },
data() { data() {
return { return {
vipLife: [], vipLife: [],
phoneNoHome: '',
rechargeType: null,
rechargeAmount: 0, rechargeAmount: 0,
tabItemStyle: {
width: '75px',
flex: 'none'
},
phoneRecharge: {}, phoneRecharge: {},
userPhoneInfo: {}, userPhoneInfo: {},
rechargeList: { rechargeType: null,
recharge: [], rechargePhoneInfo: {},
slowRecharge: []
},
rechargeTel: 'recharge',
selectedRechargeInfo: {} selectedRechargeInfo: {}
}; };
}, },
computed: { computed: {
disabled: function() { disabled: function() {
return ( return this.rechargePhoneInfo.phoneNo && this.selectedRechargeInfo.salePrice;
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);
} }
}, },
created() {}, created() {},
...@@ -117,45 +54,28 @@ export default { ...@@ -117,45 +54,28 @@ export default {
this.vipLife = data.vipLife || []; this.vipLife = data.vipLife || [];
this.phoneRecharge = data.phoneRecharge || {}; this.phoneRecharge = data.phoneRecharge || {};
this.userPhoneInfo = data.userPhoneInfo || {}; this.userPhoneInfo = data.userPhoneInfo || {};
this.changeTelFormat(this.userPhoneInfo.phoneNo); this.rechargePhoneInfo = data.userPhoneInfo || {};
this.phoneNoHome = this.userPhoneInfo.phoneNoHome; localStorage.set('phoneNo', data.userPhoneInfo.phoneNo);
this.changeRechargeType('recharge');
} }
}, },
methods: { methods: {
changeTelFormat(phone) { selectedRecharge(item) {
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) {
this.selectedRechargeInfo = item; this.selectedRechargeInfo = item;
}, },
goOrder() {}, async goOrder() {
goOrderList() { const { skuNo, salePrice } = this.selectedRechargeInfo;
this.$router.push({ name: 'orderList' }); const [res] = await orderApi.orderCreate({
}, totalFee: salePrice,
goHelper() {}, registrationLocation: this.rechargePhoneInfo.phoneNoHome,
goVipLife(spu) { virtualRechargeType: this.phoneRecharge[`${this.rechargeType}Type`],
this.$router.push({ skuList: [
name: 'vipLife', {
query: { skuNo,
spuType: spu.type count: 1
} }
]
}); });
res && this.$router.push({ path: '/pay', query: { orderNo: res.orderNo } });
} }
} }
}; };
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<p class="sms-icon"><cr-icon type="cross" color="#999999" @click="closeModal" /></p> <p class="sms-icon"><cr-icon type="cross" color="#999999" @click="closeModal" /></p>
<p class="sms-title">请输入短信验证码</p> <p class="sms-title">请输入短信验证码</p>
<p class="sms-des">为保证您账户安全,此笔交易需要短信验证</p> <p class="sms-des">为保证您账户安全,此笔交易需要短信验证</p>
<p class="sms-des phone">已发送至 {{ getPhone() }}17611682272</p> <p class="phone">已发送至 {{ getPhone() }}17611682272</p>
<cr-authcode-field <cr-authcode-field
span-size="20px" span-size="20px"
type="number" type="number"
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
<script> <script>
// import { registeredEvents } from '@/utils/sa'; // import { registeredEvents } from '@/utils/sa';
import localStorage from '@/service/localStorage.service';
import { sendSms as sendSmsApi } from '@/api/pay.api'; import { sendSms as sendSmsApi } from '@/api/pay.api';
export default { export default {
props: { props: {
...@@ -85,9 +86,9 @@ export default { ...@@ -85,9 +86,9 @@ export default {
} }
}, },
getPhone() { getPhone() {
// const phone = uni.getStorageSync('phone'); const phone = localStorage.get('phoneNo');
// const phoneS = phone.replace(/(\d{3})\d*(\d{4})/, '+86 $1 **** $2'); const phoneS = phone.replace(/(\d{3})\d*(\d{4})/, '+86 $1 **** $2');
// return phoneS; return phoneS;
}, },
sendSa() { sendSa() {
// if (!this.numberArr.length && !this.isDel) { // if (!this.numberArr.length && !this.isDel) {
...@@ -129,7 +130,7 @@ export default { ...@@ -129,7 +130,7 @@ export default {
font-size: 16px; font-size: 16px;
} }
&-des { &-des {
padding: 10px 0 0 8px; padding: 17px 0 0 8px;
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;
font-size: 12px; font-size: 12px;
...@@ -139,6 +140,7 @@ export default { ...@@ -139,6 +140,7 @@ export default {
margin: auto; margin: auto;
} }
.phone { .phone {
padding-left: 8px;
margin-top: 8px; margin-top: 8px;
color: #323233; color: #323233;
font-size: 14px; font-size: 14px;
......
.app { .pay {
// width: 100%; // width: 100%;
padding: @padding-xs; padding: @padding-xs;
} }
......
<template> <template>
<div class="app"> <div class="pay">
<!-- 支付倒计时 --> <!-- 支付倒计时 -->
<div class="price-box"> <div class="price-box">
<p class="price">{{ displayInfo.orderAmt }}100</p> <p class="price">{{ displayInfo.orderAmt }}100</p>
......
<template> <template>
<div>
<div v-if="isSuccess" class="card"> <div v-if="isSuccess" class="card">
<div class="info"> <div class="info">
<cr-image <cr-image
...@@ -36,13 +37,18 @@ ...@@ -36,13 +37,18 @@
<p class="tips"> <p class="tips">
{{ reason }} {{ reason }}
</p> </p>
<cr-button type="primary" shape="circle" class="info_button" @click="goPay">重新支付</cr-button> <cr-button type="primary" shape="circle" class="info_button" @click="goPay"
>重新支付</cr-button
>
</div>
<RecoGoods />
</div> </div>
</template> </template>
<script> <script>
import { registeredEvents } from '@/service/sa.service'; import { registeredEvents } from '@/service/sa.service';
import RecoGoods from '@/components/RecoGoods.vue';
export default { export default {
components: {}, components: { RecoGoods },
data() { data() {
return { return {
money: '00.00', money: '00.00',
......
...@@ -66,8 +66,9 @@ export default { ...@@ -66,8 +66,9 @@ export default {
} }
} }
&-item { &-item {
width: 162px; width: 159px;
height: 72px; height: 72px;
box-sizing: border-box;
border-radius: @border-radius-sm - 2; border-radius: @border-radius-sm - 2;
border: 1px solid @grey-border; border: 1px solid @grey-border;
display: flex; display: flex;
......
...@@ -58,7 +58,6 @@ export default { ...@@ -58,7 +58,6 @@ export default {
}, },
mounted() { mounted() {
this.currentTab = +this.$route.query.spuType || 0; this.currentTab = +this.$route.query.spuType || 0;
console.log(this.currentTab);
this.$nextTick(() => { this.$nextTick(() => {
this.getList(); this.getList();
}); });
...@@ -103,6 +102,7 @@ export default { ...@@ -103,6 +102,7 @@ export default {
totalFee: salePrice, totalFee: salePrice,
orderCouponIds: '', orderCouponIds: '',
freightCouponIds: '', freightCouponIds: '',
virtualRechargeType: this.spuInfo.rechargeAccountType,
skuList: [ skuList: [
{ {
skuNo, 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