Commit 0e18d1bf authored by beisir's avatar beisir

feat:订单页,收银台相关逻辑

parent 6d273608
...@@ -1186,6 +1186,28 @@ ...@@ -1186,6 +1186,28 @@
} }
} }
}, },
"@qg/ui-track-web": {
"version": "0.0.3",
"resolved": "http://npmprivate.quantgroups.com/@qg%2fui-track-web/-/ui-track-web-0.0.3.tgz",
"integrity": "sha512-eVo3fXcp3eRGG5YznmfZQa8ih03nJtmNJYCS9VCt3Q8x/mpK3iaqk7Be6+ntUUeCfVSre7uq7fXGwUEfj1ieUw==",
"requires": {
"@qg/js-bridge": "^1.1.11",
"sa-sdk-javascript": "^1.18.2",
"vue": "^2.6.14"
},
"dependencies": {
"sa-sdk-javascript": {
"version": "1.18.17",
"resolved": "http://npmprivate.quantgroups.com/sa-sdk-javascript/-/sa-sdk-javascript-1.18.17.tgz",
"integrity": "sha1-mLW7PyfL2jmwcCTb3G/Bes6H0n8="
},
"vue": {
"version": "2.6.14",
"resolved": "http://npmprivate.quantgroups.com/vue/-/vue-2.6.14.tgz",
"integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
}
}
},
"@qg/webpack-cos-cdn-upload-plugin": { "@qg/webpack-cos-cdn-upload-plugin": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "http://npmprivate.quantgroups.com/@qg%2fwebpack-cos-cdn-upload-plugin/-/webpack-cos-cdn-upload-plugin-0.0.4.tgz", "resolved": "http://npmprivate.quantgroups.com/@qg%2fwebpack-cos-cdn-upload-plugin/-/webpack-cos-cdn-upload-plugin-0.0.4.tgz",
...@@ -3991,6 +4013,11 @@ ...@@ -3991,6 +4013,11 @@
"randomfill": "^1.0.3" "randomfill": "^1.0.3"
} }
}, },
"crypto-js": {
"version": "3.3.0",
"resolved": "https://registry.nlark.com/crypto-js/download/crypto-js-3.3.0.tgz",
"integrity": "sha1-hG3RzOL2iqz6FWyFePkmpgm3l2s="
},
"css": { "css": {
"version": "2.2.4", "version": "2.2.4",
"resolved": "http://npmprivate.quantgroups.com/css/-/css-2.2.4.tgz", "resolved": "http://npmprivate.quantgroups.com/css/-/css-2.2.4.tgz",
......
import config from '@/config';
import http from '@/service/httpDecorator';
import { uiTrack } from '@/service/sa.service';
const { talosHost } = config;
const PAGE = 'home';
export const getBanner = () => {
return http.get(`${talosHost}/api/kdsp/appconfig/${PAGE}/topinfo`);
};
//获取动态内容【金刚区、运营专区等】【金刚区、运营专区-横向,运营专区-竖向, 通栏】
export const getContent = () => {
//页面:home-精选,discovery-发现,profile-我的
return http.get(`${talosHost}/api/kdsp/appconfig/${PAGE}/content`);
};
export const getGoodsList = async data => {
const scDeviceId = await uiTrack.run('saDeviceId');
return http.get(`${talosHost}/api/kdsp/recommend/goods-list`, {
params: data,
customHeader: {
scDeviceId
}
});
};
export const homeSearch = async data => {
const scDeviceId = await uiTrack.run('saDeviceId');
return http.post(`${talosHost}/api/kdsp/search/result`, data, {
customHeader: {
scDeviceId
}
});
};
// 搜索--大家都在搜的关键词
export const getTerms = data => {
return http.get(`${talosHost}/vcc/app/mall/search/page`, data);
};
// 查询用户优惠券选择查询接口
export const getCouponChooseList = data => {
return http.post(`${talosHost}/api/kdsp/coupon/activity/choose-list`, data);
};
import request from '@/service/httpDecorator'; import request from '@/service/httpDecorator';
import config from '@/config'; import config from '@/config';
import uiTrack from '@/service/sa.service'; import { uiTrack } from '@/service/sa.service';
const { talosHost } = config; const { talosHost } = config;
const queryPayInfo = function(data) { const queryPayInfo = function(data) {
......
import http from '@/service/httpDecorator';
import config from '@/config';
const { talosHost, faceHost } = config;
export default {
// 获取信用钱包用户信息
getXhkInfo() {
return http.get(`${talosHost}/api/kdsp/profile/vcc/user-account`);
},
getUserInfo() {
return http.get(`${talosHost}/api/kdsp/user/about`);
}
};
// 发送短信
export const smsCode = param => {
const data = { ...param, smsMerchant: 'XIN_YONG_QIAN_BAO' };
return http.post(`${talosHost}/vcc/usercenter/send_vcc_sms_code`, data);
};
// KA流程节点
export const kaGetNextUrl = params => {
let options = {
sonVccChannel: true
};
return http.get(`${talosHost}/api/kdsp/ka/process/get-next-url`, { params }, options);
};
// 验证短信并登录
export const friendLogin = data => {
return http.post(`${talosHost}/vcc/usercenter/fast_login_by_sms_code`, data);
};
// 验证短信类型
export const captchaType = data => {
return http.post(`${talosHost}/vcc/usercenter/captcha_type`, data);
};
// 获取图形验证
export const captcha = () => {
return http.get(`${talosHost}/vcc/usercenter/qg_captcha`);
};
export const getFaceUrl = params => {
return http.get(`${faceHost}/auth-center/ex/face/h5/auth_url.json`, { params });
};
<template>
<div class="goods">
<template v-for="item in data">
<div v-if="item.goods" :key="item.goodsId" :class="['card']" @click="toDetail(item.goods)">
<div class="card__top">
<div class="card__head">
<img class="card__head__image" object-fit="contain" :src="item.goods.goodsImage" />
</div>
<p class="card__name">
{{ 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: {
type: Array,
default: () => []
}
},
methods: {
toDetail(item) {
const params = {
count: 1,
goodsName: item.goodsName,
goodsimg: item.goodsImage
};
const query = {
skuNo: item.goodsId
};
this.$router.push({ name: 'goodDetail', params, query });
}
}
};
</script>
<style lang="less" scoped>
.goods {
height: 100%;
}
.card {
// width: 49%;
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;
-webkit-box-orient: vertical;
margin: 8px 5px 0px 14px;
.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: flex-start;
&__icon {
font-size: 12px;
color: #ec3333;
}
&__text {
font-size: 18px;
line-height: 25px;
color: #ec3333;
}
}
}
&::after {
clear: both;
}
}
</style>
<template>
<div class="reco">
<cr-divider v-show="list.length" :hairline="false">{{ desc }}</cr-divider>
<cr-list
v-model="loading"
:finished="finished"
finished-text="没有更多了"
offset="100"
@load="onLoad"
>
<div class="list">
<div class="left">
<Goods :data="leftList" />
</div>
<div class="right">
<Goods :data="rightList" />
</div>
</div>
</cr-list>
</div>
</template>
<script>
import { getGoodsList } from '@/api/pay.api.js';
import Goods from './Goods.vue';
export default {
components: {
Goods
},
props: {
desc: {
type: String,
default: '猜你喜欢'
},
pageSize: {
type: Number,
default: 20
}
},
data() {
return {
finished: false,
loading: false,
list: [],
pageNo: 1,
leftList: [],
rightList: []
};
},
mounted() {},
methods: {
async loadReco() {
this.loading = true;
try {
const [data] = await getGoodsList({
pageType: 6,
phoneNo: this.phoneNo,
pageSize: this.pageSize,
pageNo: this.pageNo
});
if (data) {
this.loading = false;
this.list = [...this.list, ...data.goodsList] || [];
this.leftList = this.list.filter((item, index) => {
if (index % 2 === 0) {
return item;
}
});
this.rightList = this.list.filter((item, index) => index % 2);
if (!data.hasNext) {
this.finished = true;
} else {
this.pageNo++;
}
} else {
this.loading = false;
this.finished = true;
}
} catch (error) {
this.loading = false;
this.finished = true;
}
},
onLoad() {
this.loadReco();
}
}
};
</script>
<style lang="less" scoped>
.reco {
width: 100%;
height: 100%;
.cr-divider {
margin: 0;
margin-bottom: 12px;
padding: 0 60px;
color: #999;
border: 0 solid #dcdcdc;
font-size: 12px;
}
& /deep/ .cr-list__finished-text {
display: none;
}
& /deep/ .cr-list__loading {
margin: 0 auto;
}
.cr-list {
box-sizing: border-box;
padding: 10px 0;
height: 100%;
.list {
height: 100%;
overflow: auto;
clear: both;
}
.left {
float: left;
width: 49%;
height: 100%;
display: flex;
flex-direction: column;
}
.right {
float: right;
width: 49%;
height: 100%;
display: flex;
flex-direction: column;
}
}
}
</style>
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
</div> </div>
</template> </template>
<script> <script>
import { getCouponChooseList } from '@/api/groupBuy'; import { getCouponChooseList } from '@/api/home.api';
import { TITLE_LIST } from '@/constants/order'; import { TITLE_LIST } from '@/constants/order';
import Empty from './empty'; import Empty from './empty';
export default { export default {
......
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
const protocol = window.location.protocol; const protocol = window.location.protocol;
const qiniuHost = protocol + '//kdspstatic.q-gp.com/'; const qiniuHost = protocol + '//kdspstatic.q-gp.com/';
const shenceHost = 'https://bn.xyqb.com/sa?project=default'; // 测试地址 const shenceHost = 'https://bn.xyqb.com/sa?project=default'; // 测试地址
const talosHost = 'https://talos-vcc2.liangkebang.net'; const talosHost = 'https://talos-tob.liangkebang.net';
const faceHost = 'https://auth-vcc2.liangkebang.net'; const faceHost = 'https://auth-tob.liangkebang.net';
const kdspHost = 'https://kdsp-api-vcc2.liangkebang.net'; const kdspHost = 'https://kdsp-api-tob.liangkebang.net';
const VCC_CHANNEL = ''; const VCC_CHANNEL = '';
const TERMINAL = 'H5'; const TERMINAL = 'H5';
const VERSION = '7.9.00'; const VERSION = '7.9.00';
......
...@@ -28,6 +28,43 @@ module.exports = [ ...@@ -28,6 +28,43 @@ module.exports = [
}, },
component: () => import('../views/order/createOrder') component: () => import('../views/order/createOrder')
}, },
{
path: '/pay',
name: 'pay',
meta: {
title: '收银台'
},
component: () => import('../views/pay')
},
{
path: '/payFail',
name: 'payFail',
alias: ['/payFail'],
meta: {
title: '支付失败'
},
component: () => import('../views/pay/payResult')
},
{
path: '/paySuccess',
name: 'paySuccess',
alias: ['/paySuccess'],
meta: {
title: '支付成功',
success: true
},
component: () => import('../views/pay/payResult')
},
{
path: '/payWaiting',
name: 'payWaiting',
meta: {
title: '支付中',
success: true
},
component: () => import('../views/pay/payWaiting')
},
{ {
path: '/address', path: '/address',
name: 'address', name: 'address',
......
...@@ -8,8 +8,8 @@ import { cipher as AES, util as UTIL, pki as PKI, md as SHA1 } from 'node-forge' ...@@ -8,8 +8,8 @@ import { cipher as AES, util as UTIL, pki as PKI, md as SHA1 } from 'node-forge'
import uuidv1 from 'uuid/v1'; import uuidv1 from 'uuid/v1';
import { parseTime } from './utils.service'; import { parseTime } from './utils.service';
import { APP_ID, PUBLIC_KEY, PRIVATE_KEY } from '@/config/encrypt.config'; import { APP_ID, PUBLIC_KEY, PRIVATE_KEY } from '@/config/encrypt.config';
// import CryptoJS from 'crypto-js'; import CryptoJS from 'crypto-js';
// import pay from '@/api/pay.api'; import pay from '@/api/pay.api';
/** /**
* @description: 数据加密 * @description: 数据加密
...@@ -38,16 +38,16 @@ export function encryption(data = '') { ...@@ -38,16 +38,16 @@ export function encryption(data = '') {
* @message {String} message 数据源 * @message {String} message 数据源
* @return {String} 加密后的数据16进制 * @return {String} 加密后的数据16进制
*/ */
// export async function encryptByDESModeEBC(message) { export async function encryptByDESModeEBC(message) {
// console.log(message); console.log(message);
// const [{ payPwdSalt }] = await pay.desSalt(); const [{ payPwdSalt }] = await pay.desSalt();
// var keyHex = CryptoJS.enc.Utf8.parse(payPwdSalt); var keyHex = CryptoJS.enc.Utf8.parse(payPwdSalt);
// var encrypted = CryptoJS.DES.encrypt(message, keyHex, { var encrypted = CryptoJS.DES.encrypt(message, keyHex, {
// mode: CryptoJS.mode.ECB, mode: CryptoJS.mode.ECB,
// padding: CryptoJS.pad.Pkcs7 padding: CryptoJS.pad.Pkcs7
// }); });
// return encrypted.ciphertext.toString().toLocaleUpperCase(); return encrypted.ciphertext.toString().toLocaleUpperCase();
// } }
/** /**
* @description: AES加密数据,默认CBC, Pki#cs7 * @description: AES加密数据,默认CBC, Pki#cs7
......
/* eslint-disable space-before-function-paren */
/* eslint-disable prettier/prettier */
/*
* @Description: 支付微信h5, jsapi, 第三方收银台,跳转)
* @Date: 2020-07-28 15:03:52
* @LastEditors: gzw
* @LastEditTime: 2021-08-11 10:33:50
*/
import qs from 'qs';
import cookies from '@/service/cookieStorage.service';
import localStorage from '@/service/localStorage.service';
/**
* payByWeixinJsapi
* @description: 微信通过jsapi支付
* 可以在微信浏览器调起支付
* @param {type}
* @return:
*/
function payByWeixinJsapi(info = {}, callback) {
function onBridgeReady() {
// eslint-disable-next-line no-undef
WeixinJSBridge.invoke('getBrandWCPayRequest', info, function (res) {
if (res.err_msg == 'get_brand_wcpay_request:ok') {
// 使用以上方式判断前端返回,微信团队郑重提示:
//res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
callback('ok');
} else if (res.err_msg == 'get_brand_wcpay_request:cancel') {
// 支付取消
callback('cancel');
} else {
// 支付失败
callback('fail');
}
});
}
if (typeof WeixinJSBridge == 'undefined') {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
}
/**
* payByWeixinH5
* @description: 微信通过H5支付
* 可以在浏览器调起微信支付
* 在羊小咩(信用钱包)app中调起微信支付,会先唤起微信,原来的页面会自动跳转到redirect_url设定的地址
* @param {type}
* @return:
*/
function payByWeixinH5(info) {
if (!info.url) return;
if (!info.params) {
info.params = {};
}
const vccToken = localStorage.get('vccToken');
const orderNo = cookies.get('orderNo')?.orderNo;
info.params.isWxH5 = 1;
info.params.vccToken = vccToken;
info.params.orderNo = orderNo;
info.params.isWx = true;
const currentPath = encodeURIComponent(
window.location.origin +
// eslint-disable-next-line prettier/prettier
'/payWaiting' +
qs.stringify(info.params, { encode: true, addQueryPrefix: true })
);
try {
const nextPage = document.createElement('a');
nextPage.setAttribute('href',);
nextPage.click();
} catch (error) {
window.location.href = `${info.url}&redirect_url=${currentPath}`;
}
}
/**
* payByALIH5
* @description: 支付宝支付
* @param {type}
* @return:
*/
function payByALIH5(info) {
if (!info.url) return;
const aliWrap = document.createElement('div');
aliWrap.id = 'ALIWEB_WRAP';
aliWrap.setAttribute('id', 'ALIWEB_WRAP');
aliWrap.innerHTML = info.url;
document.body.appendChild(aliWrap);
document.forms[0].submit();
setTimeout(() => {
const ALIWEB_WRAP = document.getElementById('ALIWEB_WRAP');
if (ALIWEB_WRAP != null) ALIWEB_WRAP.parentNode.removeChild(ALIWEB_WRAP);
}, 500);
}
/**
* payByThirdPartyCashier
* @description: 第三方收银台
* @param {type}
* @return:
*/
function payByThirdPartyCashier(info) {
if (!info.url) return;
info.params.third = 1;
const currentPath = encodeURIComponent(
window.location.origin +
'/payWaiting' +
qs.stringify(info.params, { encode: true, addQueryPrefix: true })
);
const nextPage = document.createElement('a');
nextPage.setAttribute('href', `${info.url}&redirect_uri=${currentPath}`);
nextPage.click();
window.location.href = `${info.url}&redirect_uri=${currentPath}`;
}
/**
* @description: 支付方式判断, 返回promise
* NATIVE=原生扫码支付.,APP=ap支付,,JSAPI=公众号支付/小程序支付,,MWEB=H5支付.,MICROPAY=刷卡支付,默认JSAPI
* @param {String} type 支付方式,THIRD -> 第三方,MWEB -> H5支付, JSAPI -> jsapi支付,默认支付方式,THIRD
* @param {Object/String} payInfo 支付信息
* @return {Promise} 回调
*/
export function payByWay(type = 'THIRD', payInfo) {
return new Promise((resolve, reject) => {
if (type === 'JSAPI' && !payInfo.url) {
payByWeixinJsapi(payInfo, function (e) {
if (e === 'ok') {
resolve();
} else {
reject();
}
});
} else {
if (payInfo.url.indexOf('tenpay') > -1) {
payByWeixinH5(payInfo);
reject();
} else if (type === 'ALIWEB') {
payByALIH5(payInfo);
resolve();
} else {
payByThirdPartyCashier(payInfo);
reject();
}
// resolve();
}
});
}
import Vue from 'vue'; import Vue from 'vue';
import sa from 'sa-sdk-javascript'; import sa from 'sa-sdk-javascript';
import config from '@/config'; import config from '@/config';
// +==========================
import UiTrack from '@qg/ui-track-web/src/index.js';
import router from '@/router';
import { getVccChannel } from '@/service/userInfo.service';
export const uiTrack = new UiTrack({
globalVars: {
platformType: 'H5',
parent_channel_id: getVccChannel('vccChannel') || '',
son_channel_id: getVccChannel('sonVccChannel') || ''
},
useUweb: false,
shenceHost: config.shenceHost,
showLog: config.test,
router: router // router实例
});
// +============+============
export default { export default {
init: function(router) { init: function(router) {
sa.init({ sa.init({
......
import user from '@/api/user.api';
import store from '@/store';
import { getQueryString, parseSearch } from './utils.service';
import localStorage from './localStorage.service';
// eslint-disable-next-line space-before-function-paren
export const getUserInfo = async () => {
const [data] = await user.getUserInfo();
const { userInfo } = data || {};
store.commit('CHANGE_LOGIN', userInfo);
return userInfo;
};
export const getToKen = () => {
const vccToken = getQueryString('vccToken');
const oldToken = localStorage.get('vccToken') || '';
if (!oldToken) {
store.commit('CHANGE_TOKEN', vccToken);
}
};
export function getVccChannel(channelKey = 'vccChannel') {
if (channelKey === 'sonVccChannel') {
return localStorage.get(channelKey) || parseSearch(window.location.href)[channelKey] || '';
}
return localStorage.get(channelKey) || parseSearch(window.location.href)[channelKey] || '';
}
...@@ -266,6 +266,19 @@ export function onKeyboardStateChange(callback = () => {}, off = false) { ...@@ -266,6 +266,19 @@ export function onKeyboardStateChange(callback = () => {}, off = false) {
} }
} }
export function throttle(fn, wait) {
let flag = true;
return function() {
if (flag) {
fn.apply(this, arguments);
flag = false;
setTimeout(() => {
flag = true;
}, wait);
}
};
}
export function changeTitleOfIOS() { export function changeTitleOfIOS() {
if (isIOS) { if (isIOS) {
const hackIframe = document.createElement('iframe'); const hackIframe = document.createElement('iframe');
...@@ -315,3 +328,10 @@ export function idNoFormat(value) { ...@@ -315,3 +328,10 @@ export function idNoFormat(value) {
export function dateFormat(value) { export function dateFormat(value) {
return value.replace(/(\d{4})(\d{2})(\d{2})/, '$1.$2.$3'); return value.replace(/(\d{4})(\d{2})(\d{2})/, '$1.$2.$3');
} }
export function getQueryString(name) {
var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)');
var r = window.location.search.substr(1).match(reg);
if (r != null) return unescape(r[2]);
return null;
}
<template>
<dl class="group-item" :class="{ disabled: index === 5 }">
<dt class="group-item-thumb">
<cr-image :src="goodsItem.thumbImageUrl" />
<span class="group-item-tag">3人团</span>
</dt>
<dd class="group-item-content">
<div class="group-item-title">{{ goodsItem.goodsName }}</div>
<!-- 进度条:已瓶团消耗库村/该活动商品初始库存 -->
<cr-progress
class="group-item-progress"
stroke-width="5"
color="#EC1500"
track-color="#F7F8F9"
:show-pivot="false"
:percentage="handleProgressByStatus(goodsItem)"
/>
<div class="group-item-already">已拼2333团</div>
<div class="group-item-price">成团价 ¥ <sup class="group-item-sup">0</sup></div>
<div class="group-item-delprice">售价 ¥{{ goodsItem.activityPrice }}</div>
<!-- 立即开团 -->
<!-- 已抢光 -->
<!-- 未开始 -->
<!-- 已结束 -->
<cr-button shape="circle" type="primary" :disabled="false">立即开团</cr-button>
</dd>
</dl>
</template>
<script>
export default {
name: 'GoodsCard',
props: {
goodsItem: {
type: Object,
default: () => {}
},
index: {
type: Number
}
},
methods: {
handleProgressByStatus(item) {
if (!item) return 0;
let percentage = 0;
const { showSaleCount, goodsCount } = item;
percentage = (goodsCount / (goodsCount + showSaleCount)) * 100;
percentage = percentage < 0 ? 0 : percentage;
// console.log(percentage);
// percentage = !this.topicCfg.hasStart ? 0 : percentage;
return Math.round(percentage * 100) / 100;
// return 70;
}
}
};
</script>
<style scoped lang="less">
.group-item {
display: flex;
justify-content: center;
align-items: center;
padding: 12px 0;
border-bottom: #d8d8d8 solid 1px;
position: relative;
&:last-of-type {
border-bottom: none;
}
&-thumb {
box-sizing: border-box;
width: 108px;
height: 108px;
background-color: #f7f8f9;
border-radius: 6px;
overflow: hidden;
position: relative;
padding: 3px;
margin-right: 8px;
.cr-image {
width: 100%;
height: 100%;
}
}
&-content {
flex: 1;
height: 100%;
}
&-tag {
position: absolute;
top: 0;
left: 0;
font-size: @font-size-12;
color: @white;
width: 45px;
height: 18px;
line-height: 18px;
text-align: center;
background-image: linear-gradient(269deg, #ff5d00 12%, #ff1900 86%);
border-radius: 6px 0 6px 0;
}
&-title {
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 18px;
}
&-progress {
width: 163px;
overflow: hidden;
border-radius: 5px;
margin-top: 4px;
}
&-already {
font-size: @font-size-12;
color: #999999;
margin-top: 4px;
margin-bottom: 7px;
}
&-price {
font-size: @font-size-14;
color: #f70d0d;
margin-bottom: 4px;
font-weight: bold;
}
&-sup {
font-size: @font-size-18;
}
&-delprice {
color: #999999;
font-size: @font-size-12;
}
.cr-button {
width: 90px;
height: 28px;
line-height: 28px;
border-radius: 15px;
position: absolute;
bottom: 12px;
right: 0;
}
&.disabled {
opacity: 0.3;
.group-item-price {
color: #000;
}
}
}
</style>
...@@ -54,100 +54,7 @@ ...@@ -54,100 +54,7 @@
padding-right: 8px; padding-right: 8px;
} }
.group-item {
display: flex;
justify-content: center;
align-items: center;
padding: 12px 0;
border-bottom: #d8d8d8 solid 1px;
position: relative;
&:last-of-type {
border-bottom: none;
}
&-thumb {
box-sizing: border-box;
width: 108px;
height: 108px;
background-color: #f7f8f9;
border-radius: 6px;
overflow: hidden;
position: relative;
padding: 3px;
margin-right: 8px;
.cr-image {
width: 100%;
height: 100%;
}
}
&-content {
flex: 1;
height: 100%;
}
&-tag {
position: absolute;
top: 0;
left: 0;
font-size: @font-size-12;
color: @white;
width: 45px;
height: 18px;
line-height: 18px;
text-align: center;
background-image: linear-gradient(269deg, #ff5d00 12%, #ff1900 86%);
border-radius: 6px 0 6px 0;
}
&-title {
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 18px;
}
&-progress {
width: 163px;
overflow: hidden;
border-radius: 5px;
margin-top: 4px;
}
&-already {
font-size: @font-size-12;
color: #999999;
margin-top: 4px;
margin-bottom: 7px;
}
&-price {
font-size: @font-size-14;
color: #f70d0d;
margin-bottom: 4px;
font-weight: bold;
}
&-sup {
font-size: @font-size-18;
}
&-delprice {
color: #999999;
font-size: @font-size-12;
}
.cr-button {
width: 90px;
height: 28px;
line-height: 28px;
border-radius: 15px;
position: absolute;
bottom: 12px;
right: 0;
}
&.disabled {
opacity: 0.3;
.group-item-price {
color: #000;
}
}
}
.group-portrait { .group-portrait {
&-head { &-head {
......
...@@ -43,36 +43,12 @@ ...@@ -43,36 +43,12 @@
</div> </div>
</div> </div>
<cr-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad"> <cr-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad">
<dl <goods-card
v-for="(item, index) in goodsList" v-for="(item, index) in goodsList"
:key="index" :key="index"
:class="['group-item', index == goodsList.length - 1 ? 'disabled' : '']" :index="index"
> :goods-item="item"
<dt class="group-item-thumb"> />
<cr-image :src="item.thumbImageUrl" />
<span class="group-item-tag">3人团</span>
</dt>
<dd class="group-item-content">
<div class="group-item-title">{{ item.goodsName }}</div>
<!-- 进度条:已瓶团消耗库村/该活动商品初始库存 -->
<cr-progress
class="group-item-progress"
stroke-width="5"
color="#EC1500"
track-color="#F7F8F9"
:show-pivot="false"
:percentage="handleProgressByStatus(item)"
/>
<div class="group-item-already">已拼2333团</div>
<div class="group-item-price">拼团价 ¥ <sup class="group-item-sup">0</sup></div>
<del class="group-item-delprice">{{ item.activityPrice }} </del>
<!-- 立即开团 -->
<!-- 已抢光 -->
<!-- 未开始 -->
<!-- 已结束 -->
<cr-button shape="circle" type="primary" :disabled="false">立即开团</cr-button>
</dd>
</dl>
</cr-list> </cr-list>
</div> </div>
<!-- 下方列表展示信息 end --> <!-- 下方列表展示信息 end -->
...@@ -84,13 +60,14 @@ import groupBuyApi from '@/api/groupBuy'; ...@@ -84,13 +60,14 @@ import groupBuyApi from '@/api/groupBuy';
import countDown from '@/components/countDown'; import countDown from '@/components/countDown';
import swipeCustomerInfo from '@/components/swipeCustomerInfo'; import swipeCustomerInfo from '@/components/swipeCustomerInfo';
import gpImagePath from '@/assets/images/groupBuy/bg-top.png'; import gpImagePath from '@/assets/images/groupBuy/bg-top.png';
import goodsCard from './components/goodsCard';
export default { export default {
// beforeRouteEnter(to, from, next) { // beforeRouteEnter(to, from, next) {
// next(vm => {}); // next(vm => {});
// }, // },
// eslint-disable-next-line vue/name-property-casing // eslint-disable-next-line vue/name-property-casing
name: 'groupBuyList', name: 'groupBuyList',
components: { countDown, swipeCustomerInfo }, components: { countDown, swipeCustomerInfo, goodsCard },
data() { data() {
return { return {
animate: { animate: {
...@@ -113,7 +90,7 @@ export default { ...@@ -113,7 +90,7 @@ export default {
'https://activitystatic.lkbang.net/mall-free-ui/img/avator_06.png', 'https://activitystatic.lkbang.net/mall-free-ui/img/avator_06.png',
'https://activitystatic.lkbang.net/mall-free-ui/img/avator_07.png' 'https://activitystatic.lkbang.net/mall-free-ui/img/avator_07.png'
], ],
gpCountDown: new Date('2021-09-10 00:00:00').getTime(), gpCountDown: new Date('2021-09-12 00:00:00').getTime(),
goodsList: [], goodsList: [],
loading: false, loading: false,
...@@ -149,17 +126,7 @@ export default { ...@@ -149,17 +126,7 @@ export default {
this.getGoodsList(); this.getGoodsList();
}, },
handleProgressByStatus(item) {
if (!item) return 0;
let percentage = 0;
const { showSaleCount, goodsCount } = item;
percentage = (goodsCount / (goodsCount + showSaleCount)) * 100;
percentage = percentage < 0 ? 0 : percentage;
// console.log(percentage);
// percentage = !this.topicCfg.hasStart ? 0 : percentage;
return Math.round(percentage * 100) / 100;
// return 70;
},
async getGoodsList() { async getGoodsList() {
// const { pageSize, pageNo, activityId } = this; // const { pageSize, pageNo, activityId } = this;
// const { goodsSpecialId, templateDetailId } = this.topicCfg; // const { goodsSpecialId, templateDetailId } = this.topicCfg;
......
...@@ -97,7 +97,7 @@ export default { ...@@ -97,7 +97,7 @@ export default {
}, },
data() { data() {
return { return {
gpCountDown: new Date('2021-09-10 00:00:00').getTime(), gpCountDown: new Date('2021-09-12 00:00:00').getTime(),
listLoading: false, listLoading: false,
listFinished: false listFinished: false
......
const creditPayStatusType = {
1: 'pwd',
2: 'sms'
};
const payTypeText = ['享花卡支付', '微信支付'];
const status = ['未申请', '审核中', '审核失败', '已有额度未激活', '开户成功'];
const payTypeE = [
'PD_YXMMAEC_UserClickCashierSelectXiangHuaCardPay',
'PD_YXMMAEC_UserClickCashierSelectWechatPay'
];
const codeArr = ['4034', '4035', '4036', '3005'];
// (4034, "密码错误"),
// (4035, "密码重试超限,无法验证"),
// (4036, "验证码错误");
// (3005, "组合支付方式被锁定")
// (6049, "token过期")
const payStatus = [3, 5]; // 展示支付文案
const creditStatus = [1, 2]; //展示开通文案
// (1, "VCC未申请"),
// (2, "VCC审核中"),
// (3, "VCC审核失败"),
// (4, "VCC审核成功未开户(包含开户失败)"),
// (5, "已开户"),;
const ACCOUNT_NO_APPLY = 1;
const ACCOUNT_APPLY_AUDITING = 2;
const ACCOUNT_APPLY_AUDIT_FAIL = 3;
const ACCOUNT_APPLY_FAIL = 4;
const ACCOUNT_APPLY_SUCCESS = 5;
// 1信用支付 2微信支付 3支付宝支付 4信用支付_微信支付 5信用支付_支付宝支付 6虚拟支付0元
const CREDIT_PAY = 1;
const WECHAT_PAY = 2;
const ALIPAY_PAY = 3;
const CREDIT_AND_WECHAT_PAY = 4;
const CREDIT_AND_ALIPAY_PAY = 5;
const CASH_PAY = 6;
// 三方支付
const IS_THIRD_PAY = method => [WECHAT_PAY, ALIPAY_PAY, CASH_PAY].indexOf(method) > -1;
//组合支付
const IS_GROUP_PAY = method => [CREDIT_AND_WECHAT_PAY, CREDIT_AND_ALIPAY_PAY].indexOf(method) > -1;
//信用支付
const IS_CREDIT_PAY = method =>
[CREDIT_AND_WECHAT_PAY, CREDIT_AND_ALIPAY_PAY, CREDIT_PAY].indexOf(method) > -1;
const PAYMENT_CODE_PAY = 1; // 支付密码
const SMS_VERIFICATION_CODE_PAY = 2; //短信验证码
const FACE_VERIFICATION_CODE_PAY = 4; //人脸验证
const PAY_SUCCESS = 3; // 支付成功
const PAY_PAYING = 2; //支付中
const CASH_PAY_SUCCESS = 1; // 现金券支付成功
function isDetentionFn() {
// 推荐开通享花卡
if (this.creditPayInfo.accountStatus !== ACCOUNT_APPLY_SUCCESS) {
this.$dialog({
message: '您有一笔免费额度可以使用,40天免息等你拿',
confirmButtonText: '立即开通',
cancelButtonText: '继续支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.pay();
},
onConfirm: () => {
this.goApply();
}
});
} else if (
// 推荐享花卡支付
this.creditPayInfo.accountStatus === ACCOUNT_APPLY_SUCCESS &&
this.creditPayList.isGroupPay
) {
this.$dialog({
message: '使用组合支付部分金额可免息使用40天哦!',
confirmButtonText: '组合支付',
cancelButtonText: '继续支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.pay();
},
onConfirm: () => {
// 推荐组合支付
for (let key in this.creditPayList.payList) {
if (this.creditPayList.payList[key].isRecommend) {
const type = this.creditPayList.payList[key].payType;
this.changePayType(type, this.creditPayList.payList[key].mergePayPretreatmentInfo);
this.isCheckAgreement = true;
this.pay();
return;
}
}
}
});
} else if (
this.creditPayInfo.accountStatus === ACCOUNT_APPLY_SUCCESS &&
!this.creditPayList.isGroupPay
) {
this.$dialog({
message: '使用享花卡支付可免息使用40天哦',
confirmButtonText: '享花卡支付',
cancelButtonText: '继续支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.pay();
},
onConfirm: () => {
// 切换享花卡支付
this.changePayType(CREDIT_PAY);
this.isCheckAgreement = true;
this.pay();
}
});
}
}
function havePayingOrder() {
/* 有享花卡未支付的订单 */
this.$dialog({
message: '您的享花卡额度被其他订单占用,暂时不可使用享花卡支付哦!',
confirmButtonText: '重新选择',
showCancelButton: false,
confirmButtonColor: '#EC1500'
});
}
function filterAllPayList(type, data) {
for (let item in data.payList) {
if (data.payList[item].payType === type) {
data.payList[item].isCheck = true;
continue;
}
data.payList[item].isCheck = false;
}
return data;
}
export {
status,
codeArr,
CASH_PAY,
payTypeE,
payStatus,
PAY_PAYING,
CREDIT_PAY,
WECHAT_PAY,
ALIPAY_PAY,
PAY_SUCCESS,
payTypeText,
creditStatus,
IS_THIRD_PAY,
IS_GROUP_PAY,
IS_CREDIT_PAY,
isDetentionFn,
havePayingOrder,
filterAllPayList,
ACCOUNT_NO_APPLY,
PAYMENT_CODE_PAY,
CASH_PAY_SUCCESS,
ACCOUNT_APPLY_FAIL,
creditPayStatusType,
CREDIT_AND_WECHAT_PAY,
CREDIT_AND_ALIPAY_PAY,
ACCOUNT_APPLY_SUCCESS,
ACCOUNT_APPLY_AUDITING,
ACCOUNT_APPLY_AUDIT_FAIL,
SMS_VERIFICATION_CODE_PAY,
FACE_VERIFICATION_CODE_PAY
};
<template>
<div class="contract">
<cr-checkbox
v-model="isCheck"
shape="round"
class="contract-checkbox"
btn-size="mini"
@change="change"
/>
<p class="contract-list">
勾选同意
<span
v-for="contract in contractList"
:key="contract.contractName"
@click="goview(contract.contractVisitUrl)"
>
{{ contract.contractName }}</span
>
</p>
</div>
</template>
<script>
export default {
name: 'Contract',
props: {
contractList: Array,
value: Boolean
},
data() {
return {
isCheck: this.value
};
},
methods: {
goview(url) {
window.location.href = url;
},
change(e) {
this.$emit('input', e);
}
}
};
</script>
<style lang="less">
.contract {
width: 100%;
position: relative;
display: flex;
align-items: flex-start;
color: @gray-5;
.text-10;
&-list {
color: @gray-5;
span {
color: @red;
}
}
}
</style>
<template>
<div class="coupon">
<p class="coupon-face">
<span class="coupon-amount">{{ value.faceValue }}</span>
<span class="coupon-name">{{ value.name }}</span>
</p>
<p class="coupon-content">
<span class="coupon-desc">{{ value.description }}</span>
<span class="coupon-time">{{ value.couponValidTime }}</span>
</p>
<p class="coupon-action">
<cr-button size="mini" type="primary" shape="circle" class="coupon-button" @click="goNavUrl"
>去使用</cr-button
>
</p>
</div>
</template>
<script>
export default {
props: {
value: Object
},
methods: {
goNavUrl() {
this.$emit('click');
}
}
};
</script>
<style lang="less" scoped>
.coupon {
width: 327px;
height: 97px;
background-size: 100%;
background-image: url('../../../assets/images/pay/couponCard.png');
margin-right: @padding-sm;
padding: @padding-unit;
box-sizing: border-box;
&-face {
width: 90px;
color: @red-light;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
span {
display: block;
}
}
&-amount {
.text-30;
width: 100%;
display: flex;
flex-wrap: wrap;
word-break: break-all;
text-align: center;
font-family: PingFangSC-Medium;
&::before {
content: '¥';
.text-12;
}
}
&-name {
.text-12;
}
&-content {
width: 137px;
display: inline-flex;
flex-direction: column;
align-items: center;
justify-content: center;
span {
display: inline-block;
width: 100%;
}
}
&-desc {
.text-14;
color: @black;
overflow: hidden;
font-weight: bold;
display: -webkit-box !important;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
overflow: hidden;
}
&-time {
.text-12;
display: flex;
color: @gray-5;
word-break: break-all;
align-items: flex-start;
}
&-action {
width: 90px;
display: inline-flex;
align-items: center;
justify-content: center;
text-align: center;
}
&-button {
width: 64px;
}
}
</style>
<template>
<div class="pay-type-list">
<p class="type-title">{{ value.title }}</p>
<payGroupItem
v-if="value.isGroupPay"
v-model="payList"
:pay-type="payType"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
<template v-else>
<template v-for="(item, key) of payList">
<payCardItem
v-if="item.show"
:key="key"
:pay-type="payType"
:value="item"
:disabled="disabled"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:coupon-disabled="showCoupon && !payCouponCouldBeUsed"
/>
</template>
</template>
</div>
</template>
<script>
import payCardItem from './PayCardItem';
import payGroupItem from './PayGroupCard.vue';
export default {
name: 'PayCard',
components: {
payCardItem,
payGroupItem
},
provide() {
return {
payCard: this
};
},
props: {
value: Object,
payType: Number,
payText: String,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
payCouponCouldBeUsed: Boolean,
single: { type: Boolean, default: false },
creditPay: { type: Boolean, default: false }
},
data() {
return {
payList: this?.value?.payList || {}
};
},
mounted() {},
methods: {}
};
</script>
<style lang="less">
.pay-type-list {
background-color: #fff;
border-radius: @border-radius-sm;
margin-top: @padding-sm;
.type-title {
height: 48px;
.text-16;
font-weight: bold;
padding-left: @padding-md;
position: relative;
line-height: 48px;
}
}
</style>
<template>
<div class="payCardItem">
<div
:class="['type-item', { 'b-t': !isGroupPay || value.payType === 1 }]"
@click="changePayType(value)"
>
<div class="type-item-content">
<div class="type-item-content-info">
<cr-image :src="value.icon" class="icon" mode="aspectFit" width="39" height="39" />
<div class="content-info-con">
<div class="content-info-tit">
<div class="content-info-tit-wrap">
<p class="content-info-tit">{{ value.name }}</p>
<p v-if="value.tagName" class="content-info-tit_tag">{{ value.tagName }}</p>
</div>
<p v-if="isGroupPay && value.payAmt" class="content-info-amount">
{{ value.payAmt }}
</p>
</div>
<p>{{ value.accountStatusDesc }}</p>
</div>
</div>
<div v-if="showCoupon && value.payType === 1 && !value.riskLimitDesc" class="coupon">
<p class="couponDes" :class="{ disable: couponDisabled }" @click.stop="openCouponModal">
<span v-if="couponInfo.pickupId">
{{ `满${couponInfo.limitAmount}减${couponInfo.faceValue}元` }}
</span>
<span v-else>
未选择优惠券
</span>
<cr-icon type="arrow" class="selectArrow" size="15px" />
</p>
</div>
<p v-if="value.riskLimitDesc" class="limitDes">
{{ value.riskLimitDesc }}
</p>
</div>
<cr-checkbox
v-if="!isGroupPay"
v-model="value.isCheck"
shape="round"
class="type-item-checkbox"
checked-color="#EC1500"
:disabled="disabled || value.disabled"
@click.native="changePayType(value)"
/>
</div>
</div>
</template>
<script>
export default {
name: 'PayCardItem',
inject: ['payCard', 'pay'],
props: {
value: Object,
payType: Number,
disabled: Boolean,
couponInfo: Object,
riskLimit: Boolean,
showCoupon: Boolean,
isGroupPay: Boolean,
couponDisabled: Boolean
},
methods: {
changePayType({ payType, mergePayPretreatmentInfo }) {
if (this.disabled || this.value.disabled || this.isGroupPay) {
return;
}
this.$emit('click');
this.pay.changePayType(payType, mergePayPretreatmentInfo);
},
openCouponModal() {
if (this.couponDisabled) {
this.$toast('优惠券不可更改');
return;
}
this.pay.openCouponModal(this.pay.orderNo);
}
}
};
</script>
<style lang="less" scoped>
.payCardItem {
width: 100%;
padding-bottom: 2px;
}
.coupon {
margin-top: 4px;
height: 20px;
padding-left: 51px;
display: flex;
}
.couponDes {
display: inline-block;
color: @text-color-red;
.text-12;
border: @border-width-base solid @text-color-light;
border-radius: 3px;
padding: 0 0 0 3px;
&.disable {
color: @font-color-disabled;
border-color: @font-color-disabled;
}
text {
color: @text-color-red;
.text-12;
&.disable {
color: @font-color-disabled;
}
}
}
.limitDes {
color: #ed6a0c;
background: #fffbe8;
.text-11;
padding: 4px 8px;
margin: 8px 8px 8px 4px;
border-radius: 3px;
}
.type-item {
width: 100%;
padding: 12px 8px 12px 12px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&-content {
width: 100%;
}
&-content-info {
width: 100%;
display: flex;
align-items: center;
}
&-checkbox {
width: 18px;
}
}
.icon {
width: 39px;
height: 39px;
margin-right: @padding-sm;
}
.content-info-tit {
font-size: 15px;
color: @font-color-dark;
margin-bottom: 2px;
display: flex;
align-items: center;
justify-content: space-between;
&-wrap {
display: flex;
}
&_tag {
display: inline-flex;
align-items: center;
height: 16px;
border: @border-width-base solid @text-color-light;
border-radius: 3px;
font-size: 11px;
color: @text-color-light;
margin-left: @padding-unit;
padding: 0 2px;
}
}
.content-info-con {
flex: 1;
display: flex;
flex-direction: column;
.text-12;
color: @font-color-light;
}
.content-info-amount {
width: 100px;
text-align: left;
word-break: break-all;
&::before {
content: '¥';
}
.text-15;
color: @black;
}
.selectArrow {
vertical-align: text-top;
margin-left: -2px;
}
</style>
<template>
<div>
<div @click="changePayType(thirdPayInfo)">
<div class="groupCard">
<PayCardItem
:is-group-pay="true"
:value="creditPayInfo"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:risk-limit="riskLimit"
:coupon-disabled="couponDisabled"
/>
</div>
<div class="dashed">
<p class="dashed-line" />
<cr-image
src="../../../assets/images/pay/addicon.png"
width="24px"
height="24px"
class="dashed-icon"
/>
<cr-checkbox
v-model="thirdPayInfo.isCheck"
shape="round"
checked-color="#EC1500"
:disabled="disabled || thirdPayInfo.disabled"
class="dashed-checkbox"
@click.native="changePayType(thirdPayInfo)"
/>
</div>
<div class="groupCard">
<PayCardItem :is-group-pay="true" :value="thirdPayInfo" />
</div>
</div>
<p class="group-more b-t" @click="openMore">更多支付组合<cr-icon type="arrow" size="15px" /></p>
<cr-popup v-model="morePopup" round position="bottom" :style="{ height: '30%' }" closeable>
<p class="more-title">更换支付组合</p>
<PayCardItem
v-for="item in thirdPayList"
:key="item.payType"
:value="item"
:coupon-info="couponInfo"
:show-coupon="showCoupon"
:risk-limit="riskLimit"
@click="morePopup = false"
/>
</cr-popup>
</div>
</template>
<script>
import PayCardItem from './PayCardItem.vue';
export default {
name: 'PayGroupCard',
inject: ['payCard', 'pay'],
components: {
PayCardItem
},
props: {
value: Object,
payType: Number,
disabled: Boolean,
couponInfo: Object,
showCoupon: Boolean,
riskLimit: Boolean,
couponDisabled: Boolean
},
data() {
return {
morePopup: false,
thirdPayList: [],
creditPayInfo: this.value.creditPayInfo || {}
};
},
computed: {
thirdPayInfo: function() {
let temp = {};
for (const key in this.value) {
if (this.value[key].isRecommend || this.value[key].isCheck) {
const mergePayPretreatmentInfo = this.value[key]?.mergePayPretreatmentInfo || {};
temp = {
...this.value[key],
payAmt: mergePayPretreatmentInfo?.otherPayAmt
};
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
this.creditPayInfo.payAmt = mergePayPretreatmentInfo.creditPayAmt;
}
}
return temp;
}
},
mounted() {
for (const key in this.value) {
if (this.value[key].isGroupPay) {
this.thirdPayList.push(this.value[key]);
}
}
},
methods: {
changePayType({ payType, mergePayPretreatmentInfo }) {
if (this.disabled || this.creditPayInfo.disabled) {
return;
}
this.pay.changePayType(payType, mergePayPretreatmentInfo);
},
openMore() {
this.morePopup = true;
}
}
};
</script>
<style lang="less">
.groupCard {
width: 100%;
}
.group-more {
.text-13;
width: 335px;
height: 36px;
margin: auto;
display: flex;
color: #ec1500;
position: relative;
align-items: center;
justify-content: center;
}
.cr-popup--close-top-right {
z-index: 2;
}
.dashed {
width: 100%;
padding: 0 12px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
&-line {
width: 310px;
border-bottom: 1px dashed #dcdcdc;
}
&-checkbox {
width: 18px;
}
&-icon {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
}
.more-title {
.text-16;
display: flex;
justify-content: center;
align-items: center;
height: 48px;
color: @black;
position: relative;
}
</style>
<template>
<cr-overlay :show="value">
<div class="sms-modal">
<div class="sms">
<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="phone">已发送至 {{ getPhone() }}</p>
<cr-authcode-field
span-size="24px"
type="number"
border-type="bottom"
:number="6"
height="40px"
span-color="#666"
input-color="#666"
input-size="20px"
class="sms-input"
:success="success"
/>
</div>
<div slot="footer" class="sms-action">
<p v-if="errorInfo" class="sms-error">{{ errorInfo }}</p>
<p v-if="!send" class="sms-set" @click="sendSms()">获取验证码</p>
<p v-else class="sms-set grey">{{ time }}s后重新获取验证码</p>
</div>
</div>
</cr-overlay>
</template>
<script>
import localStorage from '@/service/localStorage.service';
import { sendSms as sendSmsApi } from '@/api/pay.api';
export default {
props: {
value: {
type: Boolean,
default: true
},
orderNo: String,
flowOrderNo: String,
errorInfo: String
},
data() {
return {
send: false,
time: 60,
timer: null
};
},
computed: {},
watch: {
value: function(val) {
if (val) {
this.sendSms(1);
}
},
errorInfo: function() {}
},
onShow() {},
onLoad() {},
onReady() {},
methods: {
closeModal() {
this.clearTimer();
this.$emit('close');
},
success(smsCode) {
this.$emit('submit', smsCode);
},
async sendSms() {
// 页面摧毁清掉定时器
if (this.send) return;
const [, error] = await sendSmsApi({ orderNo: this.orderNo, flowOrderNo: this.flowOrderNo });
if (!error) {
this.send = true;
this.timer = setInterval(() => {
--this.time;
if (!this.time) {
this.clearTimer();
}
}, 1000);
}
},
getPhone() {
const phone = localStorage.get('userInfo')?.phoneNumber;
return phone;
},
clearTimer() {
clearInterval(this.timer);
this.send = false;
this.time = 60;
}
}
};
</script>
<style lang="less">
.sms-modal {
box-sizing: border-box;
position: absolute;
left: 50%;
top: 26%;
margin-left: -150px;
padding: 12px 18px 24px;
width: 300px;
border-radius: 16px;
background: #fff;
}
.sms {
&-icon {
width: 100%;
height: 20px;
line-height: 20px;
text-align: right;
}
&-title {
text-align: center;
width: 100%;
font-size: 16px;
font-weight: bold;
}
&-des {
padding: 18px 0 0 8px;
display: flex;
justify-content: flex-start;
font-size: 12px;
color: #999999;
}
&-input {
margin: auto;
}
.phone {
padding-left: 8px;
margin-top: 8px;
color: #323233;
font-size: 14px;
}
&-sms {
margin-top: 4px;
font-size: 30px;
color: #333333;
&:before {
content: '¥';
.text-12;
}
}
&-input {
margin: 20px 0;
}
}
.sms-action {
width: 100%;
display: flex;
justify-content: space-between;
p {
font-size: 14px;
color: #666666;
}
.sms-error {
flex: 1;
color: #ee0a24;
}
.sms-set {
flex: 1;
text-align: right;
}
.grey {
color: #999999;
}
}
</style>
import cookies from '@/service/cookieStorage.service';
export const goUrlExtends = {
methods: {
goHome(payStatus) {
// 商城地址
if (payStatus) {
this.$track?.registeredEvents('h5_RechargeResultPageClick', {
pay_status: payStatus,
buttons_name: '返回首页'
});
}
setTimeout(() => {
this.$router.replace({ name: 'home' });
}, 500);
},
goOrderList(payStatus) {
if (payStatus) {
this.$track?.registeredEvents('h5_RechargeResultPageClick', {
pay_status: payStatus,
buttons_name: '查看订单'
});
}
setTimeout(() => {
this.$router.replace({ name: 'orderList', params: { state: 0 } });
}, 500);
},
goOrderDetail() {
const orderNo = cookies.get('orderNo')?.orderNo;
this.$router.push({
name: 'orderDetail',
query: { orderNo }
});
}
}
};
@import url('../../style/mixins');
.pay {
padding: @padding-xs;
}
.price-box {
background-color: @white;
height: 120px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.text-13;
color: @black;
border-radius: @border-radius-sm;
.price{
.text-30;
color: @font-color-red;
font-weight: bold;
margin-top: @padding-xs;
&:before{
content: '¥';
.text-14;
font-weight: bold;
}
}
.time{
display: flex;
align-items: center;
margin-top: @padding-unit;
}
}
.payBtn{
width: 100%;
position: fixed;
padding: @padding-xs @padding-lg;
box-sizing: border-box;
z-index: 99;
bottom: 0;
left:0;
background-color:@white;
.iphonex-fix-margin;
button{
.text-16;
width: 100%;
color: @white;
font-size: @line-height-sm;
border-radius: @border-radius-lx;
span{
font-weight: bold;
}
}
button[disabled] {
color: rgba(255,255,255,.6);
}
&-primary {
@primary-bg();
}
}
.btn{
height:50px;
width: 100%;
position: fixed;
padding: 0 @padding-lg;
box-sizing: border-box;
bottom: 0;
left:0;
background-color:@white;
display: flex;
align-items: center;
justify-content: center;
button{
flex:1;
color: @white;
font-size: @line-height-sm;
border-radius: @border-radius-lx;
}
button[disabled] {
color: rgba(255,255,255,.6);
}
&-primary {
@primary-bg();
}
&-default{
border: 1px solid @cherry-color-error;
color: @cherry-color-error !important;
margin-right: @padding-xs;
background-color: @white !important;
}
}
.placeholder{
width: 100%;
height: 100px;
}
\ No newline at end of file
This diff is collapsed.
<template>
<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 v-if="money" 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" type="default" @click="goHome(payStatus)">我的拼团</cr-button>
<cr-button
shape="circle"
class="actions__back"
:plain="true"
type="primary"
@click="goPage(1)"
>查看订单</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="goPage(0)"
>重新支付</cr-button
>
</div>
<div class="activity">
<cr-image src="@/assets/images/groupBuy/activity.png" />
</div>
<div v-if="isSuccess && couponList.length" class="coupon-box">
<p class="coupon-tip">本单已享以下权益</p>
<div class="coupon-wrap">
<CouponCard
v-for="coupon in couponList"
:key="coupon.id"
:value="coupon"
class="coupon-item"
@click="goViewCouponDetail"
/>
</div>
</div>
<Goods />
</div>
</template>
<script>
import cookies from '@/service/cookieStorage.service';
import { goUrlExtends } from './extends';
import CouponCard from './components/CouponCard.vue';
import { getCouponList } from '@/api/pay.api';
import Goods from '@/components/RecoGoods/RecoGoods.vue';
export default {
components: {
Goods,
CouponCard
},
extends: goUrlExtends,
data() {
return {
money: '00.00',
orderNo: null,
isSuccess: false,
freeAmount: 0,
payStatus: '',
couponList: [],
payStatusName: '',
payMethodName: '',
payTrackInfo: {},
skuID: ''
};
},
created() {
const { orderNo, reason } = this.$route.query;
const { success } = this.$route.meta;
const amount = cookies.get('amount') || {};
this.payTrackInfo = cookies.get('payTrackInfo') || {};
this.skuID = String(cookies.get('skuID')) || '';
this.money = amount.finalAmt;
this.orderNo = orderNo;
this.reason = reason || '';
this.isSuccess = success || false;
this.freeAmount = amount.freeAmount;
this.payStatus = success ? '订单支付成功' : '订单支付失败';
this.payStatusName = success ? '支付成功' : '支付失败';
this.$track?.registeredEvents('h5_RechargeResultPageExposure', {
pay_status: this.payStatus
});
this.$track?.registeredEvents('H5_2BPaymentPageExposure', {
order_no: orderNo,
pay_result: this.payStatus,
vcc_state: this.payTrackInfo.vcc_state,
pay_method: this.payTrackInfo.pay_method,
pay_amount: amount.finalAmt
});
this.getCouponList();
this.payMethod();
},
methods: {
payMethod() {
const name = this.payTrackInfo.pay_method;
switch (name) {
case 1:
this.payMethodName = '享花卡';
break;
case 2:
this.payMethodName = '微信';
break;
case 3:
this.payMethodName = '支付宝';
break;
default:
'';
}
},
// goPay() {
// this.$track?.registeredEvents('h5_RechargeResultPageClick', {
// commodity_id: this.skuID,
// order_number: this.orderNo,
// pay_method: this.payMethodName,
// pay_status: this.payStatusName,
// buttons_name: '重新支付'
// });
// this.$router.replace({ name: 'pay', query: { orderNo: this.orderNo } });
// },
goPage(type) {
const name = type ? '查看订单' : '重新支付';
this.$track?.registeredEvents('H5_2B_PaymentResultPageBtnClick', {
commodity_id: this.skuID,
order_number: this.orderNo,
pay_method: this.payMethodName,
pay_status: this.payStatusName,
buttons_name: name
});
if (type == 1) {
this.$router.replace({ name: 'orderList', params: { state: 0 } });
} else {
this.$router.replace({ name: 'pay', query: { orderNo: this.orderNo } });
}
},
async getCouponList() {
const [data] = await getCouponList({ orderNo: this.orderNo, paySuccess: true });
console.log(data);
if (data) {
this.couponList = data.couponList || [];
}
},
goViewCouponDetail() {
this.$router.push('/');
}
}
};
</script>
<style lang="less" scoped>
.card {
margin: @padding-sm;
background-color: @white;
border-radius: @border-radius-sm;
padding: 28px @padding-sm @padding-sm @padding-sm;
.info {
display: flex;
justify-content: center;
align-items: center;
&__image {
width: 100px;
height: 100px;
}
&__text {
.text-16;
margin-left: @padding-md;
}
&__desc {
display: flex;
flex-direction: column;
}
&__money {
margin-top: @padding-xs;
}
&__free {
.text-13;
margin-top: @padding-xs;
color: @font-color-light;
}
}
.tips {
.text-12;
text-align: center;
margin-top: @padding-lg;
color: @font-color-light;
}
.actions {
display: flex;
flex-direction: row;
margin-top: @padding-lg;
justify-content: space-between;
&__back {
color: @cherry-color-error;
border: 1px solid @cherry-color-error;
}
&__order {
@primary-bg();
margin-left: @padding-sm;
}
button {
// flex: 1;
.text-16;
width: 161px;
}
}
.info_button {
width: 100%;
}
}
.activity {
margin: @padding-sm;
height: 64px;
}
.coupon-box {
box-sizing: border-box;
margin: @padding-sm;
background-color: @white;
border-radius: @border-radius-sm;
padding: @padding-sm;
height: 150px;
overflow: hidden;
}
.coupon-tip {
.text-14;
font-family: PingFangSC-Medium;
.text-14;
color: #ff4735;
letter-spacing: 0;
margin-bottom: @padding-unit;
}
.coupon-wrap {
display: flex;
flex-direction: row;
height: 120px;
width: 327px;
padding: 0;
overflow: hidden;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.coupon-item {
display: inline-flex;
}
.goods {
background-color: #fff;
box-sizing: border-box;
// width: 100%;
margin: @padding-sm;
padding: @padding-sm;
}
</style>
<template>
<div class="card">
<div class="info">
<cr-image
width="63px"
height="63px"
class="info__image"
src="../../assets/images/pay/paying.png"
/>
<div v-if="init" class="info__desc">
<p class="info__text">
<span v-if="time">{{ time }}s)</span>支付中...
</p>
</div>
</div>
<p class="tips">
努力返回支付结果中,请留心查看!
</p>
<div class="actions">
<cr-button
class="actions__back"
:plain="true"
type="primary"
shape="circle"
@click="goHome('正在支付')"
>返回首页</cr-button
>
<cr-button shape="circle" type="primary" @click="goOrderDetail('正在支付')"
>查看订单</cr-button
>
</div>
</div>
</template>
<script>
import { queryPayStatus } from '@/api/pay.api';
import cookies from '@/service/cookieStorage.service';
import localStorage from '@/service/localStorage.service';
import { goUrlExtends } from './extends';
import { isIOS } from '@/service/validation.service';
export default {
components: {},
extends: goUrlExtends,
data() {
return {
orderNo: null,
timer: null,
time: 10,
init: false,
isWx: false
};
},
created() {
this.orderNo = this.$route.query.orderNo || cookies.get('orderNo')?.orderNo;
this.isWx = this.$route.query.isWx;
if (!localStorage.get('isAppInner')) {
const schema = isIOS ? 'xincheng://' : 'app://xincheng/splash';
setTimeout(() => {
window.location.href = schema;
}, 2000);
}
},
mounted() {
this.$track?.registeredEvents('h5_RechargeResultPageExposure', {
pay_status: '正在支付'
});
const amount = cookies.get('amount') || {};
const payTrackInfo = cookies.get('payTrackInfo') || {};
this.$track?.registeredEvents('H5_2BPaymentPageExposure', {
order_no: this.orderNo,
pay_result: '正在支付',
vcc_state: payTrackInfo.vcc_state,
pay_method: payTrackInfo.pay_method,
pay_amount: amount.finalAmt
});
this.$dialog({
message: '请确认订单已完成支付',
confirmButtonText: '已完成支付',
cancelButtonText: '重新支付',
confirmButtonColor: '#EC1500',
onCancel: () => {
this.$track?.registeredEvents('h5_RechargeResultPageClick', {
pay_status: '正在支付',
buttons_name: '重新支付'
});
this.goPay();
},
onConfirm: () => {
this.init = true;
this.loop();
}
});
},
beforeDestroy() {
clearInterval(this.timer);
},
methods: {
loop() {
this.query();
this.timer = setInterval(() => {
this.time -= 1;
if (this.time % 2 === 0) {
this.query();
}
if (this.time < 1) {
clearInterval(this.timer);
}
}, 2000);
},
goSuccess() {
// 支付成功
this.$router.push({ name: 'paySuccess' });
},
goPay() {
if (this.isWx) {
this.$router.go(-2);
return;
}
this.$router.go(-1);
},
async query() {
const [data, error] = await queryPayStatus({ orderNo: this.orderNo });
if (error) {
this.$router.push({
name: 'payFail',
query: { reason: error.message, orderNo: this.orderNo }
});
return;
}
if (+data?.payStatus === 3) {
this.goSuccess();
}
}
}
};
</script>
<style lang="less" scoped>
.card {
margin: @padding-sm;
background-color: @white;
border-radius: @border-radius-sm;
padding: 28px @padding-sm @padding-sm @padding-sm;
.info {
display: flex;
justify-content: center;
align-items: center;
&__image {
width: 100px;
height: 100px;
}
&__text {
.text-16;
margin-left: @padding-md;
}
&__desc {
display: flex;
flex-direction: column;
}
&__money {
margin-top: @padding-xs;
}
&__free {
.text-13;
margin-top: @padding-xs;
color: @font-color-light;
}
}
.tips {
.text-12;
text-align: center;
margin-top: @padding-lg;
color: @font-color-light;
}
.actions {
display: flex;
flex-direction: row;
margin-top: @padding-lg;
justify-content: space-between;
&__back {
color: @cherry-color-error;
border: 1px solid @cherry-color-error;
}
&__order {
@primary-bg();
margin-left: @padding-sm;
}
button {
.text-17;
width: 161px;
}
}
.info_button {
width: 100%;
}
}
</style>
<template>
<div class="container">
<div class="pay-back-title">
<p class="text">剩余待还(元)</p>
<p class="num">{{ amount }}</p>
</div>
<p class="contain">
包含以下部分
</p>
<div class="content">
<div class="item">
<span class="title">本金</span>
<span class="text"> {{ perTermAmount }}</span>
</div>
<div class="item" @click="handleServiceCharge">
<span class="title">综合服务费</span>
<span class="text">
{{ perTermFee }}
<i class="iconfont icon-arrow" />
</span>
</div>
<div v-if="overdueFee != '0.00'" class="item" @click="handleServiceCharge">
<span class="title">逾期</span>
<span class="text">
{{ overdueFee }}
</span>
</div>
</div>
<cr-popup v-model="isPop" class="pop" round closeable position="bottom">
<h2 class="pop-title">综合服务费详情</h2>
<div class="pop-item">
<span class="title">总计</span>
<span class="text">{{ perTermFee }}</span>
</div>
<div class="pop-content">
<p class="title">
费用说明
</p>
<p class="text">
综合服务费:包括依据用户与资金提供方以及相关担保方、保险方的约定,按期向资金提供方缴纳的利息、分期服务费等,以及向担保方缴纳的担保费、保险费等在内的全部资金成本。除还款本金以及该金额外用户无需就贷款事项支付其他任何费用。
</p>
</div>
</cr-popup>
</div>
</template>
<script>
export default {
components: {},
data() {
return {
isPop: false,
overdueFee: '',
overdueFeeDesc: '',
perTermAmount: '',
perTermFee: '',
perTermFeeDesc: '',
amount: ''
};
},
computed: {},
mounted() {
const {
overdueFee,
overdueFeeDesc,
perTermAmount,
perTermFee,
perTermFeeDesc,
amount
} = this.$route.params;
this.overdueFee = overdueFee;
this.overdueFeeDesc = overdueFeeDesc;
this.perTermAmount = perTermAmount;
this.perTermFee = perTermFee;
this.perTermFeeDesc = perTermFeeDesc;
this.amount = amount;
},
methods: {
handleServiceCharge() {
this.isPop = true;
}
}
};
</script>
<style lang="less" scoped>
.pop {
padding-bottom: 80px;
.pop-title {
font-size: 12px;
color: #333333;
text-align: center;
height: 40px;
line-height: 40px;
border-bottom: 1px solid #f0f2f5;
}
.pop-item {
margin-left: 20px;
display: flex;
border-bottom: 1px solid #f0f2f5;
justify-content: space-between;
height: 48px;
line-height: 48px;
padding-right: 20px;
span {
display: block;
font-size: 14px;
color: #999999;
}
.text {
color: #333333;
}
}
.pop-content {
margin-top: 20px;
font-size: 14px;
padding-left: 20px;
box-sizing: border-box;
color: #333333;
padding-right: 10px;
.title {
margin-bottom: 14px;
}
.text {
font-size: 13px;
line-height: 1.2;
}
}
}
.pay-back-title {
padding-top: 30px;
text-align: center;
padding-bottom: 51px;
.text {
font-size: 12px;
color: #333333;
margin-bottom: 10px;
}
.num {
font-size: 30px;
color: #333333;
}
}
.container {
min-height: 100%;
background: #fff;
}
.content {
padding: 0 20px;
box-sizing: border-box;
margin-bottom: 188px;
}
.contain {
font-size: 14px;
color: #999999;
line-height: 40px;
background: #f8f8f8;
padding-left: 20px;
}
.item {
display: flex;
border-bottom: 1px solid #f0f2f5;
justify-content: space-between;
height: 48px;
line-height: 48px;
span {
display: block;
font-size: 14px;
color: #999999;
}
.text {
color: #333333;
}
}
</style>
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