feat: init push online
32
package-lock.json
generated
@ -9,6 +9,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@vant/area-data": "^2.1.0",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"image-conversion": "^2.1.1",
|
||||
"pinia": "^3.0.4",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
@ -629,6 +630,28 @@
|
||||
"integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@zxing/library": {
|
||||
"version": "0.21.3",
|
||||
"resolved": "https://registry.npmmirror.com/@zxing/library/-/library-0.21.3.tgz",
|
||||
"integrity": "sha512-hZHqFe2JyH/ZxviJZosZjV+2s6EDSY0O24R+FQmlWZBZXP9IqMo7S3nb3+2LBWxodJQkSurdQGnqE7KXqrYgow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ts-custom-error": "^3.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.4.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@zxing/text-encoding": "~0.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@zxing/text-encoding": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmmirror.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz",
|
||||
"integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==",
|
||||
"license": "(Unlicense OR Apache-2.0)",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.16.0.tgz",
|
||||
@ -1629,6 +1652,15 @@
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/ts-custom-error": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmmirror.com/ts-custom-error/-/ts-custom-error-3.3.1.tgz",
|
||||
"integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vant/area-data": "^2.1.0",
|
||||
"@zxing/library": "^0.21.3",
|
||||
"image-conversion": "^2.1.1",
|
||||
"pinia": "^3.0.4",
|
||||
"postcss-px-to-viewport-8-plugin": "^1.2.5",
|
||||
|
||||
|
Before Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 793 B |
|
Before Width: | Height: | Size: 876 B |
|
Before Width: | Height: | Size: 691 B |
|
Before Width: | Height: | Size: 304 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 419 B |
|
Before Width: | Height: | Size: 2.6 KiB |
BIN
public/img/logo-lr.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 208 KiB |
BIN
public/img/mallDetail-bg.jpg
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 277 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 912 B |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 413 B |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 579 B |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 611 B |
|
Before Width: | Height: | Size: 558 B |
|
Before Width: | Height: | Size: 949 B |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 259 B |
|
Before Width: | Height: | Size: 404 B |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 501 B |
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 365 B |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 5.2 KiB |
@ -16,9 +16,9 @@ const ERR_CODE = {
|
||||
SERVER_ERROR: 500,
|
||||
};
|
||||
|
||||
function getHeaders() {
|
||||
function getHeaders(contentType = 'application/json') {
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Type': contentType,
|
||||
};
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
@ -137,6 +137,26 @@ export const post = (url, data, timeout = DEFAULT_TIMEOUT) => request(url, data,
|
||||
export const put = (url, data, timeout = DEFAULT_TIMEOUT) => request(url, data, 'PUT', timeout);
|
||||
export const del = (url, data, timeout = DEFAULT_TIMEOUT) => request(url, data, 'DELETE', timeout);
|
||||
|
||||
/**
|
||||
* POST FormData 请求
|
||||
* @param {string} url - 请求路径
|
||||
* @param {FormData} formData - FormData 对象
|
||||
* @param {number} timeout - 超时时间 ms
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function postForm(url, formData, timeout = DEFAULT_TIMEOUT) {
|
||||
const headers = {};
|
||||
const token = localStorage.getItem('token');
|
||||
if (token) {
|
||||
headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
return fetchWithTimeout(BASE_URL + url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: formData,
|
||||
}, timeout).then(handleResponse).catch(handleError);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param {string} url - 请求路径
|
||||
@ -175,4 +195,4 @@ export function upload(url, files, timeout = 30000) {
|
||||
.catch(handleError);
|
||||
}
|
||||
|
||||
export default { get, post, put, del, upload, request };
|
||||
export default { get, post, put, del, upload, postForm, request };
|
||||
@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<div class="base-list">
|
||||
<div class="base-list" ref="listRef">
|
||||
<div v-if="loading && list.length === 0" class="loading-wrap">
|
||||
<van-loading size="24px">加载中...</van-loading>
|
||||
</div>
|
||||
<template v-for="(item, index) in list" :key="item.id">
|
||||
<slot :item="item" :index="index" />
|
||||
</template>
|
||||
<div v-if="loading && list.length > 0" class="loading-wrap">
|
||||
<van-loading size="24px">加载中...</van-loading>
|
||||
</div>
|
||||
<div v-if="finished && list.length > 0" class="finished-text">{{ finishedText }}</div>
|
||||
<div v-if="finished && list.length === 0 && !loading" class="empty-text">暂无数据</div>
|
||||
<div ref="sentinel" class="sentinel"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -29,6 +33,7 @@ export default {
|
||||
list: [],
|
||||
page: 1,
|
||||
requestId: 0,
|
||||
observer: null,
|
||||
}
|
||||
},
|
||||
emits: ['update:list', 'load', 'refresh'],
|
||||
@ -46,9 +51,32 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadMore()
|
||||
this.$nextTick(() => {
|
||||
this.observer = new IntersectionObserver((entries) => {
|
||||
if (entries[0].isIntersecting && !this.loading && !this.finished) {
|
||||
this.loadMore()
|
||||
}
|
||||
}, { rootMargin: '100px' })
|
||||
if (this.$refs.sentinel) {
|
||||
this.observer.observe(this.$refs.sentinel)
|
||||
}
|
||||
})
|
||||
window.addEventListener('scroll', this.onWindowScroll)
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.observer?.disconnect()
|
||||
window.removeEventListener('scroll', this.onWindowScroll)
|
||||
},
|
||||
methods: {
|
||||
onWindowScroll() {
|
||||
if (this.finished || this.loading) return
|
||||
const el = this.$refs.sentinel
|
||||
if (!el) return
|
||||
const rect = el.getBoundingClientRect()
|
||||
if (rect.top < window.innerHeight) {
|
||||
this.loadMore()
|
||||
}
|
||||
},
|
||||
loadMore() {
|
||||
if (this.loading) return
|
||||
const currentRequestId = this.requestId
|
||||
@ -58,7 +86,7 @@ export default {
|
||||
.then(res => {
|
||||
if (currentRequestId !== this.requestId) return
|
||||
const data = this.parseData(res)
|
||||
console.log(data);
|
||||
// console.log(data);
|
||||
const safeData = data || []
|
||||
|
||||
if (this.page === 1) {
|
||||
@ -86,7 +114,6 @@ export default {
|
||||
this._skipWatch = true
|
||||
this.loadMore()
|
||||
this.$emit('refresh', this.list)
|
||||
// 同步重置,不再用 $nextTick
|
||||
this._skipWatch = false
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<!-- $isWechat() ? '' : title || -->
|
||||
<van-nav-bar fixed :title="$route.meta.title" :safe-area-inset-top="true" placeholder left-arrow left-text="返回"
|
||||
<van-nav-bar fixed :title="title || $route.meta.title" :safe-area-inset-top="true" placeholder left-arrow left-text="返回"
|
||||
@click-left="back ? back() : $router.back();" :class="$isWechat() ? 'wechat-nav' : ''">
|
||||
<template #right>
|
||||
<slot v-if="$slots.right" name="right" />
|
||||
|
||||
18
src/main.js
@ -44,8 +44,22 @@ const router = createRouter({
|
||||
app.use(router);
|
||||
|
||||
import pinia from './stores';
|
||||
import { useDatadicStore, dictCache } from './stores/datadic';
|
||||
import { useUserStore } from './stores/user';
|
||||
app.use(pinia);
|
||||
|
||||
const datadicStore = useDatadicStore()
|
||||
datadicStore.init()
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
app.config.globalProperties.$datadic = {
|
||||
get: (code) => dictCache[code] || null,
|
||||
getContent: (code) => dictCache[code] ? dictCache[code].contents : '',
|
||||
}
|
||||
|
||||
app.config.globalProperties.$userInfo = userStore.getUserInfo;
|
||||
|
||||
app.config.globalProperties.$showDialog = showDialog;
|
||||
app.config.globalProperties.$showConfirmDialog = showConfirmDialog;
|
||||
app.config.globalProperties.$showNotify = showNotify;
|
||||
@ -152,6 +166,7 @@ app.config.globalProperties.$token = (e) => {
|
||||
};
|
||||
app.config.globalProperties.$file = (e) => {
|
||||
if (!e) return '/img/avatar.png';
|
||||
if (typeof e !== 'string') return '/img/avatar.png';
|
||||
if (e.startsWith('http://') || e.startsWith('https://'))
|
||||
return e;
|
||||
else
|
||||
@ -315,7 +330,7 @@ app.config.globalProperties.$getShareLink = () => {
|
||||
}
|
||||
|
||||
// CH
|
||||
import { post, get, put, del, request } from './api/http';
|
||||
import { post, get, put, del, request, postForm } from './api/http';
|
||||
import { isLogin, formatGMT } from './api/user';
|
||||
|
||||
app.config.globalProperties.$post = post;
|
||||
@ -323,6 +338,7 @@ app.config.globalProperties.$get = get;
|
||||
app.config.globalProperties.$put = put;
|
||||
app.config.globalProperties.$del = del;
|
||||
app.config.globalProperties.$request = request;
|
||||
app.config.globalProperties.$postForm = postForm;
|
||||
app.config.globalProperties.$isLogin = isLogin;
|
||||
app.config.globalProperties.$formatGMT = formatGMT;
|
||||
app.config.globalProperties.$formatCellphone = (e) => {
|
||||
|
||||
@ -205,7 +205,7 @@ const routes = [
|
||||
{
|
||||
path: '/GoodsDetail',
|
||||
name: 'GoodsDetail',
|
||||
component: () => import('./views/Goods/NGoodsDetail.vue'),
|
||||
component: () => import('./views/Goods/GoodsDetail.vue'),
|
||||
meta: { title: '商品详情' }
|
||||
},
|
||||
{
|
||||
@ -268,6 +268,12 @@ const routes = [
|
||||
component: () => import('./views/Merchant/MerchantIntroduction.vue'),
|
||||
meta: { title: '商家资料' }
|
||||
},
|
||||
{
|
||||
path: '/QrReader',
|
||||
name: 'QrReader',
|
||||
component: () => import('./views/Operations/QrReader.vue'),
|
||||
meta: { title: '扫一扫' }
|
||||
},
|
||||
{
|
||||
path: '/Checkout',
|
||||
name: 'Checkout',
|
||||
|
||||
38
src/stores/datadic.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { get } from '@/api/http'
|
||||
|
||||
// Module level cache for quick access
|
||||
export let dictCache = {}
|
||||
|
||||
export const useDatadicStore = defineStore('datadic', {
|
||||
state: () => ({
|
||||
dicts: {},
|
||||
loaded: false
|
||||
}),
|
||||
getters: {
|
||||
getByCode: (state) => (code) => {
|
||||
return dictCache[code] || null
|
||||
},
|
||||
getContent: (code) => {
|
||||
const item = dictCache[code]
|
||||
return item ? item.contents : ''
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async init() {
|
||||
if (this.loaded) return
|
||||
try {
|
||||
const res = await get('/v1/client/CDatadicsClient')
|
||||
if (res.status === 200 && res.data) {
|
||||
res.data.forEach(item => {
|
||||
dictCache[item.code] = item
|
||||
})
|
||||
this.dicts = dictCache
|
||||
this.loaded = true
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('数据字典加载失败', e)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -2,4 +2,7 @@ import { createPinia } from 'pinia';
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
export { useDatadicStore } from './datadic';
|
||||
export { useUserStore } from './user';
|
||||
|
||||
export default pinia;
|
||||
@ -1,18 +1,55 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { post } from '@/api/http';
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: () => ({
|
||||
user: null,
|
||||
token: localStorage.getItem('token') || '',
|
||||
// 用户基本信息
|
||||
user: null,
|
||||
cellphone: localStorage.getItem('cellphone') || '',
|
||||
nickname: localStorage.getItem('nickname') || '',
|
||||
userimg: localStorage.getItem('userimg') || '',
|
||||
// 扩展字段
|
||||
isshop: localStorage.getItem('isshop') || '',
|
||||
iscenter: localStorage.getItem('iscenter') || '',
|
||||
user_id: localStorage.getItem('user_id') || '',
|
||||
huiyuankaid: localStorage.getItem('huiyuankaid') || '',
|
||||
}),
|
||||
getters: {
|
||||
isLogin: (state) => !!state.token,
|
||||
getUser: (state) => state.user,
|
||||
getToken: (state) => state.token,
|
||||
getUser: (state) => state.user,
|
||||
getUserInfo: (state) => ({
|
||||
cellphone: state.cellphone,
|
||||
nickname: state.nickname,
|
||||
userimg: state.userimg,
|
||||
isshop: state.isshop,
|
||||
iscenter: state.iscenter,
|
||||
user_id: state.user_id,
|
||||
huiyuankaid: state.huiyuankaid,
|
||||
}),
|
||||
},
|
||||
actions: {
|
||||
setUser(user) {
|
||||
this.user = user;
|
||||
if (user) {
|
||||
// 同步到 localStorage 保持兼容
|
||||
this.cellphone = user.cellphone || '';
|
||||
this.nickname = user.nickname || '';
|
||||
this.userimg = user.userimg || '';
|
||||
this.isshop = user.isshop || '';
|
||||
this.iscenter = user.col2 || '';
|
||||
this.user_id = user.id || '';
|
||||
this.huiyuankaid = user.huiyuankaid || '';
|
||||
|
||||
localStorage.setItem('cellphone', this.cellphone);
|
||||
localStorage.setItem('nickname', this.nickname);
|
||||
localStorage.setItem('userimg', this.userimg);
|
||||
localStorage.setItem('isshop', this.isshop);
|
||||
localStorage.setItem('iscenter', this.iscenter);
|
||||
localStorage.setItem('user_id', this.user_id);
|
||||
localStorage.setItem('huiyuankaid', this.huiyuankaid);
|
||||
}
|
||||
},
|
||||
setToken(token) {
|
||||
this.token = token;
|
||||
@ -22,11 +59,36 @@ export const useUserStore = defineStore('user', {
|
||||
localStorage.removeItem('token');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 登录后获取用户信息
|
||||
*/
|
||||
async fetchUserInfo() {
|
||||
try {
|
||||
const res = await post('/v1/client/UserClient/info');
|
||||
if (res.status === 200 && res.data) {
|
||||
this.setUser(res.data);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('fetchUserInfo failed:', err);
|
||||
}
|
||||
},
|
||||
clearUser() {
|
||||
this.user = null;
|
||||
this.token = '';
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('member_username');
|
||||
this.cellphone = '';
|
||||
this.nickname = '';
|
||||
this.userimg = '';
|
||||
this.isshop = '';
|
||||
this.iscenter = '';
|
||||
this.user_id = '';
|
||||
this.huiyuankaid = '';
|
||||
|
||||
// 清除 localStorage
|
||||
const keys = [
|
||||
'token', 'cellphone', 'nickname', 'userimg',
|
||||
'isshop', 'iscenter', 'user_id', 'huiyuankaid', 'member_username'
|
||||
];
|
||||
keys.forEach(key => localStorage.removeItem(key));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -3115,12 +3115,14 @@
|
||||
|
||||
.line1 {
|
||||
.type {
|
||||
.bs;
|
||||
// .bs;
|
||||
.box;
|
||||
.box-center-center;
|
||||
width: 10vw;
|
||||
color: #000000;
|
||||
// width: 10vw;
|
||||
height: 4.8vw;
|
||||
background-color: #222222;
|
||||
padding: 0 1.2vw;
|
||||
background-color: #ffc6d4;
|
||||
border-radius: 1.333vw;
|
||||
margin-right: 1.2vw;
|
||||
}
|
||||
@ -3737,11 +3739,13 @@
|
||||
width: 86.67vw;
|
||||
margin-top: -1.33vw;
|
||||
padding: 7.8vw 3.33vw 0;
|
||||
white-space: nowrap;
|
||||
|
||||
.detail {
|
||||
.box;
|
||||
.box-align-center;
|
||||
margin-bottom: 3.07vw;
|
||||
white-space: nowrap;
|
||||
|
||||
span {
|
||||
color: #000000;
|
||||
@ -5110,6 +5114,47 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.vanupup {
|
||||
background-color: #ffffff;
|
||||
border-radius: 1.6vw;
|
||||
padding: 8.93vw 10vw;
|
||||
|
||||
.code_box {
|
||||
.box;
|
||||
.box-tb;
|
||||
|
||||
.top {
|
||||
.box;
|
||||
.box-center-center;
|
||||
margin-bottom: 6.93vw;
|
||||
font-size: 4.27vw;
|
||||
color: #333333;
|
||||
position: relative;
|
||||
|
||||
.van-icon {
|
||||
position: absolute;
|
||||
right: 0vw;
|
||||
}
|
||||
}
|
||||
|
||||
.ewm_box {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
padding: 5.2vw 4.53vw;
|
||||
background-color: #ffffff;
|
||||
border-radius: 1.6vw;
|
||||
border: solid 0.53vw #4c4c4c;
|
||||
|
||||
img {
|
||||
width: 57.6vw;
|
||||
height: 57.33vw;
|
||||
margin-bottom: 4vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.opera {
|
||||
@ -5364,6 +5409,10 @@
|
||||
.f5;
|
||||
padding: 7.6vw 4vw;
|
||||
|
||||
.van-radio {
|
||||
margin-left: 1.2vw;
|
||||
}
|
||||
|
||||
.shopinfo {
|
||||
.box;
|
||||
.box-align-center;
|
||||
@ -5448,6 +5497,11 @@
|
||||
padding: 4vw 3.07vw;
|
||||
margin-top: 4vw;
|
||||
|
||||
.van-cell {
|
||||
padding: 2.67vw 0;
|
||||
align-items: center
|
||||
}
|
||||
|
||||
.price {
|
||||
.box;
|
||||
.box-align-center;
|
||||
|
||||
@ -1434,8 +1434,8 @@ img {
|
||||
}
|
||||
|
||||
.top {
|
||||
height: 30.4vw;
|
||||
padding: 19.067vw 0 0 2vw;
|
||||
height: 20.4vw;
|
||||
padding: 10.067vw 0 0 2vw;
|
||||
.box;
|
||||
|
||||
img {
|
||||
@ -2288,4 +2288,124 @@ img {
|
||||
border: 1px solid#ca2904;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.shareimgbox {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
padding: 6.8vw 9.2vw;
|
||||
width: 80vw;
|
||||
min-width: 80vw;
|
||||
max-width: 80vw;
|
||||
max-height: 90vh;
|
||||
overflow-y: auto;
|
||||
background-color: #ffffff;
|
||||
border-radius: 2.67vw;
|
||||
|
||||
.share_box {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 10.53vw;
|
||||
margin-bottom: 4vw;
|
||||
}
|
||||
|
||||
.share_userinfo {
|
||||
.box;
|
||||
.box-align-center;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
height: 9.33vw;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.name {
|
||||
.box;
|
||||
.box-center-center;
|
||||
color: #222222;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
background-color: #fde5eb;
|
||||
border-radius: 3.33vw;
|
||||
// padding: .667vw 0;
|
||||
// padding: 1.2vw 4vw 6vw;
|
||||
padding-left: 6vw;
|
||||
padding-right: 4vw;
|
||||
margin-left: -3.33vw;
|
||||
height: 8vw;
|
||||
text-align: center;
|
||||
|
||||
span {
|
||||
line-height: 8vw;
|
||||
margin-bottom: 2.4vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.goods_info {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
margin-top: 4vw;
|
||||
|
||||
.goods_img {
|
||||
width: 61.47vw;
|
||||
height: 61.47vw;
|
||||
background-color: #ffffff;
|
||||
border-radius: 2.67vw;
|
||||
border: solid 0.27vw #841e36;
|
||||
}
|
||||
|
||||
.goods_name {
|
||||
width: 61.47vw;
|
||||
margin-top: 3.33vw;
|
||||
.text-hide(2);
|
||||
line-height: 4vw;
|
||||
height: 8vw;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.goods_price {
|
||||
margin-top: 1.33vw;
|
||||
}
|
||||
}
|
||||
|
||||
.qrcode {
|
||||
.box;
|
||||
.box-tb;
|
||||
.box-align-center;
|
||||
margin-top: 3.33vw;
|
||||
padding-bottom: 3vw;
|
||||
|
||||
img {
|
||||
width: 17.07vw;
|
||||
height: 17.07vw;
|
||||
margin-bottom: 1.2vw;
|
||||
}
|
||||
}
|
||||
|
||||
.share_result {
|
||||
margin-top: 4vw;
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
border-radius: 2.67vw;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-top: 2vw;
|
||||
color: #999;
|
||||
font-size: 3.2vw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -118,19 +118,13 @@
|
||||
<!-- 用户协议弹窗 -->
|
||||
<van-action-sheet v-model:show="showContract" safe-area-inset-bottom title="用户协议" closeable
|
||||
style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||||
<div class="w100 html">
|
||||
<p>用户协议内容...</p>
|
||||
<p>这里是用户协议的详细条款...</p>
|
||||
</div>
|
||||
<div class="w100 html" v-html="$datadic.getContent('code_yhxy')"></div>
|
||||
</van-action-sheet>
|
||||
|
||||
<!-- 隐私政策弹窗 -->
|
||||
<van-action-sheet v-model:show="showPolicy" safe-area-inset-bottom title="隐私政策" closeable
|
||||
style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||||
<div class="w100 html">
|
||||
<p>隐私政策内容...</p>
|
||||
<p>这里是隐私政策的详细条款...</p>
|
||||
</div>
|
||||
<div class="w100 html" v-html="$datadic.getContent('code_yszc')"></div>
|
||||
</van-action-sheet>
|
||||
</template>
|
||||
|
||||
@ -247,6 +241,7 @@ export default {
|
||||
}
|
||||
const userStore = useUserStore()
|
||||
userStore.setToken(data.data.token)
|
||||
userStore.fetchUserInfo()
|
||||
this.$showSuccessToast?.(data.message)
|
||||
location.replace('#/My')
|
||||
}).catch(err => {
|
||||
@ -273,6 +268,7 @@ export default {
|
||||
}
|
||||
const userStore = useUserStore()
|
||||
userStore.setToken(data.data.token)
|
||||
userStore.fetchUserInfo()
|
||||
this.$showSuccessToast?.(data.message)
|
||||
location.replace('#/My')
|
||||
}).catch(err => {
|
||||
|
||||
@ -229,20 +229,23 @@ export default {
|
||||
|
||||
this.saving = true;
|
||||
const params = {
|
||||
realname: this.tempData.realname,
|
||||
certno: this.tempData.idnumber,
|
||||
bankid: this.tempData.bankid || 0,
|
||||
bankname: this.tempData.bankname || '',
|
||||
bankcardnumber: this.tempData.bankcardnumber,
|
||||
bankcellphone: this.tempData.bankcellphone,
|
||||
sort: 0,
|
||||
certtype: '身份证',
|
||||
certvaliditytype: this.checked === 1 ? 1 : 0,
|
||||
certbegindate: this.tempData.certbegindate ? this.tempData.certbegindate.replaceAll('-', '') : '',
|
||||
certenddate: this.checked === 1 ? '' : (this.tempData.certenddate ? this.tempData.certenddate.replaceAll('-', '') : ''),
|
||||
cardtype: 1,
|
||||
provid: this.tempData.provid || '',
|
||||
areaid: this.tempData.areaid || ''
|
||||
"realname": this.tempData.realname,
|
||||
"idnumber": this.tempData.idnumber,
|
||||
"bankid": this.tempData.bankid || 0,
|
||||
"bankname": this.tempData.bankname || '',
|
||||
"bankcardnumber": this.tempData.bankcardnumber,
|
||||
"bankcellphone": this.tempData.bankcellphone,
|
||||
"sort": 0,
|
||||
"certtype": '00',
|
||||
"certvaliditytype": this.checked === 1 ? "1" : "0",
|
||||
"certbegindate": this.tempData.certbegindate ? this.tempData.certbegindate.replaceAll('-', '') : '',
|
||||
"certenddate": this.checked === 1 ? '' : (this.tempData.certenddate ? this.tempData.certenddate.replaceAll('-', '') : ''),
|
||||
"cardtype": "1",
|
||||
"provid": this.tempData.provid || '',
|
||||
"areaid": this.tempData.areaid || '',
|
||||
"isshopreceive": Boolean(this.$ls.get('isshop'))
|
||||
// "isshopreceive": false
|
||||
|
||||
};
|
||||
|
||||
this.$post('/v1/client/DUserbankcardsClient', params).then(res => {
|
||||
|
||||
@ -66,17 +66,17 @@
|
||||
<div class="name">{{ item.name }}</div>
|
||||
<div class="box box-pack-between">
|
||||
<s>¥{{ item.originalprice?.toFixed(2) }}</s>
|
||||
<div class="zeng_box">
|
||||
<!-- <div class="zeng_box">
|
||||
<span>赠</span>
|
||||
<div>
|
||||
{{ item.gongxianzhi }}贡献值
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div class="price">
|
||||
<b><span>¥</span>{{ item.saleprice?.toFixed(2) }}</b>
|
||||
<span class="r" style="color: #8a8a8a;font-size: 3.2vw;">销量{{ item.salenums
|
||||
}}</span>
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -138,6 +138,11 @@ export default {
|
||||
if (this.$route.query.id) {
|
||||
this.changeMainById(Number(this.$route.query.id));
|
||||
}
|
||||
// 如果URL带有SortType=2(从热销入口进入),默认以销量排序
|
||||
if (this.$route.query.SortType == 2) {
|
||||
this.searchParams.SortType = 2;
|
||||
this.searchParams.IsDesc = true;
|
||||
}
|
||||
},
|
||||
async loadCategories() {
|
||||
try {
|
||||
|
||||
@ -1,351 +1,286 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<div class="GoodsDetail">
|
||||
<BasePage>
|
||||
<div class="goodsdetails">
|
||||
<div class="t">
|
||||
<img :src="$file(data.img)" alt="">
|
||||
</div>
|
||||
|
||||
<!-- 海报 -->
|
||||
<div
|
||||
style="position: fixed;left: 0;top: 0; z-index: 0;width: 0px;height: 0px;opacity: 0; overflow: hidden;">
|
||||
<div id="bsCard"
|
||||
style="width: 260px; margin: 0 auto; overflow: hidden; border-radius: 10px;height: 450px;">
|
||||
<div class="b_l_w"
|
||||
style="background-color: #f2f1f7;height: max-content;padding-bottom: 15px;font-family: 'PingFang SC';">
|
||||
<div class="b_l_w" style="padding: 10px 20px">
|
||||
<img :src="$file(data.img)" style="width: 100%;height: 220px;" />
|
||||
</div>
|
||||
<div class="b_l"
|
||||
style="background: #daa366;margin-left: 10px;width: 240px;padding: 5px 10px;border-radius: 10px;">
|
||||
<div class="b_l">
|
||||
<img :src="$file($ls.get('userimg'))" width="40" height="40"
|
||||
style="border-radius: 100%; vertical-align: top" />
|
||||
</div>
|
||||
<div class="b_l"
|
||||
style=" line-height: 40px;padding-left: 5px;font-size: 14px;font-weight: bold;color: #fff;">
|
||||
{{ $ls.get('nickname')?.length > 8 ? $ls.get('nickname').substring(0, 6)
|
||||
+ "..." : $ls.get('nickname') }}
|
||||
友情推荐
|
||||
</div>
|
||||
</div>
|
||||
<div class="b_l_w" style="padding: 5px 10px">
|
||||
<div class="b_l"
|
||||
style="width: 120px;font-size: 12px;overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 2;line-clamp: 2;">
|
||||
{{ data.name }}
|
||||
</div>
|
||||
<div class="b_l" style="height: 30px;width: 1px;background: #d3d3d3;margin: 5px 7px;"></div>
|
||||
<div class="b_r" style="width: 95px; line-height: 40px; font-size: 12px">
|
||||
价格 <span style="color: #fd3c2b">¥{{ data.saleprice }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="b_l_w" style="padding: 0 10px">
|
||||
<div class="b_r" style="margin-top: 0px; margin-right: 10px">
|
||||
<img :src="$file($ls.get('shareLogo'))" width="100" />
|
||||
</div>
|
||||
<div class="b_l"
|
||||
style="width: 78px;text-align: center;padding-top: 5px;height: 95px;font-size: 12px;">
|
||||
<vue-qr :text="shareLink" backgroundColor="rgb(255,255,255,0)" style="width: 100%;"
|
||||
colorLight="rgb(255,255,255,0)"></vue-qr>
|
||||
长按扫码购买
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="goodsshow">
|
||||
<div class="t">
|
||||
<div class="tit">
|
||||
{{ data.name }}
|
||||
</div>
|
||||
<div class="desc" v-if="data.description">
|
||||
{{ data.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="price_box">
|
||||
<div class="price">
|
||||
¥{{ data.saleprice?.toFixed(2) }}
|
||||
</div>
|
||||
<div class="oprice">
|
||||
¥{{ data.originalprice?.toFixed(2) }}
|
||||
</div>
|
||||
<div class="sales r">
|
||||
已售{{ data.salenums }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="deduct_box">
|
||||
<div class="point">
|
||||
<img src="/img/de-i1.png" alt="">
|
||||
积分抵¥{{ data.deductjifen?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
|
||||
<van-popup v-model:show="showShareImg" style="width: 66.667vw;border-radius: 3.333vw;">
|
||||
<img class="w" :src="ShareImg">
|
||||
</van-popup>
|
||||
|
||||
<van-swipe class="banner" :autoplay="3000" indicator-color="white">
|
||||
<van-swipe-item v-for="item in bannerImages"><img class="w" :src="$file(item)"></van-swipe-item>
|
||||
<template #indicator="{ active, total }">
|
||||
<div class="custom-indicator">{{ active + 1 }}/{{ total }}</div>
|
||||
</template>
|
||||
</van-swipe>
|
||||
|
||||
<div class="mx-auto">
|
||||
<div class="top">
|
||||
<div class="c">
|
||||
<div class="name">
|
||||
{{ data.name }}
|
||||
</div>
|
||||
<div class="remark">
|
||||
{{ data.description }}
|
||||
</div>
|
||||
<div class="price" v-if="data.saleprice">
|
||||
<b><span>¥</span>{{ data.saleprice?.toFixed(2) }}</b>
|
||||
<s>¥{{ data.originalprice?.toFixed(2) }}</s>
|
||||
<p>已售 {{ data.salenums }}</p>
|
||||
</div>
|
||||
<!-- <div class="give">
|
||||
<div>
|
||||
<img src="/img/coin.png">
|
||||
果实可抵¥{{ $trunc(data.saleprice * data.FruitRate / 100, 2).toFixed(2) }}
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="label">
|
||||
<span><img src="/img/GroupShopDetail-i2.png">品质保障</span>
|
||||
<span><img src="/img/GroupShopDetail-i2.png">正品包邮</span>
|
||||
<span><img src="/img/GroupShopDetail-i2.png">售后无忧</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="_sku" v-if="data.ForbiddenRule && data.ForbiddenRule.length > 0">
|
||||
<van-cell icon="/img/notShip-i1.png" is-link @click="showNotShip = true">
|
||||
<template #title>
|
||||
不支持发货区域:
|
||||
<span class="span" v-for="list in data.ForbiddenRule">
|
||||
<span v-for="(item, index) in list.Region">
|
||||
{{ item.RegionName }}<font v-if="index < list.Region.length - 1">;</font>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</van-cell>
|
||||
</div>
|
||||
|
||||
<div class="_sku">
|
||||
<van-cell icon="bars" @click="showSpecs = true" :title="selectedSku" is-link />
|
||||
</div>
|
||||
|
||||
<div class="_video" v-if="videoList.length > 0" @click="showVideoList = true">
|
||||
<img src="/img/GoodsDetail-video.png">
|
||||
<p>视频介绍</p>
|
||||
</div>
|
||||
|
||||
<div class="html">
|
||||
<div class="title">
|
||||
<div class="t">
|
||||
<img src="/img/GroupShopDetail-tit-i3.png">
|
||||
<b>产品详情</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w100" v-html="data.contents"></div>
|
||||
</div>
|
||||
|
||||
<div class="vip">
|
||||
<img src="/img/de-i2.png" alt="">
|
||||
会员卡额度抵¥{{ data.deducthuiyuanka?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-share-sheet v-model:show="showShare" title="立即分享给好友" v-bind:options="options" @select="onSelect">
|
||||
</van-share-sheet>
|
||||
|
||||
<van-popup v-model:show="showTip" style="background: transparent;">
|
||||
<img src="/img/fenxiangtishi2.png" class="w" @click="showTip = false" style="height: 100vh;">
|
||||
</van-popup>
|
||||
|
||||
<van-action-bar class="b_l_w" safe-area-inset-bottom placeholder>
|
||||
<van-action-bar-icon @click="$goService()" icon="/img/GroupShopDetail-footer-i1.png" text="客服" />
|
||||
<van-action-bar-icon @click="beforeShare" icon="/img/GroupShopDetail-footer-i2.png" text="分享" />
|
||||
<van-action-bar-button @click="showSpecs = true" color="#d0241c" text="立即购买" />
|
||||
</van-action-bar>
|
||||
|
||||
<van-popup v-model:show="showSpecs" position="bottom" closeable round>
|
||||
<div class="showSpecs">
|
||||
<div class="_product">
|
||||
<img :src="$file(tempSku.img || data.img)">
|
||||
<div class="_c">
|
||||
<div class="price">
|
||||
<span>¥</span>{{ (tempSku.saleprice * tempSku.Qty).toFixed(2) }}
|
||||
</div>
|
||||
|
||||
<div class="d">
|
||||
<div class="selected box box-pack-between">
|
||||
已选:{{ tempSku.skuname }}
|
||||
<span>库存:{{ tempSku.saleleft }}</Span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-for="(spec, index) in specSelect">
|
||||
<div class="specs">
|
||||
{{ spec.name }}
|
||||
</div>
|
||||
|
||||
<div class="label">
|
||||
<a v-for="(child, childIndex) in spec.children"
|
||||
:class="selectedSpecs[spec.name] === child.name ? 'a' : ''"
|
||||
@click="selectSpec(spec.name, child.name)">
|
||||
{{ child.name }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="number" v-if="data.SpuArea != 'gift'">
|
||||
购买数量
|
||||
<van-stepper v-model="tempSku.Qty" min="1" max="99" input-width="8vw" allow-empty="true"
|
||||
theme="round" button-size="5vw" disable-input />
|
||||
</div>
|
||||
|
||||
<van-action-bar class="b_l_w" safe-area-inset-bottom placeholder>
|
||||
<van-action-bar-button @click="submit" color="linear-gradient(to right, #ff6034, #ee0a24)"
|
||||
text="立即购买" />
|
||||
</van-action-bar>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model:show="showNotShip" class="showNotShip">
|
||||
<img src="/img/notShip-i2.png">
|
||||
<div class="c">
|
||||
<div class="text">
|
||||
不支持发货区域:
|
||||
<span class="span" v-for="list in data.ForbiddenRule">
|
||||
<span v-for="(item, index) in list.Region">
|
||||
{{ item.RegionName }}<font v-if="index < list.Region.length - 1">;</font>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<a class="btn" @click="showNotShip = false">
|
||||
好的,知道了
|
||||
</a>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-action-sheet v-model:show="showVideoList" class="popup-GoodsDetail-videoList" safe-area-inset-bottom
|
||||
title="视频介绍列表" closeable style="padding: 0 0 0; min-height: 60%">
|
||||
<van-cell v-for="item in videoList" :icon="$file(item.Poster)" center :title="item.Name"
|
||||
:label="formatTime(item.Duration)" @click="video = item; showVideo = true" />
|
||||
</van-action-sheet>
|
||||
|
||||
<van-popup v-model:show="showVideo" closeable @click-overlay="video = {}" @click-close-icon="video = {}">
|
||||
<video v-if="video.Video" :poster="$file(video.Poster)" controls="" style="width: 100%;height: auto;">
|
||||
<source :src="$file(video.Video)" type="video/mp4" />
|
||||
</video>
|
||||
</van-popup>
|
||||
<hr>
|
||||
|
||||
<div class="gt">
|
||||
<div>
|
||||
<img src="/img/gt-i1.png" alt="">
|
||||
正品保障
|
||||
</div>
|
||||
<div>
|
||||
<img src="/img/gt-i1.png" alt="">
|
||||
快速发货
|
||||
</div>
|
||||
<div>
|
||||
<img src="/img/gt-i1.png" alt="">
|
||||
售后无忧
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
|
||||
<div class="spec_box" @click="showSpecs = true">
|
||||
<img src="/img/spec.png" alt="">
|
||||
<span>{{ selectedSku }}</span>
|
||||
<van-icon class="r" name="arrow"></van-icon>
|
||||
</div>
|
||||
|
||||
<div class="prodetail">
|
||||
<div class="tit">
|
||||
<img src="/img/pro.png" alt="">
|
||||
<span>产品详情</span>
|
||||
</div>
|
||||
<div class="vhtml" v-html="data.contents"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-action-bar safe-area-inset-bottom placeholder>
|
||||
<van-action-bar-icon icon="/img/vb-home.png" text="首页" @click="$navigate('Home')" />
|
||||
<van-action-bar-icon icon="/img/vb-service.png" text="客服" @click="$goService()" />
|
||||
<van-action-bar-icon icon="/img/vb-share.png" text="分享" @click="beforeShare" />
|
||||
<van-action-bar-button color="#ca2904" type="danger" text="立即购买" @click="showSpecs = true" />
|
||||
</van-action-bar>
|
||||
|
||||
<van-share-sheet v-model:show="showShare" title="立即分享给好友" :options="options" @select="onSelect" />
|
||||
|
||||
<van-popup v-model:show="showSpecs" position="bottom" closeable round>
|
||||
<div class="showspec">
|
||||
<div class="goods">
|
||||
<img :src="$file(tempSku.img || data.img)" alt="">
|
||||
<div>
|
||||
<div class="s_price">¥{{ (tempSku.saleprice * tempSku.Qty).toFixed(2) }}</div>
|
||||
<div class="s_stock">剩余:{{ tempSku.saleleft }}</div>
|
||||
<div class="s_spec">已选:{{ tempSku.skuname }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spec_box" v-for="spec in specSelect">
|
||||
<span class="title">
|
||||
{{ spec.name }}
|
||||
</span>
|
||||
|
||||
<div class="speccc">
|
||||
<div v-for="child in spec.children" class="spec_de"
|
||||
:class="selectedSpecs[spec.name] === child.name ? 'active' : 'inactive'"
|
||||
@click="selectSpec(spec.name, child.name)">
|
||||
{{ child.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buynums box">
|
||||
<span class="title">
|
||||
购买数量
|
||||
</span>
|
||||
|
||||
<van-stepper class="r" integer v-model="tempSku.Qty" min="1" max="99" theme="round" button-size="22"
|
||||
disable-input />
|
||||
|
||||
</div>
|
||||
|
||||
<van-action-bar safe-area-inset-bottom placeholder>
|
||||
<van-action-bar-button color="#ca2904" type="danger" text="立即购买" @click="submit" />
|
||||
</van-action-bar>
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
<van-popup v-model:show="show" class="shareimgbox" style="max-width: 80vw;">
|
||||
<div class="share_box" id="bsCard">
|
||||
<img class="logo" src="/img/logo-lr.png" alt="">
|
||||
|
||||
<div class="share_userinfo">
|
||||
<img :src="$file($userInfo.userimg)" alt="">
|
||||
<div class="name"><span>{{ $userInfo.nickname }}友情推荐</span></div>
|
||||
</div>
|
||||
|
||||
<div class="goods_info">
|
||||
<img class="goods_img" :src="$file(data.img)" alt="">
|
||||
<div class="goods_name">{{ data.name }}</div>
|
||||
<div class="goods_price">¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
|
||||
</div>
|
||||
|
||||
<div class="qrcode">
|
||||
<vue-qr :margin="0" :text="shareLink" backgroundColor="rgb(255,255,255)"
|
||||
colorLight="rgb(255,255,255)"></vue-qr>
|
||||
长按扫码购买
|
||||
</div>
|
||||
</div>
|
||||
<div class="share_result" v-if="ShareImg">
|
||||
<img :src="ShareImg" alt="分享海报">
|
||||
<!-- <div class="tips">长按保存图片</div> -->
|
||||
</div>
|
||||
</van-popup>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</BasePage>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { html2canvas, canvasToDataURL } from '@/utils/html2image';
|
||||
|
||||
export default {
|
||||
name: 'GoodsDetail',
|
||||
emits: ['updateShare'],
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {
|
||||
},
|
||||
specSelect: [],
|
||||
specCombinations: [],
|
||||
tempSku: {},
|
||||
showSpecs: false,
|
||||
selectedSku: '请选择规格分类',
|
||||
selectedSpecs: {},
|
||||
showNotShip: false,
|
||||
videoList: [],
|
||||
video: {},
|
||||
showVideoList: false,
|
||||
showVideo: false,
|
||||
showShare: false,
|
||||
showTip: false,
|
||||
showShareImg: false,
|
||||
ShareImg: '',
|
||||
options: [
|
||||
{ name: "分享海报", icon: "/img/ff3.png" }
|
||||
],
|
||||
shareLink: location.origin,
|
||||
cartLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bannerImages() {
|
||||
if (!this.data.imgs) return [this.data.img];
|
||||
const imgs = this.data.imgs.split(';').filter(Boolean);
|
||||
return imgs.length > 0 ? imgs : [this.data.img];
|
||||
}
|
||||
},
|
||||
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 || [];
|
||||
this.shareLink = `${location.origin}/GoodsDetail?id=${this.data.id}`;
|
||||
this.$emit('updateShare', { title: this.data.name, imageUrl: this.data.img, link: this.shareLink });
|
||||
this.initDefaultSku();
|
||||
});
|
||||
},
|
||||
initDefaultSku() {
|
||||
const mainSku = this.specCombinations.find(s => s.ismain) || this.specCombinations[0];
|
||||
if (mainSku) {
|
||||
this.tempSku = { ...mainSku, Qty: 1 };
|
||||
this.selectedSku = "已选:" + mainSku.skuname;
|
||||
}
|
||||
},
|
||||
selectSpec(specName, specValue) {
|
||||
this.selectedSpecs[specName] = specValue;
|
||||
this.refreshSku();
|
||||
},
|
||||
refreshSku() {
|
||||
const specKeys = Object.keys(this.selectedSpecs);
|
||||
if (specKeys.length === 0) return;
|
||||
|
||||
let matchedSku = this.specCombinations.find(combo => {
|
||||
return specKeys.every(key => {
|
||||
const spec = this.specSelect.find(s => s.name === key);
|
||||
if (!spec) return false;
|
||||
const specValue = this.selectedSpecs[key];
|
||||
return combo.skuname === specValue;
|
||||
});
|
||||
});
|
||||
|
||||
if (matchedSku) {
|
||||
this.tempSku = { ...matchedSku, Qty: this.tempSku.Qty || 1 };
|
||||
this.selectedSku = "已选:" + matchedSku.skuname;
|
||||
}
|
||||
},
|
||||
onSelect(option, index) {
|
||||
this.showShare = false;
|
||||
if (option.name === "分享海报") {
|
||||
this.generateShareImg();
|
||||
} else if (option.name == "复制链接") {
|
||||
this.$copyText(location.href);
|
||||
this.$showToast({ type: "success", message: "复制成功" });
|
||||
} else if (option.name == "分享好友") {
|
||||
this.showTip = true;
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
if (this.tempSku.saleleft < 1) {
|
||||
this.$showFailToast("该规格库存不足,无法购买");
|
||||
return;
|
||||
}
|
||||
this.$navigate(`/TradeConfirm?id=${this.data.id}&skuid=${this.tempSku.skuid}`);
|
||||
},
|
||||
beforeShare() {
|
||||
this.showShare = true;
|
||||
},
|
||||
async generateShareImg() {
|
||||
var shareContent = document.querySelector('#bsCard');
|
||||
var width = shareContent.offsetWidth;
|
||||
var height = shareContent.offsetHeight;
|
||||
try {
|
||||
var canvas = await html2canvas(shareContent, {
|
||||
scale: 2,
|
||||
width: width,
|
||||
height: height,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
});
|
||||
this.ShareImg = canvasToDataURL(canvas, "image/png", 1.0);
|
||||
this.showShareImg = true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
formatTime(seconds) {
|
||||
const hours = Math.floor(seconds / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${this.pad(hours)}:${this.pad(minutes)}:${this.pad(secs)}`;
|
||||
},
|
||||
pad(num) {
|
||||
return num.toString().padStart(2, '0');
|
||||
}
|
||||
name: 'GoodsDetail',
|
||||
emits: ['updateShare'],
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
specSelect: [],
|
||||
show: false,
|
||||
specCombinations: [],
|
||||
tempSku: {},
|
||||
showSpecs: false,
|
||||
selectedSku: '请选择规格分类',
|
||||
selectedSpecs: {},
|
||||
showShare: false,
|
||||
options: [
|
||||
{ name: '分享海报', icon: '/img/share.png' },
|
||||
],
|
||||
shareLink: '',
|
||||
ShareImg: '',
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
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') || '';
|
||||
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();
|
||||
});
|
||||
},
|
||||
initDefaultSku() {
|
||||
const mainSku = this.specCombinations.find(s => s.ismain) || this.specCombinations[0];
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
selectSpec(specName, specValue) {
|
||||
this.selectedSpecs[specName] = specValue;
|
||||
this.refreshSku();
|
||||
},
|
||||
refreshSku() {
|
||||
const specKeys = Object.keys(this.selectedSpecs);
|
||||
if (specKeys.length === 0) return;
|
||||
|
||||
let matchedSku = this.specCombinations.find(combo => {
|
||||
return specKeys.every(key => {
|
||||
const spec = this.specSelect.find(s => s.name === key);
|
||||
if (!spec) return false;
|
||||
const specValue = this.selectedSpecs[key];
|
||||
return combo.skuname === specValue;
|
||||
});
|
||||
});
|
||||
|
||||
if (matchedSku) {
|
||||
this.tempSku = { ...matchedSku, Qty: this.tempSku.Qty || 1 };
|
||||
this.selectedSku = "已选:" + matchedSku.skuname;
|
||||
}
|
||||
},
|
||||
onSelect(option) {
|
||||
this.showShare = false;
|
||||
if (option.name === "分享海报") {
|
||||
this.show = true;
|
||||
this.loading = true;
|
||||
this.ShareImg = '';
|
||||
// 等待 DOM 渲染后再生成
|
||||
setTimeout(() => {
|
||||
this.generateShareImg();
|
||||
}, 200);
|
||||
} else if (option.name == "复制链接") {
|
||||
this.$copyText(location.href);
|
||||
this.$showToast({ type: "success", message: "复制成功" });
|
||||
} else if (option.name == "分享好友") {
|
||||
this.showTip = true;
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
if (this.tempSku.saleleft < 1) {
|
||||
this.$showFailToast("该规格库存不足,无法购买");
|
||||
return;
|
||||
}
|
||||
this.$navigate(`/TradeConfirm?id=${this.data.id}&buynums=${this.tempSku.Qty}`);
|
||||
},
|
||||
beforeShare() {
|
||||
this.showShare = true;
|
||||
},
|
||||
async generateShareImg() {
|
||||
var shareContent = document.querySelector('#bsCard');
|
||||
if (!shareContent) return;
|
||||
var width = shareContent.offsetWidth;
|
||||
var height = shareContent.offsetHeight;
|
||||
try {
|
||||
var canvas = await html2canvas(shareContent, {
|
||||
scale: 2,
|
||||
width: width,
|
||||
height: height,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
});
|
||||
this.ShareImg = canvasToDataURL(canvas, "image/png", 1.0);
|
||||
this.loading = false;
|
||||
document.querySelector('.share_box').style.display = 'none';
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
@ -1,245 +0,0 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<div class="goodsdetails">
|
||||
<div class="t">
|
||||
<img :src="$file(data.img)" alt="">
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="goodsshow">
|
||||
<div class="t">
|
||||
<div class="tit">
|
||||
{{ data.name }}
|
||||
</div>
|
||||
<div class="desc" v-if="data.description">
|
||||
{{ data.description }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="price_box">
|
||||
<div class="price">
|
||||
¥{{ data.saleprice?.toFixed(2) }}
|
||||
</div>
|
||||
<div class="oprice">
|
||||
¥{{ data.originalprice?.toFixed(2) }}
|
||||
</div>
|
||||
<div class="sales r">
|
||||
已售{{ data.salenums }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="deduct_box">
|
||||
<div class="point">
|
||||
<img src="/img/de-i1.png" alt="">
|
||||
积分抵¥1999.00
|
||||
</div>
|
||||
|
||||
<div class="vip">
|
||||
<img src="/img/de-i2.png" alt="">
|
||||
会员卡额度抵¥1999.00
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="gt">
|
||||
<div>
|
||||
<img src="/img/gt-i1.png" alt="">
|
||||
正品保障
|
||||
</div>
|
||||
<div>
|
||||
<img src="/img/gt-i1.png" alt="">
|
||||
快速发货
|
||||
</div>
|
||||
<div>
|
||||
<img src="/img/gt-i1.png" alt="">
|
||||
售后无忧
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spec_box" @click="showSpecs = true">
|
||||
<img src="/img/spec.png" alt="">
|
||||
<span>{{ selectedSku }}</span>
|
||||
<van-icon class="r" name="arrow"></van-icon>
|
||||
</div>
|
||||
|
||||
<div class="prodetail">
|
||||
<div class="tit">
|
||||
<img src="/img/pro.png" alt="">
|
||||
<span>产品详情</span>
|
||||
</div>
|
||||
<div class="vhtml" v-html="data.contents"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-action-bar safe-area-inset-bottom placeholder>
|
||||
<van-action-bar-icon icon="/img/vb-home.png" text="首页" @click="$navigate('Home')" />
|
||||
<van-action-bar-icon icon="/img/vb-service.png" text="客服" @click="$goService()" />
|
||||
<van-action-bar-icon icon="/img/vb-share.png" text="分享" @click="beforeShare" />
|
||||
<van-action-bar-button color="#ca2904" type="danger" text="立即购买" @click="showSpecs = true" />
|
||||
</van-action-bar>
|
||||
|
||||
<van-share-sheet v-model:show="showShare" title="立即分享给好友" :options="options" @select="onSelect" />
|
||||
|
||||
<van-popup v-model:show="showSpecs" position="bottom" closeable round>
|
||||
<div class="showspec">
|
||||
<div class="goods">
|
||||
<img :src="$file(tempSku.img || data.img)" alt="">
|
||||
<div>
|
||||
<div class="s_price">¥{{ (tempSku.saleprice * tempSku.Qty).toFixed(2) }}</div>
|
||||
<div class="s_stock">剩余:{{ tempSku.saleleft }}</div>
|
||||
<div class="s_spec">已选:{{ tempSku.skuname }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="spec_box" v-for="spec in specSelect">
|
||||
<span class="title">
|
||||
{{ spec.name }}
|
||||
</span>
|
||||
|
||||
<div class="speccc">
|
||||
<div v-for="child in spec.children" class="spec_de"
|
||||
:class="selectedSpecs[spec.name] === child.name ? 'active' : 'inactive'"
|
||||
@click="selectSpec(spec.name, child.name)">
|
||||
{{ child.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="buynums box">
|
||||
<span class="title">
|
||||
购买数量
|
||||
</span>
|
||||
|
||||
<van-stepper class="r" integer v-model="tempSku.Qty" min="1" max="99" theme="round" button-size="22"
|
||||
disable-input />
|
||||
|
||||
</div>
|
||||
|
||||
<van-action-bar safe-area-inset-bottom placeholder>
|
||||
<van-action-bar-button color="#ca2904" type="danger" text="立即购买" @click="submit" />
|
||||
</van-action-bar>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
|
||||
</BasePage>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { html2canvas, canvasToDataURL } from '@/utils/html2image';
|
||||
|
||||
export default {
|
||||
name: 'GoodsDetail',
|
||||
emits: ['updateShare'],
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {},
|
||||
specSelect: [],
|
||||
specCombinations: [],
|
||||
tempSku: {},
|
||||
showSpecs: false,
|
||||
selectedSku: '请选择规格分类',
|
||||
selectedSpecs: {},
|
||||
showShare: false,
|
||||
options: [
|
||||
{ name: '分享海报', icon: '/img/share.png' },
|
||||
],
|
||||
shareLink: location.origin
|
||||
}
|
||||
},
|
||||
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 || [];
|
||||
this.shareLink = `${location.origin}/GoodsDetail?id=${this.data.id}`;
|
||||
this.$emit('updateShare', { title: this.data.name, imageUrl: this.data.img, link: this.shareLink });
|
||||
this.initDefaultSku();
|
||||
});
|
||||
},
|
||||
initDefaultSku() {
|
||||
const mainSku = this.specCombinations.find(s => s.ismain) || this.specCombinations[0];
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
selectSpec(specName, specValue) {
|
||||
this.selectedSpecs[specName] = specValue;
|
||||
this.refreshSku();
|
||||
},
|
||||
refreshSku() {
|
||||
const specKeys = Object.keys(this.selectedSpecs);
|
||||
if (specKeys.length === 0) return;
|
||||
|
||||
let matchedSku = this.specCombinations.find(combo => {
|
||||
return specKeys.every(key => {
|
||||
const spec = this.specSelect.find(s => s.name === key);
|
||||
if (!spec) return false;
|
||||
const specValue = this.selectedSpecs[key];
|
||||
return combo.skuname === specValue;
|
||||
});
|
||||
});
|
||||
|
||||
if (matchedSku) {
|
||||
this.tempSku = { ...matchedSku, Qty: this.tempSku.Qty || 1 };
|
||||
this.selectedSku = "已选:" + matchedSku.skuname;
|
||||
}
|
||||
},
|
||||
onSelect(option) {
|
||||
this.showShare = false;
|
||||
if (option.name === "分享海报") {
|
||||
this.generateShareImg();
|
||||
} else if (option.name == "复制链接") {
|
||||
this.$copyText(location.href);
|
||||
this.$showToast({ type: "success", message: "复制成功" });
|
||||
} else if (option.name == "分享好友") {
|
||||
this.showTip = true;
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
if (this.tempSku.saleleft < 1) {
|
||||
this.$showFailToast("该规格库存不足,无法购买");
|
||||
return;
|
||||
}
|
||||
this.$navigate(`/TradeConfirm?id=${this.data.id}&buynums=${this.tempSku.Qty}`);
|
||||
},
|
||||
beforeShare() {
|
||||
this.showShare = true;
|
||||
},
|
||||
async generateShareImg() {
|
||||
var shareContent = document.querySelector('#bsCard');
|
||||
if (!shareContent) return;
|
||||
var width = shareContent.offsetWidth;
|
||||
var height = shareContent.offsetHeight;
|
||||
try {
|
||||
var canvas = await html2canvas(shareContent, {
|
||||
scale: 2,
|
||||
width: width,
|
||||
height: height,
|
||||
useCORS: true,
|
||||
allowTaint: true,
|
||||
});
|
||||
this.ShareImg = canvasToDataURL(canvas, "image/png", 1.0);
|
||||
this.showShareImg = true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -27,7 +27,7 @@
|
||||
<div @click="$navigate('MerchantCashout')">
|
||||
<span>提现中金额
|
||||
</span>
|
||||
<p>¥1000.00</p>
|
||||
<p>¥{{ data.withdrawing?.toFixed(2) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -232,6 +232,8 @@ export default {
|
||||
this.$ls.set('merchant_shopname', res.data.shopname);
|
||||
this.$ls.set('merchant_shopimg', res.data.shopimg);
|
||||
this.$ls.set('merchant_cellphone', res.data.cellphone);
|
||||
|
||||
this.link = `${location.origin}${location.pathname}#/Checkout?id=${res.data.userid}`
|
||||
}),
|
||||
this.getCount()
|
||||
]
|
||||
|
||||
@ -4,33 +4,34 @@
|
||||
<div class="top">
|
||||
<span>
|
||||
提现中:
|
||||
<b class="red">¥960.00</b>
|
||||
<b class="red">¥{{ statistics.withdrawing?.toFixed(2) || '0.00' }}</b>
|
||||
</span>
|
||||
|
||||
<span class="r">
|
||||
累计提现:
|
||||
<b>¥5960.25</b>
|
||||
<b>¥{{ statistics.withdrawtotal?.toFixed(2) || '0.00' }}</b>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="list">
|
||||
<div class="item">
|
||||
<div class="line">
|
||||
<b class="state">提现中</b>
|
||||
<b class="r money red">¥<span>960</span>
|
||||
</b>
|
||||
</div>
|
||||
<div class="record">
|
||||
<div class="left">
|
||||
提现:2024.06.05 00:00:00
|
||||
<BaseList ref="listRef" class="list" url="/v1/client/DShopsClient/withdraw">
|
||||
<template #default="{ item }">
|
||||
<div class="item">
|
||||
<div class="line">
|
||||
<b class="state">{{ item.statename }}</b>
|
||||
<b class="r money red">¥<span>{{ item.wdnums?.toFixed(2) }}</span>
|
||||
</b>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
发放:2024.06.05 05:25:35
|
||||
<div class="record">
|
||||
<div class="left">
|
||||
提现:{{ $formatGMT(item.addtime, 'yyyy-MM-dd HH:ss') }}
|
||||
</div>
|
||||
<div class="right" v-if="item.trantime">
|
||||
发放:{{ $formatGMT(item.trantime, 'yyyy-MM-dd HH:ss') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</BaseList>
|
||||
</div>
|
||||
</BasePage>
|
||||
</template>
|
||||
@ -38,7 +39,19 @@
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
return {
|
||||
statistics: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.$get('/v1/client/DShopsClient/withdrawstatistics').then(res => {
|
||||
this.statistics = res.data || {}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -10,20 +10,20 @@
|
||||
<van-icon @click="onShowTerm" name="question-o"></van-icon>
|
||||
</div>
|
||||
<span class="perform">
|
||||
¥{{ data.total?.Balance.toFixed(2) }}
|
||||
¥{{ data.total.income?.toFixed(2) }}
|
||||
</span>
|
||||
|
||||
<div class="btm">
|
||||
<div>
|
||||
<span>昨日收入:</span>
|
||||
<span>
|
||||
¥{{ data.total?.YesterdayAmount.toFixed(2) }}
|
||||
¥{{ data.total.incomeyestoday?.toFixed(2) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="r" style="justify-content: end;" @click="$navigate('MerchantCashout')">
|
||||
<span>提现中:</span>
|
||||
<span>
|
||||
¥{{ data.total?.CashAmount.toFixed(2) }}
|
||||
¥{{ data.total?.withdrawing?.toFixed(2) }}
|
||||
<van-icon name="arrow"></van-icon>
|
||||
</span>
|
||||
</div>
|
||||
@ -51,27 +51,30 @@
|
||||
<div class="income">
|
||||
<span>当月收款:</span>
|
||||
<span>
|
||||
¥{{ data.total?.MonthBalance?.toFixed(2) }}
|
||||
¥{{ data.total?.incomemonth?.toFixed(2) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="details_box">
|
||||
<div class="item" v-for="item in records">
|
||||
<div class="line1">
|
||||
<span class="type">{{ type[item.RecordSource] }}</span>
|
||||
<p>剩余:{{ item.CurValue }}</p>
|
||||
<span :class="item.OpeType > 0 ? 'income' : 'expend'">{{ item.OpeType > 0 ?
|
||||
'+' : '-' }}{{ item.RecordValue?.toFixed(2) }}</span>
|
||||
<van-empty v-if="records.length === 0" description="暂无记录" />
|
||||
<template v-else>
|
||||
<div class="item" v-for="item in records">
|
||||
<div class="line1">
|
||||
<span class="type">{{ item.typename }}</span>
|
||||
<p>剩余:{{ item.balance }}</p>
|
||||
<span :class="item.nums > 0 ? 'income' : 'expend'">{{ item.nums > 0 ? '+' : '' }}
|
||||
{{ item.nums?.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div>
|
||||
操作时间:{{ item.addtime }}
|
||||
</div>
|
||||
<div>
|
||||
订单编号:{{ item.ordernum }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
购买时间:{{ item.CTime }}
|
||||
</div>
|
||||
<div>
|
||||
订单编号:{{ item.TradeCode }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -83,7 +86,7 @@
|
||||
</van-popup>
|
||||
<van-action-sheet v-model:show="showTerm" safe-area-inset-bottom title="营业收入说明" closeable
|
||||
style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||||
<div class="w100 html" v-html="data.Term" />
|
||||
<div class="w100 html" v-html="$datadic.getContent('code_yysrsm')" />
|
||||
</van-action-sheet>
|
||||
</template>
|
||||
|
||||
@ -91,41 +94,57 @@
|
||||
export default {
|
||||
name: 'MerchantIncome',
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: {
|
||||
total: {
|
||||
Balance: 12868.50,
|
||||
YesterdayAmount: 568.00,
|
||||
CashAmount: 2000.00,
|
||||
MonthBalance: 8650.00
|
||||
},
|
||||
Term: '营业收入说明:商家通过平台交易获得的收入,每日自动转入绑定银行账户。'
|
||||
balance: 0,
|
||||
incometoday: 0,
|
||||
incomeyestoday: 0,
|
||||
incomemonth: 0,
|
||||
withdrawing: 0
|
||||
}
|
||||
},
|
||||
records: [
|
||||
{ RecordSource: 'sale', OpeType: 1, RecordValue: 128.00, CurValue: 12868.50, TradeCode: 'T20260418001', CTime: '2024-04-18 10:30:00' },
|
||||
{ RecordSource: 'sale', OpeType: 1, RecordValue: 256.00, CurValue: 12868.50, TradeCode: 'T20260418002', CTime: '2024-04-18 11:20:00' },
|
||||
{ RecordSource: 'cashout', OpeType: 0, RecordValue: 500.00, CurValue: 12868.50, TradeCode: 'T20260417001', CTime: '2024-04-17 18:00:00' },
|
||||
{ RecordSource: 'sale', OpeType: 1, RecordValue: 89.00, CurValue: 12868.50, TradeCode: 'T20260417002', CTime: '2024-04-17 14:30:00' },
|
||||
],
|
||||
records: [],
|
||||
date: [`${new Date().getFullYear()}`, `${new Date().getMonth() + 1}`],
|
||||
currentDate: [`${new Date().getFullYear()}`, `${new Date().getMonth() + 1}`],
|
||||
show: false,
|
||||
showDate: false,
|
||||
loading: false,
|
||||
showTerm: false,
|
||||
type: { sale: '收入', cashout: '提现', manual: "其他" }
|
||||
param: {
|
||||
year: new Date().getFullYear(),
|
||||
month: new Date().getMonth() + 1,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
Promise.all([
|
||||
this.$get('/v1/client/DShopsClient/moneystatistics', this.param),
|
||||
this.$get('/v1/client/DShopsClient/money', this.param)
|
||||
]).then(([total, recordsRes]) => {
|
||||
this.data.total = total.data;
|
||||
this.records = recordsRes.data;
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || '加载失败');
|
||||
})
|
||||
},
|
||||
onconfirm(value) {
|
||||
this.date = this.currentDate;
|
||||
this.param = {
|
||||
year: parseInt(this.currentDate[0]),
|
||||
month: parseInt(this.currentDate[1]),
|
||||
}
|
||||
this.showDate = false;
|
||||
this.init();
|
||||
},
|
||||
onShowTerm() {
|
||||
this.showTerm = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -4,8 +4,7 @@
|
||||
<div class="intro-header">
|
||||
<div class="shop-avatar">
|
||||
<van-uploader v-model="formData.AvatarUploader" :after-read="uploadShopImg" :max-count="1" upload-icon="plus">
|
||||
<img v-if="formData.shopimg" :src="$file(formData.shopimg)" class="avatar-img" />
|
||||
<div v-else class="avatar-placeholder">
|
||||
<div class="avatar-placeholder">
|
||||
<van-icon name="photograph" size="20" />
|
||||
</div>
|
||||
</van-uploader>
|
||||
@ -23,10 +22,10 @@
|
||||
</div>
|
||||
<van-cell-group inset class="info-group">
|
||||
<van-field v-model="formData.shopname" label="店铺名称" placeholder="请输入店铺名称" required />
|
||||
<van-field v-model="formData.huili" label="惠利比例" placeholder="请输入惠利比例" type="number" required>
|
||||
<template #append>%</template>
|
||||
</van-field>
|
||||
<van-field v-model="formData.cellphone" label="联系电话" placeholder="请输入联系电话" type="tel" required />
|
||||
<van-field v-model="formData.huili" label="惠利比例" placeholder="请输入惠利比例" type="number">
|
||||
<template #extra><span style="color:#666">%</span></template>
|
||||
</van-field>
|
||||
</van-cell-group>
|
||||
</div>
|
||||
|
||||
@ -36,11 +35,12 @@
|
||||
<span class="readonly-tag">仅展示</span>
|
||||
</div>
|
||||
<van-cell-group inset class="info-group">
|
||||
<van-field v-model="formData.legalname" label="法人姓名" readonly />
|
||||
<van-field v-model="formData.legalphone" label="法人手机号" readonly />
|
||||
<van-field v-model="formData.legalpersonname" label="法人姓名" readonly />
|
||||
<van-field v-model="formData.legalpersonphone" label="法人手机号" readonly />
|
||||
<van-field label="法人身份证照片" readonly>
|
||||
<template #input>
|
||||
<div v-if="formData.legalidimg && formData.legalidimg.length" class="img-thumb" @click="previewImage(formData.legalidimg[0].url)">
|
||||
<div v-if="formData.legalidimg && formData.legalidimg.length" class="img-thumb"
|
||||
@click="previewImage(formData.legalidimg[0].url)">
|
||||
<img :src="formData.legalidimg[0].url" />
|
||||
</div>
|
||||
<span v-else class="empty">暂无</span>
|
||||
@ -48,7 +48,8 @@
|
||||
</van-field>
|
||||
<van-field label="营业执照照片" readonly>
|
||||
<template #input>
|
||||
<div v-if="formData.licenseimg && formData.licenseimg.length" class="img-thumb" @click="previewImage(formData.licenseimg[0].url)">
|
||||
<div v-if="formData.licenseimg && formData.licenseimg.length" class="img-thumb"
|
||||
@click="previewImage(formData.licenseimg[0].url)">
|
||||
<img :src="formData.licenseimg[0].url" />
|
||||
</div>
|
||||
<span v-else class="empty">暂无</span>
|
||||
@ -64,10 +65,10 @@
|
||||
<span class="readonly-tag">仅展示</span>
|
||||
</div>
|
||||
<van-cell-group inset class="info-group">
|
||||
<van-field v-model="formData.bankname" label="账户名称" readonly />
|
||||
<van-field v-model="formData.bankcard" label="银行卡号" readonly />
|
||||
<van-field v-model="formData.subbank" label="支行名称" readonly />
|
||||
<van-field v-model="formData.bankphone" label="银行预留手机号" readonly />
|
||||
<van-field v-model="formData.bankrealname" label="账户名称" readonly />
|
||||
<van-field v-model="formData.bankcardnumber" label="银行卡号" readonly />
|
||||
<!-- <van-field v-model="formData.subbank" label="支行名称" readonly /> -->
|
||||
<van-field v-model="formData.bankcellphone" label="银行预留手机号" readonly />
|
||||
</van-cell-group>
|
||||
</div>
|
||||
|
||||
@ -95,15 +96,15 @@ export default {
|
||||
AvatarUploader: [],
|
||||
huili: 20,
|
||||
cellphone: '',
|
||||
legalname: '',
|
||||
legalphone: '',
|
||||
legalpersonname: '',
|
||||
legalpersonphone: '',
|
||||
legalidimg: [],
|
||||
licenseimg: [],
|
||||
province: '',
|
||||
bankname: '',
|
||||
bankcard: '',
|
||||
bankcardnumber: '',
|
||||
subbank: '',
|
||||
bankphone: '',
|
||||
bankcellphone: '',
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -112,32 +113,37 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.$get('/v1/client/DShopsClient/my').then(res => {
|
||||
this.$get('/v1/client/DShopsClient').then(res => {
|
||||
const data = res.data
|
||||
this.formData.shopname = data.shopname
|
||||
this.formData.shopimg = data.shopimg
|
||||
this.formData.huili = data.huili || 20
|
||||
this.formData.huili = data.feeratio || 20
|
||||
this.formData.cellphone = data.cellphone
|
||||
this.formData.legalname = data.legalname
|
||||
this.formData.legalphone = data.legalphone
|
||||
this.formData.province = data.province
|
||||
this.formData.bankname = data.bankname
|
||||
this.formData.bankcard = data.bankcard
|
||||
this.formData.legalpersonname = data.legalpersonname
|
||||
this.formData.legalpersonphone = data.legalpersonphone
|
||||
this.formData.province = data.shopprovince + data.shopcity + data.shopcounty
|
||||
this.formData.bankrealname = data.bankrealname
|
||||
this.formData.bankcardnumber = data.bankcardnumber
|
||||
this.formData.subbank = data.subbank
|
||||
this.formData.bankphone = data.bankphone
|
||||
this.formData.bankcellphone = data.bankcellphone
|
||||
|
||||
if (data.shopimg) {
|
||||
this.formData.AvatarUploader = [{ url: this.$file(data.shopimg), isImage: true }]
|
||||
}
|
||||
if (data.legalidimg) {
|
||||
this.formData.legalidimg = [{ url: this.$file(data.legalidimg), isImage: true }]
|
||||
if (data.legalpersonidimg) {
|
||||
this.formData.legalidimg = [{ url: this.$file(data.legalpersonidimg), isImage: true }]
|
||||
}
|
||||
if (data.licenseimg) {
|
||||
this.formData.licenseimg = [{ url: this.$file(data.licenseimg), isImage: true }]
|
||||
if (data.businesslicenseimg) {
|
||||
this.formData.licenseimg = [{ url: this.$file(data.businesslicenseimg), isImage: true }]
|
||||
}
|
||||
})
|
||||
},
|
||||
uploadShopImg(file) {
|
||||
if (!file || !file.file) {
|
||||
this.formData.shopimg = ''
|
||||
this.formData.AvatarUploader = []
|
||||
return
|
||||
}
|
||||
this.formData.shopimg = file.file
|
||||
},
|
||||
previewImage(url) {
|
||||
@ -146,16 +152,13 @@ export default {
|
||||
async save() {
|
||||
this.saving = true
|
||||
try {
|
||||
const fd = new FormData()
|
||||
fd.append('shopname', this.formData.shopname)
|
||||
fd.append('shopphone', this.formData.cellphone)
|
||||
if (this.formData.shopimg instanceof File) {
|
||||
const url = await this.uploadFile('/v1/client/DShopsClient/shopimg', this.formData.shopimg)
|
||||
this.formData.shopimg = url
|
||||
fd.append('file', this.formData.shopimg)
|
||||
}
|
||||
|
||||
await this.$put('/v1/client/DShopsClient', {
|
||||
shopname: this.formData.shopname,
|
||||
huili: this.formData.huili,
|
||||
cellphone: this.formData.cellphone,
|
||||
})
|
||||
await this.$postForm('/v1/client/DShopsClient/shopinfo', fd)
|
||||
this.$showSuccessToast('保存成功')
|
||||
} catch (err) {
|
||||
this.$showFailToast(err.message || '保存失败')
|
||||
@ -213,7 +216,8 @@ export default {
|
||||
border: 3px solid rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.avatar-placeholder {
|
||||
.avatar-placeholder,
|
||||
.van-uploader__preview-image {
|
||||
width: 18vw;
|
||||
height: 18vw;
|
||||
border-radius: 50%;
|
||||
@ -287,6 +291,7 @@ export default {
|
||||
width: 26vw;
|
||||
font-size: 3.73vw;
|
||||
color: #666;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
:deep(.van-field__control) {
|
||||
@ -299,6 +304,17 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.van-field__append) {
|
||||
color: #666;
|
||||
font-size: 3.73vw;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
:deep(.van-field__extra) {
|
||||
color: #666;
|
||||
font-size: 3.73vw;
|
||||
}
|
||||
|
||||
:deep(.van-field__control:disabled) {
|
||||
color: #999;
|
||||
background: transparent;
|
||||
|
||||
@ -1,12 +1,17 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<div class="merchanttradedetail">
|
||||
<div class="state" :class="getStateClass(data.state)">
|
||||
<div class="state" :class="getStateClass(data.state)" v-if="type !== 'user'">
|
||||
<img :src="data.state >= 3 ? '/img/checked.png' : '/img/loading.png'" alt="">
|
||||
<b>{{ data.statename }}</b>
|
||||
</div>
|
||||
|
||||
<div class="containar">
|
||||
<div class="state box-tb box-align-center" v-else>
|
||||
<img :src="data.state >= 3 ? '/img/checked.png' : '/img/loading.png'" alt="">
|
||||
<b style="margin:2.4vw 0 0;">交易完成</b>
|
||||
</div>
|
||||
|
||||
<div class="containar" v-if="type !== 'user'">
|
||||
<div class="line"></div>
|
||||
<div class="details_box">
|
||||
<div class="detail">
|
||||
@ -21,39 +26,43 @@
|
||||
<span>付款时间:</span>
|
||||
<p>{{ $formatGMT(data.paytime, 'yyyy-MM-dd HH:mm:ss') }}</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.payway">
|
||||
<span>支付方式:</span>
|
||||
<p>{{ data.payway }}</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>交易单号:</span>
|
||||
<p>{{ data.ordernum }} <van-icon name="copy" @click="copyOrdernum" /></p>
|
||||
<p>{{ data.ordernum }}<span @click.stop="copyOrdernum">|复制</span></p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="detail">
|
||||
<span>买单商品总额:</span>
|
||||
<p>¥{{ data.ordermoney }}</p>
|
||||
<p>¥{{ data.ordermoney?.toFixed(2) }}</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.payjifen">
|
||||
<div class="detail" v-if="data.payjifen > 0">
|
||||
<span>用户积分抵扣:</span>
|
||||
<p>{{ data.payjifen }}积分</p>
|
||||
<p>-{{ data.payjifen?.toFixed(2) }}积分</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.payquan">
|
||||
<div class="detail" v-if="data.payquan > 0">
|
||||
<span>会员卡余额抵扣:</span>
|
||||
<p>¥{{ data.payquan }}</p>
|
||||
<p>-¥{{ data.payquan?.toFixed(2) }}</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>用户实付金额:</span>
|
||||
<p>¥{{ data.paymoney }}</p>
|
||||
<p>¥{{ data.paymoney?.toFixed(2) }}</p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="detail" v-if="data.discountratio">
|
||||
<span>商家让利比例:</span>
|
||||
<p>{{ data.discountratio }}%</p>
|
||||
</div>
|
||||
<hr>
|
||||
|
||||
|
||||
<div class="detail">
|
||||
<span>商家让利金额:</span>
|
||||
<p>¥{{ data.discount }}</p>
|
||||
<p>-¥{{ data.discount }}</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>商家应收金额:</span>
|
||||
@ -61,6 +70,59 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="containar" v-else>
|
||||
<div class="line"></div>
|
||||
<div class="details_box">
|
||||
<div class="detail">
|
||||
<span>商户名称:</span>
|
||||
<p>{{ data.shopname }}</p>
|
||||
</div>
|
||||
<div class="detail" v-if="type === '2'">
|
||||
<span>用户账号:</span>
|
||||
<p>{{ $formatCellphone(data.usercellphone) }}</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>下单时间:</span>
|
||||
<p>{{ $formatGMT(data.addtime, 'yyyy-MM-dd HH:mm:ss') }}</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>付款时间:</span>
|
||||
<p>{{ $formatGMT(data.paytime, 'yyyy-MM-dd HH:mm:ss') }}</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.payway">
|
||||
<span>支付方式:</span>
|
||||
<p>{{ data.payway }}</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>交易单号:</span>
|
||||
<p>{{ data.ordernum }}<span @click.stop="copyOrdernum">|复制</span></p>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="detail">
|
||||
<span>商品总额:</span>
|
||||
<p>¥{{ data.ordermoney?.toFixed(2) }}</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.payjifen">
|
||||
<span>积分抵扣:</span>
|
||||
<p>-{{ data.payjifen?.toFixed(2) }}积分</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.payquan">
|
||||
<span>会员卡余额抵扣:</span>
|
||||
<p>-{{ data.payquan?.toFixed(2) }}积分</p>
|
||||
</div>
|
||||
<div class="detail">
|
||||
<span>实付金额:</span>
|
||||
<p class="red">¥{{ data.paymoney?.toFixed(2) }}</p>
|
||||
</div>
|
||||
<div class="detail" v-if="data.discountratio">
|
||||
<span>让利比例:</span>
|
||||
<p>{{ data.discountratio }}%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</BasePage>
|
||||
</template>
|
||||
@ -70,7 +132,8 @@ export default {
|
||||
name: 'MerchantTradeDetail',
|
||||
data() {
|
||||
return {
|
||||
data: {}
|
||||
data: {},
|
||||
type: this.$route.query.type,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -79,7 +142,12 @@ export default {
|
||||
methods: {
|
||||
init() {
|
||||
const ordernum = this.$route.query.id;
|
||||
this.$get(`/v1/client/DShopsClient/order/${ordernum}`).then(res => {
|
||||
const typeParam = this.$route.query.type;
|
||||
const type = typeParam === 'user' ? '2' : (typeParam || '1');
|
||||
const api = type === '2'
|
||||
? `/v1/client/FOrdersshopClient/${ordernum}`
|
||||
: `/v1/client/DShopsClient/order/${ordernum}`;
|
||||
this.$get(api).then(res => {
|
||||
this.data = res.data || {};
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || '加载失败');
|
||||
|
||||
@ -5,12 +5,12 @@
|
||||
|
||||
<div class="top">
|
||||
<div class="info_box">
|
||||
<img class="icon" src="" alt="">
|
||||
<img class="icon" :src="$file(userimg || '/img/default-avatar.png')" alt="">
|
||||
<div class="inf">
|
||||
<b>奖励一个挖野菜</b>
|
||||
<b>{{ nickname || '未登录' }}</b>
|
||||
<div>
|
||||
<img src="/img/phone.png" alt="">
|
||||
12568951256
|
||||
{{ cellphone || '暂无手机号' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -32,7 +32,7 @@
|
||||
<van-icon name="arrow"></van-icon>
|
||||
</div>
|
||||
<b>
|
||||
2565.00
|
||||
{{ zijin?.toFixed(2) || '0.00' }}
|
||||
</b>
|
||||
</div>
|
||||
|
||||
@ -43,13 +43,13 @@
|
||||
<van-icon name="arrow"></van-icon>
|
||||
</div>
|
||||
<b>
|
||||
2565.00
|
||||
{{ xiaofeiquan?.toFixed(2) || '0.00' }}
|
||||
</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fun_box">
|
||||
<div class="item">
|
||||
<div class="item" @click="$navigate('QrReader')">
|
||||
<div class="left">
|
||||
<b>
|
||||
扫一扫
|
||||
@ -94,10 +94,16 @@ export default {
|
||||
components: { ManagerPopup },
|
||||
data() {
|
||||
return {
|
||||
managerPopupVisible: false
|
||||
managerPopupVisible: false,
|
||||
userimg: this.$ls.get('userimg') || '',
|
||||
nickname: this.$ls.get('nickname') || '',
|
||||
cellphone: this.$ls.get('cellphone') || '',
|
||||
zijin: 0,
|
||||
xiaofeiquan: 0,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
window.addEventListener('showManagerPopup', this.onShowManagerPopup)
|
||||
window.addEventListener('closeManagerPopup', this.onCloseManagerPopup)
|
||||
},
|
||||
@ -106,6 +112,19 @@ export default {
|
||||
window.removeEventListener('closeManagerPopup', this.onCloseManagerPopup)
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.$get('/v1/client/DUsersClient').then(res => {
|
||||
this.userimg = res.data.userimg
|
||||
this.nickname = res.data.nickname
|
||||
this.cellphone = res.data.cellphone
|
||||
this.zijin = res.data.zijin
|
||||
this.xiaofeiquan = res.data.xiaofeiquan
|
||||
// 更新localStorage
|
||||
this.$ls.set('userimg', res.data.userimg)
|
||||
this.$ls.set('nickname', res.data.nickname)
|
||||
this.$ls.set('cellphone', res.data.cellphone)
|
||||
})
|
||||
},
|
||||
onShowManagerPopup() {
|
||||
this.managerPopupVisible = true
|
||||
},
|
||||
|
||||
357
src/views/Operations/QrReader.vue
Normal file
@ -0,0 +1,357 @@
|
||||
<template>
|
||||
<div class="qr-scanner">
|
||||
<video ref="videoRef" class="video" playsinline autoplay></video>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<div class="mask">
|
||||
<!-- 中间扫码区域 -->
|
||||
<div class="scan-area">
|
||||
<div class="corner left-top"></div>
|
||||
<div class="corner left-bottom"></div>
|
||||
<div class="corner right-top"></div>
|
||||
<div class="corner right-bottom"></div>
|
||||
<!-- 扫描线动画 -->
|
||||
<div class="scan-line" v-if="isScanning"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 顶部提示 -->
|
||||
<div class="header">
|
||||
<div class="back" @click="$router.back()">
|
||||
<van-icon name="arrow-left" />
|
||||
</div>
|
||||
<div class="title">扫一扫</div>
|
||||
<div class="placeholder"></div>
|
||||
</div>
|
||||
|
||||
<!-- 底部提示 -->
|
||||
<div class="footer">
|
||||
<div class="tip" v-if="!isScanning && !result && !error">将二维码放入框内即可自动扫描</div>
|
||||
<div class="error" v-if="error">{{ error }}</div>
|
||||
<div class="result" v-if="result">
|
||||
<div class="label">扫描结果:</div>
|
||||
<div class="value">{{ result }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 开始扫描提示 -->
|
||||
<div class="start-tip" v-if="!isScanning && !result" @click="startScan">
|
||||
<van-icon name="scan" size="32" />
|
||||
<span>点击开始扫描</span>
|
||||
</div>
|
||||
|
||||
<!-- 核销确认弹框 -->
|
||||
<van-dialog v-model:show="showConfirm" title="确认核销" show-cancel-button @confirm="confirmVerify"
|
||||
@cancel="cancelVerify">
|
||||
<div style="padding: 20px; text-align: center;">
|
||||
是否确认核销此礼品券?<br>
|
||||
券码:{{ pendingSysid }}
|
||||
</div>
|
||||
</van-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { BrowserMultiFormatReader, NotFoundException } from '@zxing/library';
|
||||
|
||||
export default {
|
||||
name: 'QrReader',
|
||||
data() {
|
||||
return {
|
||||
videoRef: null,
|
||||
result: '',
|
||||
isScanning: false,
|
||||
error: '',
|
||||
codeReader: null,
|
||||
stream: null,
|
||||
showConfirm: false,
|
||||
pendingSysid: '',
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.videoRef = this.$refs.videoRef;
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.stopScan();
|
||||
},
|
||||
methods: {
|
||||
async startScan() {
|
||||
this.error = '';
|
||||
this.result = '';
|
||||
|
||||
try {
|
||||
this.codeReader = new BrowserMultiFormatReader();
|
||||
|
||||
await this.codeReader.decodeFromVideoDevice(
|
||||
null,
|
||||
this.videoRef,
|
||||
(decodedText, err) => {
|
||||
if (decodedText) {
|
||||
this.result = decodedText;
|
||||
this.handleScanResult(decodedText);
|
||||
this.stopScan();
|
||||
}
|
||||
if (err && !(err instanceof NotFoundException)) {
|
||||
console.error('扫码错误:', err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.isScanning = true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e.name === 'NotAllowedError' || e.name === 'PermissionDeniedError') {
|
||||
this.error = '摄像头权限被拒绝,请在设置中允许访问摄像头';
|
||||
} else if (e.name === 'NotFoundError' || e.name === 'DevicesNotFoundError') {
|
||||
this.error = '未找到摄像头设备';
|
||||
} else {
|
||||
this.error = e.message || '无法访问摄像头,请确保已授予摄像头权限';
|
||||
}
|
||||
}
|
||||
},
|
||||
stopScan() {
|
||||
if (this.codeReader) {
|
||||
this.codeReader.reset();
|
||||
}
|
||||
this.isScanning = false;
|
||||
},
|
||||
handleScanResult(text) {
|
||||
const str = String(text);
|
||||
// 如果是商家收款码链接,直接跳转
|
||||
if (str.includes('/Checkout?id=')) {
|
||||
const id = str.split('/Checkout?id=')[1];
|
||||
window.location.href = `#/Checkout?id=${id}`;
|
||||
return;
|
||||
}
|
||||
// 如果是礼品券核销链接
|
||||
const match = str.match(/DUserlipinquansClient\/([^/]+)\/state/);
|
||||
if (match) {
|
||||
this.pendingSysid = match[1];
|
||||
this.showConfirm = true;
|
||||
return;
|
||||
}
|
||||
// 如果是http链接,直接跳转
|
||||
if (str.startsWith('http://') || str.startsWith('https://')) {
|
||||
window.location.href = str;
|
||||
return;
|
||||
}
|
||||
// 其他内容显示结果
|
||||
this.result = str;
|
||||
},
|
||||
confirmVerify() {
|
||||
this.showConfirm = false;
|
||||
this.$put(`/v1/client/DUserlipinquansClient/${this.pendingSysid}/state`).then(res => {
|
||||
if (res.status === 200) {
|
||||
this.$showSuccessToast('核销成功');
|
||||
this.result = '核销成功';
|
||||
this.$navigate('CertificateRecord?type=verif')
|
||||
// setTimeout(() => {
|
||||
// this.$router.back();
|
||||
// }, 1500);
|
||||
}
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || '核销失败');
|
||||
this.error = err.message || '核销失败';
|
||||
});
|
||||
},
|
||||
cancelVerify() {
|
||||
this.showConfirm = false;
|
||||
this.pendingSysid = '';
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.qr-scanner {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: #000;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.scan-area {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 60vw;
|
||||
height: 60vw;
|
||||
max-width: 250px;
|
||||
max-height: 250px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.scan-area::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: transparent;
|
||||
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.corner {
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-color: #fff;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.left-top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
border-width: 3px 0 0 3px;
|
||||
border-radius: 4px 0 0 0;
|
||||
}
|
||||
|
||||
.left-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-width: 0 0 3px 3px;
|
||||
border-radius: 0 0 0 4px;
|
||||
}
|
||||
|
||||
.right-top {
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-width: 3px 3px 0 0;
|
||||
border-radius: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.right-bottom {
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
border-width: 0 3px 3px 0;
|
||||
border-radius: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.scan-line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 10%;
|
||||
right: 10%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, transparent, #07c160, transparent);
|
||||
animation: scanMove 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes scanMove {
|
||||
0% {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
top: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 12px 16px;
|
||||
padding-top: calc(12px + env(safe-area-inset-top));
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.back {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding: 20px;
|
||||
padding-bottom: calc(20px + env(safe-area-inset-bottom));
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tip {
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #ff4d4f;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.result {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.result .label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.result .value {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.controls {
|
||||
position: absolute;
|
||||
bottom: 100px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.start-tip {
|
||||
position: absolute;
|
||||
bottom: 120px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@ -152,9 +152,6 @@ export default {
|
||||
this.$get('/v1/client/CNewsClient', { page: 1, pageSize: 3, pid: 3 }).then(data => {
|
||||
this.data.News = data.data.items;
|
||||
}),
|
||||
this.$get('/v1/client/CDatadicsClient/code_sp').then(data => {
|
||||
this.data.Video = data.data;
|
||||
}),
|
||||
this.$get('/v1/client/CNewsClient?pid=2').then(data => {
|
||||
this.data.Trends = data.data.items;
|
||||
}),
|
||||
@ -164,6 +161,9 @@ export default {
|
||||
]).catch(err => {
|
||||
this.$showFailToast(err.message || '数据加载失败');
|
||||
});
|
||||
// 视频和背景图从数据字典获取
|
||||
this.data.Video = this.$datadic.getContent('code_sp')
|
||||
this.data.slideBg = this.$datadic.getContent('code_yqmbj')
|
||||
},
|
||||
create() {
|
||||
// this.showcreate = true;
|
||||
|
||||
@ -40,8 +40,7 @@
|
||||
<div class="deduction">
|
||||
<div class="top">
|
||||
<span>商品金额</span>
|
||||
<b class="r"><span>¥</span>{{ ((product.saleprice || 0) * (product.buynums || 1))?.toFixed(2)
|
||||
}}</b>
|
||||
<b class="r"><span>¥</span>{{ totalPrice }}</b>
|
||||
</div>
|
||||
<van-radio-group v-model="checked" checked-color="#ca2904">
|
||||
<van-radio name="1" label-position="left">
|
||||
@ -52,7 +51,8 @@
|
||||
<span>使用积分抵扣</span>
|
||||
</div>
|
||||
<p>
|
||||
(当前可用:10.00)
|
||||
(当前可用:{{ (userInfo.xiaofeijifen || 0)?.toFixed(2) }},本次可抵:{{
|
||||
actualJifenDeduct?.toFixed(2) }})
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@ -65,24 +65,26 @@
|
||||
<span>使用会员卡抵扣</span>
|
||||
</div>
|
||||
<p>
|
||||
(当前可用:10.00)
|
||||
(当前可用:{{ (userInfo.xiaofeiquan || 0)?.toFixed(2) }},本次可抵:{{
|
||||
actualQuanDeduct?.toFixed(2) }})
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
</van-radio>
|
||||
</van-radio-group>
|
||||
<div class="count">
|
||||
<div>
|
||||
<div v-if="freight > 0">
|
||||
<span>运费</span>
|
||||
<span class="price">
|
||||
{{ product.expressprice ? '¥' + product.expressprice.toFixed(2) : '¥0.00' }}
|
||||
</span>
|
||||
<span class="price">¥{{ freight.toFixed(2) }}</span>
|
||||
</div>
|
||||
<div v-if="checked">
|
||||
<span>抵扣金额</span>
|
||||
<span class="price">-¥{{ actualDeduct?.toFixed(2) || '0.00' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>实付金额</span>
|
||||
<span class="price">
|
||||
{{ product.saleprice ? '¥' + (product.saleprice * product.buynums).toFixed(2) : '¥0.00'
|
||||
}}
|
||||
¥{{ actualPay?.toFixed(2) || '0.00' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -94,7 +96,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-submit-bar class="b_l_w" placeholder :price="useFruitPrice * 100" :tip="`商品总数:${totalQuantity}件`"
|
||||
<van-submit-bar class="b_l_w" placeholder :price="actualPay * 100" :tip="`商品总数:${totalQuantity}件`"
|
||||
tip-icon="info-o" safe-area-inset-bottom>
|
||||
<template #button>
|
||||
<van-button round color="rgb(227, 83, 33)" :loading="isloading" @click="submitOrder"
|
||||
@ -114,6 +116,7 @@ export default {
|
||||
name: 'TradeConfirm',
|
||||
mounted() {
|
||||
this.loadProduct();
|
||||
this.loadUserInfo();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -124,9 +127,9 @@ export default {
|
||||
useCoupon: 0,
|
||||
fruit: 0,
|
||||
fruitData: {},
|
||||
isFruit: false,
|
||||
isloading: false,
|
||||
checked: 1,
|
||||
checked: '',
|
||||
userInfo: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -136,8 +139,45 @@ export default {
|
||||
totalPrice() {
|
||||
return ((this.product.saleprice || 0) * (this.product.buynums || 1)).toFixed(2);
|
||||
},
|
||||
// 商品金额(不含运费)
|
||||
goodsAmount() {
|
||||
return (this.product.saleprice || 0) * (this.product.buynums || 1);
|
||||
},
|
||||
// 运费
|
||||
freight() {
|
||||
return this.product.expressprice || 0;
|
||||
},
|
||||
// 积分可抵扣金额(接口返回的最高抵扣金额,乘以数量)
|
||||
maxJifenDeduct() {
|
||||
return (this.product.deductjifen || 0) * (this.product.buynums || 1);
|
||||
},
|
||||
// 会员卡可抵扣金额(接口返回的最高抵扣金额,乘以数量)
|
||||
maxQuanDeduct() {
|
||||
return (this.product.deducthuiyuanka || 0) * (this.product.buynums || 1);
|
||||
},
|
||||
// 实际积分可抵扣金额(余额够时抵接口返回金额,不够时抵余额)
|
||||
actualJifenDeduct() {
|
||||
const max = this.maxJifenDeduct;
|
||||
const available = this.userInfo.xiaofeijifen || 0;
|
||||
return Math.min(max, available, this.goodsAmount);
|
||||
},
|
||||
// 实际会员卡可抵扣金额(余额够时抵接口返回金额,不够时抵余额)
|
||||
actualQuanDeduct() {
|
||||
const max = this.maxQuanDeduct;
|
||||
const available = this.userInfo.xiaofeiquan || 0;
|
||||
return Math.min(max, available, this.goodsAmount);
|
||||
},
|
||||
// 当前选中的抵扣方式实际可抵扣金额
|
||||
actualDeduct() {
|
||||
if (!this.checked) return 0;
|
||||
return this.checked === '1' ? this.actualJifenDeduct : this.actualQuanDeduct;
|
||||
},
|
||||
// 实付金额 = 商品金额 + 运费 - 抵扣金额
|
||||
actualPay() {
|
||||
return this.goodsAmount + this.freight - this.actualDeduct;
|
||||
},
|
||||
useFruitPrice() {
|
||||
return this.totalPrice;
|
||||
return this.actualPay;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
@ -164,10 +204,17 @@ export default {
|
||||
skuname: sku?.skuname || '默认规格',
|
||||
buynums: parseInt(this.$route.query.buynums) || 1,
|
||||
typename: data.typename || '商品',
|
||||
expressprice: data.expressprice || 0
|
||||
expressprice: data.expressprice || 0,
|
||||
deductjifen: data.deductjifen || 0,
|
||||
deducthuiyuanka: data.deducthuiyuanka || 0
|
||||
};
|
||||
});
|
||||
},
|
||||
loadUserInfo() {
|
||||
this.$get('/v1/client/DUsersClient').then(res => {
|
||||
this.userInfo = res.data;
|
||||
}).catch(() => { });
|
||||
},
|
||||
onAddressConfirm(addr) {
|
||||
this.address = addr;
|
||||
},
|
||||
@ -191,7 +238,8 @@ export default {
|
||||
city: this.address.city,
|
||||
county: this.address.county,
|
||||
address: this.address.ReceiveAddress,
|
||||
isquan: this.isFruit
|
||||
ishuiyuanka: this.checked === '2',
|
||||
isjifen: this.checked === '1'
|
||||
};
|
||||
this.$post('/v1/client/FOrdersClient', form).then(data => {
|
||||
this.$showSuccessToast('提交成功');
|
||||
@ -202,10 +250,6 @@ export default {
|
||||
this.isloading = false;
|
||||
});
|
||||
},
|
||||
toggleClick() {
|
||||
if (!this.exchangePoint)
|
||||
this.isFruit = !this.isFruit;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@ -1,67 +1,63 @@
|
||||
<template>
|
||||
<BasePage :back="back" title="订单详情">
|
||||
<div class="tradelist-details" v-if="data.ordernum"
|
||||
:style="`background: url(/img/mallDetail-bg.png) no-repeat;background-size: 100% auto;`">
|
||||
:style="`background: url(/img/mallDetail-bg.jpg) no-repeat;background-size: 100% auto;`">
|
||||
<div class="top">
|
||||
<img :src="`/img/mallDetail-i${data.state}.png`">
|
||||
<img :src="`/img/mallDetail-i${data.state}.png` || '/img/mallDetail-i0.png'">
|
||||
<b style="#d2220d">{{ data.statename }}</b>
|
||||
</div>
|
||||
|
||||
<div class="_address">
|
||||
<img src="/img/tradelistDetail-i5.png">
|
||||
<div class="c">
|
||||
<div>
|
||||
{{ data.receiptrealname }}
|
||||
<span>{{ data.receiptcellphone }}</span>
|
||||
</div>
|
||||
<p>
|
||||
{{ data.receiptprovince }}{{ data.receiptcity }}{{ data.receiptcounty }}{{ data.receiptaddress
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="_goods" v-for="item in data.orderdetails" :key="item.id">
|
||||
<img :src="$file(item.proimg)">
|
||||
<div class="_goods">
|
||||
<img :src="$file(data.proimg)">
|
||||
<div class="c">
|
||||
<div class="name">
|
||||
<span>{{ item.proname }}</span>
|
||||
<span>{{ data.proname }}</span>
|
||||
</div>
|
||||
<div class="s">
|
||||
<p>{{ item.proskuname?.split(';').join('/') }}</p>
|
||||
<p>{{ data.proskuname?.split(';').join('/') }}</p>
|
||||
</div>
|
||||
<div class="price">
|
||||
<b>
|
||||
<span>¥</span>{{ item.saleprice?.toFixed(2) }}
|
||||
<span>¥</span>{{ data.proskusaleprice?.toFixed(2) }}
|
||||
</b>
|
||||
<p>x{{ item.buynums }}</p>
|
||||
<p>x{{ data.buynums }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="_detail">
|
||||
<div class="p">
|
||||
<p>
|
||||
订单号:
|
||||
<span>{{ data.ordernum }} <img @click="$copyText(data.ordernum); $showSuccessToast('复制成功')"
|
||||
src="/img/copy.png"></span>
|
||||
</p>
|
||||
<p>
|
||||
商品总数
|
||||
<span>{{data.orderdetails?.reduce((sum, sku) => sum + sku.buynums, 0)}}</span>
|
||||
</p>
|
||||
<p>
|
||||
商品总价
|
||||
<span>¥{{ data.orderoriginalmoney?.toFixed(2) }}</span>
|
||||
<span>¥{{ data.ordermoney?.toFixed(2) }}</span>
|
||||
</p>
|
||||
<p v-if="data.payjifen > 0">
|
||||
积分抵扣
|
||||
<span>-¥{{ data.payjifen?.toFixed(2) }}</span>
|
||||
</p>
|
||||
<p v-if="data.payquan > 0">
|
||||
优惠券抵扣
|
||||
会员卡额度抵扣
|
||||
<span>-¥{{ data.payquan?.toFixed(2) }}</span>
|
||||
</p>
|
||||
<p>
|
||||
实付金额
|
||||
<b style="color: #f00;">¥{{ data.needpay?.toFixed(2) }}</b>
|
||||
<b style="color: #f00;">¥{{ data.realmoney?.toFixed(2) }}</b>
|
||||
</p>
|
||||
<p v-if="data.zongsongjifen">
|
||||
赠送积分
|
||||
<span>{{ data.zongsongjifen }}</span>
|
||||
</p>
|
||||
<hr style="margin:1.67vw 0;height: 1px;border: none;background: #f5f5f580;">
|
||||
<p>
|
||||
订单号:
|
||||
<span>{{ data.ordernum }} <img @click="$copyText(data.ordernum); $showSuccessToast('复制成功')"
|
||||
src="/img/copy.png"></span>
|
||||
</p>
|
||||
<!-- <p>
|
||||
商品总数
|
||||
<span>{{ data.buynums }}</span>
|
||||
</p> -->
|
||||
|
||||
<p>
|
||||
下单时间
|
||||
<span>{{ $formatGMT(data.addtime, 'yyyy-MM-dd HH:mm:ss') }}</span>
|
||||
@ -117,6 +113,7 @@ export default {
|
||||
try {
|
||||
const res = await this.$get(`/v1/client/FOrdersClient/${this.ordernum}`);
|
||||
this.data = res.data;
|
||||
this.data.orderdetails = res.data.items;
|
||||
} catch (err) {
|
||||
this.$showFailToast(err.message || '加载失败');
|
||||
}
|
||||
|
||||
@ -41,14 +41,14 @@
|
||||
<p>x{{ item.buynums }}</p>
|
||||
</div>
|
||||
<div class="concession">
|
||||
<span>¥{{ item.realmoney?.toFixed(2) }}</span>
|
||||
<span>¥{{ item.proskusaleprice?.toFixed(2) }}</span>
|
||||
<p v-if="item.discountratio">{{ item.discountratio }}%让利</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="price">
|
||||
实付:<span>¥<b>9999.05</b></span>
|
||||
实付:<span>¥<b>{{ item.realmoney?.toFixed(2) }}</b></span>
|
||||
</div>
|
||||
|
||||
<div class="state_box">
|
||||
@ -57,10 +57,11 @@
|
||||
</b>
|
||||
|
||||
<div class="btn_box">
|
||||
<button v-if="item.state === 0">取消订单</button>
|
||||
<button v-if="item.state === 3">查看物流</button>
|
||||
<button v-if="item.state === 0" @click="cancelTrade(item)">取消订单</button>
|
||||
<!-- <button v-if="item.state === 3">查看物流</button> -->
|
||||
<button @click="$navigate(`TradeDetail?ordernum=${item.ordernum}`)">查看详情</button>
|
||||
<button v-if="item.state === 0">立即支付</button>
|
||||
<button v-if="item.state === 3" @click="confirmReceipt(item)">确认收货</button>
|
||||
<button v-if="item.state === 0" @click="$navigate(`Pay?ordernum=${item.ordernum}`)">立即支付</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -85,7 +86,7 @@
|
||||
</div>
|
||||
|
||||
<div class="price">
|
||||
实付:<span>¥<b>{{ item.ordermoney?.toFixed(2) }}</b></span>
|
||||
实付:<span>¥<b>{{ item.realmoney?.toFixed(2) }}</b></span>
|
||||
</div>
|
||||
|
||||
<div class="detail_box">
|
||||
@ -111,7 +112,7 @@
|
||||
</b>
|
||||
|
||||
<div class="btn_box">
|
||||
<button @click="$navigate(`CheckoutTrade?ordernum=${item.ordernum}&type=user`)">查看详情</button>
|
||||
<button @click="$navigate(`MerchantTradeDetail?id=${item.ordernum}&type=user`)">查看详情</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -198,6 +199,18 @@ export default {
|
||||
},
|
||||
onRefresh() {
|
||||
},
|
||||
confirmReceipt(item) {
|
||||
this.$showConfirmDialog({
|
||||
title: "是否确认收货",
|
||||
}).then(() => {
|
||||
this.$put(`/v1/client/FOrdersClient/${item.ordernum}/receipt`).then(() => {
|
||||
this.$showSuccessToast('确认收货成功');
|
||||
this.$refs.baseList?.refresh();
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || '操作失败');
|
||||
});
|
||||
}).catch(() => { });
|
||||
},
|
||||
cancelTrade(item) {
|
||||
this.$showConfirmDialog({
|
||||
title: "是否确认取消订单",
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<BasePage>
|
||||
<div class="checkout">
|
||||
<div class="checkout" v-if="data.shop && data.shop.shopname">
|
||||
<div class="shopinfo">
|
||||
<img class="icon" :src="$file()" alt="">
|
||||
<img class="icon" :src="$file(data.shop.shopimg)" alt="">
|
||||
|
||||
<div class="inf">
|
||||
<b>完美日记旗舰店</b>
|
||||
<b>{{ data.shop.shopname }}</b>
|
||||
<div class="hl">
|
||||
<img src="/img/buybill.png" alt="">
|
||||
<span>20%</span>
|
||||
<span>{{ data.shop.feeratio }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -19,27 +19,29 @@
|
||||
</b>
|
||||
|
||||
<div class="input_box">
|
||||
¥<input type="number" placeholder="请输入买单金额">
|
||||
¥<input type="number" v-model="req.ordermoney" placeholder="请输入买单金额" @input="calcDeduction">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="deduction_box">
|
||||
<van-radio-group v-model="checked">
|
||||
<van-cell-group inset center>
|
||||
<van-cell center title="积分抵扣" icon="/img/point_icon.png" clickable @click="checked = '1'">
|
||||
<van-cell center title="积分抵扣" icon="/img/point_icon.png">
|
||||
<template #right-icon>
|
||||
<span class="canuse">25000.00可用</span>
|
||||
<span class="cantuse">暂无可用</span>
|
||||
<span v-if="checked === '1'" class="use">-500.00</span>
|
||||
<van-radio name="1" />
|
||||
<span class="cantuse" v-if="!data.user.xiaofeijifen || data.user.xiaofeijifen <= 0">暂无可用</span>
|
||||
<span class="canuse" v-else-if="checked !== '1'">{{ data.user.xiaofeijifen?.toFixed(2) }}可用</span>
|
||||
<span class="use" v-else>-{{ jifenDeduction.toFixed(2) }}</span>
|
||||
<van-radio name="1" @click="toggleDeduction('jifen')"
|
||||
:disabled="!data.user.xiaofeijifen || data.user.xiaofeijifen <= 0" />
|
||||
</template>
|
||||
</van-cell>
|
||||
<van-cell title="会员卡抵扣" icon="/img/vip_icon.png" clickable @click="checked = '2'">
|
||||
<van-cell title="会员卡抵扣" icon="/img/vip_icon.png">
|
||||
<template #right-icon>
|
||||
<span class="canuse">25000.00可用</span>
|
||||
<span class="cantuse">暂无可用</span>
|
||||
<span v-if="checked === '2'" class="use">-500.00</span>
|
||||
<van-radio name="2" />
|
||||
<span class="cantuse" v-if="!data.user.xiaofeiquan || data.user.xiaofeiquan <= 0">暂无可用</span>
|
||||
<span class="canuse" v-else-if="checked !== '2'">{{ data.user.xiaofeiquan?.toFixed(2) }}可用</span>
|
||||
<span class="use" v-else>-{{ quanDeduction.toFixed(2) }}</span>
|
||||
<van-radio name="2" @click="toggleDeduction('quan')"
|
||||
:disabled="!data.user.xiaofeiquan || data.user.xiaofeiquan <= 0" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
@ -50,11 +52,11 @@
|
||||
<p>
|
||||
实付
|
||||
</p>
|
||||
<span>¥<b>500.00</b></span>
|
||||
<span>¥<b>{{ actualPay.toFixed(2) }}</b></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="payfor">
|
||||
<button class="payfor" @click="toPay">
|
||||
去支付
|
||||
</button>
|
||||
</div>
|
||||
@ -65,7 +67,74 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
checked: '1',
|
||||
checked: '',
|
||||
id: this.$route.query.id,
|
||||
data: {
|
||||
user: {},
|
||||
shop: {}
|
||||
},
|
||||
req: {
|
||||
ordermoney: '',
|
||||
"isjifen": false,
|
||||
"ishuiyuanka": false,
|
||||
"shopuserid": ''
|
||||
},
|
||||
jifenDeduction: 0,
|
||||
quanDeduction: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
actualPay() {
|
||||
let pay = this.req.ordermoney || 0;
|
||||
if (this.checked === '1') {
|
||||
pay -= this.jifenDeduction;
|
||||
} else if (this.checked === '2') {
|
||||
pay -= this.quanDeduction;
|
||||
}
|
||||
return Math.max(0, pay);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getShopInfo();
|
||||
},
|
||||
methods: {
|
||||
getShopInfo() {
|
||||
this.$get('/v1/client/DUsersClient').then(res => {
|
||||
this.data.user = res.data;
|
||||
})
|
||||
if (this.id)
|
||||
this.$get(`/v1/client/DShopsClient/${this.id}`).then(res => {
|
||||
console.log(res);
|
||||
this.data.shop = res.data;
|
||||
this.req.shopuserid = res.data.userid;
|
||||
})
|
||||
},
|
||||
toggleDeduction(type) {
|
||||
if (type === 'jifen') {
|
||||
if (!this.data.user.xiaofeijifen || this.data.user.xiaofeijifen <= 0) return;
|
||||
this.checked = '1'
|
||||
this.req.isjifen = this.checked === '1';
|
||||
this.req.ishuiyuanka = false;
|
||||
} else if (type === 'quan') {
|
||||
if (!this.data.user.xiaofeiquan || this.data.user.xiaofeiquan <= 0) return;
|
||||
this.checked = '2';
|
||||
this.req.ishuiyuanka = this.checked === '2';
|
||||
this.req.isjifen = false;
|
||||
}
|
||||
},
|
||||
calcDeduction() {
|
||||
const money = this.req.ordermoney || 0;
|
||||
// 积分可抵扣值最大为买单金额的20%的50% = 买单金额 * 10%
|
||||
this.jifenDeduction = Math.min(money * 0.1, this.data.user.xiaofeijifen || 0);
|
||||
// 会员卡余额最高是买单金额的20%的40% = 买单金额 * 8%
|
||||
this.quanDeduction = Math.min(money * 0.08, this.data.user.xiaofeiquan || 0);
|
||||
},
|
||||
toPay() {
|
||||
this.req.isjifen = this.checked === '1';
|
||||
this.req.ishuiyuanka = this.checked === '2';
|
||||
this.$post('/v1/client/FOrdersshopClient', this.req).then(res => {
|
||||
this.$navigate(`Pay?ordernum=${res.data}`)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
<div class="ewm_bg" style="font-family: 'PingFang SC'">
|
||||
<div class="d1">
|
||||
<div class="w100">
|
||||
<!-- <img id="invite_bg" :src="$file(data.Icon)" /> -->
|
||||
<img id="invite_bg" src="/img/invite_bg.jpg" crossorigin="anonymous">
|
||||
<img id="invite_bg" :src="$file($datadic.getContent('code_yqmbj'))" crossorigin="anonymous" />
|
||||
<!-- <img id="invite_bg" src="/img/invite_bg.jpg" crossorigin="anonymous"> -->
|
||||
</div>
|
||||
<img class="icon" :src="$file(data.Avatar) + '?t=1'" crossorigin="anonymous">
|
||||
<div class="name">
|
||||
|
||||
@ -269,7 +269,10 @@ export default {
|
||||
this.$ls.set('isshop', data.data.isshop);
|
||||
this.$ls.set('iscenter', data.data.col2);
|
||||
this.$ls.set('user_id', data.data.id);
|
||||
this.$ls.set('huiyuankaid', data.data.huiyuankaid)
|
||||
this.$ls.set('huiyuankaid', data.data.huiyuankaid);
|
||||
this.$ls.set('user_cv', data.data.xiaofeiquan);
|
||||
this.$ls.set('user_point', data.data.xiaofeijifen);
|
||||
|
||||
}),
|
||||
]
|
||||
)
|
||||
@ -317,40 +320,19 @@ export default {
|
||||
this.tempData.userimg = file.file;
|
||||
},
|
||||
save() {
|
||||
const fd = new FormData();
|
||||
fd.append('nickname', this.tempData.nickname);
|
||||
fd.append('cellphone', this.tempData.cellphone);
|
||||
if (this.tempData.userimg instanceof File) {
|
||||
const fd = new FormData();
|
||||
fd.append('file', this.tempData.userimg);
|
||||
fetch(`${import.meta.env.VITE_API_URL}/v1/client/DUsersClient/userimg`, {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` },
|
||||
body: fd,
|
||||
}).then(res => res.json()).then(res => {
|
||||
if (res.status !== 200) throw new Error(res.message);
|
||||
this.tempData.userimg = res.data;
|
||||
}).then(() => {
|
||||
return this.$put('/v1/client/DUsersClient', {
|
||||
nickname: this.tempData.nickname,
|
||||
cellphone: this.tempData.cellphone,
|
||||
});
|
||||
}).then(() => {
|
||||
this.$showSuccessToast('保存成功');
|
||||
this.showInfo = false;
|
||||
window.location.reload();
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || err.message || '保存失败');
|
||||
});
|
||||
} else {
|
||||
this.$put('/v1/client/DUsersClient', {
|
||||
nickname: this.tempData.nickname,
|
||||
cellphone: this.tempData.cellphone,
|
||||
}).then(() => {
|
||||
this.$showSuccessToast('保存成功');
|
||||
this.showInfo = false;
|
||||
window.location.reload();
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || '保存失败');
|
||||
});
|
||||
}
|
||||
this.$postForm('/v1/client/DUsersClient/userinfo', fd).then(() => {
|
||||
this.$showSuccessToast('保存成功');
|
||||
this.showInfo = false;
|
||||
window.location.reload();
|
||||
}).catch(err => {
|
||||
this.$showFailToast(err.message || '保存失败');
|
||||
});
|
||||
},
|
||||
valid(field, msg) {
|
||||
if (!field) {
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
<van-action-sheet v-model:show="showTerm" safe-area-inset-bottom :title="`礼包业绩说明`" closeable
|
||||
style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||||
<div class="w100 html" v-html="data.Term" />
|
||||
<div class="w100 html" v-html="$datadic.getContent('code_lbyjsm')" />
|
||||
</van-action-sheet>
|
||||
</div>
|
||||
</BasePage>
|
||||
|
||||
@ -282,7 +282,7 @@
|
||||
</van-popup>
|
||||
<van-action-sheet v-model:show="showTerm" safe-area-inset-bottom :title="`${this.$route.meta.title}说明`"
|
||||
closeable style="min-height: 50%; padding: 0px 10px 10px 10px">
|
||||
<div class="w100 html" v-html="data.Term" />
|
||||
<div class="w100 html" v-html="$datadic.getContent('code_yesm')" />
|
||||
</van-action-sheet>
|
||||
|
||||
</BasePage>
|
||||
|
||||