Compare commits

...

91 Commits

Author SHA1 Message Date
8352b02110 feat: 优化修改密码输入框 2025-02-13 23:32:57 +08:00
2d3147d65d fix: 修复设置密码在真机上兼容问题 2025-02-13 21:54:31 +08:00
1c8de323fe feat: 完善支付密码逻辑 2025-01-21 23:45:15 +08:00
b50eb2f1c2 fix: 修复设置支付密码引用问题 2025-01-21 01:12:18 +08:00
894818401e feat: 支付密码设置逻辑调整 2025-01-21 01:10:27 +08:00
596433ed86 feat: 更新版本号 2025-01-19 14:40:03 +08:00
e16e1e3bdc feat(user): 添加支付密码设置功能 2025-01-19 14:34:45 +08:00
de509c7e14 店铺用户注册强制完善手机号 2024-06-18 12:33:25 +08:00
13ee0f593c bug修复 2024-06-17 16:02:46 +08:00
14b723ce1f 优化店铺名显示 2024-06-16 00:22:40 +08:00
41384ef8e9 优化错误提示 2024-06-15 19:08:56 +08:00
f83bf3dd98 优化打开团购券分享时用户已登录&未登录,店铺已注册&未注册逻辑处理 2024-06-14 02:21:35 +08:00
6921c82ad3 补充分享未注册店铺的团购券的注册流程 2024-06-12 23:01:53 +08:00
fe91157f6c 团购商品未配置图片问题修复 2024-06-12 16:23:22 +08:00
63b30c0695 修复购买微信团购券不到帐问题 2024-06-11 23:01:50 +08:00
49b1968ba5 团购券购买数量bug修复 2024-06-09 18:57:35 +08:00
a620ecf624 商品团购券分享处理 2024-06-08 17:05:25 +08:00
4b4ee44242 商品团购分享处理 2024-06-07 14:10:43 +08:00
613c0df738 登录逻辑调整 2024-06-06 17:48:37 +08:00
8f06cf703d 登录逻辑调整 2024-06-06 17:36:31 +08:00
351e675c6d 删除日志 2024-06-06 11:49:38 +08:00
7bbf436a1c 原先团购处理 2024-06-06 11:49:38 +08:00
9d138814f2 异常处理 2024-06-06 11:49:38 +08:00
f0ca5624ea 接口错误码兼容处理 2024-06-06 11:49:38 +08:00
947d85c1c6 bug修复 2024-06-06 11:49:38 +08:00
183a95f48b bug修复 2024-06-04 10:21:16 +08:00
c56ea0dd73 bug修复 2024-06-03 22:59:17 +08:00
4d7f73aedf bug修复 2024-06-03 17:49:46 +08:00
3945b36b0a bug修复 2024-06-01 20:06:30 +08:00
4344adeb0b bug修复 2024-05-26 09:50:01 +08:00
11a0637039 优化 2024-05-24 09:41:33 +08:00
b53dc2dd4f 注册调整 2024-05-24 00:21:47 +08:00
0c0d5e55d5 注册调整 2024-05-23 15:49:43 +08:00
b7461002dc 合并master 2024-05-22 18:10:04 +08:00
68262d0519 日志功能封装 2024-05-22 18:02:54 +08:00
d757f99b5d 增加登录日志埋点 2024-05-22 18:02:54 +08:00
e910a76437 增加登录日志埋点 2024-05-22 17:00:14 +08:00
85d52f5065 更新版本 2024-05-20 14:23:43 +08:00
915ff6b184 更新版本 2024-05-20 14:22:18 +08:00
e92323a15b appid调整 2024-05-20 14:16:04 +08:00
048ce5fd05 增加key 2024-05-20 14:14:54 +08:00
448ee810de bug修复 2024-05-20 00:49:16 +08:00
6cd0361f33 优化 2024-05-17 17:08:30 +08:00
0b1f0b9e26 优化 2024-05-17 13:24:25 +08:00
531a955fe2 登录增加协议选择逻辑
增加审核测试参数处理逻辑
2024-05-17 13:12:03 +08:00
cb40ce643f 优化商品默认颜色尺码配置显示问题 2024-05-16 16:25:24 +08:00
f547216c2b 新增商品其他售价逻辑
修复下单重复创建订单问题
2024-05-16 15:38:34 +08:00
62c9994444 修复生产环境折扣价显示错误问题 2024-05-16 13:07:10 +08:00
100744cfaf 联系商家字段调整 2024-05-10 21:33:10 +08:00
74dba2ef29 充值页面调整
商品折扣显示逻辑调整
增加公众号跳转修改资料逻辑
2024-05-10 17:51:30 +08:00
dccdd43e3a bug修复 2024-05-09 23:51:16 +08:00
eb1abb69fd bug修复 2024-04-26 19:01:31 +08:00
68cce822be 优化 2024-04-26 11:41:26 +08:00
5bbfff9e21 注册有礼 2024-04-24 23:14:34 +08:00
7f6ace8ac6 注册有礼 2024-04-24 23:01:38 +08:00
bb9a32d296 bug修复 2024-04-24 21:53:31 +08:00
bfc04c54aa 优化 2024-04-20 21:58:16 +08:00
5b8c7d4a2d bug修复 2024-04-19 21:45:30 +08:00
634e801eb5 商品增加分类 2024-04-19 13:44:31 +08:00
7b3676eda7 优化占位图显示 2024-04-19 12:51:17 +08:00
6c4c2ac85d bug修复 2024-04-18 00:37:43 +08:00
b87c8e8292 团购已结束,未开始显示处理
订单待支付数量显示
2024-04-16 16:52:20 +08:00
32aa0eb971 优化 2024-04-16 11:41:18 +08:00
178e8ff5cb 问题修复 2024-04-16 01:39:23 +08:00
0562554266 问题修复 2024-04-16 00:53:34 +08:00
877469ab43 优化 2024-04-14 20:18:44 +08:00
13fd77f13b 优化 2024-04-14 17:02:49 +08:00
f2f7fcde48 购物车逻辑调整 2024-04-14 16:43:43 +08:00
16dac8d97f 优化 2024-04-14 01:43:37 +08:00
b676a1acd9 功能完善 2024-04-14 01:05:08 +08:00
a270c9be70 功能完善 2024-04-13 19:44:18 +08:00
d6f3f9c812 功能完善 2024-04-13 19:15:20 +08:00
23af4c25e3 功能完善 2024-04-13 17:20:09 +08:00
be328f9243 功能完善 2024-04-13 00:57:24 +08:00
93e9c5227b 切换公司逻辑完善 2024-04-11 21:13:52 +08:00
222bae69d9 ... 2024-04-10 14:46:29 +08:00
c94ddeed1a 团购支付 2024-04-08 21:33:15 +08:00
935d56227d 处理未登录情况 2024-03-31 19:10:00 +08:00
a06d74934f 优化 2024-03-31 18:24:22 +08:00
60cb832b02 优化 2024-03-31 18:19:26 +08:00
b502385272 购物车逻辑完善
个人信息存储优化
团购支付
2024-03-31 17:22:14 +08:00
1fc0aa432b 购物车逻辑完善
个人信息存储优化
团购下单
2024-03-31 03:19:19 +08:00
074b0057da 团购下单 2024-03-30 14:59:51 +08:00
52b63a757f 详情 2024-03-30 00:05:07 +08:00
51c708f9fa 优化 2024-03-29 14:55:40 +08:00
91af869899 优化 2024-03-25 14:44:38 +08:00
4fb648f003 积分接口联调 2024-03-21 23:22:24 +08:00
792aa4268a 购物车逻辑 2024-03-20 22:20:24 +08:00
8d7f82b07c 优化 2024-03-18 21:52:10 +08:00
7f0f11cf14 ... 2024-03-17 14:50:20 +08:00
cfdc8d088c fix: test 2024-03-17 10:50:26 +08:00
149 changed files with 8055 additions and 2565 deletions

View File

@@ -4,6 +4,8 @@ VITE_APP_TITLE='uniapp-vue3-project'
# 开发环境配置 # 开发环境配置
VITE_APP_ENV='development' VITE_APP_ENV='development'
VITE_APP_ID='wx92e663dc11d0c0a8'
# 接口地址 # 接口地址
VITE_APP_BASE_API='https://apidev.lakeapp.cn/' VITE_APP_BASE_API='https://apidev.lakeapp.cn/'

View File

@@ -4,8 +4,10 @@ VITE_APP_TITLE='uniapp-vue3-project'
# 生产环境配置 # 生产环境配置
VITE_APP_ENV='production' VITE_APP_ENV='production'
VITE_APP_ID='wx67a750d0ceed4d88'
# 接口地址 # 接口地址
VITE_APP_BASE_API='http://api.lakeapp.cn/' VITE_APP_BASE_API='https://api.lakeapp.cn/'
# 删除console # 删除console
VITE_DROP_CONSOLE=true VITE_DROP_CONSOLE=true

View File

@@ -1,11 +1,17 @@
# 页面标题 # 页面标题
VITE_APP_TITLE='uniapp-vue3-project' VITE_APP_TITLE='uniapp-vue3-project'
# 生产环境配置 # 开发环境配置
VITE_APP_ENV='production' VITE_APP_ENV='test'
VITE_APP_ID='wx92e663dc11d0c0a8'
# 接口地址 # 接口地址
VITE_APP_BASE_API='/' #VITE_APP_BASE_API='https://apidev.lakeapp.cn/'
VITE_APP_BASE_API='https://api.lakeapp.cn/'
# 删除console # 删除console
VITE_DROP_CONSOLE=true VITE_DROP_CONSOLE=false
# 是否开启Mock
VITE_USE_MOCK=true

View File

@@ -14,7 +14,7 @@ const WX_DESC = envType == 'production' ? '正式环境' : '测试环境'
(async () => { (async () => {
// @ts-ignore // @ts-ignore
const manifest = path.resolve(__dirname, './src/manifest.json') const manifest = path.resolve(__dirname, './src/manifest.json')
console.log(manifest)
// @ts-ignore // @ts-ignore
const manifestConfig = JSON.parse(fs.readFileSync(manifest).toString()) const manifestConfig = JSON.parse(fs.readFileSync(manifest).toString())
const appId = manifestConfig['mp-weixin'].appid const appId = manifestConfig['mp-weixin'].appid

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAw7dY+KsQ9Qa1Q0Amp2bNWW3/ch/2OmS5SsiXSfRgVLKcUB1C
1svyjDUzqaw53Rhn4WdwUCFQCkhWs1ud7NA/eudaqLmvrX0SaH6bJz2XCzyNoElM
amHh0U4sKEFcOcY9FPsriislpCmKnOKjyKB6fnpzIn7JxespswhiDsiKC2dk/8nB
GFeN559MYy6qOUyFrykkcF21oSfOynhw+ziqxRBIKh30dR1nUy+FZrZiVx2Bj/id
39AbZ89lK8pzdlFHtXqd56UCe5fEselh+KbT4eKIEhHoO+vSqralASLDMxijgA3V
49AUmpQ6F4nW9GfF3/IreX8X7k457M6FKo8L1QIDAQABAoIBABnTb+IgcLRkljax
mGguoJpUcYjVvuAlum2r2orfvHZwuz+3HMLLVs45lv+kwl3ygoKz65YnTzCMKySv
yj8dm8LUFJp/Jjd2cZ9oVYDwF6Zb7zRfd73x/KJp9lm5hjn5dsX1/uDUoLrw4GGj
Zolq478c2WSL5oQ/a9k8GfRpcg3e88L89r6QjZuQ3jmVxe4fJlYbUR3YXE2t3c41
v/uQfxXvvVrYGc8/jZE4AQcFbZhKu1xfl7dy8AEisZa4OxU5Gd+onHcnUgGO6oRn
x0pZQnZJZoTLJe8uB0jl+OPLLQbRcA01jITKAHKOqOQYgynfzy6xfMWhNk/4YWtf
oJ+wFmECgYEA7H/Iov01m7+k+oe3XyiQC0zMLtHWzF3thdAUJFRZPBfzb5X/F9a2
k+feK69aXHnsWtVKO1kZ+FMMs7OU0LOX+LRDeeiJqlhnuOC63NfyZ6yfgOEBq38s
C7rxgsuS6D5VvEzj57sjBGRTyfjNZVvfflBMVn9JgwVzdo9RumLnD98CgYEA09qv
IJtFlPcAbIuKQMRUnrLVDI5hMYzQUwpj4RkBOASIgCtJHUoN4GvdAaYeE6q9eeD1
TyKyIJekECejOqLdzidE/aOVRWcgiEjxjGf4e9n4/6q9CJGH7v2WMNbziALHb6We
KBwrIdaakD7m2yVZ1PxTyljCXx3wvv3IN/khSssCgYEAv8CH2+cz3RlQ2bTKcARC
bncRInEPGNqbaCCjBCXDyF6fCX1zvE9h76BTWdYujMZqZeeD7YS78sTbiqRzZ5gG
Ayedgi2R2SUezB6iQ8dvirHDDcriALv26Xfks6j9bwKDn040h0ILDVePPDZXyvPn
zNGsY4GHCA2WZa2klVv1fr8CgYBCkMPWCvOiCronYPDfyNCNzMMEXvhLCWOrs+gO
Ij0vzLzXCMLAYTRa/4lBq2Bn7Y37baOmadBuTTdPnCS9l875p30V9QBB/lVKfSDo
bQK2Z5bFPnCG8LM1dJhsYB3E7pqbif5XvAIRb8zA5gLDeMjxExeAzMLGADJU0IjI
jY7YYQKBgFy7WTwnEV4NTjus1FXj4Bf1BYJRrDRtxMNiKWZZ+C1XQqA1zWiIfDzt
B1LKjBwWZsM2ali582PcMkJYPVZBG+hMmHlAme/acpzGF4oiANZp+kCcrnxFvKvs
PAO6ui1zDN92bGNf/LQQOf1TM29sUbAFnwxNFOxvs2FbpWvFj0V3
-----END RSA PRIVATE KEY-----

View File

@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAtAuySwDuVUqiKAgtB8yhmDXMbFtgSZQiC4FtahrqAxcwcett
khfncnldZZGMUtK3dM/xA5YaKe18vish3qbymwc+863r2NKIFtWhWIglXDuutGgT
U9/IqCnh4Dx8Owlw5/EzbvHSXj1/uzwuAVO6cxkkEvz9mxYixuB3YCd8yKQ5rLnu
EwNf063fI+jDVUBeyEejSZJ0pi3OzWdXJOjS9UYv2bAYOfJluGucfDXMbfdz8euy
T8WRCPJp4X2QoQU5WcSxWnvRSllwAfposZTIcu/msXm77QwoYqCHvJ2suGzL+SXa
c73wQB289dXqHbYmSa7bPmTmOEyCPYao0oIh8wIDAQABAoIBACfpb9ywmCTQJqI5
LcJM2oWjZ22d0/p7nBBw4JBk5vtIaLTYO7HnGqeFv+6EFVdG3uRGNGdXHvWsb7q4
VQnGeIFlWc8q/t1RzllBgkLd62pG0LOtXpt5QEIHBstunRt5PE4uafhuIvRBLc+T
x58DsJFTQWv0KUP35D2yBMSL/F19qqF6oe43kYbAei8w608XXKOT/rODdk1Ie3yx
AOgM4DvTqcSdqL/SDJ6r8xoplDNk4l+QYCJ9z7JodnJfU5OQVWw8WFWRg3jP9k/S
DsojcusTs1s14X8V3/TbR1vVW6g4Q5QnNFozHaKRZAD9DuBStrL02hgAq7oXdg+g
IoMR+AECgYEA7VVuOWwt2aWXks9aPpOVs9knMdrpxbO1nE/B2JECICElFtL8xwUi
BxWcM++wJNtKTAjJfpnFGZkyHfmnB8V0itGIz1lCZTs/yraJqaLxQdbHjEnCoCRq
tl1751hsBS15XFDskKHGFZj0wv9nq3I0wUoMjhsRV+Lopq+uHCodcgMCgYEAwjTO
dwh/0cTjiGOV8iX9WdM0I3wnYfcAgDbxGKNyZNw8oQ52vzTn1VG+LcxMcy+OUq/a
B/XBjkUI4WyHfK7zM6rhlQB8pWR3i+yPyLXJ69oRQwYAeHQzgZ6Z+sSsusHcG/wN
J/Z9EqiWQK/tZsBXQxDpHqxwz9Xl1BlL0xX9BVECgYBHKsr2pR3KmgEtoMfq486M
M52xMXfQNOdMjA4QpssAX3ADvBjYhQ2DGlPQrxsesjNBQZFKSUn1Nx70JhyUE/2y
csqXgqiKOo4Sd1IocBfwKjuEMcoOw1zMepPg937MvqoZqJqHdDs11rvujS/FFWYE
X/QL2MoGlKA2+482GtrhiQKBgQCTt8jnn45hx2nuXxk5w42umkiJSTFHgbJe0+uU
+xXTA/YV50OJcrt4daG7gi8QWjbeTCYCcfrUtUvo8z0nKIeSYEMPq/wjbYTE6J4B
Y8z/2bHRkiofdPuMd0/V/20G7Nf4bUKwh/tgit0mvOpNgrWdLKq1CyMP4znal5cm
Kw520QKBgB+I8yfp/E01ruSccdgRH3WMGStuHhSxcQ8WWD9oFdOn+klxr8h4al3+
Ibo6SJkKmwg2/IEsuc52qZPH5594XfQ7HxfVf9RVwdR96epRqah/cHofIVCX1wQB
CCirZNNiBe2hQxDyAHn492a6dzcjMojDN5JS+Yt071CB0Tph7+2c
-----END RSA PRIVATE KEY-----

40
manifest.modify.ts Normal file
View File

@@ -0,0 +1,40 @@
// @ts-ignore
const fs = require('fs');
// @ts-ignore
const path = require('path');
const currentDir = path.dirname(__filename);
const manifestPath = path.join(currentDir, 'src/manifest.json');
const VITE_APP_WX_APPID = process.env.NODE_ENV === 'production' ? 'wx67a750d0ceed4d88' : 'wx92e663dc11d0c0a8';
fs.readFile(manifestPath, (err, data) => {
if(err) {
console.error(err);
} else {
try {
if(!VITE_APP_WX_APPID) {
console.log('写入失败,没读到appid');
return;
}
const _data = JSON.parse(data.toString());
_data['mp-weixin'].appid = VITE_APP_WX_APPID;
const newData = JSON.stringify(_data, null, 2);
fs.writeFile(
manifestPath,
newData,
{
encoding: 'utf-8'
},
(err) => {
if(err) {
console.log('APPID 写入失败', err);
} else {
console.log(`APPID 写入成功 ${VITE_APP_WX_APPID}`);
}
}
);
} catch (parseError) {
console.error('解析 manifest.json 失败', parseError);
}
}
});

View File

@@ -4,10 +4,12 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"dev": "uni -p mp-weixin", "dev": "uni -p mp-weixin",
"build:test": "uni build --mode test -p mp-weixin", "dev:test": "cross-env NODE_ENV=development node manifest.modify.ts && uni -p mp-weixin --mode development",
"build:prod": "uni build --mode production -p mp-weixin", "dev:prod": "cross-env NODE_ENV=production node manifest.modify.ts && uni -p mp-weixin --mode production",
"deploy:test": "uni build --mode test -p mp-weixin && node deploy.ts test", "build:test": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode test",
"deploy:prod": "uni build --mode production -p mp-weixin && node deploy.ts production", "build:prod": "cross-env NODE_ENV=production UNI_PLATFORM=mp-weixin vue-cli-service uni-build --mode production",
"deploy:test": "cross-env NODE_ENV=test node manifest.modify.ts && uni build --mode test -p mp-weixin && node deploy.ts test",
"deploy:prod": "cross-env NODE_ENV=production node manifest.modify.ts && uni build --mode production -p mp-weixin && node deploy.ts production",
"type-check": "vue-tsc --noEmit", "type-check": "vue-tsc --noEmit",
"eslint": "eslint --fix", "eslint": "eslint --fix",
"stylelint": "stylelint \"src/**/*.(vue|scss|css)\" --fix", "stylelint": "stylelint \"src/**/*.(vue|scss|css)\" --fix",
@@ -15,53 +17,53 @@
"postinstall": "simple-git-hooks" "postinstall": "simple-git-hooks"
}, },
"dependencies": { "dependencies": {
"@dcloudio/uni-app": "3.0.0-3090920231225001", "@dcloudio/uni-app": "3.0.0-alpha-4010520240507001",
"@dcloudio/uni-app-plus": "3.0.0-3090920231225001", "@dcloudio/uni-app-plus": "3.0.0-alpha-4010520240507001",
"@dcloudio/uni-components": "3.0.0-3090920231225001", "@dcloudio/uni-components": "3.0.0-alpha-4010520240507001",
"@dcloudio/uni-mp-weixin": "3.0.0-3090920231225001", "@dcloudio/uni-mp-weixin": "3.0.0-alpha-4010520240507001",
"@dcloudio/uni-mp-xhs": "3.0.0-3090920231225001", "dayjs": "^1.11.10",
"pinia": "2.0.36", "pinia": "2.0.36",
"pinia-plugin-persistedstate": "^3.2.1", "pinia-plugin-persistedstate": "^3.2.1",
"uview-plus": "^3.1.38", "uview-plus": "^3.1.41",
"vue": "3.2.47", "vue": "3.4.21",
"vue-i18n": "^9.1.9", "vue-i18n": "^9.9.0",
"z-paging": "^2.6.2" "z-paging": "^2.6.3"
}, },
"devDependencies": { "devDependencies": {
"@antfu/eslint-config": "1.1.0", "@antfu/eslint-config": "1.1.0",
"@dcloudio/types": "^3.4.3", "@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-3090920231225001", "@dcloudio/uni-automator": "3.0.0-alpha-4010520240507001",
"@dcloudio/uni-cli-shared": "3.0.0-3090920231225001", "@dcloudio/uni-cli-shared": "3.0.0-alpha-4010520240507001",
"@dcloudio/uni-stacktracey": "3.0.0-3090920231225001", "@dcloudio/uni-stacktracey": "3.0.0-alpha-4010520240507001",
"@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001", "@dcloudio/vite-plugin-uni": "3.0.0-alpha-4010520240507001",
"@types/node": "^20.8.10", "@types/node": "^20.10.3",
"@typescript-eslint/parser": "^6.10.0", "@typescript-eslint/parser": "^6.13.1",
"@uni-helper/uni-app-types": "^0.5.9", "@uni-helper/uni-app-types": "^0.5.12",
"@unocss/eslint-plugin": "^0.57.2", "@unocss/eslint-plugin": "^0.57.7",
"@vue/runtime-core": "^3.2.45", "@vue/runtime-core": "^3.4.21",
"@vue/tsconfig": "^0.4.0", "@vue/tsconfig": "^0.4.0",
"czg": "^1.7.1", "czg": "^1.7.1",
"eslint": "^8.53.0", "eslint": "^8.55.0",
"lint-staged": "^15.0.2", "lint-staged": "^15.2.0",
"miniprogram-ci": "^1.9.8", "miniprogram-ci": "^1.9.10",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-scss": "^4.0.9", "postcss-scss": "^4.0.9",
"sass": "^1.69.5", "sass": "^1.69.5",
"simple-git-hooks": "^2.9.0", "simple-git-hooks": "^2.9.0",
"stylelint": "^15.11.0", "stylelint": "^15.11.0",
"stylelint-config-recess-order": "^4.3.0", "stylelint-config-recess-order": "^4.4.0",
"stylelint-config-standard": "^34.0.0", "stylelint-config-standard": "^34.0.0",
"stylelint-config-standard-vue": "^1.0.0", "stylelint-config-standard-vue": "^1.0.0",
"stylelint-order": "^6.0.3", "stylelint-order": "^6.0.3",
"typescript": "^5.2.2", "typescript": "^5.3.2",
"unocss": "^0.57.2", "unocss": "^0.57.7",
"unocss-applet": "^0.7.7", "unocss-applet": "^0.7.8",
"unplugin-auto-import": "^0.16.7", "unplugin-auto-import": "^0.16.7",
"unplugin-vue-components": "^0.25.2", "unplugin-vue-components": "^0.25.2",
"vite": "^4.5.2", "vite": "^5.2.8",
"vite-plugin-imagemin": "^0.6.1", "vite-plugin-imagemin": "^0.6.1",
"vue-tsc": "^1.8.22" "vue-tsc": "^1.8.24"
}, },
"lint-staged": { "lint-staged": {
"src/**/*.{js,jsx,ts,tsx}": "eslint --fix", "src/**/*.{js,jsx,ts,tsx}": "eslint --fix",

3131
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,233 @@
<script setup lang="ts"> <script lang='ts'>
import { mpUpdate } from '@/utils'; import {
getCompanyId,
getQueryParam,
getRegisterStoreId,
mpUpdate,
setCompanyId,
setReferrerUserId,
setRegisterStoreId
} from '@/utils';
import { Logger } from '@/utils/common/logger';
const logger = new Logger();
export default {
onLaunch: async (options: any) => {
const miniProgram = uni.getAccountInfoSync().miniProgram;
const env = miniProgram.envVersion;
console.log(`App Launch options ${env}: `, options);
//生产
if(env === 'release') {
logger.info(`App Launch options ${env}: `, options);
async function getVersionStatus() {
return new Promise((resolve, reject) => {
uni.request({
url: 'https://api.lakeapp.cn/wechat/version_info',
success(res) {
console.log(res);
resolve(res);
},
fail(exception) {
reject(exception);
}
});
}).then((res) => {
const { version, audit } = res.data;
// if(miniProgram.version === version && audit == 1) {
if(audit == 1) {
setCompanyId('1150930317231112193');
setRegisterStoreId('1150930317436633090');
}
});
}
await getVersionStatus();
}
//测试、开发 trial,develop
else {
//生产环境
if(import.meta.env.VITE_APP_BASE_API.includes('api.lakeapp')) {
setCompanyId('1150930317231112193');
setRegisterStoreId('1150930317436633090');
// setCompanyId('1471673498334113794');
// setRegisterStoreId('1471673498413805570');
}
//测试
else {
setCompanyId('1724629180555919361');
setRegisterStoreId('1725026299888406529');
// setRegisterStoreId('17250262998884000000');
}
}
if(options?.query) {
// options.query.scene = 'companyId%3D1150930317231112193%26wxOpenId%3D111%26ticketId%3D123456%26storeId%3D1150930317436633090'
//保存登录邀请员工id
if(options.query.referrerUserId) {
setReferrerUserId(options.query.referrerUserId);
}
//保存注册门店id
if(options.query.companyId) {
setCompanyId(options.query.companyId);
}
//保存注册门店id
if(options.query.storeId) {
setRegisterStoreId(options.query.storeId);
}
if(options?.query.scene) {
//保存注册门店id
const params = decodeURIComponent(options?.query.scene);
if(params.includes('companyId')) {
setCompanyId(getQueryParam(params, 'companyId') || '');
}
if(params.includes('storeId')) {
setRegisterStoreId(getQueryParam(params, 'storeId') || '');
}
if(options?.query.scene === 'edit_avatar_nickname') {
setTimeout(() => {
uni.reLaunch({
url: '/pages/mine/subs/profile/index'
});
}, 500);
}
}
}
logger.info(`App Launch options ${env}: { companyId: `, getCompanyId() + ', storeId: ' + getRegisterStoreId() + ' }');
onLaunch(() => {
console.log('App Launch');
// #ifdef MP // #ifdef MP
mpUpdate(); mpUpdate();
// #endif // #endif
}); },
onShow(() => { onShow: async () => {
console.log('App Show'); console.log('App Show');
}); },
onHide(() => { onHide: async () => {
console.log('App Hide'); console.log('App Hide');
}); },
globalData: {
logger: logger,
companyId: getCompanyId(),
storeId: getRegisterStoreId(),
shareCompanyId: '',//分享过来的公司id
shareStoreId: '',//分享过来的店铺id
sharePath: ''//分享页面路径,方便登录后跳转
}
};
// onLaunch(async (options) => {
// console.log('App Launch options ', options);
// // globalData.logger.info('launch options : ', options);
//
// const miniProgram = uni.getAccountInfoSync().miniProgram;
// const env = miniProgram.envVersion;
//
// logger.info('launch options env : ', `${env} ${options}`);
// //生产
// if(env === 'release' || env === 'trial') {
// async function getVersionStatus() {
// return new Promise((resolve, reject) => {
// uni.request({
// url: 'https://api.lakeapp.cn/wechat/version_info',
// success(res) {
// console.log(res);
// resolve(res);
// },
// fail(exception) {
// reject(exception);
// }
// });
// }).then((res) => {
// const { version, audit } = res.data;
// // if(miniProgram.version === version && audit == 1) {
// if(audit == 1) {
// setCompanyId('1150930317231112193');
// setRegisterStoreId('1150930317436633090');
// }
// });
// }
//
// await getVersionStatus();
// }
//
// //测试、开发 trial,develop
// else {
// // setCompanyId('1724629180555919361');
// // setRegisterStoreId('1725026299888406529');
// setCompanyId('1471673498334113794');
// setRegisterStoreId('1471673498413805570');
// }
//
// if(options?.query) {
//
// // options.query.scene = 'companyId%3D1150930317231112193%26wxOpenId%3D111%26ticketId%3D123456%26storeId%3D1150930317436633090'
//
// //保存登录邀请员工id
// if(options.query.referrerUserId) {
// setReferrerUserId(options.query.referrerUserId);
// }
//
// //保存注册门店id
// if(options.query.companyId) {
// setCompanyId(options.query.companyId);
// }
//
// //保存注册门店id
// if(options.query.storeId) {
// setRegisterStoreId(options.query.storeId);
// }
//
// if(options?.query.scene) {
// function getQueryParam(queryParams: string, key: string) {
// let regex = new RegExp('(?:[?&]|^)' + key + '=([^&]+)'),
// match = queryParams.match(regex);
// return match && match[1];
// }
//
// //保存注册门店id
// const params = decodeURIComponent(options?.query.scene);
// if(params.includes('companyId')) {
// setCompanyId(getQueryParam(params, 'companyId') || '');
// }
// if(params.includes('storeId')) {
// setRegisterStoreId(getQueryParam(params, 'storeId') || '');
// }
// if(options?.query.scene === 'edit_avatar_nickname') {
// setTimeout(() => {
// uni.reLaunch({
// url: '/pages/mine/subs/profile/index'
// });
// }, 500);
// }
// }
// }
//
// // #ifdef MP
// mpUpdate();
// // #endif
// });
// onShow(() => {
// console.log('App Show');
// });
// onHide(() => {
// console.log('App Hide');
// });
</script> </script>
<style lang="scss"> <style lang='scss'>
/* 每个页面公共css */ /* 每个页面公共css */
@import 'uview-plus/index.scss'; @import 'uview-plus/index.scss';
@import '@/static/styles/common.scss'; @import '@/static/styles/common.scss';

View File

@@ -1,3 +1,6 @@
import { get } from '@/utils/request'; import { get } from '@/utils/request';
export const getCompanyInfo = (companyId: string) => get({ url: `/ext/zconfig/company_find?companyId=${companyId}` }); export const getCompanyList = (maOpenId: string) => get({ url: `/wc/wechat/company?maOpenId=${maOpenId}` });
export const getCompanyInfo = () => get({ url: `/ext/zconfig/company_find` });
export const getStoreId = (data: any) => get({ url: '/wc/wechat/get_store_id', data });

View File

@@ -1,14 +1,14 @@
import { get, post } from '@/utils'; import { get } from '@/utils';
import { CategoryBean, GoodsBean } from '@/api/goods/types';
enum URL { enum URL {
categoryList = '/ext//goods/category_list', categoryList = '/ext/goods/category_list',
goodsList = '/ext/goods/query', goodsList = '/ext/goods/query',
goodsDetail = '/ext/goods/info', goodsDetail = '/ext/goods/info'
order = '/user/logout',
} }
export const getCategoryList = (companyId: string) => get({ url: URL.categoryList + `?companyId=${companyId}` }); export const getCategoryList = () => get<CategoryBean[]>({ url: URL.categoryList });
export const getGoodsList = (params: any) => post({ url: URL.goodsList, data: params }); export const getGoodsList = (params: any) => get<GoodsBean[]>({ url: URL.goodsList, data: params });
export const getGoodsDetail = () => get({ url: URL.goodsDetail }); export const getGoodsDetail = (goodsId: string) => get<GoodsBean>({ url: URL.goodsDetail + `?goodsId=${goodsId}` });

View File

@@ -4,9 +4,67 @@ export interface CategoryBean {
} }
export interface GoodsBean { export interface GoodsBean {
code: string; id: string;
goodsId: string; goodsId: string;
goodsName: string; goodsName: string;
goodsCode: string,
allow_integral: number;
available: number;
back_factory: number;
brand_id: string;
brand_name: string;
code: string;
company_id: string;
cost_price: number;
create_time: string;
creator_id: string;
discount: number;
images: string; images: string;
market_time: string;
material_id: string;
material_name: string;
name: string;
price: number; price: number;
send_num: number;
priceExt: number;
price_ext: number;
payPrice: number;
profile: string;
remark: string;
remark1: string;
remark2: string;
remark3: string;
remark4: string;
season_id: string;
season_name: string;
status: number;
stocks: StockBean[],
store_id: string;
suplier_id: string;
suplier_name: string;
type_id: string;
type_name: string;
update_time: string;
years_id: string;
years_name: string;
/*extra params*/
goodsNum: number;
salePrice: number;
originPrice: number;
consumePrice: number;
stockStock: StockBean
checkedStock: StockBean;
checked: boolean | false;
}
export interface StockBean {
colorId: string;
colorName: string;
existingNumber: number;
sizeId: string;
sizeName: string;
stockId: string;
images: string,
count: number;
} }

View File

@@ -1,10 +1,33 @@
import { get } from '@/utils/request'; import { get, post } from '@/utils/request';
import { GroupBuyBean, RecordBean } from '@/api/groupbuy/types';
import { GoodsBean } from '@/api/goods/types';
export const getGroupBuyList = (data: { export const getGroupBuyList = (data: {
pageNum: string, pageNum: number,
pageSize: string, pageSize: number,
obj: { obj: {
companyId: string, timeStatus: number,
} }
}) => get({ url: '/wc/coupons/group/pageList', data }); }) => post({ url: '/group/coupons/wx_query', data });
export const getGroupBuyDetail = (
id: string) => get<GroupBuyBean>({ url: `wechat/coupons/group/get/${id}` });
export const getShareGoodsDetail = (shareId: string) => post<any>({ url: '/wechat/coupons/group/qrcode/' + `${shareId}` });
export const getShareGoodsCouponDetail = (couponId: string) => get<GroupBuyBean>({ url: 'wechat/coupons/get/' + `${couponId}` });
export const getGroupBuyRecordList = (groupId: string, pageNum: number, pageSize: number) => post<RecordBean[]>({
url: `wechat/coupons/group/order/list?groupId=${groupId}&pageNum=${pageNum}&pageSize=${pageSize}`
});
export const preOrder = (data: any) => post<GoodsBean>({ url: 'wechat/coupons/pre', data });
export const preOrderV2 = (data: any) => post<GoodsBean>({ url: 'wechat/coupons/group/pre_v2', data });
export const progress = (data: any) => post<any>({ url: 'wechat/coupons/group/pay/progress', data });
export const pay = (data: any) => post<any>({ url: 'wechat/coupons/group/pay', data });
export const couponPay = (data: any) => post<any>({ url: 'wechat/coupons/pay', data });

37
src/api/groupbuy/types.ts Normal file
View File

@@ -0,0 +1,37 @@
import { GoodsBean } from '@/api/goods/types';
import { CouponBean } from '@/api/user/types';
export interface GroupBuyBean {
companyId: string;
content: string;
couponsList: CouponBean[];
createTime: string;
creatorId: string;
creatorName: string;
endDate: string;
fake: number;
goods: GoodsBean;
id: string;
name: string;
offsetPrice: number;
payPrice: number;
goodsPrice: number;
periodDay: number;
price: number;
publicNum: number;
sendNum: number;
startDate: string;
status: number;
threshold: number;
title: string;
totalNum: number;
}
export interface RecordBean {
bizId: string;
createTime: string;
goodsCode: string;
goodsNum: number;
groupId: string;
memberImage: string;
}

26
src/api/order/index.ts Normal file
View File

@@ -0,0 +1,26 @@
import { get, post } from '@/utils';
enum URL {
orderCreate = '/sales/wx_order_create',
orderList = '/sales/wx_query',
orderDetail = '/order/wx_get/',
paymentList = '/sales/payment/',
overPayment = '/sales/over_payment'
}
export const orderCreate = (data: any) => post({ url: URL.orderCreate, data });
export const getOrderList = (data: {
pageNum: number,
pageSize: number,
obj: {
payStatus: number
}
}) => post({ url: URL.orderList, data });
export const getOrderDetail = (orderId: string) => get<any>({ url: URL.orderDetail + `${orderId}` });
export const getPaymentList = (orderId: string) => get<any>({ url: URL.paymentList + `${orderId}` });
export const overPayment = (data: any) => post({ url: URL.overPayment, data });

78
src/api/order/types.ts Normal file
View File

@@ -0,0 +1,78 @@
import { GoodsBean, StockBean } from '@/api/goods/types';
export interface OrderBean {
allowIntegral: boolean;
bizId: string;
classify: number;
address: any,
companyId: string;
consignTime: string;
consumerId: string;
consumerName: string;
coupon: string;
couponIds: string;
couponsStatus: number;
createTime: string;
creatorId: number;
creatorName: string;
device: string;
discount: number;
discountPrice: number;
finishTime: string;
freePrice: number;
goodsNum: number;
id: string;
integral: number;
itemNum: number;
marketingPrice: number;
marketingText: string;
modifierIds: string;
needPaid: number;
ogList: any[];
orderGoods: GoodsBean[];
changeStockNum: number;
consumePrice: number;
discountOriginPrice: number;
goodsCode: string;
goodsId: string;
goodsName: string;
goodsPriceModify: string;
goodsTypeName: string;
images: string;
offset: string;
orderId: string;
orderNo: string;
originPrice: number;
originStockNum: number;
priceModify: [];
produceIntegral: number;
salePrice: number;
stockId: string;
stockStock: StockBean[];
payStatus: number;//状态 1.未支付 2.已支付
payTypeIds: string;
printed: string;
produceIntegralNumber: number;
profit: string;
reducePrice: number;
relatedId: string;
remark: string;
replacementOrder: string;
saleNum: number;
salers: [];
status: number;
storeId: string;
storeName: string;
storeOrder: string;
totalPrice: number;
transactionPrice: number;
type: number;
typeName: string;
updateTime: string;
updated: boolean;
useGold: number;
wholePrice: number;
//extra field
countdown: number;
}

View File

@@ -1,30 +1,51 @@
/** /**
* 用户信息相关接口 * 用户信息相关接口
*/ */
import type { LoginParams, LoginResult, RegisterParams } from './types'; import type { CouponBean, LoginParams, LoginResult, RegisterParams, TerminalBean, SetPayPasswordParams,PayPasswordResponse} from './types';
import { get, post } from '@/utils/request'; import { get, post } from '@/utils/request';
import type { UserState } from '@/store/modules/user/types'; import { UserBean } from '@/store/modules/user/types';
enum URL { enum URL {
// login = '/member/login', // login = '/member/login',
login = '/wc/wechat/LoginByMa', login = '/wc/wechat/LoginByMa',
loginByCode = '/wc/wechat/LoginByMaCode', loginByCode = '/wc/wechat/LoginByMaCode',
register = '/wc/wechat/register', register = '/wc/wechat/register',
telephone = '/wc/wechat/telephone',
uploadAvatar = '/wc/wechat/uploadImage',
logout = '/user/logout', logout = '/user/logout',
profile = '/user/profile', // profile = '/user/profile',
profile = 'wc/wechat/member_detail',
updateProfile = '/member/wx_update',
// wc/wechat/member_detail
addressList = '/ext/addr/list', addressList = '/ext/addr/list',
addressDetail = '/ext/addr/find', addressDetail = '/ext/addr/find',
addressCreate = '/ext/addr/create', addressCreate = '/ext/addr/create',
addressUpdate = '/ext/addr/update', addressUpdate = '/ext/addr/update',
addressDelete = '/ext/addr/delete', addressDelete = '/ext/addr/delete',
dynamicCode = '/member/dynccode', dynamicCode = '/member/dynccode',
rechargeList = '/ext/recharge/rule_config' rechargeList = '/ext/recharge/rule_config',
preRecharge = '/memberIncoming/wx_incoming_pre',
recharge = '/memberIncoming/wx_save',
rechargeVerify = '/memberIncoming/wx_paid',
couponList = '/wechat/coupons/coupons/pageList',
integralList = '/ext/member/integral_query',
tradeList = '/memberIncoming/wx_balance_records',
terminal = 'wechat/coupons/terminal?companyId=',
cardLink = '/wc/wechat/get_card_url',
registerCoupon = '/couponsStrategy/wx_register_coupon',
setPayPassword = '/app/pay/set_paypwd',
} }
export const getUserProfile = () => get<UserState>({ url: URL.profile }); export const getUserProfile = () => get<UserBean>({ url: URL.profile });
export const updateProfile = (data: any) => post<any>({ url: URL.updateProfile, data });
export const login = (data: LoginParams) => post<LoginResult>({ url: URL.login, data }); export const login = (data: LoginParams) => post<LoginResult>({ url: URL.login, data });
export const loginByCode = (code: string, companyId: string) => post<any>({ url: URL.loginByCode + `?code=${code}` }); export const loginByCode = (code: string, companyId: string) => post<any>({ url: URL.loginByCode + `?code=${code}` });
export const getTelephone = (data: any) => post<any>({ url: URL.telephone, data });
export const register = (data: RegisterParams) => post<LoginResult>({ url: URL.register, data }); export const register = (data: RegisterParams) => post<LoginResult>({ url: URL.register, data });
export const logout = () => post<any>({ url: URL.logout }); export const logout = () => post<any>({ url: URL.logout });
@@ -34,10 +55,33 @@ export const getAddressDetail = (id: string) => get<any>({ url: URL.addressDetai
export const addressCreate = (data: any) => post<any>({ url: URL.addressCreate, data }); export const addressCreate = (data: any) => post<any>({ url: URL.addressCreate, data });
export const addressUpdate = (data: any) => post<any>({ url: URL.addressUpdate, data }); export const addressUpdate = (data: any) => post<any>({ url: URL.addressUpdate, data });
export const addressDelete = (id: string) => post<any>({ url: URL.addressDelete + `?id=${id}` }); export const addressDelete = (id: string) => post<any>({ url: URL.addressDelete + `?addrid=${id}` });
export const getDynamicCode = () => post<any>({ url: URL.dynamicCode }); export const getDynamicCode = () => post<any>({ url: URL.dynamicCode });
export const getRechargeList = () => get<any>({ url: URL.rechargeList }); export const getRechargeList = () => get<any>({ url: URL.rechargeList });
export const getCouponList = (data: any) => post<any>({ url: '/couponseUse/pageList', data }); export const preRecharge = (data: any) => post({ url: URL.preRecharge, data });
export const recharge = (data: any) => post({ url: URL.recharge, data });
export const rechargeVerify = (data: any) => post({ url: URL.rechargeVerify, data });
export const getCouponList = (data: any) => post<CouponBean[]>({ url: URL.couponList, data });
export const getIntegralList = (data: any) => get({ url: URL.integralList, data });
export const getTerminal = (companyId: string) => get<TerminalBean>({ url: URL.terminal + companyId });
export const getTradeList = (data: any) => post<any>({ url: URL.tradeList, data });
export const getCardLink = () => get<string>({ url: URL.cardLink });
export const getRegisterCoupon = () => get<any>({ url: URL.registerCoupon });
/**
* 设置支付密码
*/
export const setPayPassword = (data: SetPayPasswordParams) => post<PayPasswordResponse>({
url: URL.setPayPassword,
data
});

View File

@@ -1,10 +1,11 @@
import { UserState } from '@/store/modules/user/types'; import { UserBean } from '@/store/modules/user/types';
export interface LoginParams { export interface LoginParams {
// phone: string; // phone: string;
code: string; code: string;
userInfo: any; userInfo: any;
referrerUserId: string; storeId: string;
// referrerUserId: string;
} }
export interface RegisterParams { export interface RegisterParams {
@@ -17,7 +18,7 @@ export interface RegisterParams {
birthday: string, birthday: string,
companyId: string, companyId: string,
creatorId: string, creatorId: string,
gender: number gender: string
} }
export interface LoginByCodeParams { export interface LoginByCodeParams {
@@ -27,7 +28,105 @@ export interface LoginByCodeParams {
export interface LoginResult { export interface LoginResult {
sessionKey: string; sessionKey: string;
user: UserState; user: UserBean;
userInfo: any; userInfo: any;
token: string; token: string;
maOpenId: string;
}
export interface IntegralBean {
id: string;
createtime: string;
exsitvalue: number;
remark: string;
type: number;
value: number;
}
export interface CouponBean {
companyId: string;
couponsId: string;
createTime: string;
creatorId: number;
creatorName: string;
dayType: number;
endTime: string;
id: string;
images: string;
imcomingId: string;
memberId: string;
memberName: string;
name: string;
notice: number;
offsetPrice: number;
orderId: null;
periodDay: number;
reduce: number;
remark: string;
startTime: string;
status: number;
strategyType: string;
telephone: string;
threshold: number;
type: number;
userTime: string;
checked: boolean;
}
export interface TerminalBean {
applyId: string;
bindId: string;
bindName: string;
bindStatus: number;
company: string;
companyId: string;
companyName: string;
contact: string;
createTime: string;
creatorId: string;
creatorName: string;
deviceRole: string;
employee: string;
expireTime: string;
id: string;
managerPermission: string;
name: string;
permission: {
deletablePer: boolean,
profitPer: boolean,
exportTypes: any[],
checkoutPer: boolean,
showPhoneNumber: boolean,
storePer: any[]
};
checkoutPer: boolean;
deletablePer: boolean;
exportPer: boolean;
exportTypes: any[];
profitPer: boolean;
showPhoneNumber: boolean;
storePer: any[];
signDate: string;
status: number;
store: string;
storeId: string;
storeName: string;
storeOrder: number;
terminalKey: string;
terminalSn: string;
token: string;
type: number;
}
export interface SetPayPasswordParams {
paywithpwd: number // 0: 开启支付密码, 1: 关闭支付密码
oldpwd: string,
newpwd: string
}
export interface PayPasswordResponse {
success: boolean
message: string
hasPassword?: boolean
} }

View File

@@ -0,0 +1,109 @@
<template>
<uni-popup ref='popupRef' type='bottom' :mask-click='false' @touchmove.stop.prevent=''>
<view class='content'>
<text class='title'>切换门店</text>
<scroll-view scroll-y style='max-height: 600rpx;padding: 15rpx'>
<view class='store-item' v-for='(item, index) in companyList' :key='index' @click='() => {
handleClick(index)
}'>
<view class='c-flex-row'>
<text>{{ item?.companyName || '' }}</text>
<image v-if='index===currentIndex' src='/static/images/ic_checkmark_red.png' />
</view>
</view>
</scroll-view>
<view class='close-btn primary-button' @click='close'>
<text>取消</text>
</view>
</view>
</uni-popup>
</template>
<script lang='ts' setup>
const props = defineProps({
companyList: {
type: Array,
default: () => []
}
});
const popupRef = ref();
const currentIndex = ref(0);
let callback: Function;
const show = (companyId: string, fn: Function) => {
if(companyId) {
currentIndex.value = props.companyList.findIndex(item => item.id === companyId);
}
callback = fn;
popupRef.value.open();
};
const handleClick = (index: number) => {
currentIndex.value = index;
callback(index);
close();
};
const close = () => {
popupRef.value.close();
};
defineExpose({
show, close
});
</script>
<style lang='scss' scoped>
.content {
display: flex;
flex-direction: column;
background: #FFFFFF;
border-radius: 20rpx 20rpx 0 0;
padding: 20rpx 30rpx 30rpx 30rpx;
.title {
display: flex;
align-self: center;
font-size: 30rpx;
font-weight: bold;
padding: 20rpx;
}
.store-item {
display: flex;
flex-direction: column;
font-size: 35rpx;
color: #333333;
//padding: 20rpx 20rpx;
.c-flex-row {
text {
display: flex;
flex: 1;
}
image {
width: 30rpx;
height: 30rpx;
margin-right: 30rpx;
}
}
}
.store-item:after {
content: '';
background: #F5F5F5;
height: 0.5rpx;
width: 100%;
margin: 25rpx 0;
}
.close-btn {
background: #F5F5F5;
color: #333333;
justify-content: center;
}
}
</style>

View File

@@ -0,0 +1,290 @@
<template>
<uni-popup ref="popup" type="center" :safe-area="true" :mask-click="false">
<view class="pay-password-dialog">
<view class="dialog-header">
<text class="title">{{ getDialogTitle }}</text>
<u-icon name="close" size="20" color="#999" @click="hide" />
</view>
<view class="form-container">
<!-- 关闭支付密码选项 -->
<view class="switch-item" v-if="hasOldPassword">
<text>关闭支付密码</text>
<u-switch v-model="isClosePassword" activeColor="#F32B2B" @change="handleSwitchChange" />
</view>
<!-- 旧密码 - 只在修改密码或关闭密码时显示 -->
<view class="form-item" v-if="hasOldPassword">
<text class="label">原密码</text>
<view class="input-wrapper">
<u-input v-model="oldPassword" type="number" :border="false" placeholder="请输入原密码" :maxlength="6"
:disabled="isClosePassword" :password="!showOldPassword" @input="handleInput($event, 'oldPassword')" />
<u-icon :name="showOldPassword ? 'eye-off' : 'eye-fill'" size="20" color="#999"
v-if="hasOldPassword && !isClosePassword" @click="showOldPassword = !showOldPassword" />
</view>
</view>
<!-- 新密码和确认密码部分 -->
<template v-if="!isClosePassword">
<!-- 新密码 -->
<view class="form-item">
<text class="label">新密码</text>
<view class="input-wrapper">
<u-input v-model="newPassword" type="number" placeholder="请输入6位数字密码" maxlength="6" :border="false"
:password="!showNewPassword" @input="handleInput($event, 'newPassword')" />
<u-icon :name="showNewPassword ? 'eye-off' : 'eye-fill'" size="20" color="#999"
@click="showNewPassword = !showNewPassword" />
</view>
</view>
<!-- 确认密码 -->
<view class="form-item">
<text class="label">确认密码</text>
<view class="input-wrapper">
<u-input v-model="confirmPassword" type="number" :border="false" placeholder="请再次输入密码" maxlength="6"
:password="!showConfirmPassword" @input="handleInput($event, 'confirmPassword')" />
<u-icon :name="showConfirmPassword ? 'eye-off' : 'eye-fill'" size="20" color="#999"
@click="showConfirmPassword = !showConfirmPassword" />
</view>
</view>
</template>
</view>
<view class="footer">
<button :class="{ 'primary-button': isFormValid, 'disabled-button': !isFormValid }" :disabled="!isFormValid"
@click.stop='handleConfirm'>
完成
</button>
</view>
</view>
</uni-popup>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useUserStore } from '@/store'
const userStore = useUserStore()
const popup = ref<any>(null)
// 密码显示状态 - 默认都是不可见的
const showOldPassword = ref(false)
const showNewPassword = ref(false)
const showConfirmPassword = ref(false)
// 重置密码可见性
const resetPasswordVisibility = () => {
showOldPassword.value = false
showNewPassword.value = false
showConfirmPassword.value = false
}
// 表单数据
const oldPassword = ref('')
const newPassword = ref('')
const confirmPassword = ref('')
// 是否有旧密码(是否已开启支付密码)
const hasOldPassword = ref(false)
// 是否关闭支付密码
const isClosePassword = ref(false)
// 获取弹框标题
const getDialogTitle = computed(() => {
if (!hasOldPassword.value) return '设置支付密码'
if (isClosePassword.value) return '关闭支付密码'
return '修改支付密码'
})
// 表单验证逻辑更新
const isFormValid = computed(() => {
if (isClosePassword.value) {
// 关闭支付密码时,只要有开关就可以点击完成
return true
} else if (!hasOldPassword.value) {
// 首次设置密码
if (!newPassword.value || !confirmPassword.value) return false
if (newPassword.value.length !== 6 || confirmPassword.value.length !== 6) return false
return newPassword.value === confirmPassword.value
} else {
// 修改密码
if (!oldPassword.value || !newPassword.value || !confirmPassword.value) return false
if (oldPassword.value.length !== 6 || newPassword.value.length !== 6 || confirmPassword.value.length !== 6) return false
return newPassword.value === confirmPassword.value
}
})
// 处理输入限制只能输入数字且最多6位
const handleInput = (value: string, field: 'oldPassword' | 'newPassword' | 'confirmPassword') => {
// 只保留数字且最多6位
const numericValue = value?.replace(/[^\d]/g, '').slice(0, 6)
// 更新对应字段的值
switch (field) {
case 'oldPassword':
oldPassword.value = numericValue
break
case 'newPassword':
newPassword.value = numericValue
break
case 'confirmPassword':
confirmPassword.value = numericValue
break
}
}
// 生成随机6位数字
const generateRandomPassword = () => {
return Math.floor(Math.random() * 1000000).toString().padStart(6, '0')
}
// 重置表单更新
const resetForm = () => {
oldPassword.value = ''
newPassword.value = ''
confirmPassword.value = ''
resetPasswordVisibility()
isClosePassword.value = false
}
// 显示弹框
const show = async () => {
hasOldPassword.value = userStore.userInfo.paywithpwd === 1
isClosePassword.value = false
resetPasswordVisibility()
popup.value?.open('center')
}
// 隐藏弹框
const hide = () => {
popup.value?.close()
resetForm()
}
// 确认按钮处理更新
const handleConfirm = async () => {
try {
await userStore.setPayPassword({
paywithpwd: isClosePassword.value ? 0 : 1,
oldpwd: hasOldPassword.value ? oldPassword.value : '',
newpwd: isClosePassword.value ? '' : newPassword.value,
})
// 更新本地状态
userStore.userInfo.paywithpwd = isClosePassword.value ? 0 : 1
uni.showToast({
title: isClosePassword.value ? '已关闭支付密码' : '设置成功',
icon: 'success',
})
hide()
resetForm()
} catch (error) {
uni.showToast({
title: '操作失败',
icon: 'error',
})
}
}
// 处理开关切换
const handleSwitchChange = (value: boolean) => {
isClosePassword.value = value
// 切换时重置密码输入
if (value) {
newPassword.value = ''
confirmPassword.value = ''
oldPassword.value = generateRandomPassword() // 生成随机6位数字
resetPasswordVisibility()
} else {
oldPassword.value = ''
}
}
// 暴露方法给父组件使用
defineExpose({
show,
hide
})
</script>
<style lang="scss" scoped>
.pay-password-dialog {
width: 600rpx;
padding: 40rpx;
background-color: #fff;
border-radius: 24rpx;
position: relative;
.dialog-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
}
.form-container {
.switch-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
margin-bottom: 20rpx;
text {
font-size: 28rpx;
color: #333;
}
}
.form-item {
margin-bottom: 30rpx;
.label {
font-size: 30rpx;
color: #333;
margin-bottom: 16rpx;
display: block;
}
.input-wrapper {
display: flex;
align-items: center;
background-color: #f5f5f5;
border-radius: 12rpx;
padding: 20rpx;
height: 50rpx;
:deep(.u-border) {
border-style: hidden;
}
:deep(.u-input) {
flex: 1;
height: 100%;
background-color: transparent;
border: none;
font-size: 10rpx;
}
:deep(.u-input__input) {
height: 100%;
line-height: 60rpx;
font-size: 10rpx;
border: none;
padding: 0;
}
}
}
}
.footer {
padding: 0 30rpx;
}
}
</style>

View File

@@ -0,0 +1,92 @@
<template>
<sqb-pay style='width: 100%;' @bindnavigateTo='navigateTo'
:return_url='payParams.return_url'
:total_amount='payParams.total_amount'
:terminal_sn='payParams.terminal_sn'
:client_sn='payParams.client_sn'
:subject='payParams.subject'
:subject_img='payParams.subject_img '
:merchant_name='payParams.merchant_name'
:notify_url='payParams.notify_url'
:sign='payParams.sign'>
<slot name='button'></slot>
</sqb-pay>
</template>
<script lang='ts' setup>
import { PropType } from 'vue';
import { parseParameter, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { useUserStore } from '@/store';
const userState = useUserStore();
const { terminalInfo } = storeToRefs(userState);
export interface SqbPayParams {
return_url: string;
total_amount: number;
client_sn: string;
terminal_sn: string;
subject: string;
subject_img: string;
merchant_name: string;
notify_url: string;
sign: string;
}
const props = defineProps({
payParams: {
type: Object as PropType<SqbPayParams>,
default: () => {
return {
return_url: '',
total_amount: 0,
client_sn: '',
terminal_sn: '',
subject: '',
subject_img: '',
merchant_name: '',
notify_url: '',
sign: ''
};
}
}
});
watch(() => props.payParams, (newValue, prevValue) => {
if(newValue) {
let signParams = buildSignParams.value;
const signStr = parseParameter(signParams) + '&key=' + terminalInfo.value.terminalKey;
props.payParams.sign = hexMD5(signStr).toUpperCase();
}
});
const buildSignParams = computed(() => {
return sortASCII({
client_sn: props.payParams.client_sn || '',
return_url: props.payParams.return_url,
total_amount: Number((props.payParams.total_amount * 100).toFixed(2)),
terminal_sn: props.payParams.terminal_sn,
subject: props.payParams.subject,
subject_img: props.payParams.subject_img,
merchant_name: props.payParams.merchant_name,
notify_url: props.payParams.notify_url
}, true);
});
const navigateTo = (e: any) => {
console.log('--------------_>>>>>>navigateTo ', e);
uni.redirectTo({
url: e.detail.url,
fail(e) {
uni.showToast({
title: '支付失败'
});
}
});
};
</script>
<style lang='scss' scoped>
</style>

View File

@@ -14,7 +14,7 @@
<view class='divider' style='margin: 40rpx 0' /> <view class='divider' style='margin: 40rpx 0' />
<view class='c-flex-row' @click.stop='doPayment(PAYMENT_TYPE_BALANCE)'> <view class='c-flex-row' @click.stop='doPayment(PAYMENT_TYPE_BALANCE)'>
<image :src='assetsUrl("ic_balance.png")' /> <image :src='assetsUrl("ic_balance.png")' />
<text>余额剩余100</text> <text>余额剩余{{ userInfo.balance }}</text>
<image :src='assetsUrl(currentType===PAYMENT_TYPE_BALANCE?"ic_checkbox_active.png":"ic_checkbox_normal.png")' /> <image :src='assetsUrl(currentType===PAYMENT_TYPE_BALANCE?"ic_checkbox_active.png":"ic_checkbox_normal.png")' />
</view> </view>
</view> </view>
@@ -23,11 +23,16 @@
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { useUserStore } from '@/store';
const popupRef = ref(); const popupRef = ref();
const PAYMENT_TYPE_WECHAT = 0; const PAYMENT_TYPE_WECHAT = ref(0);
const PAYMENT_TYPE_BALANCE = 1; const PAYMENT_TYPE_BALANCE = ref(1);
const currentType = ref(PAYMENT_TYPE_WECHAT); const currentType = ref(PAYMENT_TYPE_WECHAT.value);
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const emits = defineEmits(['change']); const emits = defineEmits(['change']);
const show = () => { const show = () => {
@@ -42,10 +47,10 @@ const doPayment = (paymentType: number) => {
emits('change', paymentType); emits('change', paymentType);
currentType.value = paymentType; currentType.value = paymentType;
switch (paymentType) { switch (paymentType) {
case PAYMENT_TYPE_WECHAT: case PAYMENT_TYPE_WECHAT.value:
console.log('PAYMENT_TYPE_WECHAT'); console.log('PAYMENT_TYPE_WECHAT');
break; break;
case PAYMENT_TYPE_BALANCE: case PAYMENT_TYPE_BALANCE.value:
console.log('PAYMENT_TYPE_BALANCE'); console.log('PAYMENT_TYPE_BALANCE');
break; break;
} }

View File

@@ -2,35 +2,41 @@
<uni-popup ref='popupRef' type='bottom' :mask-click='false' @touchmove.stop.prevent=''> <uni-popup ref='popupRef' type='bottom' :mask-click='false' @touchmove.stop.prevent=''>
<view class='content'> <view class='content'>
<view class='c-flex-row' style='align-items: flex-start'> <view class='c-flex-row' style='align-items: flex-start'>
<image class='goods-image' :src='assetsUrl("test_bg.png")' /> <image class='goods-image' :src='goodsDetailBean?.images||defaultImage' />
<view class='c-flex-column' style='flex: 1'> <view class='c-flex-column' style='flex: 1;display: inline-grid'>
<text class='goods-name'>女童夏装套装洋气装短袖阔</text> <text class='goods-name'>{{ goodsDetailBean?.name || '未知' }}</text>
<text class='goods-price'>29.90</text> <text class='goods-price'>
{{ flashPrice > 0 ? `${flashPrice}` : `${goodsDetailBean?.consumePrice || 0}` || 0
}}
</text>
</view> </view>
<image class='close-image' :src='assetsUrl("ic_close.png")' @click.stop='close' /> <image class='close-image' :src='assetsUrl("ic_close.png")' @click.stop='close' />
</view> </view>
<view class='sku-view c-flex-column'> <view class='sku-view c-flex-column'>
<view class='sku-title'>颜色</view> <view class='sku-title'>颜色</view>
<view class='sku-color-list c-flex-row'> <view class='sku-color-list c-flex-row'>
<view class='sku-item' :class='{"sku-item-active":currentColorIndex==index,"sku-item-disabled":index==3}' <view class='sku-item'
:class='{"sku-item-active":currentColorIndex==index}'
v-for='(item, index) in skuColorList' :key='index' v-for='(item, index) in skuColorList' :key='index'
@click='colorChange(index)'> @click='colorChange(index)'>
<text>{{ item }}</text> <text>{{ item||'均色' }}</text>
</view> </view>
</view> </view>
<view class='sku-title' style='margin-top: 43rpx'>尺码</view> <view class='sku-title' style='margin-top: 43rpx'>尺码</view>
<view class='sku-color-list c-flex-row'> <view class='sku-color-list c-flex-row'>
<view class='sku-item' :class='{"sku-item-active":currentSizeIndex==index,"sku-item-disabled":index==3}' <view class='sku-item'
v-if='(skuSizeList?.length||0)>0'
:class='{"sku-item-active":currentSizeIndex==index,"sku-item-disabled":item.existingNumber<=0}'
v-for='(item, index) in skuSizeList' :key='index' v-for='(item, index) in skuSizeList' :key='index'
@click='sizeChange(index)'> @click='sizeChange(index)'>
<text>{{ item }}</text> <text>{{ item.sizeName||'均码' }}</text>
</view> </view>
<text v-else style='color: #999999;font-size: 30rpx'>暂无库存</text>
</view> </view>
<view class='c-flex-row' style='margin-top: 52rpx'> <view class='c-flex-row' style='margin-top: 52rpx'>
<text class='sku-title'>购买数量</text> <text class='sku-title'>购买数量</text>
<view class='count-change-view c-flex-row'> <view class='count-change-view c-flex-row'>
<view class='count-image' @click.stop='countChange(false)'> <view class='count-image' @click.stop='countChange(false)'>
<image :src='assetsUrl("ic_reduce.png")' /> <image :src='assetsUrl("ic_reduce.png")' />
@@ -41,49 +47,122 @@
</view> </view>
</view> </view>
</view> </view>
<button class='primary-button' @click='confirm'>确定</button> <button class='primary-button' :plain='currentSizeIndex<0' :disabled='currentSizeIndex<0' @click='confirm'>
确定
</button>
</view> </view>
</view> </view>
</uni-popup> </uni-popup>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { PropType, ref } from 'vue';
import { assetsUrl, defaultImage } from '@/utils/assets';
import { showToast } from '@/utils';
import { getGoodsDetail } from '@/api/goods';
import { GoodsBean, StockBean } from '@/api/goods/types';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const props = defineProps({
flashPrice: {
type: Number,
default: -1
},
exists: Object as PropType<StockBean>
});
const popupRef = ref(); const popupRef = ref();
const skuColorList = ref(['黑色', '白色']); const goodsDetailBean = ref<GoodsBean>();
const skuColorList = ref<string[]>([]);
const currentColorIndex = ref(0); const currentColorIndex = ref(0);
const skuSizeList = ref(['120cm', '130cm', '140cm', '150cm', '160cm', '170cm']); const currentSizeIndex = ref(-1);
const currentSizeIndex = ref(0);
const goodsCount = ref(1); const goodsCount = ref(1);
let callback: Function;
const show = () => { const show = async (goodsId: string, fn: Function) => {
callback = fn;
goodsDetailBean.value = await getGoodsDetail(goodsId);
goodsDetailBean.value.price = userStore.getRealGoodsPrice(goodsDetailBean.value?.price,goodsDetailBean.value?.priceExt)
goodsDetailBean.value.consumePrice = Number((goodsDetailBean.value?.price * userInfo?.value.userDiscount).toFixed(2));
if((goodsDetailBean.value?.stocks?.length || 0) <= 0) {
showToast('暂无库存');
return;
}
goodsDetailBean.value?.stocks?.map((res: { colorName: string }) => res.colorName).forEach((colorName: string) => {
if(!skuColorList.value.includes(colorName)) {
skuColorList.value.push(colorName);
}
});
setTimeout(() => {
popupRef.value.open(); popupRef.value.open();
}, 200);
if(props.exists) {
const colorIndex = skuColorList.value?.findIndex(res => res === props.exists?.colorName);
const sizeIndex = skuSizeList.value?.findIndex(res => res.sizeId === props.exists?.sizeId);
colorChange(colorIndex || 0);
sizeChange(sizeIndex || 0);
goodsCount.value = props.exists?.count;
}
if(skuSizeList) {
// skuSizeList.value![0].existingNumber = 2;
// skuSizeList.value![2].existingNumber = 2;
}
}; };
const close = () => { const skuSizeList = computed(() => {
popupRef.value.close(); const currentColorName = skuColorList.value[currentColorIndex.value];
}; let sizeList = goodsDetailBean.value?.stocks?.filter((res: { colorName: string }) => {
return res.colorName === currentColorName;
});
currentSizeIndex.value = sizeList?.findIndex(res => res.existingNumber > 0) || 0;
return sizeList;
});
const colorChange = (index: number) => { const colorChange = (index: number) => {
currentColorIndex.value = index; currentColorIndex.value = index;
}; };
const sizeChange = (index: number) => { const sizeChange = (index: number) => {
if((skuSizeList.value![index].existingNumber || 0) <= 0) {
showToast('库存不足');
} else {
currentSizeIndex.value = index; currentSizeIndex.value = index;
}
}; };
const countChange = (isPlus: boolean) => { const countChange = (isPlus: boolean) => {
if(isPlus) { if(isPlus) {
goodsCount.value++; goodsCount.value += 1;
} else { } else {
if(goodsCount.value > 0) { if(goodsCount.value > 1) {
goodsCount.value--; goodsCount.value -= 1;
} }
} }
}; };
const confirm = () => { const confirm = () => {
if(callback instanceof Function) {
callback({
...goodsDetailBean.value,
checkedStock: {
...skuSizeList.value![currentSizeIndex.value],
count: goodsCount.value
}
});
}
popupRef.value.close();
};
const close = () => {
popupRef.value.close(); popupRef.value.close();
}; };
@@ -112,6 +191,7 @@ defineExpose({
color: #333333; color: #333333;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden;
} }
.goods-price { .goods-price {
@@ -208,7 +288,8 @@ defineExpose({
.primary-button { .primary-button {
width: 100%; width: 100%;
margin-top: 100rpx; margin-top: 50rpx;
border: none;
} }
} }
} }

View File

@@ -14,12 +14,17 @@
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
defineProps({ const props = defineProps({
titles: { titles: {
type: Object as PropType<string[]>, type: Object as PropType<string[]>,
default: '' default: ''
}, },
index: {
type: Number,
default: 0
},
fixed: { fixed: {
type: Boolean, type: Boolean,
default: true default: true
@@ -42,6 +47,10 @@ defineProps({
const emits = defineEmits(['change']); const emits = defineEmits(['change']);
const currentIndex = ref<number>(0); const currentIndex = ref<number>(0);
watch(() => props.index, (newVal) => {
currentIndex.value = newVal;
});
const onChange = (index: number) => { const onChange = (index: number) => {
currentIndex.value = index; currentIndex.value = index;
emits('change', index); emits('change', index);

View File

@@ -1,17 +1,15 @@
{ {
"name": "SURE-MP", "name": "SUKE-MP",
"appid": "", "appid": "",
"description": "", "description": "",
"versionName": "1.0.0", "versionName": "3.0.4",
"versionCode": "100", "versionCode": "304",
"transformPx": false, "transformPx": false,
"app-plus": "app-plus": {
{
"usingComponents": true, "usingComponents": true,
"nvueStyleCompiler": "uni-app", "nvueStyleCompiler": "uni-app",
"compilerVersion": 3, "compilerVersion": 3,
"splashscreen": "splashscreen": {
{
"alwaysShowBeforeRender": true, "alwaysShowBeforeRender": true,
"waiting": true, "waiting": true,
"autoclose": true, "autoclose": true,
@@ -19,25 +17,25 @@
}, },
"modules": {} "modules": {}
}, },
"mp-weixin": "mp-weixin": {
{
"appid": "wx92e663dc11d0c0a8", "appid": "wx92e663dc11d0c0a8",
// "appid": "wx67a750d0ceed4d88", "setting": {
"setting":
{
"urlCheck": false "urlCheck": false
}, },
"usingComponents": true "usingComponents": true,
"plugins": {
"sqb-pay": {
"version": "1.3.1",
"provider": "wx55540b288c5ce319"
}
}
}, },
"uniStatistics": "uniStatistics": {
{
"enable": false "enable": false
}, },
"vueVersion": "3", "vueVersion": "3",
"h5": "h5": {
{ "router": {
"router":
{
"mode": "hash", "mode": "hash",
"base": "/uniapp-vue3-template/" "base": "/uniapp-vue3-template/"
} }

View File

@@ -4,7 +4,8 @@
"^u--(.*)": "uview-plus/components/u-$1/u-$1.vue", "^u--(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^up-(.*)": "uview-plus/components/u-$1/u-$1.vue", "^up-(.*)": "uview-plus/components/u-$1/u-$1.vue",
"^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue", "^u-([^-].*)": "uview-plus/components/u-$1/u-$1.vue",
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue" "^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue",
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue"
} }
}, },
"pages": [ "pages": [
@@ -32,6 +33,18 @@
"style": { "style": {
"navigationBarTitleText": "我的" "navigationBarTitleText": "我的"
} }
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "VIP顾客中心"
}
},
{
"path": "pages/coupons/ticketsBuy/ticketsBuy",
"style": {
"navigationBarTitleText": "团购详情"
}
} }
], ],
"subPackages": [ "subPackages": [
@@ -44,6 +57,12 @@
}, },
{ {
"path": "register/index", "path": "register/index",
"style": {
"navigationBarTitleText": "用户注册"
}
},
{
"path": "register/reward",
"style": { "style": {
"navigationBarTitleText": "注册有礼" "navigationBarTitleText": "注册有礼"
} }
@@ -64,9 +83,33 @@
"navigationBarTitleText": "商品团购券" "navigationBarTitleText": "商品团购券"
} }
}, },
{
"path": "groupbuy/order-confirm",
"style": {
"navigationBarTitleText": "确认订单",
"usingComponents": {
"sqb-pay": "plugin://sqb-pay/sqb-pay"
}
}
},
{
"path": "groupbuy/order-coupon-confirm",
"style": {
"navigationBarTitleText": "确认订单",
"usingComponents": {
"sqb-pay": "plugin://sqb-pay/sqb-pay"
}
}
},
{ {
"path": "webview/index", "path": "webview/index",
"navigationBarTitleText": "网页" "navigationBarTitleText": "网页"
},
{
"path": "payresult/index",
"style": {
"navigationBarTitleText": "支付结果"
}
} }
] ]
}, },
@@ -93,7 +136,10 @@
{ {
"path": "order/order-confirm", "path": "order/order-confirm",
"style": { "style": {
"navigationBarTitleText": "确认订单" "navigationBarTitleText": "确认订单",
"usingComponents": {
"sqb-pay": "plugin://sqb-pay/sqb-pay"
}
} }
}, },
{ {
@@ -128,7 +174,10 @@
{ {
"path": "recharge/index", "path": "recharge/index",
"style": { "style": {
"navigationBarTitleText": "会员充值" "navigationBarTitleText": "会员充值",
"usingComponents": {
"sqb-pay": "plugin://sqb-pay/sqb-pay"
}
} }
}, },
{ {
@@ -146,7 +195,10 @@
{ {
"path": "order/detail", "path": "order/detail",
"style": { "style": {
"navigationBarTitleText": "订单详情" "navigationBarTitleText": "订单详情",
"usingComponents": {
"sqb-pay": "plugin://sqb-pay/sqb-pay"
}
} }
}, },
{ {
@@ -166,6 +218,12 @@
"style": { "style": {
"navigationBarTitleText": "新增收货地址" "navigationBarTitleText": "新增收货地址"
} }
},
{
"path": "contact/index",
"style": {
"navigationBarTitleText": "联系商家"
}
} }
] ]
} }

View File

@@ -0,0 +1,120 @@
<template>
<view class='coupon-content'>
<image :src='assetsUrl("bg_coupon.png")' />
<view class='left-content accent-text-color' :class='{"left-content-disabled": item.status==1}'>
<text>{{ item.reduce }}</text>
<text>{{ item.threshold }}元可用</text>
</view>
<view class='right-content accent-text-color' :class='{"right-content-disabled": item.status==1}'>
<text>{{ item?.name }}</text>
<text>有效期至{{ dayjs(item.startTime).format('YYYY-MM-DD') }}</text>
</view>
</view>
</template>
<script lang='ts' setup>
import { assetsUrl } from '@/utils/assets';
import dayjs from 'dayjs';
defineProps({
item: Object
});
</script>
<style lang='scss' scoped>
.coupon-content {
display: flex;
flex-direction: row;
height: 200rpx;
position: relative;
margin: 10rpx 30rpx 20rpx 30rpx;
image {
width: 100%;
height: 100%;
position: absolute;
}
.left-content {
display: flex;
flex-direction: column;
position: relative;
align-items: center;
justify-content: center;
padding-left: 50rpx;
text:nth-of-type(1) {
font-size: 55rpx;
font-weight: bold;
}
text:nth-of-type(1):after {
content: '元';
font-size: 24rpx;
}
text:nth-of-type(2) {
font-size: 26rpx;
font-weight: 400;
}
}
.left-content-disabled {
@extend .left-content;
color: #C2C2C2;
}
.right-content {
display: flex;
flex-direction: column;
position: relative;
flex: 1;
justify-content: center;
margin-left: 60rpx;
text:nth-of-type(1) {
font-size: 30rpx;
}
text:nth-of-type(2) {
font-size: 26rpx;
margin-top: 20rpx;
color: #333333;
}
.btn-text {
display: flex;
width: 146rpx;
height: 55rpx;
align-items: center;
justify-content: center;
border-radius: 28rpx;
border: #F32B2B solid 2rpx;
font-size: 28rpx;
font-weight: bold;
color: #F32B2B;
position: absolute;
right: 20rpx;
}
.btn-text-disabled {
@extend .btn-text;
color: #C2C2C2;
border: none;
}
}
.right-content-disabled {
@extend .right-content;
color: #C2C2C2;
text:nth-of-type(2) {
color: #C2C2C2;
}
}
}
</style>

View File

@@ -2,13 +2,13 @@
<view class='content'> <view class='content'>
<view class='swiper-container'> <view class='swiper-container'>
<swiper class='swiper' :interval='1500' :duration='1000' @change='swiperChange'> <swiper class='swiper' :interval='1500' :duration='1000' @change='swiperChange'>
<swiper-item v-for='(item,index) in bannerList' :key='index'> <swiper-item v-for='(item,index) in bannerList||[defaultImage]' :key='index'>
<image :src='assetsUrl("test_bg.png")' /> <image :src='item' mode='aspectFill' />
</swiper-item> </swiper-item>
</swiper> </swiper>
<view class='indicator'> <view class='indicator'>
<text>{{ swiperIndex + 1 }}</text> <text>{{ swiperIndex + 1 }}</text>
<text>/{{ bannerList.length }}</text> <text>/{{ bannerList?.length || 0 }}</text>
</view> </view>
</view> </view>
<view class='countdown c-flex-row'> <view class='countdown c-flex-row'>
@@ -16,29 +16,34 @@
超级\n秒杀 超级\n秒杀
</view> </view>
<view class='price c-flex-column' style='flex: 1'> <view class='price c-flex-column' style='flex: 1'>
<text>52</text> <text>{{ groupBuyBean?.payPrice || 0 }}</text>
<text>65</text> <text v-if='(groupBuyBean?.price||0)>0&&!isCouponGoodsDetail'>¥{{ groupBuyBean?.price || 0 }}</text>
</view> </view>
<view class='countdown-time c-flex-column'> <view v-if='isPending()' class='countdown-time c-flex-column'>
<view class='c-flex-row'>180 <view class='c-flex-row'>{{ countdownTime?.days || 0 }}天
<text class='time'>23</text> <text class='time'>{{ countdownTime?.hours || '00' }}</text>
: :
<text class='time'>23</text> <text class='time'>{{ countdownTime?.minutes || '00' }}</text>
: :
<text class='time'>23</text> <text class='time'>{{ countdownTime?.seconds || '00' }}</text>
</view> </view>
<text>距离活动结束仅剩</text> <text>距离活动结束仅剩</text>
</view> </view>
<view v-else>
<text style='font-size: 35rpx;color: #FFFFFF'>
{{ isNotStated() ? '未开始' : '已结束' }}
</text>
</view>
</view> </view>
<view class='goods-info-view c-flex-column'> <view class='goods-info-view c-flex-column'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='goods-price accent-text-color'>39.89</text> <text class='goods-price accent-text-color'>{{ groupBuyBean?.payPrice || 0 }}</text>
<text>销量2653</text> <text>销量{{ groupBuyBean?.totalNum || 0 }}</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text>女童夏装套装洋气装短袖阔腿裤子夏装夏装套装</text> <text style='flex: 1'>{{ groupBuyBean?.name || '未知' }}</text>
<view class='share-button c-flex-column'> <view class='share-button c-flex-column'>
<image :src='assetsUrl("ic_share.png")'></image> <image :src='assetsUrl("ic_share.png")'></image>
<button class='btn' plain open-type='share'>分享</button> <button class='btn' plain open-type='share'>分享</button>
@@ -46,103 +51,327 @@
</view> </view>
</view> </view>
<view class='goods-sku-view c-flex-column'> <view class='goods-sku-view c-flex-column' v-if='groupBuyBean?.goods'>
<view class='c-flex-row' @click.stop='showSkuDialog'> <view class='c-flex-row' @click.stop='showSkuDialog'>
<text>选择</text> <text>选择</text>
<text>规格 颜色/尺码</text> <text>规格 颜色/尺码</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' /> <image :src='assetsUrl("ic_arrow_right_gray.png")' />
<text>共1种颜色可选</text> <text>共{{ getStockColorCount }}种颜色可选</text>
</view>
<view class='divider' style='width:auto;height: 0.5rpx;margin-left: 100rpx' />
<view class='c-flex-row' @click.stop='showSkuDialog'>
<text>参数</text>
<text>品牌 风格 季节 款号</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' />
</view> </view>
</view> </view>
<view class='recommend-view c-flex-column'> <view class='recommend-view c-flex-column' v-if='(recommendList?.length||0)>0'>
<text>浏览此商品的客户还浏览了</text> <text>浏览此商品的客户还浏览了</text>
<scroll-view scroll-x> <scroll-view scroll-x>
<view style='display: inline-block' v-for='(item, index) in [1, 2, 3, 4]' <view style='display: inline-block' v-for='(item, index) in recommendList'
:key='index'> :key='index'>
<view class='recommend-item c-flex-column'> <view class='recommend-item c-flex-column'>
<image :src='assetsUrl("test_bg.png")' /> <image :src='item.images||defaultImage' />
<text>女童夏装套装</text> <text>{{ item.goodsName || '未知' }}</text>
<text class='goods-price'>22</text> <text class='goods-price'>{{ item.price }}</text>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<view class='goods-detail c-flex-column'> <!-- 商品详情图片 -->
<text>商品详情</text> <view class='card-view image-container' v-if='groupBuyBean?.content'>
<image :src='assetsUrl("test_bg.png")' mode='aspectFill' /> <text class='card-view-title'>商品详情</text>
<image :src='assetsUrl("test_bg.png")' mode='aspectFill' /> <text v-if='isCouponGoodsDetail' style='padding: 20rpx 35rpx;font-size: 30rpx'>
<image :src='assetsUrl("test_bg.png")' mode='aspectFill' /> {{ groupBuyBean?.content }}
</text>
<image v-else
v-for='(item,index) in JSON.parse(groupBuyBean?.content).filter((a: any) => a.type === 2).map((b: any) => b.images)'
:src='item' mode='aspectFill' :key='index' />
</view>
<!-- 商品详情 -->
<view class='card-view goods-container' v-if='groupBuyBean?.goods'>
<view class='c-flex-row'>
<image :src='groupBuyBean?.goods?.images' />
<view class='c-flex-column'>
<text class='goods-name'>{{ groupBuyBean?.goods?.name }}</text>
<text style='color: #a6a6a6'>精选</text>
<view class='tag-view c-flex-row'>
<text>团长推荐</text>
<text>今日必买</text>
</view>
<text style='color: #a6a6a6'>不参与会员折扣</text>
<text style='color: #F32B2B'>¥{{ groupBuyBean?.goods?.payPrice }}</text>
<text class='bought_count'>已团{{ groupBuyBean?.sendNum }}</text>
</view>
</view>
</view>
<!-- 商品详情优惠券 -->
<view class='card-view coupon-container' v-if='groupBuyBean?.couponsList&&groupBuyBean?.couponsList?.length>0'>
<text class='card-view-title'>赠送优惠券</text>
<view class='c-flex-column'>
<coupon-item v-for='(item,index) in groupBuyBean?.couponsList' :key='index' :item='item' />
</view>
</view>
<!-- 商品详情跟团记录 -->
<view class='card-view record-container' v-if='recordList&&(recordList?.length||0)>0'>
<text class='card-view-title'>跟团记录</text>
<u-list :list='recordList' :border='false' @scrolltolower='loadMore'>
<u-list-item v-for='(item,index) in recordList' :key='index'>
<view class='item-view c-flex-row'>
<image :src='item.memberImage' />
<view class='c-flex-column' style='flex: 1'>
<text>{{ item.goodsCode }}</text>
<text>{{ item.createTime }}</text>
</view>
<text>+{{ item.goodsNum }}</text>
</view>
</u-list-item>
<u-loadmore status='loading' />
</u-list>
</view> </view>
<view class='bottom-view c-flex-row'> <view class='bottom-view c-flex-row'>
<view class='small-button-view' @click.stop='goBack'> <button v-if='isPending()' class='place-order-button' @click.stop='placeOrder'>跟团购买</button>
<view class='small-button-item'> <button v-else class='place-order-button-disable' :plain='true' :disabled='true'>
<image :src='assetsUrl("ic_goods_store.png")' /> {{ isNotStated() ? '即将开始' : '已结束' }}
<text>商家</text> </button>
</view>
<view class='small-button-item' @click.stop='goPath("/pages/mine/subs/order/index")'>
<image :src='assetsUrl("ic_goods_order.png")' />
<text>订单</text>
</view>
<view class='small-button-item'>
<view class='shoppingcart-count' @click.stop='goPath("/pages/mall/subs/shoppingcart/index")'>
<image :src='assetsUrl("ic_goods_shoppingcart.png")' />
<text>12</text>
</view>
<text>购物车</text>
</view> </view>
</view> </view>
<sku-dialog ref='skuDialogRef' :flash-price='Number(groupBuyBean?.payPrice) || 0' />
<view class='primary-button-view c-flex-row'>
<view class='add-shoppingcart-button' @click.stop='addShoppingCart'>加入购物车</view>
<view class='place-order-button' @click.stop='placeOrder'>立即下单</view>
</view>
</view>
</view>
<sku-dialog ref='skuDialogRef' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import { goPath } from '@/utils'; import dayjs from 'dayjs';
import { formatTimeWithZeroPad, getCompanyId, getOpenId, goLogin, goPath, isLogin, showToast } from '@/utils';
import SkuDialog from '@/components/sku-dialog.vue'; import SkuDialog from '@/components/sku-dialog.vue';
import CouponItem from './components/coupon-item.vue';
import {
getGroupBuyDetail,
getGroupBuyRecordList,
getShareGoodsCouponDetail,
getShareGoodsDetail,
preOrderV2
} from '@/api/groupbuy';
import { getGoodsList } from '@/api/goods';
import { GoodsBean } from '@/api/goods/types';
import { useUserStore } from '@/store';
import { GroupBuyBean, RecordBean } from '@/api/groupbuy/types';
import { getCompanyList } from '@/api/company';
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const skuDialogRef = ref(); const skuDialogRef = ref();
const groupBuyBean = ref<GroupBuyBean>();
const recommendList = ref<GoodsBean[]>();
const isCouponGoodsDetail = ref(false);
const bannerList = ref([1, 2, 3, 4]); const bannerList = ref([]);
const swiperIndex = ref(0); const swiperIndex = ref(0);
let interval: number;
const countdownTime = ref<{
days: string,
hours: string,
minutes: string,
seconds: string
}>();
const recordList = ref<RecordBean[]>([]);
const currentPageNum = ref(1);
const shareGoodsId = ref();
const shareCouponId = ref();
onLoad(async (e: any) => {
if(isLogin()) {
await uni.showLoading();
if(e.id != undefined) {
groupBuyBean.value = await getGroupBuyDetail(e.id);
}
if(e.shareId != undefined) {
shareGoodsId.value = e.shareId;
const { group, share } = await getShareGoodsDetail(e.shareId);
groupBuyBean.value = group;
}
if(e.couponId != undefined) {
shareCouponId.value = e.couponId;
isCouponGoodsDetail.value = true;
groupBuyBean.value = await getShareGoodsCouponDetail(e.couponId);
groupBuyBean.value.price = groupBuyBean.value?.offsetPrice;
bannerList.value = groupBuyBean.value?.image?.split(',');
}
const maOpenId = getOpenId() || userInfo.value?.maOpenId;
const data = await getCompanyList(maOpenId);
const companyList = data.map((item: any) => item.company);
const userList = data.map((item: any) => item.user);
let index = companyList.findIndex((res: {
id: string
}) => res.id === (getApp()?.globalData?.shareCompanyId || getCompanyId()));
if(index < 0) {
uni.showModal({
title: '提示',
content: '当前店铺还未注册请先注册',
showCancel: true,
success: (res) => {
if(res.confirm) {
goPath('/pages/common/register/index');
} else {
goPath('/pages/home/index');
}
}
});
} else {
await userStore.setUserInfo(userList[index]);
}
// setCompanyId('1512403904150138881');
// groupBuyBean.value = await getGroupBuyDetail('1740922051118063618');
if(groupBuyBean.value) {
countdown();
if(e.couponId === undefined) {
groupBuyBean.value.goods.price = groupBuyBean.value?.price || 0;
bannerList.value = JSON.parse(groupBuyBean.value?.content)?.filter((item: any) => item.type === 2).map((item: any) => item.images);
await fetchRecommendList();
await fetchBuyRecordList();
}
} else {
showToast('查询失败', {
complete: () => {
const pages = getCurrentPages();
if(pages.map(item => item?.route?.includes('pages/home/index'))) {
uni.navigateBack();
} else {
uni.reLaunch({
url: '/pages/home/index'
});
}
}
});
}
uni.hideLoading();
} else {
goLogin(true);
}
});
onUnload(() => {
if(interval) {
clearInterval(interval);
}
});
onShareAppMessage((e) => {
let path = `/pages/common/groupbuy/detail?id=${groupBuyBean.value?.id}&companyId=${getApp().globalData?.companyId}&storeId=${getApp().globalData?.storeId}`;
if(shareGoodsId.value) {
path = `/pages/common/groupbuy/detail?shareId=${shareGoodsId.value}&companyId=${getApp().globalData?.companyId}&storeId=${getApp().globalData?.storeId}`;
} else if(shareCouponId.value) {
path = `/pages/common/groupbuy/detail?couponId=${shareCouponId.value}&companyId=${getApp().globalData?.companyId}&storeId=${getApp().globalData?.storeId}`;
}
return {
title: groupBuyBean.value?.goods?.name || 'VIP顾客中心',
path: path
};
});
const isNotStated = () => {
return dayjs(groupBuyBean.value?.startDate).isAfter(Date.now());
};
const isPending = () => {
return dayjs(groupBuyBean.value?.startDate).isBefore(Date.now()) && dayjs(groupBuyBean.value?.endDate).isAfter(Date.now());
};
const isEnded = () => {
return dayjs(groupBuyBean.value?.endDate).isBefore(Date.now());
};
const getStockColorCount = computed(() => {
const list = Array.from(new Set(groupBuyBean.value?.goods?.stocks?.map(item => item.colorName)))
.map(colorName => groupBuyBean.value?.goods?.stocks?.find(item => item.colorName === colorName)!);
return (list?.length) || 0;
});
const fetchRecommendList = async () => {
const { rows } = await getGoodsList({
page: {
page: 1,
pageSize: 20,
bean: {
keyword: '',
typeIds: []
}
}
});
recommendList.value = rows;
};
const fetchBuyRecordList = async (refresh: boolean = true) => {
if(!refresh) {
currentPageNum.value += 1;
}
const { list } = await getGroupBuyRecordList(groupBuyBean?.value?.id || '', currentPageNum.value, 20);
recordList.value = recordList.value.concat(list);
};
const loadMore = () => {
fetchBuyRecordList(false);
};
const swiperChange = (e: any) => { const swiperChange = (e: any) => {
swiperIndex.value = e.detail.current; swiperIndex.value = e.detail.current;
}; };
const goBack = () => { const countdown = () => {
uni.navigateBack(); interval = setInterval(() => {
if(groupBuyBean.value?.endDate) {
let now = new Date();
let end = dayjs(groupBuyBean.value?.endDate).toDate().getTime();
let remaining = Math.floor((end - now.getTime()) / 1000);
if(remaining > 0) {
countdownTime.value = {
days: formatTimeWithZeroPad(Math.floor(remaining / 60 / 60 / 24)),
hours: formatTimeWithZeroPad(Math.floor(remaining / 60 / 60 % 24)),
minutes: formatTimeWithZeroPad(Math.floor(remaining / 60 % 60)),
seconds: formatTimeWithZeroPad(Math.floor(remaining % 60))
};
} else {
clearInterval(interval);
}
}
}, 1000);
}; };
const showSkuDialog = () => { const showSkuDialog = (fn: Function) => {
skuDialogRef.value.show(); skuDialogRef.value.show(groupBuyBean?.value?.goods?.goodsId, fn);
}; };
const addShoppingCart = () => { const placeOrder = async () => {
uni.showToast({
title: '加入购物车成功', async function createGoodsOrder(bean: GoodsBean) {
icon: 'none' await uni.showLoading();
const params = {
'colorId': bean.checkedStock.colorId,
'sizeId': bean.checkedStock.sizeId,
'goodsId': groupBuyBean?.value?.goods.goodsId,
'groupId': groupBuyBean?.value?.id,
'memberId': userInfo.value.id,
'shareId': '123456'
};
const result = await preOrderV2(params);
uni.hideLoading();
goPath(`/pages/common/groupbuy/order-confirm?orderBean=${encodeURIComponent(JSON.stringify(result))}`);
}
if(isCouponGoodsDetail.value) {
goPath(`/pages/common/groupbuy/order-coupon-confirm?bean=${encodeURIComponent(JSON.stringify(groupBuyBean.value))}`);
} else {
showSkuDialog((e: GoodsBean) => {
createGoodsOrder(e);
}); });
}; }
const placeOrder = () => {
goPath('/pages/mall/subs/order/order-confirm');
}; };
</script> </script>
@@ -191,8 +420,7 @@ const placeOrder = () => {
height: 130rpx; height: 130rpx;
position: relative; position: relative;
padding: 15rpx 30rpx; padding: 15rpx 30rpx;
background: #F32B2B; background-image: url('https://img.lakeapp.cn/wx/images/bg_groupbuy_countdown.png');
background-image: url("../../../static/images/bg_groupbuy_countdown.png");
background-size: 100% 100%; background-size: 100% 100%;
.super-second-kill { .super-second-kill {
@@ -249,13 +477,14 @@ const placeOrder = () => {
margin-left: 12rpx; margin-left: 12rpx;
margin-right: 12rpx; margin-right: 12rpx;
background: #FFFFFF; background: #FFFFFF;
border-radius: 7rpx 7rpx 7rpx 7rpx; border-radius: 7rpx;
color: #F32B2B; color: #F32B2B;
} }
.time:nth-of-type(1) { .time:nth-of-type(1) {
margin-left: 18rpx; margin-left: 18rpx;
} }
.time:nth-of-type(3) { .time:nth-of-type(3) {
margin-right: 0; margin-right: 0;
} }
@@ -323,13 +552,13 @@ const placeOrder = () => {
.goods-sku-view { .goods-sku-view {
background: #FFFFFF; background: #FFFFFF;
padding: 0 30rpx; padding: 20rpx 30rpx;
margin-top: 20rpx; margin-top: 20rpx;
view:nth-of-type(n+1) { view:nth-of-type(n+1) {
position: relative; position: relative;
align-items: center; align-items: center;
height: 105rpx; height: 80rpx;
text:nth-of-type(1) { text:nth-of-type(1) {
width: 100rpx; width: 100rpx;
@@ -355,14 +584,14 @@ const placeOrder = () => {
font-size: 24rpx; font-size: 24rpx;
color: #999999; color: #999999;
position: absolute; position: absolute;
top: 80rpx; top: 50rpx;
left: 100rpx; left: 100rpx;
} }
} }
view:nth-of-type(1) { view:nth-of-type(1) {
align-items: flex-start; align-items: flex-start;
padding-top: 40rpx; //padding-top: 20rpx;
} }
} }
@@ -415,101 +644,138 @@ const placeOrder = () => {
.bottom-view { .bottom-view {
background: #FFFFFF; background: #FFFFFF;
padding: 20rpx 30rpx 78rpx 33rpx; padding: 20rpx 30rpx 78rpx 30rpx;
position: fixed; position: fixed;
left: 0; left: 0;
right: 0; right: 0;
bottom: 0; bottom: 0;
.small-button-view {
display: flex;
flex-direction: row;
flex: 1;
justify-content: space-between;
margin-right: 35rpx;
.small-button-item {
display: flex;
flex-direction: column;
align-items: center;
image {
width: 34rpx;
height: 34rpx;
margin-bottom: 10rpx;
}
text {
font-weight: 400;
font-size: 20rpx;
color: #333333;
}
.shoppingcart-count {
display: flex;
position: relative;
text {
display: flex;
box-sizing: border-box;
align-items: center;
justify-content: center;
position: absolute;
top: -15rpx;
right: -20rpx;
min-width: 35rpx;
min-height: 30rpx;
background: #F32B2B;
color: #FFFFFF;
padding: 2rpx;
border-radius: 50%;
border: 2rpx solid #FFFFFF;
}
}
}
}
.primary-button-view {
font-weight: bold;
font-size: 30rpx;
.add-shoppingcart-button {
display: flex;
width: 224rpx;
height: 80rpx;
align-items: center;
justify-content: center;
background: #FFE2E2;
color: #F32B2B;
border-radius: 40rpx 0 0 40rpx;
}
.place-order-button { .place-order-button {
display: flex; display: flex;
width: 198rpx; flex: 1;
font-weight: bold;
width: 100%;
font-size: 30rpx;
height: 80rpx; height: 80rpx;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background: #F32B2B; background: #F32B2B;
color: #FFFFFF; color: #FFFFFF;
border-radius: 0 40rpx 40rpx 0; border-radius: 40rpx;
border: none;
} }
.place-order-button-disable {
@extend .place-order-button;
background: #b9b5b5;
color: #FFFFFF;
} }
} }
.goods-detail { .card-view {
display: flex;
flex-direction: column;
background: #FFFFFF; background: #FFFFFF;
margin-top: 20rpx; margin-top: 20rpx;
text { .card-view-title {
margin: 20rpx 30rpx; margin: 20rpx 30rpx;
font-weight: bold; font-weight: bold;
font-size: 30rpx; font-size: 30rpx;
color: #333333; color: #333333;
} }
}
.image-container {
image { image {
width: 100%; width: 100%;
} }
} }
.goods-container {
padding: 30rpx;
position: relative;
margin-left: 20rpx;
margin-right: 20rpx;
image {
width: 200rpx;
height: 200rpx;
margin-right: 15rpx;
border-radius: 8rpx;
}
.c-flex-column {
justify-content: space-between;
position: relative;
flex: 1;
text {
margin-top: 5rpx;
}
.goods-name {
font-size: 30rpx;
color: #333333;
}
.tag-view {
text {
background: #fff0ee;
margin-right: 10rpx;
padding: 5rpx 10rpx;
color: #fc6e51;
border-radius: 8rpx;
}
}
.bought_count {
position: absolute;
bottom: 0;
right: 0;
color: #fc6e51;
font-size: 20rpx;
}
}
}
.coupon-container {
margin-left: 20rpx;
margin-right: 20rpx;
}
.record-container {
margin-left: 20rpx;
margin-right: 20rpx;
.item-view {
margin: 10rpx 30rpx;
image {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.c-flex-column {
margin: 0 15rpx;
text:nth-of-type(1) {
font-size: 25rpx;
color: #333333;
}
text:nth-of-type(2) {
font-size: 25rpx;
color: #666666;
margin-top: 10rpx;
}
}
text:nth-of-type(1) {
font-size: 30rpx;
color: #F32B2B;
}
}
}
</style> </style>

View File

@@ -6,42 +6,74 @@
</view> </view>
<scroll-view class='scroll-view'> <scroll-view class='scroll-view'>
<view class='c-flex-column' v-for='(item, index) in 10' :key='index'> <!-- <u-list class='scroll-view' :border='false' @scrolltolower='loadMore'>-->
<view class='item c-flex-row' @click.stop='goPath("/pages/common/groupbuy/detail")'> <!-- <u-list-item v-for='(item,index) in groupbuyList' :key='index'>-->
<image class='goods-image' :src='assetsUrl("test_bg.png")' /> <view class='c-flex-column' v-for='(item,index) in groupbuyList' :key='index'>
<view class='item c-flex-row' @click.stop='goPath(`/pages/common/groupbuy/detail?id=${item.id}`)'>
<image class='goods-image' mode='aspectFill' :src='JSON.parse(item.content)[0].images||defaultImage' />
<view class='c-flex-column' style='flex: 1'> <view class='c-flex-column' style='flex: 1'>
<view class='goods-name'>女童夏装套装洋气装短袖阔腿裤短</view> <view class='goods-name'>{{ item.name }}</view>
<view class='middle-view c-flex-row'> <view class='middle-view c-flex-row'>
<text>原价56</text> <text>原价{{ item.price }}</text>
<view class='decline c-flex-row'> <view v-if='(item.price - item.payPrice)>0' class='decline c-flex-row'>
<image :src='assetsUrl("ic_decline.png")' /> <image :src='assetsUrl("ic_decline.png")' />
<text>直降15</text> <text>直降¥{{ (item.price - item.payPrice).toFixed(2) }}</text>
</view> </view>
<text>即将恢复</text> <text>{{ tabIndex == 0 ? '即将恢复' : '即将开始' }}</text>
</view> </view>
<view class='bottom-price c-flex-row' :class='{"bottom-price-disabled":tabIndex==1}'> <view class='bottom-price c-flex-row' :class='{"bottom-price-disabled":tabIndex==1}'>
<text>41</text> <text>{{ item.payPrice }}</text>
<text>团购</text> <text>团购</text>
</view> </view>
</view> </view>
</view> </view>
<view class='divider' style='margin: 0 30rpx' /> <view class='divider' style='margin: 0 30rpx' />
</view> </view>
<!-- </u-list-item>-->
<!-- </u-list>-->
<u-empty v-if='groupbuyList.length === 0' text='暂无团购活动' marginTop='100' />
</scroll-view> </scroll-view>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import { getGroupBuyList } from '@/api/groupbuy'; import { getGroupBuyList } from '@/api/groupbuy';
import { goPath } from '@/utils'; import { goPath } from '@/utils';
const tabIndex = ref(0); const tabIndex = ref(0);
const groupbuyList = ref<any[]>([]);
const currentPageNum = ref(1);
onLoad((e) => { onLoad((e) => {
getGroupBuyList(); fetchData();
}); });
watch(() => tabIndex.value, () => {
fetchData();
});
const fetchData = async (refresh: boolean = true) => {
await uni.showLoading();
if(!refresh) {
currentPageNum.value += 1;
}
const { list } = await getGroupBuyList({
pageNum: currentPageNum.value,
pageSize: 100,
obj: {
timeStatus: tabIndex.value + 1
}
});
groupbuyList.value = list;
uni.hideLoading();
};
const loadMore = () => {
fetchData(false);
};
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -82,6 +114,7 @@ onLoad((e) => {
.scroll-view { .scroll-view {
background: #FFFFFF; background: #FFFFFF;
height: 100vh;
.item { .item {
padding: 20rpx 30rpx 40rpx 30rpx; padding: 20rpx 30rpx 40rpx 30rpx;
@@ -140,7 +173,7 @@ onLoad((e) => {
height: 83rpx; height: 83rpx;
margin-top: 20rpx; margin-top: 20rpx;
align-self: flex-end; align-self: flex-end;
background-image: url("../../../static/images/bg_groupbuy_price.png"); background-image: url("https://img.lakeapp.cn/wx/images/bg_groupbuy_price.png");
background-size: 100% 100%; background-size: 100% 100%;
color: #FFFFFF; color: #FFFFFF;
justify-content: space-between; justify-content: space-between;
@@ -149,7 +182,7 @@ onLoad((e) => {
font-weight: bold; font-weight: bold;
font-size: 40rpx; font-size: 40rpx;
color: #FFFFFF; color: #FFFFFF;
margin-left: 40rpx; margin-left: 30rpx;
margin-bottom: 5rpx; margin-bottom: 5rpx;
} }
@@ -168,7 +201,7 @@ onLoad((e) => {
} }
.bottom-price-disabled { .bottom-price-disabled {
background-image: url("../../../static/images/bg_groupbuy_price_disabled.png"); background-image: url("https://img.lakeapp.cn/wx/images/bg_groupbuy_price_disabled.png");
} }
} }
} }

View File

@@ -0,0 +1,199 @@
<template>
<view class='content'>
<view class='card-view'>
<template class='c-flex-row' v-for='item in orderBean?.orderGoods' :key='item.id'>
<image class='goods-image' :src='item?.images||defaultImage' />
<view class='c-flex-column' style='flex: 1'>
<text class='goods-name'>{{ item?.goodsName || item?.goodsCode }}</text>
<text class='goods-sku'>{{ item?.stockStock?.colorName }} {{ item?.stockStock?.sizeName }}</text>
</view>
<view class='c-flex-column'>
<text class='goods-num'>x{{ item?.goodsNum }}</text>
<view class='c-flex-row'>
<text class='goods-price' style='margin-right: 10rpx;text-decoration: line-through'>¥{{ item?.originPrice }}
</text>
<text class='goods-price'>¥{{ item?.consumePrice }}</text>
</view>
</view>
</template>
</view>
<view class='card-view'>
<text style='flex: 1'>实付</text>
<text style='color: #F32B2B'>¥{{ orderBean?.totalPrice || 0 }}</text>
</view>
<view class='bottom-view c-flex-row'>
<sqb-pay @navigateTo='navigateTo'
:return_url='buildSqbParams.return_url'
:total_amount='buildSqbParams.total_amount'
:terminal_sn='buildSqbParams.terminal_sn'
:client_sn='buildSqbParams.client_sn'
:subject='buildSqbParams.subject'
:subject_img='buildSqbParams.subject_img '
:merchant_name='buildSqbParams.merchant_name'
:notify_url='buildSqbParams.notify_url'
:sign='buildSqbParams.sign'>
<button class='confirm-button' @click='payment'>支付</button>
</sqb-pay>
</view>
</view>
</template>
<script lang='ts' setup>
import { pay, progress } from '@/api/groupbuy';
import { OrderBean } from '@/api/order/types';
import { parseParameter, showToast, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { useUserStore } from '@/store';
import { handlePayResult } from '@/utils/order';
import { defaultImage } from '@/utils/assets';
const userState = useUserStore();
const { terminalInfo } = storeToRefs(userState);
const orderBean = ref<OrderBean>();
onLoad(async (e: any) => {
orderBean.value = JSON.parse(decodeURIComponent(e?.orderBean));
await userState.fetchTerminal();
if(!terminalInfo.value) {
showToast('当前店铺未配置收款渠道,请联系商家');
}
});
const buildSqbParams = computed(() => {
const params = sortASCII({
client_sn: orderBean.value?.id || '',
return_url: '/pages/common/payresult/index',
total_amount: Number(((orderBean.value?.totalPrice || 0) * 100).toFixed(2)),
terminal_sn: terminalInfo.value?.terminalSn,
subject: orderBean?.value?.orderGoods[0]?.goodsName || '商品团购券',
subject_img: orderBean?.value?.orderGoods[0]?.images || 'xxx',
merchant_name: terminalInfo.value?.companyName,
notify_url: 'https://www.baidu.com'
}, true);
return {
...params,
sign: hexMD5(parseParameter(params) + '&key=' + terminalInfo.value.terminalKey).toUpperCase()
};
});
const navigateTo = (e: any) => {
handlePayResult(orderBean.value?.id, e, {
onSuccess: (options: any) => {
showToast('支付成功', {
icon: 'success',
complete: () => {
pay({
'orderId': orderBean.value?.id,
'result': 'xxx'
}).then(res => {
uni.navigateBack();
});
uni.hideLoading();
}
});
},
onFailure: (e: any) => {
showToast(e, {
complete: () => {
uni.navigateBack();
}
});
}
});
};
const payment = () => {
if(!terminalInfo.value) {
showToast('当前店铺未配置收款渠道,请联系商家');
return;
}
const params = {
'id': orderBean.value?.id,
'orderSn': buildSqbParams.value.client_sn,
'terminal_key': terminalInfo.value.terminalKey,
'terminal_sn': terminalInfo.value.terminalSn
};
progress(params);
};
</script>
<style lang='scss' scoped>
.content {
.card-view:nth-of-type(1) {
margin-top: 30rpx;
}
}
.card-view {
display: flex;
flex-direction: row;
margin: 10rpx 30rpx;
background: #FFFFFF;
font-size: 35rpx;
padding: 30rpx;
.goods-image {
width: 130rpx;
height: 130rpx;
margin-right: 15rpx;
border-radius: 8rpx;
}
.goods-name {
color: #333333;
}
.goods-sku {
font-size: 30rpx;
color: #666666;
}
.c-flex-column {
justify-content: space-between;
.goods-num {
display: flex;
font-size: 30rpx;
justify-content: flex-end;
color: #999999;
}
.goods-price {
@extend .goods-num;
}
}
}
.bottom-view {
background: #FFFFFF;
padding: 20rpx 30rpx 78rpx 33rpx;
position: fixed;
left: 0;
right: 0;
bottom: 0;
sqb-pay {
flex: 1;
.confirm-button {
display: flex;
flex: 1;
font-weight: bold;
width: 100%;
font-size: 30rpx;
height: 80rpx;
align-items: center;
justify-content: center;
background: #F32B2B;
color: #FFFFFF;
border-radius: 40rpx;
}
}
}
</style>

View File

@@ -0,0 +1,299 @@
<template>
<view class='content'>
<view class='card-view' style='justify-content: flex-start'>
<view class='c-flex-row' style='align-items: center;flex: 1'>
<image class='goods-image' :src='imageList?.length>0?imageList[0]:defaultImage' />
<view class='c-flex-column' style='margin-left: 10rpx;flex: 1'>
<text class='goods-name'>{{ groupBuyBean?.name || groupBuyBean?.title }}</text>
<text class='date'>{{ groupBuyBean?.startDate }}{{ groupBuyBean?.endDate }}</text>
</view>
<text class='goods-price'>¥{{ groupBuyBean?.payPrice }}</text>
</view>
</view>
<view class='card-view'>
<text style='flex: 1'>数量</text>
<view class='count-change-view c-flex-row'>
<view class='count-image' @click.stop='countChange(false)'>
<image :src='assetsUrl("ic_reduce.png")' />
</view>
<text>{{ goodsCount }}</text>
<view class='count-image' @click.stop='countChange(true)'>
<image :src='assetsUrl("ic_plus.png")' />
</view>
</view>
</view>
<view class='card-view'>
<text style='flex: 1'>实付</text>
<text style='color: #F32B2B'>¥{{ totalPrice || 0 }}</text>
</view>
<view class='bottom-view c-flex-row'>
<sqb-pay @navigateTo='navigateTo'
:return_url='buildSqbParams.return_url'
:total_amount='buildSqbParams.total_amount'
:terminal_sn='buildSqbParams.terminal_sn'
:client_sn='buildSqbParams.client_sn'
:subject='buildSqbParams.subject'
:subject_img='buildSqbParams.subject_img '
:merchant_name='buildSqbParams.merchant_name'
:notify_url='buildSqbParams.notify_url'
:sign='buildSqbParams.sign'>
<button class='confirm-button' @click='payment'>支付</button>
</sqb-pay>
</view>
</view>
</template>
<script lang='ts' setup>
import { couponPay, preOrder } from '@/api/groupbuy';
import { OrderBean } from '@/api/order/types';
import { parseParameter, showToast, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { useUserStore } from '@/store';
import { handlePayResult } from '@/utils/order';
import { assetsUrl, defaultImage } from '@/utils/assets';
import { GroupBuyBean } from '@/api/groupbuy/types';
const userState = useUserStore();
const { userInfo, terminalInfo } = storeToRefs(userState);
const groupBuyBean = ref<GroupBuyBean>();
const imageList = ref([]);
const orderBean = ref<OrderBean>();
const goodsCount = ref(1);
onLoad(async (e: any) => {
try {
groupBuyBean.value = JSON.parse(decodeURIComponent(e?.bean));
if(groupBuyBean.value) {
imageList.value = groupBuyBean.value?.image?.split(',');
}
await userState.fetchTerminal(groupBuyBean.value?.companyId || '');
createOrder();
} catch (e) {
getApp().globalData?.logger?.error('order-coupon-confirm: exception ', e);
}
});
const countChange = (isPlus: boolean) => {
if(isPlus) {
if(goodsCount.value < (groupBuyBean.value?.publicNum || 0)) {
goodsCount.value += 1;
} else {
showToast('超出最大购买数量');
}
} else {
if(goodsCount.value > 1) {
goodsCount.value -= 1;
}
}
};
const totalPrice = computed(() => {
return Number(((groupBuyBean.value?.payPrice || 0) * goodsCount.value).toFixed(2));
});
const buildSqbParams = computed(() => {
const params = sortASCII({
client_sn: orderBean.value?.id || '',
return_url: '/pages/common/payresult/index',
total_amount: Number(((totalPrice.value || 0) * 100).toFixed(2)),
terminal_sn: terminalInfo.value?.terminalSn,
subject: groupBuyBean.value?.name || '商品团购券',
subject_img: (imageList.value?.length > 0 && imageList.value[0]) || 'xxx',
merchant_name: terminalInfo.value?.companyName,
notify_url: 'https://www.baidu.com'
}, true);
getApp().globalData?.logger?.info('order-coupon-confirm: buildSqbParams before sign: ', params);
const paramsSign = {
...params,
sign: hexMD5(parseParameter(params) + '&key=' + terminalInfo.value?.terminalKey).toUpperCase()
};
getApp().globalData?.logger?.info('order-coupon-confirm: buildSqbParams after sign: ', paramsSign);
return paramsSign;
});
const navigateTo = (e: any) => {
handlePayResult(orderBean.value?.id, e, {
onSuccess: (options: any) => {
showToast('支付成功', {
icon: 'success',
complete: () => {
// delete orderBean.value && orderBean.value!.createTime;
couponPay({
...orderBean.value,
payResult: JSON.stringify(options),
status: 2
}).then(res => {
uni.navigateBack();
});
uni.navigateBack();
}
});
},
onFailure: (e: any) => {
getApp().globalData?.logger?.error('coupon pay failure params: ', buildSqbParams.value, e);
uni.showToast({
title: e,
icon: 'none',
duration: 1500,
complete(result) {
if(e?.includes('订单号重复')) {
createOrder();
}
}
});
}
});
};
const createOrder = () => {
if(!terminalInfo.value) {
showToast('当前店铺未配置收款渠道,请联系商家');
return;
}
const params = {
id: groupBuyBean.value?.id,
cash: totalPrice.value,
num: goodsCount.value,
memberId: userInfo.value.id,
payBo: {
terminal_sn: terminalInfo.value?.terminalSn,
terminal_key: terminalInfo.value?.terminalKey,
merchant_name: userInfo.value.companyName,
total_amount: totalPrice.value,
subject: groupBuyBean.value?.name || '微信团购券',
description: groupBuyBean.value?.title || '微信团购券',
subject_img: (imageList.value?.length > 0 && imageList.value[0]) || '',
device_id: groupBuyBean.value?.creatorId,
return_url: '/pages/payResult/index'
}
};
preOrder(params).then(res => {
orderBean.value = res as any;
if(orderBean.value) {
orderBean.value!.clientSn = res?.id;
}
}).catch(err => {
getApp().globalData?.logger?.error('order-coupon-confirm: preOrder exception ', err);
});
};
const payment = () => {
};
</script>
<style lang='scss' scoped>
.content {
.card-view:nth-of-type(1) {
margin-top: 30rpx;
}
}
.card-view {
display: flex;
flex-direction: row;
margin: 10rpx 30rpx;
background: #FFFFFF;
font-size: 35rpx;
padding: 30rpx;
.goods-image {
display: flex;
width: 130rpx;
height: 130rpx;
margin-right: 15rpx;
border-radius: 8rpx;
}
.goods-name {
display: flex;
font-size: 35rpx;
font-weight: bold;
color: #333333;
margin-left: 10rpx;
}
.date {
font-size: 30rpx;
color: #999999;
margin-left: 10rpx;
margin-top: 10rpx;
}
.goods-price {
display: flex;
font-size: 30rpx;
margin-right: 10rpx;
justify-content: flex-end;
color: #333333;
}
.count-change-view {
.count-image {
display: flex;
align-items: center;
justify-content: center;
width: 54rpx;
height: 54rpx;
background: #FBFBFB;
image {
width: 15rpx;
height: 2rpx;
}
}
.count-image:nth-of-type(2) image {
width: 17rpx;
height: 17rpx;
}
text {
display: flex;
width: 54rpx;
height: 54rpx;
align-items: center;
justify-content: center;
font-weight: 400;
font-size: 30rpx;
color: #333333;
background: #F2F2F2;
}
}
}
.bottom-view {
background: #FFFFFF;
padding: 20rpx 30rpx 78rpx 33rpx;
position: fixed;
left: 0;
right: 0;
bottom: 0;
sqb-pay {
flex: 1;
.confirm-button {
display: flex;
flex: 1;
font-weight: bold;
width: 100%;
font-size: 30rpx;
height: 80rpx;
align-items: center;
justify-content: center;
background: #F32B2B;
color: #FFFFFF;
border-radius: 40rpx;
}
}
}
</style>

View File

@@ -10,28 +10,89 @@
微信登录 微信登录
</button> </button>
<view class='hint'> <view class='hint'>
登录代表同意 <image @click.stop='bindCheck'
<text class='link'> :src='isAgreePrivacy?("/static/images/ic_checked_green.png"):("/static/images/ic_checked_gray.png")' />
用户协议隐私政策
<text @click.stop='bindCheck'>请先阅读并同意
<text class='link' @click.stop='openPrivacy'>
小程序隐私保护协议
</text> </text>
并授权使用您的账号信息如昵称头像收获地址以便您统一管理 并授权使用您的账号信息如昵称头像收获地址以便您统一管理
</text>
</view> </view>
</view> </view>
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { getCompanyList } from '@/api/company';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { getCompanyId, getOpenId, goPath, showToast } from '@/utils';
const userStore = useUserStore(); const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const isAgreePrivacy = ref(false);
function wechatLogin() { function wechatLogin() {
if(!isAgreePrivacy.value) {
showToast('请先阅读并同意小程序隐私保护协议');
return;
}
uni.showLoading(); uni.showLoading();
userStore.authLogin().then(() => { userStore.login().then(async () => {
const maOpenId = getOpenId() || userInfo.value?.maOpenId;
const data = await getCompanyList(maOpenId);
const companyList = data.map((item: any) => item.company);
uni.hideLoading(); uni.hideLoading();
//登录成功先判断当前公司有没有注册过
let index = companyList.findIndex((res: {
id: string
}) => res.id === (getApp()?.globalData?.shareCompanyId || getCompanyId()));
if(index < 0) {
uni.showModal({
title: '提示',
content: '当前店铺还未注册,请先注册',
showCancel: true,
success: (res) => {
if(res.confirm) {
goPath('/pages/common/register/index');
} else {
goPath('/pages/home/index');
}
}
});
return;
}
if(getApp()?.globalData && getApp()?.globalData?.sharePath) {
uni.reLaunch({
url: getApp()?.globalData?.sharePath,
onSuccess: () => {
getApp().globalData!.sharePath = '';
}
});
} else {
uni.reLaunch({ url: '/pages/home/index' }); uni.reLaunch({ url: '/pages/home/index' });
}
}); });
} }
const bindCheck = () => {
isAgreePrivacy.value = !isAgreePrivacy.value;
};
const openPrivacy = () => {
wx.openPrivacyContract({
success: () => {
}, // 打开成功
fail: () => {
}, // 打开失败
complete: () => {
}
});
};
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -70,14 +131,23 @@ function wechatLogin() {
} }
.hint { .hint {
display: inline-block;
padding: 20rpx 50rpx; padding: 20rpx 50rpx;
font-size: 20rpx; font-size: 25rpx;
color: $u-tips-color; color: $u-tips-color;
position: absolute; position: absolute;
bottom: 0; align-items: center;
bottom: 50rpx;
.link { .link {
color: $u-warning; color: $u-warning;
} }
image {
width: 30rpx;
height: 30rpx;
margin-right: 10rpx;
margin-bottom: -5rpx;
}
} }
</style> </style>

View File

@@ -0,0 +1,11 @@
<template>
<text>支付成功</text>
</template>
<script lang='ts' setup>
</script>
<style lang='scss' scoped>
</style>

View File

@@ -1,144 +1,221 @@
<template> <template>
<view class='content'> <view class='content'>
<image class='bg-image' :src='assetsUrl("bg_register.png")' /> <view class='c-flex-column avatar-container'>
<view class='card-view'> <image class='avatar' :src='avatarUrl' />
<view class='mobile-view c-flex-row'> <button class='avatar-button' open-type='chooseAvatar' @chooseavatar='chooseAvatar' />
<image :src='assetsUrl("ic_register_mobile.png")' />
<input placeholder='请输入手机号' inputmode='number' maxlength='11' />
</view> </view>
<view class='c-flex-row' style='margin-top: 60rpx'>
<view class='captcha-view c-flex-row'> <label class='row-title'>姓名</label>
<input class='row-value' type='nickname' placeholder='请输入手机号' v-model='nickName' />
</view>
<view class='divider' />
<view class='c-flex-row'> <view class='c-flex-row'>
<image :src='assetsUrl("ic_register_captcha.png")' /> <label class='row-title'>手机号</label>
<input placeholder='请输入验证码' inputmode='number' maxlength='6' /> <input class='row-value' placeholder='请输入手机号' type='text' v-model='telephone' />
<button class='primary-button authorize-button' open-type='getPhoneNumber' @getphonenumber='getPhoneNumber'>
获取手机号
</button>
</view>
<view class='divider' />
<view class='c-flex-row'>
<label class='row-title'>生日</label>
<picker mode='date' @change='changeDate'>
<text class='row-value'>{{ dayjs(birthday).format('YYYY-MM-DD') || '请选择生日' }}</text>
</picker>
</view>
<view class='divider' />
<view class='c-flex-row'>
<label class='row-title'>性别</label>
<view class='c-flex-row' @click.stop='changeGender(0)'>
<image class='gender-image'
:src='assetsUrl(gender==="男"?"ic_checkbox_active.png":"ic_checkbox_normal.png")' />
<text class='gender-text'></text>
</view>
<view class='c-flex-row' @click.stop='changeGender(1)'>
<image class='gender-image'
:src='assetsUrl(gender==="女"?"ic_checkbox_active.png":"ic_checkbox_normal.png")' />
<text class='gender-text'></text>
</view> </view>
<text @click.stop='startCountdown'>{{ countdown }}</text>
</view> </view>
<text class='btn_register' @click.stop='register'>注册领券</text> <view style='height: 100rpx' />
</view> <button class='primary-button submit-button' @click='save'>确定</button>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { ref } from 'vue';
import { getTelephone } from '@/api/user';
import { useUserStore } from '@/store';
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { getRegisterStoreId, getSessionKey, showToast } from '@/utils';
import dayjs from 'dayjs';
const countdown = ref('获取验证码'); const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
onLoad((e) => { const birthday = ref(userInfo.value.birthday || dayjs().format('YYYY-MM-DD'));
const nickName = ref(userInfo.value.nickName);
const gender = ref(userInfo.value.gender);
const telephone = ref(userInfo.value.telephone);
const avatarUrl = ref(userInfo.value.image);
const chooseAvatar = (e: any) => {
uni.showLoading();
uni.uploadFile({
url: import.meta.env.VITE_APP_BASE_API + '/wc/wechat/uploadImage',
filePath: e.detail.avatarUrl,
name: 'fileName',
header: {
'Content-Type': 'multipart/form-data'
},
success: (res: any) => {
avatarUrl.value = JSON.parse(res.data).data;
},
error: (err: any) => {
showToast('上传失败');
},
complete() {
uni.hideLoading();
}
}); });
};
const startCountdown = () => { const getPhoneNumber = (e: any) => {
if(countdown.value !== '获取验证码') { if(e.detail.errMsg === 'getPhoneNumber:ok') {
uni.login({
success: res => {
getTelephone({
sessionKey: getSessionKey(),
code: res.code,
iv: e.detail.iv,
encryptedData: e.detail.encryptedData
}).then(response => {
if(response.phoneNumber) {
telephone.value = response.phoneNumber;
}
});
}
});
}
};
const changeDate = (e: any) => {
birthday.value = e.detail.value;
};
const changeGender = (index: number) => {
gender.value = index === 0 ? '男' : '女';
};
const save = async () => {
if(!getRegisterStoreId()) {
showToast('请从公众号消息窗口点击店名打开小程序');
return; return;
} }
let time = 120;
let interval = setInterval(() => { if(!telephone.value) {
time--; showToast('请输入手机号');
countdown.value = `${time}s 重新获取`; return;
if(time == 0) {
clearInterval(interval);
countdown.value = '获取验证码';
} }
}, 1000);
await uni.showLoading();
const registerForm = {
image: avatarUrl.value,
avatarUrl: avatarUrl.value,
nickName: nickName.value,
telephone: telephone.value,
birthday: dayjs(birthday.value).isValid() ? dayjs(birthday.value).format('YYYY-MM-DD HH:mm:ss') : dayjs().format('YYYY-MM-DD HH:mm:ss'),
gender: gender.value
}; };
const register = () => { getApp().globalData?.logger.info('register params: ', registerForm);
const result = await userStore.register(registerForm);
getApp().globalData?.logger.info('register params: ', registerForm);
if(result) {
showToast('注册成功');
if(getApp()?.globalData && getApp()?.globalData?.sharePath) {
uni.reLaunch({
url: getApp()?.globalData?.sharePath,
onSuccess: () => {
getApp().globalData!.sharePath = '';
}
});
} else {
await uni.reLaunch({
url: '/pages/home/index'
});
}
}
uni.hideLoading();
}; };
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.content { .content {
position: relative; display: flex;
height: 100vh; background: white;
padding: 30rpx;
.divider {
margin: 30rpx 5rpx;
} }
.bg-image { .row-title {
position: fixed; font-size: 30rpx;
height: 100vh; color: #666666;
width: 100%; width: 150rpx;
top: 0;
left: 0;
right: 0;
bottom: 0;
} }
.card-view { .row-value {
background: #FFFFFF;
border-radius: 10rpx;
margin: 0 50rpx;
padding: 40rpx;
top: 600rpx;
position: relative;
.mobile-view {
border-radius: 10rpx;
border: 1rpx solid #D8D8D8;
padding-left: 28rpx;
image {
width: 36rpx;
height: 36rpx;
}
input {
flex: 1;
height: 80rpx;
font-size: 30rpx; font-size: 30rpx;
color: #333333; color: #333333;
margin-left: 26rpx;
} }
} }
.captcha-view { .avatar-container {
align-items: center;
margin-top: 20rpx; margin-top: 20rpx;
position: relative;
.c-flex-row { .avatar {
width: 150rpx;
height: 150rpx;
border-radius: 10rpx; border-radius: 10rpx;
border: 1rpx solid #D8D8D8; background-color: #f2f2f2;
padding-left: 28rpx;
flex: 1;
image {
width: 33rpx;
height: 26rpx;
} }
input { .avatar-button {
height: 80rpx; width: 150rpx;
font-size: 30rpx; height: 150rpx;
background-color: transparent;
position: absolute;
top: 0;
}
}
.authorize-button {
width: auto;
font-size: 20rpx;
min-width: auto;
height: 50rpx;
}
.gender-image {
width: 37rpx;
height: 37rpx;
}
.gender-text {
width: 80rpx;
color: #333333; color: #333333;
margin-left: 26rpx;
}
}
text {
display: flex;
height: 80rpx;
background: #FFD436;
border-radius: 10rpx;
margin-left: 12rpx;
padding: 0 15rpx;
font-size: 28rpx;
align-items: center;
justify-content: center;
color: #FFFFFF;
}
}
.btn_register {
display: flex;
align-items: center;
justify-content: center;
height: 90rpx;
background: #F53636;
border-radius: 10rpx;
font-weight: 400;
font-size: 30rpx; font-size: 30rpx;
color: #FFFFFF; margin-left: 10rpx;
margin-top: 46rpx;
} }
.submit-button {
display: flex;
margin-top: 40rpx;
} }
</style> </style>

View File

@@ -0,0 +1,228 @@
<template>
<view class='content'>
<image class='bg-image' :src='assetsUrl("bg_register.png")' />
<image :src='assetsUrl("ic_register_gift_text.png")'
style='align-self: center;position: relative;width: 356rpx;height: 124rpx;margin-top: 70rpx' />
<view class='card-view'>
<view class='mobile-view c-flex-row' style='display: none'>
<image :src='assetsUrl("ic_register_mobile.png")' />
<input placeholder='请输入手机号' inputmode='number' maxLength='11' />
</view>
<view class='captcha-view c-flex-row' style='display: none'>
<view class='c-flex-row'>
<image :src='assetsUrl("ic_register_captcha.png")' />
<input placeholder='请输入验证码' inputmode='number' maxLength='6' />
</view>
<text @click.stop='startCountdown'>{{ countdown }}</text>
</view>
<text class='btn_register' style='display: none' @click.stop='register'>注册领券</text>
<image :src='companyConfigInfo.regqrcode||defaultImage' :show-menu-by-longpress='true' mode='aspectFill'
style='width: 439rpx;height: 439rpx;align-self: center' />
<text style='font-size: 26rpx;
margin-top: 10rpx;
align-self: center;color: #666666;'>长按扫码注册
</text>
<template v-if='couponBean&&couponBean.status==0'>
<view class='divider' />
<view class='coupon c-flex-column' @click.stop='goPath("/pages/mine/subs/coupon/index")'>
<view class='c-flex-row'>
<text class='coupon-name'>{{ couponBean.title }}</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' />
</view>
<text class='expired-time'>
有效期至{{ dayjs(Date.now()).add(couponBean.triggerEndDay, 'day').format('YYYY.MM.DD') }}
</text>
</view>
<view class='divider' />
<text style='font-size: 28rpx;
font-weight: bold;
color: #333333;margin-top: 20rpx'>福利详情
</text>
<text style='font-size: 28rpx;
color: #333333;margin-top: 14rpx'>1.优惠金额{{ couponBean.reduce }}\n
2.{{ couponBean.threshold }}{{ couponBean.reduce }}\n
3.赠送{{ couponBean.publicNum }}
</text>
</template>
<template v-else>
<text style='font-size: 28rpx;
font-weight: bold;
color: #333333;margin-top: 20rpx'>
微信扫一扫领取会员卡
</text>
<text style='font-size: 28rpx;
color: #333333;margin-top: 14rpx'>
1及时接受消费小票\n
2随时查询消费记录\n
3及时接收优惠信息
</text>
</template>
</view>
</view>
</template>
<script lang='ts' setup>
import { assetsUrl, defaultImage } from '@/utils/assets';
import { useUserStore } from '@/store';
import { goPath } from '@/utils';
import { getRegisterCoupon } from '@/api/user';
import dayjs from 'dayjs';
const userStore = useUserStore();
const { companyConfigInfo } = storeToRefs(userStore);
const countdown = ref('获取验证码');
const couponBean = ref();
onLoad(async (e) => {
couponBean.value = await getRegisterCoupon();
});
const startCountdown = () => {
if(countdown.value !== '获取验证码') {
return;
}
let time = 120;
let interval = setInterval(() => {
time--;
countdown.value = `${time}s 重新获取`;
if(time == 0) {
clearInterval(interval);
countdown.value = '获取验证码';
}
}, 1000);
};
const register = () => {
};
</script>
<style lang='scss' scoped>
.content {
position: relative;
height: 100vh;
}
.bg-image {
position: fixed;
height: 100vh;
width: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.card-view {
background: #FFFFFF;
border-radius: 10rpx;
margin: 0 40rpx;
padding: 50rpx;
top: 150rpx;
position: relative;
.mobile-view {
border-radius: 10rpx;
border: 1rpx solid #D8D8D8;
padding-left: 28rpx;
image {
width: 36rpx;
height: 36rpx;
}
input {
flex: 1;
height: 80rpx;
font-size: 30rpx;
color: #333333;
margin-left: 26rpx;
}
}
.captcha-view {
margin-top: 20rpx;
.c-flex-row {
border-radius: 10rpx;
border: 1rpx solid #D8D8D8;
padding-left: 28rpx;
flex: 1;
image {
width: 33rpx;
height: 26rpx;
}
input {
height: 80rpx;
font-size: 30rpx;
color: #333333;
margin-left: 26rpx;
}
}
text {
display: flex;
height: 80rpx;
background: #FFD436;
border-radius: 10rpx;
margin-left: 12rpx;
padding: 0 15rpx;
font-size: 28rpx;
align-items: center;
justify-content: center;
color: #FFFFFF;
}
}
.btn_register {
display: flex;
align-items: center;
justify-content: center;
height: 90rpx;
background: #F53636;
border-radius: 10rpx;
font-weight: 400;
font-size: 30rpx;
color: #FFFFFF;
margin-top: 46rpx;
}
.divider {
border-bottom-style: dashed;
border-color: #E98585;
border-width: 0.2rpx;
margin-top: 34rpx
}
.coupon {
margin-top: 30rpx;
.coupon-name {
display: flex;
font-size: 36rpx;
color: #D95554;
flex: 1;
}
image {
width: 13rpx;
height: 24rpx;
}
.expired-time {
font-size: 28rpx;
color: #333333;
margin-top: 2rpx;
}
}
}
</style>

View File

@@ -1,12 +1,12 @@
<template> <template>
<web-view class="h-full" :src="url" /> <web-view class='h-full' :src='url' />
</template> </template>
<script setup lang="ts"> <script setup lang='ts'>
const url = ref<string>(''); const url = ref<string>('');
onLoad((params: any) => { onLoad((params: any) => {
if(params.url) if(params.url)
url.value = params.url; url.value = decodeURIComponent(params.url);
}); });
</script> </script>

View File

@@ -0,0 +1,52 @@
<template>
</template>
<script lang='ts' setup>
import { getStoreId } from '@/api/company';
import { isLogin, setCompanyId, setRegisterStoreId } from '@/utils';
onLoad(async (options) => {
getApp().globalData?.logger?.info('ticketsBuy share options: ', options);
let couponId = options?.couponsId;
if(couponId) {
const path = `/pages/common/groupbuy/detail?couponId=${couponId}`;
if(!isLogin() && getApp()?.globalData) {
getApp().globalData!.sharePath = path;
}
if(options?.companyId && options?.creatorId) {
const storeId = await getStoreId({
companyid: options?.companyId,
deviceid: options?.creatorId
});
setCompanyId(options?.companyId);
if(storeId) {
setRegisterStoreId(storeId);
if(getApp()?.globalData) {
getApp().globalData!.shareCompanyId = options?.companyId;
getApp().globalData!.shareStoreId = storeId;
}
}
}
setTimeout(() => {
uni.reLaunch({
url: path
});
}, 300);
} else {
uni.reLaunch({
url: '/pages/home/index'
});
}
});
</script>
<style lang='scss' scoped>
</style>

View File

@@ -2,40 +2,44 @@
<view class='u-flex-column'> <view class='u-flex-column'>
<swiper class='swiper' :indicator-dots='true' :autoplay='true' :interval='3000' :duration='1000' <swiper class='swiper' :indicator-dots='true' :autoplay='true' :interval='3000' :duration='1000'
@change='swiperChange'> @change='swiperChange'>
<swiper-item v-for='(item, index) in bannerList' :key='index'> <swiper-item v-for='(item, index) in companyConfigInfo?.bannerinx' :key='index'>
<image :src='item' mode='aspectFill' /> <image :src='item.src||defaultImage' mode='aspectFill' />
</swiper-item> </swiper-item>
</swiper> </swiper>
<view class='basic-view'> <view class='basic-view'>
<view class='indicator-view'> <view class='indicator-view'>
<view v-for='(item,index) in bannerList' :key='item' class='normal' <view v-for='(item,index) in companyConfigInfo?.bannerinx' :key='item' class='normal'
:class="{'active': index === currentBannerIndex}"> :class="{'active': index === currentBannerIndex}">
</view> </view>
</view> </view>
<view class='user-info-card'> <view class='user-info-card' @click.stop='goPath("/pages/mine/index",true)'>
<image class='user-avatar' :src='userInfo.image' mode='aspectFill' /> <image class='user-avatar' :src='userInfo?.image||defaultAvatar' mode='aspectFill' />
<text class='user-name primary-text-color'>{{ userInfo?.nickName }}</text> <text class='user-name primary-text-color'>{{ userInfo?.nickName || '点击注册会员' }}</text>
<view class='integral-view primary-text-color' @click.stop='goPath("/pages/mine/subs/integral/index")'> <view class='integral-view primary-text-color' @click.stop='goPath("/pages/mine/subs/integral/index",true)'>
<text>{{ userInfo?.integration }}</text> <text>{{ userInfo?.integration || 0 }}</text>
<text>积分</text> <text>积分</text>
</view> </view>
<view class='divider' style='height: 83rpx' /> <view class='divider' style='height: 83rpx' />
<view class='balance-view' @click.stop='goPath("/pages/mine/subs/recharge/index")'> <view class='balance-view' @click.stop='goPath("/pages/mine/subs/recharge/index",true)'>
<text class='accent-text-color'>{{ userInfo?.balance }}</text> <text class='accent-text-color'>{{ userInfo?.balance || 0 }}</text>
<text>余额</text> <text>余额()</text>
</view>
<view class='divider' style='height: 83rpx' />
<view class='switch-view' @click.stop='switchCompany'>
<image :src='assetsUrl("ic_switch_company.png")' />
</view> </view>
</view> </view>
<view class='menu-view'> <view class='menu-view'>
<view @click.stop='goPath("/pages/mine/subs/recharge/index")'> <view @click.stop='goPath("/pages/mine/subs/recharge/index",true)'>
<image :src='assetsUrl("ic_member_card.png")' style='width: 108rpx;height: 72rpx;padding: 11rpx 0' /> <image :src='assetsUrl("ic_member_card2.png")' style='width: 108rpx;height: 72rpx;padding: 11rpx 0' />
<text>会员充值</text> <text>会员充值</text>
</view> </view>
<view class='divider' style='margin: 0;height: 153rpx' /> <view class='divider' style='margin: 0;height: 153rpx' />
<view @click.stop='goPath("/pages/mine/subs/coupon/index")'> <view @click.stop='goPath("/pages/mine/subs/coupon/index",true)'>
<image :src='assetsUrl("ic_coupon.png")' style='width: 108rpx;height: 95rpx' /> <image :src='assetsUrl("ic_coupon2.png")' style='width: 108rpx;height: 95rpx' />
<text>优惠券</text> <text>优惠券</text>
</view> </view>
</view> </view>
@@ -53,51 +57,181 @@
<view class='bottom-banner-view'> <view class='bottom-banner-view'>
<swiper :indicator-dots='true' :autoplay='true' :interval='3000' :duration='1000'> <swiper :indicator-dots='true' :autoplay='true' :interval='3000' :duration='1000'>
<swiper-item v-for='(item, index) in recommendBannerList' :key='index'> <swiper-item v-for='(item, index) in companyConfigInfo?.bannerhot' :key='index'>
<image :src='item' mode='aspectFill' /> <image :src='item.src' mode='aspectFill' />
</swiper-item> </swiper-item>
</swiper> </swiper>
</view> </view>
</view> </view>
</view> </view>
<CompanyDialog ref='companyDialogRef' :companyList='companyList' />
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { getCompanyInfo } from '@/api/company'; import CompanyDialog from '@/components/company-dialog.vue';
import { getCompanyList } from '@/api/company';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultAvatar, defaultImage } from '@/utils/assets';
import { goPath } from '@/utils'; import { getOpenId, goPath, isLogin } from '@/utils';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { UserBean } from '@/store/modules/user/types';
const store = useUserStore(); const companyDialogRef = ref();
const { userInfo } = storeToRefs(store); const userStore = useUserStore();
const bannerList = ref<string[]>([]); const { userInfo, companyConfigInfo } = storeToRefs(userStore);
const currentBannerIndex = ref(0); const currentBannerIndex = ref(0);
const recommendBannerList = ref<string[]>([]);
const submenuList = [ const submenuList = [
{ {
title: '注册有礼', title: '注册有礼',
icon: assetsUrl('ic_register_gift.png'), icon: assetsUrl('ic_register_gift2.png'),
path: '/pages/common/register/index' path: '/pages/common/register/reward'
// path: '/pages/common/register/index'
// path: '/pages/common/groupbuy/detail?id=1800768359753097217'
// path: '/pages/common/groupbuy/detail?shareId=1797548579864748033'
// path: '/pages/coupons/ticketsBuy/ticketsBuy?couponsId=1800381015472513026'
//1799264392762138626
}, },
{ {
title: '团购秒杀', title: '团购秒杀',
icon: assetsUrl('ic_groupbuy.png'), icon: assetsUrl('ic_groupbuy2.png'),
path: '/pages/common/groupbuy/index' path: '/pages/common/groupbuy/index'
}, },
{ {
title: '入群有礼', title: '入群有礼',
icon: assetsUrl('ic_group_gift.png'), icon: assetsUrl('ic_group_gift2.png'),
path: '/pages/home/subs/group/join' path: '/pages/home/subs/group/join'
} }
]; ];
const companyList = ref([]);
const userList = ref<UserBean[]>([]);
onLoad(async (e) => { onMounted(() => {
const data = await getCompanyInfo(userInfo.value.companyId); wx.getPrivacySetting({
bannerList.value = data.bannerinx?.map((res: { src: string }) => res.src); success: res => {
recommendBannerList.value = data.bannerhot?.map((res: { src: string }) => res.src); console.log(res); // 返回结果为: res = { needAuthorization: true/false, privacyContractName: '《xxx隐私保护指引》' }
if(res.needAuthorization) {
// 需要弹出隐私协议
// agreementPopupRef.value.show();
} else {
// 用户已经同意过隐私协议,所以不需要再弹出隐私协议,也能调用隐私接口
}
},
fail: (res) => {
console.log('fail ', res);
},
complete: () => {
}
}); });
});
onLoad((e) => {
console.log('home load e ', e);
});
onShow(async () => {
if(isLogin()) {
// userStore.getProfile();
fetchCompanyList();
} else {
// setToken('42ae187265fb4688804fd294cbcf99f1')
uni.showModal({
title: '提示',
content: '您还未登录,请先登录',
showCancel: true,
success: (res) => {
if(res.confirm) {
goPath('/pages/common/login/index');
}
}
});
}
});
const switchCompany = () => {
fetchCompanyList((companyList: any[], userList: any[]) => {
companyDialogRef.value.show(getApp().globalData?.companyId, (index: number) => {
userStore.setUserInfo(userList[index]);
userStore.setCompanyInfo(companyList[index]);
// avatarModifyRemind();
});
});
};
function avatarModifyRemind() {
setTimeout(() => {
if(userInfo.value.image === defaultAvatar) {
uni.showModal({
title: '提示',
content: '为了提供更优质的个性化服务,请先修改头像',
showCancel: true,
success: (res) => {
if(res.confirm) {
goPath('/pages/mine/subs/profile/index');
}
}
});
}
}, 300);
}
const fetchCompanyList = (fn: any = undefined) => {
const maOpenId = getOpenId() || userInfo.value?.maOpenId || (userList.value.filter((res: UserBean) => res?.maOpenId !== '')[0]?.maOpenId);
getCompanyList(maOpenId).then(res => {
companyList.value = res.map((res: { company: any }) => res.company);
userList.value = res.map((res: { user: any }) => res.user);
if(companyList.value.length === 0) {
showRegisterModal();
return;
}
if(fn != undefined && fn instanceof Function) {
fn(companyList.value, userList.value);
} else {
let index = companyList.value.findIndex((res: { id: string }) => res.id === getApp()?.globalData?.companyId);
//未在当前公司下注册
if(index < 0) {
showRegisterModal();
return;
}
//分享过来的公司id和店铺id
if(getApp()?.globalData?.shareCompanyId && getApp()?.globalData?.shareStoreId) {
index = companyList.value.findIndex((res: { id: string }) => res.id === getApp().globalData?.shareCompanyId);
if(index < 0) {
showRegisterModal();
return;
}
}
userStore.setUserInfo(userList.value[index]);
userStore.setCompanyInfo(companyList.value[index]);
// avatarModifyRemind();
//首页选中处理后清空分享数据,避免再次选中
if(getApp()?.globalData?.shareCompanyId && getApp()?.globalData?.shareStoreId) {
getApp().globalData!.shareCompanyI = '';
getApp().globalData!.shareStoreId = '';
}
}
}
);
};
const showRegisterModal = () => {
uni.showModal({
title: '提示',
content: '当前店铺还未注册,请先注册',
showCancel: true,
success: (res) => {
if(res.confirm) {
goPath('/pages/common/register/index');
}
}
});
};
const swiperChange = (e: any) => { const swiperChange = (e: any) => {
currentBannerIndex.value = e.detail.current; currentBannerIndex.value = e.detail.current;
@@ -113,7 +247,7 @@ const swiperChange = (e: any) => {
image { image {
width: 100%; width: 100%;
height: 100%; height: 520rpx;
} }
} }
@@ -127,8 +261,11 @@ const swiperChange = (e: any) => {
.indicator-view { .indicator-view {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
position: relative; position: absolute;
justify-content: center; justify-content: center;
left: 0;
right: 0;
top: -20rpx;
.normal { .normal {
width: 16rpx; width: 16rpx;
@@ -170,6 +307,10 @@ const swiperChange = (e: any) => {
font-size: 30rpx; font-size: 30rpx;
font-weight: bold; font-weight: bold;
margin-left: 16rpx; margin-left: 16rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 20rpx;
} }
.integral-view { .integral-view {
@@ -198,10 +339,17 @@ const swiperChange = (e: any) => {
font-weight: bold; font-weight: bold;
} }
} }
.switch-view {
image {
width: 50rpx;
height: 50rpx;
}
}
} }
.divider { .divider {
margin: 0 40rpx; margin: 0 30rpx;
width: 0.5rpx; width: 0.5rpx;
height: 100%; height: 100%;
background: #C7C7C7; background: #C7C7C7;

View File

@@ -3,19 +3,24 @@
<text class='title'>入群步骤</text> <text class='title'>入群步骤</text>
<view class='indicator' /> <view class='indicator' />
<text class='step-title'> <text class='step-title'>
1.长按识别下方二维码添加xxxx 1.长按识别下方二维码添加店铺福利群
</text> </text>
<view class='divider' style='margin-top: 28rpx' /> <view class='divider' style='margin-top: 28rpx' />
<image :src=' assetsUrl("test_qrcode.png")' show-menu-by-longpress /> <image :src=' companyConfigInfo.groupqrcode||defaultImage' show-menu-by-longpress />
<text class='step-title'> <text class='step-title'>
2.识别xxxx发您的二维码即可入群 2.识别店铺福利群发您的二维码即可入群
</text> </text>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { useUserStore } from '@/store';
import { defaultImage } from '@/utils/assets';
const userStore = useUserStore();
const { companyConfigInfo } = storeToRefs(userStore);
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>

56
src/pages/index/index.vue Normal file
View File

@@ -0,0 +1,56 @@
<template>
</template>
<script lang='ts' setup>
import { isLogin, setCompanyId, setRegisterStoreId } from '@/utils';
import { getStoreId } from '@/api/company';
onLoad(async (options) => {
getApp().globalData?.logger?.info('app share index/index options: ', options);
if(options?.companyId) {
setCompanyId(options?.companyId);
let storeId = options?.storeId || await getStoreId({
companyid: options?.companyId,
deviceid: options?.creatorId
});
if(storeId) {
setRegisterStoreId(storeId);
}
if(getApp()?.globalData) {
getApp().globalData!.shareCompanyId = options?.companyId;
getApp().globalData!.shareStoreId = storeId;
}
}
let goodsShareId = '';
if(options?.shareId) {
goodsShareId = options?.shareId;
}
getApp().globalData?.logger?.info('app share index/index options: goodsShareId ', goodsShareId);
if(goodsShareId) {
const path = `/pages/common/groupbuy/detail?shareId=${goodsShareId}`;
if(!isLogin() && getApp()?.globalData) {
getApp().globalData!.sharePath = path;
}
setTimeout(() => {
uni.reLaunch({
url: path
});
}, 300);
} else {
uni.reLaunch({
url: '/pages/home/index'
});
}
});
</script>
<style lang='scss' scoped>
</style>

View File

@@ -1,9 +1,9 @@
<template> <template>
<view class='content'> <view class='content' v-if='companyConfigInfo.mallopen==1'>
<view class='search-view'> <view class='search-view'>
<view class='search-input' @click.stop='goPath("/pages/mall/subs/search/index")'> <view class='search-input' @click.stop='goPath("/pages/mall/subs/search/index")'>
<image :src=' assetsUrl("ic_search.png")'></image> <image :src=' assetsUrl("ic_search.png")'></image>
<input placeholder='输入名称、款号搜索' /> <input placeholder='输入名称、款号搜索' :disabled='true' />
</view> </view>
</view> </view>
@@ -16,59 +16,169 @@
</view> </view>
</scroll-view> </scroll-view>
<scroll-view class='goods-list' :scroll-y='true' type='custom'> <scroll-view class='goods-list' :scroll-y='true' type='custom' @scrolltolower='loadMore'>
<grid-view type='masonry' :cross-axis-count='2'> <grid-view type='masonry' :cross-axis-count='2'>
<view v-for='(item, index) in goodsList' :key='index' class='goods-item' <view v-for='(item, index) in goodsList' :key='index' class='goods-item'
@click.stop='goPath(`/pages/mall/subs/goods/detail?goodsId=${item.goodsId}`)'> @click.stop='goPath(`/pages/mall/subs/goods/detail?goodsId=${item.goodsId}`)'>
<image class='goods-image' :src='item.images' /> <image class='goods-image' :src='item.images||defaultImage' />
<text class='goods-name'>{{ item.goodsName }}</text> <text class='goods-name'>{{ item.goodsName || '未知' }}</text>
<text class='goods-price'>¥{{ item.price }}</text> <text class='goods-price'>¥{{ (item.price * userInfo.userDiscount).toFixed(2) }}
<image class='add-image' :src='assetsUrl("ic_add_goods.png")' /> <text v-if='userInfo.userDiscount>0&&userInfo.userDiscount<1'
style='text-decoration: line-through;color: #999999;font-size: 25rpx'>
¥{{ item.price }}
</text>
</text>
<image class='add-image' :src='assetsUrl("ic_add_goods.png")' @click.stop='addShoppingCart(item)' />
</view> </view>
</grid-view> </grid-view>
<u-empty v-if='goodsList.length === 0' text='暂无商品数据' />
</scroll-view> </scroll-view>
</view> </view>
<view class='shopping-cart' @click.stop='goPath("/pages/mall/subs/shoppingcart/index")'> <view class='shopping-cart' @click.stop='goPath("/pages/mall/subs/shoppingcart/index")'>
<image :src='assetsUrl("ic_shopping_cart.png")' /> <image :src='assetsUrl("ic_shopping_cart.png")' />
<text>10</text> <text v-if='totalCount>0'>{{ totalCount }}</text>
</view> </view>
</view> </view>
<view class='content' v-else style='align-items: center;justify-content:center;'>
<image :src='assetsUrl("bg_mall_close.png")' style='width: 200rpx; height: 200rpx;' />
</view>
<sku-dialog ref='skuDialogRef' />
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { assetsUrl } from '@/utils/assets'; import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app';
import { goPath } from '@/utils'; import SkuDialog from '@/components/sku-dialog.vue';
import { assetsUrl, defaultImage } from '@/utils/assets';
import { goPath, isLogin, showToast } from '@/utils';
import { getCategoryList, getGoodsList } from '@/api/goods'; import { getCategoryList, getGoodsList } from '@/api/goods';
import { CategoryBean, GoodsBean } from '@/api/goods/types'; import { CategoryBean, GoodsBean } from '@/api/goods/types';
import useShoppingCartStore from '@/store/modules/shoppingcart';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const { userInfo, companyConfigInfo } = storeToRefs(userStore);
const shoppingCartStore = useShoppingCartStore();
const { totalCount } = storeToRefs(shoppingCartStore);
const skuDialogRef = ref();
const categoryList = ref<CategoryBean[]>([]); const categoryList = ref<CategoryBean[]>([]);
const categorySelectedIndex = ref<number>(0); const categorySelectedIndex = ref<number>(0);
const goodsList = ref<GoodsBean[]>([]); const goodsList = ref<GoodsBean[]>([]);
const currentPageNum = ref<number>(1);
const companyId = '1140269023306428417';
onLoad(() => { onLoad(() => {
if(!isLogin()) {
return;
}
userStore.$onAction(({ name, after }) => {
after(() => {
//更新用户信息
if(name === 'setUserInfo') {
fetchCategoryList();
shoppingCartStore.resetData();
}
});
});
fetchCategoryList(); fetchCategoryList();
}); });
onShareAppMessage((e) => {
return {
title: 'VIP顾客中心',
path: '/pages/mall/index'
};
});
onShareTimeline((e) => {
return {
title: 'VIP顾客中心',
path: '/pages/mall/index'
};
});
const fetchCategoryList = async () => { const fetchCategoryList = async () => {
categoryList.value = await getCategoryList(companyId); if(companyConfigInfo.value.mallopen == 1) {
const list = await getCategoryList();
list.unshift({ typeName: '上新精选', typeId: '2' });
list.unshift({ typeName: '热销商品', typeId: '1' });
list.unshift({ typeName: '全部分类', typeId: '0' });
categoryList.value = list;
await changeCategory(0); await changeCategory(0);
}
}; };
const changeCategory = async (index: number) => { const changeCategory = async (index: number) => {
categorySelectedIndex.value = index; categorySelectedIndex.value = index;
const data = await getGoodsList({ await fetchGoodsList();
pageNum: 1, };
pageSize: 2,
const fetchGoodsList = async (refresh: boolean = true) => {
currentPageNum.value = refresh ? 1 : currentPageNum.value + 1;
let typeId = [categoryList.value[categorySelectedIndex.value].typeId];
let sort = undefined;
if(typeId[0] === '0') {
typeId = [];
sort = undefined;
} else if(typeId[0] === '1') {
typeId = [];
sort = 'good_num DESC';
} else if(typeId[0] === '2') {
typeId = [];
sort = 'update_time DESC';
}
const { rows } = await getGoodsList({
page: {
pageNum: currentPageNum.value,
pageSize: 30,
bean: { bean: {
companyId: companyId,
keyword: '', keyword: '',
typeIds: [categoryList.value[index].typeId] typeIds: typeId,
sort: sort
}
} }
}); });
goodsList.value = data.rows; if(refresh) {
goodsList.value = rows;
} else {
goodsList.value = goodsList.value.concat(rows);
}
goodsList.value.forEach(e => {
e.price = userStore.getRealGoodsPrice(e.price, e.priceExt);
});
};
const loadMore = () => {
fetchGoodsList(false);
};
const randomImageHeight = () => {
const height = getRandomFloatInRange(1, 2) * 200 + 'rpx';
console.log('--------->>>', height);
return height;
};
const getRandomFloatInRange = (a: number, b: number) => {
return Math.random() * (b - a) + a;
};
const addShoppingCart = (goodsBean: GoodsBean) => {
skuDialogRef.value.show(goodsBean.goodsId, (e: GoodsBean) => {
//重新选择sku后判断当前购物中是否存在相同sku的商品
const existsIndex = shoppingCartStore.getSameGoodsIndex(e?.id || '', e.checkedStock.colorId, e.checkedStock.sizeId);
//不存在则更新当前商品sku
if(existsIndex < 0) {
shoppingCartStore.save(e);
}
//如果已存在,则更新已存在商品数量,并删除当前商品
else {
shoppingCartStore.updateCount(existsIndex, e.checkedStock.count);
}
showToast('添加购物车成功');
});
}; };
</script> </script>
@@ -110,7 +220,8 @@ const changeCategory = async (index: number) => {
.category-list { .category-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 210rpx; width: 280rpx;
height: 100vh;
.category-item { .category-item {
display: flex; display: flex;
@@ -122,9 +233,11 @@ const changeCategory = async (index: number) => {
white-space: nowrap; white-space: nowrap;
text { text {
width: 100%; width: 60%;
text-align: center; text-align: center;
font-size: 30rpx; font-size: 30rpx;
text-overflow: ellipsis;
overflow: hidden;
} }
} }
@@ -148,6 +261,7 @@ const changeCategory = async (index: number) => {
display: flex; display: flex;
background: white; background: white;
padding: 38rpx 20rpx 0 20rpx; padding: 38rpx 20rpx 0 20rpx;
height: 100vh;
.goods-item { .goods-item {
display: flex; display: flex;
@@ -159,7 +273,7 @@ const changeCategory = async (index: number) => {
.goods-image { .goods-image {
border-radius: 10rpx; border-radius: 10rpx;
width: 100%; width: 100%;
max-height: 200rpx; max-height: 300rpx;
} }
.goods-name { .goods-name {

View File

@@ -1,13 +1,15 @@
<template> <template>
<uni-popup type='bottom' ref='popupRef' :mask-click='false'> <uni-popup type='bottom' ref='popupRef' :is-mask-click='false'>
<view class='popup-content c-flex-column'> <view class='popup-content c-flex-column'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text>优惠券</text> <text>优惠券</text>
<text @click.stop='close'>确定</text> <text @click.stop='confirm'>确定</text>
</view> </view>
<scroll-view> <scroll-view scroll-y>
<coupon-item v-for='item in coupons' :item='item' /> <coupon-item v-if='coupons.length>0' v-for='(item,index) in coupons' :item='item' :key='index'
@confirm='confirmItem(index)' />
<u-loadmore v-else status='loading' />
</scroll-view> </scroll-view>
</view> </view>
</uni-popup> </uni-popup>
@@ -15,62 +17,53 @@
<script lang='ts' setup> <script lang='ts' setup>
import CouponItem from '../components/coupon-item.vue'; import CouponItem from '../components/coupon-item.vue';
import { getCompanyId } from '@/utils';
import { getCouponList } from '@/api/user';
import { CouponBean } from '@/api/user/types';
import { useUserStore } from '@/store';
const popupRef = ref(); const popupRef = ref();
const coupons = [{
id: 1, const userStore = useUserStore();
title: '优惠券', const { userInfo } = storeToRefs(userStore);
desc: '满100减10',
price: 100, const props = defineProps({
threshold: 10, orderPrice: {
priceDesc: '满100减10', type: Number,
useTime: '2021-12-31', default: 0
status: 1, }
useStatus: '未使用' });
}, { const emits = defineEmits(['confirm']);
id: 1, const coupons = ref<CouponBean[]>([]);
title: '优惠券',
desc: '满100减10', const fetchData = async (status: number) => {
price: 200, const { list } = await getCouponList({
threshold: 80, companyId: getApp()?.globalData?.companyId,
priceDesc: '满100减10', memberId: userInfo.value?.id,
useTime: '2021-01-01', status: status,
status: 1, pageNum: 1,
useStatus: '未使用' pageSize: 100
}, { });
id: 1, list.filter((item: CouponBean) => item.status = (props.orderPrice >= item.threshold) ? 0 : 1);
title: '优惠券', coupons.value = list;
desc: '满100减10', };
price: 300,
threshold: 60,
priceDesc: '满100减10',
useTime: '2023-12-31',
status: '0',
useStatus: '未使用'
}, {
id: 1,
title: '优惠券',
desc: '满100减10',
price: 50,
threshold: 50,
priceDesc: '满100减10',
useTime: '2021-10-31',
status: 0,
useStatus: '未使用'
}, {
id: 1,
title: '优惠券',
desc: '满1000减30',
price: 200,
threshold: 30,
priceDesc: '满100减10',
useTime: '2024-12-31',
status: 0,
useStatus: '未使用'
}];
const show = () => { const show = () => {
popupRef.value.open(); popupRef.value.open();
fetchData(0);
};
const confirmItem = (index: number) => {
coupons.value.forEach(item => {
item.checked = false;
});
coupons.value[index].checked = !coupons.value[index].checked;
};
const confirm = () => {
close();
emits('confirm', coupons.value.filter(item => item.checked)[0]);
}; };
const close = () => { const close = () => {
@@ -86,7 +79,7 @@ defineExpose({
.popup-content { .popup-content {
background: #FFFFFF; background: #FFFFFF;
border-radius: 20rpx 20rpx 0 0; border-radius: 20rpx 20rpx 0 0;
padding: 38rpx 45rpx 0 45rpx; padding: 38rpx 45rpx 10rpx 45rpx;
view:nth-of-type(1) { view:nth-of-type(1) {
display: flex; display: flex;
@@ -113,6 +106,7 @@ defineExpose({
} }
scroll-view { scroll-view {
height: 600rpx;
max-height: 600rpx; max-height: 600rpx;
} }

View File

@@ -1,28 +1,35 @@
<template> <template>
<view class='coupon-content'> <view class='coupon-content' @click.stop='()=>{emits("confirm")}'>
<image v-if='item.status==0' :src='assetsUrl("bg_coupon.png")' /> <image v-if='item?.status==0' :src='assetsUrl("bg_coupon.png")' />
<image v-else-if='item.status==1' :src='assetsUrl("bg_coupon_expired.png")' /> <image v-else-if='item?.status==1' :src='assetsUrl("bg_coupon_expired.png")' />
<view class='left-content accent-text-color' :class='{"left-content-disabled": item.status==1}'> <view class='left-content accent-text-color' :class='{"left-content-disabled": item?.status==1}'>
<text>{{ item.price }}</text> <text>{{ item?.reduce }}</text>
<text>{{ item.threshold }}元可用</text> <text>满{{ item?.threshold }}元可用</text>
</view> </view>
<view class='right-content accent-text-color' :class='{"right-content-disabled": item.status==1}'> <view class='right-content accent-text-color' :class='{"right-content-disabled": item?.status==1}'>
<text>{{ item?.title }}</text> <text>{{ item?.name }}</text>
<text>有效期至{{ item.useTime }}</text> <text>有效期至{{ dayjs(item?.startTime).format('YYYY-MM-DD') }}</text>
<image class='checkbox' :src='assetsUrl("ic_checkbox_active.png")' /> <image v-if='item?.status==0' class='checkbox'
:src='assetsUrl(item.checked?"ic_checkbox_active.png":"ic_checkbox_normal.png")'
/>
</view> </view>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { PropType } from 'vue';
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { CouponBean } from '@/api/user/types';
import dayjs from 'dayjs';
defineProps({ defineProps({
item: Object item: Object as PropType<CouponBean>
}); });
const emits = defineEmits(['confirm']);
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -74,7 +81,7 @@ defineProps({
position: relative; position: relative;
flex: 1; flex: 1;
justify-content: center; justify-content: center;
margin-left: 100rpx; margin-left: 60rpx;
text:nth-of-type(1) { text:nth-of-type(1) {
font-size: 30rpx; font-size: 30rpx;

View File

@@ -2,25 +2,31 @@
<view class='content'> <view class='content'>
<view class='swiper-container'> <view class='swiper-container'>
<swiper class='swiper' :interval='1500' :duration='1000' @change='swiperChange'> <swiper class='swiper' :interval='1500' :duration='1000' @change='swiperChange'>
<swiper-item v-for='(item,index) in bannerList' :key='index'> <swiper-item v-for='(item,index) in [goodsBean?.images]' :key='index'>
<image src='/static/images/test_bg.png' /> <image :src='item||defaultImage' mode='aspectFill' />
</swiper-item> </swiper-item>
</swiper> </swiper>
<view class='indicator'> <view class='indicator' style='display: none'>
<text>{{ swiperIndex + 1 }}</text> <text>{{ swiperIndex + 1 }}</text>
<text>/{{ bannerList.length }}</text> <text>/{{ bannerList.length }}</text>
</view> </view>
</view> </view>
<view class='goods-info-view c-flex-column'> <view class='goods-info-view c-flex-column' style='margin-right: 10rpx'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='goods-price accent-text-color'>39.89</text> <text class='goods-price accent-text-color'>
<text>销量2653</text> ¥{{ ((goodsBean?.price || 0) * userInfo.userDiscount).toFixed(2) }}
<text v-if='userInfo.userDiscount>0&&userInfo.userDiscount<1'
style='display:unset;text-decoration: line-through;font-size: 30rpx;color: #999999'>
¥{{ goodsBean?.price || 0 }}
</text>
</text>
<text>销量{{ goodsBean?.salesCount || 0 }}</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text>女童夏装套装洋气装短袖阔腿裤子夏装夏装套装</text> <text style='flex: 1'>{{ goodsBean?.name || '未知' }}</text>
<view class='share-button c-flex-column'> <view class='share-button'>
<image :src='assetsUrl("ic_share.png")'></image> <image :src='assetsUrl("ic_share.png")'></image>
<button class='btn' plain open-type='share'>分享</button> <button class='btn' plain open-type='share'>分享</button>
</view> </view>
@@ -32,31 +38,35 @@
<text>选择</text> <text>选择</text>
<text>规格 颜色/尺码</text> <text>规格 颜色/尺码</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' /> <image :src='assetsUrl("ic_arrow_right_gray.png")' />
<text>1种颜色可选</text> <text>{{ getStockColorCount }}种颜色可选</text>
</view> </view>
<view class='divider' style='width:auto;height: 0.5rpx;margin-left: 100rpx' /> <view class='divider' style='width:auto;height: 0.5rpx;margin-left: 100rpx;display: none' />
<view class='c-flex-row' @click.stop='showSkuDialog'> <view class='c-flex-row' style='display: none' @click.stop='showSkuDialog'>
<text>参数</text> <text>参数</text>
<text>品牌 风格 季节 款号</text> <text>品牌 风格 季节 款号</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' /> <image :src='assetsUrl("ic_arrow_right_gray.png")' />
</view> </view>
</view> </view>
<view class='recommend-view c-flex-column'> <view class='recommend-view c-flex-column' v-if='(recommendList?.length||0)>0'>
<text>浏览此商品的客户还浏览了</text> <text>浏览此商品的客户还浏览了</text>
<scroll-view scroll-x> <scroll-view scroll-x>
<view style='display: inline-block' v-for='(item, index) in [1, 2, 3, 4]' <view style='display: inline-block' v-for='(item, index) in recommendList'
:key='index'> :key='index'>
<view class='recommend-item c-flex-column'> <view class='recommend-item c-flex-column'>
<image :src='assetsUrl("test_bg.png")' /> <image :src='item.images||defaultImage' />
<text>女童夏装套装</text> <text>{{ item.goodsName || '未知' }}</text>
<text class='goods-price'>22</text> <text class='goods-price'>{{ (item.price * userInfo.userDiscount).toFixed(2) }}
<text v-if='userInfo.userDiscount>0&&userInfo.userDiscount<1'
style='text-decoration: line-through;font-size: 25rpx;color: #999999'>{{ item.price }}
</text>
</text>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<view class='goods-detail c-flex-column'> <view class='goods-detail c-flex-column' style='display: none'>
<text>商品详情</text> <text>商品详情</text>
<image :src='assetsUrl("test_bg.png")' mode='aspectFill' /> <image :src='assetsUrl("test_bg.png")' mode='aspectFill' />
<image :src='assetsUrl("test_bg.png")' mode='aspectFill' /> <image :src='assetsUrl("test_bg.png")' mode='aspectFill' />
@@ -78,7 +88,7 @@
<view class='small-button-item'> <view class='small-button-item'>
<view class='shoppingcart-count' @click.stop='goPath("/pages/mall/subs/shoppingcart/index")'> <view class='shoppingcart-count' @click.stop='goPath("/pages/mall/subs/shoppingcart/index")'>
<image :src='assetsUrl("ic_goods_shoppingcart.png")' /> <image :src='assetsUrl("ic_goods_shoppingcart.png")' />
<text>12</text> <text v-if='totalCount>0'>{{ totalCount }}</text>
</view> </view>
<text>购物车</text> <text>购物车</text>
</view> </view>
@@ -90,19 +100,66 @@
</view> </view>
</view> </view>
</view> </view>
<sku-dialog ref='skuDialogRef' /> <sku-dialog ref='skuDialogRef' :bean='goodsBean' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import { goPath } from '@/utils';
import SkuDialog from '@/components/sku-dialog.vue'; import SkuDialog from '@/components/sku-dialog.vue';
import { goPath, showToast } from '@/utils';
import { getGoodsDetail, getGoodsList } from '@/api/goods';
import { GoodsBean } from '@/api/goods/types';
import useShoppingCartStore from '@/store/modules/shoppingcart';
import { useUserStore } from '@/store';
const shoppingCartStore = useShoppingCartStore();
const { totalCount } = storeToRefs(shoppingCartStore);
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const goodsBean = ref<GoodsBean>();
const recommendList = ref<GoodsBean[]>();
const skuDialogRef = ref(); const skuDialogRef = ref();
const bannerList = ref([1, 2, 3, 4]); const bannerList = ref([]);
const swiperIndex = ref(0); const swiperIndex = ref(0);
onLoad(async (e: any) => {
await uni.showLoading();
goodsBean.value = await getGoodsDetail(e.goodsId);
goodsBean.value.price = userStore.getRealGoodsPrice(goodsBean.value?.price, goodsBean.value?.priceExt);
const { rows } = await getGoodsList({
page: {
page: 1,
pageSize: 20,
bean: {
keyword: '',
typeIds: []
}
}
});
recommendList.value = rows;
recommendList.value?.forEach(e => {
e.price = userStore.getRealGoodsPrice(e.price, e.priceExt);
});
uni.hideLoading();
});
onShareAppMessage((e) => {
return {
title: goodsBean.value?.name || 'VIP顾客中心',
path: `/pages/common/groupbuy/detail?id=${goodsBean.value?.id}&companyId=${getApp().globalData?.companyId}&storeId=${getApp().globalData?.storeId}`
};
});
const getStockColorCount = computed(() => {
const list = Array.from(new Set(goodsBean.value?.stocks?.map(item => item.colorName)))
.map(colorName => goodsBean.value?.stocks?.find(item => item.colorName === colorName)!);
return list.length;
});
const swiperChange = (e: any) => { const swiperChange = (e: any) => {
swiperIndex.value = e.detail.current; swiperIndex.value = e.detail.current;
}; };
@@ -111,19 +168,30 @@ const goBack = () => {
uni.navigateBack(); uni.navigateBack();
}; };
const showSkuDialog = () => { const showSkuDialog = (fn: Function) => {
skuDialogRef.value.show(); skuDialogRef.value.show(goodsBean.value?.id, fn);
}; };
const addShoppingCart = () => { const addShoppingCart = () => {
uni.showToast({ showSkuDialog((e: GoodsBean) => {
title: '加入购物车成功', const index = shoppingCartStore.getSameGoodsIndex(goodsBean.value?.id || '', e.checkedStock?.colorId, e.checkedStock?.sizeId);
icon: 'none' if(index >= 0) {
shoppingCartStore.updateCount(index, e.checkedStock?.count);
} else {
shoppingCartStore.save(e);
}
showToast('加入购物车成功');
}); });
}; };
const placeOrder = () => { const placeOrder = () => {
goPath('/pages/mall/subs/order/order-confirm'); showSkuDialog((e: GoodsBean) => {
const orderBean = {
totalPrice: (e.consumePrice * e.checkedStock.count).toFixed(2),
orderGoods: [e]
};
goPath(`/pages/mall/subs/order/order-confirm?orderBean=${encodeURIComponent(JSON.stringify(orderBean))}`);
});
}; };
</script> </script>
@@ -172,6 +240,9 @@ const placeOrder = () => {
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
view:nth-of-type(1) { view:nth-of-type(1) {
display: flex;
flex-direction: row;
text:nth-of-type(1) { text:nth-of-type(1) {
display: flex; display: flex;
font-size: 40rpx; font-size: 40rpx;
@@ -181,10 +252,10 @@ const placeOrder = () => {
} }
.goods-price:before { .goods-price:before {
content: "¥"; //content: "¥";
font-size: 30rpx; //font-size: 30rpx;
margin-bottom: 5rpx; //margin-bottom: 5rpx;
margin-right: 5rpx; //margin-right: 5rpx;
} }
text:nth-of-type(2) { text:nth-of-type(2) {
@@ -203,6 +274,8 @@ const placeOrder = () => {
} }
.share-button { .share-button {
display: flex;
flex-direction: column;
align-items: center; align-items: center;
image { image {
@@ -224,13 +297,13 @@ const placeOrder = () => {
.goods-sku-view { .goods-sku-view {
background: #FFFFFF; background: #FFFFFF;
padding: 0 30rpx; padding: 20rpx 30rpx;
margin-top: 20rpx; margin-top: 20rpx;
view:nth-of-type(n+1) { view:nth-of-type(n+1) {
position: relative; position: relative;
align-items: center; align-items: center;
height: 105rpx; height: 85rpx;
text:nth-of-type(1) { text:nth-of-type(1) {
width: 100rpx; width: 100rpx;
@@ -256,14 +329,14 @@ const placeOrder = () => {
font-size: 24rpx; font-size: 24rpx;
color: #999999; color: #999999;
position: absolute; position: absolute;
top: 80rpx; top: 50rpx;
left: 100rpx; left: 100rpx;
} }
} }
view:nth-of-type(1) { view:nth-of-type(1) {
align-items: flex-start; align-items: flex-start;
padding-top: 40rpx; //padding-top: 20rpx;
} }
} }
@@ -401,6 +474,7 @@ const placeOrder = () => {
.goods-detail { .goods-detail {
background: #FFFFFF; background: #FFFFFF;
margin-top: 20rpx; margin-top: 20rpx;
text { text {
margin: 20rpx 30rpx; margin: 20rpx 30rpx;
font-weight: bold; font-weight: bold;

View File

@@ -1,4 +1,5 @@
<template> <template>
<scroll-view scroll-y>
<view class='content'> <view class='content'>
<view class='c-flex-row'> <view class='c-flex-row'>
<view class='tab c-flex-row' :class='{"tab-active": tabIndex === 0}' @click.stop='tabIndex=0'> <view class='tab c-flex-row' :class='{"tab-active": tabIndex === 0}' @click.stop='tabIndex=0'>
@@ -13,36 +14,45 @@
<view v-show='tabIndex==1' class='address-view c-flex-column' <view v-show='tabIndex==1' class='address-view c-flex-column'
@click.stop='goPath("/pages/mine/subs/address/index")'> @click.stop='goPath("/pages/mine/subs/address/index")'>
<view v-if='deliveryAddress?.addrid'>
<view class='user-info c-flex-row'> <view class='user-info c-flex-row'>
<text>默认</text> <text class='status' v-if='deliveryAddress?.defaultstatus==1'>默认</text>
<text>黄先生</text> <text>{{ deliveryAddress?.name }}</text>
<text>155****6532</text> <text>{{ deliveryAddress?.mobile }}</text>
<view style='flex: 1' /> <view style='flex: 1' />
<image :src='assetsUrl("ic_arrow_right_gray.png")' /> <image :src='assetsUrl("ic_arrow_right_gray.png")' />
</view> </view>
<view class='addr c-flex-row'> <view class='addr c-flex-row'>
<image :src='assetsUrl("ic_location.png")' /> <image :src='assetsUrl("ic_location.png")' />
<text>湖南省 长沙市 详细地址详细地址详细地址</text> <text>{{ deliveryAddress?.addr }}</text>
</view>
</view>
<view v-else class='user-info c-flex-row'>
<text style='flex: 1'>请选择收货地址</text>
<image :src='assetsUrl("ic_arrow_right_gray.png")' />
</view> </view>
<image class='dashed-line' :src='assetsUrl("ic_address_dashed_line.png")' /> <image class='dashed-line' :src='assetsUrl("ic_address_dashed_line.png")' />
</view> </view>
<view class='goods-info-view c-flex-column'> <view class='goods-info-view c-flex-column'>
<view class='c-flex-row'> <view class='c-flex-row' style='margin-bottom: 20rpx' v-for='(item,index) in orderBean?.orderGoods'
<image class='goods-image' :src='assetsUrl("test_bg.png")' /> :key='index'>
<image class='goods-image' :src='item.images||defaultImage' />
<view class='c-flex-column' style='flex: 1;'> <view class='c-flex-column' style='flex: 1;'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='goods-name'>女童夏装套装洋气装短袖阔腿裤子夏装套装</text> <text class='goods-name'>{{ item.name || '未知' }}</text>
</view> </view>
<text style='color: #999999;margin-top: 30rpx'> <text style='color: #999999;margin-top: 30rpx'>
2356235654 {{ item.code }}
</text> </text>
<view class='bottom-view c-flex-row'> <view class='bottom-sku-view c-flex-row'>
<text> <text>
紫色120cm x1 {{ item.checkedStock.colorName || '均色' }}{{ item.checkedStock.sizeName || '均码' }}
x{{ item.checkedStock.count }}
</text> </text>
<text>23.20</text> <text>¥{{ item.price }}</text>
</view> </view>
</view> </view>
</view> </view>
@@ -50,24 +60,24 @@
<view class='remark-view c-flex-row'> <view class='remark-view c-flex-row'>
<text>备注</text> <text>备注</text>
<input placeholder='请填写订单备注' /> <input placeholder='请填写订单备注' @input='orderBean!.remark = $event.detail.value' />
</view> </view>
</view> </view>
<view class='card-view'> <view class='card-view'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='card-view-title'>商品总价</text> <text class='card-view-title'>商品总价</text>
<text class='card-view-value'>29.90</text> <text class='card-view-value'>{{ orderBean?.totalPrice || 0 }}</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row' style='display: none'>
<text class='card-view-title'>运费</text> <text class='card-view-title'>运费</text>
<text class='card-view-value'>8</text> <text class='card-view-value'>0</text>
</view> </view>
<view class='c-flex-row' @click.stop='showCouponDialog'> <view class='c-flex-row' @click.stop='showCouponDialog'>
<text class='card-view-title'>优惠券 <text class='card-view-title'>优惠券
<text style='font-size: 22rpx;color: #F32B2B;'>已选最大优惠</text> <text style='font-size: 22rpx;color: #F32B2B;'>已选最大优惠</text>
</text> </text>
<view class='card-view-value' style='color: #F32B2B;margin: 0'>-20 <view class='card-view-value' style='color: #F32B2B;margin: 0'>-{{ checkedCoupon?.reduce || 0 }}
<image :src='assetsUrl("ic_arrow_right_gray.png")' /> <image :src='assetsUrl("ic_arrow_right_gray.png")' />
</view> </view>
</view> </view>
@@ -76,7 +86,7 @@
<view style='flex: 1' /> <view style='flex: 1' />
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='card-view-value'>共1件商品 合计 <text class='card-view-value'>共1件商品 合计
<text style='font-size: 34rpx;color:#F32B2B'>¥29.90</text> <text style='font-size: 34rpx;color:#F32B2B'>¥{{ totalPrice || 0 }}</text>
</text> </text>
</view> </view>
</view> </view>
@@ -85,48 +95,198 @@
<view class='payment-way c-flex-row' @click.stop='changePayment'> <view class='payment-way c-flex-row' @click.stop='changePayment'>
<text>支付方式</text> <text>支付方式</text>
<view>{{ paymentType === 0 ? '微信支付' : '余额' }} <view>{{ paymentType === 0 ? '微信支付' : '余额' }}
<image :src='assetsUrl("ic_arrow_right_gray.png")' /> <image style='display: none' :src='assetsUrl("ic_arrow_right_gray.png")' />
</view> </view>
</view> </view>
<view class='bottom-view c-flex-row'> <view class='bottom-view c-flex-row'>
<text>合计</text> <text>合计</text>
<text>29.90</text> <text>{{ totalPrice || 0 }}</text>
<text @click.stop='payment'>确认订单</text> <sqb-pay @navigateTo='navigateTo'
:return_url='buildSqbParams.return_url'
:total_amount='buildSqbParams.total_amount'
:terminal_sn='buildSqbParams.terminal_sn'
:client_sn='buildSqbParams.client_sn'
:subject='buildSqbParams.subject'
:subject_img='buildSqbParams.subject_img '
:merchant_name='buildSqbParams.merchant_name'
:notify_url='buildSqbParams.notify_url'
:sign='buildSqbParams.sign'>
<button class='confirm-order' @click='createOrder'>确认订单</button>
</sqb-pay>
</view> </view>
</view> </view>
</scroll-view>
<payment-dialog ref='paymentDialogRef' @change='args => paymentType=args' /> <payment-dialog ref='paymentDialogRef' @change='args => paymentType=args' />
<coupon-dialog ref='couponDialogRef' /> <coupon-dialog ref='couponDialogRef' @confirm='confirmCoupon' :order-price='orderBean?.totalPrice' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import PaymentDialog from '@/components/payment-dialog.vue'; import PaymentDialog from '@/components/payment-dialog.vue';
import CouponDialog from '@/pages/mall/subs/components/coupon-dialog.vue'; import CouponDialog from '@/pages/mall/subs/components/coupon-dialog.vue';
import { goPath } from '@/utils'; import { goPath, parseParameter, showToast, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { getPaymentList, orderCreate, overPayment } from '@/api/order';
import { CouponBean } from '@/api/user/types';
import { OrderBean } from '@/api/order/types';
import { useUserStore } from '@/store';
import { handlePayResult } from '@/utils/order';
import useShoppingCartStore from '@/store/modules/shoppingcart';
const userStore = useUserStore();
const { userInfo, terminalInfo, deliveryAddress } = storeToRefs(userStore);
const shoppingCartStore = useShoppingCartStore();
const couponDialogRef = ref(); const couponDialogRef = ref();
const paymentDialogRef = ref(); const paymentDialogRef = ref();
const paymentType = ref(0); const paymentType = ref(0);
const tabIndex = ref(0); const tabIndex = ref(0);
const checkedCoupon = ref<CouponBean>();
const orderBean = ref<OrderBean>();
onLoad((e) => {
orderBean.value = JSON.parse(decodeURIComponent(e?.orderBean));
createOrder();
});
const totalPrice = computed(() => {
return (orderBean?.value?.totalPrice || 0) - (checkedCoupon.value?.reduce || 0);
});
const buildSqbParams = computed(() => {
const params = sortASCII({
client_sn: orderBean.value?.id || '',
return_url: '/pages/common/payresult/index',
total_amount: Number(((totalPrice.value || 0) * 100).toFixed(2)),
terminal_sn: terminalInfo.value?.terminalSn,
subject: orderBean?.value?.orderGoods[0].name || '未知',
subject_img: orderBean?.value?.orderGoods[0].images || 'xxx',
merchant_name: terminalInfo.value?.companyName,
notify_url: 'https://www.baidu.com'
}, true);
return {
...params,
sign: hexMD5(parseParameter(params) + '&key=' + terminalInfo.value?.terminalKey).toUpperCase()
};
});
const changePayment = () => { const changePayment = () => {
console.log(paymentDialogRef); // paymentDialogRef.value.show();
paymentDialogRef.value.show();
}; };
const showCouponDialog = () => { const showCouponDialog = () => {
couponDialogRef.value.show(); couponDialogRef.value.show();
}; };
const payment = () => { const confirmCoupon = (item: CouponBean) => {
checkedCoupon.value = item;
createOrder();
};
const navigateTo = (e: any) => {
handlePayResult(orderBean.value?.id, e, {
onSuccess: (options:any) => {
console.log('pay success');
payment();
},
onFailure: (e:any) => {
console.error('pay onFailure ',e);
if(orderBean.value?.id) {
orderBean.value.id = '';
}
createOrder();
}
});
};
const createOrder = async () => {
if(!terminalInfo.value) {
showToast('当前店铺未配置收款渠道,请联系商家');
return;
}
//邮寄
if(tabIndex.value == 1 && !deliveryAddress.value.addrid) {
showToast('请选择收货地址');
return;
}
const params = {
// 'discount': 0,
// 'freePrice': 0,
'reducePrice': checkedCoupon.value == undefined ? 0 : checkedCoupon.value?.reduce,
'totalPrice': totalPrice.value,
// 'integral': 0,
// 'allowIntegral': 0,
// 'produceIntegralNumber': 0,
'couponIds': checkedCoupon.value?.id,
'remark': orderBean.value?.remark,
'address': tabIndex.value == 0 ? undefined : JSON.stringify(deliveryAddress.value),
'orderGoods': orderBean?.value?.orderGoods?.map(item => (
{
'goodsId': item.id,
'goodsCode': item.code,
'goodsNum': item.checkedStock.count,
'stockId': item.checkedStock.stockId,
'originPrice': item.price,
// 'consumePrice': item.price * ((userInfo.value?.levelEntity?.discount || 0) / 100),
'consumePrice': item.consumePrice,
'discount': userInfo.value?.levelEntity?.discount || 0
// 'discountOriginPrice': 0,
// 'produceIntegral': 0,
// 'priceModify': [0],
// 'offset': 0
}
))
};
if(orderBean.value?.id == undefined || orderBean.value?.id == '') {
await uni.showLoading();
const result = await orderCreate(params);
orderBean.value!.id = result.id;
uni.hideLoading();
}
//删除购物车已存在商品
orderBean?.value?.orderGoods?.forEach(item => {
const index = shoppingCartStore.getSameGoodsIndex(item.id, item.checkedStock.colorId, item.checkedStock.sizeId);
if(index >= 0) {
shoppingCartStore.delete(index);
}
});
};
const payment = async () => {
await uni.showLoading();
const paymentList = await getPaymentList(orderBean.value?.id || '');
const paymentParams = {
orderId: orderBean?.value?.id,
produceIntegralNumber: Math.round((orderBean?.value?.totalPrice || 0) + (orderBean?.value?.useGold || 0)),
transactionPrice: orderBean?.value?.totalPrice,
useGold: orderBean.value?.useGold || 0,
detailBos: [{
integralNumber: Math.floor(orderBean?.value?.totalPrice || 0),
onlinePayResult: '',
payName: '收钱吧',
payTypeId: paymentList.find((res: any) => res.type === 'ShouQianBa')?.id,
transactionPrice: Number(orderBean?.value?.totalPrice)
}]
};
await overPayment(paymentParams);
showToast('支付成功', {
icon: 'success',
complete: () => {
uni.navigateBack();
uni.hideLoading();
}
});
}; };
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
.content { .content {
padding: 20rpx 30rpx; padding: 20rpx 30rpx 200rpx 30rpx;
} }
.tab { .tab {
@@ -176,7 +336,7 @@ const payment = () => {
margin-left: 20rpx; margin-left: 20rpx;
margin-right: 20rpx; margin-right: 20rpx;
text:nth-of-type(1) { .status {
padding: 2rpx 10rpx; padding: 2rpx 10rpx;
font-size: 24rpx; font-size: 24rpx;
color: #F32B2B; color: #F32B2B;
@@ -248,7 +408,7 @@ const payment = () => {
flex: 1; flex: 1;
} }
.bottom-view { .bottom-sku-view {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin-top: 10rpx; margin-top: 10rpx;
@@ -261,8 +421,9 @@ const payment = () => {
} }
text:nth-of-type(2) { text:nth-of-type(2) {
font-size: 24rpx; font-size: 30rpx;
color: #333333; color: #333333;
font-weight: bold;
margin-right: 5rpx; margin-right: 5rpx;
} }
@@ -291,6 +452,7 @@ const payment = () => {
text-align: right; text-align: right;
font-size: 28rpx; font-size: 28rpx;
color: #333333; color: #333333;
z-index: 0;
} }
} }
} }
@@ -355,8 +517,7 @@ const payment = () => {
bottom: 0; bottom: 0;
left: 0; left: 0;
right: 0; right: 0;
padding: 12rpx 30rpx 78rpx 30rpx; padding: 20rpx 30rpx 78rpx 30rpx;
text:nth-of-type(1) { text:nth-of-type(1) {
font-weight: 400; font-weight: 400;
@@ -372,13 +533,18 @@ const payment = () => {
flex: 1; flex: 1;
} }
text:nth-of-type(3) { sqb-pay .confirm-order {
padding: 17rpx 45rpx; display: flex;
border-radius: 43rpx; padding: 0 45rpx;
border: 1rpx solid #F32B2B; height: 80rpx;
border: 2rpx solid #F32B2B;
font-weight: bold; font-weight: bold;
font-size: 30rpx; font-size: 30rpx;
color: #F32B2B; color: #F32B2B;
align-items: center;
justify-content: center;
background: #FFFFFF;
border-radius: 40rpx;
} }
} }
</style> </style>

View File

@@ -3,32 +3,69 @@
<view class='search-view'> <view class='search-view'>
<view class='search-input'> <view class='search-input'>
<image :src='assetsUrl("ic_search.png")' /> <image :src='assetsUrl("ic_search.png")' />
<input placeholder='输入名称、款号搜索' /> <input placeholder='输入名称、款号搜索' @input='bindInput' />
</view> </view>
</view> </view>
<scroll-view class='goods-list' :scroll-y='true' type='custom'> <scroll-view class='goods-list'>
<grid-view type='masonry' :cross-axis-count='2'> <grid-view :cross-axis-count='2'>
<view v-for='(item, index) in goodsList' :key='index' class='goods-item' <view v-for='(item, index) in goodsList' :key='index' class='goods-item'
@click.stop='goPath(`/pages/mall/subs/goods/goods-detail?goodsId=${item.goodsId}`)'> @click.stop='goPath(`/pages/mall/subs/goods/detail?goodsId=${item.goodsId}`)'>
<image class='goods-image' :src='item.images' /> <image class='goods-image' :src='item.images||defaultImage' />
<text class='goods-name'>{{ item.goodsName }}</text> <text class='goods-name'>{{ item.goodsName || '未知' }}</text>
<text class='goods-price'>¥{{ item.price }}</text> <text class='goods-price'>¥{{ (item.price * userInfo.userDiscount).toFixed(2) }}
<image class='add-image' :src='assetsUrl("ic_add_goods.png")' /> <text v-if='userInfo.userDiscount>0&&userInfo.userDiscount<1'
style='text-decoration: line-through;color: #999999;font-size: 25rpx'>¥{{ item.price }}
</text>
</text>
<image class='add-image' :src='assetsUrl("ic_add_goods.png")' @click.stop='addShoppingCart(item)' />
</view> </view>
</grid-view> </grid-view>
</scroll-view> </scroll-view>
</view> </view>
<sku-dialog ref='skuDialogRef' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import SkuDialog from '@/components/sku-dialog.vue';
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import { goPath } from '@/utils'; import { goPath } from '@/utils';
import { getGoodsList } from '@/api/goods';
import useShoppingCartStore from '@/store/modules/shoppingcart';
import { GoodsBean } from '@/api/goods/types'; import { GoodsBean } from '@/api/goods/types';
import { useUserStore } from '@/store';
const goodsList = ref<GoodsBean[]>([1, 2, 3, 4, 1, 2, 2, 2]); const skuDialogRef = ref();
const shoppingCartStore = useShoppingCartStore();
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const goodsList = ref<GoodsBean[]>([]);
onLoad((e) => {
});
const bindInput = async (e: any) => {
const { rows } = await getGoodsList({
page: {
pageNum: 1,
pageSize: 100,
bean: {
keyword: e.detail.value,
typeIds: []
}
}
});
goodsList.value = rows;
};
const addShoppingCart = (goodsBean: GoodsBean) => {
skuDialogRef.value.show(goodsBean.goodsId, (e: GoodsBean) => {
shoppingCartStore.save(e);
});
};
</script> </script>
@@ -77,14 +114,14 @@ const goodsList = ref<GoodsBean[]>([1, 2, 3, 4, 1, 2, 2, 2]);
.goods-image { .goods-image {
border-radius: 10rpx; border-radius: 10rpx;
width: 100%; width: 100%;
max-height: 200rpx; max-height: 400rpx;
} }
.goods-name { .goods-name {
font-size: 28rpx; font-size: 28rpx;
font-weight: 400; font-weight: 400;
color: #333333; color: #333333;
-webkit-line-clamp: 2; -webkit-line-clamp: 1;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
margin-top: 10rpx; margin-top: 10rpx;

View File

@@ -1,29 +1,41 @@
<template> <template>
<view class='content'> <view class='content'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='manage' @click.stop='isEditMode = !isEditMode'>管理</text> <text class='manage' @click.stop='isEditMode = !isEditMode' :style='isEditMode?"color:#F32B2B":"color:#333333"'>
{{ isEditMode ? '退出管理' : '管理' }}
</text>
</view> </view>
<view class='card-view'> <view class='card-view' v-if='totalCount>0'>
<scroll-view> <scroll-view>
<view class='c-flex-column' v-for='(item, index) in [1,2,3,5]' :key='index'> <view class='c-flex-column' v-for='(item, index) in shoppingCartList' :key='index'>
<view class='c-flex-row'> <view class='c-flex-row'>
<image v-show='isEditMode' class='checkbox' :src='assetsUrl("ic_checkbox_active_red.png")' /> <image class='checkbox'
<image class='goods-image' :src='assetsUrl("test_bg.png")' /> :src='assetsUrl(item.checked?"ic_checkbox_active_red.png":"ic_checkbox_normal.png")'
@click.stop='handleCheck(item)' />
<image class='goods-image' :src='item.images||defaultImage' />
<view class='c-flex-column'> <view class='c-flex-column'>
<text>女童夏装套装洋气装短袖阔腿裤子夏装夏装套装 <text>{{ item.name }}</text>
<view class='c-flex-row'>
<view class='sku-view' @click.stop='showSkuDialog(index)'>
<text>{{ item?.checkedStock?.colorName||'均色' }}{{ item?.checkedStock?.sizeName||'均码' }}
x{{ item?.checkedStock?.count }}
</text> </text>
<view class='sku-view'>
<text>紫色120cm x1</text>
<image :src='assetsUrl("ic_arrow_down_gray.png")' /> <image :src='assetsUrl("ic_arrow_down_gray.png")' />
</view> </view>
<text>29.90</text> </view>
<text style='margin-top: 50rpx'>
¥{{ (item?.price * userInfo.userDiscount).toFixed(2) }}
<text v-if='userInfo.userDiscount>0&&userInfo.userDiscount<1'
style='text-decoration: line-through;color: #999999;font-size: 25rpx'>¥{{ item?.price }}
</text>
</text>
<view class='count-change-view c-flex-row'> <view class='count-change-view c-flex-row'>
<view class='count-image' @click.stop='countChange(false)'> <view class='count-image' @click.stop='countChange(index,false)'>
<image :src='assetsUrl("ic_reduce.png")' /> <image :src='assetsUrl("ic_reduce.png")' />
</view> </view>
<text>{{ goodsCount }}</text> <text>{{ item?.checkedStock?.count || 0 }}</text>
<view class='count-image' @click.stop='countChange(true)'> <view class='count-image' @click.stop='countChange(index,true)'>
<image :src='assetsUrl("ic_plus.png")' /> <image :src='assetsUrl("ic_plus.png")' />
</view> </view>
</view> </view>
@@ -32,35 +44,120 @@
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<u-empty v-else text='暂无数据' margin-top='100' />
<view class='bottom-view c-flex-row'> <view class='bottom-view c-flex-row'>
<view class='c-flex-row' @click.stop='checkedAll = !checkedAll'> <view class='c-flex-row' @click.stop='handleAllCheck'>
<image :src='assetsUrl(checkedAll?"ic_checkbox_active.png":"ic_checkbox_normal.png")' /> <image :src='assetsUrl(checkedAll?"ic_checkbox_active.png":"ic_checkbox_normal.png")' />
<text>全选</text> <text>全选</text>
</view> </view>
<view style='flex: 1' /> <view style='flex: 1' />
<text v-if='isEditMode' class='settlement' @click.stop='deleteShoppingCart'>删除</text>
<view v-else class='c-flex-row'>
<text>合计 <text>合计
<text>29.90</text> <text>{{ totalPayPrice.toFixed(2) }}</text>
</text> </text>
<text class='settlement' @click.stop='settlement'>结算</text> <text class='settlement' @click.stop='settlement'>结算</text>
</view> </view>
</view> </view>
</view>
<sku-dialog ref='skuDialogRef' :bean='temporaryGoodsBean' :exists='temporaryStockBean' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import useShoppingCartStore from '@/store/modules/shoppingcart';
import { goPath, showToast } from '@/utils';
import { GoodsBean, StockBean } from '@/api/goods/types';
import SkuDialog from '@/components/sku-dialog.vue';
import { ref } from 'vue';
import { useUserStore } from '@/store';
const isEditMode = ref(false); const isEditMode = ref(false);
const checkedAll = ref(false); const checkedAll = ref(false);
const goodsCount = ref(1);
const countChange = (isPlus: Boolean) => { const shoppingCartStore = useShoppingCartStore();
const { shoppingCartList, totalCount } = storeToRefs(shoppingCartStore);
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const skuDialogRef = ref();
const temporaryGoodsBean = ref<GoodsBean>();
const temporaryStockBean = ref<StockBean>();
const totalPayPrice = computed(() => {
return shoppingCartList.value.filter(res => res.checked).reduce((a, b) => a + b.consumePrice * b.checkedStock.count, 0) || 0;
});
const handleCheck = (item: any) => {
item.checked = !item.checked;
if(!item.checked) {
checkedAll.value = false;
} else {
checkedAll.value = shoppingCartList.value.every(res => res.checked);
}
};
const handleAllCheck = () => {
checkedAll.value = !checkedAll.value;
shoppingCartList.value.forEach(res => {
res.checked = checkedAll.value;
});
};
const deleteShoppingCart = () => {
shoppingCartStore.deleteIfChecked();
checkedAll.value = false;
isEditMode.value = false;
};
const showSkuDialog = (index: number) => {
new Promise((resolve, _) => {
temporaryGoodsBean.value = shoppingCartList.value[index];
temporaryStockBean.value = shoppingCartList.value[index].checkedStock;
resolve(temporaryGoodsBean.value);
}).then((res: any) => {
skuDialogRef.value.show(res.id, (e: GoodsBean) => {
//重新选择sku后判断当前购物中是否存在相同sku的商品
const existsIndex = shoppingCartStore.getSameGoodsIndex(res?.id || '', e.checkedStock.colorId, e.checkedStock.sizeId);
//不存在则更新当前商品sku
if(existsIndex < 0) {
shoppingCartStore.updateStock(index, e.checkedStock);
}
//如果已存在,则更新已存在商品数量,并删除当前商品
else {
shoppingCartStore.updateCount(existsIndex, e.checkedStock.count - (temporaryGoodsBean?.value?.checkedStock?.count || 0));
if(existsIndex != index) {
shoppingCartStore.delete(index);
}
}
});
});
};
const countChange = (index: number, isPlus: Boolean) => {
if(isPlus) {
shoppingCartStore.updateCount(index, 1);
} else {
if(shoppingCartList.value[index].checkedStock.count > 1) {
shoppingCartStore.updateCount(index, -1);
}
}
}; };
const settlement = () => { const settlement = () => {
if(shoppingCartList.value.filter(res => res.checked).length == 0) {
showToast('请选择商品');
return;
}
const orderBean = {
totalPrice: totalPayPrice.value,
orderGoods: shoppingCartList.value.filter(res => res.checked)
};
goPath(`/pages/mall/subs/order/order-confirm?orderBean=${encodeURIComponent(JSON.stringify(orderBean))}`);
}; };
</script> </script>
@@ -80,10 +177,10 @@ const settlement = () => {
} }
.card-view { .card-view {
margin: 30rpx; margin: 30rpx 30rpx 180rpx 30rpx;
border-radius: 10rpx; border-radius: 10rpx;
background: #FFFFFF; background: #FFFFFF;
padding: 10rpx 20rpx 20rpx 17rpx; padding: 10rpx 20rpx 10rpx 17rpx;
.c-flex-column:nth-of-type(1) { .c-flex-column:nth-of-type(1) {
margin: 20rpx 0; margin: 20rpx 0;
@@ -110,16 +207,16 @@ const settlement = () => {
text:nth-of-type(1) { text:nth-of-type(1) {
font-weight: 400; font-weight: 400;
font-size: 28rpx; font-size: 30rpx;
color: #333333; color: #333333;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden;
} }
.sku-view { .sku-view {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
width: 210rpx;
position: relative; position: relative;
background: #F2F2F2; background: #F2F2F2;
margin-top: 10rpx; margin-top: 10rpx;
@@ -211,7 +308,6 @@ const settlement = () => {
text:nth-of-type(1) { text:nth-of-type(1) {
font-size: 30rpx; font-size: 30rpx;
color: #333333;
text { text {
font-weight: bold; font-weight: bold;

View File

@@ -3,30 +3,30 @@
<view class='user-info-view'> <view class='user-info-view'>
<image <image
class='image-bg' class='image-bg'
:src=" assetsUrl('test_bg.png')" :src='companyConfigInfo.userbgcover'
/> />
<view class='top-row'> <view class='top-row'>
<image :src='userInfo.image' /> <image :src='userInfo?.image||defaultAvatar' />
<view class='u-flex-column'> <view class='u-flex-column'>
<text>{{ userInfo.nickName }}</text> <text>{{ userInfo?.nickName || '未登录' }}</text>
<text>{{ userInfo.telephone }}</text> <text>{{ userInfo?.telephone }}</text>
</view> </view>
<image :src='assetsUrl("ic_qrcode_white.png")' /> <image :src='assetsUrl("ic_qrcode_white.png")' @click.stop='gotoPath("qrcode")' />
</view> </view>
<view class='bottom-row'> <view class='bottom-row'>
<view @click.stop='gotoPath("/pages/mine/subs/integral/index")'> <view @click.stop='gotoPath("/pages/mine/subs/integral/index")'>
<text>{{ userInfo.integration }}</text> <text>{{ userInfo?.integration || 0 }}</text>
<text>积分</text> <text>积分</text>
</view> </view>
<view @click.stop='gotoPath("/pages/mine/subs/recharge/index")'> <view @click.stop='gotoPath("/pages/mine/subs/recharge/index")'>
<text>{{ userInfo.balance }}</text> <text>{{ userInfo?.balance || 0 }}</text>
<text>余额</text> <text>余额</text>
</view> </view>
<view @click.stop='gotoPath("/pages/mine/subs/coupon/index")'> <view @click.stop='gotoPath("/pages/mine/subs/coupon/index")'>
<text>3</text> <text>{{ userInfo?.couponsCount || 0 }}</text>
<text>优惠券</text> <text>优惠券</text>
</view> </view>
</view> </view>
@@ -34,7 +34,7 @@
<view class='integral-add-view'> <view class='integral-add-view'>
<text>将会员卡放入微信卡包及时了解积分变动</text> <text>将会员卡放入微信卡包及时了解积分变动</text>
<text>去添加</text> <text @click.stop='openCard'>去添加</text>
<image :src='assetsUrl("ic_arrow_right_yellow.png")' /> <image :src='assetsUrl("ic_arrow_right_yellow.png")' />
</view> </view>
@@ -45,7 +45,7 @@
<view v-for='(item,index) in orderActionList' :key='index' @click.stop='gotoPath(item.path)'> <view v-for='(item,index) in orderActionList' :key='index' @click.stop='gotoPath(item.path)'>
<image :src='item.icon' /> <image :src='item.icon' />
<text>{{ item.title }}</text> <text>{{ item.title }}</text>
<text v-if='item.amount>0'>{{ item.amount }}</text> <text v-if='index==0&&unPaidOrderCount>0'>{{ unPaidOrderCount }}</text>
</view> </view>
</view> </view>
@@ -59,34 +59,40 @@
<text>{{ item.title }}</text> <text>{{ item.title }}</text>
</view> </view>
</view> </view>
</view>
</view>
<!-- 支付密码设置弹框 -->
<pay-password-dialog ref="payPasswordDialogRef" />
<official-account-dialog ref='officialAccountDialogRef' /> <official-account-dialog ref='officialAccountDialogRef' />
</template> </template>
<script setup lang='ts'> <script setup lang='ts'>
import { useUserStore } from '@/store'; import { assetsUrl, defaultAvatar } from '@/utils/assets';
import { assetsUrl } from '@/utils/assets'; import { goLogin, goPath, isLogin } from '@/utils';
import { goPath } from '@/utils'; import { isPending } from '@/utils/order';
import { getCardLink } from '@/api/user';
import { getOrderList } from '@/api/order';
import OfficialAccountDialog from '@/components/official-account-dialog.vue'; import OfficialAccountDialog from '@/components/official-account-dialog.vue';
import PayPasswordDialog from '@/components/pay-password-dialog.vue';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const officialAccountDialogRef = ref(); const officialAccountDialogRef = ref();
const payPasswordDialogRef = ref<any>(null);
const orderActionList = ref([{ const orderActionList = ref([{
title: '进行中', title: '未支付',
icon: assetsUrl('ic_order_progressing.png'), icon: assetsUrl('ic_order_progressing.png'),
path: '/pages/mine/subs/order/index', path: '/pages/mine/subs/order/index?index=1'
amount: 10
}, { }, {
title: '结束', title: '支付',
icon: assetsUrl('ic_order_finish.png'), icon: assetsUrl('ic_order_finish.png'),
path: '/pages/mine/subs/order/index?index=1', path: '/pages/mine/subs/order/index?index=2'
amount: 0
}, { }, {
title: '全部订单', title: '全部订单',
icon: assetsUrl('ic_order_all.png'), icon: assetsUrl('ic_order_all.png'),
path: '/pages/mine/subs/order/index?index=2', path: '/pages/mine/subs/order/index?index=0'
amount: 0
}]); }]);
const serviceList = [ const serviceList = [
@@ -112,7 +118,12 @@ const serviceList = [
}, { }, {
title: '联系商家', title: '联系商家',
icon: assetsUrl('ic_member_service_contact.png'), icon: assetsUrl('ic_member_service_contact.png'),
path: '' path: '/pages/mine/subs/contact/index'
},
{
title: '支付密码',
icon: '/static/images/ic_member_pay_password.png',
path: 'pay_password'
} }
// ,{ // ,{
// title: '推广中心', // title: '推广中心',
@@ -121,24 +132,72 @@ const serviceList = [
// } // }
]; ];
const title = ref<string>();
title.value = import.meta.env.VITE_APP_TITLE;
const store = useUserStore(); const store = useUserStore();
const { userInfo } = storeToRefs(store); const { userInfo, companyInfo, companyConfigInfo } = storeToRefs(store);
const gotoPath = (path: string) => { const cardLink = ref('');
if(path === 'follow_official_account') { const unPaidOrderCount = ref(0);
showOfficialAccountDialog();
} else { onLoad(() => {
goPath(path); // if(!isLogin()) {
// goLogin();
// return;
// }
});
onShow(async () => {
if(isLogin()) {
const { list } = await getOrderList({
pageNum: 1,
pageSize: 9999,
obj: { payStatus: 1 }
});
unPaidOrderCount.value = list.filter((item: any) => isPending(item))?.length || 0;
const { cardurl } = await getCardLink();
cardLink.value = cardurl;
await userStore.getProfile();
} }
});
const openCard = () => {
// const url = 'https://mp.weixin.qq.com/bizmall/activatemembercard?action=preshow&&encrypt_card_id=LI%2FNyDp0x3z8XXorvvrHzSR4VUPa2vlioBg2xkDT3HqybiAFNsNgjH7pBpyKGrSA&outer_str=1702887425732870145&biz=MzU2MTg5MjgxMg%3D%3D#wechat_redirect';
goPath(`/pages/common/webview/index?url=${encodeURIComponent(cardLink.value)}`);
// uni.navigateToMiniProgram({ appId: 'wxeb490c6f9b154ef9' });
// uni.addCard({
// cardList: [
// {
// cardId: 'pzCtO1NU8FDcF3v09_Q76-7ZuN8I',
// cardExt: JSON.stringify({ 'code': '12345', openId: 'wxc480826360455685', 'timestamp': Date.now(), 'signature': '' })
// }
// ]
// });
}; };
const showOfficialAccountDialog = () => { const showOfficialAccountDialog = () => {
officialAccountDialogRef.value.show(); officialAccountDialogRef.value.show();
}; };
const showPayPasswordDialog = () => {
payPasswordDialogRef.value?.show();
};
const gotoPath = (path: string) => {
if(path === 'follow_official_account') {
showOfficialAccountDialog();
} else if(path === 'pay_password') {
showPayPasswordDialog();
} else if(path === 'qrcode') {
uni.switchTab({
url: '/pages/qrcode/index'
});
} else if(path === 'call') {
uni.makePhoneCall({
phoneNumber: companyInfo.value.telphone
});
} else {
goPath(path, true);
}
};
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -158,6 +217,7 @@ const showOfficialAccountDialog = () => {
width: 100%; width: 100%;
height: 100%; height: 100%;
position: absolute; position: absolute;
border-radius: 8rpx;
} }
.top-row { .top-row {

View File

@@ -1,33 +1,34 @@
<template> <template>
<view class='card-view'> <view class='card-view' @click.stop='emits("onChecked",item)'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text>默认</text> <text class='status' v-if='item?.defaultstatus==1'>默认</text>
<text> <text>
黄先生 {{ item?.name }}
<!-- {{ item.name }}-->
</text> </text>
<text> <text>
13xxxxxx8900 {{ item?.mobile }}
<!-- {{ item.mobile }}-->
</text> </text>
</view> </view>
<text class='address'> <text class='address'>
湖南省 长沙市 雨花区 地址地址地址 {{ item?.addr }}
{{ item.address }}
</text> </text>
<view class='btn-view c-flex-row'> <view class='btn-view c-flex-row'>
<text>编辑</text> <text @click.stop='emits("onEdit",item)'>编辑</text>
<text>删除</text> <text @click.stop='emits("onDelete",item)'>删除</text>
</view> </view>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { PropType } from 'vue';
defineProps({ defineProps({
item: <any>[] item: Object as PropType<{ name: string, mobile: string, addr: string, defaultstatus: number }>
}); });
const emits = defineEmits(['onEdit', 'onDelete', 'onChecked']);
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -36,7 +37,7 @@ defineProps({
padding: 38rpx 30rpx 30rpx 34rpx; padding: 38rpx 30rpx 30rpx 34rpx;
view:nth-of-type(1) { view:nth-of-type(1) {
text:nth-of-type(1) { .status {
border-radius: 5rpx; border-radius: 5rpx;
border: 1rpx solid #F32B2B; border: 1rpx solid #F32B2B;
color: #F32B2B; color: #F32B2B;

View File

@@ -2,20 +2,22 @@
<view class='content'> <view class='content'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text>收货人</text> <text>收货人</text>
<input placeholder='请输入收货人姓名' /> <input placeholder='请输入收货人姓名' v-model='params.name' @input='bindConsignee' />
</view> </view>
<view class='divider' /> <view class='divider' />
<view class='c-flex-row'> <view class='c-flex-row'>
<text>手机号</text> <text>手机号</text>
<input placeholder='请输入收货人手机号' maxlength='11' /> <input placeholder='请输入收货人手机号' v-model='params.mobile' @input='bindMobile' :maxlength='11' />
</view> </view>
<view class='divider' /> <view class='divider' />
<view class='c-flex-row detail-row'> <view class='c-flex-row detail-row'>
<text>详细地址</text> <text>详细地址</text>
<view class='c-flex-column'> <view class='c-flex-column'>
<textarea placeholder='详细地址:如街道、门牌\n号、楼栋号、单元室等' /> <textarea placeholder='详细地址:如街道、门牌\n号、楼栋号、单元室等'
v-model='params.addr'
@input='bindAddress' />
<view class='tips-view c-flex-row'> <view class='tips-view c-flex-row'>
<image :src='assetsUrl("ic_tips.png")' /> <image :src='assetsUrl("ic_tips.png")' />
<text>记得完善门牌号</text> <text>记得完善门牌号</text>
@@ -27,7 +29,8 @@
<view class='c-flex-column'> <view class='c-flex-column'>
<text style='width: auto'>设为默认地址</text> <text style='width: auto'>设为默认地址</text>
<text style='width: auto'>提醒每次下单会默认推荐使用该地址</text> <text style='width: auto'>提醒每次下单会默认推荐使用该地址</text>
<switch color='#F32B2B' /> <!-- @change='ev=>{params.defaultstatus = ev.detail.value}'-->
<switch color='#F32B2B' @change='bindStatus' :checked='params.defaultstatus == 1' />
</view> </view>
<view class='bottom-button-view'> <view class='bottom-button-view'>
@@ -39,25 +42,65 @@
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { addressCreate } from '@/api/user'; import { addressCreate, addressUpdate } from '@/api/user';
const save = () => { const params = ref<{
addressCreate( addrid: string,
{ name: string,
bean: { mobile: string,
'name': '高昂', zonecode: string,
'mobile': '15475655487', addr: string,
'zonecode': '420101001001', defaultstatus: number
'addr': '肯德基挂', }>({
'defaultstatus': 1 addrid: '',
name: '',
mobile: '',
zonecode: '',
addr: '',
defaultstatus: 0
});
onLoad(async (e: any) => {
if(e.bean) {
params.value = JSON.parse(decodeURIComponent(e.bean));
} }
});
const bindConsignee = (e: any) => {
params.value.name = e.detail.value;
};
const bindMobile = (e: any) => {
params.value.mobile = e.detail.value;
};
const bindAddress = (e: any) => {
params.value.addr = e.detail.value;
};
const bindStatus = (e: any) => {
params.value.defaultstatus = e.detail.value == true ? 1 : 0;
};
const save = async () => {
let result = false;
if(params.value.addrid) {
result = await addressUpdate(params.value);
} else {
result = await addressCreate(
params.value
);
} }
).then(() => {
if(result === true) {
uni.showToast({ uni.showToast({
title: '保存成功', title: '保存成功',
icon: 'none' icon: 'none',
}); success(result) {
uni.navigateBack();
}
}); });
}
}; };
</script> </script>

View File

@@ -1,7 +1,11 @@
<template> <template>
<view class='content'> <view class='content'>
<scroll-view :scroll-y='true'> <scroll-view :scroll-y='true'>
<item v-for='(item,index) in addressList' :key='index' :item='item' /> <item v-for='(item,index) in addressList' :key='index' :item='item'
@on-checked='onChecked(item)'
@on-edit='args => goPath(`create?bean=${encodeURIComponent(JSON.stringify(args))}`)'
@on-delete='args => {handleDelete(args)}' />
<u-empty v-if='addressList.length === 0' text='暂无数据' margin-top='100' />
</scroll-view> </scroll-view>
<view class='bottom-button-view'> <view class='bottom-button-view'>
@@ -14,30 +18,54 @@
import Item from './components/item.vue'; import Item from './components/item.vue';
import { goPath } from '@/utils'; import { goPath } from '@/utils';
import { getAddressList } from '@/api/user'; import { addressDelete, getAddressList } from '@/api/user';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const addressList = ref<{ const addressList = ref<{
name: string, name: string,
mobile: string, mobile: string,
address: string, addr: string,
status: number status: number,
}[]>([{ defaultstatus: number,
name: '黄先生', }[]>([]);
mobile: '13xxxxxx8900',
status: 1
}, {
name: '李先生',
mobile: '13xxxxxx8900',
status: 0
}]);
onLoad((e) => { onLoad((e) => {
// fetchAddressList();
});
onShow(() => {
fetchAddressList(); fetchAddressList();
}); });
const fetchAddressList = async () => { const fetchAddressList = async () => {
const { data } = await getAddressList(); addressList.value = await getAddressList();
addressList.value = data; };
const handleDelete = async (item: any) => {
uni.showModal(
{
title: '提示',
content: '确定删除该地址吗',
success: async (res) => {
if(res.confirm) {
const result = await addressDelete(item?.addrid);
if(result) {
await fetchAddressList();
}
}
}
}
);
};
const onChecked = (e: any) => {
const pages = getCurrentPages();
if(pages.length >= 2 && pages[pages.length - 2].route?.includes('pages/mall/subs/order/order-confirm')) {
userStore.setDeliveryAddress(e);
uni.navigateBack();
}
}; };
</script> </script>

View File

@@ -0,0 +1,61 @@
<template>
<view class='card-view'>
<view class='c-flex-row'>
<text class='key'>姓名</text>
<text class='value'>{{ companyConfigInfo?.contactname}}</text>
</view>
<view class='divider' />
<view class='c-flex-row'>
<text class='key'>手机号</text>
<text class='value'>{{companyConfigInfo?.contacttelephone}}</text>
<image :src='assetsUrl("ic_contact_merchant.png")' @click.stop='call' />
</view>
</view>
</template>
<script lang='ts' setup>
import { useUserStore } from '@/store';
import { assetsUrl } from '@/utils/assets';
const { companyConfigInfo } = storeToRefs(useUserStore());
const call = () => {
uni.makePhoneCall({
phoneNumber: companyConfigInfo.value.contacttelephone
});
};
</script>
<style lang='scss' scoped>
.card-view {
background-color: #fff;
padding: 30rpx 20rpx;
margin: 24rpx;
border-radius: 10rpx;
.divider {
margin: 30rpx 0;
}
.c-flex-row {
.key {
font-size: 30rpx;
color: #7E7E7E;
width: 120rpx;
}
.value {
display: flex;
font-weight: 400;
font-size: 30rpx;
color: #333333;
flex: 1;
}
image {
width: 39rpx;
height: 41rpx;
margin-right: 10rpx;
}
}
}
</style>

View File

@@ -1,16 +1,16 @@
<template> <template>
<view class='coupon-content'> <view class='coupon-content'>
<image v-if='item.status==0' :src='assetsUrl("bg_coupon.png")' /> <image v-if='item?.status==0' :src='assetsUrl("bg_coupon.png")' />
<image v-else-if='item.status==1' :src='assetsUrl("bg_coupon_expired.png")' /> <image v-else-if='item?.status==1||item?.status==2' :src='assetsUrl("bg_coupon_expired.png")' />
<view class='left-content accent-text-color' :class='{"left-content-disabled": item.status==1}'> <view class='left-content accent-text-color' :class='{"left-content-disabled": item?.status!=0}'>
<text>{{ item.price }}</text> <text>{{ item?.reduce }}</text>
<text>{{ item.threshold }}元可用</text> <text>{{ item?.threshold }}元可用</text>
</view> </view>
<view class='right-content accent-text-color' :class='{"right-content-disabled": item.status==1}'> <view class='right-content accent-text-color' :class='{"right-content-disabled": item?.status!=0}'>
<text>{{ item?.title }}</text> <text>{{ item?.name }}</text>
<text>有效期至{{ item.useTime }}</text> <text>有效期至{{ dayjs(item?.endTime).format('YYYY-MM-DD') }}</text>
<text class='btn-text' :class='{"btn-text-disabled": item.status==1}'> <text class='btn-text' :class='{"btn-text-disabled": item?.status!=0}' @click.stop='goPath("/pages/mall/index")'>
立即使用 立即使用
</text> </text>
</view> </view>
@@ -21,9 +21,13 @@
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import dayjs from 'dayjs';
import { PropType } from 'vue';
import { CouponBean } from '@/api/user/types';
import { goPath } from '@/utils';
defineProps({ defineProps({
item: Object item: Object as PropType<CouponBean>
}); });
</script> </script>
@@ -76,7 +80,7 @@ defineProps({
position: relative; position: relative;
flex: 1; flex: 1;
justify-content: center; justify-content: center;
margin-left: 100rpx; margin-left: 65rpx;
text:nth-of-type(1) { text:nth-of-type(1) {
font-size: 30rpx; font-size: 30rpx;
@@ -113,6 +117,7 @@ defineProps({
.right-content-disabled { .right-content-disabled {
@extend .right-content; @extend .right-content;
color: #C2C2C2; color: #C2C2C2;
text:nth-of-type(2) { text:nth-of-type(2) {
color: #C2C2C2; color: #C2C2C2;
} }

View File

@@ -1,79 +1,73 @@
<template> <template>
<tabbar :titles='["全部","未使用","已使用"]' :indicator-color='"#D95554"' @change='args => {}' /> <tabbar :titles='["全部","未使用","已使用"]' :indicator-color='"#D95554"' @change='tabChange' />
<scroll-view :scroll-y='true' class='content'> <u-list @scrolltolower='onLoadMore'>
<coupon-item v-for='item in coupons' :item='item' /> <coupon-item v-for='item in couponList' :item='item' :key='item.id' />
</scroll-view> <u-loadmore v-if='couponList.length>0' :status='loadingStatus' />
<u-empty v-if='couponList.length === 0' text='暂无优惠券' marginTop='100' />
</u-list>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { ref } from 'vue';
import CouponItem from './components/coupon-item.vue'; import CouponItem from './components/coupon-item.vue';
import { getCouponList } from '@/api/user'; import { getCouponList } from '@/api/user';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { getCompanyId } from '@/utils';
import { CouponBean } from '@/api/user/types';
const store = useUserStore(); const store = useUserStore();
const { userInfo } = storeToRefs(store); const { userInfo } = storeToRefs(store);
const coupons = ref([{ const couponStatus = ref<number | any>(undefined);
id: 1, const couponList = ref<CouponBean[]>([]);
title: '优惠券', const currentPageNum = ref(1);
desc: '满100减10', const loadingStatus = ref('loading');
price: 100,
threshold: 10,
priceDesc: '满100减10',
useTime: '2021-12-31',
status: 1,
useStatus: '未使用'
}, {
id: 1,
title: '优惠券',
desc: '满100减10',
price: 200,
threshold: 80,
priceDesc: '满100减10',
useTime: '2021-01-01',
status: 1,
useStatus: '未使用'
}, {
id: 1,
title: '优惠券',
desc: '满100减10',
price: 300,
threshold: 60,
priceDesc: '满100减10',
useTime: '2023-12-31',
status: '0',
useStatus: '未使用'
}, {
id: 1,
title: '优惠券',
desc: '满100减10',
price: 50,
threshold: 50,
priceDesc: '满100减10',
useTime: '2021-10-31',
status: 0,
useStatus: '未使用'
}, {
id: 1,
title: '优惠券',
desc: '满1000减30',
price: 200,
threshold: 30,
priceDesc: '满100减10',
useTime: '2024-12-31',
status: 0,
useStatus: '未使用'
}]);
onLoad(async () => { onLoad(async () => {
const { data } = await getCouponList({ await fetchData();
obj: {
memberId: userInfo.value?.id, status: 0
}, pageNum: 1, pageSize: 1000
}); });
coupons.value = data.list;
const tabChange = (index: number) => {
switch (index) {
case 0:
couponStatus.value = undefined;
break;
case 1:
couponStatus.value = 0;
break;
case 2:
couponStatus.value = 1;
break;
}
currentPageNum.value = 1;
fetchData();
};
const fetchData = async (refresh: boolean = true) => {
loadingStatus.value = 'loading';
if(!refresh) {
currentPageNum.value += 1;
}
const { list } = await getCouponList({
companyId: getCompanyId(),
memberId: userInfo.value?.id,
status: couponStatus.value,
pageNum: currentPageNum.value,
pageSize: 20
}); });
if(refresh) {
couponList.value = list;
} else {
couponList.value = couponList.value.concat(list);
}
loadingStatus.value = 'no';
};
const onLoadMore = () => {
fetchData(false);
};
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>

View File

@@ -1,23 +1,25 @@
<template> <template>
<view class='card-view'> <view class='card-view'>
<text class='category-title'>2023-02-12</text> <!-- <text class='category-title'>2023-02-12</text>-->
<view class='item c-flex-column' v-for='(item,index) in 3' :key='index'> <!-- <view class='item c-flex-column' v-for='(item,index) in 3' :key='index'>-->
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='primary-text-color' style='flex: 1'>消费</text> <text class='primary-text-color' style='flex: 1'>{{item?.remark}}</text>
<text class='accent-text-color'>-30</text> <text class='accent-text-color'>{{item?.value}}</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='secondary-text-color' style='flex: 1'>2023-06-27 15:02:11</text> <text class='secondary-text-color' style='flex: 1'>{{ item?.createtime }}</text>
<text style='color: #999999'>会员积分30.00</text> <text style='color: #999999'>会员积分30.00</text>
</view> </view>
<view class='divider' style='margin-top: 20rpx'/> <!-- <view class='divider' style='margin-top: 20rpx'/>-->
</view> <!-- </view>-->
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { IntegralBean } from '@/api/user/types';
defineProps({ defineProps({
item: Object item: Object as PropType<IntegralBean>
}); });
</script> </script>
@@ -26,7 +28,7 @@ defineProps({
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 16rpx 23rpx 20rpx 30rpx; padding: 16rpx 23rpx 20rpx 30rpx;
margin: 20rpx 0; margin: 10rpx 0;
} }
.category-title { .category-title {

View File

@@ -1,20 +1,49 @@
<template> <template>
<view class='content'> <view class='content'>
<view class='top-view c-flex-column'> <view class='top-view c-flex-column'>
<text>2520</text> <text>{{ userInfo?.integration }}</text>
<text>会员积分</text> <text>会员积分</text>
</view> </view>
<text class='integral-detail-title'>积分明细</text> <text class='integral-detail-title'>积分明细</text>
<scroll-view style='margin: 10rpx 20rpx'> <scroll-view style='margin: 10rpx 20rpx'>
<u-list @scrolltolower='loadMore'>
<integral-item v-for='(item,index) in tradeList' :key='index' :item='item' /> <integral-item v-for='(item,index) in tradeList' :key='index' :item='item' />
<u-empty v-if='tradeList.length === 0' text='暂无数据' margin-top='100' />
</u-list>
</scroll-view> </scroll-view>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import IntegralItem from '@/pages/mine/subs/integral/components/integral-item.vue'; import IntegralItem from './components/integral-item.vue';
import { getIntegralList } from '@/api/user';
import { useUserStore } from '@/store';
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const tradeList = ref([]);
const currentPageNum = ref(1);
onLoad((e) => {
fetchData();
});
const fetchData = async (refresh: boolean = true) => {
currentPageNum.value = refresh ? 1 : currentPageNum.value + 1;
const { rows } = await getIntegralList({
fbean: { 'pageNum': currentPageNum.value, 'pageSize': 20, 'bean': {} }
});
if(refresh) {
tradeList.value = rows;
} else {
tradeList.value = tradeList.value.concat(rows);
}
};
const loadMore = () => {
fetchData(false);
};
const tradeList = ref([1, 2, 3, 4, 5]);
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>

View File

@@ -1,46 +1,68 @@
<template> <template>
<view class='card-view' @click.stop='goPath("/pages/mine/subs/order/detail")'> <view class='card-view' @click.stop='goPath(`/pages/mine/subs/order/detail?orderId=${item?.id}`)'>
<view class='goods-info-view c-flex-row'> <view class='goods-info-view c-flex-row'>
<image class='goods-image' :src='assetsUrl("test_bg.png")' /> <image class='goods-image' :src='item?.orderGoods[0].images||defaultImage' />
<view class='c-flex-column' style='flex: 1;'> <view class='c-flex-column' style='flex: 1;'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='goods-name'>女童夏装套装洋气装短袖阔腿裤子夏装套装</text> <text class='goods-name'>{{ item?.orderGoods[0].goodsName || '未知' }}</text>
<text class='status'>未支付</text> <text class='status'>
<text v-if='item?.payStatus == 2'>已支付</text>
<text v-else-if='isPending(item)'>待支付</text>
<text v-else style='color: #999999'>已过期</text>
</text>
</view> </view>
<view class='bottom-view c-flex-row'> <view class='bottom-view c-flex-row'>
<text> <text>
均色120cm {{ item?.orderGoods[0].stockStock.colorName||'均色' }}{{ item?.orderGoods[0].stockStock.sizeName||'均码' }}
</text> </text>
<text> <text>
1 {{ item?.itemNum }}
</text> </text>
<text>23.20</text> <text>{{ item?.totalPrice }}</text>
</view> </view>
</view> </view>
</view> </view>
<view class='action-view c-flex-row'> <view class='action-view c-flex-row'>
<view class='countdown c-flex-row'> <view class='countdown c-flex-row' v-if='isPending(item)'>
<image :src='assetsUrl("ic_time.png")' /> <image :src='assetsUrl("ic_time.png")' />
<text class='primary-text-color'>支付剩余时间 <text class='primary-text-color'>支付剩余时间
<text class='accent-text-color'>05:23</text> <text class='accent-text-color'>{{ dayjs.unix(item?.countdown || 0).format('mm:ss') || '00:00' }}</text>
</text> </text>
</view> </view>
<view v-else style='flex: 1' />
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='secondary-text-color'>加入购物车</text> <text class='add-shoppingcart secondary-text-color' @click.stop='add(item?.orderGoods[0])'>加入购物车</text>
<text class='accent-text-color'>立即支付</text> <text class='payment accent-text-color' v-if='isPending(item)'
@click.stop='goPath(`/pages/mine/subs/order/detail?orderId=${item?.id}`)'>立即支付
</text>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { PropType } from 'vue';
import { assetsUrl, defaultImage } from '@/utils/assets';
import { goPath } from '@/utils'; import { goPath } from '@/utils';
import { isPending } from '@/utils/order';
import { OrderBean } from '@/api/order/types';
import { GoodsBean } from '@/api/goods/types';
import dayjs from 'dayjs';
const emits = defineEmits(['addShoppingCart', 'payment']);
defineProps({ defineProps({
item: Object item: Object as PropType<OrderBean>
}); });
const add = (item: GoodsBean | undefined) => {
emits('addShoppingCart', item);
};
const pay = (orderId: string | undefined) => {
emits('payment', orderId);
};
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>
@@ -133,13 +155,15 @@ defineProps({
border-radius: 34rpx; border-radius: 34rpx;
} }
text:nth-of-type(1) { .add-shoppingcart {
border: 1rpx solid #ACACAC; border: 2rpx solid #ACACAC;
} }
text:nth-of-type(2) {
border: 1rpx solid #F32B2B; .payment {
border: 2rpx solid #F32B2B;
margin-left: 20rpx; margin-left: 20rpx;
box-sizing: border-box;
} }
} }
} }

View File

@@ -1,6 +1,7 @@
<template> <template>
<scroll-view scroll-y>
<view class='content'> <view class='content'>
<view class='countdown c-flex-row'> <view class='countdown c-flex-row' v-if='isPending(orderBean?.order)'>
<image :src='assetsUrl("ic_order.png")' /> <image :src='assetsUrl("ic_order.png")' />
<view class='c-flex-column'> <view class='c-flex-column'>
<text> <text>
@@ -9,101 +10,203 @@
<view class='c-flex-row'> <view class='c-flex-row'>
<image :src='assetsUrl("ic_time.png")' /> <image :src='assetsUrl("ic_time.png")' />
<text>支付剩余时间 <text>支付剩余时间
<text style='color: #F32B2B'>05:23</text> <text style='color: #F32B2B'>{{ dayjs.unix(orderBean?.order?.countdown || 0).format('mm:ss') }}</text>
</text> </text>
</view> </view>
</view> </view>
</view> </view>
<view class='address-view c-flex-column'> <view class='address-view c-flex-column'
v-if='orderBean?.order?.address'>
<view class='user-info c-flex-row'> <view class='user-info c-flex-row'>
<text>默认</text> <text class='status' v-if='orderBean.order.address.defaultstatus==1'>默认</text>
<text>黄先生</text> <text>{{ orderBean?.order?.address.name }}</text>
<text>155****6532</text> <text>{{ orderBean?.order?.address.mobile }}</text>
<view style='flex: 1' /> <view style='flex: 1' />
<text style='color: #5B96FB'>复制</text> <text style='color: #5B96FB'
@click.stop='copy(orderBean?.order?.address.name+","+orderBean?.order?.address.mobile+","+orderBean?.order?.address.addr)'>
复制
</text>
</view> </view>
<view class='addr c-flex-row'> <view class='addr c-flex-row'>
<image :src='assetsUrl("ic_location.png")' /> <image :src='assetsUrl("ic_location.png")' />
<text>湖南省 长沙市 详细地址详细地址详细地址</text> <text>{{ orderBean?.order?.address.addr }}</text>
</view> </view>
<image class='dashed-line' :src='assetsUrl("ic_address_dashed_line.png")' /> <image class='dashed-line' :src='assetsUrl("ic_address_dashed_line.png")' />
</view> </view>
<view class='goods-info-view c-flex-column'> <view class='goods-info-view c-flex-column'>
<view class='c-flex-row'> <view class='c-flex-row' style='margin-bottom: 20rpx' v-for='(item,index) in orderBean?.order.orderGoods'
<image class='goods-image' :src='assetsUrl("test_bg.png")' /> :key='index'>
<image class='goods-image' :src='item.stockStock.images||defaultImage' />
<view class='c-flex-column' style='flex: 1;'> <view class='c-flex-column' style='flex: 1;'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='goods-name'>女童夏装套装洋气装短袖阔腿裤子夏装套装</text> <text class='goods-name'>{{ item.goodsName || '未知' }}</text>
</view> </view>
<text style='color: #999999;margin-top: 30rpx'> <text style='color: #999999;margin-top: 30rpx'>
2356235654 {{ item.goodsCode }}
</text> </text>
<view class='bottom-view c-flex-row'> <view class='bottom-view c-flex-row'>
<text> <text>
紫色120cm x1 {{ item.stockStock.colorName||'均色' }}{{ item.stockStock.sizeName||'均码' }} x{{ item.goodsNum }}
</text> </text>
<text>23.20</text> <text>{{ item.consumePrice }}</text>
</view> </view>
</view> </view>
</view> </view>
<view class='divider' /> <view class='divider' />
<view class='remark-view c-flex-row'> <view class='remark-view c-flex-row'>
<text style='flex: 1'>备注</text> <text style='flex: 1'>备注</text>
<text>无备注</text> <text>{{ orderBean?.order?.remark || '无备注' }}</text>
</view> </view>
</view> </view>
<view class='card-view'> <view class='card-view'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='card-view-title'>商品总价</text> <text class='card-view-title'>商品总价</text>
<text class='card-view-value'>29.90</text> <text class='card-view-value'>{{ orderBean?.order?.totalPrice }}</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='card-view-title'>运费</text> <text class='card-view-title'>运费</text>
<text class='card-view-value'>8</text> <text class='card-view-value'>0</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='card-view-title'>优惠券 <text class='card-view-title'>优惠券
<text style='font-size: 22rpx;color: #F32B2B;'>已选最大优惠</text> <text style='font-size: 22rpx;color: #F32B2B;'>已选最大优惠</text>
</text> </text>
<text class='card-view-value' style='color: #F32B2B;margin: 0'>-20 <text class='card-view-value' style='color: #F32B2B;margin: 0'>-{{ orderBean?.order?.reducePrice }}
</text> </text>
</view> </view>
<view class='divider' /> <view class='divider' />
<view class='c-flex-row'> <view class='c-flex-row'>
<view style='flex: 1' /> <view style='flex: 1' />
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='card-view-value'>1件商品 合计 <text class='card-view-value'>{{ orderBean?.order?.itemNum }}件商品 合计
<text style='font-size: 34rpx;color:#F32B2B'>¥29.90</text> <text style='font-size: 34rpx;color:#F32B2B'>¥{{ orderBean?.order?.totalPrice }}</text>
</text> </text>
</view> </view>
</view> </view>
</view> </view>
<view class='order-no c-flex-column'> <view class='order-no c-flex-column'>
<text>订单编号1524625555555 <text>订单编号{{ orderBean?.order?.id }}
<text style='color: #5B96FB;margin-left: 10rpx'>复制</text> <text style='color: #5B96FB;margin-left: 10rpx' @click.stop='copy(orderBean?.order?.id||"")'>复制</text>
</text>
<text>下单时间{{ orderBean?.order?.createTime }}</text>
<text>
支付状态
<text v-if='orderBean?.order?.payStatus == 2' style='color: #F32B2B'>已支付</text>
<text v-else-if='isPending(orderBean?.order)' style='color: #F32B2B'>待支付</text>
<text v-else style='color: #999999'>已过期</text>
</text> </text>
<text>下单时间2022-02-22 122255</text>
</view> </view>
<view class='bottom-action-view c-flex-row'> <view class='bottom-action-view c-flex-row' v-if='isPending(orderBean?.order)'>
<text @click.stop='cancel'>取消订单</text> <text @click.stop='cancel' style='display: none'>取消订单</text>
<text @click.stop='payment'>立即支付</text> <sqb-pay @navigateTo='navigateTo'
:return_url='buildSqbParams.return_url'
:total_amount='buildSqbParams.total_amount'
:terminal_sn='buildSqbParams.terminal_sn'
:client_sn='buildSqbParams.client_sn'
:subject='buildSqbParams.subject'
:merchant_name='buildSqbParams.merchant_name'
:notify_url='buildSqbParams.notify_url'
:sign='buildSqbParams.sign'>
<button @click='payment'>立即支付</button>
</sqb-pay>
</view> </view>
</view> </view>
</scroll-view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl, defaultImage } from '@/utils/assets';
import { getOrderDetail } from '@/api/order';
import { OrderBean } from '@/api/order/types';
import { copy, parseParameter, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { useUserStore } from '@/store';
import { getOrderDeadline, handlePayResult, isPending } from '@/utils/order';
import dayjs from 'dayjs';
const userState = useUserStore();
const { terminalInfo } = storeToRefs(userState);
const orderBean = ref<{
order: OrderBean,
stock: any[],
paymentList: []
}>();
let currentInterval = 0;
onLoad(async (e: any) => {
await fetchData(e.orderId);
handleCountdown();
});
const fetchData = async (orderId: string) => {
await uni.showLoading();
orderBean.value = await getOrderDetail(orderId);
if(orderBean.value?.order.address) {
orderBean.value.order.address = JSON.parse(orderBean?.value.order?.address);
}
uni.hideLoading();
};
onUnload(() => {
clearInterval(currentInterval);
});
const buildSqbParams = computed(() => {
const params = sortASCII({
client_sn: orderBean.value?.order?.id || '',
return_url: '/pages/common/payresult/index',
total_amount: Number(((orderBean.value?.order?.totalPrice || 0) * 100).toFixed(2)),
terminal_sn: terminalInfo.value.terminalSn,
subject: '商品支付',
merchant_name: terminalInfo.value.companyName,
notify_url: 'https://www.baidu.com'
}, true);
return {
...params,
sign: hexMD5(parseParameter(params) + '&key=' + terminalInfo.value.terminalKey).toUpperCase()
};
});
const navigateTo = (e: any) => {
handlePayResult(orderBean.value?.order.id, e, {
onSuccess: (options:any) => {
fetchData(orderBean.value?.order.id || '');
}
});
};
const handleCountdown = () => {
clearInterval(currentInterval);
currentInterval = setInterval(() => {
if(orderBean.value != undefined) {
if(isPending(orderBean.value?.order)) {
let second = (getOrderDeadline(orderBean.value?.order) - Date.now()) / 1000;
orderBean.value.order.countdown = second;
if(second <= 0) {
fetchData(orderBean.value?.order.id || '');
}
second--;
} else {
clearInterval(currentInterval);
fetchData(orderBean.value?.order.id || '');
}
}
}, 1000);
};
const cancel = () => { const cancel = () => {
}; };
const payment = () => { const payment = () => {
console.log(buildSqbParams.value);
}; };
</script> </script>
@@ -148,14 +251,14 @@ const payment = () => {
} }
.address-view { .address-view {
padding-top: 38rpx; padding-top: 30rpx;
background: #FFFFFF; background: #FFFFFF;
.user-info { .user-info {
margin-left: 20rpx; margin-left: 20rpx;
margin-right: 20rpx; margin-right: 20rpx;
text:nth-of-type(1) { .status {
padding: 2rpx 10rpx; padding: 2rpx 10rpx;
font-size: 24rpx; font-size: 24rpx;
color: #F32B2B; color: #F32B2B;
@@ -310,14 +413,15 @@ const payment = () => {
color: #333333; color: #333333;
} }
text:nth-of-type(1) { text:nth-of-type(3) {
margin-bottom: 12rpx; margin-bottom: 0rpx;
} }
} }
.bottom-action-view { .bottom-action-view {
background: #FFFFFF; background: #FFFFFF;
position: fixed; position: fixed;
height: 80rpx;
justify-content: flex-end; justify-content: flex-end;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -328,19 +432,21 @@ const payment = () => {
font-weight: 400; font-weight: 400;
font-size: 30rpx; font-size: 30rpx;
color: #333333; color: #333333;
border-radius: 34rpx; border-radius: 50rpx;
padding: 17rpx 45rpx; padding: 17rpx 45rpx;
border: 1rpx solid #707070; border: 1rpx solid #707070;
margin-right: 20rpx; margin-right: 20rpx;
} }
text:nth-of-type(2) { sqb-pay button {
padding: 17rpx 45rpx; padding: 0 45rpx;
border-radius: 43rpx; border-radius: 50rpx;
border: 1rpx solid #F32B2B; border: 2rpx solid #F32B2B;
font-weight: bold; font-weight: bold;
background: #FFFFFF;
font-size: 30rpx; font-size: 30rpx;
color: #F32B2B; color: #F32B2B;
box-sizing: border-box;
} }
} }
</style> </style>

View File

@@ -1,13 +1,102 @@
<template> <template>
<tabbar :titles="['全部', '进行中', '已结束']" @change='args => {}' /> <tabbar :titles="['全部', '未支付', '已支付']" :index='payStatus' @change='tabChange' />
<order-item v-for='(item,index) in orderList' :key='index' :item='item' /> <u-list :list='orderList' :border='false' @scrolltolower='loadMore'>
<u-list-item v-for='(item,index) in orderList' :key='index'>
<order-item :item='item' @addShoppingCart='addShoppingCart' @pay='payment' />
</u-list-item>
<u-loadmore v-if='orderList.length>0' :status='loadingStatus' />
<u-empty v-if='orderList.length === 0' text='暂无数据' margin-top='100' />
</u-list>
<sku-dialog ref='skuDialogRef' :exists='temporaryStockBean' />
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { ref } from 'vue';
import { showToast } from '@/utils';
import { getOrderDeadline, isPending } from '@/utils/order';
import { getOrderList } from '@/api/order';
import OrderItem from '@/pages/mine/subs/order/components/order-item.vue'; import OrderItem from '@/pages/mine/subs/order/components/order-item.vue';
import { OrderBean } from '@/api/order/types';
import SkuDialog from '@/components/sku-dialog.vue';
import { GoodsBean, StockBean } from '@/api/goods/types';
import useShoppingCartStore from '@/store/modules/shoppingcart';
const orderList = ref([1, 2, 4, 5]); const shoppingCartStore = useShoppingCartStore();
const skuDialogRef = ref();
const temporaryStockBean = ref<StockBean>();
const orderList = ref<OrderBean[]>([]);
const currentPageNum = ref(1);
const payStatus = ref(0);
const loadingStatus = ref('loading');
let currentInterval = 0;
onLoad((e: any) => {
const index = Number(e.index) || 0;
tabChange(index);
});
onUnload(() => {
clearInterval(currentInterval);
});
const tabChange = (index: number) => {
payStatus.value = index;
fetchData(true);
};
const fetchData = async (refresh: boolean = true) => {
loadingStatus.value = 'loading';
if(!refresh) {
currentPageNum.value += 1;
}
const { list } = await getOrderList({
pageNum: currentPageNum.value,
pageSize: 20,
obj: { payStatus: payStatus.value }
});
if(refresh) {
orderList.value = list;
} else {
orderList.value = orderList.value.concat(list);
}
loadingStatus.value = 'no';
handleCountdown(orderList.value.filter(item => item.payStatus == 1));
};
const handleCountdown = (items: OrderBean[]) => {
clearInterval(currentInterval);
currentInterval = setInterval(() => {
items.forEach(item => {
if(isPending(item)) {
let second = (getOrderDeadline(item) - Date.now()) / 1000;
item.countdown = second;
if(item.countdown <= 0) {
item.countdown = 0;
}
second--;
}
});
}, 1000);
};
const loadMore = () => {
fetchData(false);
};
const addShoppingCart = (item: GoodsBean) => {
skuDialogRef.value.show(item.goodsId, (e: GoodsBean) => {
shoppingCartStore.save(e);
showToast('加入购物车成功');
});
};
const payment = (orderId: string) => {
console.log(orderId);
};
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>

View File

@@ -1,30 +1,32 @@
<template> <template>
<view class='content'> <view class='content'>
<view class='top-card-view'> <view class='top-card-view'>
<image class='bg-image' :src='assetsUrl("test_bg.png")' /> <image class='bg-image' :src='companyConfigInfo.userbgcover' />
<image class='avatar-image' :src='userInfo.image' /> <image class='avatar-image' :src='params.image' />
<text>{{ userInfo.nickName }}</text> <text>{{ userInfo.nickName }}</text>
<text>会员卡</text> <text>{{ userInfo.levelName }}</text>
</view> </view>
<view class='basic-info-view c-flex-column'> <view class='basic-info-view c-flex-column'>
<view class='c-flex-row'> <view class='c-flex-row'>
<text>头像</text> <text>头像</text>
<view class='avatar-view'> <view class='avatar-view'>
<image class='avatar-image' :src='userInfo.image' /> <image class='avatar-image' :src='params.image' />
<button class='avatar-btn' open-type='chooseAvatar' /> <button class='avatar-btn' open-type='chooseAvatar' @chooseavatar='chooseAvatar' />
</view> </view>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='nickname'>姓名</text> <text class='nickname'>姓名</text>
<input placeholder='请输入姓名' type='nickname' v-model='userInfo.nickName' /> <input placeholder='请输入姓名' type='nickname' v-model='userInfo.nickName' @change='bindNickname' />
</view> </view>
<view class='divider' /> <view class='divider' />
<view class='c-flex-row'> <view class='c-flex-row'>
<text>手机号</text> <text>手机号</text>
<input placeholder='15523653265' v-model='userInfo.telephone'/> <input placeholder='请输入手机号' :disabled='(userInfo.telephone?.length||0)!=0'
v-model='params.telephone'
@input='bindTelephone' />
</view> </view>
<view class='divider' /> <view class='divider' />
@@ -32,12 +34,12 @@
<text>性别</text> <text>性别</text>
<view class='c-flex-row' @click.stop='changeGender(0)'> <view class='c-flex-row' @click.stop='changeGender(0)'>
<image class='gender-image' <image class='gender-image'
:src='assetsUrl(currentGender==0?"ic_checkbox_active.png":"ic_checkbox_normal.png")' /> :src='assetsUrl(params?.gender=="男"?"ic_checkbox_active.png":"ic_checkbox_normal.png")' />
<text class='gender-text'></text> <text class='gender-text'></text>
</view> </view>
<view class='c-flex-row' @click.stop='changeGender(1)'> <view class='c-flex-row' @click.stop='changeGender(1)'>
<image class='gender-image' <image class='gender-image'
:src='assetsUrl(currentGender==1?"ic_checkbox_active.png":"ic_checkbox_normal.png")' /> :src='assetsUrl(params?.gender=="女"?"ic_checkbox_active.png":"ic_checkbox_normal.png")' />
<text class='gender-text'></text> <text class='gender-text'></text>
</view> </view>
</view> </view>
@@ -45,31 +47,108 @@
<view class='divider' /> <view class='divider' />
<view class='c-flex-row'> <view class='c-flex-row'>
<text>生日</text> <text>生日</text>
<picker mode='date' @change='changeDate'> <picker mode='date' :disabled='(userInfo.birthday?.length||0)!=0' @change='changeDate'>
<text>请选择生日</text> <text>{{ dayjs(params?.birthday || Date.now()).format('YYYY-MM-DD') || '请选择生日' }}</text>
</picker> </picker>
</view> </view>
</view> </view>
<button class='primary-button'>保存</button> <button class='primary-button' @click.stop='save'>保存</button>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { showToast } from '@/utils';
import { updateProfile } from '@/api/user';
import dayjs from 'dayjs';
const store= useUserStore() const store = useUserStore();
const {userInfo} = storeToRefs(store) const { userInfo, companyConfigInfo } = storeToRefs(store);
const currentGender = ref<number>(0); const params = ref<{
avatarUrl: string | undefined;
image: string;
nickName: string;
telephone: string;
gender: string;
birthday: string;
}>({
avatarUrl: userInfo.value.image,
image: userInfo.value.image,
nickName: userInfo.value.nickName,
telephone: userInfo.value.telephone,
gender: userInfo.value.gender,
birthday: userInfo.value.birthday || dayjs().format('YYYY-MM-DD')
});
onLoad(() => {
// store.getProfile();
});
const chooseAvatar = (e: any) => {
uni.showLoading();
uni.uploadFile({
url: import.meta.env.VITE_APP_BASE_API + '/wc/wechat/uploadImage',
filePath: e.detail.avatarUrl,
name: 'fileName',
header: {
'Content-Type': 'multipart/form-data'
},
success: (res: any) => {
params.value!.avatarUrl = JSON.parse(res.data).data;
params.value!.image = JSON.parse(res.data).data;
},
error: (err: any) => {
showToast('上传失败');
},
complete() {
uni.hideLoading();
}
});
};
const bindNickname = (e: any) => {
params.value!.nickName = e.detail.value;
};
const bindTelephone = (e: any) => {
params.value!.telephone = e.detail.value;
};
const changeGender = (index: number) => { const changeGender = (index: number) => {
currentGender.value = index; params.value!.gender = index == 0 ? '男' : '女';
}; };
const changeDate = (e: any) => { const changeDate = (e: any) => {
console.log(e); params.value!.birthday = e.detail.value;
};
const save = async () => {
const newUserInfo = {
...userInfo.value,
...params.value
};
delete params.value.avatarUrl;
const result = await updateProfile({
...params.value,
birthday: dayjs(params.value.birthday).isValid() ? dayjs(params.value.birthday).format('YYYY-MM-DD HH:mm:ss') : dayjs().format('YYYY-MM-DD HH:mm:ss'),
birthdayType: 0
});
if(result) {
await store.setUserInfo(newUserInfo);
showToast('修改成功', {
icon: 'success', complete: () => {
uni.navigateBack();
}
});
} else {
showToast('服务异常,操作失败', { icon: 'error' });
}
}; };
</script> </script>

View File

@@ -4,29 +4,55 @@
<image :src='assetsUrl("bg_member_recharge.png")' /> <image :src='assetsUrl("bg_member_recharge.png")' />
<view class='balance-content'> <view class='balance-content'>
<text>累计余额</text> <text>累计余额</text>
<text>1</text> <text>{{ userInfo?.totalIncoming }}</text>
</view> </view>
<view class='balance-content'> <view class='balance-content'>
<text>当前余额</text> <text>当前余额</text>
<text class='accent-text-color'>{{ userInfo.balance }}</text> <text class='accent-text-color'>{{ userInfo?.balance }}</text>
</view> </view>
</view> </view>
<view class='recharge-option-card'> <view class='recharge-option-card'>
<text>充值金额</text> <view class='top-border'></view>
<tabbar :titles='rechargeItems?.map(res=>res.rechargeamount)' @change='change' /> <text class='title'>充值金额</text>
<view class='divider' /> <scroll-view scroll-y style='flex: 1;padding-bottom: 180rpx'>
<view class='option-item'> <view class='option-item' :class="{'option-item-active': currentIndex==index}"
<image :src='assetsUrl("bg_member_recharge_item.png")' /> v-for='(item,index) in rechargeItems' :key='index' @click.stop='change(index)'>
<text>充值{{ rechargeItems[currentIndex].rechargeamount }}{{ rechargeItems[currentIndex].rewardamount }}</text> <image v-if='currentIndex==index' class='checked-image' src='/static/images/ic_recharge_checked.png' />
<label class='amount-title'>{{ item.rechargeamount }}</label>
<view class='divider' style='margin-bottom: 36rpx' />
<view class='card-view'>
<view class='title-container'>
<text>充值{{ item?.rechargeamount || 0 }}{{ item?.rewardamount || 0
}}
</text>
<text class='accent-text-color'> <text class='accent-text-color'>
实得{{ rechargeItems[currentIndex].rechargeamount + (rechargeItems[currentIndex].rewardamount) }} 实得{{ item?.rechargeamount + (item?.rewardamount) }}
</text>
</view>
<text class='description'>{{ item.rewardamount || 0 }}{{ item.rewardcouponamount || 0
}}元优惠券共{{ item.rewardcouponnum || 0 }}
返利{{ item.rewardrefundamount || 0 }}
</text> </text>
</view> </view>
</view> </view>
</scroll-view>
</view>
<view class='bottom-button-view'> <view class='bottom-button-view'>
<!-- <payment-button style='flex: 1' :payParams='buildSignParams'>-->
<sqb-pay style='width: 100%;' @navigateTo='navigateTo'
:return_url='buildSqbParams.return_url'
:total_amount='buildSqbParams.total_amount'
:terminal_sn='buildSqbParams.terminal_sn'
:client_sn='buildSqbParams.client_sn'
:subject='buildSqbParams.subject'
:merchant_name='buildSqbParams.merchant_name'
:notify_url='buildSqbParams.notify_url'
:sign='buildSqbParams.sign'>
<button class='primary-button'>充值</button> <button class='primary-button'>充值</button>
</sqb-pay>
<!-- </payment-button>-->
</view> </view>
</view> </view>
</template> </template>
@@ -34,21 +60,87 @@
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import { assetsUrl } from '@/utils/assets';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { getRechargeList } from '@/api/user'; import { getRechargeList, preRecharge, rechargeVerify } from '@/api/user';
import { parseParameter, sortASCII } from '@/utils';
import { hexMD5 } from '@/utils/common/md5';
import { handlePayResult } from '@/utils/order';
const store = useUserStore(); const userState = useUserStore();
const { userInfo } = storeToRefs(store); const { userInfo, terminalInfo } = storeToRefs(userState);
const rechargeItems = ref([]); const rechargeOptionBackgroundUrl = assetsUrl('bg_member_recharge_item.png');
const rechargeItems = ref<{
itemid: string
rechargeamount: number
rewardamount: number
rewardcouponamount: number,
rewardcouponnum: number
rechargetype: number,
rewardrefundamount: number
}[]>([]);
const currentIndex = ref<number>(0); const currentIndex = ref<number>(0);
const preRechargeOrderId = ref();
onLoad(async () => { onLoad(async () => {
const { rechargetype, ruleitems } = await getRechargeList(); const { ruleitems } = await getRechargeList();
rechargeItems.value = ruleitems; rechargeItems.value = ruleitems;
// rechargeItems.value.push(rechargeItems.value[0]);
// rechargeItems.value.push(rechargeItems.value[0]);
// rechargeItems.value.push(rechargeItems.value[0]);
// rechargeItems.value.push(rechargeItems.value[0]);
console.log(rechargeItems.value);
await change(0);
}); });
const change = (index: number) => { const change = async (index: number) => {
currentIndex.value = index; currentIndex.value = index;
const { id } = await preRecharge({
itemid: rechargeItems.value[index].itemid
});
preRechargeOrderId.value = id;
};
const buildSqbParams = computed(() => {
const params = sortASCII({
client_sn: preRechargeOrderId.value,
return_url: '/pages/common/payresult/index',
total_amount: Number(((rechargeItems.value[currentIndex.value]?.rechargeamount || 0) * 100).toFixed(2)),
terminal_sn: terminalInfo.value.terminalSn,
subject: '充值',
merchant_name: terminalInfo.value.companyName,
notify_url: 'https://www.baidu.com'
}, true);
return {
...params,
sign: hexMD5(parseParameter(params) + '&key=' + terminalInfo.value.terminalKey).toUpperCase()
};
});
const navigateTo = (e: any) => {
handlePayResult(preRechargeOrderId.value, e, {
onSuccess: async (options: any) => {
await goRecharge();
await userState.setUserInfo({
...userInfo.value,
balance: Number(userInfo.value.balance) + Number(rechargeItems.value[currentIndex.value].rechargeamount) + Number(rechargeItems.value[currentIndex.value]?.rewardamount)
});
await uni.navigateBack();
},
onFailure: (e: any) => {
}
});
};
const goRecharge = async () => {
// const result = await recharge({
// itemid: rechargeItems.value[currentIndex.value].itemid
// });
// console.log('platform recharge result ', result);
await rechargeVerify({
payid: preRechargeOrderId.value
});
}; };
</script> </script>
@@ -89,53 +181,109 @@ const change = (index: number) => {
.recharge-option-card { .recharge-option-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background: #FFFFFF; background: #F5F5F5;
border-radius: 30rpx 30rpx 0 0;
position: relative; position: relative;
margin-top: -25rpx; //margin-top: -25rpx;
text:nth-of-type(1) { .top-border {
background: #FFFFFF;
height: 30rpx;
margin-top: -35rpx;
border-radius: 30rpx 30rpx 0 0;
}
.title {
font-size: 30rpx; font-size: 30rpx;
font-weight: bold; font-weight: bold;
color: #333333; color: #333333;
margin-top: 20rpx; background: #FFFFFF;
margin-left: 30rpx; border-radius: 10rpx 10rpx 0 0;
padding: 10rpx 20rpx 20rpx 20rpx;
margin-bottom: 20rpx; margin-bottom: 20rpx;
margin-top: -15rpx;
} }
.option-item { .option-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin: 10rpx 20rpx 20rpx 20rpx;
background: #FFFFFF;
border-radius: 20rpx;
padding-top: 20rpx;
box-sizing: border-box;
border: solid #00000000 2rpx;
position: relative; position: relative;
margin: 36rpx 20rpx;
image { .amount-title {
font-weight: bold;
font-size: 40rpx;
width: fit-content;
color: #333333;
margin-left: 30rpx;
}
.amount-title:after {
content: "";
display: block;
border: 2rpx solid black;
}
.checked-image {
width: 70rpx;
height: 70rpx;
position: absolute;
right: 0;
top: 0;
border-radius: 0 18rpx 0 0
}
.card-view {
display: flex;
flex-direction: column;
width: 100%; width: 100%;
height: 167rpx; height: 167rpx;
border-radius: 20rpx; border-radius: 20rpx;
} //background-image: url(v-bind(rechargeOptionBackgroundUrl));
background-image: url('https://img.lakeapp.cn/wx/images/bg_member_recharge_item.png');
background-size: 100% 100%;
justify-content: flex-start;
color: #FFFFFF;
.title-container {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-top: 27rpx;
padding: 0 30rpx;
text:nth-of-type(1) { text:nth-of-type(1) {
display: flex;
font-size: 36rpx; font-size: 36rpx;
color: #FFFFFF;
margin-top: 27rpx;
margin-left: 30rpx;
position: absolute;
} }
text:nth-of-type(2) { text:nth-of-type(2) {
display: flex; display: flex;
font-size: 30rpx; font-size: 30rpx;
margin-top: 85rpx;
margin-left: 30rpx;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 185rpx; width: 185rpx;
height: 50rpx; height: 50rpx;
background: #FFFFFF; background: #FFFFFF;
border-radius: 25rpx; border-radius: 25rpx;
position: absolute;
} }
} }
.description {
margin-top: 30rpx;
margin-left: 30rpx;
}
}
}
.option-item-active {
@extend .option-item;
border: 2rpx solid #f95e5d;
}
} }
</style> </style>

View File

@@ -1,23 +1,26 @@
<template> <template>
<view class='card-view'> <view class='card-view'>
<text class='category-title'>2023-02-12</text> <!-- <text class='category-title'>2023-02-12</text>-->
<view class='item c-flex-column' v-for='(item,index) in 3' :key='index'> <!-- <view class='item c-flex-column' v-for='(item,index) in 3' :key='index'>-->
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='primary-text-color' style='flex: 1'>消费</text> <text class='primary-text-color' style='flex: 1'>{{ item.name }}</text>
<text class='accent-text-color'>-30</text> <text class='accent-text-color'>{{item.type===1?'-':'+'}}{{
item.transactionPrice || 0 }}</text>
</view> </view>
<view class='c-flex-row'> <view class='c-flex-row'>
<text class='secondary-text-color' style='flex: 1'>2023-06-27 15:02:11</text> <text class='secondary-text-color' style='flex: 1'>{{ item.createTime }}</text>
<text style='color: #999999'>会员余额30.00</text> <text style='color: #999999'>会员余额{{ item.finalPrice || 0 }}</text>
</view>
<view class='divider' style='margin-top: 20rpx'/>
</view> </view>
<!-- <view class='divider' style='margin-top: 20rpx' />-->
<!-- </view>-->
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { PropType } from 'vue';
defineProps({ defineProps({
item: Object item: Object as PropType<any>
}); });
</script> </script>
@@ -26,7 +29,7 @@ defineProps({
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 16rpx 23rpx 20rpx 30rpx; padding: 16rpx 23rpx 20rpx 30rpx;
margin: 20rpx 0; margin: 20rpx 0 10rpx 0;
} }
.category-title { .category-title {

View File

@@ -1,30 +1,81 @@
<template> <template>
<tabbar :titles='["全部","充值","消费"]' :item-active-color='"#D95554"' :indicator-color='"#D95554"' /> <tabbar :titles='["全部","充值","消费"]' @change='changeTab' :item-active-color='"#D95554"'
:indicator-color='"#D95554"' />
<view class='content'> <view class='content'>
<view class='c-flex-row'> <!-- <view class='c-flex-row'>-->
<text>选择日期</text> <!-- <text>选择日期</text>-->
<picker mode='date' @change='changeDate'> <!-- <picker mode='date' @change='changeDate'>-->
<view class='current-date c-flex-row'> <!-- <view class='current-date c-flex-row'>-->
<text>2024-02-10</text> <!-- <text>{{ tradeDate }}</text>-->
<image :src='assetsUrl("ic_triangle_down.png")' /> <!-- <image :src='assetsUrl("ic_triangle_down.png")' />-->
</view> <!-- </view>-->
</picker> <!-- </picker>-->
</view> <!-- </view>-->
<scroll-view :scroll-y='true'> <u-list @scrolltolower='loadMore'>
<trade-item v-for='(item,index) in tradeList' :key='index' :item='item' /> <trade-item v-for='(item,index) in tradeList' :key='index' :item='item' />
</scroll-view> <u-loadmore v-if='tradeList.length>0' :status='loadingStatus' />
<u-empty v-if='tradeList.length === 0' text='暂无数据' margin-top='100' />
</u-list>
</view> </view>
</template> </template>
<script lang='ts' setup> <script lang='ts' setup>
import { assetsUrl } from '@/utils/assets'; import TradeItem from './components/trade-item.vue';
import TradeItem from '@/pages/mine/subs/trade/components/trade-item.vue'; import { getTradeList } from '@/api/user';
import dayjs from 'dayjs';
const tradeList = ref([1, 2, 3, 4, 5]); const tradeList = ref([]);
const tradeType = ref('');
const tradeDate = ref();
const currentPageNum = ref(1);
const loadingStatus = ref('loading');
const changeDate = () => { onLoad((e) => {
tradeDate.value = dayjs(Date.now()).format('YYYY-MM-DD');
fetchData();
});
const changeDate = (e: any) => {
tradeDate.value = e.detail.value;
fetchData();
};
const changeTab = (index: number) => {
switch (index) {
case 0:
tradeType.value = '';
break;
case 1:
tradeType.value = '会员充值';
break;
case 2:
tradeType.value = '余额消费';
break;
}
fetchData();
};
const fetchData = async (refresh: boolean = true) => {
currentPageNum.value = refresh ? 1 : currentPageNum.value + 1;
loadingStatus.value = 'loading';
const { list } = await getTradeList({
// startDate: tradeDate.value,
// endDate: dayjs(tradeDate.value).add(1, 'day').format('YYYY-MM-DD'),
name: tradeType.value,
pageNum: currentPageNum.value,
pageSize: 20
});
if(refresh) {
tradeList.value = list;
} else {
tradeList.value = tradeList.value.concat(list);
}
loadingStatus.value = 'no';
};
const loadMore = () => {
fetchData(false);
}; };
</script> </script>
@@ -46,7 +97,7 @@ const changeDate = () => {
image { image {
width: 19rpx; width: 19rpx;
height: 11rpx; height: 11rpx;
margin-left: 57rpx; margin-left: 15rpx;
} }
} }
</style> </style>

View File

@@ -3,15 +3,15 @@
<view class='member-info-view'> <view class='member-info-view'>
<image :src='assetsUrl("ic_crown.png")' /> <image :src='assetsUrl("ic_crown.png")' />
<view class='flex flex-col'> <view class='flex flex-col'>
<text>武汉白银会员</text> <text>{{ userInfo?.levelName }}</text>
<view /> <view />
</view> </view>
</view> </view>
<view class='qrcode-card'> <view class='qrcode-card'>
<view class='balance-view'> <view class='balance-view'>
<text class='balance-text'>账户余额{{ userInfo.balance || 0 }}</text> <text class='balance-text'>账户余额{{ userInfo?.balance || 0 }}</text>
<view class='btn-recharge' @click.stop='goPath("/pages/mine/subs/recharge/index")'> <view class='btn-recharge' @click.stop='goPath("/pages/mine/subs/recharge/index",true)'>
<text>去充值</text> <text>去充值</text>
<image :src='assetsUrl("ic_arrow_right.png")' /> <image :src='assetsUrl("ic_arrow_right.png")' />
</view> </view>
@@ -33,10 +33,9 @@
<text>卡信息</text> <text>卡信息</text>
</view> </view>
<text>店铺名称武汉xx店</text> <text>{{ getStoreName() }}</text>
<text>253654587852</text> <text>{{ userInfo?.storeId }}</text>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
@@ -47,7 +46,7 @@ import { assetsUrl } from '@/utils/assets';
import { generateBarCode, generateQrCode } from '@/api/common'; import { generateBarCode, generateQrCode } from '@/api/common';
import { getDynamicCode } from '@/api/user'; import { getDynamicCode } from '@/api/user';
import { useUserStore } from '@/store'; import { useUserStore } from '@/store';
import { goPath } from '@/utils'; import { goPath, isLogin } from '@/utils';
const store = useUserStore(); const store = useUserStore();
const { userInfo } = storeToRefs(store); const { userInfo } = storeToRefs(store);
@@ -57,18 +56,25 @@ const codeContent = ref<string>('');
const codeRefreshInterval = ref(30); const codeRefreshInterval = ref(30);
onLoad(() => { onLoad(() => {
if(!isLogin()) {
// goLogin();
return;
}
generateCode(); generateCode();
setInterval(() => { setInterval(() => {
codeRefreshInterval.value -= 1; codeRefreshInterval.value -= 1;
if(codeRefreshInterval.value == 0) { if(codeRefreshInterval.value == 0) {
codeRefreshInterval.value = 30; codeRefreshInterval.value = 30;
codeContent.value = Date.now().toFixed(0);
generateCode(); generateCode();
} }
}, 1000); }, 1000);
}); });
const getStoreName = () => {
return `${userInfo.value?.nickName || userInfo.value?.name}${userInfo.value?.storeName || userInfo.value?.companyName || userInfo.value?.creatorName}`;
};
const generateCode = async () => { const generateCode = async () => {
const { dynccode } = await getDynamicCode(); const { dynccode } = await getDynamicCode();
codeContent.value = dynccode; codeContent.value = dynccode;

View File

@@ -0,0 +1,28 @@
<template>
<view class="user-page">
<!-- 其他现有内容 -->
<!-- 在联系商家后面添加支付密码设置入口 -->
<u-cell-group>
<!-- 现有的联系商家单元格 -->
<u-cell
title="支付密码"
is-link
@click="navigateToPayPassword"
/>
</u-cell-group>
<!-- 其他现有内容 -->
</view>
</template>
<script setup lang="ts">
// ... 其他导入 ...
// 跳转到支付密码设置页面
const navigateToPayPassword = () => {
uni.navigateTo({
url: '/pages/common/pay-password/index'
})
}
</script>

View File

@@ -3,7 +3,9 @@ import { getToken } from '@/utils/auth';
// 登录页面 // 登录页面
const loginPage = '/pages/common/login/index'; const loginPage = '/pages/common/login/index';
// 页面白名单 // 页面白名单
const whiteList = ['/', '/pages/common/login/index', '/pages/home/index']; const whiteList = ['/', '/pages/common/login/index', '/pages/home/index','/pages/common/register/index',
'/pages/common/register/reward',
'/pages/mine/index','/pages/mine/subs/order/index'];
// 检查地址白名单 // 检查地址白名单
function checkWhite(url: string) { function checkWhite(url: string) {
@@ -23,11 +25,12 @@ list.forEach((item) => {
return true; return true;
} }
else { else {
if (checkWhite(to.url)) // if (checkWhite(to.url))
// return true;
//
// uni.reLaunch({ url: loginPage });
// return false;
return true; return true;
uni.reLaunch({ url: loginPage });
return false;
} }
}, },
fail(err) { fail(err) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1011 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Some files were not shown because too many files have changed in this diff Show More