Compare commits

..

10 Commits

Author SHA1 Message Date
63a7f0ccf4 feat: 5.26 update 2026-05-27 08:30:36 +08:00
107ebd7a1d html!!!save 2026-05-26 21:25:23 +08:00
4f22196d68 save1 2026-05-26 21:10:30 +08:00
a496b528c3 save 2026-05-26 18:46:23 +08:00
1585a8c74b update 2026-05-25 16:13:10 +08:00
7381a330b2 fix: 5.23文档修复 2026-05-25 15:41:09 +08:00
6a20d18c12 feat: 5.22 update 2026-05-23 08:36:36 +08:00
736adae554 feat: 5.21 update 2026-05-21 17:23:54 +08:00
7047232930 save 2026-05-21 09:56:45 +08:00
4a992c9847 生成图片方法更改 2026-05-21 09:09:54 +08:00
58 changed files with 1962 additions and 1632 deletions

View File

@ -1,2 +1,2 @@
VITE_API_URL = "https://m.taigurun.cn" VITE_API_URL = "https://m.taigurun.cn"
VITE_OSS_URL = "https://m.taigurun.cn" VITE_OSS_URL = "https://m.taigurun.cn/uploads"

View File

@ -1,2 +1,2 @@
VITE_API_URL = "https://m.taigurun.cn" VITE_API_URL = "https://m.taigurun.cn"
VITE_OSS_URL = "https://m.taigurun.cn" VITE_OSS_URL = "https://m.taigurun.cn/uploads"

View File

@ -12,7 +12,6 @@
<body> <body>
<div id="app" v-cloak></div> <div id="app" v-cloak></div>
<script src="/js/echarts.min.js"></script> <script src="/js/echarts.min.js"></script>
<script src="/js/html2canvas.min.js"></script>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
<style> <style>
[v-cloak] { [v-cloak] {

57
package-lock.json generated
View File

@ -10,6 +10,8 @@
"dependencies": { "dependencies": {
"@vant/area-data": "^2.1.0", "@vant/area-data": "^2.1.0",
"@zxing/library": "^0.21.3", "@zxing/library": "^0.21.3",
"html-to-image": "^1.11.13",
"html2canvas": "^1.4.1",
"image-conversion": "^2.1.1", "image-conversion": "^2.1.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"postcss-px-to-viewport-8-plugin": "^1.2.5", "postcss-px-to-viewport-8-plugin": "^1.2.5",
@ -664,6 +666,15 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/birpc": { "node_modules/birpc": {
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz", "resolved": "https://registry.npmmirror.com/birpc/-/birpc-2.9.0.tgz",
@ -710,6 +721,15 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
@ -822,6 +842,25 @@
"integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/html-to-image": {
"version": "1.11.13",
"resolved": "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.13.tgz",
"integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==",
"license": "MIT"
},
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"license": "MIT",
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -1636,6 +1675,15 @@
"url": "https://github.com/sponsors/mesqueeb" "url": "https://github.com/sponsors/mesqueeb"
} }
}, },
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"license": "MIT",
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.16", "version": "0.2.16",
"resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.16.tgz", "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.16.tgz",
@ -1818,6 +1866,15 @@
"node": "^20.19.0 || >=22.12.0" "node": "^20.19.0 || >=22.12.0"
} }
}, },
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"license": "MIT",
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
},
"node_modules/vant": { "node_modules/vant": {
"version": "4.9.24", "version": "4.9.24",
"resolved": "https://registry.npmmirror.com/vant/-/vant-4.9.24.tgz", "resolved": "https://registry.npmmirror.com/vant/-/vant-4.9.24.tgz",

View File

@ -11,6 +11,8 @@
"dependencies": { "dependencies": {
"@vant/area-data": "^2.1.0", "@vant/area-data": "^2.1.0",
"@zxing/library": "^0.21.3", "@zxing/library": "^0.21.3",
"html-to-image": "^1.11.13",
"html2canvas": "^1.4.1",
"image-conversion": "^2.1.1", "image-conversion": "^2.1.1",
"pinia": "^3.0.4", "pinia": "^3.0.4",
"postcss-px-to-viewport-8-plugin": "^1.2.5", "postcss-px-to-viewport-8-plugin": "^1.2.5",

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
public/img/address.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/img/point.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

BIN
public/img/pointmall_bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -1,235 +0,0 @@
/*
* Canvas2Image v0.1
* Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk
* MIT License [http://www.opensource.org/licenses/mit-license.php]
*/
var Canvas2Image = (function() {
// check if we have canvas support
var bHasCanvas = false;
var oCanvas = document.createElement("canvas");
if (oCanvas.getContext("2d")) {
bHasCanvas = true;
}
// no canvas, bail out.
if (!bHasCanvas) {
return {
saveAsBMP : function(){},
saveAsPNG : function(){},
saveAsJPEG : function(){}
}
}
var bHasImageData = !!(oCanvas.getContext("2d").getImageData);
var bHasDataURL = !!(oCanvas.toDataURL);
var bHasBase64 = !!(window.btoa);
var strDownloadMime = "image/octet-stream";
// ok, we're good
var readCanvasData = function(oCanvas) {
var iWidth = parseInt(oCanvas.width);
var iHeight = parseInt(oCanvas.height);
return oCanvas.getContext("2d").getImageData(0,0,iWidth,iHeight);
}
// base64 encodes either a string or an array of charcodes
var encodeData = function(data) {
var strData = "";
if (typeof data == "string") {
strData = data;
} else {
var aData = data;
for (var i=0;i<aData.length;i++) {
strData += String.fromCharCode(aData[i]);
}
}
return btoa(strData);
}
// creates a base64 encoded string containing BMP data
// takes an imagedata object as argument
var createBMP = function(oData) {
var aHeader = [];
var iWidth = oData.width;
var iHeight = oData.height;
aHeader.push(0x42); // magic 1
aHeader.push(0x4D);
var iFileSize = iWidth*iHeight*3 + 54; // total header size = 54 bytes
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
aHeader.push(iFileSize % 256); iFileSize = Math.floor(iFileSize / 256);
aHeader.push(iFileSize % 256);
aHeader.push(0); // reserved
aHeader.push(0);
aHeader.push(0); // reserved
aHeader.push(0);
aHeader.push(54); // dataoffset
aHeader.push(0);
aHeader.push(0);
aHeader.push(0);
var aInfoHeader = [];
aInfoHeader.push(40); // info header size
aInfoHeader.push(0);
aInfoHeader.push(0);
aInfoHeader.push(0);
var iImageWidth = iWidth;
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
aInfoHeader.push(iImageWidth % 256); iImageWidth = Math.floor(iImageWidth / 256);
aInfoHeader.push(iImageWidth % 256);
var iImageHeight = iHeight;
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
aInfoHeader.push(iImageHeight % 256); iImageHeight = Math.floor(iImageHeight / 256);
aInfoHeader.push(iImageHeight % 256);
aInfoHeader.push(1); // num of planes
aInfoHeader.push(0);
aInfoHeader.push(24); // num of bits per pixel
aInfoHeader.push(0);
aInfoHeader.push(0); // compression = none
aInfoHeader.push(0);
aInfoHeader.push(0);
aInfoHeader.push(0);
var iDataSize = iWidth*iHeight*3;
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
aInfoHeader.push(iDataSize % 256); iDataSize = Math.floor(iDataSize / 256);
aInfoHeader.push(iDataSize % 256);
for (var i=0;i<16;i++) {
aInfoHeader.push(0); // these bytes not used
}
var iPadding = (4 - ((iWidth * 3) % 4)) % 4;
var aImgData = oData.data;
var strPixelData = "";
var y = iHeight;
do {
var iOffsetY = iWidth*(y-1)*4;
var strPixelRow = "";
for (var x=0;x<iWidth;x++) {
var iOffsetX = 4*x;
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+2]);
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX+1]);
strPixelRow += String.fromCharCode(aImgData[iOffsetY+iOffsetX]);
}
for (var c=0;c<iPadding;c++) {
strPixelRow += String.fromCharCode(0);
}
strPixelData += strPixelRow;
} while (--y);
var strEncoded = encodeData(aHeader.concat(aInfoHeader)) + encodeData(strPixelData);
return strEncoded;
}
// sends the generated file to the client
var saveFile = function(strData) {
document.location.href = strData;
}
var makeDataURI = function(strData, strMime) {
return "data:" + strMime + ";base64," + strData;
}
// generates a <img> object containing the imagedata
var makeImageObject = function(strSource) {
var oImgElement = document.createElement("img");
oImgElement.src = strSource;
return oImgElement;
}
var scaleCanvas = function(oCanvas, iWidth, iHeight) {
if (iWidth && iHeight) {
var oSaveCanvas = document.createElement("canvas");
oSaveCanvas.width = iWidth;
oSaveCanvas.height = iHeight;
oSaveCanvas.style.width = iWidth+"px";
oSaveCanvas.style.height = iHeight+"px";
var oSaveCtx = oSaveCanvas.getContext("2d");
oSaveCtx.drawImage(oCanvas, 0, 0, oCanvas.width, oCanvas.height, 0, 0, iWidth, iHeight);
return oSaveCanvas;
}
return oCanvas;
}
return {
saveAsPNG : function(oCanvas, bReturnImg, iWidth, iHeight) {
if (!bHasDataURL) {
return false;
}
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
var strData = oScaledCanvas.toDataURL("image/png");
if (bReturnImg) {
return makeImageObject(strData);
} else {
saveFile(strData.replace("image/png", strDownloadMime));
}
return true;
},
saveAsJPEG : function(oCanvas, bReturnImg, iWidth, iHeight) {
if (!bHasDataURL) {
return false;
}
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
var strMime = "image/jpeg";
var strData = oScaledCanvas.toDataURL(strMime);
// check if browser actually supports jpeg by looking for the mime type in the data uri.
// if not, return false
if (strData.indexOf(strMime) != 5) {
return false;
}
if (bReturnImg) {
return makeImageObject(strData);
} else {
saveFile(strData.replace(strMime, strDownloadMime));
}
return true;
},
saveAsBMP : function(oCanvas, bReturnImg, iWidth, iHeight) {
if (!(bHasImageData && bHasBase64)) {
return false;
}
var oScaledCanvas = scaleCanvas(oCanvas, iWidth, iHeight);
var oData = readCanvasData(oScaledCanvas);
var strImgData = createBMP(oData);
if (bReturnImg) {
return makeImageObject(makeDataURI(strImgData, "image/bmp"));
} else {
saveFile(makeDataURI(strImgData, strDownloadMime));
}
return true;
}
};
})();

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,19 @@
<template> <template>
<router-view /> <router-view v-slot="{ Component, route }">
</template> <keep-alive :include="cacheList">
<component :is="Component" :key="route.name" />
</keep-alive>
</router-view>
</template>
<script>
export default {
name: 'App',
computed: {
cacheList() {
const routes = this.$router.getRoutes()
return routes.filter(r => r.meta.cache).map(r => r.name)
}
}
}
</script>

View File

@ -5,15 +5,15 @@
const BASE_URL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:3001'; const BASE_URL = import.meta.env.VITE_API_URL || 'http://127.0.0.1:3001';
// 默认超时时间 15 秒 // 默认超时时间 15 秒
const DEFAULT_TIMEOUT = 15000; const DEFAULT_TIMEOUT = 30000;
// 错误码定义 // 错误码定义
const ERR_CODE = { const ERR_CODE = {
SUCCESS: 200, SUCCESS: 200,
UNAUTHORIZED: 401, UNAUTHORIZED: 401,
FORBIDDEN: 403, FORBIDDEN: 403,
NOT_FOUND: 404, NOT_FOUND: 404,
SERVER_ERROR: 500, SERVER_ERROR: 500,
}; };
function getHeaders(contentType = 'application/json') { function getHeaders(contentType = 'application/json') {
@ -63,6 +63,11 @@ function handleResponse(response) {
* 处理 token 失效 * 处理 token 失效
*/ */
function handleUnauthorized() { function handleUnauthorized() {
// 如果 localStorage 中本来就没有 token说明是未登录状态不做处理
if (!localStorage.getItem('member_token')) {
return;
}
// 清除登录状态 // 清除登录状态
localStorage.removeItem('member_token'); localStorage.removeItem('member_token');
localStorage.removeItem('member_username'); localStorage.removeItem('member_username');

View File

@ -5,8 +5,10 @@ export function isLogin() {
export function formatGMT(t, format) { export function formatGMT(t, format) {
// 传入的格式 "2026-04-01T14:06:56.5879258" // 传入的格式 "2026-04-01T14:06:56.5879258"
if (!t) if (!t)
return return ''
const index = t.indexOf('T'); const index = t.indexOf('T');
if (index === -1)
return t
const date = t.substring(0, index).split('-') const date = t.substring(0, index).split('-')
const time = t.substring(index + 1).split(':') const time = t.substring(index + 1).split(':')
@ -20,7 +22,7 @@ export function formatGMT(t, format) {
const sctime = time[2].split('.') const sctime = time[2].split('.')
const second = sctime[0].padStart(2, '0') const second = sctime[0].padStart(2, '0')
const millisecond = sctime[1].substring(0, 2).padStart(2, '0') const millisecond = sctime[1] ? sctime[1].substring(0, 2).padStart(2, '0') : '00'
return format return format
.replace('yyyy', year) .replace('yyyy', year)
.replace('MM', month) .replace('MM', month)

View File

@ -25,6 +25,7 @@ export default {
pageSize: { type: Number, default: 10 }, pageSize: { type: Number, default: 10 },
finishedText: { type: String, default: '没有更多了' }, finishedText: { type: String, default: '没有更多了' },
parseData: { type: Function, default: (res) => res.data.items }, parseData: { type: Function, default: (res) => res.data.items },
cacheKey: { type: String, default: '' },
}, },
data() { data() {
return { return {
@ -34,6 +35,8 @@ export default {
page: 1, page: 1,
requestId: 0, requestId: 0,
observer: null, observer: null,
_cacheKey: null,
_dataCache: null,
} }
}, },
emits: ['update:list', 'load', 'refresh'], emits: ['update:list', 'load', 'refresh'],
@ -50,6 +53,19 @@ export default {
deep: true deep: true
} }
}, },
created() {
if (this.cacheKey) {
const cached = sessionStorage.getItem(this.cacheKey)
if (cached) {
try {
const { list, page, finished, params } = JSON.parse(cached)
if (JSON.stringify(this.params) === JSON.stringify(params)) {
this._dataCache = { list, page, finished }
}
} catch (e) { }
}
}
},
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
this.observer = new IntersectionObserver((entries) => { this.observer = new IntersectionObserver((entries) => {
@ -66,6 +82,14 @@ export default {
beforeUnmount() { beforeUnmount() {
this.observer?.disconnect() this.observer?.disconnect()
window.removeEventListener('scroll', this.onWindowScroll) window.removeEventListener('scroll', this.onWindowScroll)
if (this.cacheKey && this.list.length > 0) {
sessionStorage.setItem(this.cacheKey, JSON.stringify({
list: this.list,
page: this.page,
finished: this.finished,
params: this.params
}))
}
}, },
methods: { methods: {
onWindowScroll() { onWindowScroll() {
@ -79,6 +103,13 @@ export default {
}, },
loadMore() { loadMore() {
if (this.loading) return if (this.loading) return
if (this._dataCache && this._dataCache.params && JSON.stringify(this.params) === JSON.stringify(this._dataCache.params)) {
this.list = this._dataCache.list
this.page = this._dataCache.page
this.finished = this._dataCache.finished
this.loading = false
return
}
const currentRequestId = this.requestId const currentRequestId = this.requestId
this.loading = true this.loading = true
const request = this.method === 'get' ? this.$get : this.$post const request = this.method === 'get' ? this.$get : this.$post
@ -123,6 +154,8 @@ export default {
<style scoped> <style scoped>
.base-list { .base-list {
min-height: 200px; min-height: 200px;
display: flex;
flex-direction: column;
} }
.loading-wrap { .loading-wrap {
@ -130,6 +163,20 @@ export default {
justify-content: center; justify-content: center;
align-items: center; align-items: center;
padding: 20px; padding: 20px;
width: 100%;
box-sizing: border-box;
}
.loading-wrap :deep(.van-loading) {
display: inline-flex;
align-items: center;
flex-direction: row;
white-space: nowrap;
float: none !important;
}
.loading-wrap :deep(.van-loading__text) {
float: none !important;
} }
.finished-text, .finished-text,

View File

@ -1,7 +1,8 @@
<template> <template>
<!-- $isWechat() ? '' : title || --> <!-- -->
<van-nav-bar fixed :title="title || $route.meta.title" :safe-area-inset-top="true" placeholder left-arrow left-text="返回" <van-nav-bar fixed :title="$isWechat() ? '' : title || $route.meta.title" :safe-area-inset-top="true" placeholder
@click-left="back ? back() : $router.back();" :class="$isWechat() ? 'wechat-nav' : ''"> left-arrow left-text="返回" @click-left="back ? back() : $router.back();"
:class="$isWechat() ? 'wechat-nav' : ''">
<template #right> <template #right>
<slot v-if="$slots.right" name="right" /> <slot v-if="$slots.right" name="right" />
<van-icon v-else-if="!hideHome" name="wap-home-o" size="18" @click="$router.replace('/')" /> <van-icon v-else-if="!hideHome" name="wap-home-o" size="18" @click="$router.replace('/')" />

View File

@ -87,6 +87,11 @@ export default {
}) })
}, },
getPermissions() { getPermissions() {
if (!this.$isLogin()) {
// this.$showFailToast('')
// location.replace('#/Login')
return
}
this.$get('/v1/client/DUsersClient').then(res => { this.$get('/v1/client/DUsersClient').then(res => {
this.PermissionsData = { this.PermissionsData = {
id: res.data.id, id: res.data.id,
@ -101,7 +106,7 @@ export default {
this.$showConfirmDialog({ this.$showConfirmDialog({
title: "确认退出?", title: "确认退出?",
}).then(() => { }).then(() => {
this.$ls.remove('token'); this.$ls.remove('member_token');
location.replace('#/Login'); location.replace('#/Login');
setTimeout(() => { setTimeout(() => {
location.reload(); location.reload();

View File

@ -53,9 +53,24 @@ datadicStore.init()
const userStore = useUserStore() const userStore = useUserStore()
// 全局前置守卫
router.beforeEach((to, from, next) => {
if (to.meta.title) {
document.title = to.meta.title;
}
if (to.meta.noLogin) {
return next();
}
const isLogin = userStore.isLogin;
if (!isLogin) {
return next({ name: 'Login', query: { redirect: to.fullPath } });
}
next();
});
app.config.globalProperties.$datadic = { app.config.globalProperties.$datadic = {
get: (code) => dictCache[code] || null, get: (code) => dictCache[code] || null,
getContent: (code) => dictCache[code] ? dictCache[code].contents : '', getContent: (code) => dictCache[code] ? dictCache[code].contents : '',
} }
app.config.globalProperties.$userInfo = userStore.getUserInfo; app.config.globalProperties.$userInfo = userStore.getUserInfo;

View File

@ -18,7 +18,7 @@ const routes = [
path: '/Gift', path: '/Gift',
name: 'Gift', name: 'Gift',
component: () => import('./views/Tabbars/Gift.vue'), component: () => import('./views/Tabbars/Gift.vue'),
meta: { title: '礼区', cache: true } meta: { title: '礼区', cache: true }
}, },
{ {
path: '/Mall', path: '/Mall',
@ -26,6 +26,12 @@ const routes = [
component: () => import('./views/Tabbars/Mall.vue'), component: () => import('./views/Tabbars/Mall.vue'),
meta: { title: '商城', cache: true } meta: { title: '商城', cache: true }
}, },
{
path: '/PointMall',
name: 'PointMall',
component: () => import('./views/Tabbars/PointMall.vue'),
meta: { title: '积分区', cache: true }
},
{ {
path: '/My', path: '/My',
name: 'My', name: 'My',
@ -35,6 +41,14 @@ const routes = [
], ],
meta: { noLogin: true } meta: { noLogin: true }
}, },
{
path: '/WxCallback',
name: 'WxCallback',
component: () => import('./views/Account/WxCallback.vue'),
meta: {
noLogin: true,
}
},
{ {
path: '/Login', path: '/Login',
name: 'Login', name: 'Login',
@ -67,7 +81,7 @@ const routes = [
name: 'CV', name: 'CV',
component: () => import('./views/User/Wallet/CV.vue'), component: () => import('./views/User/Wallet/CV.vue'),
meta: { meta: {
title: '会员卡额度', title: '会员卡',
} }
}, },
{ {
@ -134,7 +148,7 @@ const routes = [
path: '/Invite', path: '/Invite',
name: 'Invite', name: 'Invite',
component: () => import('./views/User/Invite.vue'), component: () => import('./views/User/Invite.vue'),
meta: { title: '邀请码' } meta: { title: '邀请码', cache: true }
}, },
{ {
path: '/Team', path: '/Team',
@ -176,43 +190,43 @@ const routes = [
path: '/Business_School', path: '/Business_School',
name: 'Business_School', name: 'Business_School',
component: () => import('./views/Platform/Business_School.vue'), component: () => import('./views/Platform/Business_School.vue'),
meta: { title: '商学院' } meta: { title: '商学院', noLogin: true }
}, },
{ {
path: '/School_detail', path: '/School_detail',
name: 'School_detail', name: 'School_detail',
component: () => import('./views/Platform/School_Detail.vue'), component: () => import('./views/Platform/School_Detail.vue'),
meta: { title: '商学院详情' } meta: { title: '商学院详情', noLogin: true, }
}, },
{ {
path: '/School_Category', path: '/School_Category',
name: 'School_Category', name: 'School_Category',
component: () => import('./views/Platform/School_Category.vue'), component: () => import('./views/Platform/School_Category.vue'),
meta: { title: '商学院分类' } meta: { title: '商学院分类', noLogin: true, }
}, },
{ {
path: '/Column', path: '/Column',
name: 'Column', name: 'Column',
component: () => import('./views/Platform/Column.vue'), component: () => import('./views/Platform/Column.vue'),
meta: { title: '栏目列表' } meta: { title: '栏目列表', noLogin: true }
}, },
{ {
path: '/ColumnDetail', path: '/ColumnDetail',
name: 'ColumnDetail', name: 'ColumnDetail',
component: () => import('./views/Platform/ColumnDetail.vue'), component: () => import('./views/Platform/ColumnDetail.vue'),
meta: { title: '栏目详情' } meta: { title: '栏目详情', noLogin: true }
}, },
{ {
path: '/GoodsDetail', path: '/GoodsDetail',
name: 'GoodsDetail', name: 'GoodsDetail',
component: () => import('./views/Goods/GoodsDetail.vue'), component: () => import('./views/Goods/GoodsDetail.vue'),
meta: { title: '商品详情' } meta: { title: '商品详情', noLogin: true, }
}, },
{ {
path: '/Category', path: '/Category',
name: 'Category', name: 'Category',
component: () => import('./views/Goods/Category.vue'), component: () => import('./views/Goods/Category.vue'),
meta: { title: '全部分类' } meta: { title: '全部分类', noLogin: true, }
}, },
{ {
path: '/Operations', path: '/Operations',
@ -268,6 +282,12 @@ const routes = [
component: () => import('./views/Merchant/MerchantIntroduction.vue'), component: () => import('./views/Merchant/MerchantIntroduction.vue'),
meta: { title: '商家资料' } meta: { title: '商家资料' }
}, },
{
path: '/PayCode',
name: 'PayCode',
component: () => import('./views/Merchant/PayCode.vue'),
meta: { title: '收款码' }
},
{ {
path: '/QrReader', path: '/QrReader',
name: 'QrReader', name: 'QrReader',
@ -304,6 +324,9 @@ const routes = [
path: '/SyncAuth', path: '/SyncAuth',
name: 'SyncAuth', name: 'SyncAuth',
component: () => import('./views/SyncAuth.vue'), component: () => import('./views/SyncAuth.vue'),
meta: {
noLogin: true,
}
}, },
{ {
path: '/404', path: '/404',
@ -334,19 +357,3 @@ const router = createRouter({
}) })
export default router export default router
// 全局前置守卫
router.beforeEach((to, from, next) => {
const userStore = useUserStore();
if (to.meta.title) {
document.title = to.meta.title;
}
if (to.meta.noLogin) {
return next();
}
const isLogin = userStore.isLogin;
if (!isLogin) {
return next({ name: 'Login', query: { redirect: to.fullPath } });
}
next();
});

View File

@ -1,9 +1,10 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { get } from '@/api/http' import { get } from '@/api/http'
// Module level cache for quick access
export let dictCache = {} export let dictCache = {}
const SESSION_KEY = 'dict_cache'
export const useDatadicStore = defineStore('datadic', { export const useDatadicStore = defineStore('datadic', {
state: () => ({ state: () => ({
dicts: {}, dicts: {},
@ -20,6 +21,18 @@ export const useDatadicStore = defineStore('datadic', {
}, },
actions: { actions: {
async init() { async init() {
// 优先从 sessionStorage 读取缓存
const cached = sessionStorage.getItem(SESSION_KEY)
if (cached) {
try {
dictCache = JSON.parse(cached)
this.dicts = dictCache
this.loaded = true
return
} catch (e) {
sessionStorage.removeItem(SESSION_KEY)
}
}
if (this.loaded) return if (this.loaded) return
try { try {
const res = await get('/v1/client/CDatadicsClient') const res = await get('/v1/client/CDatadicsClient')
@ -29,6 +42,7 @@ export const useDatadicStore = defineStore('datadic', {
}) })
this.dicts = dictCache this.dicts = dictCache
this.loaded = true this.loaded = true
sessionStorage.setItem(SESSION_KEY, JSON.stringify(dictCache))
} }
} catch (e) { } catch (e) {
console.error('数据字典加载失败', e) console.error('数据字典加载失败', e)

View File

@ -1,5 +1,5 @@
import { defineStore } from 'pinia'; import { defineStore } from 'pinia';
import { post } from '@/api/http'; import { get } from '@/api/http';
export const useUserStore = defineStore('user', { export const useUserStore = defineStore('user', {
state: () => ({ state: () => ({
@ -64,7 +64,7 @@ export const useUserStore = defineStore('user', {
*/ */
async fetchUserInfo() { async fetchUserInfo() {
try { try {
const res = await post('/v1/client/UserClient/info'); const res = await get('/v1/client/DUsersClient');
if (res.status === 200 && res.data) { if (res.status === 200 && res.data) {
this.setUser(res.data); this.setUser(res.data);
} }

View File

@ -11,7 +11,7 @@
.b_l_w; .b_l_w;
.box; .box;
.box-wrap; .box-wrap;
padding: 24vw 0 0; padding: 12vw 0 0;
&::before { &::before {
content: ''; content: '';
@ -78,17 +78,20 @@
width: 3.73vw; width: 3.73vw;
} }
span { input {
margin-left: 2.67vw; border: none;
font-size: 4vw; flex: 1;
line-height: 6.67vw; padding: 3.33vw 1.2vw;
color: #222222; font-size: 3.47vw;
color: #333;
width: 100%;
} }
}
.input_box { :-ms-input-placeholder,
.box; ::-webkit-input-placeholder,
.box-align-center; input::placeholder {
color: #999;
}
.hqyzm { .hqyzm {
.bs; .bs;
@ -102,18 +105,8 @@
} }
} }
input { .input_box {
border: none; display: none;
width: 100%;
padding: 3.33vw 0;
font-size: 3.47vw;
color: #333;
}
:-ms-input-placeholder,
::-webkit-input-placeholder,
input::placeholder {
color: #999;
} }
} }
} }
@ -134,6 +127,10 @@
.box-center-center; .box-center-center;
margin-top: 5.6vw; margin-top: 5.6vw;
width: 100%; width: 100%;
a {
color: #f00;
}
} }
.toLogin { .toLogin {
@ -318,12 +315,12 @@
.box-align-center; .box-align-center;
img { img {
width: 3.73vw; width: 4vw;
height: 3.73vw; height: 4vw;
} }
span { span {
font-size: 3.47vw; font-size: 3.67vw;
margin-left: 1.6vw; margin-left: 1.6vw;
color: #333333; color: #333333;
} }
@ -387,10 +384,17 @@
.fast_to { .fast_to {
.box; .box;
.box-tb;
.box-pack-between; .box-pack-between;
flex-wrap: wrap; width: 100%;
gap: 2.4vw; gap: 2.4vw;
>div {
.box;
gap: 2.4vw;
// margin-bottom: 2.4vw;
}
img { img {
width: 44.8vw; width: 44.8vw;
height: 17.33vw; height: 17.33vw;
@ -577,7 +581,7 @@
padding-top: 4.8vw; padding-top: 4.8vw;
border-radius: 2vw; border-radius: 2vw;
flex-wrap: wrap; flex-wrap: wrap;
border: solid 0.13vw #ffffff; border: solid 1px #ffffff;
.top { .top {
.box; .box;
@ -1054,7 +1058,7 @@
padding: 2.93vw 2.67vw; padding: 2.93vw 2.67vw;
background-color: #ffffff; background-color: #ffffff;
border-radius: 3.33vw; border-radius: 3.33vw;
border: solid 0.13vw #85cb58; border: solid 1px #85cb58;
} }
.choose { .choose {
@ -1065,7 +1069,7 @@
border-radius: 2vw; border-radius: 2vw;
background: url(/img/gift_bg1.png); background: url(/img/gift_bg1.png);
background-size: 100% 100%; background-size: 100% 100%;
// border: solid 0.13vw #85cb58; // border: solid 1px #85cb58;
padding: 4vw 4vw; padding: 4vw 4vw;
margin-top: 6.53vw; margin-top: 6.53vw;
width: 100%; width: 100%;
@ -1217,7 +1221,7 @@
.box-align-center; .box-align-center;
padding-bottom: 3.33vw; padding-bottom: 3.33vw;
margin-top: 3.33vw; margin-top: 3.33vw;
border-bottom: solid 0.13vw #33333380; border-bottom: solid 1px #33333380;
.img { .img {
.box; .box;
@ -2076,6 +2080,7 @@
.banner img { .banner img {
width: 100%; width: 100%;
margin-top: 3.07vw; margin-top: 3.07vw;
border-radius: 2.2vw;
} }
.label_box { .label_box {
@ -2149,6 +2154,7 @@
img { img {
margin: 3.33vw 0 1.87vw; margin: 3.33vw 0 1.87vw;
height: 14.4vw; height: 14.4vw;
border-radius: 50%;
} }
} }
} }
@ -2225,7 +2231,7 @@
width: 26.4vw; width: 26.4vw;
height: 26.4vw; height: 26.4vw;
border-radius: 2.67vw; border-radius: 2.67vw;
border: solid 0.13vw #f7f7f7; border: solid 1px #f7f7f7;
margin-bottom: 1.73vw; margin-bottom: 1.73vw;
} }
@ -2431,7 +2437,7 @@
// position: absolute; // position: absolute;
// left: 0; // left: 0;
// top: 0; // top: 0;
// width: 20.13vw; // width: 21px;
// height: 100%; // height: 100%;
// z-index: 1; // z-index: 1;
// background: url(/img/mall_i35.png) no-repeat; // background: url(/img/mall_i35.png) no-repeat;
@ -2460,6 +2466,7 @@
.box; .box;
.box-pack-between; .box-pack-between;
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: row;
} }
.good { .good {
@ -2473,7 +2480,7 @@
>div { >div {
padding: 0 3vw 3.6vw; padding: 0 3vw 3.6vw;
height: 100%; // height: 100%;
} }
img { img {
@ -2630,7 +2637,7 @@
height: 6.13vw; height: 6.13vw;
padding: 0 1.47vw; padding: 0 1.47vw;
border-radius: 1.07vw; border-radius: 1.07vw;
border: solid 0.13vw #222222; border: solid 1px #222222;
color: #1b1b1b; color: #1b1b1b;
p { p {
@ -3040,7 +3047,7 @@
z-index: 3; z-index: 3;
font-size: 3.33vw; font-size: 3.33vw;
margin-bottom: 4vw; margin-bottom: 4vw;
padding: 0 0.13vw; padding: 0 1px;
// color: #474747; // color: #474747;
// padding: 2.8vw 4.93vw 2.53vw 4.93vw; // padding: 2.8vw 4.93vw 2.53vw 4.93vw;
@ -3168,7 +3175,7 @@
// .item { // .item {
// margin-top: 6vw; // margin-top: 6vw;
// padding-bottom: 5.2vw; // padding-bottom: 5.2vw;
// border-bottom: 0.13vw solid #f5f5f5; // border-bottom: 1px solid #f5f5f5;
// >div { // >div {
// .box; // .box;
@ -3280,7 +3287,7 @@
width: 13.33vw; width: 13.33vw;
height: 5.33vw; height: 5.33vw;
border-radius: 0.67vw; border-radius: 0.67vw;
border: solid 0.13vw #ffffff; border: solid 1px #ffffff;
white-space: nowrap; white-space: nowrap;
img { img {
@ -3325,7 +3332,7 @@
.box; .box;
.box-align-center; .box-align-center;
border-radius: 0.67vw; border-radius: 0.67vw;
border: solid 0.13vw #ffffff; border: solid 1px #ffffff;
span { span {
color: #999999; color: #999999;
@ -3382,7 +3389,7 @@
flex-wrap: wrap; flex-wrap: wrap;
background-color: #fff5f5; background-color: #fff5f5;
border-radius: 2vw; border-radius: 2vw;
border: solid 0.13vw #fc6a39; border: solid 1px #fc6a39;
margin: 2.933vw 0; margin: 2.933vw 0;
padding: 3.867vw; padding: 3.867vw;
@ -3496,7 +3503,7 @@
height: 6.67vw; height: 6.67vw;
background: transparent; background: transparent;
border-radius: 1.07vw; border-radius: 1.07vw;
border: solid 0.13vw #000000; border: solid 1px #000000;
white-space: nowrap; white-space: nowrap;
} }
} }
@ -4834,7 +4841,7 @@
// .bs; // .bs;
b { b {
font-size: 3.73vw; font-size: 4vw;
} }
span { span {
@ -4910,9 +4917,13 @@
} }
.list_title { .list_title {
font-size: 4vw; font-size: 4vw !important;
margin: 4vw 0; margin: 4vw 0;
font-weight: bold; font-weight: bold;
.van-tab {
font-size: 4vw !important;
}
} }
.list { .list {
@ -5058,12 +5069,18 @@
} }
.list { .list {
.box;
.box-tb;
.box-align-center;
width: 100%;
margin-top: 4vw; margin-top: 4vw;
padding: 0 4vw; padding: 0 4vw;
overflow: hidden;
.item { .item {
.box; .box;
.box-align-center; .box-align-center;
width: 100%;
margin-bottom: 4vw; margin-bottom: 4vw;
img { img {
@ -5442,7 +5459,7 @@
width: max-content; width: max-content;
height: 4.67vw; height: 4.67vw;
border-radius: 1.33vw; border-radius: 1.33vw;
border: solid 0.13vw #e2593a; border: solid 1px #e2593a;
img { img {
height: 4.67vw; height: 4.67vw;
@ -5565,4 +5582,8 @@
font-weight: bold; font-weight: bold;
width: 100%; width: 100%;
} }
}
.paycode-page {
.f5;
} }

View File

@ -51,6 +51,15 @@ img {
object-fit: contain; object-fit: contain;
} }
// v-html容器中img样式
.html,
.vhtml {
img {
max-width: 100%;
margin-top: 1.2vw;
}
}
[v-cloak] { [v-cloak] {
display: none !important; display: none !important;
} }
@ -538,8 +547,9 @@ img {
>img { >img {
width: 16vw; width: 16vw;
height: 16vw; height: 16vw;
.y50; border-radius: 50%;
margin-right: 2.933vw; margin-right: 2.933vw;
object-fit: contain;
} }
.text { .text {
@ -884,6 +894,29 @@ img {
padding-bottom: 2.53vw; padding-bottom: 2.53vw;
border-bottom: 1px solid #f5f5f580; border-bottom: 1px solid #f5f5f580;
.state {
.box;
.box-center-center;
.bs;
height: 4.67vw;
padding: 0 1.2vw;
border-radius: 1.07vw;
margin-right: 1.2vw;
}
.state1 {
background-color: #2990f0;
}
.state3 {
background-color: #ea4747;
}
.state4 {
background-color: #229427;
}
img { img {
width: 3.2vw; width: 3.2vw;
height: 3.2vw; height: 3.2vw;
@ -893,6 +926,7 @@ img {
.goods_box { .goods_box {
.box; .box;
.box-align-center;
margin-top: 2.8vw; margin-top: 2.8vw;
img { img {
@ -904,9 +938,9 @@ img {
.goods_info { .goods_info {
.box; .box;
.box-tb; .box-tb;
.box-pack-around; .box-pack-between;
width: 100%; width: 100%;
height: 21.33vw; height: 18.33vw;
>div { >div {
@ -1733,6 +1767,15 @@ img {
} }
} }
._remark {
.box;
.box-align-center;
border-radius: 2vw;
padding: 3.333vw;
background: #fff;
margin-bottom: 4vw;
}
>button { >button {
.b_l_w; .b_l_w;
margin: 3.333vw 0; margin: 3.333vw 0;
@ -2194,6 +2237,15 @@ img {
background-color: #ffffff; background-color: #ffffff;
border-radius: 1.6vw; border-radius: 1.6vw;
.vhtml {
padding: 3.33vw 0;
img {
max-width: 100%;
margin-top: 1.2vw;
}
}
.tit { .tit {
.box; .box;
.box-align-center; .box-align-center;
@ -2298,31 +2350,55 @@ img {
.box; .box;
.box-tb; .box-tb;
.box-align-center; .box-align-center;
padding: 6.8vw 9.2vw;
width: 80vw; width: 80vw;
min-width: 80vw; min-width: 80vw;
max-width: 80vw; max-width: 80vw;
max-height: 90vh; max-height: 90vh;
overflow-y: auto; overflow-y: auto;
background-color: #ffffff;
border-radius: 2.67vw; border-radius: 2.67vw;
.share_box { .share_box {
.box; .box;
.box-tb; .box-tb;
.box-align-center; margin: 0 auto;
padding: 6.8vw 9.2vw;
width: 100%; width: 100%;
flex-shrink: 0; flex-shrink: 0;
background-image: linear-gradient(180deg,
#f5cfd8 0%,
#fff4f4 100%);
border-radius: 2.67vw;
} }
.logo { .bottom_box {
height: 10.53vw; .box;
margin-bottom: 4vw; .box-align-center;
.box-pack-around;
width: 100%;
padding: 3.33vw 0;
.logo {
width: 18.93vw;
height: 22.27vw;
}
.qrcode {
.box;
.box-tb;
.box-align-center;
img {
width: 17.07vw;
height: 17.07vw;
margin-bottom: 1.2vw;
}
}
} }
.share_userinfo { .share_userinfo {
.box; .box;
.box-align-center; .box-align-center;
margin: 0 auto;
position: relative; position: relative;
img { img {
@ -2335,22 +2411,22 @@ img {
.name { .name {
.box; .box;
.box-center-center; .box-center-center;
color: #222222; .bs;
position: relative; position: relative;
z-index: 1; z-index: 1;
background-color: #fde5eb; background-color: #841e36;
border-radius: 3.33vw; border-radius: 3.33vw;
// padding: .667vw 0; // padding: .667vw 0;
// padding: 1.2vw 4vw 6vw; // padding: 1.2vw 4vw 6vw;
padding-left: 6vw; padding-left: 6vw;
padding-right: 4vw; padding-right: 4vw;
margin-left: -3.33vw; margin-left: -4.33vw;
height: 8vw; height: 8vw;
text-align: center; text-align: center;
span { span {
line-height: 8vw; line-height: 8vw;
margin-bottom: 2.4vw; // margin-bottom: 2.4vw;
} }
} }
} }
@ -2377,28 +2453,30 @@ img {
height: 8vw; height: 8vw;
text-align: center; text-align: center;
} }
.goods_price {
margin-top: 1.33vw;
}
} }
.qrcode { .price {
.box; .box;
.box-tb;
.box-align-center; .box-align-center;
margin-top: 3.33vw; .box-pack-between;
padding-bottom: 3vw; width: 100%;
padding-bottom: 3.33vw;
border-bottom: 1px #fff solid;
img { .goods_price {
width: 17.07vw; font-size: 4vw;
height: 17.07vw; font-weight: bold;
margin-bottom: 1.2vw; color: #ea3e23;
}
.o_price {
color: #b1b1b1;
text-decoration: line-through;
} }
} }
.share_result { .share_result {
margin-top: 4vw; // margin-top: 4vw;
text-align: center; text-align: center;
img { img {

View File

@ -161,6 +161,7 @@ a {
.box { .box {
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
// flex-shrink: 0;
} }
/*从左至右*/ /*从左至右*/

View File

@ -1,159 +1,108 @@
/** /**
* HTML转图片工具 * HTML转图片工具
* 基于 html2canvas + canvas2image * 基于 html-to-image 将DOM元素转换为图片
*/ */
import { toCanvas } from 'html-to-image'
/** /**
* 将HTML元素转换为canvas * 将DOM元素转换为图片
* @param {HTMLElement} element - DOM元素 * @param {string|HTMLElement} selector - DOM选择器或元素
* @param {Object} options - 配置参数 * @param {Object} options - 配置选项
* @param {number} [options.scale=2] - 缩放比例 * @param {number} [options.width] - 自定义宽度会应用到元素上
* @param {number} [options.width] - 指定宽度 * @param {number} [options.height] - 自定义高度会应用到元素上
* @param {number} [options.height] - 指定高度 * @param {number} [options.canvasWidth] - 输出画布宽度
* @param {boolean} [options.useCORS=true] - 是否支持跨域 * @param {number} [options.canvasHeight] - 输出画布高度
* @param {number} [options.dpi=window.devicePixelRatio] - DPI * @param {number} [options.pixelRatio] - 像素比例默认2
* @returns {Promise<HTMLCanvasElement>} * @param {boolean} [options.useCORS] - 是否允许跨域默认true
* @param {string} [options.format] - 图片格式'png''jpeg'默认'png'
* @param {number} [options.quality] - 图片质量0-1仅jpeg有效默认1.0
* @returns {Promise<string>} - 返回base64图片数据URL
*/ */
export async function html2canvas(element, options = {}) { export async function toDataURL(selector, options = {}) {
const { const {
scale = 2, width,
width, height,
height, canvasWidth,
useCORS = true, canvasHeight,
dpi = window.devicePixelRatio, pixelRatio = 2,
onclone, useCORS = true,
} = options; format = 'png',
quality = 1.0
} = options
const opts = { const element = typeof selector === 'string' ? document.querySelector(selector) : selector
scale, if (!element) {
useCORS, throw new Error('Element not found')
dpi, }
logging: false,
backgroundColor: '#ffffff',
};
if (width) opts.width = width; const canvasEl = await toCanvas(element, {
if (height) opts.height = height; width,
height,
canvasWidth,
canvasHeight,
pixelRatio,
cors: useCORS,
backgroundColor: '#ffffff',
fetchLikeCORS: true
})
// 添加 onclone 回调用于调试 const mimeType = format === 'jpeg' ? 'image/jpeg' : 'image/png'
if (onclone) { return canvasEl.toDataURL(mimeType, quality)
opts.onclone = onclone;
}
const html2canvasFn = window.html2canvas;
if (!html2canvasFn) {
throw new Error('html2canvas not loaded, please include html2canvas.min.js');
}
return html2canvasFn(element, opts);
} }
/** /**
* 将canvas转换为dataURL * 将DOM元素转换为图片并下载
* @param {HTMLCanvasElement} canvas * @param {string|HTMLElement} selector - DOM选择器或元素
* @param {string} [type='image/png'] * @param {string} [filename] - 下载文件名不含扩展名
* @param {number} [quality=1.0] * @param {Object} options - 配置选项同toDataURL
* @returns {string}
*/ */
export function canvasToDataURL(canvas, type = 'image/png', quality = 1.0) { export async function toImageDownload(selector, filename = 'download', options = {}) {
return canvas.toDataURL(type, quality); const dataUrl = await toDataURL(selector, options)
const link = document.createElement('a')
link.download = filename + '.png'
link.href = dataUrl
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} }
/** /**
* 将canvas转换为Blob * 将DOM元素转换为Blob
* @param {HTMLCanvasElement} canvas * @param {string|HTMLElement} selector - DOM选择器或元素
* @param {string} [type='image/png'] * @param {Object} options - 配置选项同toDataURL
* @param {number} [quality=1.0]
* @returns {Promise<Blob>} * @returns {Promise<Blob>}
*/ */
export function canvasToBlob(canvas, type = 'image/png', quality = 1.0) { export async function toBlob(selector, options = {}) {
return new Promise((resolve, reject) => { const dataUrl = await toDataURL(selector, options)
canvas.toBlob(blob => {
if (blob) { const arr = dataUrl.split(',')
resolve(blob); const mime = arr[0].match(/:(.*?);/)[1]
} else { const bstr = atob(arr[1])
reject(new Error('Canvas to Blob failed')); let n = bstr.length
} const u8arr = new Uint8Array(n)
}, type, quality); while (n--) {
}); u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
} }
/** /**
* 将HTML元素直接转成dataURL图片 * 通过 dataURL 直接下载图片
* @param {HTMLElement} element - DOM元素
* @param {Object} options - html2canvas配置
* @param {string} [type='image/png'] - 图片类型
* @param {number} [quality=1.0] - 图片质量
* @returns {Promise<string>} dataURL
*/ */
export async function htmlToDataURL(element, options = {}, type = 'image/png', quality = 1.0) { export function downloadByDataURL(dataUrl, filename = 'download') {
const canvas = await html2canvas(element, options); const link = document.createElement('a')
return canvasToDataURL(canvas, type, quality); link.download = filename + '.png'
link.href = dataUrl
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} }
/** export default {
* 将HTML元素直接转成Blob toDataURL,
* @param {HTMLElement} element - DOM元素 toImageDownload,
* @param {Object} options - html2canvas配置 toBlob,
* @param {string} [type='image/png'] - 图片类型 downloadByDataURL
* @param {number} [quality=1.0] - 图片质量 }
* @returns {Promise<Blob>}
*/
export async function htmlToBlob(element, options = {}, type = 'image/png', quality = 1.0) {
const canvas = await html2canvas(element, options);
return canvasToBlob(canvas, type, quality);
}
/**
* 下载canvas为图片
* @param {HTMLCanvasElement} canvas
* @param {string} filename - 文件名不带扩展名
* @param {string} [type='png'] - 图片格式 ('png' | 'jpeg' | 'webp')
*/
export function downloadCanvas(canvas, filename, type = 'png') {
const mimeType = `image/${type}`;
const dataURL = canvasToDataURL(canvas, mimeType, 1.0);
const link = document.createElement('a');
link.download = `${filename}.${type}`;
link.href = dataURL;
link.click();
}
/**
* 将HTML元素转成图片并下载
* @param {HTMLElement} element - DOM元素
* @param {string} filename - 文件名不带扩展名
* @param {Object} options - html2canvas配置
* @param {string} [type='png'] - 图片格式
*/
export async function htmlToImage(element, filename, options = {}, type = 'png') {
const canvas = await html2canvas(element, options);
downloadCanvas(canvas, filename, type);
}
/**
* 将dataURL保存为图片文件
* @param {string} dataURL - 图片dataURL
* @param {string} filename - 文件名不带扩展名
*/
export function downloadDataURL(dataURL, filename) {
const link = document.createElement('a');
link.download = filename;
link.href = dataURL;
link.click();
}
/**
* 将Blob保存为图片文件
* @param {Blob} blob - 图片Blob
* @param {string} filename - 文件名不带扩展名
*/
export async function downloadBlob(blob, filename) {
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.download = filename;
link.href = url;
link.click();
setTimeout(() => URL.revokeObjectURL(url), 100);
}

View File

@ -25,9 +25,6 @@
<div class="line"> <div class="line">
<div class="title"> <div class="title">
<img src="/img/login-i1.png" alt=""> <img src="/img/login-i1.png" alt="">
<span>手机号</span>
</div>
<div class="input_box">
<input type="tel" maxlength="11" :placeholder="active === 1 && !isReg ? '请输入手机号码作为账号' : '请输入手机号'" <input type="tel" maxlength="11" :placeholder="active === 1 && !isReg ? '请输入手机号码作为账号' : '请输入手机号'"
v-model="formData.Phone" /> v-model="formData.Phone" />
<van-icon v-if="formData.Phone" @click="formData.Phone = ''" name="cross" size="5vw"></van-icon> <van-icon v-if="formData.Phone" @click="formData.Phone = ''" name="cross" size="5vw"></van-icon>
@ -38,9 +35,6 @@
<div class="line" v-if="active === 1 || isReg"> <div class="line" v-if="active === 1 || isReg">
<div class="title"> <div class="title">
<img src="/img/login-i2.png" alt=""> <img src="/img/login-i2.png" alt="">
<span>验证码</span>
</div>
<div class="input_box">
<input type="text" placeholder="请输入验证码" v-model="formData.SmsCode" maxlength="6" /> <input type="text" placeholder="请输入验证码" v-model="formData.SmsCode" maxlength="6" />
<van-button class="hqyzm" @click="getSmsCode" :disabled="countdown.isCounting"> <van-button class="hqyzm" @click="getSmsCode" :disabled="countdown.isCounting">
{{ countdown.isCounting ? `已发送(${countdown.seconds}s)` : '获取验证码' }} {{ countdown.isCounting ? `已发送(${countdown.seconds}s)` : '获取验证码' }}
@ -52,9 +46,6 @@
<div class="line" v-if="active === 0 || isReg"> <div class="line" v-if="active === 0 || isReg">
<div class="title"> <div class="title">
<img src="/img/login-i3.png" alt=""> <img src="/img/login-i3.png" alt="">
<span>密码</span>
</div>
<div class="input_box">
<input :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" v-model="formData.Password" <input :type="showPassword ? 'text' : 'password'" placeholder="请输入密码" v-model="formData.Password"
@keydown.enter="handleSubmit" /> @keydown.enter="handleSubmit" />
<van-icon @click="showPassword = !showPassword" :name="showPassword ? 'closed-eye' : 'eye-o'" <van-icon @click="showPassword = !showPassword" :name="showPassword ? 'closed-eye' : 'eye-o'"
@ -66,9 +57,6 @@
<div class="line" v-if="isReg"> <div class="line" v-if="isReg">
<div class="title"> <div class="title">
<img src="/img/login-i4.png" alt=""> <img src="/img/login-i4.png" alt="">
<span>确认密码</span>
</div>
<div class="input_box">
<input :type="showConfirmPassword ? 'text' : 'password'" placeholder="请确认密码" v-model="formData.RePassword" <input :type="showConfirmPassword ? 'text' : 'password'" placeholder="请确认密码" v-model="formData.RePassword"
@keydown.enter="handleSubmit" /> @keydown.enter="handleSubmit" />
<van-icon @click="showConfirmPassword = !showConfirmPassword" <van-icon @click="showConfirmPassword = !showConfirmPassword"
@ -80,10 +68,7 @@
<div class="line" v-if="isReg"> <div class="line" v-if="isReg">
<div class="title"> <div class="title">
<img src="/img/login-i5.png" alt=""> <img src="/img/login-i5.png" alt="">
<span>邀请人</span> <input type="text" maxlength="11" placeholder="请填写邀请人账号(选填)" v-model="formData.Recommend" />
</div>
<div class="input_box">
<input type="text" placeholder="请填写邀请人账号(选填)" v-model="formData.Recommend" />
<van-icon v-if="formData.Recommend" @click="formData.Recommend = ''" name="cross" size="5vw"></van-icon> <van-icon v-if="formData.Recommend" @click="formData.Recommend = ''" name="cross" size="5vw"></van-icon>
</div> </div>
</div> </div>
@ -95,7 +80,7 @@
</div> </div>
<!-- 用户协议 --> <!-- 用户协议 -->
<van-checkbox icon-size="2.667vw" class="agreement" v-model="checked" checked-color="#841e36"> <van-checkbox icon-size="3.2vw" class="agreement" v-model="checked" checked-color="#841e36">
选中即代表同意<a @click.stop="showContract = true">用户协议</a><a @click.stop="showPolicy = true">隐私政策</a> 选中即代表同意<a @click.stop="showContract = true">用户协议</a><a @click.stop="showPolicy = true">隐私政策</a>
</van-checkbox> </van-checkbox>
@ -160,42 +145,35 @@ export default {
}, },
methods: { methods: {
init() { init() {
// code
if (sessionStorage.getItem('wx_code_processed') === '1') {
sessionStorage.removeItem('wx_code_processed')
return
}
//
if (this.$isLogin?.()) { if (this.$isLogin?.()) {
location.replace('#/') location.replace('#/')
return return
} }
// this.checkWxLogin(); //
const code = JSON.stringify(this.$route.query.code); if (this.$isWechatMini()) {
if (code) { wx.miniProgram.reLaunch({ url: `/pages/user/login` })
alert(code) return
}
this.$post('/v1/client/AuthClient/loginwxweb', { code }).then(res => { // code code
this.$showSuccessToast('登录成功') if (this.$isWechat()) {
if (res.data?.openid) { if (sessionStorage.getItem('wx_callback_code')) {
localStorage.setItem('openid', res.data.openid) // code token + code
} } else {
}).catch(err => { this.checkWxLogin()
alert(JSON.stringify(err)) }
this.$showFailToast(err.message || '微信登录失败') return
})
} }
this.$closeSkeleton?.()
}, },
checkWxLogin() { checkWxLogin() {
if (!this.$isWechat()) return if (!this.$isWechat()) return
if (this.$route.query.code) return const REDIRECT_URI = encodeURIComponent('https://m.taigurun.cn/#/WxCallback')
// console.log(window.location.href); window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxdff8dd2032f1e054&redirect_uri=${REDIRECT_URI}&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect`
this.$get('/v1/client/AuthClient/loginwxweburl', {
returnUrl: `${encodeURIComponent(window.location.href)}`
}).then(res => {
alert(JSON.stringify(res))
if (res.data) {
window.location.href = res.data
}
}).catch(res => {
alert(res)
})
}, },
validatePhone(phone) { validatePhone(phone) {
if (!phone) { if (!phone) {
@ -217,8 +195,6 @@ export default {
}, },
getSmsCode() { getSmsCode() {
if (!this.validatePhone(this.formData.Phone)) return if (!this.validatePhone(this.formData.Phone)) return
//
this.countdown.isCounting = true this.countdown.isCounting = true
this.countdown.seconds = 60 this.countdown.seconds = 60
this.countdown.timer = setInterval(() => { this.countdown.timer = setInterval(() => {
@ -229,14 +205,13 @@ export default {
clearInterval(this.countdown.timer) clearInterval(this.countdown.timer)
} }
}, 1000) }, 1000)
this.$post('/v1/client/AuthClient/sendcode', { this.$post('/v1/client/AuthClient/sendcode', {
"purpose": this.isReg ? "register" : "login", purpose: this.isReg ? 'register' : 'login',
"cellphone": this.formData.Phone cellphone: this.formData.Phone
}).then(data => { }).then(data => {
if (data.status === 200) { if (data.status === 200) {
this.$showSuccessToast?.('发送成功') this.$showSuccessToast?.('发送成功')
this.smsCode = data.data // this.smsCode = data.data
} }
}).catch(() => { }).catch(() => {
this.countdown.isCounting = false this.countdown.isCounting = false
@ -246,7 +221,6 @@ export default {
handleSubmit() { handleSubmit() {
if (!this.validatePhone(this.formData.Phone)) return if (!this.validatePhone(this.formData.Phone)) return
if (!this.validateAgreement()) return if (!this.validateAgreement()) return
if (this.isReg) { if (this.isReg) {
this.handleRegister() this.handleRegister()
} else { } else {
@ -254,8 +228,8 @@ export default {
} }
}, },
handleLogin() { handleLogin() {
//
if (this.active === 0) { if (this.active === 0) {
//
if (!this.formData.Password) { if (!this.formData.Password) {
this.$showFailToast?.('请输入密码') this.$showFailToast?.('请输入密码')
return return
@ -270,18 +244,13 @@ export default {
this.$showFailToast?.(data.message) this.$showFailToast?.(data.message)
return return
} }
const userStore = useUserStore() this.afterLogin(data.data.token)
userStore.setToken(data.data.token)
userStore.fetchUserInfo()
this.$showSuccessToast?.(data.message)
location.replace('#/My')
}).catch(err => { }).catch(err => {
this.loading = false this.loading = false
this.$showFailToast?.(err.message || '登录失败') this.$showFailToast?.(err.message || '登录失败')
}) })
} } else {
// //
else {
if (!this.formData.SmsCode) { if (!this.formData.SmsCode) {
this.$showFailToast?.('请输入验证码') this.$showFailToast?.('请输入验证码')
return return
@ -297,27 +266,57 @@ export default {
this.$showFailToast?.(data.message) this.$showFailToast?.(data.message)
return return
} }
const userStore = useUserStore() this.afterLogin(data.data.token)
userStore.setToken(data.data.token)
userStore.fetchUserInfo()
this.$showSuccessToast?.(data.message)
location.replace('#/My')
}).catch(err => { }).catch(err => {
this.loading = false this.loading = false
this.$showFailToast?.(err.message || '登录失败') this.$showFailToast?.(err.message || '登录失败')
}) })
} }
}, },
// / token openid
afterLogin(token) {
const userStore = useUserStore()
userStore.setToken(token)
userStore.fetchUserInfo()
this.bindWxOpenid(token)
},
// token + code openid
bindWxOpenid(token) {
const code = sessionStorage.getItem('wx_callback_code')
if (!code) {
this.$showSuccessToast?.('登录成功')
location.replace('#/My')
return
}
fetch(`${import.meta.env.VITE_API_URL}/v1/client/AuthClient/loginwxweb`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(code),
}).then(res =>
res.json()
).then(res => {
// alert(JSON.stringify(res))
sessionStorage.removeItem('wx_callback_code')
if (res.message) {
localStorage.setItem('openid', res.message)
// alert('openid' + res.message)
}
this.$showSuccessToast?.('登录成功')
location.replace('#/My')
}).catch(() => {
sessionStorage.removeItem('wx_callback_code')
this.$showSuccessToast?.('登录成功')
location.replace('#/My')
})
},
handleRegister() { handleRegister() {
if (!this.formData.SmsCode) { if (!this.formData.SmsCode) {
this.$showFailToast?.('请输入验证码') this.$showFailToast?.('请输入验证码')
return return
} }
//
if (this.formData.SmsCode !== this.smsCode) {
this.$showFailToast?.('验证码错误')
return
}
if (!this.formData.Password) { if (!this.formData.Password) {
this.$showFailToast?.('请输入密码') this.$showFailToast?.('请输入密码')
return return
@ -330,22 +329,19 @@ export default {
this.$showFailToast?.('密码至少6位') this.$showFailToast?.('密码至少6位')
return return
} }
this.loading = true this.loading = true
this.$post('/v1/client/AuthClient/register', { this.$post('/v1/client/AuthClient/register', {
cellphone: this.formData.Phone, cellphone: this.formData.Phone,
userpwd: this.formData.Password, userpwd: this.formData.Password,
pcellphone: this.formData.Recommend pcellphone: this.formData.Recommend,
code: this.formData.SmsCode
}).then(data => { }).then(data => {
this.loading = false this.loading = false
if (data.status !== 200) { if (data.status !== 200) {
this.$showFailToast?.(data.message) this.$showFailToast?.(data.message)
return return
} }
const userStore = useUserStore() this.afterLogin(data.data.token)
userStore.setToken(data.data.token)
this.$showSuccessToast?.('注册成功')
location.replace('#/My')
}).catch(err => { }).catch(err => {
this.loading = false this.loading = false
this.$showFailToast?.(err.message || '注册失败') this.$showFailToast?.(err.message || '注册失败')
@ -361,7 +357,7 @@ export default {
this.formData.Recommend = inviteCode; this.formData.Recommend = inviteCode;
localStorage.setItem('recommend', inviteCode); localStorage.setItem('recommend', inviteCode);
// //
this.isReg = true; // this.isReg = true;
} }
}, },
beforeUnmount() { beforeUnmount() {

View File

@ -13,10 +13,6 @@
<div class="line"> <div class="line">
<div class="title"> <div class="title">
<img src="/img/login-i1.png" alt=""> <img src="/img/login-i1.png" alt="">
<span>账号</span>
</div>
<div class="input_box">
<input type="phone" maxlength="11" placeholder="请输入账号(手机号)" v-model="data.cellphone" <input type="phone" maxlength="11" placeholder="请输入账号(手机号)" v-model="data.cellphone"
onblur="window.scrollTo(0, 0);" /> onblur="window.scrollTo(0, 0);" />
<van-icon @click="data.cellphone = ''" name="cross" size="5vw"></van-icon> <van-icon @click="data.cellphone = ''" name="cross" size="5vw"></van-icon>
@ -26,10 +22,6 @@
<div class="line"> <div class="line">
<div class="title"> <div class="title">
<img src="/img/login-i2.png" alt=""> <img src="/img/login-i2.png" alt="">
<span>验证码</span>
</div>
<div class="input_box">
<input type="text" placeholder="请输入验证码" v-model="data.code" maxlength="6" <input type="text" placeholder="请输入验证码" v-model="data.code" maxlength="6"
onblur="window.scrollTo(0, 0);" /> onblur="window.scrollTo(0, 0);" />
<van-button class="hqyzm" @click="sendCode" :disabled="isSend"> <van-button class="hqyzm" @click="sendCode" :disabled="isSend">
@ -41,10 +33,6 @@
<div class="line"> <div class="line">
<div class="title"> <div class="title">
<img src="/img/login-i3.png" alt=""> <img src="/img/login-i3.png" alt="">
<span>密码</span>
</div>
<div class="input_box">
<input :type="show1 ? 'text' : 'password'" placeholder="请输入新的登录密码" v-model="data.password" <input :type="show1 ? 'text' : 'password'" placeholder="请输入新的登录密码" v-model="data.password"
@keydown.enter="login" onblur="window.scrollTo(0, 0);" /> @keydown.enter="login" onblur="window.scrollTo(0, 0);" />
<van-icon @click="show1 = true" name="eye-o" size="5vw" v-if="!show1"></van-icon> <van-icon @click="show1 = true" name="eye-o" size="5vw" v-if="!show1"></van-icon>
@ -55,9 +43,6 @@
<div class="line"> <div class="line">
<div class="title"> <div class="title">
<img src="/img/login-i4.png" alt=""> <img src="/img/login-i4.png" alt="">
<span>确认密码</span>
</div>
<div class="input_box">
<input :type="show2 ? 'text' : 'password'" placeholder="请再次输入登录密码" v-model="data.RePassword" <input :type="show2 ? 'text' : 'password'" placeholder="请再次输入登录密码" v-model="data.RePassword"
@keydown.enter="login" onblur="window.scrollTo(0, 0);" /> @keydown.enter="login" onblur="window.scrollTo(0, 0);" />
<van-icon @click="show2 = true" name="eye-o" size="5vw" v-if="!show2"></van-icon> <van-icon @click="show2 = true" name="eye-o" size="5vw" v-if="!show2"></van-icon>

View File

@ -0,0 +1,20 @@
<template></template>
<script>
export default {
name: 'WxCallback',
mounted() {
const url = new URL(window.location.href)
const code = url.searchParams.get('code')
const inviteCode = url.searchParams.get('invite')
if (code) {
sessionStorage.setItem('wx_callback_code', code)
// invite Login mounted
this.$router.replace('/Login' + (inviteCode ? `?invite=${inviteCode}` : ''))
} else {
this.$router.replace('/')
}
}
}
</script>

View File

@ -11,12 +11,8 @@
<img :src="i.bankspname === '支付宝' ? '/img/aliicon.png' : '/img/bankcardicon.png'" alt=""> <img :src="i.bankspname === '支付宝' ? '/img/aliicon.png' : '/img/bankcardicon.png'" alt="">
<div> <div>
<span> <span>
{{ i.bankspname === '支付宝' ? '支付宝账号' : i.bankspname }}{{ i.bankid === 91 ? i.bankcellphone {{ i.realname }}({{ i.bankcardnumber }})
: i.bankcardnumber }}
</span> </span>
<p>
2小时内到账
</p>
</div> </div>
</div> </div>
</template> </template>
@ -38,9 +34,9 @@
</div> </div>
<div class="btn_box"> <div class="btn_box">
<button class="alipay" @click="showAddAliPayPopup = true"> <!-- <button class="alipay" @click="showAddAliPayPopup = true">
添加支付宝 添加支付宝
</button> </button> -->
<button class="bankcard" @click="showAddPopup = true"> <button class="bankcard" @click="showAddPopup = true">
添加银行卡 添加银行卡
</button> </button>
@ -97,8 +93,10 @@
:formatter="formatter" /> :formatter="formatter" />
<van-field v-model="tempData.bankcardnumber" label="银行卡号" placeholder="请输入银行卡号" <van-field v-model="tempData.bankcardnumber" label="银行卡号" placeholder="请输入银行卡号"
:rules="[{ required: true, message: '请填写银行卡号' }]" clearable required :formatter="formatter" /> :rules="[{ required: true, message: '请填写银行卡号' }]" clearable required :formatter="formatter" />
<van-field v-model="tempData.bankname" label="银行名称" placeholder="请输入银行名称" <!--
:rules="[{ required: true, message: '请填写银行名称' }]" clearable required /> <van-field v-model="tempData.bankname" label="银行名称" placeholder="请选择银行名称"
:rules="[{ required: true, message: '请选择银行名称' }]" readonly required is-link
@click="showBankPicker = true" /> -->
<van-field v-model="regionName" label="开户地区" placeholder="请选择开户地区" <van-field v-model="regionName" label="开户地区" placeholder="请选择开户地区"
:rules="[{ required: true, message: '请选择开户地区' }]" clearable required is-link @click="showRegion = true;" :rules="[{ required: true, message: '请选择开户地区' }]" clearable required is-link @click="showRegion = true;"
readonly /> readonly />
@ -122,6 +120,10 @@
<van-cascader v-model="region" title="请选择地区" :options="areaList" :field-names="defaultProps" @change="onChange" <van-cascader v-model="region" title="请选择地区" :options="areaList" :field-names="defaultProps" @change="onChange"
@finish="onFinish" @close="showRegion = false" /> @finish="onFinish" @close="showRegion = false" />
</van-popup> </van-popup>
<!-- <van-popup v-model:show="showBankPicker" round position="bottom">
<van-picker title="选择银行" :columns="bankColumns" @confirm="onBankConfirm" @cancel="showBankPicker = false" />
</van-popup> -->
</div> </div>
</BasePage> </BasePage>
@ -134,17 +136,6 @@ export default {
name: 'CashoutAccount', name: 'CashoutAccount',
mounted() { mounted() {
this.init(); this.init();
this.getBankId()
},
watch: {
'tempData.bankname': function (newVal) {
if (newVal && this.bankidGroup.length) {
const bank = this.bankidGroup.find(item => item.name.includes(newVal) || newVal.includes(item.name))
if (bank) {
this.tempData.bankid = bank.id
}
}
}
}, },
data() { data() {
return { return {
@ -161,7 +152,6 @@ export default {
maxDate: new Date('2099/12/31'), maxDate: new Date('2099/12/31'),
regionName: '', regionName: '',
region: '', region: '',
bankidGroup: {},
areaList: useCascaderAreaData(), areaList: useCascaderAreaData(),
defaultProps: { defaultProps: {
text: 'text', text: 'text',
@ -170,6 +160,8 @@ export default {
}, },
} }
}, },
computed: {
},
methods: { methods: {
init() { init() {
this.$get('/v1/client/DUserbankcardsClient').then(data => { this.$get('/v1/client/DUserbankcardsClient').then(data => {
@ -181,11 +173,6 @@ export default {
formatter(value) { formatter(value) {
return value.replace(/\s/g, ''); return value.replace(/\s/g, '');
}, },
getBankId() {
this.$get('/v1/client/DBanksClient').then(res => {
this.bankidGroup = res.data;
})
},
onChange({ selectedOptions }) { onChange({ selectedOptions }) {
this.tempData.provid = selectedOptions[0]?.value || ''; this.tempData.provid = selectedOptions[0]?.value || '';
}, },
@ -233,7 +220,7 @@ export default {
"realname": this.tempData.realname, "realname": this.tempData.realname,
"idnumber": this.tempData.idnumber, "idnumber": this.tempData.idnumber,
"bankid": this.tempData.bankid || 0, "bankid": this.tempData.bankid || 0,
"bankname": this.tempData.bankname || '', // "bankname": this.tempData.bankname || '',
"bankcardnumber": this.tempData.bankcardnumber, "bankcardnumber": this.tempData.bankcardnumber,
"bankcellphone": this.tempData.bankcellphone, "bankcellphone": this.tempData.bankcellphone,
"sort": 0, "sort": 0,
@ -252,6 +239,9 @@ export default {
this.$post('/v1/client/DUserbankcardsClient', params).then(res => { this.$post('/v1/client/DUserbankcardsClient', params).then(res => {
this.$showSuccessToast('添加成功'); this.$showSuccessToast('添加成功');
this.tempData = {}; this.tempData = {};
this.regionName = '';
this.region = '';
this.checked = 0;
this.init(); this.init();
this.showAddPopup = false; this.showAddPopup = false;
}).catch(err => { }).catch(err => {

View File

@ -1,5 +1,5 @@
<template> <template>
<BasePage :back="back"> <BasePage>
<van-tabs class="b_l_w f5" v-model:active="activeTab" animated :sticky="true" @change="onTabChange"> <van-tabs class="b_l_w f5" v-model:active="activeTab" animated :sticky="true" @change="onTabChange">
<van-tab title="全部" name=""></van-tab> <van-tab title="全部" name=""></van-tab>
<van-tab v-for="item in states" :title="item.label" :name="item.value"></van-tab> <van-tab v-for="item in states" :title="item.label" :name="item.value"></van-tab>

View File

@ -1,5 +1,5 @@
<template> <template>
<BasePage> <BasePage :title="data.name || '产品详情'">
<div class="goodsdetails"> <div class="goodsdetails">
<div class="t"> <div class="t">
<img :src="$file(data.img)" alt=""> <img :src="$file(data.img)" alt="">
@ -125,8 +125,6 @@
<van-popup v-model:show="show" class="shareimgbox" style="max-width: 80vw;"> <van-popup v-model:show="show" class="shareimgbox" style="max-width: 80vw;">
<div class="share_box" id="bsCard"> <div class="share_box" id="bsCard">
<img class="logo" src="/img/logo-lr.png" alt="">
<div class="share_userinfo"> <div class="share_userinfo">
<img :src="$file($userInfo.userimg)" alt=""> <img :src="$file($userInfo.userimg)" alt="">
<div class="name"><span>{{ $userInfo.nickname }}友情推荐</span></div> <div class="name"><span>{{ $userInfo.nickname }}友情推荐</span></div>
@ -135,18 +133,24 @@
<div class="goods_info"> <div class="goods_info">
<img class="goods_img" :src="$file(data.img)" alt=""> <img class="goods_img" :src="$file(data.img)" alt="">
<div class="goods_name">{{ data.name }}</div> <div class="goods_name">{{ data.name }}</div>
<div class="goods_price">¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
</div> </div>
<div class="qrcode"> <div class="price">
<vue-qr :margin="0" :text="shareLink" backgroundColor="rgb(255,255,255)" <div class="goods_price">¥{{ data.saleprice?.toFixed(2) || '0.00' }}</div>
colorLight="rgb(255,255,255)"></vue-qr> <div class="o_price">¥{{ data.originalprice?.toFixed(2) || '0.00' }}</div>
长按扫码购买 </div>
<div class="bottom_box">
<div class="qrcode">
<vue-qr :margin="0" :text="shareLink" backgroundColor="#ffffff00" colorLight="rgb(255,255,255)"></vue-qr>
长按扫码购买
</div>
<img class="logo" src="/img/logo.png" alt="">
</div> </div>
</div> </div>
<div class="share_result" v-if="ShareImg"> <div class="share_result" v-if="ShareImg">
<img :src="ShareImg" alt="分享海报"> <img :src="ShareImg" alt="分享海报">
<!-- <div class="tips">长按保存图片</div> -->
</div> </div>
</van-popup> </van-popup>
@ -158,7 +162,8 @@
</template> </template>
<script> <script>
import { html2canvas, canvasToDataURL } from '@/utils/html2image';
import { toDataURL } from '@/utils/html2image.js'
export default { export default {
name: 'GoodsDetail', name: 'GoodsDetail',
@ -182,7 +187,9 @@ export default {
], ],
shareLink: '', shareLink: '',
ShareImg: '', ShareImg: '',
loading: false loading: false,
transparent: 'rgba(0,0,0,0)',
shareBoxHidden: false
} }
}, },
methods: { methods: {
@ -221,10 +228,7 @@ export default {
let matchedSku = this.specCombinations.find(combo => { let matchedSku = this.specCombinations.find(combo => {
return specKeys.every(key => { return specKeys.every(key => {
const spec = this.specSelect.find(s => s.name === key); return combo.skuname.includes(this.selectedSpecs[key]);
if (!spec) return false;
const specValue = this.selectedSpecs[key];
return combo.skuname === specValue;
}); });
}); });
@ -237,12 +241,22 @@ export default {
this.showShare = false; this.showShare = false;
if (option.name === "分享海报") { if (option.name === "分享海报") {
this.show = true; this.show = true;
//
if (this.ShareImg && this.shareBoxHidden) {
this.loading = false;
return;
}
//
if (this.ShareImg) {
this.hideShareBox();
this.loading = false;
return;
}
this.loading = true; this.loading = true;
this.ShareImg = '';
// DOM // DOM
setTimeout(() => { setTimeout(() => {
this.generateShareImg(); this.generateShareImg();
}, 200); }, 500);
} else if (option.name == "复制链接") { } else if (option.name == "复制链接") {
this.$copyText(location.href); this.$copyText(location.href);
this.$showToast({ type: "success", message: "复制成功" }); this.$showToast({ type: "success", message: "复制成功" });
@ -263,23 +277,40 @@ export default {
async generateShareImg() { async generateShareImg() {
var shareContent = document.querySelector('#bsCard'); var shareContent = document.querySelector('#bsCard');
if (!shareContent) return; if (!shareContent) return;
var width = shareContent.offsetWidth;
var height = shareContent.offsetHeight;
try { try {
var canvas = await html2canvas(shareContent, { // Wait for all images inside shareContent to load
scale: 2, const images = shareContent.querySelectorAll('img');
width: width, for (const img of images) {
height: height, if (!img.complete) {
await new Promise((resolve) => {
img.onload = resolve;
img.onerror = resolve;
});
}
}
// Wait for vue-qr to render
await new Promise(resolve => setTimeout(resolve, 500));
this.ShareImg = await toDataURL(shareContent, {
pixelRatio: 2,
useCORS: true, useCORS: true,
allowTaint: true, format: 'png'
}); });
this.ShareImg = canvasToDataURL(canvas, "image/png", 1.0); this.hideShareBox();
this.loading = false; this.loading = false;
document.querySelector('.share_box').style.display = 'none';
} catch (e) { } catch (e) {
console.error(e); console.error(e);
this.loading = false; this.loading = false;
} }
},
hideShareBox() {
const shareBox = document.querySelector('.share_box');
if (shareBox) {
shareBox.style.display = 'none';
this.shareBoxHidden = true;
}
} }
} }
} }

View File

@ -1,10 +1,5 @@
<template> <template>
<router-view v-slot="{ Component, route }"> <router-view />
<keep-alive>
<component :is="Component" :key="route.name" v-if="route.meta.cache" />
</keep-alive>
<component :is="Component" :key="route.name" v-if="!route.meta.cache" />
</router-view>
<van-tabbar v-model="DefaultActive" placeholder @change="changeActive" active-color="#841e36" inactive-color="#1b1b1b" <van-tabbar v-model="DefaultActive" placeholder @change="changeActive" active-color="#841e36" inactive-color="#1b1b1b"
fixed> fixed>
<van-tabbar-item v-for="item in Tabbars" :key="item.Name" :name="item.Name" :badge="getBadge(item)" :dot="item.Dot" <van-tabbar-item v-for="item in Tabbars" :key="item.Name" :name="item.Name" :badge="getBadge(item)" :dot="item.Dot"
@ -32,7 +27,7 @@ export default {
}, },
{ {
Name: 'Gift', Name: 'Gift',
Label: '商品区', Label: '礼包区',
Icon_Active: '/img/Gift_Active.png', Icon_Active: '/img/Gift_Active.png',
Icon_Inactive: '/img/Gift_Inactive.png', Icon_Inactive: '/img/Gift_Inactive.png',
}, },
@ -42,6 +37,12 @@ export default {
Icon_Active: '/img/Mall_Active.png', Icon_Active: '/img/Mall_Active.png',
Icon_Inactive: '/img/Mall_Inactive.png', Icon_Inactive: '/img/Mall_Inactive.png',
}, },
{
Name: 'PointMall',
Label: '积分区',
Icon_Active: '/img/PointMall_Active.png',
Icon_Inactive: '/img/PointMall_Inactive.png',
},
{ {
Name: 'My', Name: 'My',
Label: '我的', Label: '我的',
@ -49,7 +50,7 @@ export default {
Icon_Inactive: '/img/My_Inactive.png', Icon_Inactive: '/img/My_Inactive.png',
}, },
], ],
} }
}, },
watch: { watch: {
'$route.path': { '$route.path': {
@ -65,21 +66,34 @@ export default {
methods: { methods: {
updateActive() { updateActive() {
const routeName = this.$route.name const routeName = this.$route.name
if (routeName === 'PointMall') {
return
}
const tabbar = this.Tabbars.find(item => item.Name === routeName) const tabbar = this.Tabbars.find(item => item.Name === routeName)
if (tabbar) { if (tabbar) {
this.DefaultActive = tabbar.Name this.DefaultActive = tabbar.Name
} }
}, },
changeActive(name) { changeActive(name) {
if (name === 'PointMall') {
this.$showToast('暂未开放');
this.DefaultActive = this.$route.name; //
return
}
this.$router.push({ name }) this.$router.push({ name })
}, },
getBadge(item) { getBadge(item) {
return item.Badge || '' return item.Badge || ''
}, },
onTabClick(name) { onTabClick(name) {
if (name === 'PointMall') {
return
}
if (this.$route.name === name) { if (this.$route.name === name) {
// //
this.$router.replace({ name }) // this.$router.replace({ name })
location.reload()
} else { } else {
this.$router.push({ name }) this.$router.push({ name })
} }

View File

@ -1,53 +1,53 @@
<template> <template>
<BasePage> <!-- <BasePage> -->
<ManagerPopup v-model="managerPopupVisible" /> <ManagerPopup v-model="managerPopupVisible" />
<div class="merchant"> <div class="merchant">
<div class="shopinfo"> <div class="shopinfo">
<img class="tx" :src="$file(data.shopimg)"> <img class="tx" :src="$file(data.shopimg)">
<div class="info"> <div class="info">
<b>{{ truncatedShopname }}</b> <b>{{ truncatedShopname }}</b>
<p>账号{{ data.shopphone }}</p> <p>账号{{ data.shopphone }}</p>
</div>
<div class="service-box" @click="$showManagerPopup()">
管理中心
<van-icon name="arrow"></van-icon>
</div>
</div> </div>
<div class="wallet"> <div class="service-box" @click="$showManagerPopup()">
<div class="tit"> 管理中心
<span @click="$navigate('MerchantIncome')"> 营业收入() <van-icon name="arrow"></van-icon> <van-icon name="arrow"></van-icon>
</div>
</div>
<div class="wallet">
<div class="tit">
<span @click="$navigate('MerchantIncome')"> 营业收入() <van-icon name="arrow"></van-icon>
</span>
<div class="line">
<b @click="$navigate('MerchantIncome')">
{{ data.shangjiashouru?.toFixed(2) }}
</b>
<!-- v-if="data.Income" -->
<div @click="$navigate('MerchantCashout')">
<span>提现中金额
</span>
<p>{{ data.withdrawing?.toFixed(2) }}</p>
</div>
</div>
</div>
<div class="cc">
<div class="ll" @click="$navigate('Balance')">
<span>
余额<van-icon name="arrow"></van-icon>
</span> </span>
<div class="line"> <b>{{ data.keyongyue?.toFixed(2) }}</b>
<b @click="$navigate('MerchantIncome')">
{{ data.shangjiashouru?.toFixed(2) }}
</b>
<!-- v-if="data.Income" -->
<div @click="$navigate('MerchantCashout')">
<span>提现中金额
</span>
<p>{{ data.withdrawing?.toFixed(2) }}</p>
</div>
</div>
</div> </div>
<div class="cc"> <div class="ll" @click="$navigate('CV')">
<div class="ll" @click="$navigate('Balance')"> <span>
<span> 会员卡<van-icon name="arrow"></van-icon>
可用余额<van-icon name="arrow"></van-icon> </span>
</span> <b>{{ data.huiyuanka?.toFixed(2) }}</b>
<b>{{ data.keyongyue?.toFixed(2) }}</b>
</div>
<div class="ll" @click="$navigate('CV')">
<span>
会员卡额度<van-icon name="arrow"></van-icon>
</span>
<b>{{ data.huiyuanka?.toFixed(2) }}</b>
</div>
</div> </div>
</div> </div>
</div>
<!-- <div class="block"> <!-- <div class="block">
<div class="d1" @click="$navigate('MerchantTrade')"> <div class="d1" @click="$navigate('MerchantTrade')">
<b>订单记录</b> <b>订单记录</b>
<p>查看订单记录</p> <p>查看订单记录</p>
@ -73,104 +73,97 @@
</div> --> </div> -->
<div class="fastTo"> <div class="fastTo">
<div class="ft1" @click="showBottom = true"> <div class="ft1" @click="$navigate('PayCode?id=' + id)">
<span>收款码</span> <span>收款码</span>
<img src="/img/ft_i1.png" alt=""> <img src="/img/ft_i1.png" alt="">
</div>
<div class="ft2" @click="$navigate('MerchantTrade')">
<span>订单记录</span>
<img src="/img/ft_i2.png" alt="">
</div>
<div class="ft3" @click="$navigate('MerchantIntroduction')">
<span>商家资料</span>
<img src="/img/ft_i3.png" alt="">
</div>
</div> </div>
<div class="countbox"> <div class="ft2" @click="$navigate('MerchantTrade')">
<div class="title"> <span>订单记录</span>
<b>经营数据</b> <img src="/img/ft_i2.png" alt="">
</div>
<div class="box line">
<span>截止{{ data.endTimes }}</span>
<p class="refresh" @click="getCount">刷新</p>
<p class="cou r" @click="$navigate('Statistics')">统计<van-icon name="arrow"></van-icon></p>
</div>
<div class="list">
<div class="item">
<span>
今日营业额
</span>
<b>{{ countData.todayyingyee?.toFixed(2) }}</b>
<p>昨日<span>{{ countData.yesterdayyingyee?.toFixed(2) }}</span></p>
</div>
<div class="item">
<span>
今日应收
</span>
<b>{{ countData.todayyingshou?.toFixed(2) }}</b>
<p>昨日<span>{{ countData.yesterdayingshou?.toFixed(2) }}</span></p>
</div>
<div class="item">
<span>
今日订单数
</span>
<b>{{ countData.todaydingdanshu }}</b>
<p>昨日<span>{{ countData.yesterdaydingdanshu }}</span></p>
</div>
<div class="item">
<span>
今日惠利金额
</span>
<b>{{ countData.todayyouhui?.toFixed(2) }}</b>
<p>昨日<span>{{ countData.yesterdayyouhui?.toFixed(2) }}</span></p>
</div>
</div>
</div> </div>
<div class="ft3" @click="$navigate('MerchantIntroduction')">
<span>商家资料</span>
<img src="/img/ft_i3.png" alt="">
</div>
</div> </div>
<van-action-sheet v-model:show="showBottom" title="收款码" @click-overlay="onCloseBottom" @opened="onOpenBottom"> <div class="countbox">
<div class="paycode-wrap"> <div class="title">
<div class="paycode-original" ref="paycodeOriginal" v-show="!paycodeImg"> <b>经营数据</b>
<img src="/img/paycode.jpg" alt="" style="width: 100%; display: block;"> </div>
<div class="info"> <div class="box line">
<img :src="$file(data.shopimg)" alt="" style="border-radius: 50%;"> <span>截止{{ data.endTimes }}</span>
</div> <p class="refresh" @click="getCount">刷新</p>
<div class="name"> <p class="cou r" @click="$navigate('Statistics')">统计<van-icon name="arrow"></van-icon></p>
<span>{{ data.shopname }}</span> </div>
</div>
<div class="code"> <div class="list">
<vue-qr v-if="!loading" :margin="0" :text="link" backgroundColor="rgb(255,255,255)" <div class="item">
colorLight="rgb(255,255,255)"></vue-qr> <span>
</div> 今日营业额
</span>
<b>{{ countData.todayyingyee?.toFixed(2) }}</b>
<p>昨日<span>{{ countData.yesterdayyingyee?.toFixed(2) }}</span></p>
</div> </div>
<div class="paycode-result" v-if="paycodeImg">
<img :src="paycodeImg" style="width: 100%;" alt="收款码"> <div class="item">
<span>
今日应收
</span>
<b>{{ countData.todayyingshou?.toFixed(2) }}</b>
<p>昨日<span>{{ countData.yesterdayingshou?.toFixed(2) }}</span></p>
</div>
<div class="item">
<span>
今日订单数
</span>
<b>{{ countData.todaydingdanshu }}</b>
<p>昨日<span>{{ countData.yesterdaydingdanshu }}</span></p>
</div>
<div class="item">
<span>
今日惠利金额
</span>
<b>{{ countData.todayyouhui?.toFixed(2) }}</b>
<p>昨日<span>{{ countData.yesterdayyouhui?.toFixed(2) }}</span></p>
</div> </div>
</div> </div>
</van-action-sheet> </div>
</BasePage>
</div>
<!-- </BasePage> -->
</template> </template>
<script> <script>
import { html2canvas } from '@/utils/html2image'
import ManagerPopup from "@/components/ManagerPopup.vue" import ManagerPopup from "@/components/ManagerPopup.vue"
export default { export default {
name: 'Merchant', name: 'Merchant',
components: { ManagerPopup }, components: { ManagerPopup },
mounted() { mounted() {
this.init() this.$get('/v1/client/DUsersClient').then(res => {
this.id = res.data.id;
if (!res.data.isshop) {
this.$showFailToast('您不是商家账号!')
// localStorage.removeItem('member_token')
location.replace('#/My')
return
}
this.init();
}).catch(() => {
this.$showFailToast('登录状态异常,请重新登录')
// localStorage.removeItem('member_token')
location.replace('#/Login')
})
window.addEventListener('showManagerPopup', this.onShowManagerPopup) window.addEventListener('showManagerPopup', this.onShowManagerPopup)
window.addEventListener('closeManagerPopup', this.onCloseManagerPopup) window.addEventListener('closeManagerPopup', this.onCloseManagerPopup)
}, },
@ -181,33 +174,11 @@ export default {
data() { data() {
return { return {
managerPopupVisible: false, managerPopupVisible: false,
showInfo: false, id: this.$route.query.id || this.$ls.get('user_id'),
showBottom: false,
paycodeImg: '',
isCapturing: false,
link: '',
id: this.$route.query.id,
data: { data: {
id: 'merchant_001',
MerchantAvatar: '',
MerchantName: '泰古润直营店',
MerchantPhone: '13800138000',
Income: 12868.50,
Point: 5680.00,
TodayAmount: 5680.00,
YesterdayAmount: 4200.00,
TodayDeduct: 1280.00,
YesterdayDeduct: 980.00,
TodayQty: 45,
YesterdayQty: 38,
MonthIncome: 45600.00,
PrevMonthIncome: 38500.00,
endTimes: new Date().toLocaleString('zh-CN', { hour12: false }) endTimes: new Date().toLocaleString('zh-CN', { hour12: false })
}, },
countData: {}, countData: {},
States: 0,
expiring: 3,
loading: false,
} }
}, },
computed: { computed: {
@ -226,87 +197,14 @@ export default {
init() { init() {
Promise.all([ Promise.all([
this.$get(`/v1/client/DShopsClient/${this.id}`).then(res => { this.$get(`/v1/client/DShopsClient/${this.id}`).then(res => {
// console.log(res);
this.data = res.data; this.data = res.data;
this.data.endTimes = new Date().toLocaleString('zh-CN', { hour12: false }); this.data.endTimes = new Date().toLocaleString('zh-CN', { hour12: false });
this.$ls.set('merchant_shopname', res.data.shopname); this.$ls.set('merchant_shopname', res.data.shopname);
this.$ls.set('merchant_shopimg', res.data.shopimg); this.$ls.set('merchant_shopimg', res.data.shopimg);
this.$ls.set('merchant_cellphone', res.data.cellphone); this.$ls.set('merchant_cellphone', res.data.cellphone);
this.link = `${location.origin}${location.pathname}#/Checkout?id=${res.data.userid}`
}), }),
this.getCount() this.getCount()
] ])
)
},
async onOpenBottom() {
this.paycodeImg = ''
this.$nextTick(async () => {
const el = this.$refs.paycodeOriginal
if (!el) return
const scale = 2
const elRect = el.getBoundingClientRect()
//
//
const canvas = document.createElement('canvas')
const w = el.offsetWidth * scale
const h = el.offsetHeight * scale
canvas.width = w
canvas.height = h
const ctx = canvas.getContext('2d')
// img
const bg = el.querySelector('img')
const bgCanvas = await html2canvas(bg, { scale, useCORS: true, width: el.offsetWidth, height: el.offsetHeight })
ctx.drawImage(bgCanvas, 0, 0, w, h)
//
const infoEl = el.querySelector('.info')
const avatarEl = infoEl.querySelector('img')
const avatarCanvas = await html2canvas(avatarEl, { scale, useCORS: true, backgroundColor: 'transparent' })
const infoRect = infoEl.getBoundingClientRect()
const avatarSize = 30 * scale
const avatarX = (infoRect.left - elRect.left) * scale
const avatarY = (infoRect.top - elRect.top) * scale
ctx.save()
ctx.beginPath()
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2)
ctx.clip()
ctx.drawImage(avatarCanvas, avatarX, avatarY, avatarSize, avatarSize)
ctx.restore()
//
const codeEl = el.querySelector('.code')
const codeCanvas = await html2canvas(codeEl, { scale, useCORS: true, backgroundColor: '#ffffff' })
const codeRect = codeEl.getBoundingClientRect()
ctx.drawImage(
codeCanvas,
(codeRect.left - elRect.left) * scale,
(codeRect.top - elRect.top) * scale,
codeRect.width * scale,
codeRect.height * scale
)
// (.name)
const nameEl = el.querySelector('.name')
const nameRect = nameEl.getBoundingClientRect()
const fontSize = 20 * scale
ctx.font = `${fontSize}px 'PingFang SC'`
ctx.fillStyle = '#333'
ctx.textBaseline = 'middle'
if (this.data.shopname)
ctx.fillText(this.truncatedShopname, (nameRect.left - elRect.left) * scale, (nameRect.top - elRect.top) * scale + fontSize / 2)
this.paycodeImg = canvas.toDataURL('image/png')
})
},
onCloseBottom() {
this.showBottom = false
// this.paycodeImg = ''
}, },
getCount() { getCount() {
this.$get('/v1/client/DShopsClient/statistics').then(res => { this.$get('/v1/client/DShopsClient/statistics').then(res => {
@ -319,52 +217,4 @@ export default {
</script> </script>
<style lang="less"> <style lang="less"></style>
.paycode-original {
position: relative;
width: 100%;
font-family: 'PingFang SC';
>img {
width: 100%;
display: block;
}
.info {
display: flex;
align-items: center;
position: absolute;
left: 30.67vw;
top: 32.27vw;
img {
width: 6.4vw;
height: 6.4vw;
border-radius: 50%;
}
}
.name {
position: absolute;
left: 39.33vw;
top: 33.47vw;
span {
font-size: 4vw;
color: #333;
}
}
.code {
position: absolute;
top: 42.4vw;
left: 23.33vw;
canvas,
img {
width: 53.33vw;
height: 53.33vw;
}
}
}
</style>

View File

@ -0,0 +1,187 @@
<template>
<BasePage>
<div class="paycode-page">
<div class="paycode-wrap">
<!-- 生成后的图片 -->
<div v-if="generatedImage" class="paycode-generated">
<img :src="generatedImage" alt="收款码">
</div>
<!-- 原始元素 -->
<div v-else class="paycode-original" ref="paycodeOriginalRef">
<img src="/img/paycode.jpg" alt="" style="width: 100%; display: block;" class="bg-img" @load="onBgLoad">
<div class="user-info">
<div class="info">
<img :src="$file(shopimg)" alt="">
</div>
<div class="name">
<span>{{ shopname?.length > 8 ? shopname.slice(0, 8) + '...' : shopname }}</span>
</div>
</div>
<div class="code">
<vue-qr v-if="link" :margin="0" :text="link" backgroundColor="rgb(255,255,255)"
colorLight="rgb(255,255,255)"></vue-qr>
</div>
</div>
</div>
<div class="save-btn">
<van-button type="primary" color="#ca2904" round block :loading="loading" @click="handleSave">{{ loading ?
'生成中...' : ('保存图片')
}}</van-button>
</div>
</div>
</BasePage>
</template>
<script>
import { toDataURL, downloadByDataURL } from '@/utils/html2image'
export default {
name: 'PayCode',
data() {
return {
shopname: '',
shopimg: '',
link: '',
loading: false,
generatedImage: '',
bgLoaded: false,
}
},
mounted() {
this.init()
},
methods: {
init() {
const id = this.$route.query.id
this.$get(`/v1/client/DShopsClient/${id}`).then(res => {
this.shopname = res.data.shopname
this.shopimg = res.data.shopimg
this.link = `${location.origin}${location.pathname}#/Checkout?id=${res.data.userid}`
this.$nextTick(() => {
this.generateImage()
})
}).catch(err => {
this.$showFailToast('加载失败')
})
},
onBgLoad() {
this.bgLoaded = true
},
async generateImage() {
if (this.loading) return
this.loading = true
try {
await this.$nextTick()
// Wait for fonts with fallback for WeChat/Safari compatibility
if (document.fonts && document.fonts.ready) {
await document.fonts.ready
}
await new Promise(resolve => setTimeout(resolve, 500))
const el = this.$refs.paycodeOriginalRef
if (!el) {
throw new Error('Element not found')
}
this.generatedImage = await toDataURL(el, { format: 'png', pixelRatio: 3, useCORS: true })
} catch (e) {
console.error('生成收款码失败:', e)
this.$showFailToast('生成失败')
} finally {
this.loading = false
}
},
async handleSave() {
if (this.loading) return
try {
this.loading = true
if (!this.generatedImage) {
await this.generateImage()
}
if (this.generatedImage) {
downloadByDataURL(this.generatedImage, `收款码_${this.shopname}`)
this.$showSuccessToast('保存成功')
}
} catch (e) {
console.error('保存失败:', e)
this.$showFailToast('保存失败')
} finally {
this.loading = false
}
},
},
}
</script>
<style lang="less" scoped>
.paycode-page {
// min-height: 100vh;
background: #f5f5f5;
padding-bottom: 20px;
}
.paycode-generated {
img {
width: 100%;
display: block;
}
}
.paycode-original {
position: relative;
width: 100%;
font-family: 'PingFang SC';
>img {
width: 100%;
display: block;
}
.user-info {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 32.27vw;
display: flex;
align-items: center;
}
.info {
display: flex;
align-items: center;
img {
width: 6.4vw;
height: 6.4vw;
// border-radius: 50%;
}
}
.name {
margin-left: 2.67vw;
// margin-bottom: 2.4vw;
span {
font-size: 4vw;
color: #333;
}
}
.code {
position: absolute;
top: 42.4vw;
left: 23.33vw;
canvas,
img {
width: 53.33vw;
height: 53.33vw;
}
}
}
.save-btn {
margin: 20px;
}
</style>

View File

@ -1,90 +1,90 @@
<template> <template>
<BasePage> <!-- <BasePage> -->
<ManagerPopup v-model="managerPopupVisible" /> <ManagerPopup v-model="managerPopupVisible" />
<div class="opera"> <div class="opera">
<div class="top"> <div class="top">
<div class="info_box"> <div class="info_box">
<img class="icon" :src="$file(userimg || '/img/default-avatar.png')" alt=""> <img class="icon" :src="$file(userimg || '/img/default-avatar.png')" alt="">
<div class="inf"> <div class="inf">
<b>{{ nickname || '未登录' }}</b> <b>{{ nickname || '未登录' }}</b>
<div> <div>
<img src="/img/phone.png" alt=""> <img src="/img/phone.png" alt="">
{{ cellphone || '暂无手机号' }} {{ cellphone || '暂无手机号' }}
</div>
</div> </div>
</div> </div>
<div class="shopcenter" @click="$showManagerPopup()">
<span>
管理中心
<van-icon name="arrow" />
</span>
</div>
</div> </div>
<div class="containar"> <div class="shopcenter" @click="$showManagerPopup()">
<div class="wallet_box"> <span>
<div class="item" @click="$navigate('Balance')"> 管理中心
<div class="t"> <van-icon name="arrow" />
<img src="/img/op-i1.png" alt=""> </span>
<span>余额</span> </div>
<van-icon name="arrow"></van-icon> </div>
</div>
<b>
{{ zijin?.toFixed(2) || '0.00' }}
</b>
</div>
<div class="item" @click="$navigate('CV')"> <div class="containar">
<div class="t"> <div class="wallet_box">
<img src="/img/op-i2.png" alt=""> <div class="item" @click="$navigate('Balance')">
<span>会员卡</span> <div class="t">
<van-icon name="arrow"></van-icon> <img src="/img/op-i1.png" alt="">
</div> <span>余额</span>
<b> <van-icon name="arrow"></van-icon>
{{ xiaofeiquan?.toFixed(2) || '0.00' }}
</b>
</div> </div>
<b>
{{ zijin?.toFixed(2) || '0.00' }}
</b>
</div> </div>
<div class="fun_box"> <div class="item" @click="$navigate('CV')">
<div class="item" @click="$navigate('QrReader')"> <div class="t">
<div class="left"> <img src="/img/op-i2.png" alt="">
<b> <span>会员卡</span>
扫一扫 <van-icon name="arrow"></van-icon>
</b> </div>
<p>核销礼品券</p> <b>
</div> {{ xiaofeiquan?.toFixed(2) || '0.00' }}
</b>
</div>
</div>
<img src="/img/op-i3.png" alt=""> <div class="fun_box">
<div class="item" @click="$navigate('QrReader')">
<div class="left">
<b>
扫一扫
</b>
<p>核销礼品券</p>
</div> </div>
<div class="item" @click="$navigate('CertificateRecord?type=verif')"> <img src="/img/op-i3.png" alt="">
<div class="left"> </div>
<b>
核销记录
</b>
<p>查看礼品券核销记录</p>
</div>
<img src="/img/op-i4.png" alt=""> <div class="item" @click="$navigate('CertificateRecord?type=verif')">
<div class="left">
<b>
核销记录
</b>
<p>查看礼品券核销记录</p>
</div> </div>
<div class="item" @click="$navigate('Allow')"> <img src="/img/op-i4.png" alt="">
<div class="left"> </div>
<b>
业绩统计
</b>
<p>查看业绩统计数据</p>
</div>
<img src="/img/op-i5.png" alt=""> <div class="item" @click="$navigate('Allow')">
<div class="left">
<b>
业绩统计
</b>
<p>查看业绩统计数据</p>
</div> </div>
<img src="/img/op-i5.png" alt="">
</div> </div>
</div> </div>
</div> </div>
</BasePage> </div>
<!-- </BasePage> -->
</template> </template>
<script> <script>

View File

@ -6,14 +6,14 @@
<!-- <img style="width: 26.4vw;" src="/img/bs-icon.png" alt=""> --> <!-- <img style="width: 26.4vw;" src="/img/bs-icon.png" alt=""> -->
<div style="margin-top: 3vw;" class="box box-align-center"> <div style="margin-top: 3vw;" class="box box-align-center">
<van-tabs v-model:active="activeTab" color="#ca2904" title-active-color="#ca2904" class="tabs" <van-tabs v-model:active="activeTab" color="#ca2904" title-active-color="#ca2904" class="tabs"
line-height="0.67vw" line-width="8vw" :ellipsis="false" @click-tab="changeTab(category.id)"> line-height="0.67vw" line-width="8vw" :ellipsis="false">
<van-tab v-for="category in categories" :key="category.id" :name="category.id" <van-tab v-for="category in categories" :key="category.id" :name="category.id"
:title="category.name"></van-tab> :title="category.name"></van-tab>
</van-tabs> </van-tabs>
<!-- <img class="r" @click="showLeft = true" style="height: 4vw;" src="/img/filter.png" alt="filter"> --> <!-- <img class="r" @click="showLeft = true" style="height: 4vw;" src="/img/filter.png" alt="filter"> -->
</div> </div>
</div> </div>
<BaseList :url="`/v1/client/CBusinessschoolsClient`" :params="params" class="list_item"> <BaseList cache-key="Business_School" :url="`/v1/client/CBusinessschoolsClient`" :params="params" class="list_item">
<template #default="{ item }"> <template #default="{ item }">
<div class="school-list" :key="item.id" @click="$navigate(`/School_Detail?id=${item.id}`)"> <div class="school-list" :key="item.id" @click="$navigate(`/School_Detail?id=${item.id}`)">
<div class="img"> <div class="img">
@ -28,7 +28,7 @@
</div> </div>
<div class="times"> <div class="times">
<span>{{ $formatGMT(item.addtime, 'yyyy-MM-dd HH:mm:ss') }}</span> <span>{{ $formatGMT(item.addtime, 'yyyy-MM-dd HH:mm:ss') }}</span>
<span>阅览量 {{ item.ReadCount }}</span> <span>阅览量 {{ item.click }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -80,9 +80,7 @@ export default {
activeTab: 1, activeTab: 1,
showLeft: false, showLeft: false,
categories: [], categories: [],
params: { _categoriesCache: null,
pid: 1
}
} }
}, },
computed: { computed: {
@ -97,19 +95,20 @@ export default {
}, },
methods: { methods: {
getList() { getList() {
if (this._categoriesCache) {
this.categories = this._categoriesCache
return
}
this.$get('/v1/client/CBusinessschoolcatesClient',).then(res => { this.$get('/v1/client/CBusinessschoolcatesClient',).then(res => {
this.categories = res.data; this.categories = res.data;
// console.log(this.categories); this._categoriesCache = res.data;
}) })
},
changeTab(id) {
this.activeTab = id;
this.showLeft = false;
} }
}, },
onTabClick({ name }) {
this.$navigate(`/School_Category?id=${name}`)
},
changeTab(id) {
this.activeTab = id;
this.showLeft = false;
}
} }
</script> </script>

View File

@ -1,13 +1,13 @@
<template> <template>
<!-- :title="currentColumn.name || '栏目列表'" --> <!-- -->
<BasePage title="新闻列表"> <BasePage :title="currentColumn.name || ''">
<!-- <div class="column-list" v-if="currentColumn"> --> <!-- <div class="column-list" v-if="currentColumn"> -->
<!-- v-if="currentColumn.ColumnType === 'list'" --> <!-- v-if="currentColumn.ColumnType === 'list'" -->
<div class="column-list"> <div class="column-list b_l_w" v-if="articleList.length > 0">
<div> <div>
<div v-if="articleList.length > 0" @click="$navigate(`/ColumnDetail?id=${item.id}`)" class="column" <div @click="$navigate(`/ColumnDetail?id=${item.id}`)" class="column" v-for="item in articleList"
v-for="item in articleList" :key="item.id"> :key="item.id">
<img :src="$file(item.img)" class="icon" /> <img v-if="item.img" :src="$file(item.img)" class="icon" />
<div class="overview"> <div class="overview">
<span class="title">{{ item.name }}</span> <span class="title">{{ item.name }}</span>
<div class="desc"> <div class="desc">
@ -19,7 +19,7 @@
</div> </div>
</div> </div>
<!-- <van-empty v-else class="b_l_w" description="暂无数据" /> --> <van-empty v-else class="b_l_w" description="暂无数据" />
<!-- </div> --> <!-- </div> -->
<!-- <van-empty v-else class="b_l_w" description="栏目不存在" /> --> <!-- <van-empty v-else class="b_l_w" description="栏目不存在" /> -->
</BasePage> </BasePage>
@ -30,9 +30,10 @@ export default {
name: 'Column', name: 'Column',
emits: ['updateShare'], emits: ['updateShare'],
mounted() { mounted() {
this.$get('/v1/client/CNewsClient?pid=3').then(data => { this.$get(`/v1/client/CNewsClient?pid=${this.pid}`).then(data => {
// console.log(data); // console.log(data);
this.articleList = data.data.items; this.articleList = data.data.items;
this.currentColumn.name = data.data.items[0].pidname;
}) })
}, },
data() { data() {
@ -40,6 +41,8 @@ export default {
columns: [], columns: [],
articles: [], articles: [],
articleList: [], articleList: [],
currentColumn: {},
pid: this.$route.query.pid || 3,
} }
}, },
@ -47,8 +50,11 @@ export default {
} }
</script> </script>
<style> <style scoped>
.column-list { .column-list {
display: flex;
flex-direction: column;
.column { .column {
display: flex; display: flex;
padding: 3.333vw; padding: 3.333vw;

View File

@ -31,6 +31,17 @@ export default {
} }
}, },
mounted() { mounted() {
const cacheKey = `ColumnDetail_${this.id}`
const cached = sessionStorage.getItem(cacheKey)
if (cached) {
try {
this.article = JSON.parse(cached)
this.$nextTick(() => {
this.setupImagePreview();
});
return
} catch (e) {}
}
this.getDetail(this.id); this.getDetail(this.id);
this.$nextTick(() => { this.$nextTick(() => {
this.setupImagePreview(); this.setupImagePreview();
@ -40,6 +51,7 @@ export default {
getDetail(id) { getDetail(id) {
this.$get(`/v1/client/CNewsClient/${id}`).then(data => { this.$get(`/v1/client/CNewsClient/${id}`).then(data => {
this.article = data.data; this.article = data.data;
sessionStorage.setItem(`ColumnDetail_${id}`, JSON.stringify(data.data))
}).catch(err => { }).catch(err => {
this.$showFailToast(err.message); this.$showFailToast(err.message);
}); });
@ -91,6 +103,13 @@ export default {
.content { .content {
max-width: 100%; max-width: 100%;
padding: 3.333vw; padding: 3.333vw;
overflow: hidden;
img {
max-width: 100%;
display: block;
margin: 10px auto;
}
} }
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<template> <template>
<BasePage :title="article.Title"> <BasePage :title="article.name">
<div class="b_l_w" style="padding: 3.333vw; border-bottom: 1px solid #ebedf0"> <div class="b_l_w" style="padding: 3.333vw; border-bottom: 1px solid #ebedf0">
<span style="font-size: 24px">{{ article.name }}</span> <span style="font-size: 24px">{{ article.name }}</span>
<div style="display: flex; justify-content: space-between; margin-top: 12px"> <div style="display: flex; justify-content: space-between; margin-top: 12px">
@ -24,9 +24,10 @@ export default {
} }
}, },
beforeCreate() { beforeCreate() {
this.$get(`/v1/client/CBusinessschoolsClient/${this.$route.query.id}`).then(res => { const id = this.$route.query.id
this.$get(`/v1/client/CBusinessschoolsClient/${id}`).then(res => {
this.article = res.data; this.article = res.data;
// console.log(this.article); // sessionStorage.setItem(cacheKey, JSON.stringify(res.data))
}) })
}, },
mounted() { mounted() {
@ -36,26 +37,26 @@ export default {
}, },
methods: { methods: {
setupImagePreview() { // setupImagePreview() {
const contentEl = this.$refs.contentRef; // const contentEl = this.$refs.contentRef;
if (!contentEl) return; // if (!contentEl) return;
const images = contentEl.querySelectorAll('img'); // const images = contentEl.querySelectorAll('img');
this.imageUrls = Array.from(images).map(img => img.src); // this.imageUrls = Array.from(images).map(img => img.src);
images.forEach((img, index) => { // images.forEach((img, index) => {
img.addEventListener('click', () => { // img.addEventListener('click', () => {
this.previewImages(index); // this.previewImages(index);
}); // });
}); // });
}, // },
previewImages(startPosition) { // previewImages(startPosition) {
this.$showImagePreview({ // this.$showImagePreview({
images: this.imageUrls, // images: this.imageUrls,
startPosition, // startPosition,
closeable: true, // closeable: true,
}); // });
} // }
} }
} }
</script> </script>

View File

@ -3,21 +3,27 @@
</template> </template>
<script> <script>
import { useUserStore } from '@/stores/user'
export default { export default {
mounted() { mounted() {
if (this.token) if (this.token) {
localStorage.setItem('member_token', this.token); localStorage.setItem('member_token', this.token)
// if (this.miniopenid) const userStore = useUserStore()
// localStorage.setItem('miniopenid', this.miniopenid); userStore.setToken(this.token)
if (this.target) setTimeout(() => {
location.replace(`#/${this.target}`); const target = this.target ? `/${this.target}` : '/Home'
else // console.log(target);
location.replace('#/');
this.$router.replace(target)
}, 200)
} else {
this.$router.replace('/Home')
}
}, },
data() { data() {
return { return {
token: decodeURIComponent(this.$route.query.token || ''), token: decodeURIComponent(this.$route.query.token || ''),
// miniopenid: decodeURIComponent(this.$route.query.miniopenid || ''),
target: decodeURIComponent(this.$route.query.target || ''), target: decodeURIComponent(this.$route.query.target || ''),
} }
}, },

View File

@ -1,123 +0,0 @@
<template>
<div class="agriculture">
<div class="top">
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item>
<img style="width: 100%;" src="/img/agri_bg.png" alt="">
</van-swipe-item>
</van-swipe>
<div class="search">
<img src="/img/search-w.png" alt="">
<input type="text" placeholder="搜索您想要的产品">
</div>
</div>
<div class="tabs">
<van-tabs style="max-width: 90%;" line-height="0" v-model:active="active" :ellipsis="false"
title-active-color="#13774d" title-inactive-color="#4e4e4e">
<van-tab title="新品" name=""></van-tab>
<van-tab v-for="i in tabs" :name="i.id" :title="i.name"></van-tab>
</van-tabs>
<img @click="showLeft = true" class="r" style="width: 5.4vw;" src="/img/filter.png" alt="">
</div>
<BaseList url="/v1/client/EProsClient" class="goods_list" :params="{ mallstate: 2, pid: active }">
<template #default="{ item }">
<div class="goods_item" :key="item.id" @click="toDetail(item.id)">
<img :src="$file(item.img)" alt="">
<div>
<span class="title">{{ item.name }}</span>
<p>¥{{ item.originalprice }}</p>
<div class="money">
<div>
<span>¥
<b>{{ item.saleprice }}</b>.00
</span>
</div>
<img class="r" style="width: 5.6vw;" src="/img/cart.png" alt="">
</div>
</div>
</div>
</template>
</BaseList>
<!-- <div class="goods_list">
<div class="goods_item" v-for="item in goods" :key="item.id" @click="toDetail(item.id)">
<img :src="item.Icon" alt="">
<div>
<span class="title">{{ item.SubTitle }}</span>
<p>¥{{ item.OPrice }}</p>
<div class="money">
<div>
<span>¥
<b>{{ item.Price }}</b>.00
</span>
</div>
<img class="r" style="width: 5.6vw;" src="/img/cart.png" alt="">
</div>
</div>
</div>
</div> -->
</div>
<van-popup v-model:show="showLeft" position="left" :style="{ width: '30%', height: '100%' }">
<div v-for="i in tabs">
<div @click="changeTab(i.id)" style="height: 10vw;width: 100%;border-bottom: solid 1px #f5f5f5;"
class="box box-center-center">
{{ i.name }}
</div>
</div>
</van-popup>
</template>
<script>
export default {
name: 'Agriculture',
activated() {
this.getTab()
},
data() {
return {
active: '',
showLeft: false,
tabs: []
}
},
mounted() {
this.getTab()
},
methods: {
getTab() {
this.$get('/v1/client/EProcatesClient/2').then(res => {
this.tabs = res.data
})
},
changeTab(id) {
this.active = id;
this.showLeft = false;
},
toDetail(e) {
this.$navigate(`/GoodsDetail?id=${e}`)
}
},
computed: {
filteredGoods() {
// if (this.active === 0) {
// return this.goods
// }
// const categoryId = this.active
// return this.goods.filter(item => item.CategoryId === categoryId)
}
}
}
</script>
<style>
.finished-text {
width: 100%;
}
</style>

View File

@ -38,33 +38,21 @@
</div> </div>
</div> </div>
<!-- v-if="selected.id" --> <!-- v-if="selected.id" -->
<div class="detail"> <div class="detail" v-if="selected.id">
<!-- <div> <div v-if="selected.jifen">
<span class="title"> <span class="title">
购买产品数量 赠送积分数额
</span>
<span class="r">
<van-stepper v-model="value" />
</span>
</div> -->
<div>
<span class="title">
赠送积分
</span>
<b class="r"> 1980.00
<!-- {{
// (selected.saleprice * value || 0)?.toFixed(2)
}} -->
</b>
</div>
<div>
<span class="title">
赠送会员卡额度
</span> </span>
<b class="r"> <b class="r">
<!-- {{ (Number(selected.gongxianzhi) || 0)?.toFixed(2) }} --> {{ selected.jifen?.toFixed(2) }}
<span></span> 6000.00 </b>
</div>
<div v-if="selected.huiyuanka">
<span class="title">
赠送会员卡额
</span>
<b class="r">
<span></span> {{ selected.huiyuanka?.toFixed(2) }}
</b> </b>
</div> </div>
</div> </div>
@ -125,7 +113,7 @@
<!-- <van-checkbox icon-size="2.667vw" class="agreement" v-model="checked" checked-color="#85cb58"> <!-- <van-checkbox icon-size="2.667vw" class="agreement" v-model="checked" checked-color="#85cb58">
已阅读并同意<a @click.stop="showContract = true">保供臻选商务权益礼包电子协议</a> 已阅读并同意<a @click.stop="showContract = true">保供臻选商务权益礼包电子协议</a>
</van-checkbox> --> </van-checkbox> -->
</div> </div>
</div> </div>
<!-- </div> --> <!-- </div> -->
@ -140,7 +128,7 @@
请选择礼包产品 请选择礼包产品
</div> </div>
<b style="color: #f00;font-size: 3.2vw;"> <b style="color: #f00;font-size: 3.2vw;">
礼包产品一经售出概不退还 礼包产品为活动产品不支持退货退款
</b> </b>
</div> </div>
</div> </div>
@ -160,7 +148,7 @@
<div class="name"> <div class="name">
<b>{{ item.name }}</b> <b>{{ item.name }}</b>
<div class="text"> <div class="text">
{{ item.skuname }} {{ item.description }}
<b style="line-height: 5.6vw;"> <b style="line-height: 5.6vw;">
<span><span></span>{{ item.saleprice }}</span> <span><span></span>{{ item.saleprice }}</span>
<!-- <span> /{{ item.skuname }}</span> --> <!-- <span> /{{ item.skuname }}</span> -->
@ -201,7 +189,7 @@
<!-- <BasePwd v-model:show="showPayPwd" :title="`交易密码`" @val="submitOrder" /> --> <!-- <BasePwd v-model:show="showPayPwd" :title="`交易密码`" @val="submitOrder" /> -->
<van-action-sheet v-model:show="showDetail" title="权益详情" style="min-height: 60vh;padding: 3.333vw;"> <van-action-sheet v-model:show="showDetail" title="权益详情" style="min-height: 60vh;padding: 3.333vw;">
<div class="w100" v-html="data.SpuDetail"></div> <div class="w100 html" v-html="data.SpuDetail"></div>
</van-action-sheet> </van-action-sheet>
<van-action-sheet v-model:show="showContract" safe-area-inset-bottom title="保供臻选商务权益礼包电子协议" closeable <van-action-sheet v-model:show="showContract" safe-area-inset-bottom title="保供臻选商务权益礼包电子协议" closeable
@ -254,7 +242,8 @@ export default {
}, },
methods: { methods: {
init() { init() {
Promise.all([]) Promise.all([
])
}, },
onAddressConfirm(address) { onAddressConfirm(address) {
this.address.ReceiveName = address.ReceiveName; this.address.ReceiveName = address.ReceiveName;
@ -283,8 +272,17 @@ export default {
this.submitOrder(); this.submitOrder();
}, },
onShowDetail(e) { onShowDetail(e) {
this.data.SpuDetail = e.SpuDetail; this.$get(`/v1/client/EProsClient/${e.id}`)
this.showDetail = true; .then(res => {
this.data.SpuDetail = res.data.contents;
this.showDetail = true;
})
.catch(() => {
this.$showFailToast('数据加载失败');
});
// this.data.SpuDetail = e.SpuDetail;
// this.spuid = e.id;
// this.showDetail = true;
}, },
onRefresh() { onRefresh() {
this.$refs.BaseList.refresh() this.$refs.BaseList.refresh()

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="index-page"> <div class="index-page">
<van-swipe v-if="data && data.Carousel" @change="onSwipe" :autoplay="3000" indicator-color="white"> <van-swipe v-if="data && data.Carousel" :autoplay="3000" indicator-color="white">
<van-swipe-item v-for="item in data.Carousel"> <van-swipe-item v-for="item in data.Carousel">
<a @click="item.link ? $openUrl(item.Url) : ''"><img :src="$file(item.img) || '/img/avatar.png'" <a @click="item.link ? $openUrl(item.Url) : ''"><img :src="$file(item.img) || '/img/avatar.png'"
width="100%"></a> width="100%"></a>
@ -16,10 +16,13 @@
</div> </div>
<van-swipe vertical v-bind:touchable="false" class="notice-swipe" style="height: 12vw" :autoplay="3000" <van-swipe vertical v-bind:touchable="false" class="notice-swipe" style="height: 12vw" :autoplay="3000"
v-bind:show-indicators="false"> v-bind:show-indicators="false">
<van-swipe-item class="v" style="height: 12vw; line-height: 6vw" v-for="item in data.Trends" :key="index"> <van-swipe-item class="v" style="height: 12vw; line-height: 6vw" v-for="(group, index) in trendsGrouped"
<a :key="item.id" @click="$navigate(`/ColumnDetail?id=${item.id}`)"> :key="index">
<span></span>{{ item.name }} <template v-for="item in group">
</a> <a @click="$navigate(`/Column?pid=2`)">
<span></span>{{ item.name }}
</a>
</template>
</van-swipe-item> </van-swipe-item>
</van-swipe> </van-swipe>
</div> </div>
@ -27,7 +30,7 @@
<div class="column3_part"> <div class="column3_part">
<div class="p1" @click="$navigate('/ColumnDetail?id=1')"> <div class="p1" @click="$navigate('/ColumnDetail?id=1')">
<img src="/img/index-column-i1.png"> <img src="/img/index-column-i1.png">
<span>公司介绍</span> <span>平台介绍</span>
<p>了解泰古润</p> <p>了解泰古润</p>
</div> </div>
<div class="p2" @click="$navigate('/Business_School')"> <div class="p2" @click="$navigate('/Business_School')">
@ -41,7 +44,7 @@
</div> --> </div> -->
<div class="p3" @click="$navigate('/Column?pid=3')"> <div class="p3" @click="$navigate('/Column?pid=3')">
<img src="/img/index-column-i4.png"> <img src="/img/index-column-i4.png">
<span>公司动态</span> <span>平台动态</span>
<p>最新动态资讯</p> <p>最新动态资讯</p>
</div> </div>
</div> </div>
@ -117,33 +120,27 @@
</template> </template>
<script> <script>
// import date from "../utils/date.js";
import date from "../../utils/date.js" import date from "../../utils/date.js"
export default { export default {
name: 'Home', name: 'Home',
mounted() { mounted() {
this.init(); this.init();
this.data = { Carousel: [], News: [], Trends: [], Video: null };
this.options = [];
}, },
data() { data() {
return { return {
data: {}, data: {},
month: date.getMonth_C(), month: date.getMonth_C(),
showcreate: false, }
showcreateing: false, },
checked: 0, computed: {
tempData: {}, trendsGrouped() {
if (!this.data.Trends) return [];
showRegion: false, const result = [];
options: [], for (let i = 0; i < this.data.Trends.length; i += 2) {
defaultProps: { result.push(this.data.Trends.slice(i, i + 2));
value: "id", }
text: "name", return result;
},
region: '',
loading: false,
} }
}, },
methods: { methods: {
@ -165,94 +162,6 @@ export default {
this.$showFailToast(err.message || '数据加载失败'); this.$showFailToast(err.message || '数据加载失败');
}); });
}, },
create() {
// this.showcreate = true;
this.$post('Member/GetApplyAgent').then(res => {
const data = res.data || res;
if (!data.AgentCode)
this.showcreate = true;
else {
this.showcreateing = true;
}
}).catch(err => {
this.$showFailToast(err.message);
});
},
changebox(e) {
this.checked = e;
this.tempData.AgentRegion = '';
this.tempData.AgentRegionName = '';
this.region = '';
},
onChange(e) {
if (e.selectedOptions.length > 0 && e.selectedOptions[0].level == 'province' && this.checked == 4) {
e.selectedOptions[e.tabIndex].children = null;
this.showRegion = false;
return;
}
else if (e.selectedOptions.length > 1 && e.selectedOptions[1].level == 'city' && this.checked == 3) {
e.selectedOptions[e.tabIndex].children = null;
this.showRegion = false;
return;
}
else if (e.selectedOptions.length > 2 && e.selectedOptions[2].level == 'district' && this.checked == 2) {
e.selectedOptions[e.tabIndex].children = null;
this.showRegion = false;
return;
}
if (e.selectedOptions[e.tabIndex].leaf) {
e.selectedOptions[e.tabIndex].children = null;
this.showRegion = false;
return;
}
else if (!e.selectedOptions[e.tabIndex].children || e.selectedOptions[e.tabIndex].children.length == 0) {
this.$post('Common/GetRegions', { value: e.value }).then(res => {
const data = res.data || res;
if (data.length > 0) {
data.forEach(x => { x.children = [] });
e.selectedOptions[e.tabIndex].children = data;
}
else {
e.selectedOptions[e.tabIndex].children = null;
this.showRegion = false;
}
});
}
},
onFinish(e) {
this.showRegion = false;
this.tempData.AgentRegion = e.selectedOptions.map(x => x.id).join(';');
this.tempData.AgentRegionName = e.selectedOptions.map(x => x.name).join('');
},
submit() {
if (!this.tempData.AgentName) {
this.$showFailToast('请输入姓名');
return;
}
if (!this.tempData.AgentPhone) {
this.$showFailToast('请输入手机号');
return;
}
if (!this.$validPhone(this.tempData.AgentPhone)) {
this.$showFailToast('手机号格式错误');
return;
}
if (!this.tempData.AgentRegion) {
this.$showFailToast('请选择共创区域');
return;
}
this.loading = true;
this.$post('Member/ApplyAgent', this.tempData).then(res => {
this.tempData = {};
this.$showSuccessToast('提交成功');
this.showcreate = false;
}).catch(err => {
this.$showFailToast(err.message);
}).finally(() => {
this.loading = false;
});
},
} }
} }
</script> </script>

View File

@ -5,7 +5,7 @@
<img src="/img/search.png" alt=""> <img src="/img/search.png" alt="">
<input v-model="searchParams.Key" placeholder="请输入关键词搜索" <input v-model="searchParams.Key" placeholder="请输入关键词搜索"
@search="$navigate('Category?Key=' + searchParams.Key)"> @search="$navigate('Category?Key=' + searchParams.Key)">
<button @click="$navigate('Category?Key=' + searchParams.Key)">搜索</button> <button @click="$navigate('Category?Key=' + searchParams.Key)" :style="btnStyle">搜索</button>
</div> </div>
<div class="banner"> <div class="banner">
<van-swipe :autoplay="3000" indicator-color="white" @change="onSwipeChange"> <van-swipe :autoplay="3000" indicator-color="white" @change="onSwipeChange">
@ -15,26 +15,7 @@
</van-swipe-item> </van-swipe-item>
</van-swipe> </van-swipe>
</div> </div>
<!-- <div class="label_box">
<div class="">
<img src="/img/mall_i1.png" alt="">
<span>
官方商城
</span>
</div>
<div class="">
<img src="/img/mall_i2.png" alt="">
<span>
正品保障
</span>
</div>
<div class="">
<img src="/img/mall_i3.png" alt="">
<span>
售后无忧
</span>
</div>
</div> -->
</div> </div>
<div class="cg_bigbox"> <div class="cg_bigbox">
@ -218,21 +199,15 @@
<script> <script>
export default { export default {
name: 'Mall', name: 'Mall',
// emits: ['changeTabbar'],
activated() {
this.init();
},
mounted() {
this.init();
},
data() { data() {
return { return {
activeType: '', activeType: '',
searchParams: { searchParams: {
mallstate: 3, mallstate: 3,
pid: '', pid: '',
Key: '',
}, },
show: true,
data: { data: {
Carousel: [], Carousel: [],
SpuType: [], SpuType: [],
@ -242,53 +217,66 @@ export default {
bgcolor: '', bgcolor: '',
} }
}, },
activated() {
this.$refs.BaseList?.refresh()
},
mounted() {
this.init()
},
methods: { methods: {
init() { init() {
Promise.all([ Promise.all([
this.$get(`/v1/client/EProcatesClient`).then(res => { this.$get('/v1/client/EProcatesClient').then(res => {
this.data.SpuType = res.data this.data.SpuType = res.data.filter(i => i.homeshowed)
}), }),
this.$get(`/v1/client/CSlidesClient?pid=2`).then(res => { this.$get('/v1/client/CSlidesClient?pid=2').then(res => {
this.data.Carousel = res.data this.data.Carousel = res.data
if (res.data?.[0]?.bgcolor) { this.bgcolor = res.data?.[0]?.bgcolor || ''
this.bgcolor = res.data[0].bgcolor
}
}), }),
this.$get('/v1/client/EProsClient?recommend=true').then(res => { this.$get('/v1/client/EProsClient?recommend=true').then(res => {
this.hotData = res.data.items; this.hotData = res.data.items || []
}), }),
this.$get('/v1/client/EProcatesClient/1').then(res => { this.$get('/v1/client/EProcatesClient/1').then(res => {
this.typeData = res.data const filtered = res.data.filter(i => i.homeshowed)
this.searchParams.pid = res.data?.[0]?.id || '' this.typeData = filtered
if (filtered.length > 0) {
this.activeType = filtered[0].id
this.searchParams.pid = filtered[0].id
}
}), }),
]).catch(err => { ]).catch(err => {
}); this.$showFailToast(err.message || '加载失败')
}, })
groupItems(items, num) {
const grouped = [];
for (let i = 0; i < items.length; i += num) {
grouped.push(items.slice(i, i + num));
}
return grouped;
}, },
changeTab(type) { changeTab(type) {
this.searchParams.pid = type; this.searchParams.pid = type
this.$refs.BaseList.refresh()
}, },
onSwipeChange(index) { onSwipeChange(index) {
const item = this.data.Carousel[index] this.bgcolor = this.data.Carousel[index]?.bgcolor || ''
this.bgcolor = item?.bgcolor || '' },
}
onSearch() {
this.$refs.BaseList?.refresh()
},
onClear() {
this.searchParams.Key = ''
this.$refs.BaseList?.refresh()
},
}, },
computed: { computed: {
bgStyle() { bgStyle() {
if (this.bgcolor) { return this.bgcolor ? { background: `linear-gradient(${this.bgcolor}, #f5f5f5)` } : {}
return { },
background: `linear-gradient(${this.bgcolor}, #f5f5f5)` btnStyle() {
} return this.bgcolor ? { background: this.bgcolor } : {}
} },
return {}
}
}, },
} }
</script> </script>

View File

@ -0,0 +1,290 @@
<template>
<div class="pointmall">
<div class="top">
<van-swipe class="my-swipe" :autoplay="3000" indicator-color="white">
<van-swipe-item>
<img style="width: 100%;" src="/img/pointmall_bg.jpg" alt="">
</van-swipe-item>
</van-swipe>
<!-- <div class="search">
<img src="/img/search-w.png" alt="">
<input type="text" placeholder="搜索您想要的产品">
</div> -->
</div>
<div class="contaiar">
<div class="tabs">
<van-tabs style="max-width: 90%;" background="transparent" line-height="0.8vw" line-width="9.33vw;"
v-model:active="active" :ellipsis="false" color="#ca2904" title-active-color="#ca2904"
title-inactive-color="#4e4e4e">
<van-tab title="新品" name=""></van-tab>
<van-tab v-for="i in tabs" :name="i.id" :title="i.name"></van-tab>
</van-tabs>
<img @click="showLeft = true" class="filter_img" style="width: 5.4vw;" src="/img/filter.png" alt="">
</div>
<!-- <BaseList url="/v1/client/EProsClient" class="goods_list" :params="{ mallstate: 2, pid: active }">
<template #default="{ item }">
<div class="goods_item" :key="item.id" @click="toDetail(item.id)">
<img :src="$file(item.img)" alt="">
<div>
<span class="title">{{ item.name }}</span>
<p>¥{{ item.originalprice }}</p>
<div class="money">
<div>
<span>¥
<b>{{ item.saleprice }}</b>.00
</span>
</div>
<img class="r" style="width: 5.6vw;" src="/img/cart.png" alt="">
</div>
</div>
</div>
</template>
</BaseList> -->
<div class="goods_list">
<div class="goods_item" v-for="item in goods" :key="item.id" @click="toDetail(item.id)">
<img :src="item.Icon" alt="">
<div>
<span class="title">{{ item.SubTitle }}</span>
<div class="point_box">
<img style="width: 3.33vw;height: 3.2vw;" src="/img/point.png" alt="">
<span style="font-size: 3.2vw; color: #d9a452;">积分可抵500.00</span>
</div>
<!-- <p>¥{{ item.OPrice }}</p> -->
<div class="money">
<div>
<span>¥</span>
<b>{{ item.Price }}</b>
</div>
<span class="oprice">
¥{{ item.OPrice }}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<van-popup v-model:show="showLeft" position="left" :style="{ width: '30%', height: '100%' }">
<div v-for="i in tabs">
<div @click="changeTab(i.id)" style="height: 10vw;width: 100%;border-bottom: solid 1px #f5f5f5;"
class="box box-center-center">
{{ i.name }}
</div>
</div>
</van-popup>
</template>
<script>
export default {
name: 'PointMall',
activated() {
this.getTab()
},
data() {
return {
active: '',
showLeft: false,
tabs: [],
goods: [
{
id: 1, Icon:
'/img/avatar.png', SubTitle:
'积分礼品测试商品1', OPrice: '99.00',
Price: '9.90'
},
{
id: 2, Icon:
'/img/avatar.png', SubTitle:
'积分礼品测试商品2', OPrice:
'199.00', Price: '19.90'
},
{
id: 3, Icon:
'/img/avatar.png', SubTitle:
'积分礼品测试商品3', OPrice:
'299.00', Price: '29.90'
},
]
}
},
mounted() {
this.getTab()
},
methods: {
getTab() {
this.$get('/v1/client/EProcatesClient/2').then(res => {
this.tabs = res.data
})
},
changeTab(id) {
this.active = id;
this.showLeft = false;
},
toDetail(e) {
this.$navigate(`/GoodsDetail?id=${e}`)
}
},
computed: {
filteredGoods() {
// if (this.active === 0) {
// return this.goods
// }
// const categoryId = this.active
// return this.goods.filter(item => item.CategoryId === categoryId)
}
}
}
</script>
<style scoped>
.pointmall {
&::before {
content: '';
width: 100vw;
height: 100vh;
position: fixed;
left: 0;
top: 0;
background: #f5f5f5;
z-index: -1;
}
}
.finished-text {
width: 100%;
}
.top {
position: relative;
z-index: 0;
}
.contaiar {
position: relative;
background-color: #f5f5f5;
border-radius: 8vw 8vw 0vw 0vw;
margin-top: -12vw;
z-index: 1;
.tabs {
display: flex;
align-items: center;
justify-content: center;
padding: 0 5vw;
margin-top: 4vw;
position: relative;
.filter_img {
width: 5.4vw;
height: 5.4vw;
position: absolute;
right: 5vw;
}
}
}
.goods_list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 0 4vw;
margin-top: 4vw;
.goods_item {
background-color: #fefefe;
border-radius: 2vw;
margin-bottom: 4vw;
width: 44.8vw;
max-width: 44.8vw;
img {
width: 44.8vw;
height: 44.8vw;
background-color: #fefefe;
border-radius: 2vw 2vw 0vw 0vw;
}
>div {
padding: 3.2vw;
}
.title {
width: 38.4vw;
max-width: 38.4vw;
font-size: 3.47vw;
overflow: hidden !important;
text-overflow: ellipsis;
display: -webkit-box !important;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
line-clamp: 2;
line-height: 4vw;
height: 8vw;
color: #333;
margin-bottom: 3.2vw;
}
.point_box {
display: flex;
align-items: center;
margin-bottom: 3.2vw;
height: 5.33vw;
background-color: #fff6dd;
border-radius: 0.8vw;
border: solid 0.27vw #f1c98c;
padding: 0 2vw;
width: fit-content;
img {
width: 3.33vw;
height: 3.2vw;
margin-right: 1.6vw;
}
span {
font-size: 3.2vw;
color: #d9a452;
}
}
.money {
display: flex;
justify-content: space-between;
align-items: center;
color: #ea3e23;
span {
color: #ca2904;
text-decoration: line-through;
}
b {
font-size: 4vw;
color: #ea3e23;
text-decoration: none !important;
}
.oprice {
font-size: 3.2vw;
color: #b1b1b1;
}
}
}
}
</style>

View File

@ -106,7 +106,7 @@ export default {
}, },
methods: { methods: {
back() { back() {
this.$router.push('/Tradelist'); this.$router.push('/TradeList');
}, },
async loadData() { async loadData() {
try { try {
@ -135,27 +135,67 @@ export default {
try { try {
const res = await this.$post(`/v1/client/FOrdersClient/${this.ordernum}/pay`, { const res = await this.$post(`/v1/client/FOrdersClient/${this.ordernum}/pay`, {
Paychannel: paychannel, paychannel: paychannel,
Openid: method === 'wechat' ? openid : '' openid: method === 'wechat' ? openid : ''
}); });
if (res.data && res.data.startsWith('http')) {
// if (method === 'wechat') {
location.href = res.data; // 使 WeixinJSBridge
} else if (res.status === 200) { this.callWechatPay(res.data);
// } else if (method === 'balance') {
//
this.$showSuccessToast(res.message || '支付成功'); this.$showSuccessToast(res.message || '支付成功');
setTimeout(() => { setTimeout(() => {
this.onPayCompleted(); this.onPayCompleted();
}, 1500); }, 1500);
} else if (res.data && res.data.startsWith('http')) {
//
location.href = res.data;
this.showPayResult = true;
} }
this.showPayResult = true;
} catch (err) { } catch (err) {
this.showPayResult = false; if (method !== 'balance' && method !== 'wechat') {
this.showPayResult = false;
}
this.$showFailToast(err.message || '支付失败'); this.$showFailToast(err.message || '支付失败');
} }
}, },
callWechatPay(payData) {
//
const onBridgeReady = () => {
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
appId: payData.appId,
timeStamp: payData.timeStamp,
nonceStr: payData.nonceStr,
package: payData.package,
signType: payData.signType,
paySign: payData.paySign
},
(res) => {
if (res.err_msg === 'get_brand_wcpay_request:ok') {
this.$showSuccessToast('支付成功');
this.onPayCompleted();
} else {
// this.showPayResult = true;
}
}
);
};
if (typeof WeixinJSBridge === 'undefined') {
if (document.addEventListener) {
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
} else if (document.attachEvent) {
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
} else {
onBridgeReady();
}
},
onPayCompleted() { onPayCompleted() {
this.$router.replace('/Tradelist'); this.$router.replace('/TradeList');
}, },
onRePay() { onRePay() {
this.showPayResult = false; this.showPayResult = false;

View File

@ -7,6 +7,21 @@
<b style="#d2220d">{{ data.statename }}</b> <b style="#d2220d">{{ data.statename }}</b>
</div> </div>
<div class="_address">
<div class="icon">
<img src="/img/address.png">
</div>
<div class="c">
<div class="name">
{{ data.receiptrealname }} {{ data.receiptcellphone }}
</div>
<div class="s">
<p>{{ data.receiptprovince }}{{ data.receiptcity }}{{ data.receiptcounty }}{{
data.receiptaddress }}</p>
</div>
</div>
</div>
<div class="_goods"> <div class="_goods">
<img :src="$file(data.proimg)"> <img :src="$file(data.proimg)">
<div class="c"> <div class="c">
@ -25,6 +40,12 @@
</div> </div>
</div> </div>
<div class="_remark" v-if="data.remark">
<p>买家留言: </p>
<span class="r">
{{ data.remark || '无' }}
</span>
</div>
<div class="_detail"> <div class="_detail">
<div class="p"> <div class="p">
<p> <p>
@ -43,9 +64,13 @@
实付金额 实付金额
<b style="color: #f00;">{{ data.realmoney?.toFixed(2) }}</b> <b style="color: #f00;">{{ data.realmoney?.toFixed(2) }}</b>
</p> </p>
<p v-if="data.paychannelname">
支付方式
<span>{{ data.paychannelname }}</span>
</p>
<p v-if="data.zongsongjifen"> <p v-if="data.zongsongjifen">
赠送积分 赠送积分
<span>{{ data.zongsongjifen }}</span> <span>{{ data.zongsongjifen?.toFixed(2) }}</span>
</p> </p>
<hr style="margin:1.67vw 0;height: 1px;border: none;background: #f5f5f580;"> <hr style="margin:1.67vw 0;height: 1px;border: none;background: #f5f5f580;">
<p> <p>
@ -107,7 +132,7 @@ export default {
}, },
methods: { methods: {
back() { back() {
this.$router.back(); this.$navigate('/TradeList');
}, },
async loadData() { async loadData() {
try { try {

View File

@ -1,5 +1,5 @@
<template> <template>
<BasePage> <BasePage :back="back">
<div class="tradelist"> <div class="tradelist">
<div class="tabs"> <div class="tabs">
<van-tabs class="area" v-model:active="TradeArea" color="#f00" title-active-color="#222" <van-tabs class="area" v-model:active="TradeArea" color="#f00" title-active-color="#222"
@ -13,30 +13,27 @@
<van-tab v-for="i in tradeStates" :title="i.label" :name="String(i.value)"></van-tab> <van-tab v-for="i in tradeStates" :title="i.label" :name="String(i.value)"></van-tab>
</van-tabs> </van-tabs>
</div> </div>
<van-search v-model="searchParams.ordernum" left-icon=" " shape="round" placeholder="输入订单号搜索" @search="onSearch" <van-search v-model="_searchParams.ordernum" left-icon=" " shape="round" placeholder="输入订单号搜索" @search="onSearch"
@clear="onClear"> @clear="onClear">
<template #right-icon> <template #right-icon>
<van-icon name="/img/sort-search.png" @click="onSearch"></van-icon> <van-icon name="/img/sort-search.png" @click="onSearch"></van-icon>
</template> </template>
</van-search> </van-search>
<BaseList v-if="TradeArea !== 2" ref="baseList" url="/v1/client/FOrdersClient" :params="searchParams" <BaseList ref="baseList" url="/v1/client/FOrdersClient" :params="searchParams" :parseData="parseData"
:parseData="res => res.data.items" style="padding: 3.33vw;" @refresh="onRefresh"> style="padding: 3.33vw;" @refresh="onRefresh">
<template #default="{ item }"> <template #default="{ item }">
<div class="list"> <div class="list">
<div class="order_box"> <div class="order_box">
<span> <span :class="'state' + item.mallstate" class="state">{{ item.mallstatename.slice(0, 2) }}</span>
订单编号:{{ item.ordernum }} <span>订单编号:{{ item.ordernum }}</span>
</span>
<img src="/img/copy_b.png" @click="$copyText(item.ordernum); $showSuccessToast('复制成功')" alt=""> <img src="/img/copy_b.png" @click="$copyText(item.ordernum); $showSuccessToast('复制成功')" alt="">
</div> </div>
<div class="goods_box"> <div class="goods_box">
<img :src="$file(item.proimg)" alt=""> <img :src="$file(item.shopimg || item.proimg)" alt="">
<div class="goods_info"> <div class="goods_info">
<div class="tit">{{ item.proname }}</div> <div class="tit">{{ item.shopname || item.proname }}</div>
<div class="spec"> <div class="spec" v-if="!item.shopname">
<span>{{ item.proskuname }}</span> <span>{{ item.proskuname }}</span>
<p>x{{ item.buynums }}</p> <p>x{{ item.buynums }}</p>
</div> </div>
@ -46,20 +43,17 @@
</div> </div>
</div> </div>
</div> </div>
<div class="price"> <div class="price">
实付<span><b>{{ item.realmoney?.toFixed(2) }}</b></span> 实付<span><b>{{ item.realmoney?.toFixed(2) }}</b></span>
</div> </div>
<div class="state_box"> <div class="state_box">
<b :class="'b' + item.state"> <b :class="'b' + item.state">{{ item.statename }}</b>
{{ item.statename }}
</b>
<div class="btn_box"> <div class="btn_box">
<button v-if="item.state === 0" @click="cancelTrade(item)">取消订单</button> <button v-if="item.state === 0" @click="cancelTrade(item)">取消订单</button>
<!-- <button v-if="item.state === 3">查看物流</button> --> <button v-if="!item.shopname" @click="$navigate(`TradeDetail?ordernum=${item.ordernum}`)">查看详情</button>
<button @click="$navigate(`TradeDetail?ordernum=${item.ordernum}`)">查看详情</button> <button v-else @click="$navigate(`MerchantTradeDetail?id=${item.ordernum}`)">查看详情</button>
<button v-if="item.state === 3 || item.state === 4" @click="showLogistics(item)">物流信息</button>
<button v-if="item.state === 3" @click="confirmReceipt(item)">确认收货</button> <button v-if="item.state === 3" @click="confirmReceipt(item)">确认收货</button>
<button v-if="item.state === 0" @click="$navigate(`Pay?ordernum=${item.ordernum}`)">立即支付</button> <button v-if="item.state === 0" @click="$navigate(`Pay?ordernum=${item.ordernum}`)">立即支付</button>
</div> </div>
@ -68,58 +62,26 @@
</template> </template>
</BaseList> </BaseList>
<BaseList v-else ref="baseList" url="/v1/client/FOrdersshopClient" :params="searchParams"
:parseData="res => res.data.items" style="padding: 3.33vw;" @refresh="onRefresh"> <van-popup v-model:show="logisticsVisible" round position="bottom" teleport="body">
<template #default="{ item }"> <div class="logistics-popup">
<div class="list"> <div class="logistics-tit">物流信息</div>
<div class="merchant_info"> <div class="logistics-content">
<img class="icon" :src="$file(item.shopimg)" alt=""> <div class="logistics-item">
<div class="inf"> <span class="label">物流公司</span>
<span>{{ item.shopname }}</span> <span class="value">{{ currentLogistics?.exportname }}</span>
<div>
<p>{{ item.ordermoney?.toFixed(2) }}</p>
<span class="dr" v-if="item.discountratio">
{{ item.discountratio }}%让利
</span>
</div>
</div>
</div> </div>
<div class="logistics-item">
<div class="price"> <span class="label">物流单号</span>
实付<span><b>{{ item.realmoney?.toFixed(2) }}</b></span> <span class="value">{{ currentLogistics?.exportnum }}</span>
</div> <img src="/img/copy_b.png" @click="$copyText(currentLogistics?.exportnum); $showSuccessToast('复制成功')" alt="">
<div class="detail_box">
<div>
<span>订单编号:</span>
<span class="r">
{{ item.ordernum }}
<img src="/img/copy_b.png" @click="$copyText(item.ordernum); $showSuccessToast('复制成功')" alt="">
</span>
</div>
<div>
<span>下单时间:</span>
<span class="r">
{{ $formatGMT(item.addtime, 'yyyy-MM-dd HH:mm:ss') }}
</span>
</div>
</div>
<div class="state_box">
<b :class="'b' + item.state">
{{ item.statename }}
</b>
<div class="btn_box">
<button @click="$navigate(`MerchantTradeDetail?id=${item.ordernum}&type=user`)">查看详情</button>
</div>
</div> </div>
</div> </div>
</template> <div class="logistics-close">
</BaseList> <van-button type="primary" block round color="#ca2904" @click="logisticsVisible = false">关闭</van-button>
</div>
</div>
</van-popup>
</div> </div>
</BasePage> </BasePage>
</template> </template>
@ -127,102 +89,213 @@
<script> <script>
export default { export default {
name: 'TradeList', name: 'TradeList',
data() { data() {
return { return {
activeTab: '', TradeArea: this.$route.query.TradeArea || 0,
TradeArea: Number(this.$route.query.TradeArea) || 3, activeTab: this.$route.query.state || '',
searchParams: { _searchParams: {
ordernum: '', ordernum: '',
state: '', state: '',
mallstate: Number(this.$route.query.TradeArea) || 3,
}, },
tradeStates: [ tradeStates: [
{
"value": 0,
"label": "待付款"
},
{
"value": 1,
"label": "待发货"
},
{
"value": 3,
"label": "待收货"
},
{
"value": 4,
"label": "已完成"
}
], ],
mallList: [ mallList: [
{ value: 3, label: '商城订单' }, { value: 0, label: '全部订单' },
{ value: 1, label: '礼包订单' }, {
{ value: 2, label: '买单订单' }, "value": 1,
"label": "礼包订单"
},
{
"value": 3,
"label": "商城订单"
},
{
"value": 4,
"label": "买单订单"
}
], ],
_initializing: true,
logisticsVisible: false,
currentLogistics: null,
} }
}, },
mounted() {
this.getTradeStates(); computed: {
parseData() {
return res => res.data.items
},
searchParams() {
const params = {
ordernum: this._searchParams.ordernum,
state: this._searchParams.state === '' ? '' : Number(this._searchParams.state),
}
if (this.TradeArea !== 0) {
params.mallstate = this.TradeArea
}
return params
},
}, },
watch: { watch: {
TradeArea(val) { '$route.query.TradeArea': {
this.searchParams.mallstate = val; handler(val) {
this.TradeArea = val !== undefined ? Number(val) : 0
},
immediate: true
}, },
'$route.query.state': { '$route.query.state': {
handler(newVal) { handler(val) {
if (newVal !== undefined && newVal !== '') { this.activeTab = val || ''
this.activeTab = String(newVal); this._searchParams.state = val || ''
// this.getTradeStates(); },
} else { immediate: true
this.activeTab = '';
}
}
} }
}, },
mounted() {
},
methods: { methods: {
back() { //
this.$router.push('/My') // async loadFilterOptions() {
}, // try {
// const [stateRes, areaRes] = await Promise.all([
// this.$get('/v1/client/FOrdersClient/orderstate'),
// this.$get('/v1/client/EProsClient/mallstate'),
// ])
// this.tradeStates = stateRes.data || []
// this.mallList = [{ value: 0, label: '' }, ...(areaRes.data || [])]
// this._initializing = false
// } catch (err) {
// this.$showFailToast(err.message || '')
// }
// },
// Tab -
onTabChange(name) { onTabChange(name) {
this.searchParams.state = name; this._searchParams.state = name
this.$refs.baseList?.refresh(); // this.$refs.baseList?.refresh()
}, },
changeMall(name) {
this.searchParams.mallstate = Number(name) || 3; // Tab -
this.searchParams.state = ''; changeMall(val) {
this.$refs.baseList?.refresh(); this.TradeArea = val
this._searchParams.state = ''
this.activeTab = ''
// refreshBaseList watch params
}, },
// &
onSearch() { onSearch() {
this.$refs.baseList?.refresh(); this.$refs.baseList?.refresh()
}, },
onClear() { onClear() {
this.searchParams.ordernum = ''; this._searchParams.ordernum = ''
this.$refs.baseList?.refresh(); this.$refs.baseList?.refresh()
},
getTradeStates() {
this.$get('/v1/client/FOrdersClient/orderstate').then(res => {
this.tradeStates = res.data;
this.$nextTick(() => {
const stateFromRoute = this.$route.query.state;
if (stateFromRoute !== undefined && stateFromRoute !== '') {
this.activeTab = String(stateFromRoute);
this.searchParams.state = stateFromRoute;
}
});
}).catch((err) => {
this.$showFailToast(err.message || '获取订单状态失败');
});
},
onRefresh() {
}, },
//
confirmReceipt(item) { confirmReceipt(item) {
this.$showConfirmDialog({ this.$showConfirmDialog({ title: '是否确认收货' })
title: "是否确认收货", .then(() => this.$put(`/v1/client/FOrdersClient/${item.ordernum}/receipt`))
}).then(() => { .then(() => {
this.$put(`/v1/client/FOrdersClient/${item.ordernum}/receipt`).then(() => { this.$showSuccessToast('确认收货成功')
this.$showSuccessToast('确认收货成功'); this.$refs.baseList?.refresh()
this.$refs.baseList?.refresh(); })
}).catch(err => { .catch(err => this.$showFailToast(err.message || '操作失败'))
this.$showFailToast(err.message || '操作失败');
});
}).catch(() => { });
}, },
//
cancelTrade(item) { cancelTrade(item) {
this.$showConfirmDialog({ this.$showConfirmDialog({ title: '是否确认取消订单' })
title: "是否确认取消订单", .then(() => this.$put(`/v1/client/FOrdersClient/${item.ordernum}/cancel`))
}).then(() => { .then(() => {
this.$put(`/v1/client/FOrdersClient/${item.ordernum}/cancel`).then(() => { this.$showSuccessToast('取消订单成功')
this.$showSuccessToast('取消订单成功'); this.$refs.baseList?.refresh()
this.$refs.baseList?.refresh(); })
}).catch(err => { .catch(err => this.$showFailToast(err.message || '取消失败'))
this.$showFailToast(err.message || '取消失败');
});
}).catch(() => { });
}, },
}
//
showLogistics(item) {
this.currentLogistics = item
this.logisticsVisible = true
},
back() {
this.$navigate('/My');
},
onRefresh() { },
},
} }
</script> </script>
<style lang="less" scoped>
.logistics-popup {
padding: 20px;
.logistics-tit {
font-size: 16px;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}
.logistics-content {
background: #f5f5f5;
border-radius: 8px;
padding: 15px;
margin-bottom: 20px;
}
.logistics-item {
display: flex;
align-items: center;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
.label {
width: 70px;
color: #999;
}
.value {
flex: 1;
color: #333;
}
img {
width: 16px;
height: 16px;
margin-left: 8px;
}
}
.logistics-close {
margin-top: 10px;
}
}
</style>

View File

@ -1,34 +1,27 @@
<template> <template>
<BasePage> <BasePage>
<!-- v-if="data.Icon" -->
<div class="ewm_bg" style="font-family: 'PingFang SC'"> <div class="ewm_bg" style="font-family: 'PingFang SC'">
<div class="d1"> <div class="d1">
<div class="w100"> <div class="w100">
<img id="invite_bg" :src="$file($datadic.getContent('code_yqmbj'))" crossorigin="anonymous" /> <img id="invite_bg" src="/img/invite_bg.jpg" crossorigin="anonymous">
<!-- <img id="invite_bg" src="/img/invite_bg.jpg" crossorigin="anonymous"> -->
</div> </div>
<img class="icon" :src="$file(data.Avatar) + '?t=1'" crossorigin="anonymous"> <img class="icon" :src="$file(data.Avatar)" crossorigin="anonymous">
<div class="name"> <div class="name">
<b>{{ $substring(data.NickName, 0, 8) }}</b> <b>{{ $substring(data.NickName, 0, 8) }}</b>
<p>邀请你使用泰古润平台<br>
共赴葡园品鉴醇香</p>
</div> </div>
<p class="tips">邀请你使用泰古润平台<br>积分带卡消费享受更多实惠</p>
<div class="ewm_test box box-tb box-align-center"> <div class="ewm_test box box-tb box-align-center">
<vue-qr v-if="!loading" :text="link" backgroundColor="rgb(255,255,255)" <vue-qr v-if="!loading" :text="link" backgroundColor="rgb(255,255,255)"
colorLight="rgb(255,255,255)"></vue-qr> colorLight="rgb(255,255,255)"></vue-qr>
<!-- <span>扫码立即注册</span> -->
</div> </div>
</div> </div>
</div> </div>
<div id="sc_ewm" class="b_l_w bx" style="font-family: 'PingFang SC'"></div> <div id="sc_ewm" class="b_l_w bx" style="font-family: 'PingFang SC'"></div>
</BasePage> </BasePage>
</template> </template>
<script> <script>
import { html2canvas, canvasToDataURL } from '@/utils/html2image'; import html2canvas from 'html2canvas'
import { useUserStore } from '@/stores/user';
export default { export default {
name: 'Invite', name: 'Invite',
@ -36,80 +29,96 @@ export default {
return { return {
loading: true, loading: true,
data: { data: {
Icon: '',
Avatar: '', Avatar: '',
NickName: '', NickName: '',
}, },
link: '', link: '',
userStore: useUserStore(), }
};
}, },
mounted() { mounted() {
this.loading = false; this.init()
this.init().then(() => {
//
this.$nextTick(() => {
const img = document.getElementById("invite_bg");
if (img.complete) {
this.CreatePoster(".ewm_bg > div", "#sc_ewm");
} else {
img.onload = () => this.CreatePoster(".ewm_bg > div", "#sc_ewm");
}
});
});
}, },
methods: { methods: {
async init() { async init() {
try { try {
// const userInfo = await this.$get('/v1/client/DUsersClient')
const userInfo = await this.$get('/v1/client/DUsersClient');
if (userInfo?.data) { if (userInfo?.data) {
this.data.Avatar = userInfo.data.userimg || ''; this.data.Avatar = userInfo.data.userimg || ''
this.data.NickName = userInfo.data.nickname || ''; this.data.NickName = userInfo.data.nickname || ''
this.data.Icon = userInfo.data.Icon || '';
} }
// const inviteCode = userInfo?.data?.cellphone || ''
const inviteCode = userInfo?.data?.cellphone || ''; this.link = `${location.origin}${location.pathname}#/Login?invite=${inviteCode}`
this.link = `${location.origin}${location.pathname}#/Login?invite=${inviteCode}`;
} catch (err) { } catch (err) {
console.error('获取用户信息失败:', err); console.error('获取用户信息失败:', err)
} finally {
this.loading = false
this.$nextTick(() => {
this.generatePoster()
})
} }
}, },
async CreatePoster(divID, targetID) {
var shareContent = document.querySelector(divID); async generatePoster() {
var width = shareContent.offsetWidth; const el = document.querySelector('.ewm_bg > .d1')
var height = shareContent.offsetHeight; const bgImg = document.querySelector('#invite_bg')
if (!el || !bgImg) return
try { try {
// // Convert local image to base64 for Safari compatibility
const avatarImg = shareContent.querySelector('.icon'); if (bgImg.src.startsWith('/')) {
if (avatarImg && !avatarImg.complete) { await this.loadImageToBase64(bgImg)
await new Promise((resolve, reject) => {
avatarImg.onload = resolve;
avatarImg.onerror = resolve; //
setTimeout(resolve, 5000); //
});
} }
var canvas = await html2canvas(shareContent, { if (document.fonts?.ready) {
await document.fonts.ready
}
await new Promise(resolve => setTimeout(resolve, 500))
const canvas = await html2canvas(el, {
scale: 2, scale: 2,
width: width,
height: height,
useCORS: true, useCORS: true,
allowTaint: false, backgroundColor: '#ffffff',
}); logging: false
var dataUrl = canvasToDataURL(canvas, "image/png", 1.0); })
var newImg = document.createElement("img"); const dataUrl = canvas.toDataURL('image/png')
newImg.src = dataUrl;
newImg.width = width; const target = document.querySelector('#sc_ewm')
newImg.height = height; if (target) {
document.querySelector(targetID).appendChild(newImg); target.innerHTML = ''
const img = document.createElement('img')
img.src = dataUrl
img.style.width = '100%'
img.style.display = 'block'
target.appendChild(img)
}
} catch (err) { } catch (err) {
console.error('生成海报失败', err); console.error('生成海报失败', err)
} }
}, },
loadImageToBase64(imgEl) {
return new Promise((resolve, reject) => {
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = () => {
try {
const canvas = document.createElement('canvas')
canvas.width = img.width
canvas.height = img.height
const ctx = canvas.getContext('2d')
ctx.drawImage(img, 0, 0)
imgEl.src = canvas.toDataURL('image/png')
resolve()
} catch (e) {
reject(e)
}
}
img.onerror = reject
img.src = imgEl.src
})
},
}, },
}; }
</script> </script>
<style lang="less"> <style lang="less">
@ -127,19 +136,22 @@ export default {
.name { .name {
position: absolute; position: absolute;
left: 20.27vw; left: 34vw;
bottom: 25.6vw; bottom: 46vw;
line-height: 6vw;
width: 100%; width: 100%;
b { b {
font-size: 3.47vw; font-size: 3.47vw;
color: #3a0a05;
} }
p { }
font-size: 3.2vw;
} .tips {
font-size: 3.2vw;
color: #3a0a05;
position: absolute;
left: 20.27vw;
bottom: 32.4vw;
} }
.icon { .icon {
@ -147,7 +159,7 @@ export default {
width: 10.67vw; width: 10.67vw;
height: 10.67vw; height: 10.67vw;
left: 20.27vw; left: 20.27vw;
bottom: 42vw; bottom: 43vw;
border-radius: 50%; border-radius: 50%;
} }

View File

@ -43,7 +43,7 @@
<div @click="$navigate('CV')"> <div @click="$navigate('CV')">
<div class="t"> <div class="t">
<img src="/img/my_w2.png" alt=""> <img src="/img/my_w2.png" alt="">
<span>会员卡额度</span> <span>会员卡</span>
</div> </div>
<b>{{ data.xiaofeiquan?.toFixed(2) }}</b> <b>{{ data.xiaofeiquan?.toFixed(2) }}</b>
</div> </div>
@ -68,24 +68,24 @@
</div> </div>
<div class="c"> <div class="c">
<van-badge :dot="data.ordercountdaifukuan > 0" :offset="[3, -3]"> <van-badge :dot="data.ordercountdaifukuan > 0" :offset="[3, -3]">
<div class="st_box" @click="$navigate(`TradeList?TradeArea=3&state=0`)"> <div class="st_box" @click="$navigate(`/TradeList?state=0`)">
<img src="/img/my_o1.png"> <img src="/img/my_o1.png">
<p>待付款</p> <p>待付款</p>
</div> </div>
</van-badge> </van-badge>
<van-badge :dot="data.ordercountdaifahuo > 0" :offset="[3, -3]"> <van-badge :dot="data.ordercountdaifahuo > 0" :offset="[3, -3]">
<div class="st_box" @click="$navigate(`TradeList?TradeArea=3&state=1`)"> <div class="st_box" @click="$navigate(`/TradeList?state=1`)">
<img src="/img/my_o2.png"> <img src="/img/my_o2.png">
<p>待发货</p> <p>待发货</p>
</div> </div>
</van-badge> </van-badge>
<van-badge :dot="data.ordercountdaishouhuo > 0" :offset="[3, -3]"> <van-badge :dot="data.ordercountdaishouhuo > 0" :offset="[3, -3]">
<div class="st_box" @click="$navigate(`TradeList?TradeArea=3&state=3`)"> <div class="st_box" @click="$navigate(`/TradeList?state=3`)">
<img src="/img/my_o3.png"> <img src="/img/my_o3.png">
<p>待收货</p> <p>待收货</p>
</div> </div>
</van-badge> </van-badge>
<div class="st_box" @click="$navigate(`TradeList?TradeArea=3&state=4`)"> <div class="st_box" @click="$navigate(`/TradeList?state=4`)">
<img src="/img/my_o4.png"> <img src="/img/my_o4.png">
<p>已完成</p> <p>已完成</p>
</div> </div>
@ -93,10 +93,14 @@
</div> </div>
<div class="fast_to"> <div class="fast_to">
<img src="/img/my_f1.jpg" @click="toInvite()" alt=""> <div>
<img src="/img/my_f2.jpg" @click="$navigate('Team')" alt=""> <img src="/img/my_f1.jpg" @click="toInvite()" alt="">
<img src="/img/my_f3.jpg" @click="$navigate('Allow')" alt=""> <img src="/img/my_f2.jpg" @click="$navigate('Team')" alt="">
<img src="/img/my_f4.jpg" @click="$navigate('Certificate')" alt=""> </div>
<div>
<img src="/img/my_f3.jpg" @click="$navigate('Allow')" alt="">
<img src="/img/my_f4.jpg" @click="$navigate('Certificate')" alt="">
</div>
</div> </div>
<div class="service_center"> <div class="service_center">
@ -199,7 +203,7 @@
<van-dialog v-model:show="showFollow" :closeOnClickOverlay="false" :showConfirmButton="false" <van-dialog v-model:show="showFollow" :closeOnClickOverlay="false" :showConfirmButton="false"
style="background: transparent; display: flex; align-items: center; flex-direction: column;"> style="background: transparent; display: flex; align-items: center; flex-direction: column;">
<img :src="$file(data.FollowQRCode)" style="width: 60vw" /> <img :src="$file(FollowQRCode)" style="width: 60vw" />
<div style="margin: 3.333vw 0"> <div style="margin: 3.333vw 0">
<span style="color: #fff">为了更好为您提供服务<br />请长按识别二维码关注公众号</span> <span style="color: #fff">为了更好为您提供服务<br />请长按识别二维码关注公众号</span>
</div> </div>
@ -218,8 +222,28 @@ export default {
components: { ManagerPopup }, components: { ManagerPopup },
computed: { computed: {
}, },
beforeRouteEnter(to, from, next) {
if (!localStorage.getItem('member_token')) {
next({ name: 'Login', query: { redirect: to.fullPath } });
} else {
next();
}
},
mounted() { mounted() {
this.init(); this.init();
this.$get('/v1/client/HWxinfoClient/qrcode').then(res => {
this.FollowQRCode = res.data;
});
if (this.$isWechat()) {
// alert(localStorage.getItem('openid'))
this.$get(`/v1/client/DUsersClient/subscribe?openid=${localStorage.getItem('openid')}`).then(res => {
// alert(JSON.stringify(res))
if (res.data === false) {
this.showFollow = true;
}
}).catch(() => { });
// this.showFollow = true;
}
window.addEventListener('showManagerPopup', this.onShowManagerPopup) window.addEventListener('showManagerPopup', this.onShowManagerPopup)
window.addEventListener('closeManagerPopup', this.onCloseManagerPopup) window.addEventListener('closeManagerPopup', this.onCloseManagerPopup)
}, },
@ -246,27 +270,33 @@ export default {
hfLoading: true, hfLoading: true,
isSendCode: false, isSendCode: false,
sendCodeTime: 0, sendCodeTime: 0,
FollowQRCode: '',
} }
}, },
methods: { methods: {
init() { init() {
Promise.all( if (!this.$isLogin()) {
[ this.$showFailToast('登录状态异常,请重新登录')
this.$get('/v1/client/DUsersClient').then(data => { location.replace('#/Login')
// console.log(data); return
this.data = data.data; }
this.$ls.set('cellphone', data.data.cellphone); window.addEventListener('showManagerPopup', this.onShowManagerPopup)
this.$ls.set('userimg', data.data.userimg); window.addEventListener('closeManagerPopup', this.onCloseManagerPopup)
this.$ls.set('nickname', data.data.nickname); Promise.all([
this.$ls.set('isshop', data.data.isshop); this.$get('/v1/client/DUsersClient').then(data => {
this.$ls.set('iscenter', data.data.col2); // console.log(data);
this.$ls.set('user_id', data.data.id); this.data = data.data;
this.$ls.set('huiyuankaid', data.data.huiyuankaid); this.$ls.set('cellphone', data.data.cellphone);
this.$ls.set('user_cv', data.data.xiaofeiquan); this.$ls.set('userimg', data.data.userimg);
this.$ls.set('user_point', data.data.xiaofeijifen); this.$ls.set('nickname', data.data.nickname);
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('user_cv', data.data.xiaofeiquan);
this.$ls.set('user_point', data.data.xiaofeijifen);
}),
]
) )
}, },
@ -281,7 +311,7 @@ export default {
this.$showConfirmDialog({ this.$showConfirmDialog({
title: "确认退出?", title: "确认退出?",
}).then(() => { }).then(() => {
this.$ls.remove('token'); this.$ls.remove('member_token');
location.replace('#/Login'); location.replace('#/Login');
setTimeout(() => { setTimeout(() => {
location.reload(); location.reload();
@ -365,7 +395,7 @@ export default {
confirmButtonText: '确定', confirmButtonText: '确定',
}).then(() => { }).then(() => {
this.saving = false; this.saving = false;
this.$ls.remove('token'); this.$ls.remove('member_token');
location.replace('#/Login'); location.replace('#/Login');
}); });
} else { } else {

View File

@ -75,7 +75,8 @@
<div class="list_title"> <div class="list_title">
<!-- 直推好友列表({{ total.TotalCount }}) --> <!-- 直推好友列表({{ total.TotalCount }}) -->
<van-tabs v-model:active="active" background="transparent" color="#ad9573"> <van-tabs v-model:active="active" background="transparent" color="#ad9573" style="font-size: 4vw;"
font-size="4vw">
<van-tab :title="'分享的好友(' + total.friendcount + ')'"></van-tab> <van-tab :title="'分享的好友(' + total.friendcount + ')'"></van-tab>
<van-tab :title="'拓展的商家'"></van-tab> <van-tab :title="'拓展的商家'"></van-tab>
<van-tab :title="'我的推荐人'"></van-tab> <van-tab :title="'我的推荐人'"></van-tab>
@ -223,7 +224,7 @@ export default {
.merchantinfo img { .merchantinfo img {
width: 12vw; width: 12vw;
height: 12vw; height: 12vw;
border-radius: 50%; /* border-radius: 50%; */
} }
.merchantinfo .inf { .merchantinfo .inf {

View File

@ -17,7 +17,7 @@
<div class="db"> <div class="db">
<div class="left"> <div class="left">
<span></span> <span></span>
<b>{{ data.totalsum?.toFixed(2) }}</b> <b>{{ data.totalsum?.toFixed(2) }}</b>
</div> </div>
@ -52,7 +52,7 @@
<template #default="{ item }"> <template #default="{ item }">
<div class="item"> <div class="item">
<div class="t"> <div class="t">
<span class="tit">当前{{ item.balance?.toFixed(2) }}</span> <span class="tit">当前{{ item.balance?.toFixed(2) }}</span>
<span :class="item.nums > 0 ? 'in' : 'out'" class="r"> <span :class="item.nums > 0 ? 'in' : 'out'" class="r">
<span v-if="item.nums > 0">增加+</span> <span v-if="item.nums > 0">增加+</span>
<span v-else>减少</span> <span v-else>减少</span>

View File

@ -3,7 +3,8 @@
<div class="verif"> <div class="verif">
<div class="search_box"> <div class="search_box">
<img src="/img/search.png" alt=""> <img src="/img/search.png" alt="">
<input type="text" placeholder="输入用户账号/礼品券码搜索" v-model="keyword1"> <input type="text" placeholder="输入用户账号/礼品券码搜索" v-model="keyword1" v-if="type === 'verif'">
<input type="text" placeholder="输入用户账号/会员卡号搜索" v-model="keyword1" v-else>
</div> </div>
<BaseList ref="listRef" <BaseList ref="listRef"
@ -17,9 +18,12 @@
<span>{{ item.nickname || item.username || '未知用户' }}</span> <span>{{ item.nickname || item.username || '未知用户' }}</span>
<p>账号{{ item.cellphone || item.username }}</p> <p>账号{{ item.cellphone || item.username }}</p>
</div> </div>
<span class="r tt"> <span class="r tt" v-if="type === 'verif'">
已核销 已核销
</span> </span>
<span class="r pre" v-else>
赠送金额 <b style="color: f00;">500.00</b>
</span>
</div> </div>
<div class="info_box"> <div class="info_box">
<div> <div>
@ -28,17 +32,27 @@
src="/img/copy_b.png" alt=""></span> src="/img/copy_b.png" alt=""></span>
</div> </div>
<div> <div v-if="type === 'verif'">
<span class="left">礼品券码</span> <span class="left">礼品券码</span>
<span class="r rg">{{ item.sysid }}<img @click="$copyText(item.sysid); $showSuccessToast('复制成功')" <span class="r rg">{{ item.sysid }}<img @click="$copyText(item.sysid); $showSuccessToast('复制成功')"
src="/img/copy_b.png" alt=""></span> src="/img/copy_b.png" alt=""></span>
</div> </div>
<div> <div v-else>
<span class="left">会员卡号</span>
<span class="r rg">{{ item.huiyuankaid }}<img
@click="$copyText(item.huiyuankaid); $showSuccessToast('复制成功')" src="/img/copy_b.png" alt=""></span>
</div>
<div v-if="type === 'verif'">
<span class="left">核销时间</span> <span class="left">核销时间</span>
<span class="r rg">{{ formatTime(item.writeofftime) }}</span> <span class="r rg">{{ formatTime(item.writeofftime) }}</span>
</div> </div>
<div v-else>
<span class="left">赠送时间</span>
<span class="r rg">{{ formatTime(item.addtime) }}</span>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@ -5,7 +5,7 @@
<div class="tit"> <div class="tit">
被赠送人账号 被赠送人账号
</div> </div>
<input v-model="cellphone" type="text" maxlength="11" @blur="checkExists" placeholder="请输入被赠送人ID"> <input v-model="cellphone" type="text" maxlength="11" @blur="checkExists" placeholder="请输入被赠送人手机号">
<hr> <hr>
<span class="errorinfo">{{ errorInfo }}</span> <span class="errorinfo">{{ errorInfo }}</span>
</div> </div>
@ -36,8 +36,8 @@
<div class="quota_box"> <div class="quota_box">
<div class="title"> <div class="title">
<b>赠送</b> <b>赠送</b>
<span class="r"> <span class="r" @click="showTerm = true">
<van-icon name="info-o" /> <van-icon name="info-o" />
赠送说明 赠送说明
</span> </span>
@ -52,6 +52,11 @@
</div> </div>
</div> </div>
</BasePage> </BasePage>
<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="$datadic.getContent('code_hykzssm')" />
</van-action-sheet>
</template> </template>
<script> <script>
@ -103,6 +108,7 @@ export default {
amount: 500.00, amount: 500.00,
checked: 'balance', checked: 'balance',
checkedCH: "余额", checkedCH: "余额",
showTerm: false,
left: 0, left: 0,
loading: false, loading: false,
toUser: { NickName: null }, toUser: { NickName: null },
@ -176,6 +182,7 @@ export default {
}).then(data => { }).then(data => {
if (data.status === 200) { if (data.status === 200) {
this.$showSuccessToast('赠卡成功!') this.$showSuccessToast('赠卡成功!')
this.$navigate('CertificateRecord')
} }
}).catch(err => { }).catch(err => {
this.$showFailToast(err.message); this.$showFailToast(err.message);