Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
cauchy-ui
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ui
cauchy-ui
Commits
dc05b25f
Commit
dc05b25f
authored
Jul 06, 2021
by
FE-安焕焕
👣
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
下订单逻辑
parent
f31000f9
Changes
19
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
770 additions
and
305 deletions
+770
-305
order.api.js
src/api/order.api.js
+2
-1
pay.api.js
src/api/pay.api.js
+19
-6
RecoGoods.vue
src/components/RecoGoods.vue
+98
-0
RecoGoodsItem.vue
src/components/RecoGoodsItem.vue
+205
-0
encrypt.config.js
src/service/encrypt.config.js
+26
-0
encrypt.js
src/service/encrypt.js
+62
-63
httpDecorator.js
src/service/httpDecorator.js
+0
-1
utils.service.js
src/service/utils.service.js
+2
-1
RechargeInput.vue
src/views/home/components/RechargeInput.vue
+86
-0
RechargeList.vue
src/views/home/components/RechargeList.vue
+72
-0
RechargeTop.vue
src/views/home/components/RechargeTop.vue
+110
-0
index.less
src/views/home/index.less
+1
-75
index.vue
src/views/home/index.vue
+34
-114
SmsModal.vue
src/views/pay/components/SmsModal.vue
+7
-5
index.less
src/views/pay/index.less
+1
-1
index.vue
src/views/pay/index.vue
+1
-1
payResult.vue
src/views/pay/payResult.vue
+41
-35
SkuList.vue
src/views/vipLife/components/SkuList.vue
+2
-1
index.vue
src/views/vipLife/index.vue
+1
-1
No files found.
src/api/order.api.js
View file @
dc05b25f
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
)
}
);
}
};
src/api/pay.api.js
View file @
dc05b25f
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
};
src/components/RecoGoods.vue
0 → 100644
View file @
dc05b25f
<
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
>
src/components/RecoGoodsItem.vue
0 → 100644
View file @
dc05b25f
<
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
>
src/service/encrypt.config.js
0 → 100644
View file @
dc05b25f
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-----
`
;
src/service/encrypt.js
View file @
dc05b25f
...
...
@@ -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
;
}
src/service/httpDecorator.js
View file @
dc05b25f
import
HttpRequest
from
'
@qg/ui-request
'
;
import
{
Toast
}
from
'
@qg/cherry-ui
'
;
import
store
from
'
@/store
'
;
console
.
log
(
HttpRequest
);
const
http
=
new
HttpRequest
(
{},
{},
...
...
src/service/utils.service.js
View file @
dc05b25f
...
...
@@ -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
);
}
...
...
src/views/home/components/RechargeInput.vue
0 → 100644
View file @
dc05b25f
<
template
>
<p
class=
"center-phone"
>
<span
v-if=
"rechargeTel"
class=
"center-phone-location"
>
{{
isDefaultphone
?
'
默认号码
'
:
'
未知号码
'
}}{{
phoneNoHome
?
`(${phoneNoHome
}
)`
:
''
}}
<
/spa
n
>
<
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
:
-
2
px
;
}
}
}
<
/style
>
src/views/home/components/
PhoneRecharge
.vue
→
src/views/home/components/
RechargeList
.vue
View file @
dc05b25f
<
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
{
getS
puList
,
getS
kuList
}
=
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
(
'
selectedRech
e
rge
'
,
item
);
this
.
$emit
(
'
selectedRech
a
rge
'
,
item
);
}
}
};
</
script
>
<
style
lang=
"less"
>
@import './index';
.center-recharge {
height: 300px;
margin-top: @padding-xs;
margin-bottom: @padding-xs;
}
</
style
>
src/views/home/components/RechargeTop.vue
0 → 100644
View file @
dc05b25f
<
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
>
src/views/home/index.less
View file @
dc05b25f
.
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();
}
...
...
src/views/home/index.vue
View file @
dc05b25f
<
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
}
)`
:
''
}}
<
/spa
n
>
<
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
}
}
);
}
}
}
;
...
...
src/views/pay/components/SmsModal.vue
View file @
dc05b25f
...
...
@@ -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: 1
0
px 0 0 8px;
padding: 1
7
px 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;
...
...
src/views/pay/index.less
View file @
dc05b25f
.
app
{
.
pay
{
// width: 100%;
padding: @padding-xs;
}
...
...
src/views/pay/index.vue
View file @
dc05b25f
<
template
>
<div
class=
"
app
"
>
<div
class=
"
pay
"
>
<!-- 支付倒计时 -->
<div
class=
"price-box"
>
<p
class=
"price"
>
{{
displayInfo
.
orderAmt
}}
100
</p>
...
...
src/views/pay/payResult.vue
View file @
dc05b25f
<
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
'
,
...
...
src/views/vipLife/components/SkuList.vue
View file @
dc05b25f
...
...
@@ -66,8 +66,9 @@ export default {
}
}
&-item {
width: 1
62
px;
width: 1
59
px;
height: 72px;
box-sizing: border-box;
border-radius: @border-radius-sm - 2;
border: 1px solid @grey-border;
display: flex;
...
...
src/views/vipLife/index.vue
View file @
dc05b25f
...
...
@@ -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
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment