feat: save

This commit is contained in:
chenhao 2026-05-28 10:49:20 +08:00
parent 081f583f10
commit 239191fbe3
22 changed files with 416 additions and 212 deletions

View File

@ -13,6 +13,7 @@
<div id="app" v-cloak></div>
<script src="/js/echarts.min.js"></script>
<script type="module" src="/src/main.js"></script>
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<style>
[v-cloak] {
display: none;

BIN
public/img/no.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
public/img/pay_point.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -14,6 +14,59 @@ export default {
const routes = this.$router.getRoutes()
return routes.filter(r => r.meta.cache).map(r => r.name)
}
},
watch: {
$route(to, from) {
const title = to.meta.title || ''
this.updateTitle(title)
}
},
methods: {
updateTitle(title) {
document.title = title
if (this.$isWechat()) {
// iframe
const iframe = document.createElement('iframe')
iframe.style.display = 'none'
iframe.src = 'https://www.baidu.com/favicon.ico?' + Date.now()
iframe.onload = () => {
setTimeout(() => {
document.body.removeChild(iframe)
}, 0)
}
document.body.appendChild(iframe)
}
},
updateShare(title, desc, imgUrl) {
if (wx && this.$isWechat()) {
// this.$CommonAPI.Post('GetJSSDKConfig', location.href).then(data => {
let shareinfo = {
title: title || '泰古润' || this.wechatShareInfo.ShareTitle,
desc: desc || '汇聚平凡梦想,共创美好未来' || this.wechatShareInfo.ShareContent,
imgUrl: this.$file(imgUrl || this.wechatShareInfo.ShareLogo),
link: this.$getShareLink(),
};
if (this.$isWechatMini())
wx.miniProgram.postMessage({ data: { share: shareinfo } });
data.jsApiList.push('wx-open-launch-weapp');
data.openTagList = ['wx-open-launch-weapp'];
wx.config(data);
wx.ready(() => {
wx.updateAppMessageShareData(shareinfo);
wx.updateTimelineShareData(shareinfo);
if (location.hash == '#/Weapp') {
document.addEventListener('WeixinOpenTagsError', function (e) {
alert(e.detail.errMsg); // 使退使JS-SDK
});
}
});
wx.error((e) => {
if (location.hash == '#/Weapp')
alert(JSON.stringify(e));
});
// });
}
},
}
}
</script>

View File

@ -79,18 +79,6 @@ export default {
})
window.addEventListener('scroll', this.onWindowScroll)
},
beforeUnmount() {
this.observer?.disconnect()
window.removeEventListener('scroll', this.onWindowScroll)
if (this.cacheKey && this.list.length > 0) {
sessionStorage.setItem(this.cacheKey, JSON.stringify({
list: this.list,
page: this.page,
finished: this.finished,
params: this.params
}))
}
},
methods: {
onWindowScroll() {
if (this.finished || this.loading) return

View File

@ -55,17 +55,14 @@ const userStore = useUserStore()
// 全局前置守卫
router.beforeEach((to, from, next) => {
if (to.meta.title) {
document.title = to.meta.title;
}
if (to.meta.noLogin) {
return next();
}
const isLogin = userStore.isLogin;
if (!isLogin) {
return next({ name: 'Login', query: { redirect: to.fullPath } });
}
next();
if (to.meta.noLogin) {
return next();
}
const isLogin = userStore.isLogin;
if (!isLogin) {
return next({ name: 'Login', query: { redirect: to.fullPath } });
}
next();
});
app.config.globalProperties.$datadic = {
@ -336,7 +333,7 @@ app.config.globalProperties.$validIdCard = (idCard) => {
app.config.globalProperties.$getShareLink = () => {
const query = app.config.globalProperties.$route.query;
const recommend = localStorage.getItem('member_username');
const recommend = localStorage.getItem('cellphone');
let link = Object.keys(query).filter(x => x != 'RecommendCode').map(x => {
return `${x}=${query[x]}`;
}).join('&');

View File

@ -344,16 +344,3 @@ const routes = [
]
export { routes }
const router = createRouter({
history: createWebHashHistory(),
routes,
scrollBehavior(to, from, savedPosition) {
if (savedPosition) {
return savedPosition;
}
return { top: 0 };
},
})
export default router

View File

@ -902,6 +902,7 @@ img {
padding: 0 1.2vw;
border-radius: 1.07vw;
margin-right: 1.2vw;
background-color: #000;
}
.state1 {
@ -910,13 +911,16 @@ img {
.state3 {
background-color: #ea4747;
}
.state4 {
background-color: #229427;
}
.state5 {
background-color: #f9a225;
}
img {
width: 3.2vw;
height: 3.2vw;
@ -2138,6 +2142,16 @@ img {
color: #ea3e23;
}
.allpoint {
color: #333;
b {
color: #ea3e23;
font-size: 4vw;
margin-right: 1.2vw;
}
}
.oprice {
color: #b1b1b1;
margin-left: 3.87vw;
@ -2180,6 +2194,8 @@ img {
border: solid 0.27vw #e56f5d;
color: #e56f5d;
}
}
hr {
@ -2221,6 +2237,7 @@ img {
padding: 0 4vw;
font-size: 3.73vw;
color: #303030;
white-space: nowrap;
img {
width: 3.73vw;
@ -2463,6 +2480,18 @@ img {
padding-bottom: 3.33vw;
border-bottom: 1px #fff solid;
.allpointp {
margin-top: 2.4vw;
span {
font-size: 4vw;
font-weight: bold;
color: #ea3e23;
margin-right: 1.2vw;
}
}
.goods_price {
font-size: 4vw;
font-weight: bold;

View File

@ -15,8 +15,8 @@
<div class="lg_box">
<!-- 登录方式切换 tabs -->
<van-tabs color="#841e36" line-height="1.07vw" line-width="12vw" v-model:active="active" v-if="!isReg">
<van-tab title="账号登录"></van-tab>
<van-tab title="手机号登录"></van-tab>
<van-tab title="手机号快捷登录"></van-tab>
<van-tab title="账号+密码登录"></van-tab>
</van-tabs>
<!-- 表单区域 -->
@ -25,14 +25,14 @@
<div class="line">
<div class="title">
<img src="/img/login-i1.png" alt="">
<input type="tel" maxlength="11" :placeholder="active === 1 && !isReg ? '请输入手机号码作为账号' : '请输入手机号'"
<input type="tel" maxlength="11" :placeholder="active === 0 && !isReg ? '请输入手机号码作为账号' : '请输入手机号'"
v-model="formData.Phone" />
<van-icon v-if="formData.Phone" @click="formData.Phone = ''" name="cross" size="5vw"></van-icon>
</div>
</div>
<!-- 验证码 (手机号登录或注册时显示) -->
<div class="line" v-if="active === 1 || isReg">
<div class="line" v-if="active === 0 || isReg">
<div class="title">
<img src="/img/login-i2.png" alt="">
<input type="text" placeholder="请输入验证码" v-model="formData.SmsCode" maxlength="6" />
@ -43,7 +43,7 @@
</div>
<!-- 密码 (账号登录或注册时显示) -->
<div class="line" v-if="active === 0 || isReg">
<div class="line" v-if="active === 1 || isReg">
<div class="title">
<img src="/img/login-i3.png" alt="">
<input :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" v-model="formData.Password"
@ -228,7 +228,7 @@ export default {
}
},
handleLogin() {
if (this.active === 0) {
if (this.active === 1) {
//
if (!this.formData.Password) {
this.$showFailToast?.('请输入密码')
@ -352,7 +352,7 @@ export default {
this.init()
//
const params = new URLSearchParams(location.hash.split('?')[1] || '');
const inviteCode = params.get('invite');
const inviteCode = params.get('RecommendCode') || params.get('invite') || localStorage.getItem('recommend');
if (inviteCode) {
this.formData.Recommend = inviteCode;
localStorage.setItem('recommend', inviteCode);

View File

@ -6,12 +6,12 @@ export default {
mounted() {
const url = new URL(window.location.href)
const code = url.searchParams.get('code')
const inviteCode = url.searchParams.get('invite')
const inviteCode = url.searchParams.get('RecommendCode') || url.searchParams.get('invite') || localStorage.getItem('recommend')
if (code) {
sessionStorage.setItem('wx_callback_code', code)
// invite Login mounted
this.$router.replace('/Login' + (inviteCode ? `?invite=${inviteCode}` : ''))
this.$router.replace('/Login' + (inviteCode ? `?RecommendCode=${inviteCode}` : ''))
} else {
this.$router.replace('/')
}

View File

@ -60,7 +60,7 @@
<BaseList class="pro-list" ref="productList" :url="`/v1/client/EProsClient`"
:params="queryParams" @load="onLoad">
<template #default="{ item }">
<div class="a1" @click="$navigate(`/GoodsDetail?id=${item.id}`)">
<div class="a1" @click="$navigate('/GoodsDetail?id=' + item.id)">
<img :src="$file(item.img)" />
<div class="c">
<div class="name">{{ item.name }}</div>
@ -97,9 +97,6 @@ import BaseList from '../../components/BaseList.vue';
export default {
name: 'Category',
components: { BaseList },
mounted() {
this.init();
},
data() {
return {
showMainCategory: false,
@ -107,7 +104,6 @@ export default {
searchKey: '',
secondCategory: '',
goodsCategoryIndex: 0,
//
allCategories: [],
firstCategories: [],
secondCategories: [],
@ -115,7 +111,6 @@ export default {
selectedSecond: [],
selectedThird: [],
mainCategory: '',
//
currentThirdId: '',
searchParams: {
SortType: 0,
@ -124,6 +119,7 @@ export default {
queryParams: {
mallstate: 3,
pid: '',
name: '',
},
imageSources: [
"/img/pro-list-px-0.png",
@ -132,6 +128,18 @@ export default {
],
}
},
beforeRouteLeave(to, from, next) {
sessionStorage.setItem('category_scroll', window.scrollY)
next()
},
deactivated() {
sessionStorage.setItem('category_scroll', window.scrollY)
},
mounted() {
this.init()
},
activated() {
},
methods: {
async init() {
await this.loadCategories();
@ -143,6 +151,11 @@ export default {
this.searchParams.SortType = 2;
this.searchParams.IsDesc = true;
}
// URLname
if (this.$route.query.name) {
this.searchKey = this.$route.query.name;
this.queryParams.name = this.searchKey;
}
},
async loadCategories() {
try {
@ -234,6 +247,7 @@ export default {
this.refreshProductList();
},
onSearch() {
this.queryParams.name = this.searchKey;
this.refreshProductList();
},
onLoad() {

View File

@ -1,5 +1,5 @@
<template>
<BasePage :title="data.name || '产品详情'">
<BasePage :title="data.name || '产品详情'" :back="back">
<div class="goodsdetails">
<div class="t">
<img :src="$file(data.img)" alt="">
@ -17,24 +17,27 @@
</div>
<div class="price_box">
<div class="price">
<div class="price" v-if="data.mallstate !== 5">
¥{{ data.saleprice?.toFixed(2) }}
</div>
<div class="oprice">
<div class="oprice" v-if="data.mallstate !== 5">
¥{{ data.originalprice?.toFixed(2) }}
</div>
<div class="allpoint" v-if="data.mallstate === 5">
<b>{{ data.saleprice?.toFixed(2) || '0.00' }}</b>积分
</div>
<div class="sales r">
已售{{ data.salenums }}
</div>
</div>
<div class="deduct_box">
<div class="point">
<div class="point" v-if="data.deductjifen > 0">
<img src="/img/de-i1.png" alt="">
积分抵{{ data.deductjifen?.toFixed(2) || '0.00' }}
</div>
<div class="vip">
<div class="vip" v-if="data.deducthuiyuanka > 0">
<img src="/img/de-i2.png" alt="">
会员卡额度抵{{ data.deducthuiyuanka?.toFixed(2) || '0.00' }}
</div>
@ -58,6 +61,16 @@
</div>
</div>
<div class="spec_box" @click="showArea = true">
<img src="/img/spec.png" alt="">
<span>不支持发货区域</span>
<span
style="color: #999999;max-width: 43vw;width: 43vw;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
{{ formattedForbidArea }}
</span>
<van-icon class="r" name="arrow"></van-icon>
</div>
<div class="spec_box" @click="showSpecs = true">
<img src="/img/spec.png" alt="">
<span>{{ selectedSku }}</span>
@ -136,8 +149,11 @@
</div>
<div class="price">
<div class="goods_price">¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
<div class="o_price">¥{{ data.originalprice?.toFixed(2) || '0.00' }}</div>
<div class="allpointp" v-if="data.mallstate === 5">
<span>{{ data.saleprice?.toFixed(2) || '0.00' }}</span>积分
</div>
<div class="goods_price" v-else>¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
<div class="o_price" v-if="data.mallstate !== 5">¥{{ data.originalprice?.toFixed(2) || '0.00' }}</div>
</div>
<div class="bottom_box">
@ -155,6 +171,29 @@
</van-popup>
<van-popup v-model:show="showArea" safe-area-inset-bottom
style=" width: 86.67vw; background-color: #fff7f7; border-radius: 2.67vw;overflow: visible;" class="area_popup"
@click.self="showArea = false">
<div class="area_box" style="position: relative;">
<img style="width:31.73vw;position: absolute;top: -16vw;z-index: 1;left: 50%;transform: translateX(-50%);"
src="/img/no.png" alt="">
<div
style="font-size: 3.73vw;color: #333333;padding:18vw 3.33vw 3.33vw;display: flex;flex-direction: column;align-items: center;justify-content:center;border-bottom: 1px solid #f5f5f5;">
<span style="">
不支持发货区域
<span style="font-size: 3.73vw;;color: #ea3e23;">
{{ formattedForbidArea }}
</span>
</span>
</div>
<button @click="showArea = false"
style="height: 16vw;width:100%;border:none;background-color: transparent; font-size: 3.73vw;font-weight: bold;color:#333;">
好的知道了
</button>
</div>
</van-popup>
</div>
</BasePage>
@ -175,6 +214,8 @@ export default {
return {
data: {},
specSelect: [],
hide: true,
showArea: false,
show: false,
specCombinations: [],
tempSku: {},
@ -192,13 +233,26 @@ export default {
shareBoxHidden: false
}
},
computed: {
formattedForbidArea() {
if (!this.data.forbidbuyarea) return '暂无'
try {
//
const str = this.data.forbidbuyarea.replace(/\\"/g, '"')
const arr = JSON.parse(str)
return arr.map(s => s.split('-')[1] || s).join('、')
} catch {
return this.data.forbidbuyarea.replace(/\\"/g, '"')
}
}
},
methods: {
init() {
this.$get(`/v1/client/EProsClient/${this.$route.query.id}`).then(res => {
this.data = res.data;
this.specSelect = this.data.specSelect || [];
this.specCombinations = this.data.specCombinations || [];
const recommend = localStorage.getItem('member_username') || '';
const recommend = localStorage.getItem('cellphone') || '';
this.shareLink = `${location.origin}/#/GoodsDetail?id=${this.data.id}${recommend ? '&RecommendCode=' + recommend : ''}`;
this.$emit('updateShare', { title: this.data.name, imageUrl: this.data.img, link: this.shareLink });
this.initDefaultSku();
@ -209,11 +263,14 @@ export default {
if (mainSku) {
this.tempSku = { ...mainSku, Qty: 1 };
this.selectedSku = "已选:" + mainSku.skuname;
// skuname
this.specSelect.forEach(spec => {
const child = spec.children?.find(c => mainSku.skuname.includes(c.name));
if (child) {
this.selectedSpecs[spec.name] = child.name;
// skuname
const skuParts = mainSku.skuname.split('-').filter(Boolean);
this.specSelect.forEach((spec, specIndex) => {
if (skuParts[specIndex]) {
const child = spec.children?.find(c => skuParts[specIndex].includes(c.name));
if (child) {
this.selectedSpecs[spec.name] = child.name;
}
}
});
}
@ -227,8 +284,10 @@ export default {
if (specKeys.length === 0) return;
let matchedSku = this.specCombinations.find(combo => {
return specKeys.every(key => {
return combo.skuname.includes(this.selectedSpecs[key]);
const skuParts = combo.skuname.split('-').filter(Boolean);
return specKeys.every((key, specIndex) => {
const selectedVal = this.selectedSpecs[key];
return skuParts[specIndex]?.includes(selectedVal);
});
});
@ -237,6 +296,9 @@ export default {
this.selectedSku = "已选:" + matchedSku.skuname;
}
},
back() {
window.history.back();
},
onSelect(option) {
this.showShare = false;
if (option.name === "分享海报") {
@ -269,7 +331,7 @@ export default {
this.$showFailToast("该规格库存不足,无法购买");
return;
}
this.$navigate(`/TradeConfirm?id=${this.data.id}&buynums=${this.tempSku.Qty}`);
this.$navigate(`/TradeConfirm?id=${this.data.id}&skuid=${this.tempSku.skuid}&buynums=${this.tempSku.Qty}`);
},
beforeShare() {
this.showShare = true;

View File

@ -68,7 +68,7 @@
</div>
<div>
<b>{{ order.discount }}</b>
<span>利金额</span>
<span>利金额</span>
</div>
<div class="red">
<b>{{ order.income }}</b>

View File

@ -54,14 +54,14 @@
<p>¥{{ data.paymoney?.toFixed(2) }}</p>
</div>
<div class="detail" v-if="data.discountratio">
<span>商家利比例</span>
<span>商家利比例</span>
<p>{{ data.discountratio }}%</p>
</div>
<hr>
<div class="detail">
<span>商家利金额</span>
<span>商家利金额</span>
<p>-¥{{ data.discount }}</p>
</div>
<div class="detail">
@ -118,7 +118,7 @@
<p class="red">¥{{ data.paymoney?.toFixed(2) }}</p>
</div>
<div class="detail" v-if="data.discountratio">
<span>利比例</span>
<span>利比例</span>
<p>{{ data.discountratio }}%</p>
</div>
</div>

View File

@ -31,17 +31,6 @@ export default {
}
},
mounted() {
const cacheKey = `ColumnDetail_${this.id}`
const cached = sessionStorage.getItem(cacheKey)
if (cached) {
try {
this.article = JSON.parse(cached)
this.$nextTick(() => {
this.setupImagePreview();
});
return
} catch (e) {}
}
this.getDetail(this.id);
this.$nextTick(() => {
this.setupImagePreview();
@ -49,9 +38,16 @@ export default {
},
methods: {
getDetail(id) {
if (id === '1') {
this.$get('/v1/client/CNewscatesClient/1').then(res => {
this.article = res.data;
}).catch(err => {
this.$showFailToast(err.message);
});
return;
}
this.$get(`/v1/client/CNewsClient/${id}`).then(data => {
this.article = data.data;
sessionStorage.setItem(`ColumnDetail_${id}`, JSON.stringify(data.data))
}).catch(err => {
this.$showFailToast(err.message);
});

View File

@ -3,9 +3,9 @@
<div class="top">
<div class="search_box">
<img src="/img/search.png" alt="">
<input v-model="searchParams.Key" placeholder="请输入关键词搜索"
@search="$navigate('Category?Key=' + searchParams.Key)">
<button @click="$navigate('Category?Key=' + searchParams.Key)" :style="btnStyle">搜索</button>
<input v-model="searchParams.name" placeholder="请输入关键词搜索"
@search="$navigate('Category?name=' + searchParams.name)">
<button @click="$navigate('Category?name=' + searchParams.name)" :style="btnStyle">搜索</button>
</div>
<div class="banner">
<van-swipe :autoplay="3000" indicator-color="white" @change="onSwipeChange">
@ -138,12 +138,6 @@
<span class="title">
{{ item.name }}
</span>
<!-- <div class="zeng_box">
<span></span>
<div>
{{ item.gongxianzhi }}贡献值
</div>
</div> -->
<span class="oldprice">
{{ item.originalprice.toFixed(2) }}
</span>
@ -152,8 +146,6 @@
<span class="money"></span>
<span v-html="$toMoney(item.saleprice, 'b')"></span>
</div>
<!-- <img style="width:5.6vw;height: 5.07vw;margin-bottom: 0;" class="r" src="/img/cart.png" alt=""> -->
</div>
</div>
</div>
@ -206,7 +198,7 @@ export default {
searchParams: {
mallstate: 3,
pid: '',
Key: '',
name: '',
},
data: {
Carousel: [],
@ -215,17 +207,19 @@ export default {
hotData: [],
typeData: [],
bgcolor: '',
_fromDetail: false,
}
},
activated() {
this.$refs.BaseList?.refresh()
deactivated() {
sessionStorage.setItem('mall_scroll', window.scrollY)
},
mounted() {
this.init()
},
activated() {
this.$refs.BaseList?.refresh()
},
methods: {
init() {
Promise.all([
@ -265,7 +259,7 @@ export default {
},
onClear() {
this.searchParams.Key = ''
this.searchParams.name = ''
this.$refs.BaseList?.refresh()
},
},

View File

@ -17,58 +17,28 @@
<van-tabs style="max-width: 90%;" background="transparent" line-height="0.8vw" line-width="9.33vw;"
v-model:active="active" :ellipsis="false" color="#ca2904" title-active-color="#ca2904"
title-inactive-color="#4e4e4e">
<van-tab title="新品" name=""></van-tab>
<van-tab title="全部" name=""></van-tab>
<van-tab v-for="i in tabs" :name="i.id" :title="i.name"></van-tab>
</van-tabs>
<img @click="showLeft = true" class="filter_img" style="width: 5.4vw;" src="/img/filter.png" alt="">
<!-- <img @click="showLeft = true" class="filter_img" style="width: 5.4vw;" src="/img/filter.png" alt=""> -->
</div>
<!-- <BaseList url="/v1/client/EProsClient" class="goods_list" :params="{ mallstate: 2, pid: active }">
<BaseList url="/v1/client/EProsClient" class="goods_list" :params="{ mallstate: 5, pid: active }">
<template #default="{ item }">
<div class="goods_item" :key="item.id" @click="toDetail(item.id)">
<img :src="$file(item.img)" alt="">
<div>
<span class="title">{{ item.name }}</span>
<p>¥{{ item.originalprice }}</p>
<div class="money">
<div>
<span>¥
<b>{{ item.saleprice }}</b>.00
</span>
<b>{{ item.jifen?.toFixed(2) }}</b>积分
</div>
<img class="r" style="width: 5.6vw;" src="/img/cart.png" alt="">
</div>
</div>
</div>
</template>
</BaseList> -->
<div class="goods_list">
<div class="goods_item" v-for="item in goods" :key="item.id" @click="toDetail(item.id)">
<img :src="item.Icon" alt="">
<div>
<span class="title">{{ item.SubTitle }}</span>
<div class="point_box">
<img style="width: 3.33vw;height: 3.2vw;" src="/img/point.png" alt="">
<span style="font-size: 3.2vw; color: #d9a452;">积分可抵500.00</span>
</div>
<!-- <p>¥{{ item.OPrice }}</p> -->
<div class="money">
<div>
<span>¥</span>
<b>{{ item.Price }}</b>
</div>
<span class="oprice">
¥{{ item.OPrice }}
</span>
</div>
</div>
</div>
</div>
</BaseList>
</div>
@ -123,7 +93,7 @@ export default {
},
methods: {
getTab() {
this.$get('/v1/client/EProcatesClient/2').then(res => {
this.$get('/v1/client/EProcatesClient/5').then(res => {
this.tabs = res.data
})
},
@ -181,7 +151,7 @@ export default {
display: flex;
align-items: center;
justify-content: center;
padding: 0 5vw;
/* padding: 0 8vw 0 0; */
margin-top: 4vw;
position: relative;
@ -190,7 +160,6 @@ export default {
height: 5.4vw;
position: absolute;
right: 5vw;
}
}
}
@ -233,7 +202,7 @@ export default {
line-height: 4vw;
height: 8vw;
color: #333;
margin-bottom: 3.2vw;
margin-bottom: 2.93vw;
}
.point_box {
@ -263,24 +232,13 @@ export default {
display: flex;
justify-content: space-between;
align-items: center;
color: #ea3e23;
span {
color: #ca2904;
text-decoration: line-through;
}
color: #333;
font-size: 3.2vw;
b {
font-size: 4vw;
color: #ea3e23;
text-decoration: none !important;
}
.oprice {
font-size: 3.2vw;
color: #b1b1b1;
margin-right: 1.2vw;
}
}

View File

@ -2,16 +2,17 @@
<BasePage :back="back" title="订单支付">
<div class="pay">
<div class="amount">
<span></span>{{ orderInfo.needpay }}
<span></span>{{ orderInfo.needpay || orderInfo.payjifen }}
</div>
<div class="trade-code">
订单号{{ orderInfo.ordernum }}
</div>
<van-cell-group class="pay-method" inset>
<van-checkbox-group v-model="payMethod" disabled>
<van-checkbox-group v-model="payMethod">
<!-- 余额支付 -->
<van-cell class="balance-pay" title="余额支付" is-link @click="payMethod = ['balance']">
<van-cell class="balance-pay" title="余额支付" is-link @click="payMethod = ['balance']"
v-if="orderInfo.mallstate !== 5">
<template #icon>
<img class="pay-icon" src="/img/pay_balance.png" />
</template>
@ -24,7 +25,7 @@
</van-cell>
<!-- 支付宝支付 -->
<van-cell title="支付宝支付" is-link @click="payMethod = ['alipay']">
<van-cell title="支付宝支付" is-link @click="payMethod = ['alipay']" v-if="orderInfo.mallstate !== 5">
<template #icon>
<img class="pay-icon" src="/img/pay_alipay.png" />
</template>
@ -34,7 +35,7 @@
</van-cell>
<!-- 微信支付 -->
<van-cell title="微信支付" is-link @click="payMethod = ['wechat']">
<van-cell title="微信支付" is-link @click="payMethod = ['wechat']" v-if="orderInfo.mallstate !== 5">
<template #icon>
<img class="pay-icon" src="/img/pay_weixin.png" />
</template>
@ -43,7 +44,8 @@
</template>
</van-cell>
<van-cell v-if="!hide" title="快捷支付HJ(更顺畅)" is-link @click="payMethod = ['bankcard']">
<van-cell v-if="!hide && orderInfo.mallstate !== 5" title="快捷支付HJ(更顺畅)" is-link
@click="payMethod = ['bankcard']">
<template #icon>
<img class="pay-icon" src="/img/pay_bankcard.png" />
</template>
@ -51,6 +53,15 @@
<van-checkbox name="bankcard" :class="{ selected: payMethod.includes('bankcard') }" />
</template>
</van-cell>
<van-cell v-if="orderInfo.mallstate === 5" title="积分支付" is-link @click="payMethod = ['point']">
<template #icon>
<img class="pay-icon" src="/img/pay_point.png" />
</template>
<template #right-icon>
<van-checkbox name="point" :class="{ selected: payMethod.includes('point') }" />
</template>
</van-cell>
</van-checkbox-group>
<van-button type="primary" size="large" round="" style="width: 100%;margin-top: 6.67vw;"
@ -87,7 +98,7 @@ export default {
wallet: {
totalsum: 0,
},
payMethod: ['balance'],
payMethod: [],
showPayResult: false,
paychannelList: [],
}
@ -114,6 +125,13 @@ export default {
const orderRes = await this.$get(`/v1/client/FOrdersClient/${this.ordernum}`);
this.orderInfo = orderRes.data;
//
if (this.orderInfo.mallstate === 5) {
this.payMethod = ['point'];
} else {
this.payMethod = ['balance'];
}
//
const walletRes = await this.$get('/v1/client/DUsermoneysClient/statistics');
this.wallet = walletRes.data;
@ -128,7 +146,8 @@ export default {
balance: 4,
alipay: 2,
wechat: 1,
bankcard: 3
bankcard: 3,
point: 5,
};
const paychannel = channelMap[method];
const openid = localStorage.getItem('openid') || '';
@ -142,7 +161,7 @@ export default {
if (method === 'wechat') {
// 使 WeixinJSBridge
this.callWechatPay(res.data);
} else if (method === 'balance') {
} else if (method === 'balance' || method === 'point') {
//
this.$showSuccessToast(res.message || '支付成功');
setTimeout(() => {

View File

@ -26,7 +26,10 @@
<div>
<p>{{ product.typename }}订单</p>
<b class="r"><span></span>{{ product.saleprice?.toFixed(2) }}</b>
<b class="r" v-if="product.mallstate !== 5"><span></span>{{ product.saleprice?.toFixed(2)
}}</b>
<b class="r" v-else><span>{{ product.saleprice?.toFixed(2) }}</span>积分</b>
</div>
<div>
<p>规格{{ product.skuname }}</p>
@ -36,42 +39,45 @@
</div>
</div>
<div class="payway">
<div class="payway" v-if="product.mallstate !== 5">
<div class="deduction">
<div class="top">
<span>商品金额</span>
<b class="r"><span></span>{{ totalPrice }}</b>
</div>
<van-radio-group v-model="checked" checked-color="#ca2904">
<van-radio name="1" label-position="left">
<template #default>
<div class="way">
<div class="line">
<img src="/img/point_icon.png" alt="">
<span>使用积分抵扣</span>
</div>
<p>
(当前可用{{ (userInfo.xiaofeijifen || 0)?.toFixed(2) }}本次可抵{{
<div class="radio-group">
<div class="way" :class="{ active: checked === '1' }" @click="toggleRadio('1')">
<div class="radio-content">
<div class="line">
<img src="/img/point_icon.png" alt="">
<span>使用积分抵扣</span>
</div>
<p>
(当前可用{{ (userInfo.xiaofeijifen || 0)?.toFixed(2) }}本次可抵{{
actualJifenDeduct?.toFixed(2) }})
</p>
</p>
</div>
<div class="radio-icon">
<van-icon v-if="checked === '1'" name="success" />
</div>
</div>
<div class="way" :class="{ active: checked === '2' }" @click="toggleRadio('2')">
<div class="radio-content">
<div class="line">
<img src="/img/vip_icon.png" alt="">
<span>使用会员卡抵扣</span>
</div>
</template>
</van-radio>
<van-radio name="2" label-position="left">
<template #default>
<div class="way">
<div class="line">
<img src="/img/vip_icon.png" alt="">
<span>使用会员卡抵扣</span>
</div>
<p>
(当前可用{{ (userInfo.xiaofeiquan || 0)?.toFixed(2) }}本次可抵{{
<p>
(当前可用{{ (userInfo.xiaofeiquan || 0)?.toFixed(2) }}本次可抵{{
actualQuanDeduct?.toFixed(2) }})
</p>
</div>
</template>
</van-radio>
</van-radio-group>
</p>
</div>
<div class="radio-icon">
<van-icon v-if="checked === '2'" name="success" />
</div>
</div>
</div>
<div class="count">
<div v-if="freight > 0">
<span>运费</span>
@ -97,7 +103,7 @@
</div>
<van-submit-bar class="b_l_w" placeholder :price="actualPay * 100" :tip="`商品总数:${totalQuantity}件`"
tip-icon="info-o" safe-area-inset-bottom>
tip-icon="info-o" safe-area-inset-bottom :currency="product.mallstate === 5 ? '' : '¥'">
<template #button>
<van-button round color="rgb(227, 83, 33)" :loading="isloading" @click="submitOrder"
style="height: 10vw;">
@ -117,6 +123,7 @@ export default {
mounted() {
this.loadProduct();
this.loadUserInfo();
this.loadDefaultAddress();
},
data() {
return {
@ -130,6 +137,7 @@ export default {
isloading: false,
checked: '',
userInfo: {},
_lastChecked: '',
};
},
computed: {
@ -206,7 +214,8 @@ export default {
typename: data.typename || '商品',
expressprice: data.expressprice || 0,
deductjifen: data.deductjifen || 0,
deducthuiyuanka: data.deducthuiyuanka || 0
deducthuiyuanka: data.deducthuiyuanka || 0,
mallstate: data.mallstate || 0,
};
});
},
@ -215,9 +224,41 @@ export default {
this.userInfo = res.data;
}).catch(() => { });
},
loadDefaultAddress() {
this.$get('/v1/client/DUseraddressClient').then(res => {
const list = res.data || [];
const defaultAddr = list.find(item => item.defaulted);
if (defaultAddr) {
this.address = {
ReceiveName: defaultAddr.name,
ReceivePhone: defaultAddr.phone,
ReceiveRegionName: defaultAddr.province + defaultAddr.city + defaultAddr.county,
ReceiveAddress: defaultAddr.address,
province: defaultAddr.province,
city: defaultAddr.city,
county: defaultAddr.county,
};
}
}).catch(() => { });
},
onAddressConfirm(addr) {
this.address = addr;
},
toggleRadio(name) {
if (this.checked === name) {
this.checked = '';
} else {
this.checked = name;
}
},
onRadioChange(val) {
if (this._lastChecked === val) {
this.checked = '';
this._lastChecked = '';
} else {
this._lastChecked = val;
}
},
submitOrder() {
if (!this.address.ReceiveName) {
this.$showFailToast("请选择收货地址!");
@ -253,3 +294,58 @@ export default {
},
};
</script>
<style scoped>
.radio-group {
display: flex;
flex-direction: column;
gap: 12px;
}
.way {
display: flex;
align-items: flex-start;
padding: 2.53vw;
/* border: 1px solid #eee; */
border-radius: 8px;
flex-direction: row !important;
align-items: center;
}
/* .way.active {
border-color: #ca2904;
background: #fef8f7;
} */
.radio-icon {
width: 20px;
height: 20px;
border: 1px solid #ccc;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
color: #ca2904;
font-size: 4.2vw;
}
.way.active .radio-icon {
border-color: #ca2904;
}
.radio-content {
flex: 1;
}
.line {
display: flex;
align-items: center;
gap: 8px;
}
.line img {
width: 3.33vw;
height: 3.33vw;
}
</style>

View File

@ -39,7 +39,7 @@
</div>
<div class="concession">
<span>¥{{ item.proskusaleprice?.toFixed(2) }}</span>
<p v-if="item.discountratio">{{ item.discountratio }}%让利</p>
<p v-if="item.discountratio">惠利{{ item.discountratio }}%</p>
</div>
</div>
</div>
@ -74,7 +74,8 @@
<div class="logistics-item">
<span class="label">物流单号</span>
<span class="value">{{ currentLogistics?.exportnum }}</span>
<img src="/img/copy_b.png" @click="$copyText(currentLogistics?.exportnum); $showSuccessToast('复制成功')" alt="">
<img src="/img/copy_b.png" @click="$copyText(currentLogistics?.exportnum); $showSuccessToast('复制成功')"
alt="">
</div>
</div>
<div class="logistics-close">
@ -176,19 +177,19 @@ export default {
methods: {
//
// async loadFilterOptions() {
// try {
// const [stateRes, areaRes] = await Promise.all([
// this.$get('/v1/client/FOrdersClient/orderstate'),
// this.$get('/v1/client/EProsClient/mallstate'),
// ])
// this.tradeStates = stateRes.data || []
// this.mallList = [{ value: 0, label: '' }, ...(areaRes.data || [])]
// this._initializing = false
// } catch (err) {
// this.$showFailToast(err.message || '')
// }
// },
async loadFilterOptions() {
try {
const [stateRes, areaRes] = await Promise.all([
this.$get('/v1/client/FOrdersClient/orderstate'),
this.$get('/v1/client/EProsClient/mallstate'),
])
this.tradeStates = stateRes.data || []
this.mallList = [{ value: 0, label: '全部订单' }, ...(areaRes.data || [])]
this._initializing = false
} catch (err) {
this.$showFailToast(err.message || '加载失败')
}
},
// Tab -
onTabChange(name) {

View File

@ -47,7 +47,7 @@ export default {
this.data.NickName = userInfo.data.nickname || ''
}
const inviteCode = userInfo?.data?.cellphone || ''
this.link = `${location.origin}${location.pathname}#/Login?invite=${inviteCode}`
this.link = `${location.origin}${location.pathname}#/Login?RecommendCode=${inviteCode}`
} catch (err) {
console.error('获取用户信息失败:', err)
} finally {

View File

@ -13,7 +13,16 @@ export default defineConfig({
https: false,
},
plugins: [
vue(),
vue(
{
template: {
compilerOptions: {
// 忽略wx-open-launch 开头的组件,这些是微信的默认组件
isCustomElement: (tag) => tag.includes('wx-open-launch')
}
}
}
),
Components({
resolvers: [VantResolver()],
}),