Compare commits

...

172 Commits

Author SHA1 Message Date
tangxinyue 4fcbc5b06f 王者荣耀 2026-06-05 16:04:25 +08:00
tangxinyue bbfe66093e Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-05 14:39:33 +08:00
tangxinyue bf57816244 修改王者主页 2026-06-05 14:38:37 +08:00
小李 ebd2c2111c Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-05 14:38:14 +08:00
小李 cb56b3ae7e 日志 2026-06-05 14:38:03 +08:00
tangxinyue e501d35d6b Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-05 11:30:13 +08:00
tangxinyue aea48b6c58 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-06-05 11:30:03 +08:00
小李 216af408cc 图片 2026-06-05 11:28:23 +08:00
tangxinyue 65270811da 完成王者主页页面 2026-06-05 11:24:43 +08:00
小李 507973d7dc 充值页 2026-06-05 11:15:33 +08:00
小李 45b2bf3f7f 通话详情去掉默认图片 2026-06-04 14:11:33 +08:00
小李 0616283418 通话和银行卡优化 2026-06-04 14:10:28 +08:00
小李 a47cc2a9cd Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-04 10:02:48 +08:00
小李 b560d901ab 通话详情ios,工商银行,建设银行,农业银行 2026-06-04 10:02:38 +08:00
tangxinyue 6569f821e8 完成短信(苹果,华为,小米,vivo)各个机型发送图片功能,预览图片页面及图片信息列表展示样式 2026-06-04 09:55:08 +08:00
tangxinyue 1cd2226f2b 修改淘宝京东618购物图片,12306底部banner 2026-06-01 11:48:20 +08:00
tangxinyue 8ec9c844f0 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-01 10:03:49 +08:00
tangxinyue cffb2c29fb 修改未发送图片名称 2026-06-01 10:03:09 +08:00
小李 72674f4d77 优化 2026-06-01 09:57:56 +08:00
小李 73b2b3cf25 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-30 17:32:13 +08:00
小李 a6074b340b 优化 2026-05-30 17:32:06 +08:00
tangxinyue 37cf237ff1 1 2026-05-30 16:24:07 +08:00
tangxinyue 7836f9855a 修改安卓底部导航栏颜色 2026-05-30 16:16:50 +08:00
tangxinyue c9b393b117 1 2026-05-30 15:50:33 +08:00
tangxinyue 7eb68e8c15 修改618图标间距 2026-05-30 15:47:31 +08:00
tangxinyue 788f4e6b5e 1 2026-05-30 15:35:53 +08:00
tangxinyue c1e4e7be24 增加菜单长按时长 2026-05-30 15:30:15 +08:00
tangxinyue 5dbb4add21 pdd添加默认数据拼单头像 2026-05-30 15:23:13 +08:00
tangxinyue 724138f672 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-30 15:12:07 +08:00
tangxinyue 85996e19b1 优化pdd商品价格可输入小数点 2026-05-30 15:11:45 +08:00
小李 dab4d15c24 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-30 14:52:57 +08:00
小李 88ebca65e7 通话优化,银行卡优化 2026-05-30 14:52:51 +08:00
tangxinyue 033a3bd9ba pdd添加优惠卷和618活动券显示 2026-05-30 14:39:16 +08:00
tangxinyue e3e29f7f18 优化调整pdd购物 2026-05-30 11:18:21 +08:00
小李 5e02decbf5 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-30 09:28:16 +08:00
小李 d72a0da52c 优化 2026-05-30 09:28:11 +08:00
tangxinyue ce76f694b8 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-05-29 18:33:25 +08:00
tangxinyue 43efe376ec 完成pdd购物模块 2026-05-29 18:32:20 +08:00
小李 9210162f17 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-29 15:30:14 +08:00
小李 dc692498db 通话详情,招商银行,61主题 2026-05-29 15:30:08 +08:00
tangxinyue 8eca8cc6ce Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-27 17:55:32 +08:00
小李 b0af3d1d5e Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-27 17:55:19 +08:00
tangxinyue 4b8dc92739 oppo短信图片预览 2026-05-27 17:55:11 +08:00
小李 4cb094c272 优化通话 2026-05-27 17:55:02 +08:00
tangxinyue 0cef1a2016 oppo短信发送图片 2026-05-27 16:59:04 +08:00
tangxinyue ca12683f7e Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-05-27 16:48:30 +08:00
tangxinyue f0bb586b14 oppp短信发送图片 2026-05-27 16:47:38 +08:00
小李 1d87a13e69 版本号 2026-05-27 16:38:52 +08:00
小李 86434ab815 通话详情页 2026-05-27 16:14:34 +08:00
tangxinyue 1d4fd42cbb 优化 2026-05-25 09:40:19 +08:00
小李 eb59411bda 工资条水印跳转 2026-05-21 17:54:56 +08:00
tangxinyue cfe224cf83 修改淘宝闪购充值来源名称 2026-05-21 17:49:45 +08:00
tangxinyue d04cf2cd2f Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-21 17:30:19 +08:00
tangxinyue ce4a66ac3c 添加水印,计算进行中闪购订单 2026-05-21 17:30:09 +08:00
小李 865c3e5715 证书,锦旗加上水印 2026-05-21 17:29:30 +08:00
tangxinyue 4cb7784d4a 完成外卖列表详情 2026-05-21 16:31:33 +08:00
tangxinyue 1bb582d51a 完成关于本机页面上滑毛玻璃效果 2026-05-19 16:34:22 +08:00
tangxinyue 2d488612b4 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-19 14:21:14 +08:00
tangxinyue b7e2889875 完成关于本机 2026-05-19 14:20:16 +08:00
小李 5b40a807e4 充值页优化 2026-05-19 14:20:08 +08:00
小李 970a252352 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-05-18 17:20:19 +08:00
小李 00946076aa 充值主题 2026-05-18 17:18:47 +08:00
tangxinyue b46210a235 新建外卖详情页 2026-05-15 18:34:52 +08:00
小李 d690a8d48c Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-15 09:38:41 +08:00
小李 07d50cc1e9 锦旗优化,证书优化 2026-05-15 09:38:13 +08:00
tangxinyue 519c0f85d6 完成淘宝购物模块 2026-05-15 09:36:12 +08:00
tangxinyue 55c624621d Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-13 18:17:24 +08:00
tangxinyue 7012a8624c 淘宝购物详情页80% 2026-05-13 18:16:38 +08:00
小李 b250034b47 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-13 17:56:18 +08:00
小李 2d13055961 锦旗优化,证书首页 2026-05-13 17:56:13 +08:00
tangxinyue 026d39a0e5 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-05-11 11:26:38 +08:00
tangxinyue 20c4e7a0de 完成列表页 2026-05-11 11:25:45 +08:00
小李 56e34166ba 锦旗 2026-05-09 10:16:24 +08:00
小李 5acaddf086 新增锦旗 2026-05-08 14:31:39 +08:00
小李 b3b76b9bd9 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-05-06 09:20:12 +08:00
小李 133ec5b7ff 版本号 2026-05-06 09:19:22 +08:00
tangxinyue 5e7738a6db 修改版本号 2026-04-30 16:21:56 +08:00
tangxinyue feb6491733 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-30 15:27:30 +08:00
tangxinyue 3586dc4a35 修改webview 2026-04-30 15:26:27 +08:00
小李 38cbbd2ed1 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-29 17:50:46 +08:00
小李 c54c630b30 充值页面主题 2026-04-29 17:50:28 +08:00
tangxinyue 1601a3e4ae 修改启动白屏 2026-04-29 14:28:18 +08:00
tangxinyue f3be53fe2f 细化uni-alipay-other埋点,替换12306火车票样式 2026-04-27 11:14:14 +08:00
tangxinyue e6aa5f29ed 优化京东购物模块 2026-04-27 09:19:48 +08:00
tangxinyue 8c39d28ff0 替换京东随机数据 2026-04-24 14:56:30 +08:00
tangxinyue 0962120ab4 测试 2026-04-20 11:10:46 +08:00
tangxinyue de27c2770d 测试Eslint 2026-04-20 10:51:25 +08:00
tangxinyue 6983450d59 测试Eslint 2026-04-20 10:46:27 +08:00
tangxinyue c6ecc56060 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-20 09:32:41 +08:00
tangxinyue c506a7fd42 修改支付问题 2026-04-20 09:32:31 +08:00
小李 2b46401bd8 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-17 18:05:55 +08:00
小李 8c560a0b8d 1 2026-04-17 18:05:31 +08:00
tangxinyue 009422675d Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-17 11:16:11 +08:00
tangxinyue 2ea96ae0fa 修改飞书记录问题 2026-04-17 11:16:04 +08:00
小李 436c94b9a8 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-17 09:42:30 +08:00
小李 8c2355c8a0 新增证书 2026-04-17 09:42:17 +08:00
tangxinyue e01f4865e6 完成京东秒送外卖模块 2026-04-15 18:38:50 +08:00
tangxinyue bce2fd578f 购物新增页面85%详情页20% 2026-04-13 18:34:13 +08:00
tangxinyue 21eb405746 完成京东购物 2026-04-10 15:29:12 +08:00
tangxinyue 52af4ca4ff Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-10 14:08:58 +08:00
tangxinyue c7a434a24d 完成京东购物模块 2026-04-10 14:08:42 +08:00
小李 3fc1fd8261 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-04-10 14:07:56 +08:00
小李 712b19dc4b genhuan 2026-04-10 14:07:08 +08:00
小李 ad67486e13 更换图片 2026-04-10 14:06:53 +08:00
tangxinyue 107f39efef 完成京东购物新增删除编辑查看详情 2026-04-09 18:30:33 +08:00
tangxinyue 8c3298af2a 完成京东购物新增 2026-04-08 18:32:10 +08:00
tangxinyue a2dc82ab4b 短信优化地区可修改 2026-04-07 16:02:17 +08:00
tangxinyue 7c65356773 完成从夯到拉排名 2026-04-03 15:42:44 +08:00
tangxinyue 2eb5f2be92 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-03 13:55:14 +08:00
tangxinyue ebe71e1688 完成从夯道拉排名 2026-04-03 13:54:54 +08:00
小李 c00cb2e135 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-04-02 13:48:39 +08:00
小李 fb3eec2ea3 充值页面新增兑换会员码 2026-04-02 13:48:32 +08:00
tangxinyue 755aff7422 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-03-28 15:44:32 +08:00
tangxinyue b839c6c4ff 京东购物列表页50% 2026-03-28 15:43:55 +08:00
小李 4b25dc3b07 充值页,版本号 2026-03-28 11:31:11 +08:00
小李 a5d27a3283 充值页样式 2026-03-28 11:06:23 +08:00
小李 583ea5a536 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-28 10:52:30 +08:00
小李 b17e6c0147 字体其他 2026-03-28 10:52:21 +08:00
tangxinyue 7dcc94c937 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-28 10:46:26 +08:00
tangxinyue 0a87094581 修改转账成功页面间距 2026-03-28 10:46:08 +08:00
小李 01500366f2 充值页 2026-03-28 10:37:59 +08:00
小李 30a63eda71 充值页面 2026-03-28 10:37:43 +08:00
小李 c97560d691 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-27 09:41:29 +08:00
小李 bd995c7d04 充值页 2026-03-27 09:41:18 +08:00
tangxinyue 3fd398ffa4 转账成功页面添加埋点 2026-03-26 12:03:02 +08:00
tangxinyue 4dbe88a024 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-03-26 11:51:39 +08:00
tangxinyue 935ea47b92 完成转账成功页面及购物app首页 2026-03-26 11:50:54 +08:00
小李 fb6942abbd 日志 2026-03-24 15:20:11 +08:00
小李 6229949c1a Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-19 17:05:48 +08:00
小李 f96b11a0cc 通话苹果样式 2026-03-19 17:05:35 +08:00
tangxinyue cfbcb6ac8d 优化苹果短信样式 2026-03-19 16:59:39 +08:00
tangxinyue 78c99caaa5 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-03-19 15:29:34 +08:00
tangxinyue 3b14a94cce 替换iphone短信图标 2026-03-19 15:26:11 +08:00
小李 7b1d55dfa3 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-03-19 15:20:07 +08:00
小李 003d11355c 头部通话提示 2026-03-19 15:18:12 +08:00
tangxinyue bd829907f4 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-19 14:56:30 +08:00
tangxinyue cab9b6b4fd 添加双击切换发言人功能添加华为通知信息,调整vivo搜素样式 2026-03-19 14:56:12 +08:00
小李 288b62c8b1 通话引导提示 2026-03-19 11:12:17 +08:00
小李 f005971605 身份证样式对齐 2026-03-18 11:26:49 +08:00
小李 7856f31273 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-03-18 11:14:13 +08:00
小李 2eb1981b8d Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	pages/index/index.nvue
2026-03-18 11:13:21 +08:00
tangxinyue 16d2ea2cbd 短信界面添加水印 2026-03-18 10:54:45 +08:00
tangxinyue 2c7da360f4 完成支付宝短信 2026-03-18 10:12:20 +08:00
tangxinyue 261918d871 短信完成80% 2026-03-16 17:32:44 +08:00
小李 b5685a0d6f 身份证,通话 2026-03-16 17:32:36 +08:00
tangxinyue a88ddf46bc 完成oppo,vivo聊天界面ui 2026-03-13 10:52:42 +08:00
tangxinyue 0959867786 完成苹果,华为,小米短信聊天界面ui 2026-03-12 18:34:58 +08:00
tangxinyue eefe793b8f 完成短信列表样式 2026-03-11 18:31:57 +08:00
小李 21c79178fd Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-11 11:24:26 +08:00
小李 bd99f8e013 通话样式组件封装完成 2026-03-11 11:24:00 +08:00
tangxinyue 48eec51491 合并代码 2026-03-11 11:23:53 +08:00
tangxinyue ef71a0a669 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-10 16:30:48 +08:00
tangxinyue 465f53d15b 完成短信入口及导航栏 2026-03-10 16:30:29 +08:00
小李 6095979884 封装通话组件 2026-03-10 16:29:23 +08:00
小李 b1e6fa9368 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-10 09:57:32 +08:00
小李 41fa7f7398 通话列表模拟 2026-03-10 09:57:25 +08:00
tangxinyue 16b3a536a4 完成飞猪火车票 2026-03-10 09:46:54 +08:00
tangxinyue b8975f5015 完成去哪儿火车票 2026-03-09 16:01:04 +08:00
tangxinyue 46de3cbd57 完成去哪儿火车票 2026-03-06 14:19:05 +08:00
tangxinyue 0e43722c8e Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-05 16:26:56 +08:00
tangxinyue b149dbd858 修改火车票埋点配置 2026-03-05 16:26:38 +08:00
小李 4a63d5415f Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-05 11:12:13 +08:00
小李 62f9f50d35 身份证 2026-03-05 11:12:05 +08:00
tangxinyue b77ee8073d 调整火车票修改逻辑 2026-03-04 17:05:48 +08:00
tangxinyue bf692f48e5 完成携程火车票 2026-03-04 17:00:36 +08:00
tangxinyue b7ae173cd5 1.调整充值页
2.完成携程火车票界面ui
2026-03-04 11:37:29 +08:00
tangxinyue 14d4245a62 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	pages/index/index.nvue
2026-03-03 15:48:32 +08:00
tangxinyue cfce21f87e 火车票app首页 2026-03-03 15:45:32 +08:00
小李 a092014941 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-03-03 15:44:35 +08:00
小李 b15217007a 身份证 2026-03-03 15:44:04 +08:00
tangxinyue e233f84b4d 修改充值页字体引用 2026-03-03 14:55:08 +08:00
tangxinyue d8f440ed1f 修改小宝模拟器bug 2026-03-02 14:36:46 +08:00
728 changed files with 71584 additions and 3793 deletions

8
.eslintignore Normal file
View File

@ -0,0 +1,8 @@
node_modules/
uni_modules/
unpackage/
dist/
*.min.js
*.css
*.scss
*.json

41
.eslintrc.js Normal file
View File

@ -0,0 +1,41 @@
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
'vue/setup-compiler-macros': true
},
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential'
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module'
},
plugins: [
'vue'
],
// 核心拦截规则
rules: {
'no-undef': 'error', // 静态拦截致命错误:防止引用不存在的参数(造成崩溃的主因)
'no-unused-vars': 'warn', // 变量未使用仅作黄牌警告,降低大盘爆红率
'vue/multi-word-component-names': 'off', // 兼容历史单字组件命名
'no-empty': 'warn',
'no-constant-condition': 'warn'
},
globals: {
uni: 'readonly',
plus: 'readonly',
wx: 'readonly',
getApp: 'readonly',
getCurrentPages: 'readonly',
setTimeout: 'readonly',
clearTimeout: 'readonly',
setInterval: 'readonly',
clearInterval: 'readonly',
console: 'readonly',
__dirname: 'readonly'
}
}

2
.husky/pre-commit Normal file
View File

@ -0,0 +1,2 @@
#!/usr/bin/env sh
npx lint-staged

352
App.vue
View File

@ -1,194 +1,206 @@
<script>
export default {
globalData: {
NativeEvent: true,
recentNativeEvent: "", //
recentNativeData: 0 //
},
onLaunch: function (options) {
// === wgt ===
console.log('=== App Launch 开始 ===')
console.log('启动参数:', JSON.stringify(options))
console.log('环境:', process.env.NODE_ENV)
export default {
globalData: {
NativeEvent: true,
recentNativeEvent: "", //
recentNativeData: 0 //
},
onLaunch: function(options) {
// console.log=()=>{}
// === wgt ===
console.log('=== App Launch 开始 ===')
console.log('启动参数:', JSON.stringify(options))
console.log('环境:', process.env.NODE_ENV)
uni.setStorageSync('onNativeEventReceive', "no")
//
uni.removeStorageSync('jumpTarget_url');
const startTime = Date.now()
uni.setStorageSync('onNativeEventReceive', "no")
//
uni.removeStorageSync('jumpTarget_url');
const startTime = Date.now()
// 1.
const systemInfo = uni.getSystemInfoSync()
uni.setStorageSync('systemInfo', {
platform: systemInfo.platform,
system: systemInfo.system,
osName: systemInfo.osName,
osVersion: systemInfo.osVersion,
statusBarHeight: systemInfo.statusBarHeight,
windowWidth: systemInfo.windowWidth,
windowHeight: systemInfo.windowHeight,
isIOS: systemInfo.platform === 'ios',
isAndroid: systemInfo.platform === 'android'
})
// 1.
const systemInfo = uni.getSystemInfoSync()
uni.setStorageSync('systemInfo', {
platform: systemInfo.platform,
system: systemInfo.system,
osName: systemInfo.osName,
osVersion: systemInfo.osVersion,
statusBarHeight: systemInfo.statusBarHeight,
windowWidth: systemInfo.windowWidth,
windowHeight: systemInfo.windowHeight,
isIOS: systemInfo.platform === 'ios',
isAndroid: systemInfo.platform === 'android'
})
// 2. 宿
this.setupNativeEventListener()
// 2. 宿
this.setupNativeEventListener()
// 3.
this.initConfig(options)
// 3.
this.initConfig(options)
//
console.log(`=== App 启动完成,耗时: ${Date.now() - startTime}ms ===`)
//
console.log(`=== App 启动完成,耗时: ${Date.now() - startTime}ms ===`)
//
this.$apiUserEvent('all', {
type: 'event',
key: 'index',
value: "进入支付宝模拟器首页",
})
},
//
this.$apiUserEvent('all', {
type: 'event',
key: 'index',
value: "进入支付宝模拟器首页",
})
},
onShow: function () {
console.log('App Show')
// onLaunch
},
onShow: function() {
console.log('App Show')
// onLaunch
},
onHide: function () {
console.log('App Hide')
},
onExit: function () {
//
// #ifdef APP
// plus.sqlite.closeDatabase({ name: 'zyds' })
uni.sendNativeEvent('unimp_stop_alipay', "stop", ret => {
// console.log('宿App' + ret);
});
// #endif
console.log('App onExit')
},
onHide: function() {
console.log('App Hide')
},
onExit: function() {
//
// #ifdef APP
// plus.sqlite.closeDatabase({ name: 'zyds' })
uni.sendNativeEvent('unimp_stop_alipay', "stop", ret => {
// console.log('宿App' + ret);
});
// #endif
console.log('App onExit')
},
methods: {
/**
* 设置宿主消息监听 onLaunch 中调用
*/
setupNativeEventListener() {
if (this.globalData.NativeEvent) {
this.globalData.NativeEvent = false
console.log('开始监听宿主消息')
methods: {
/**
* 设置宿主消息监听 onLaunch 中调用
*/
setupNativeEventListener() {
if (this.globalData.NativeEvent) {
this.globalData.NativeEvent = false
console.log('开始监听宿主消息')
uni.onNativeEventReceive((event, data) => {
if (event) {
console.log('接收到宿主消息:', event, data)
if (typeof uni.onNativeEventReceive === 'function') {
uni.onNativeEventReceive((event, data) => {
if (event) {
console.log('接收到宿主消息:', event, data)
if (event == "token") {
let header = uni.getStorageSync('header') || {}
header["x-token"] = data
uni.setStorageSync('header', header)
console.log('已更新 token')
if (event == "token") {
let header = uni.getStorageSync('header') || {}
header["x-token"] = data
uni.setStorageSync('header', header)
console.log('已更新 token')
//宿
try {
this.$getUserInfo()
} catch (error) {
console.error('获取用户信息失败:', error)
//宿
try {
this.$getUserInfo()
} catch (error) {
console.error('获取用户信息失败:', error)
}
} else if (event == "jump") {
if (data) {
console.log('接收到跳转指令,已缓存目标地址:', data);
uni.setStorageSync('jumpTarget_url', data);
// onShow
uni.reLaunch({
url: '/pages/index/index'
});
}
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
this.globalData.recentNativeEvent = event
this.globalData.recentNativeData = data
}
}
} else if (event == "jump") {
if (data) {
console.log('接收到跳转指令,已缓存目标地址:', data);
uni.setStorageSync('jumpTarget_url', data);
// onShow
uni.reLaunch({
url: '/pages/index/index'
});
}
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
this.globalData.recentNativeEvent = event
this.globalData.recentNativeData = data
})
} else {
console.log('uni.onNativeEventReceive 不可用,跳过监听')
}
}
},
/**
* 初始化应用配置
*/
initConfig(options) {
console.log('=== 配置初始化 ===')
//
const hasExtraData = options?.referrerInfo?.extraData &&
JSON.stringify(options.referrerInfo.extraData) !== '{}'
console.log('宿主是否传递配置:', hasExtraData)
if (hasExtraData) {
console.log('→ 使用宿主 extraData')
this.initFromExtraData(options.referrerInfo.extraData)
} else {
console.log('→ 宿主未传递配置,使用默认配置')
// 退使
this.initDevelopmentConfig()
}
},
/**
* 从外部数据初始化配置
*/
initFromExtraData(extraData) {
// Storage I/O
const storageData = {
host: extraData.host,
header: {
"x-token": extraData['x-token'],
"x-version": extraData['x-version'],
"x-platform": extraData['x-platform'],
"x-device-id": extraData['x-device-id'],
"x-mobile-brand": extraData['x-mobile-brand'],
"x-mobile-model": extraData['x-mobile-model'],
"x-channel": extraData['x-channel'],
"x-package": extraData['x-package'],
"x-click-id": extraData['x-click-id'],
// #ifdef APP-PLUS
"header": {
"version": this.$version,
}
}
// #endif
},
encrypt: extraData['encrypt'],
decrypt: extraData['decrypt']
}
//
Object.keys(storageData).forEach(key => {
uni.setStorageSync(key, storageData[key])
})
if (extraData['isCombo']) {
uni.setStorageSync('isCombo', extraData['isCombo'])
}
},
/**
* 初始化开发环境配置也作为默认配置
*/
initDevelopmentConfig() {
console.log('初始化默认配置')
//
const devConfig = {
host: "https://flaunt.batiao8.com/",
header: {
"x-token": "ebe42caa-845a-4cb2-a324-18abe8ced7c0"
},
decrypt: "e4rOtnF8tJjtHO7ecZeJHN1rapED5ImB",
encrypt: "xn08hYoizXhZ1zHP8DVqfCm2yHxPmhil"
}
Object.keys(devConfig).forEach(key => {
uni.setStorageSync(key, devConfig[key])
})
console.log('默认配置初始化完成')
}
},
/**
* 初始化应用配置
*/
initConfig(options) {
console.log('=== 配置初始化 ===')
//
const hasExtraData = options?.referrerInfo?.extraData &&
JSON.stringify(options.referrerInfo.extraData) !== '{}'
console.log('宿主是否传递配置:', hasExtraData)
if (hasExtraData) {
console.log('→ 使用宿主 extraData')
this.initFromExtraData(options.referrerInfo.extraData)
} else {
console.log('→ 宿主未传递配置,使用默认配置')
// 退使
this.initDevelopmentConfig()
}
},
/**
* 从外部数据初始化配置
*/
initFromExtraData(extraData) {
// Storage I/O
const storageData = {
host: extraData.host,
header: {
"x-token": extraData['x-token'],
"x-version": extraData['x-version'],
"x-platform": extraData['x-platform'],
"x-device-id": extraData['x-device-id'],
"x-mobile-brand": extraData['x-mobile-brand'],
"x-mobile-model": extraData['x-mobile-model'],
"x-channel": extraData['x-channel'],
"x-package": extraData['x-package'],
"x-click-id": extraData['x-click-id'],
// #ifdef APP-PLUS
"header": {
"version": this.$version,
}
// #endif
},
encrypt: extraData['encrypt'],
decrypt: extraData['decrypt']
}
//
Object.keys(storageData).forEach(key => {
uni.setStorageSync(key, storageData[key])
})
},
/**
* 初始化开发环境配置也作为默认配置
*/
initDevelopmentConfig() {
console.log('初始化默认配置')
//
const devConfig = {
host: "https://flaunt.batiao8.com/",
header: { "x-token": "ebe14dab-1879-4c5d-9148-727b96b30aad" },
decrypt: "e4rOtnF8tJjtHO7ecZeJHN1rapED5ImB",
encrypt: "xn08hYoizXhZ1zHP8DVqfCm2yHxPmhil"
}
Object.keys(devConfig).forEach(key => {
uni.setStorageSync(key, devConfig[key])
})
console.log('默认配置初始化完成')
}
}
}
</script>
<style>
@import "./common/color.css";
/* #ifndef APP-NVUE */
@import "./common/color.css";
/* #endif */
</style>

View File

@ -30,4 +30,4 @@ page {
line-height: 1.5;
color: var(--text-color);
background-color: var(--page-bg-color);
}
}

View File

@ -130,401 +130,401 @@ text {
}
.codefun-ml-2 {
margin-left: 4rpx;
margin-left: 2px;
}
.codefun-mt-2 {
margin-top: 4rpx;
margin-top: 2px;
}
.codefun-ml-4 {
margin-left: 8rpx;
margin-left: 4px;
}
.codefun-mt-4 {
margin-top: 8rpx;
margin-top: 4px;
}
.codefun-ml-6 {
margin-left: 12rpx;
margin-left: 6px;
}
.codefun-mt-6 {
margin-top: 12rpx;
margin-top: 6px;
}
.codefun-ml-8 {
margin-left: 16rpx;
margin-left: 8px;
}
.codefun-mt-8 {
margin-top: 16rpx;
margin-top: 8px;
}
.codefun-ml-10 {
margin-left: 20rpx;
margin-left: 10px;
}
.codefun-mt-10 {
margin-top: 20rpx;
margin-top: 10px;
}
.codefun-ml-12 {
margin-left: 24rpx;
margin-left: 12px;
}
.codefun-mt-12 {
margin-top: 24rpx;
margin-top: 12px;
}
.codefun-ml-14 {
margin-left: 28rpx;
margin-left: 14px;
}
.codefun-mt-14 {
margin-top: 28rpx;
margin-top: 14px;
}
.codefun-ml-16 {
margin-left: 32rpx;
margin-left: 16px;
}
.codefun-mt-16 {
margin-top: 32rpx;
margin-top: 16px;
}
.codefun-ml-18 {
margin-left: 36rpx;
margin-left: 18px;
}
.codefun-mt-18 {
margin-top: 36rpx;
margin-top: 18px;
}
.codefun-ml-20 {
margin-left: 40rpx;
margin-left: 20px;
}
.codefun-mt-20 {
margin-top: 40rpx;
margin-top: 20px;
}
.codefun-ml-22 {
margin-left: 44rpx;
margin-left: 22px;
}
.codefun-mt-22 {
margin-top: 44rpx;
margin-top: 22px;
}
.codefun-ml-24 {
margin-left: 48rpx;
margin-left: 24px;
}
.codefun-mt-24 {
margin-top: 48rpx;
margin-top: 24px;
}
.codefun-ml-26 {
margin-left: 52rpx;
margin-left: 26px;
}
.codefun-mt-26 {
margin-top: 52rpx;
margin-top: 26px;
}
.codefun-ml-28 {
margin-left: 56rpx;
margin-left: 28px;
}
.codefun-mt-28 {
margin-top: 56rpx;
margin-top: 28px;
}
.codefun-ml-30 {
margin-left: 60rpx;
margin-left: 30px;
}
.codefun-mt-30 {
margin-top: 60rpx;
margin-top: 30px;
}
.codefun-ml-32 {
margin-left: 64rpx;
margin-left: 32px;
}
.codefun-mt-32 {
margin-top: 64rpx;
margin-top: 32px;
}
.codefun-ml-34 {
margin-left: 68rpx;
margin-left: 34px;
}
.codefun-mt-34 {
margin-top: 68rpx;
margin-top: 34px;
}
.codefun-ml-36 {
margin-left: 72rpx;
margin-left: 36px;
}
.codefun-mt-36 {
margin-top: 72rpx;
margin-top: 36px;
}
.codefun-ml-38 {
margin-left: 76rpx;
margin-left: 38px;
}
.codefun-mt-38 {
margin-top: 76rpx;
margin-top: 38px;
}
.codefun-ml-40 {
margin-left: 80rpx;
margin-left: 40px;
}
.codefun-mt-40 {
margin-top: 80rpx;
margin-top: 40px;
}
.codefun-ml-42 {
margin-left: 84rpx;
margin-left: 42px;
}
.codefun-mt-42 {
margin-top: 84rpx;
margin-top: 42px;
}
.codefun-ml-44 {
margin-left: 88rpx;
margin-left: 44px;
}
.codefun-mt-44 {
margin-top: 88rpx;
margin-top: 44px;
}
.codefun-ml-46 {
margin-left: 92rpx;
margin-left: 46px;
}
.codefun-mt-46 {
margin-top: 92rpx;
margin-top: 46px;
}
.codefun-ml-48 {
margin-left: 96rpx;
margin-left: 48px;
}
.codefun-mt-48 {
margin-top: 96rpx;
margin-top: 48px;
}
.codefun-ml-50 {
margin-left: 100rpx;
margin-left: 50px;
}
.codefun-mt-50 {
margin-top: 100rpx;
margin-top: 50px;
}
.codefun-ml-52 {
margin-left: 104rpx;
margin-left: 52px;
}
.codefun-mt-52 {
margin-top: 104rpx;
margin-top: 52px;
}
.codefun-ml-54 {
margin-left: 108rpx;
margin-left: 54px;
}
.codefun-mt-54 {
margin-top: 108rpx;
margin-top: 54px;
}
.codefun-ml-56 {
margin-left: 112rpx;
margin-left: 56px;
}
.codefun-mt-56 {
margin-top: 112rpx;
margin-top: 56px;
}
.codefun-ml-58 {
margin-left: 116rpx;
margin-left: 58px;
}
.codefun-mt-58 {
margin-top: 116rpx;
margin-top: 58px;
}
.codefun-ml-60 {
margin-left: 120rpx;
margin-left: 60px;
}
.codefun-mt-60 {
margin-top: 120rpx;
margin-top: 60px;
}
.codefun-ml-62 {
margin-left: 124rpx;
margin-left: 62px;
}
.codefun-mt-62 {
margin-top: 124rpx;
margin-top: 62px;
}
.codefun-ml-64 {
margin-left: 128rpx;
margin-left: 64px;
}
.codefun-mt-64 {
margin-top: 128rpx;
margin-top: 64px;
}
.codefun-ml-66 {
margin-left: 132rpx;
margin-left: 66px;
}
.codefun-mt-66 {
margin-top: 132rpx;
margin-top: 66px;
}
.codefun-ml-68 {
margin-left: 136rpx;
margin-left: 68px;
}
.codefun-mt-68 {
margin-top: 136rpx;
margin-top: 68px;
}
.codefun-ml-70 {
margin-left: 140rpx;
margin-left: 70px;
}
.codefun-mt-70 {
margin-top: 140rpx;
margin-top: 70px;
}
.codefun-ml-72 {
margin-left: 144rpx;
margin-left: 72px;
}
.codefun-mt-72 {
margin-top: 144rpx;
margin-top: 72px;
}
.codefun-ml-74 {
margin-left: 148rpx;
margin-left: 74px;
}
.codefun-mt-74 {
margin-top: 148rpx;
margin-top: 74px;
}
.codefun-ml-76 {
margin-left: 152rpx;
margin-left: 76px;
}
.codefun-mt-76 {
margin-top: 152rpx;
margin-top: 76px;
}
.codefun-ml-78 {
margin-left: 156rpx;
margin-left: 78px;
}
.codefun-mt-78 {
margin-top: 156rpx;
margin-top: 78px;
}
.codefun-ml-80 {
margin-left: 160rpx;
margin-left: 80px;
}
.codefun-mt-80 {
margin-top: 160rpx;
margin-top: 80px;
}
.codefun-ml-82 {
margin-left: 164rpx;
margin-left: 82px;
}
.codefun-mt-82 {
margin-top: 164rpx;
margin-top: 82px;
}
.codefun-ml-84 {
margin-left: 168rpx;
margin-left: 84px;
}
.codefun-mt-84 {
margin-top: 168rpx;
margin-top: 84px;
}
.codefun-ml-86 {
margin-left: 172rpx;
margin-left: 86px;
}
.codefun-mt-86 {
margin-top: 172rpx;
margin-top: 86px;
}
.codefun-ml-88 {
margin-left: 176rpx;
margin-left: 88px;
}
.codefun-mt-88 {
margin-top: 176rpx;
margin-top: 88px;
}
.codefun-ml-90 {
margin-left: 180rpx;
margin-left: 90px;
}
.codefun-mt-90 {
margin-top: 180rpx;
margin-top: 90px;
}
.codefun-ml-92 {
margin-left: 184rpx;
margin-left: 92px;
}
.codefun-mt-92 {
margin-top: 184rpx;
margin-top: 92px;
}
.codefun-ml-94 {
margin-left: 188rpx;
margin-left: 94px;
}
.codefun-mt-94 {
margin-top: 188rpx;
margin-top: 94px;
}
.codefun-ml-96 {
margin-left: 192rpx;
margin-left: 96px;
}
.codefun-mt-96 {
margin-top: 192rpx;
margin-top: 96px;
}
.codefun-ml-98 {
margin-left: 196rpx;
margin-left: 98px;
}
.codefun-mt-98 {
margin-top: 196rpx;
margin-top: 98px;
}
.codefun-ml-100 {
margin-left: 200rpx;
margin-left: 100px;
}
.codefun-mt-100 {
margin-top: 200rpx;
margin-top: 100px;
}

View File

@ -256,7 +256,12 @@
display: flex;
}
.shrink-0 {
flex-shrink: 0;
}
.flex-column {
display: flex;
flex-direction: column;
}
@ -282,6 +287,11 @@
align-items: center;
}
.flex-align-start {
display: flex;
align-items: flex-start;
}
.flex-justify-center {
display: flex;
justify-content: center;

View File

@ -12,7 +12,7 @@
</view>
<view class="money alipay-font"
:class="item.isAdd ? (isBalance ? 'add-color' : 'red-add-color') : 'minus-color', { 'line-height-51rpx': isBalance }">
{{ item.isAdd ? '+' : '-' }}{{ Number(item.money).toFixed(2) }}
{{ item.isAdd ? '+' : '-' }}{{ numberUtil.formatMoneyWithThousand(item.money) }}
</view>
</view>
@ -39,7 +39,7 @@
<view v-if="item.isRefund" class="refund" :class="{ 'item-box': !isBalance }">已全额退款</view>
<view v-if="isBalance" class="balance secondary" :class="{ 'item-box': !isBalance }">余额
<text class="balance-text wx-font-regular">{{
Number(item.balance).toFixed(2)
numberUtil.formatMoneyWithThousand(item.balance)
}}</text>
</view>
</view>
@ -58,6 +58,10 @@ import {
reactive
} from 'vue'
import {
numberUtil
} from '@/utils/common.js'
//
const props = defineProps({
list: {
@ -94,7 +98,7 @@ const handleTouchStart = (e, item) => {
event: e,
item
})
}, 1500)
}, 1000)
}
const handleTouchMove = (e) => {

View File

@ -0,0 +1,232 @@
<template>
<view class="header" :class="['header_'+type]">
<view class="title" v-if="type!='vivo'">
{{title}}
</view>
<view class="search" v-if="type!='oppo'&&type!='huawei'&&type!='vivo'">
<view class="left" >
<image src="/static/image/call/iosSearchLeft.png" mode=""></image>
{{searchTitle}}
</view>
<image v-if="type=='ios'" src="/static/image/call/iosSearchRight.png" mode=""></image>
</view>
<view class="selectType" v-if="type=='huawei'">
<view class="btn " :class="{'active':active}" @click.stop="setActive(true)">
全部来电
</view>
<view class="btn" :class="{'active':!active}" @click.stop="setActive(false)">
未接来电
</view>
</view>
<view class="selectTypeVivo" v-if="type=='vivo'">
<view class="btn " :class="{'active':active}" @click.stop="setActive(true)">
全部
</view>
<view class="btn" :class="{'active':!active}" @click.stop="setActive(false)">
未接
</view>
</view>
<view class="select" v-if="type=='xiaomi'">
全部通话
<image src="/static/image/call/xiaomiHeaderSelectImg.png" mode=""></image>
</view>
</view>
</template>
<script setup>
import {
onMounted,
reactive,
ref,
toRefs
} from 'vue'
const topPopup = ref()
//
const props = defineProps({
type: {
type: String,
default: 'ios'
},
})
const data = reactive({
active:true,
statusBarHeight: 0,
showTipLayer: true,
title:'最近通话',
searchTitle:"搜索",
list:[
"个人收藏",
"最近通话",
"通讯录",
"拨号键盘",
"语音留言"
]
})
let {
active,
searchTitle,
title,
list,
showTipLayer
} = toRefs(data)
onMounted(() => {
if(props.type=='xiaomi'){
searchTitle.value="搜索联系人"
title.value="通话"
// console.log('aaaaaaaaaaa')
}else if(props.type=='oppo'){
title.value="通话"
}else if(props.type=='huawei'){
title.value="电话"
}
})
function setActive(status){
active.value=status
uni.$emit('setActive',status)
}
</script>
<style scoped lang="scss">
.header{
padding: 0 15px;
background-color: #fff;
.title{
font-weight: bold;
font-size: 32px;
color: #1A1A1A;
}
.search{
margin-top: 10px;
width: 100%;
height: 34px;
background: #EEEEF0;
border-radius: 12px 12px 12px 12px;
display: flex;
align-items: center;
justify-content: space-between;
.left{
display: flex;
align-items: center;
font-size: 16px;
color: #838383;
}
image{
width: 16px;
height: 16px;
margin: 0 8px;
}
}
.select{
padding-left: 15px;
margin-top: 20px;
display: flex;
align-items: center;
font-weight: 400;
font-size: 13px;
color: #8B8DA5;
image{
width: 13px;
height: 13px;
margin-left: 6px;
}
}
}
.selectTypeVivo{
padding-top: 18px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
.btn{
width: 62px;
background-color: #F6F6F6;
text-align: center;
font-size: 13px;
color: #555555;
height: 30px;
line-height: 30px;
border-radius: 8px 0 0 8px;
margin: 0 1px;
}
.btn:nth-child(2){
border-radius:0 8px 8px 0;
}
.active{
font-size: 13px;
color: #1A1A1A;
background: #DCF6E6;
}
}
.header_huawei{
padding-bottom: 14px;
.title{
padding-left: 3px;
font-weight: bold;
font-size: 30px !important;
color: #1A1A1A !important;
}
.selectType{
margin-top: 18px;
height: 38px;
background: #F4F4F4;
border-radius: 19px 19px 19px 19px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 2px;
.btn{
width: 50%;
text-align: center;
font-size: 13px;
color: #555555;
height: 34px;
line-height: 34px;
}
.active{
font-size: 13px;
color: #1A1A1A;
background: #fff;
border-radius: 19px 19px 19px 19px;
}
}
}
.header_xiaomi{
.title{
padding-left: 15px;
font-weight: 400 !important;
font-size: 30px !important;
color: #1A1A1A !important;
}
.search{
margin-top: 10px;
height: 41px !important;
background: #F0F0F0 !important;
border-radius: 21px 21px 21px 21px !important;
.left{
font-size: 16px !important;
color: #A9A9A9 !important;
}
image{
width: 16px;
height: 16px;
margin: 0 8px;
}
}
}
.header_xiaomi{
.search{
.left{
margin-left: 20rpx;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,375 @@
<template>
<view>
<uni-nav-bar :backgroundColor="navBgColor" class="nav-bar" :border="false" :title="title" fixed="true"
statusBar="true">
<template v-slot:left>
<slot name="left">
<view :class="[type+'LeftTitle']" :style="{opacity:(type!='huawei'&&type!='oppo'?1: navOpacity)}">
{{LeftTitle}}
</view>
</slot>
</template>
<view class="nav-bar-title " :class="['nav-bar-title-'+type]">
<slot>
<view class="iosBox" v-if="type=='ios'">
<view class="btn " :class="{'active':active}" @click.stop="setActive(true)">
全部来电
</view>
<view class="btn" :class="{'active':!active}" @click.stop="setActive(false)">
未接来电
</view>
</view>
<view class="oppoBox" v-if="type=='oppo'" :style="{opacity: navOpacity?1-navOpacity:1}">
<view class="btn " :class="{'active':active}" @click.stop="setActive(true)">
全部
</view>
<view class="btn" :class="{'active':!active}" @click.stop="setActive(false)">
未接
</view>
</view>
<view class="title" v-else-if="type=='xiaomi'" :style="{opacity: navOpacity}">
通话
</view>
</slot>
</view>
<template v-slot:right>
<slot name="right">
<view class="rightImgBox" :class="['rightImgBox'+type]">
<image v-if="type=='oppo'||type=='vivo'" class="rightImg"
:src="`/static/image/call/${type}NavRightImg2.png`"></image>
<image v-if="type!='ios'" class="rightImg" :class="['rightImg_'+type]"
:src="`/static/image/call/${type}NavRightImg.png`"></image>
</view>
</slot>
</template>
</uni-nav-bar>
<view class="tipLayer" :style="{ top: `${45 + data.statusBarHeight}px` }" v-if="isTipLayer&&showTipLayer">
<view class="tipLayer-content">
<view class="title">
<slot name="tipLayer">点击此处<text>[{{ tipLayerText }}]</text></slot>
</view>
<image class="close" src="/static/image/common/tipLayer-close.png" mode="" @click="closeTipLayer"></image>
<image v-if="type=='ios'||type=='oppo'" class="triangleImg" src="/static/image/common/tipLayer-eye2.png"></image>
<image v-else class="triangleImg" src="/static/image/common/tipLayer-eye.png"></image>
</view>
</view>
</view>
</template>
<script setup>
import {
onMounted,
reactive,
ref,
toRefs,
watch
} from 'vue'
const topPopup = ref()
//
const props = defineProps({
bgColor: {
type: String,
default: '#fff'
},
textColor: {
type: String,
default: '#000'
},
title: {
type: String,
default: ''
},
type: {
type: String,
default: 'ios'
},
scrollTop: {
type: Number,
default: 0
},
tipLayerText: {
type: String,
default: ''
},
isTipLayer: {
type: Boolean,
default: false
},
})
const data = reactive({
active: true,
statusBarHeight: 0,
LeftTitle: '',
showTipLayer: true,
navOpacity: 0, //
navBgColor: props.bgColor //
})
let {
active,
LeftTitle,
showTipLayer,
navOpacity,
navBgColor
} = toRefs(data)
// scrollTop
watch(() => props.scrollTop, (newValue, oldValue) => {
// console.log('scrollTop changed:', newValue);
// scrollTop
if (newValue > 0) {
// scrollTop 0
navOpacity.value = Math.min(1, newValue / 100);
//
// navBgColor.value = `rgba(255, 255, 255, ${navOpacity.value})`;
} else {
// scrollTop 0 1
navOpacity.value = 0;
// navBgColor.value = props.bgColor;
}
});
onMounted(() => {
if (props.type == 'ios') {
LeftTitle.value = '编辑'
} else if (props.type == 'vivo') {
LeftTitle.value = '拨号'
} else if (props.type == 'oppo') {
LeftTitle.value = '通话'
} else if (props.type == 'huawei') {
LeftTitle.value = '电话'
} else {
LeftTitle.value = ''
}
//
const systemInfo = uni.getSystemInfoSync();
data.statusBarHeight = systemInfo.statusBarHeight || 0;
if (props.isTipLayer) {
if (uni.getStorageSync("call_" + props.type) == props.type) {
showTipLayer.value = false
}
}
})
function setActive(status) {
active.value = status
uni.$emit('setActive', status)
}
const closeTipLayer = () => {
showTipLayer.value = false
uni.setStorageSync("call_" + props.type, props.type)
emit("refresh")
}
</script>
<style scoped lang="scss">
::v-deep .uni-navbar__header-btns {
width: 27vw !important;
}
.iosLeftTitle {
font-weight: 400;
font-size: 16px;
color: #018AE0;
}
.rightImgBox {
display: flex;
align-items: center;
image {
width: 24px;
height: 24px;
}
}
.rightImg_oppo {
margin-left: 26px;
}
.rightImg_vivo {
margin-left: 26px;
margin-right: 16px;
}
.oppoLeftTitle {
padding-left: 8px;
font-weight: bold;
font-size: 32px;
color: #1A1A1A;
}
.huaweiLeftTitle {
padding-left: 8px;
font-weight: bold;
font-size: 30px;
color: #1A1A1A;
}
.vivoLeftTitle {
padding-left: 34px;
font-weight: bold;
font-size: 30px;
color: #1A1A1A;
}
.nav-bar-title-xiaomi {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
font-weight: bold;
font-size: 18px;
color: #1A1A1A;
}
.nav-bar-title-oppo {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
.oppoBox {
padding: 0 2px;
width: 100%;
display: flex;
align-items: center;
justify-content: space-around;
width: 137px;
height: 34px;
background: #E9E9E9;
border-radius: 17px 17px 17px 17px;
.btn {
font-size: 12px;
color: #6B6B6B;
width: 67px;
height: 30px;
text-align: center;
line-height: 30px;
}
.active {
color: #1A1A1A;
background: #FFFFFF;
border-radius: 17px 17px 17px 17px;
}
}
}
.nav-bar-title-ios {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
.iosBox {
width: 100%;
display: flex;
align-items: center;
justify-content: space-around;
width: 145px;
height: 30px;
background: #EEEEEE;
border-radius: 8px 8px 8px 8px;
.btn {
font-size: 12px;
color: #1A1A1A;
width: 69px;
height: 26px;
text-align: center;
line-height: 26px;
}
.active {
background: #FFFFFF;
box-shadow: 0px 0px 2px 0px rgba(0, 0, 0, 0.1);
border-radius: 6px 6px 6px 6px;
margin: 0 2px;
}
}
}
.rightImg_xiaomi {
width: 20px !important;
height: 20px !important;
}
.rightImg_huawei {
width: 38px !important;
height: 38px !important;
}
.tipLayer {
box-sizing: border-box;
min-width: 200px !important;
height: 48px;
background: #B8EDFE;
border-radius: 8px 8px 8px 8px;
position: fixed;
/* top: 115px; */
left: 50%;
transform: translateX(-50%);
z-index: 999;
.tipLayer-content {
position: relative;
.title {
font-weight: 450;
font-size: 14px;
color: #268FFF;
line-height: 48px;
text-align: center;
text {
font-size: 14px;
font-weight: bold;
color: #006ADD;
}
::v-deep text {
font-size: 14px;
font-weight: bold;
color: #006ADD;
}
}
.triangleImg {
width: 111px;
height: 52px;
pointer-events: none;
position: absolute;
top: -23px;
left: calc(50% - 111px);
}
.triangle {
position: absolute;
top: -57px;
left: calc(50% - 40px);
pointer-events: none;
}
.close {
position: absolute;
top: -5px;
right: -5px;
width: 18px;
height: 18px;
}
}
}
</style>

View File

@ -0,0 +1,193 @@
<template>
<view class="footer" :class="['footer_'+type]" :style="data.style">
<view class="item" v-for="(item,index) in list" :key="index">
<image :src="`/static/image/call/${type}TabbarImg${index+1}.png`" mode=""></image>
<text>{{item}}</text>
</view>
</view>
<view class="footer footerZhangwei" :class="['footer_'+type]">
</view>
</template>
<script setup>
import {
onMounted,
reactive,
ref,
toRefs
} from 'vue'
const topPopup = ref()
//
const props = defineProps({
bgColor: {
type: String,
default: '#fff'
},
textColor: {
type: String,
default: '#000'
},
title: {
type: String,
default: ''
},
type: {
type: String,
default: 'ios'
},
type2: {
type: String,
default: ''
},
})
const data = reactive({
statusBarHeight: 0,
showTipLayer: true,
style:{},
list: [
"个人收藏",
"最近通话",
"通讯录",
"拨号键盘",
"语音留言"
]
})
let {
list,
showTipLayer
} = toRefs(data)
onMounted(() => {
if (props.type == 'xiaomi') {
list.value = ["通话",
"联系人",
"营业厅"
]
}else if (props.type == 'oppo') {
list.value = ["通话",
"联系人",
"营业厅"
]
}else if (props.type == 'huawei') {
list.value = ["电话",
"联系人",
"收藏"
]
}else if (props.type == 'vivo') {
list.value = ["拨号",
"联系人",
"收藏"
]
}
if(props.type2=='ios'){
data.style={'background-color':props.bgColor}
}
})
</script>
<style scoped lang="scss">
.footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: space-around;
background: #f7f7f7;
padding-top: 2px;
.item {
display: flex;
flex-direction: column;
text-align: center;
align-items: center;
image {
width: 32px;
height: 32px;
}
text {
font-size: 10px;
color: #959597;
}
}
}
.footer_ios {
padding-bottom: constant(safe-area-inset-bottom) !important; // IOS<11.2
padding-bottom: env(safe-area-inset-bottom) !important; // IOS>11.2
.item:nth-child(2) {
text {
color: #007AFC;
}
}
}
.footerZhangwei{
position: relative !important;
opacity: 0 !important;
padding-top: 0 !important;
height: 47px;
background-color: rgba(0, 0, 0, 0) !important;
}
.footer_huawei {
background: #FAFAFA !important;
padding-top: 3px;
.item {
image {
width: 24px;
height: 24px;
}
}
.item:nth-child(1) {
text {
color: #0060EA;
}
}
}
.footer_vivo {
background: #FAFAFA !important;
padding-top: 13px;
padding-bottom: 13px;
.item {
image {
width: 24px;
height: 24px;
}
}
.item:nth-child(1) {
text {
color: #1BA552;
}
}
}
.footer_xiaomi ,.footer_oppo{
padding-top: 7px;
padding-bottom: 17px;
background-color: #fff !important;
.item {
image {
width: 24px;
height: 24px;
}
text {
font-size: 10px;
color: #999999;
}
}
.item:nth-child(1) {
text {
color: #333;
}
}
}
</style>

View File

@ -0,0 +1,246 @@
<template>
<view class="auto-width-input-container" :class="{ 'is-textarea': type === 'textarea' }">
<!-- 测量层用于计算宽度的影藏文本必须保持与 input 相同的字体样式 -->
<text v-if="type !== 'textarea'" class="measure-text"
:style="[inputStyle, { visibility: 'hidden', position: 'absolute', whiteSpace: 'nowrap' }]">
{{ modelValue || placeholder }}
</text>
<!-- 输入层 -->
<template v-if="type !== 'textarea'">
<input v-if="!disabled" class="auto-input" :type="type" :value="modelValue" :placeholder="placeholder"
:placeholder-style="placeholderStyle" :style="[inputStyle, { width: finalInputWidth }]" @input="onInput"
:maxlength="maxlength" :focus="isFocus" @blur="onBlur" :readonly="readonly" />
<view v-else class="auto-input flex-align-center"
:style="[inputStyle, { width: finalInputWidth }, !modelValue ? placeholderStyleObject : {}]">
{{ modelValue || placeholder }}
</view>
</template>
<template v-else>
<textarea v-if="!disabled" class="auto-textarea" :value="modelValue" :placeholder="placeholder"
:placeholder-style="placeholderStyle" :style="[inputStyle]" @input="onInput" :maxlength="maxlength"
auto-height :focus="isFocus" @blur="onBlur" :readonly="readonly" />
<view v-else class="auto-textarea" :style="[inputStyle, !modelValue ? placeholderStyleObject : {}]">
{{ modelValue || placeholder }}
</view>
</template>
<!-- 编辑图标 -->
<image v-if="showEdit" class="edit-icon" src="/static/image/common/edit.png" @click="handleFocusIcon"></image>
</view>
</template>
<script setup>
import { ref, computed, watch, nextTick, getCurrentInstance, onMounted } from 'vue';
const props = defineProps({
modelValue: {
type: [String, Number],
default: ''
},
type: {
type: String,
default: 'text' // 'number', 'tel', 'digit' 'textarea'
},
placeholder: {
type: String,
default: '请输入'
},
placeholderStyle: {
type: String,
default: 'color: #999;'
},
fontSize: {
type: String,
default: '28rpx'
},
fontWeight: {
type: String,
default: 'normal'
},
color: {
type: String,
default: '#1A1A1A'
},
maxlength: {
type: Number,
default: 140
},
minWidth: {
type: String,
default: '20rpx'
},
extraWidth: {
type: Number,
default: 10 //
},
showEdit: {
type: Boolean,
default: false
},
readonly: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
}
});
const emit = defineEmits(['update:modelValue', 'change']);
const instance = getCurrentInstance();
const inputWidth = ref(props.minWidth);
//
const finalInputWidth = computed(() => {
// textarea 使
if (props.type === 'textarea') return '100%';
// class
const classStr = instance.proxy.$attrs.class || '';
if (classStr.includes('flex-1') || classStr.includes('w100')) {
return '100%';
}
return inputWidth.value;
});
const isFocus = ref(false);
const placeholderStyleObject = computed(() => {
const style = {};
if (!props.placeholderStyle) return style;
const parts = props.placeholderStyle.split(';');
parts.forEach(part => {
const [key, value] = part.split(':');
if (key && value) {
const camelKey = key.trim().replace(/-([a-z])/g, (g) => g[1].toUpperCase());
style[camelKey] = value.trim();
}
});
return style;
});
const inputStyle = computed(() => ({
fontSize: props.fontSize,
fontWeight: props.fontWeight,
color: props.color,
fontFamily: 'inherit'
}));
/**
* 点击图标触发聚焦
*/
const handleFocusIcon = () => {
isFocus.value = false;
nextTick(() => {
isFocus.value = true;
});
};
/**
* 失去焦点处理
*/
const onBlur = () => {
isFocus.value = false;
};
/**
* 核心逻辑测量隐藏文本的物理宽度
*/
const updateWidth = () => {
if (props.type === 'textarea') return;
//
nextTick(() => {
const query = uni.createSelectorQuery().in(instance.proxy);
query.select('.measure-text').boundingClientRect(data => {
if (data && data.width) {
heatWidth(data.width);
}
}).exec();
});
};
const heatWidth = (width) => {
//
inputWidth.value = (width + props.extraWidth) + 'px';
};
const onInput = (e) => {
const val = e.detail.value;
emit('update:modelValue', val);
emit('change', val);
};
//
watch(() => props.modelValue, () => {
updateWidth();
});
//
onMounted(() => {
updateWidth();
});
</script>
<style lang="less" scoped>
.auto-width-input-container {
position: relative;
display: inline-flex; //
align-items: center;
vertical-align: middle;
max-width: 100%;
flex-wrap: nowrap;
// flex-1 width: 100% flex
&.flex-1,
&.w100 {
display: flex !important;
width: 100% !important;
}
&.is-textarea {
display: flex !important;
width: 100%;
align-items: flex-start;
flex-direction: row !important;
}
.measure-text {
left: -9999rpx;
top: -9999rpx;
pointer-events: none;
}
.auto-input {
min-width: v-bind('props.minWidth');
padding: 0;
margin: 0;
text-align: inherit;
height: 1.4em;
line-height: 1.4em;
flex-shrink: 0;
// input
& {
flex: 1;
}
}
.auto-textarea {
flex: 1 !important;
width: 0 !important;
min-width: 0;
min-height: 1.4em;
padding: 0;
margin: 0;
line-height: 1.4em;
display: block;
}
.edit-icon {
width: 28rpx;
height: 28rpx;
margin-left: 8rpx;
flex-shrink: 0;
margin-top: 4rpx;
}
}
</style>

View File

@ -0,0 +1,223 @@
<template>
<view>
<view class="right-btn" @click="open">
<image src="/static/image/recharge/rightBtnImg.png" mode=""></image>
</view>
<view class="maskExchange" v-if="isMaskExchange">
<view class="box">
<view class="info" v-if="!isExchange">
<view :style="styles">
<uni-easyinput :styles="styles" v-model="code" placeholder="请输入兑换码" :inputBorder="false"
placeholderStyle="text-align: center;"></uni-easyinput>
</view>
<view class="btn" :class="{'noValue':code==''}" @click="getExchange">
兑换
</view>
</view>
<view class="info" v-else>
<view class="title">
{{couponVip.exchange_name}}
</view>
<view class="title">
{{couponVip.exchange_value}}
</view>
<view class="time">
截至日期{{couponVip.expire_time}}
</view>
<view class="btn btn2" @click="submit">
兑换
</view>
</view>
</view>
<view class="close" @click="close">
<image src="/static/image/recharge/closeE.png" mode=""></image>
</view>
</view>
</view>
</template>
<script>
import {
postJson
} from "@/utils/requests.js"
export default {
name: "exchange",
data() {
return {
code: "",
isMaskExchange: false,
isExchange: false,
couponVip: {
},
styles: {
backgroundColor: '#FFFCEC',
padding: '10px ',
'border-radius': '10px'
}
};
},
methods: {
open() {
this.isMaskExchange = true
this.isExchange = false
this.code = ''
},
close() {
this.isMaskExchange = false
this.code = ''
},
async getExchange() {
if (this.code == '') {
return
}
let couponVip = await this.$requestPromise({
url: 'api/activity/exchange',
method: "GET",
data: {
code: this.code
}
})
console.log(couponVip)
if (couponVip.code != 0) {
uni.showToast({
icon: "none",
title: "兑换码有误"
})
return
}
this.isExchange = true
this.couponVip = couponVip.data
},
async submit() {
let exchangeRes = await postJson('q', 'api/activity/exchange', {
code: this.code
})
if (exchangeRes.code != 0) {
uni.showToast({
icon: "none",
title: "券已使用过了"
})
return
} else {
uni.showToast({
icon: "none",
title: "兑换成功"
})
//app
let user = await proxy.$requestPromise({
url: 'api/user',
method: "GET",
data: {}
})
if (user.code == 0) {
data.appUser = user.data
console.log("app用户信息", data.appUser);
}
this.isMaskExchange = false
this.isExchange = false
uni.navigateBack({
delta: 1
});
}
}
}
}
</script>
<style lang="scss">
.maskExchange {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
width: 100vw;
height: 100vh;
z-index: 999;
background-color: rgba(0, 0, 0, .5);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.box {
background-image: url('/static/image/recharge/exchange.png');
background-size: 307px 120px;
background-repeat: no-repeat;
width: 307px;
padding: 120px 0 20px 0;
.info {
padding: 16px;
background-color: #fff;
border-radius: 0 0 26px 26px;
input {
background: #FFFCEC;
border-radius: 11px 11px 11px 11px;
padding: 18px 0;
text-align: center;
}
.title {
font-size: 20px;
color: #1a1a1a;
text-align: center;
margin-bottom: 24px;
}
.time {
font-size: 16px;
color: #AAAAAA;
text-align: center;
}
}
.btn {
color: #fff;
margin-top: 32px;
font-size: 18px;
padding: 16px 0;
color: #FFFFFF;
background: linear-gradient(360deg, #4B3F30 0%, #181713 56.43%, #504834 100%), #D8D8D8;
border-radius: 60px;
text-align: center;
}
.btn2 {
margin-top: 12px;
}
.noValue {
opacity: 0.5;
}
}
.close {
margin-top: 16px;
width: 34px;
height: 34px;
image {
width: 100%;
height: 100%;
}
}
}
.right-btn {
position: fixed;
top: 100rpx;
right: 36rpx;
z-index: 1;
width: 54px;
height: 26px;
image {
width: 100%;
height: 100%;
}
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,327 @@
<template>
<view class="preview-container" :class="phone === 'mi' ? 'mi-bg' : 'oppo-bg'" v-if="show">
<template v-if="phone === 'oppo' || !phone || phone === 'iphone' || phone === 'huawei' || phone === 'vivo'">
<view class="header" @tap.stop :style="{ 'padding-top': statusBarHeight }">
<image class="icon-back" src="/static/image/phone-message/oppo/back-white.png" @tap="close"></image>
<image class="icon-download" src="/static/image/phone-message/oppo/save-white.png"></image>
</view>
<!-- 图片显示 -->
<swiper class="preview-swiper" :current="current" @change="onChange">
<swiper-item v-for="(imgSrc, index) in images" :key="index">
<image class="preview-img" :src="imgSrc" mode="aspectFit"></image>
</swiper-item>
</swiper>
</template>
<template v-else-if="phone === 'mi'">
<view class="mi-header" @tap.stop :style="{ 'padding-top': statusBarHeight }">
<!-- 复用现有返回图标并通过滤镜反色 -->
<image class="mi-icon-back" src="/static/image/phone-message/oppo/back-white.png" @tap="close"></image>
<view class="mi-header-center">
<view class="mi-date">{{ currentFormatDate }}</view>
<view class="mi-time">{{ currentFormatTime }}</view>
</view>
<view class="mi-icon-right"></view> <!-- 占位符以居中 -->
</view>
<swiper class="preview-swiper mi-swiper" :current="current" @change="onChange">
<swiper-item v-for="(imgSrc, index) in images" :key="index">
<view class="mi-img-container">
<image class="mi-preview-img" :src="imgSrc" mode="aspectFill"></image>
</view>
</swiper-item>
</swiper>
<view class="mi-footer">
<view class="mi-footer-item">
<image class="mi-footer-icon" src="/static/image/phone-message/mi/baocun.png"></image>
<view class="mi-footer-text">保存</view>
</view>
<view class="mi-footer-item">
<view class="mi-more-icon"></view>
<view class="mi-footer-text">更多</view>
</view>
</view>
</template>
</view>
</template>
<script setup>
import { ref, watch, computed } from 'vue';
const statusBarHeight = (uni.getSystemInfoSync().statusBarHeight || 44) + 'px';
const props = defineProps({
show: Boolean,
images: {
type: Array,
default: () => []
},
times: {
type: Array,
default: () => []
},
currentIndex: {
type: Number,
default: 0
},
phone: {
type: String,
default: ''
}
});
const emit = defineEmits(['update:show']);
const current = ref(0);
watch(() => props.currentIndex, (val) => {
current.value = val;
});
const onChange = (e) => {
current.value = e.detail.current;
};
const currentFormatDate = computed(() => {
if (props.times && props.times.length > current.value) {
const timeVal = props.times[current.value];
if (timeVal) {
if (typeof timeVal === 'string' && timeVal.includes('-')) {
const parts = timeVal.split(' ');
if (parts.length > 0) {
const dateParts = parts[0].split('-');
if (dateParts.length === 3) {
return `${dateParts[0]}${parseInt(dateParts[1])}${parseInt(dateParts[2])}`;
}
}
}
// Fallback
const safeTimeStr = String(timeVal).replace(/-/g, '/');
const d = new Date(safeTimeStr);
if (!isNaN(d.getTime())) {
return `${d.getFullYear()}${d.getMonth() + 1}${d.getDate()}`;
}
}
}
return '';
});
const currentFormatTime = computed(() => {
if (props.times && props.times.length > current.value) {
const timeVal = props.times[current.value];
if (timeVal) {
if (typeof timeVal === 'string' && timeVal.includes(':')) {
const parts = timeVal.split(' ');
if (parts.length > 1) {
return parts[1];
}
}
// Fallback
const safeTimeStr = String(timeVal).replace(/-/g, '/');
const d = new Date(safeTimeStr);
if (!isNaN(d.getTime())) {
const h = d.getHours().toString().padStart(2, '0');
const m = d.getMinutes().toString().padStart(2, '0');
return `${h}:${m}`;
}
}
}
return '';
});
const close = () => {
emit('update:show', false);
};
// const download = () => {
// const currentSrc = props.images[current.value];
// if (!currentSrc) return;
// //
// uni.saveImageToPhotosAlbum({
// filePath: currentSrc,
// success: function () {
// uni.showToast({
// title: '',
// icon: 'success'
// });
// },
// fail: function () {
// uni.showToast({
// title: '',
// icon: 'none'
// });
// }
// });
// };
</script>
<style scoped>
.preview-container {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 999999;
display: flex;
flex-direction: column;
}
.oppo-bg {
background-color: #000000;
}
.mi-bg {
background-color: #FFFFFF;
}
.header {
box-sizing: content-box;
width: 100%;
/* 预留状态栏高度 */
height: 98rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
top: 0;
left: 0;
z-index: 2;
background-color: rgba(0, 0, 0, 0.6);
}
.icon-back {
width: 48rpx;
height: 48rpx;
margin: 0 34rpx;
}
.icon-download {
width: 48rpx;
height: 48rpx;
margin: 0 34rpx;
}
.preview-swiper {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
.preview-img {
width: 100%;
height: 100%;
display: block;
}
/* ================= 小米样式 ================= */
.mi-header {
box-sizing: content-box;
width: 100%;
height: 98rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
top: 0;
left: 0;
z-index: 2;
background-color: #FFFFFF;
}
.mi-icon-back {
width: 48rpx;
height: 48rpx;
margin: 0 34rpx;
filter: invert(1);
/* 白变黑 */
}
.mi-icon-right {
width: 48rpx;
height: 48rpx;
margin: 0 34rpx;
}
.mi-header-center {
display: flex;
flex-direction: column;
align-items: center;
}
.mi-date {
font-size: 32rpx;
color: #333333;
font-weight: 500;
}
.mi-time {
font-size: 24rpx;
color: #999999;
margin-top: 4rpx;
}
.mi-swiper {
z-index: 1;
display: flex;
align-items: center;
}
.mi-img-container {
width: 100vw;
height: 100vw;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
.mi-preview-img {
width: 100%;
height: 100%;
display: block;
}
.mi-footer {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 160rpx;
background-color: #FFFFFF;
display: flex;
justify-content: space-around;
align-items: flex-start;
padding-top: 20rpx;
z-index: 2;
}
.mi-footer-item {
display: flex;
flex-direction: column;
align-items: center;
}
.mi-footer-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 8rpx;
}
.mi-more-icon {
width: 48rpx;
height: 48rpx;
line-height: 38rpx;
font-size: 40rpx;
text-align: center;
color: #000;
margin-bottom: 8rpx;
font-weight: bold;
}
.mi-footer-text {
font-size: 24rpx;
color: #666666;
}
</style>

View File

@ -0,0 +1,778 @@
<template>
<view :style="`${phone}-style`">
<uni-swipe-action class="swipe-action">
<!-- 使用插槽 请自行给定插槽内容宽度-->
<uni-swipe-action-item class="swipe-action-item" v-for="item in list" :key="item.id">
<view class="flex flex-align-center " @click="clickItem(item)">
<view class="item flex w100">
<view class="flex flex-align-center left-box">
<view :class="{ 'opacity-0': !item.unRead }" class="dot shrink-0"><text
v-if="phone == 'huawei' || phone == 'oppo'">{{ item.unReadNumber
> 99
? '99+' : (item.unReadNumber || 1) }}</text>
</view>
<image class="img avatar shrink-0" :class="item.imgShape"
:src="item.img || `/static/image/phone-message/${phone}/default.png`" mode="aspectFill">
</image>
</view>
<view class="border-box m-l-24 flex-1 flex flex-align-start">
<view class="main-box flex-1">
<view class="title-box flex-between">
<text class="title">{{ displayTitle(item.title) }}</text>
<text class="time">{{ formatDate(getLastMessage(item.chatList)?.time || item.time)
}}</text>
</view>
<view class="content">
<view v-if="isImageMsg(getLastMessage(item.chatList))"
class="flex flex-align-center">
<template v-if="phone === 'iphone'">
<text>附件: 1张照片</text>
</template>
<template v-else-if="phone === 'huawei'">
<text>您的好友给您发了一个图片</text>
</template>
<template v-else-if="phone == 'oppo'">
<image style="width: 24rpx;height: 24rpx;margin-right: 16rpx;"
src="/static/image/phone-message/oppo/link.png"></image>
<text>[图片]</text>
</template>
<template v-else>
<text>[图片]</text>
</template>
</view>
<rich-text v-else :nodes="getLastMessage(item.chatList)?.content || ''"></rich-text>
</view>
</view>
<view class="box-right h100 flex-column flex-align-center">
<image v-if="phone == 'iphone'" src="/static/image/phone-message/iphone/right.png">
</image>
<image v-if="item.noNotice && phone == 'iphone'" class="m-t-8"
src="/static/image/phone-message/iphone/notice.png">
</image>
</view>
</view>
</view>
</view>
<template v-slot:right>
<view class="flex flex-align-center" style="margin-left: 1px;">
<view class="btn flex-center flex-align-center edit" style="color: #fff;"
@click="editItem(item)">
<image :src="`/static/image/phone-message/edit.png`">
</image>
</view>
<view class="btn flex-center flex-align-center delete" @click="deleteItem(item)">
<image
:src="`/static/image/phone-message/${phone == 'huawei' || phone == 'vivo' ? 'huawei' : 'iphone'}/delete.png`">
</image>
</view>
</view>
</template>
</uni-swipe-action-item>
</uni-swipe-action>
</view>
</template>
<script setup>
import {
dateUtil
} from '@/utils/common.js';
//
const emit = defineEmits(['item-click', 'delete-item', 'edit-item'])
const props = defineProps({
//
phone: {
type: String,
default: 'iphone'
},
list: {
type: Array,
default: () => []
}
})
/**
* 时间日期格式化判断
* @param date
*/
const formatDate = (date) => {
if (props.phone == 'oppo') {
return dateUtil.formatMessageTime(date, true)
} else if (props.phone == 'huawei') {
return dateUtil.formatMessageTime(date, true, 'YYYY年M月D日')
} else if (props.phone == 'vivo') {
let d = date;
if (typeof d === 'string') {
d = new Date(d.replace(/-/g, '/'));
} else if (typeof d === 'number') {
d = new Date(d);
}
const isCurrentYear = d.getFullYear() === new Date().getFullYear();
return dateUtil.formatMessageTime(date, true, isCurrentYear ? 'M月D日' : 'YYYY/M/D')
} else {
return dateUtil.formatMessageTime(date)
}
}
/**
* 格式化联系人名称处理 OPPO 纯数字11位手机号显示
*/
const displayTitle = (title) => {
let t = title || '';
if (props.phone === 'oppo') {
const noSpace = t.replace(/\s+/g, '');
if (/^\d{11}$/.test(noSpace)) {
return noSpace.replace(/(\d{3})(\d{4})(\d{4})/, '$1 $2 $3');
}
}
return t;
}
/**
* 判断该消息是否为图片消息
*/
const isImageMsg = (message) => {
if (!message) return false;
if (message.type === 'image') return true;
if (message.content && typeof message.content === 'string') {
const str = message.content.trim();
if (str.startsWith('<img') && str.endsWith('/>') && str.indexOf('<img') === str.lastIndexOf('<img')) {
return true;
}
}
return false;
}
/**
* 获取最新一条有效消息
*/
const getLastMessage = (chatList) => {
if (!chatList || chatList.length === 0) return null;
return chatList[chatList.length - 1];
}
/**
* 点击列表元素
*/
const clickItem = (item) => {
emit('item-click', item)
}
/**
* 删除列表元素
*/
const deleteItem = (item) => {
emit('delete-item', item)
}
/**
* 修改元素
* @param item
*/
const editItem = (item) => {
emit('edit-item', item)
}
</script>
<style>
@import '@/common/main.css';
</style>
<style lang="less" scoped>
.m-t-4 {
margin-top: 4rpx;
}
.m-t-8 {
margin-top: 8rpx;
}
.m-l-24 {
margin-left: 24rpx;
}
.opacity-0 {
opacity: 0;
}
.swipe-action-item {
background-color: #FFFFFF;
}
.avatar {
border-radius: 50%;
}
.edit {
background-color: #5855D6;
image {
width: 48rpx;
height: 48rpx;
}
}
.main-box {
.title-box {
margin-bottom: 6rpx;
.title {
width: 20px;
flex: 1;
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
white-space: nowrap;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
}
.time {
flex-shrink: 0;
margin-left: 20rpx;
}
}
}
//
.iphone-style {
.circle {
border-radius: 50% !important;
}
.square {
border-radius: 16rpx !important;
}
.swipe-action {
margin-top: 28rpx;
}
.swipe-action-item:first-child {
.border-box {
position: relative;
}
.border-box::before {
position: absolute;
content: '';
width: 100%;
height: 1px;
top: 0;
left: 0;
right: 0;
background-color: #D8D8D8;
transform: scaleY(0.3);
}
}
.item {
padding: 0 0 0 14rpx;
height: 146rpx;
align-items: center;
.dot {
width: 20rpx;
height: 20rpx;
background-color: #007BFD;
border-radius: 50%;
}
.img {
width: 84rpx;
height: 84rpx;
margin-left: 16rpx;
border-radius: 50%;
}
.border-box {
position: relative;
padding: 18rpx 24rpx 14rpx 0;
height: 100%;
// box-shadow: inset 0 -0.3px 0 0 #D8D8D8;
}
.border-box::after {
position: absolute;
content: '';
width: 100%;
height: 1px;
bottom: 0;
left: 0;
right: 0;
background-color: #D8D8D8;
transform: scaleY(0.3);
}
.main-box {
.title-box {
margin-bottom: 6rpx;
.title {
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
font-weight: 600;
}
.time {
color: #838383;
font-size: 28rpx;
line-height: 28rpx;
margin-right: 18rpx;
}
}
.content {
font-size: 28rpx;
line-height: 38rpx;
color: #838383;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
.box-right {
image {
width: 28rpx;
height: 28rpx;
}
}
}
.btn {
width: 140rpx;
height: 146rpx;
image {
width: 48rpx;
height: 48rpx;
}
}
.delete {
background-color: #FC3E30;
}
.edit {
background-color: #5855D6;
}
}
//
.mi-style {
.item {
padding-top: 44rpx;
height: 170rpx;
.left-box {
position: relative;
height: 40px;
align-items: flex-start;
padding-left: 54rpx;
}
.dot {
position: absolute;
width: 20rpx;
height: 20rpx;
background-color: #FA3D30;
border-radius: 50%;
left: 16rpx;
top: 50%;
transform: translateY(-50%);
}
.img {
width: 76rpx;
height: 76rpx;
}
.border-box {
position: relative;
padding: 4rpx 52rpx 8rpx 0;
margin-left: 22rpx;
height: 100%;
}
.main-box {
.title-box {
margin-bottom: 6rpx;
.title {
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
}
.time {
color: #9A9A9A;
font-size: 26rpx;
line-height: 28rpx;
}
}
.content {
font-size: 26rpx;
line-height: 38rpx;
color: #656565;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
.box-right {
image {
width: 28rpx;
height: 28rpx;
}
}
}
.btn {
width: 140rpx;
height: 146rpx;
image {
width: 48rpx;
height: 48rpx;
}
}
.delete {
background-color: #FC3E30;
}
.edit {
background-color: #5855D6;
image {
width: 48rpx;
height: 48rpx;
}
}
}
// oppo
.oppo-style {
.item {
padding-top: 28rpx;
height: 178rpx;
.left-box {
position: relative;
height: 40px;
align-items: flex-start;
padding-left: 34rpx;
}
.dot {
width: 28rpx;
height: 28rpx;
position: absolute;
display: flex;
align-items: center;
justify-content: center;
background-color: #E93A22;
color: #FFFFFF;
line-height: 18rpx;
font-size: 20rpx;
border-radius: 16rpx;
right: -8rpx;
top: -4rpx;
z-index: 1;
border: 2rpx solid #FFFFFF;
}
.img {
width: 76rpx;
height: 76rpx;
}
.border-box {
position: relative;
padding: 4rpx 0 28rpx 0;
margin-left: 32rpx;
margin-right: 34rpx;
height: 100%;
}
.border-box::after {
position: absolute;
content: '';
width: 100%;
height: 1px;
bottom: 0;
left: 0;
right: 0;
background-color: #E2E2E2;
transform: scaleY(0.3);
}
.main-box {
.title-box {
margin-bottom: 16rpx;
.title {
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
font-weight: 500;
}
.time {
color: #656565;
font-size: 26rpx;
line-height: 28rpx;
}
}
.content {
font-size: 28rpx;
line-height: 38rpx;
color: #737373;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
.box-right {
image {
width: 28rpx;
height: 28rpx;
}
}
}
.btn {
width: 140rpx;
height: 146rpx;
image {
width: 48rpx;
height: 48rpx;
}
}
.delete {
background-color: #FC3E30;
}
}
//
.huawei-style {
.item {
padding: 0 32rpx;
height: 156rpx;
align-items: center;
.left-box {
position: relative;
height: 40px;
align-items: flex-start;
}
.dot {
position: absolute;
padding: 6rpx 10rpx;
background-color: #E93A22;
color: #FFFFFF;
line-height: 20rpx;
font-size: 20rpx;
border-radius: 16rpx;
right: -8rpx;
top: -12rpx;
z-index: 1;
}
.img {
width: 76rpx;
height: 76rpx;
}
.border-box {
padding: 18rpx 0;
position: relative;
margin-left: 30rpx;
margin-top: 2rpx;
height: 100%;
box-shadow: 0 -0.3px 0 0 #CFCFCF;
}
// .border-box::after {
// position: absolute;
// content: '';
// width: 100%;
// height: 1px;
// bottom: 0;
// left: 0;
// right: 0;
// background-color: #CFCFCF;
// transform: scaleY(0.3);
// }
.main-box {
.title-box {
margin-bottom: 6rpx;
.title {
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
font-weight: 500;
}
.time {
color: #656565;
font-size: 24rpx;
line-height: 28rpx;
}
}
.content {
font-size: 28rpx;
line-height: 38rpx;
color: #6F6F6F;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
.box-right {
image {
width: 28rpx;
height: 28rpx;
}
}
}
.btn {
width: 140rpx;
height: 156rpx;
image {
width: 76rpx;
height: 76rpx;
}
}
.delete {
background-color: #F4F4F4;
}
.edit {
background-color: #5855D6;
image {
width: 48rpx;
height: 48rpx;
}
}
}
// vivo
.vivo-style {
.item {
padding: 0 50rpx 0 24rpx;
height: 172rpx;
align-items: center;
.dot {
width: 12rpx;
height: 12rpx;
background-color: #409DFE;
border-radius: 50%;
flex-shrink: 0;
margin-right: 10rpx;
}
.img {
width: 80rpx;
height: 80rpx;
}
.border-box {
padding: 28rpx 0 18rpx;
position: relative;
margin-left: 24rpx;
margin-top: 2rpx;
height: 100%;
}
.main-box {
.title-box {
margin-bottom: 18rpx;
.title {
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
font-weight: 500;
}
.time {
color: #7C7C7C;
font-size: 26rpx;
line-height: 26rpx;
}
}
.content {
font-size: 28rpx;
line-height: 38rpx;
color: #6F6F6F;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
.box-right {
image {
width: 28rpx;
height: 28rpx;
}
}
}
.btn {
width: 140rpx;
height: 156rpx;
image {
width: 76rpx;
height: 76rpx;
}
}
.delete {
background-color: #F4F4F4;
}
.edit {
background-color: #5855D6;
image {
width: 48rpx;
height: 48rpx;
}
}
}
</style>

View File

@ -0,0 +1,589 @@
<template>
<view :class="`${phone}-style`">
<!-- 导航样式 -->
<view class="nav-bar-box" :class="{ 'border-nav-bar-box': isScroll }">
<NavBar :isBack="false" :bgColor="isScroll ? data.navBar.bgColor : '#fff'" :buttonGroup="buttonGroup"
@button-click="util.clickTitlePopupButton" tipLayerType="message-list-tip" isTipLayer
isClickNavBarOpenPopup tipLayerText="添加短信" @add="emit('add')">
<!-- 左侧文字图标 -->
<template v-slot:left>
<view v-if="phone == 'iphone'" class="flex flex-align-center">
<image @click="util.goBack" class="left-icon" src="/static/image/phone-message/iphone/back.png"
mode=""></image>
<text class="left-text">过滤条件</text>
</view>
<view v-if="(phone == 'huawei' && isScroll) || phone == 'vivo'" class="flex flex-align-center">
<text class="left-text">信息</text>
</view>
</template>
<!-- 中间标题 -->
<template v-slot:center>
<view v-if="phone == 'iphone' && isScroll" class="center-text">
信息
</view>
</template>
<!-- 右侧图标 -->
<template v-slot:right>
<!-- iphone -->
<view v-if="phone == 'iphone'">
<image class="right-icon mg-r-30" src="/static/image/phone-message/iphone/more.png" mode="">
</image>
<image class="right-icon mg-r-5" src="/static/image/phone-message/iphone/edit.png" mode="">
</image>
</view>
<!-- mi -->
<view v-if="phone == 'mi'">
<image class="right-icon" src="/static/image/phone-message/mi/setting.png" mode=""></image>
</view>
<!-- oppo -->
<view v-if="phone == 'oppo'">
<image class="right-icon mg-r-52" src="/static/image/phone-message/oppo/search.png" mode="">
</image>
<image class="right-icon mg-r-14" src="/static/image/phone-message/oppo/more.png" mode="">
</image>
</view>
<!-- huawei -->
<view v-if="phone == 'huawei'">
<image v-if="isScroll" class="right-icon"
src="/static/image/phone-message/huawei/nav-search.png" mode="">
</image>
<image class="right-icon" src="/static/image/phone-message/huawei/add.png" mode=""></image>
<image class="right-icon" src="/static/image/phone-message/huawei/more.png" mode=""></image>
</view>
<!-- vivo -->
<view v-if="phone == 'vivo'">
<image class="right-icon" src="/static/image/phone-message/vivo/select.png" mode="">
</image>
<image class="right-icon" src="/static/image/phone-message/vivo/add.png" mode=""></image>
<image class="right-icon m-r-34" src="/static/image/phone-message/vivo/more.png" mode="">
</image>
</view>
</template>
</NavBar>
</view>
<!-- 主体内容 -->
<view class="main-container">
<!-- 顶部搜索栏样式 -->
<view class="top-box">
<view v-if="showInfo.text" class="text">{{ showInfo.text }}</view>
<view v-if="showInfo.secondText" class="second-text">{{ showInfo.secondText }}</view>
<view v-if="showInfo.placeholder" class="search-box flex flex-align-center">
<image class="icon" :src="`/static/image/phone-message/${props.phone}/search.png`">
</image>
<input class="input flex-1" :placeholder="showInfo.placeholder" type="text" disabled>
<image v-if="phone != 'mi'" class="icon"
:src="`/static/image/phone-message/${props.phone}/mic.png`">
</image>
</view>
</view>
<slot>
</slot>
</view>
<view class="bottom-placeholder"></view>
<!-- 底部样式 -->
<template v-if="phone == 'mi' || phone == 'oppo'">
<image class="add-message" @click="emit('add')"
:src="`/static/image/phone-message/${props.phone}/add-message.png`"></image>
<view class="bottom-box flex">
<view class="item flex-1 h100">
<image class="icon" :src="`/static/image/phone-message/${props.phone}/bottom-left.png`"></image>
<text>{{ showInfo.bottomLtext }}</text>
</view>
<view class="item flex-1 h100">
<image class="icon" :src="`/static/image/phone-message/${props.phone}/bottom-right.png`"></image>
<text class="grey">{{ showInfo.bottomRtext }}</text>
</view>
</view>
</template>
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import {
ref,
reactive,
computed
} from 'vue'
import {
util
} from '@/utils/common.js';
const emit = defineEmits(['add', 'setSim', 'setNoticeCount'])
const props = defineProps({
//
phone: {
type: String,
default: 'iphone'
},
//
isScroll: {
type: Boolean,
default: false
},
noticeCount: {
type: Number,
default: 0
}
})
const buttonGroup = computed(() => {
const groups = [{
name: "添加短信",
click: () => {
emit('add')
}
}, {
name: "设置卡1卡2运营商",
click: () => {
emit('setSim')
}
}]
if (props.phone == 'huawei') {
groups.push({
name: "设置通知信息未读数",
click: () => {
emit('setNoticeCount')
}
})
}
return groups
})
const data = reactive({
navBar: {
title: '信息',
bgColor: '#FFFFFF',
},
})
//
const showInfo = computed(() => {
let text, placeholder, secondText, bottomLtext, bottomRtext
switch (props.phone) {
case "iphone":
text = "信息"
placeholder = '搜索'
data.navBar.bgColor = '#F8F8F8'
break;
case "mi":
text = "主要"
placeholder = '搜索短信'
bottomLtext = "主要"
bottomRtext = "推广"
break;
case "oppo":
text = "消息"
placeholder = ''
bottomLtext = "消息"
bottomRtext = "通知"
break;
case "huawei":
text = "信息"
placeholder = '搜索信息'
secondText = props.noticeCount > 0 ? `${props.noticeCount} 条未读` : ''
break;
case "vivo":
placeholder = '搜索信息'
break;
default:
break;
}
return { text, placeholder, secondText, bottomLtext, bottomRtext }
})
</script>
<style>
@import '@/common/main.css';
page {
background-color: #FFFFFF;
}
</style>
<style lang="less" scoped>
::v-deep .uni-navbar__header-btns {
width: 100px !important;
flex: 1;
}
::v-deep .uni-navbar__header {
align-items: center !important;
}
.bottom-box {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 120rpx;
border-top: 1rpx solid #E7E7E7;
background-color: #ffffff;
z-index: 9;
.item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.icon {
width: 44rpx;
height: 44rpx;
}
text {
font-size: 20rpx;
color: #333;
}
.grey {
color: #999999;
}
}
}
//
.iphone-style {
.mg-r-30 {
margin-right: 60rpx;
}
.mg-r-5 {
margin-right: 10rpx;
}
.left-icon {
width: 40rpx;
height: 40rpx;
}
.right-icon {
width: 48rpx;
height: 48rpx;
}
.left-text {
font-size: 32rpx;
color: #0278E2;
margin-left: 10rpx;
}
.center-text {
font-size: 32rpx;
color: #1a1a1a;
}
.border-nav-bar-box {
::v-deep .nav-bar-container {
box-shadow: 0 -0.3px 0 0 #B5B5B5 inset;
}
}
.main-container {
.top-box {
padding: 20rpx 32rpx 0;
.text {
color: #1A1A1A;
font-size: 64rpx;
font-weight: 700;
line-height: 72rpx;
}
.search-box {
margin-top: 20rpx;
background-color: #EEEEF0;
height: 68rpx;
border-radius: 24rpx;
padding: 0 16rpx;
.icon {
width: 32rpx;
height: 32rpx;
}
.input {
margin: 0 14rpx;
::v-deep .input-placeholder {
color: #838383;
font-size: 32rpx;
}
}
}
}
}
}
//
.mi-style {
.right-icon {
width: 40rpx;
height: 40rpx;
margin-right: 30rpx;
}
.main-container {
.top-box {
padding: 0 22rpx 0;
.text {
color: #1A1A1A;
font-size: 60rpx;
font-weight: 500;
}
.search-box {
margin-top: 32rpx;
background-color: #F0F0F0;
height: 84rpx;
border-radius: 42rpx;
padding: 0 34rpx;
.icon {
width: 32rpx;
height: 32rpx;
}
.input {
margin: 0 18rpx;
::v-deep .input-placeholder {
color: #A9A9A9;
font-size: 32rpx;
}
}
}
}
}
.add-message {
position: fixed;
bottom: 168rpx;
right: 44rpx;
width: 120rpx;
height: 120rpx;
}
.bottom-placeholder {
height: 120rpx;
width: 100%;
}
}
// oppo
.oppo-style {
.mg-r-52 {
margin-right: 52rpx;
}
.mg-r-14 {
margin-right: 14rpx;
}
.right-icon {
width: 48rpx;
height: 48rpx;
}
.main-container {
.top-box {
padding: 16rpx 34rpx 0;
margin-bottom: 50rpx;
.text {
color: #1A1A1A;
font-size: 64rpx;
font-weight: 900;
}
}
}
.add-message {
width: 116rpx;
height: 116rpx;
position: fixed;
bottom: 200rpx;
right: 46rpx;
}
.bottom-placeholder {
height: 148rpx;
width: 100%;
}
.bottom-box {
height: 148rpx;
background-color: #FAFAFA;
border-top: 1rpx solid #DCDCDC;
.item {
.icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 12rpx;
}
text {
color: #191919;
}
.grey {
color: #717171;
}
}
}
}
//
.huawei-style {
::v-deep .uni-navbar__header-container {
flex: 0;
}
.left-text {
color: #1a1a1a;
font-size: 50rpx;
font-weight: 700;
margin-left: 10rpx;
}
.right-icon {
width: 76rpx;
height: 76rpx;
margin: 0 8rpx;
}
.border-nav-bar-box {
::v-deep .nav-bar {
border-bottom: 1rpx solid #D1D1D1;
}
}
.main-container {
.top-box {
padding: 40rpx 34rpx 0;
.text {
color: #1A1A1A;
font-size: 60rpx;
font-weight: 700;
line-height: 60rpx;
}
.second-text {
color: #646464;
font-size: 28rpx;
margin-top: 10rpx;
}
.search-box {
margin-top: 40rpx;
background-color: #F4F4F4;
height: 76rpx;
border-radius: 38rpx;
padding: 0 24rpx;
.icon {
width: 32rpx;
height: 32rpx;
}
.input {
margin: 0 14rpx;
::v-deep .input-placeholder {
color: #646464;
font-size: 32rpx;
}
}
}
}
}
}
// vivo
.vivo-style {
::v-deep .uni-navbar__header-container {
flex: 0;
}
.m-r-34 {
margin-right: 34rpx !important;
}
.right-icon {
width: 44rpx;
height: 44rpx;
margin-right: 52rpx;
}
.left-text {
font-size: 54rpx;
color: #1A1A1A;
font-weight: 700;
margin-left: 30rpx;
}
.main-container {
.top-box {
padding: 0 46rpx;
.search-box {
position: relative;
margin-top: 70rpx;
background-color: #ffffff;
height: 76rpx;
border-radius: 38rpx;
padding: 0 10rpx;
.icon {
width: 40rpx;
height: 40rpx;
flex-shrink: 0;
}
.input {
flex: 1;
margin: 0 20rpx;
::v-deep .input-placeholder {
color: #9A9A9A;
font-size: 32rpx;
}
}
}
.search-box::after {
position: absolute;
content: '';
width: 100%;
height: 12rpx;
background-color: #F0F0F0;
bottom: 0;
left: 0;
}
}
}
}
</style>

View File

@ -1,19 +1,19 @@
<template>
<view style="width: 100%;" :style="{ height: `calc(${data.statusBarHeight}px + 88rpx)` }">
<view class="placeholder-box" style="width: 100%;" :style="{ height: `calc(${data.statusBarHeight}px + 88rpx)` }">
<!-- <slot name="statusBar"></slot> -->
</view>
<view class="nav-bar-container" :style="{ backgroundColor: bgColor, zIndex: zIndex }">
<view class="status-placeholder" :style="{ height: `${data.statusBarHeight}px` }">
<slot name="statusBar"></slot>
</view>
<view style="width: 100%;height: 88rpx;" @click="openPopup">
<view class="nav-box" style="width: 100%;height: 88rpx;" @click="openPopup">
<slot>
<uni-nav-bar backgroundColor="#00000000" class="nav-bar" :border="false" :title="title" v-bind="$attrs"
v-on="$attrs">
<template v-slot:left>
<view class="nav-bar-left">
<view class="nav-bar-left" @click.stop="isClickNavBarOpenPopup ? openPopup() : ''">
<slot name="left">
<view class="left-icon" @click.stop="onBack">
<view v-if="isBack" class="left-icon" @click.stop="onBack">
<image class="nav-icon-back"
:src="`/static/image/nav-bar/back-${textColor == '#fff' || textColor == '#fffffff' ? 'white' : 'black'}.png`"
mode="">
@ -28,7 +28,7 @@
</slot>
</view>
<template v-slot:right>
<view class="nav-bar-right" @click.stop="onRightClick">
<view class="nav-bar-right" @click.stop="isClickNavBarOpenPopup ? openPopup() : onRightClick()">
<slot name="right">
<view v-if="isRightIcon" class="right-icon">
<image class="nav-icon-more"
@ -82,276 +82,294 @@
</template>
<script setup>
import popup from '../popup/popup.vue'
import {
onMounted,
reactive,
ref,
toRefs
} from 'vue'
import popup from '../popup/popup.vue'
import {
onMounted,
reactive,
ref,
toRefs
} from 'vue'
const topPopup = ref()
const topPopup = ref()
//
const props = defineProps({
bgColor: {
type: String,
default: '#fff'
},
textColor: {
type: String,
default: '#000'
},
title: {
type: String,
default: ''
},
buttonGroup: {
type: Array,
default: () => []
},
isRightIcon: {
type: Boolean,
default: false
},
isRightButton: {
type: Boolean,
default: false
},
rightButtonText: {
type: String,
default: '确定'
},
zIndex: {
type: Number,
default: 999
},
noBack: {
type: Boolean,
default: false
},
tipLayerText: {
type: String,
default: ''
},
isTipLayer: {
type: Boolean,
default: false
},
tipLayerType: {
type: String,
default: ''
//
const props = defineProps({
bgColor: {
type: String,
default: '#fff'
},
textColor: {
type: String,
default: '#000'
},
title: {
type: String,
default: ''
},
buttonGroup: {
type: Array,
default: () => []
},
isRightIcon: {
type: Boolean,
default: false
},
isRightButton: {
type: Boolean,
default: false
},
rightButtonText: {
type: String,
default: '确定'
},
zIndex: {
type: Number,
default: 999
},
noBack: {
type: Boolean,
default: false
},
isBack: {
type: Boolean,
default: true
},
tipLayerText: {
type: String,
default: ''
},
isTipLayer: {
type: Boolean,
default: false
},
tipLayerType: {
type: String,
default: ''
},
isClickNavBarOpenPopup: {
type: Boolean,
default: false
}
})
//
const emit = defineEmits(['back', 'right-click', 'button-click', 'refresh'])
const data = reactive({
statusBarHeight: 0,
showTipLayer: true,
})
let {
showTipLayer
} = toRefs(data)
onMounted(() => {
//
const systemInfo = uni.getSystemInfoSync();
data.statusBarHeight = systemInfo.statusBarHeight || 0;
if (props.isTipLayer) {
if (uni.getStorageSync(props.tipLayerType) == props.tipLayerType) {
showTipLayer.value = false
}
}
})
const closeTipLayer = () => {
showTipLayer.value = false
uni.setStorageSync(props.tipLayerType, props.tipLayerType)
emit("refresh")
}
})
//
const emit = defineEmits(['back', 'right-click', 'button-click', 'refresh'])
const data = reactive({
statusBarHeight: 0,
showTipLayer: true,
})
let {
showTipLayer
} = toRefs(data)
onMounted(() => {
//
const systemInfo = uni.getSystemInfoSync();
data.statusBarHeight = systemInfo.statusBarHeight || 0;
if (props.isTipLayer) {
if (uni.getStorageSync(props.tipLayerType) == props.tipLayerType) {
showTipLayer.value = false
const openPopup = () => {
if (props.buttonGroup.length > 0) {
topPopup.value.open()
}
}
})
const closeTipLayer = () => {
showTipLayer.value = false
uni.setStorageSync(props.tipLayerType, props.tipLayerType)
emit("refresh")
}
const openPopup = () => {
if (props.buttonGroup.length > 0) {
topPopup.value.open()
//
const onBack = () => {
emit('back')
//
if (props.noBack) return
uni.navigateBack()
}
}
//
const onBack = () => {
emit('back')
//
if (props.noBack) return
uni.navigateBack()
}
//
const onRightClick = () => {
emit('right-click')
}
//
const onRightClick = () => {
emit('right-click')
}
const closeTopPopup = () => {
topPopup.value.close()
}
const buttonClick = (button) => {
topPopup.value.close()
emit('button-click', button)
}
const buttonClick = (button) => {
closeTopPopup()
emit('button-click', button)
}
//
defineExpose({
openPopup,
closeTopPopup
})
</script>
<style scoped>
@import "/common/main.css";
@import "/common/main.css";
.nav-bar-container {
display: flex;
flex-direction: column;
position: fixed !important;
top: 0;
left: 0;
right: 0;
z-index: 999;
}
.nav-bar {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 1;
}
.status-placeholder {
width: 100%;
}
::v-deep .uni-navbar__content {
width: 100%;
}
.nav-bar-left {
display: flex;
align-items: center;
justify-content: flex-start;
}
.nav-icon-back {
width: 24px;
height: 24px;
}
.nav-icon-more {
width: 26px;
height: 26px;
}
.nav-bar-title {
flex: 1;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
font-size: 17px;
font-weight: 500;
}
.nav-bar-right {
display: flex;
align-items: center;
justify-content: flex-end;
}
.right-button {
font-size: 12px;
border-radius: 16px;
color: #fff;
text-align: center;
line-height: 30px;
height: 30px;
min-width: 60px;
background: linear-gradient(90deg, #187AFF 0%, #3295FC 100%);
}
.button-box {
width: calc(50% - 8rpx);
text-align: center;
margin-top: 16rpx;
}
.button {
border: 1px solid #E4E4E4;
border-radius: 8px;
height: 42px;
line-height: 42px;
font-size: 28rpx;
}
.tipLayer {
box-sizing: border-box;
min-width: 200px !important;
height: 48px;
background: #B8EDFE;
border-radius: 8px 8px 8px 8px;
position: fixed;
/* top: 115px; */
left: 50%;
transform: translateX(-50%);
z-index: 999;
}
.tipLayer-content {
position: relative;
}
.title {
font-weight: 450;
font-size: 14px;
color: #268FFF;
line-height: 48px;
text-align: center;
text {
font-size: 14px;
font-weight: bold;
color: #006ADD;
.nav-bar-container {
display: flex;
flex-direction: column;
position: fixed !important;
top: 0;
left: 0;
right: 0;
z-index: 999;
}
::v-deep text {
font-size: 14px;
font-weight: bold;
color: #006ADD;
.nav-bar {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
z-index: 1;
}
}
.triangleImg {
width: 111px;
height: 52px;
pointer-events: none;
position: absolute;
top: -23px;
left: calc(50% - 111px);
}
.status-placeholder {
width: 100%;
}
.triangle {
position: absolute;
top: -57px;
left: calc(50% - 40px);
pointer-events: none;
}
::v-deep .uni-navbar__content {
width: 100%;
}
.close {
position: absolute;
top: -5px;
right: -5px;
width: 18px;
height: 18px;
}
.nav-bar-left {
display: flex;
align-items: center;
justify-content: flex-start;
}
.nav-icon-back {
width: 24px;
height: 24px;
}
.nav-icon-more {
width: 26px;
height: 26px;
}
.nav-bar-title {
flex: 1;
margin: auto;
display: flex;
align-items: center;
justify-content: center;
font-size: 17px;
font-weight: 500;
}
.nav-bar-right {
display: flex;
align-items: center;
justify-content: flex-end;
}
.right-button {
font-size: 12px;
border-radius: 16px;
color: #fff;
text-align: center;
line-height: 30px;
height: 30px;
min-width: 60px;
background: linear-gradient(90deg, #187AFF 0%, #3295FC 100%);
}
.button-box {
width: calc(50% - 8rpx);
text-align: center;
margin-top: 16rpx;
}
.button {
border: 1px solid #E4E4E4;
border-radius: 8px;
height: 42px;
line-height: 42px;
font-size: 28rpx;
}
.tipLayer {
box-sizing: border-box;
min-width: 200px !important;
height: 48px;
background: #B8EDFE;
border-radius: 8px 8px 8px 8px;
position: fixed;
/* top: 115px; */
left: 50%;
transform: translateX(-50%);
z-index: 999;
}
.tipLayer-content {
position: relative;
}
.title {
font-weight: 450;
font-size: 14px;
color: #268FFF;
line-height: 48px;
text-align: center;
text {
font-size: 14px;
font-weight: bold;
color: #006ADD;
}
::v-deep text {
font-size: 14px;
font-weight: bold;
color: #006ADD;
}
}
.triangleImg {
width: 111px;
height: 52px;
pointer-events: none;
position: absolute;
top: -23px;
left: calc(50% - 111px);
}
.triangle {
position: absolute;
top: -57px;
left: calc(50% - 40px);
pointer-events: none;
}
.close {
position: absolute;
top: -5px;
right: -5px;
width: 18px;
height: 18px;
}
</style>

View File

@ -0,0 +1,831 @@
<template>
<view class="shopping-card" @longpress="onLongPress">
<!-- Header -->
<view class="card-header">
<view class="shop-info">
<!-- <view class="">
</view> -->
<image v-if="item.shopType === 'self'" style="width: 50rpx;margin-right: 12rpx;border-radius: 4rpx;"
src="/static/image/shopping/jingdong/detail/ziyin.png" mode="widthFix"></image>
<image v-if="item.shopType === 'waimai'" style="width: 50rpx;margin-right: 8rpx;border-radius: 4rpx;"
src="/static/image/shopping/jingdong/waimai/waimai.png" mode="widthFix"></image>
<!-- <view class="tag waimai-tag">外卖</view> -->
<image class="jd-tag" v-if="item.shopType === 'jd'" src="/static/image/shopping/jingdong/JD.png">
</image>
<text class="shop-name">{{ item.shopName }}</text>
<uni-icons class="right-icon" type="right" size="12" color="#1A1A1A"></uni-icons>
</view>
<view class="order-status">
<view v-if="item.status === '等待付款'" class="status-warning">
<image style="width: 116rpx;height: 30rpx;" src="/static/image/shopping/jingdong/dengdaifukuan.png">
</image>
<text class="status-desc" v-if="item.statusDesc">{{
item.shopType != 'waimai' ? formatStatusDesc(item.statusDesc) :
formatWaimaiStatusDesc(item.statusDesc) }}</text>
</view>
<text v-else class="status-text"
:class="{ red: item.statusColor === 'red' || item.status === '正在出库' || item.status === '商家备餐中' || item.status === '骑手到店取餐中' || item.status === '骑手已到店' }">{{
item.status }}</text>
</view>
</view>
<!-- Tracking -->
<view class="tracking-box" :class="{ 'waimai-tracking': item.shopType === 'waimai' }"
v-if="item.trackingTitle || item.trackingDesc">
<view class="waimai-tracking-icon" v-if="item.shopType === 'waimai'">
<image style="width: 32rpx; height: 32rpx;" src="/static/image/shopping/jingdong/waimai-logo.png"
mode="aspectFit" v-if="!item.trackingIcon">
</image>
<!-- <image style="width: 28rpx; height: 28rpx;" :src="item.trackingIcon" mode="aspectFit" v-else></image> -->
</view>
<view class="tracking-content">
<view class="tracking-header" v-if="item.shopType !== 'waimai'">
<image class="truck-icon" v-if="!item.trackingIcon"
src="/static/image/shopping/jingdong/cangku.png">
</image>
<image class="truck-icon" :src="item.trackingIcon" mode="aspectFit" v-else></image>
<text class="tracking-title">{{ item.trackingTitle }}</text>
</view>
<view class="tracking-main-title" v-else>
<view class="rich-title">预计<span style="color:#E40C24">{{ item.trackingTitle }}</span> 可送达</view>
</view>
<text class="tracking-desc" v-if="item.trackingDesc">{{ item.trackingDesc }}</text>
<text class="tracking-time">{{ item.trackingTime }}</text>
</view>
<uni-icons v-if="item.shopType !== 'waimai'" class="right-icon" type="right" size="14"
color="#3D3D3D"></uni-icons>
</view>
<!-- Product -->
<view class="product-box"
:class="{ multiple: (item.products && item.products.length > 1) || (item.images && item.images.length > 0) }">
<template v-if="item.products && item.products.length > 1">
<scroll-view class="product-images-scroll" scroll-x :show-scrollbar="false">
<image class="product-img small" v-for="(prod, idx) in item.products" :key="idx" :src="prod.image"
mode="aspectFill"></image>
</scroll-view>
</template>
<template v-else-if="item.products && item.products.length === 1">
<image class="product-img" :src="item.products[0].image" mode="aspectFill"></image>
</template>
<template v-else-if="item.images && item.images.length > 0">
<scroll-view class="product-images-scroll" scroll-x :show-scrollbar="false">
<image class="product-img small" v-for="(img, idx) in item.images" :key="idx" :src="img"
mode="aspectFill"></image>
</scroll-view>
</template>
<template v-else>
<image class="product-img" :src="item.image" mode="aspectFill"></image>
<view class="product-info">
<text class="product-title" :class="{ 'shopping-title': item.shopType !== 'waimai' }">{{
truncatedTitle }}</text>
<text class="product-desc" v-if="item.desc">{{ item.desc }}</text>
<view class="product-tags" v-if="item.tags && item.tags.length">
<text class="tag" v-for="(tag, index) in item.tags" :key="index">{{ tag }}</text>
</view>
</view>
</template>
<view class='flex flex-justify-between flex-align-center'
:class="{ 'flex-1': item.products && item.products.length === 1 }">
<view class="product-info flex-1">
<template v-if="item.products && item.products.length === 1">
<text class="product-title" :class="{ 'shopping-title': item.shopType !== 'waimai' }">{{
truncatedTitle }}</text>
<text class="product-desc" v-if="item.products[0].desc">{{ item.products[0].desc }}</text>
<view class="product-tags" v-if="item.products[0].service">
<text class="tag">{{ item.products[0].service }}</text>
</view>
</template>
</view>
<view class="product-price-box">
<view class="price-wrap wx-font-regular">
<text class="price-symbol"></text>
<text class="price-num">{{ item.shopType === 'waimai' ?
(item.totalPrice ? Number(item.totalPrice).toFixed(2) : '0.00')
:
safeFormatPrice(item)
}}</text>
</view>
<text class="product-count"
v-if="item.count || (item.products && item.products[0] && item.products[0].count)">{{
item.count || (item.products && item.products[0] && item.products[0].count) }}</text>
</view>
</view>
</view>
<!-- Promo -->
<view class="promo-box"
:class="{ 'plus-promo': item.promoType === 'plus', 'cashback-promo': item.shopType == 'waimai' && (item.status == '骑手到店取餐中' || item.status == '商家备餐中'), 'coupon-promo': item.promoType === 'coupon' }">
<template v-if="item.promoType === 'plus'">
<view class="plus-row">
<image class="plus-tag" src="/static/image/shopping/jingdong/plus.png"></image>
<text class="promo-text">{{ item.promoText }}</text>
</view>
<text class="promo-action">解锁权益 <uni-icons type="right" size="10" color="#654629"></uni-icons> </text>
</template>
<template v-else-if="item.shopType == 'waimai' && (item.status == '骑手到店取餐中' || item.status == '商家备餐中')">
<text class="promo-text text-basic">当笔订单返现成功到账<text style="color: #E31700;">点击领取</text></text>
<uni-icons type="right" size="10" color="#1A1A1A"></uni-icons>
</template>
<template v-else-if="item.promoType === 'coupon'">
<view class="coupon-row">
<view class="coupon-tag">购物券</view>
<text class="promo-text">{{ item.promoText }}</text>
</view>
<text class="promo-action red">{{ item.promoAction || '去领券' }} <uni-icons type="right" size="10"
color="#ED1C04"></uni-icons></text>
</template>
<template v-else-if="item.promoType === 'text'">
<text class="promo-text text-basic">本单可享 <text class="highlight">{{ item.promoHighlight
}}</text>快去支付吧~</text>
</template>
<template v-else>
<text class="promo-text text-basic">当笔订单返现成功到账<text style="color: #E31700;">点击领取</text></text>
</template>
</view>
<!-- Footer -->
<view class="footer-actions">
<view class="more-box" v-if="item.status == '完成' && item.shopType != 'waimai'">
<text class="more-text">更多</text>
</view>
<view class="empty-space" v-else></view>
<view class="action-buttons">
<view class="btn" :class="btn.type || 'default'"
v-for="(btn, index) in getButtons(item.shopType, item.status, item)" :key="index"
@click="$emit('btnClick', btn)">
{{ btn.text }}
<view class="btn-badge" v-if="btn.badge">{{ btn.badge }}</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { defineProps, defineEmits, ref, onMounted, watch, getCurrentInstance } from 'vue';
const props = defineProps({
item: {
type: Object,
required: true,
default: () => ({})
}
});
const emit = defineEmits(['btnClick', 'longpress']);
const onLongPress = (e) => {
emit('longpress', { event: e, item: props.item });
};
// --- JS ---
const instance = getCurrentInstance();
const truncatedTitle = ref('');
const calculateFitTitle = () => {
const originalTitle = props.item.products?.[0]?.title || props.item.title || '';
if (props.item.shopType === 'waimai') {
truncatedTitle.value = originalTitle;
return;
}
setTimeout(() => {
const query = uni.createSelectorQuery().in(instance);
//
query.select('.product-info').boundingClientRect(res => {
if (res && res.width) {
const containerWidth = res.width;
const fontSize = uni.upx2px(26); // 26rpx
let currentWidth = 0;
let cutIndex = 0;
let found = false;
for (let i = 0; i < originalTitle.length; i++) {
const char = originalTitle[i];
const charCode = char.charCodeAt(0);
let weight = 1;
if (charCode <= 255) {
if (/[A-Z]/.test(char)) weight = 0.7;
else if (/[a-z]/.test(char)) weight = 0.5;
else if (/[0-9]/.test(char)) weight = 0.55;
else if (char === ' ') weight = 0.25;
else weight = 0.5;
}
currentWidth += weight * fontSize;
if (currentWidth > containerWidth - uni.upx2px(4)) {
cutIndex = i;
found = true;
break;
}
}
truncatedTitle.value = found ? originalTitle.slice(0, cutIndex - 1) : originalTitle;
} else {
truncatedTitle.value = originalTitle;
}
}).exec();
}, 200);
};
onMounted(calculateFitTitle);
watch(() => props.item, calculateFitTitle, { deep: true, immediate: true });
// --- JS ---
const formatWaimaiStatusDesc = (desc) => {
if (!desc || !desc.includes(':')) return desc;
const parts = desc.split(':');
if (parts.length === 2) {
const m = parseInt(parts[0]) || 0;
const s = parseInt(parts[1]) || 0;
if (m > 0) {
return `${m}分钟`;
} else {
return `${s}`;
}
}
return desc;
};
const formatStatusDesc = (desc) => {
if (!desc || !desc.includes(' : ')) return desc;
const parts = desc.split(' : ');
if (parts.length >= 2) {
return `${parts[0]}${parts[1]}`;
}
return desc;
};
const safeFormatPrice = (item) => {
const val = item.price || (item.products && item.products[0] && item.products[0].price);
const num = Number(val);
return isNaN(num) ? '0.00' : num.toFixed(2);
};
const getButtons = (shopType, status, item) => {
const buttons = [];
if (status == '等待付款') {
buttons.push({ text: '取消订单', type: 'default' });
buttons.push({ text: '查看发票', type: 'default' });
buttons.push({ text: '修改订单', type: 'default' });
buttons.push({ text: '去支付', type: 'primary' });
}
if (shopType == 'waimai') {
if (status == '骑手到店取餐中') {
buttons.push({ text: '申请退款', type: 'default' });
buttons.push({ text: '查看发票', type: 'default' });
}
if (status == '商家备餐中') {
buttons.push({ text: '申请退款', type: 'default' });
buttons.push({ text: '查看发票', type: 'default' });
}
if (status == '完成') {
buttons.push({ text: '删除订单', type: 'default' });
buttons.push({ text: '查看发票', type: 'default' });
buttons.push({ text: '退换/售后', type: 'default' });
buttons.push({ text: '再次购买', type: 'primary' });
}
if (status == '已取消') {
buttons.push({ text: '删除订单', type: 'default' });
buttons.push({ text: '钱款去向', type: 'default' });
buttons.push({ text: '再次购买', type: 'primary' });
}
} else if (shopType != 'waimai') {
if (status == '正在出库') {
buttons.push({ text: '查看发票', type: 'default' });
buttons.push({ text: '再次购买', type: 'default' });
buttons.push({ text: '申请退款', type: 'default' });
buttons.push({ text: '修改订单', type: 'default' });
}
if (status == '完成') {
if (item?.trackingTitle == '已签收') {
buttons.push({ text: '卖了换钱', type: 'default' });
buttons.push({ text: '退还/售后', type: 'default' });
buttons.push({ text: '再次购买', type: 'primary' });
} else {
buttons.push({ text: '再次购买', type: 'default' });
buttons.push({ text: '申请退款', type: 'default' });
buttons.push({ text: '修改订单', type: 'default' });
}
}
if (status == '已取消') {
buttons.push({ text: '再次购买', type: 'default' });
buttons.push({ text: '申请退款', type: 'default' });
buttons.push({ text: '修改订单', type: 'primary' });
}
}
console.log(shopType, status);
return buttons;
}
</script>
<style>
@import "/common/main.css";
</style>
<style lang="less" scoped>
.shopping-card {
background-color: #FFFFFF;
border-radius: 24rpx;
padding: 34rpx 18rpx 30rpx;
margin: 0 0 16rpx 0;
box-sizing: border-box;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.shop-info {
flex: 1;
width: 20px;
display: flex;
align-items: center;
.tag {
font-size: 20rpx;
padding: 2rpx 6rpx;
border-radius: 4rpx;
margin-right: 8rpx;
font-weight: 500;
color: #FFFFFF;
line-height: 26rpx;
flex-shrink: 0;
&.self-tag {
background-color: #FC3538;
}
&.waimai-tag {
background-color: #FEE74B;
color: #1A1A1A;
}
}
.jd-tag {
width: 32rpx;
height: 32rpx;
flex-shrink: 0;
margin-right: 12rpx;
}
.shop-name {
white-space: nowrap;
font-size: 26rpx;
line-height: 28rpx;
color: #1A1A1A;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
}
.right-icon {
margin-right: 48rpx;
}
}
.order-status {
flex-shrink: 0;
font-size: 26rpx;
.waimai-status-warning {
display: flex;
align-items: center;
height: 32rpx;
border: 2rpx solid #ED1C04;
border-radius: 16rpx;
overflow: hidden;
.w-badge {
background-color: #ED1C04;
color: #FFFFFF;
font-size: 20rpx;
height: 100%;
line-height: 32rpx;
padding: 0 10rpx;
}
.w-time {
color: #ED1C04;
font-size: 20rpx;
line-height: 32rpx;
padding: 0 10rpx;
background-color: #FFFFFF;
}
}
.status-warning {
display: flex;
align-items: center;
background-color: #FEE4E5;
border-radius: 24rpx;
height: 30rpx;
padding-right: 8rpx;
.status-badge {
background-color: #E40C24;
color: #FFFFFF;
font-size: 22rpx;
padding: 0 12rpx;
height: 32rpx;
line-height: 32rpx;
border-radius: 16rpx;
margin-right: 8rpx;
}
.status-desc {
font-size: 22rpx;
line-height: 24rpx;
margin-left: 4rpx;
color: #ED1C04;
}
}
.status-text {
font-size: 22rpx;
color: #87868E;
&.red {
color: #ED1C04;
}
&.gray {
color: #999999;
}
&.black {
color: #1A1A1A;
}
}
}
}
.tracking-box {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #F9F9F9;
padding: 12rpx 20rpx 18rpx;
border-radius: 12rpx;
margin-bottom: 24rpx;
&.waimai-tracking {
align-items: flex-start;
background-color: #F7F8FC;
border-radius: 12rpx;
padding: 12rpx 20rpx 18rpx;
.waimai-tracking-icon {
margin-right: 10rpx;
margin-top: 2rpx;
}
.tracking-main-title {
font-size: 26rpx;
color: #1A1A1A;
font-weight: 700;
line-height: 36rpx;
margin-bottom: 8rpx;
}
.tracking-desc {
margin-top: 0;
font-size: 22rpx;
line-height: 22rpx;
color: #1A1A1A;
margin-left: -42rpx;
}
.tracking-time {
margin-left: -42rpx;
}
}
.tracking-content {
flex: 1;
.tracking-header {
display: flex;
align-items: center;
height: 32rpx;
margin-bottom: 14rpx;
.truck-icon {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
.tracking-title {
font-size: 26rpx;
color: #1A1A1A;
font-weight: 700;
}
}
.tracking-desc {
font-size: 22rpx;
line-height: 22rpx;
color: #1A1A1A;
display: block;
}
.tracking-time {
margin-top: 12rpx;
font-size: 22rpx;
line-height: 22rpx;
color: #767676;
}
}
.right-icon {}
}
.product-box {
display: flex;
margin-bottom: 24rpx;
align-items: center;
&.multiple {
.product-images-scroll {
flex: 1;
width: 0;
white-space: nowrap;
.product-img {
display: inline-block;
width: 132rpx;
height: 132rpx;
margin-right: 14rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
}
}
.product-price-box {
margin-left: 10rpx;
justify-content: center;
align-items: flex-end;
}
}
.product-img {
width: 144rpx;
height: 144rpx;
border-radius: 12rpx;
flex-shrink: 0;
margin-right: 14rpx;
background-color: #F5F5F5;
}
.product-info {
flex: 1;
display: flex;
flex-direction: column;
width: 20rpx;
.product-title {
font-size: 26rpx;
color: #1A1A1A;
line-height: 36rpx;
// white-space: nowrap;
text-overflow: clip;
overflow: hidden;
&.shopping-title {
width: 100%;
white-space: nowrap;
}
}
.product-desc {
font-size: 22rpx;
line-height: 24rpx;
margin-top: 8rpx;
color: #87868E;
overflow: hidden;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
line-clamp: 2;
text-overflow: ellipsis;
}
.product-tags {
display: flex;
flex-wrap: wrap;
margin-top: 8rpx;
.tag {
font-size: 20rpx;
line-height: 20rpx;
color: #C59446;
margin-right: 12rpx;
}
}
}
.product-price-box {
display: flex;
flex-direction: column;
align-items: flex-end;
margin-left: 20rpx;
.price-wrap {
color: #1A1A1A;
font-size: 30rpx;
margin-bottom: 4rpx;
line-height: 36rpx;
display: flex;
align-items: baseline;
.price-symbol {
font-weight: 500;
}
.price-num {
font-weight: 600;
}
}
.product-count {
font-size: 22rpx;
color: #87868E;
}
}
}
.promo-box {
background-color: #FEEFF2;
padding: 14rpx;
border-radius: 4rpx;
margin-bottom: 24rpx;
font-size: 22rpx;
line-height: 22rpx;
&.plus-promo,
&.cashback-promo,
&.coupon-promo {
display: flex;
justify-content: space-between;
align-items: center;
height: 52rpx;
padding: 0 16rpx;
border-radius: 8rpx;
}
&.plus-promo {
background-color: #FFEDDF;
.plus-row {
display: flex;
align-items: center;
.plus-tag {
width: 56rpx;
height: 26rpx;
margin-right: 8rpx;
}
.promo-text {
font-size: 22rpx;
line-height: 22rpx;
color: #654629;
}
}
.promo-action {
font-size: 22rpx;
color: #A3824E;
}
}
&.cashback-promo,
&.coupon-promo {
background-color: #FCF5F5;
.promo-text {
font-size: 22rpx;
line-height: 22rpx;
color: #1A1A1A;
.highlight {
color: #ED1C04;
}
}
.promo-action {
font-size: 22rpx;
display: flex;
align-items: center;
&.red {
color: #ED1C04;
}
}
}
&.coupon-promo {
.coupon-row {
display: flex;
align-items: center;
.coupon-tag {
font-size: 20rpx;
background-color: #ED1C04;
color: #FFFFFF;
padding: 2rpx 8rpx;
border-radius: 4rpx;
margin-right: 8rpx;
}
}
}
.text-basic {
font-size: 22rpx;
line-height: 22rpx;
color: #1A1A1A;
display: block;
.highlight {
font-size: 22rpx;
line-height: 22rpx;
color: #E31700;
}
}
}
.footer-actions {
display: flex;
justify-content: space-between;
align-items: center;
.more-box {
display: flex;
align-items: center;
.more-text {
white-space: nowrap;
font-size: 22rpx;
color: #87868E;
}
}
.empty-space {
flex: 1;
}
.action-buttons {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: flex-end;
position: relative;
z-index: 998;
.btn {
position: relative;
// z-index: 998;
height: 56rpx;
line-height: 56rpx;
padding: 0 24rpx;
border-radius: 8rpx;
font-size: 22rpx;
margin-left: 16rpx;
border: 0.5px solid #C7C7C7;
color: #1A1A1A;
background-color: #FFFFFF;
&.primary {
border-color: #ED1C04;
color: #ED1C04;
}
.btn-badge {
box-sizing: border-box;
position: absolute;
top: -8px;
right: -1px;
background-image: url(/static/image/shopping/jingdong/bubble.png);
background-size: 100% 100%;
background-repeat: no-repeat;
color: #FFFFFF;
font-size: 10px;
line-height: 11px;
padding: 2px 4px 0;
white-space: nowrap;
width: 45px;
height: 22px;
}
}
}
}
}
</style>

View File

@ -7,7 +7,7 @@
<image v-else class="colse" src="/static/image/watermarkColseDark.png" mode=""></image> -->
<image class="watermarkImg" src="/static/image/watermark.png" mode=""></image>
<image class="colse" src="/static/image/watermarkColse.png" mode=""
@click="$goRechargePage('watermark_close')"></image>
@click="$goRechargePage('watermark_close', source)"></image>
</view>
</view>
@ -19,6 +19,10 @@ export default {
dark: {
type: String,
default: 'light'
},
source: {
type: String,
default: 'uni_alipay_other'
}
},
name: "watermark",
@ -56,7 +60,7 @@ export default {
justify-content: center;
z-index: 99 !important;
background-image: url('/static/image/watermarkBG.png');
background-size: 125px 125px;
background-size: 500rpx 500rpx;
.watermarkBox {
width: 200px;

BIN
eslint-output.json Normal file

Binary file not shown.

15
main.js
View File

@ -16,20 +16,21 @@ app.$mount()
import {
createSSRApp
} from 'vue'
import {
store,
useStore
} from './store'
export function createApp() {
const app = createSSRApp(App)
// 从缓存读取系统信息已在App.vue中获取
app.config.globalProperties.$pageData = pageData
const systemInfo = uni.getStorageSync('systemInfo') || {}
let systemInfo = uni.getStorageSync('systemInfo') || {}
if (!systemInfo.platform) {
systemInfo = uni.getSystemInfoSync() || {}
}
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
// #ifdef APP-PLUS
app.config.globalProperties.$system = plus.os.name;
// #endif
app.config.globalProperties.$systemInfo = systemInfo
uni.setStorageSync('version', '1.0.2.sp4')
uni.setStorageSync('version', '1.0.6.sp9')
app.config.globalProperties.$version = uni.getStorageSync('version')
app.use(globalMethods);
return {
app

View File

@ -1,37 +1,46 @@
{
"name": "alipay-emulator",
"appid": "__UNI__D535736",
"description": "",
"versionName": "1.0.0",
"versionCode": 100,
"transformPx": false,
"name" : "alipay-emulator",
"appid" : "__UNI__D535736",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : 100,
"transformPx" : false,
/* 5+App */
"app-plus": {
"darkmode": false,
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
"app-harmony" : {
"distribute" : {
"splashScreens" : {
"startWindowIcon" : "resource/icon.png", //
"startWindowBackground" : "#123456" //
}
}
},
"app-plus" : {
"darkmode" : false,
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : false,
"delay" : 0
},
"optimization": {
"subPackages": true
"optimization" : {
"subPackages" : true
},
"runmode": "liberate", //
"runmode" : "liberate", //
/* */
"modules": {
"Camera": {},
"Payment": {},
"LivePusher": {}
"modules" : {
"Camera" : {},
"Payment" : {},
"LivePusher" : {}
},
/* */
"distribute": {
"distribute" : {
/* android */
"android": {
"permissions": [
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
@ -50,52 +59,51 @@
]
},
/* ios */
"ios": {
"dSYMs": false
"ios" : {
"dSYMs" : false
},
/* SDK */
"sdkConfigs": {
"payment": {
"weixin": {
"__platform__": [
"ios",
"android"
],
"appid": "123456",
"UniversalLinks": "https://hhhhh.com/apple-app-site-association/"
"sdkConfigs" : {
"payment" : {
"weixin" : {
"__platform__" : [ "ios", "android" ],
"appid" : "123456",
"UniversalLinks" : "https://hhhhh.com/apple-app-site-association/"
},
"alipay": {
"__platform__": [
"ios",
"android"
]
"alipay" : {
"__platform__" : [ "ios", "android" ]
}
}
}
},
"nvueLaunchMode": ""
"nvueLaunchMode" : ""
},
/* */
"quickapp": {},
"quickapp" : {},
/* */
"mp-weixin": {
"appid": "",
"setting": {
"urlCheck": false
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents": true
"usingComponents" : true
},
"mp-alipay": {
"usingComponents": true
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu": {
"usingComponents": true
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao": {
"usingComponents": true
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics": {
"enable": false
"uniStatistics" : {
"enable" : false
},
"vueVersion": "3"
}
"vueVersion" : "3",
"h5" : {
"router" : {
"mode" : "history"
}
}
}

2308
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,39 @@
{
"id": "lius-DomVideoPlayer",
"name": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
"displayName": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
"version": "2.0.0",
"description": "APP 项目中uniapp 提供的的 video 原生视频组件层级太高,难以遮挡;该视频播放器可以被其他元素进行覆盖、遮挡,页面具有更高的定制性。",
"keywords": [
"video-player",
"视频播放器",
"视频覆盖",
"视频层级",
"视频遮挡"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
}
}
"id": "lius-DomVideoPlayer",
"name": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
"displayName": "video-player 视频播放器 html5视频播放器-解决频层级、覆盖",
"version": "2.0.0",
"description": "APP 项目中uniapp 提供的的 video 原生视频组件层级太高,难以遮挡;该视频播放器可以被其他元素进行覆盖、遮挡,页面具有更高的定制性。",
"keywords": [
"video-player",
"视频播放器",
"视频覆盖",
"视频层级",
"视频遮挡"
],
"dcloudext": {
"category": [
"前端组件",
"通用组件"
]
},
"dependencies": {
"crypto-js": "^4.2.0",
"lottie-web": "^5.13.0",
"uuid": "^13.0.0"
},
"lint-staged": {
"*.{js,vue}": [
"eslint"
]
},
"devDependencies": {
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.33.0",
"husky": "^9.1.7",
"lint-staged": "^16.4.0"
},
"scripts": {
"prepare": "husky"
}
}

View File

@ -9,11 +9,148 @@
}
}
],
"subPackages": [
"subPackages": [{
"root": "pages/call-log",
"pages": [{
"path": "call",
"style": {
"navigationBarTitleText": "通话记录页面",
"navigationStyle": "custom"
}
}, {
"path": "detail/callDetail",
"style": {
"navigationBarTitleText": "通话记录详情",
"navigationStyle": "custom"
}
}]
},
{
"root": "pages/message",
"pages": [{
"path": "list-index",
"style": {
"navigationBarTitleText": "短信列表首页",
"navigationStyle": "custom"
}
},
{
"path": "chat-page/chat-page",
"style": {
"navigationBarTitleText": "短信聊天页面",
"navigationStyle": "custom",
"app-plus": {
"softinputMode": "adjustResize"
}
}
}
]
},
{
"root": "pages/shopping",
"pages": [{
"path": "index",
"style": {
"navigationBarTitleText": "购物app首页",
"navigationStyle": "custom"
}
},
{
"path": "jingdong/list-index",
"style": {
"navigationBarTitleText": "京东列表首页",
"navigationStyle": "custom"
}
},
{
"path": "jingdong/order-detail/order-detail",
"style": {
"navigationBarTitleText": "订单详情",
"navigationStyle": "custom"
}
},
{
"path": "jingdong/add-order/add-order",
"style": {
"navigationBarTitleText": "添加订单",
"navigationStyle": "custom"
}
},
{
"path": "jingdong/add-waimai/add-waimai",
"style": {
"navigationBarTitleText": "添加外卖订单页面",
"navigationStyle": "custom"
}
},
{
"path": "jingdong/waimai-order-detail/waimai-order-detail",
"style": {
"navigationBarTitleText": "外卖订单详情",
"navigationStyle": "custom"
}
},
{
"path": "taobao/list-index",
"style": {
"navigationBarTitleText": "淘宝购物外卖订单列表",
"navigationStyle": "custom"
}
},
{
"path": "taobao/shopping-order-detail/shopping-order-detail",
"style": {
"navigationBarTitleText": "淘宝购物详情页",
"navigationStyle": "custom"
}
},
{
"path": "taobao/add-shopping-order/add-shopping-order",
"style": {
"navigationBarTitleText": "新增淘宝购物订单",
"navigationStyle": "custom"
}
},
{
"path": "taobao/shangou-order-detail/shangou-order-detail",
"style": {
"navigationBarTitleText": "淘宝闪购订单详情",
"navigationStyle": "custom"
}
},
{
"path": "taobao/add-shangou-order/add-shangou-order",
"style": {
"navigationBarTitleText": "添加淘宝闪购订单详情",
"navigationStyle": "custom"
}
},
{
"path": "pdd/list-index",
"style": {
"navigationBarTitleText": "拼多多购物列表",
"navigationStyle": "custom"
}
},
{
"path": "pdd/order-detail/order-detail",
"style": {
"navigationBarTitleText": "拼多多订单详情",
"navigationStyle": "custom"
}
},
{
"path": "pdd/add-order/add-order",
"style": {
"navigationBarTitleText": "pdd添加修改订单信息",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/balance",
"pages": [
{
"pages": [{
"path": "index",
"style": {
"navigationBarTitleText": "余额页面",
@ -27,13 +164,19 @@
"navigationBarTitleText": "快速入口页面",
"navigationStyle": "custom"
}
},
{
"path": "transfer/transfer",
"style": {
"navigationBarTitleText": "转账模拟",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/bill",
"pages": [
{
"pages": [{
"path": "bill-list/bill-list",
"style": {
"navigationBarTitleText": "账单列表页面",
@ -58,8 +201,7 @@
},
{
"root": "pages/ant-credit-pay",
"pages": [
{
"pages": [{
"path": "index",
"style": {
"navigationBarTitleText": "花呗首页",
@ -77,8 +219,7 @@
},
{
"root": "pages/finance-management",
"pages": [
{
"pages": [{
"path": "index",
"style": {
"navigationBarTitleText": "理财首页",
@ -103,8 +244,7 @@
},
{
"root": "pages/other",
"pages": [
{
"pages": [{
"path": "/video-group-chat/video-group-chat",
"style": {
"navigationBarTitleText": "视频群聊",
@ -170,13 +310,130 @@
"navigationStyle": "custom",
"navigationBarTextStyle": "white"
}
},
{
"path": "card/card",
"style": {
"navigationBarTitleText": "身份证",
"navigationStyle": "custom"
}
},
{
"path": "qf-image/qf-image",
"style": {
"navigationBarTitleText": "图片裁剪",
"navigationStyle": "custom"
}
},
{
"path": "train-tickets/ctrip-train-tickets/ctrip-train-tickets",
"style": {
"navigationBarTitleText": "携程火车票",
"navigationStyle": "custom"
}
},
{
"path": "train-tickets/qunar-train-tickets/qunar-train-tickets",
"style": {
"navigationBarTitleText": "去哪儿火车票",
"navigationStyle": "custom"
}
},
{
"path": "train-tickets/fliggy-train-tickets/fliggy-train-tickets",
"style": {
"navigationBarTitleText": "飞猪火车票",
"navigationStyle": "custom"
}
},
{
"path": "ranking/ranking",
"style": {
"navigationBarTitleText": "从夯倒拉排名",
"navigationStyle": "custom"
}
},
{
"path": "certificate/graduate",
"style": {
"navigationBarTitleText": "证书",
"navigationStyle": "custom"
}
},
{
"path": "silkBanner/silkBanner",
"style": {
"navigationBarTitleText": "锦旗",
"navigationStyle": "custom"
}
},
{
"path": "certificate/index",
"style": {
"navigationBarTitleText": "证书",
"navigationStyle": "custom"
}
},
{
"path": "about-this-iphone/about-this-iphone",
"style": {
"navigationBarTitleText": "苹果关于本机",
"navigationStyle": "custom"
}
},
{
"path" : "bank/zsyh",
"style" :
{
"navigationBarTitleText" : "招商银行",
"navigationStyle": "custom"
}
},
{
"path" : "bank/index",
"style" :
{
"navigationBarTitleText" : "选择银行卡",
"navigationStyle": "custom"
}
},
{
"path" : "bank/gsyh",
"style" :
{
"navigationBarTitleText" : "工商银行",
"navigationStyle": "custom"
}
},
{
"path" : "bank/nyyh",
"style" :
{
"navigationBarTitleText" : "农业银行",
"navigationStyle": "custom"
}
},
{
"path" : "bank/jsyh",
"style" :
{
"navigationBarTitleText" : "建设银行",
"navigationStyle": "custom"
}
},
{
"path" : "game/honor-of-kings",
"style" :
{
"navigationBarTitleText" : "王者荣耀",
"navigationStyle": "custom"
}
}
]
},
{
"root": "pages/common",
"pages": [
{
"pages": [{
"path": "hot-icon/hot-icon",
"style": {
"navigationBarTitleText": "热门图标",
@ -203,6 +460,13 @@
"navigationBarTitleText": "充值页",
"navigationStyle": "custom"
}
},
{
"path": "call-and-message-entry/call-and-message-entry",
"style": {
"navigationBarTitleText": "呼叫和短信入口",
"navigationStyle": "custom"
}
}
]
}
@ -216,7 +480,9 @@
"pages/common",
"pages/finance-management",
"pages/ant-credit-pay",
"pages/other"
"pages/other",
"pages/message",
"pages/call-log"
]
}
},

View File

@ -1,21 +1,22 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark :dark="data.dark" source="uni_alipay_huabei" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_huabei')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="page-container">
<view class="main-container">
<NavBar v-if="!selectedImage" title="花呗" :bgColor="data.navBar.bgColor" tipLayerType="huabei-tip" isTipLayer
tipLayerText="修改花呗信息" :buttonGroup="buttonGroup" @button-click="clickTitlePopupButton">
<NavBar ref="navBarRef" v-if="!selectedImage" title="花呗" :bgColor="data.navBar.bgColor"
tipLayerType="huabei-tip" isTipLayer tipLayerText="修改花呗信息" :buttonGroup="buttonGroup"
@button-click="clickTitlePopupButton">
<!-- 使用作用域插槽自定义按钮渲染特别是switch的checked绑定 -->
<template #button="{ button }">
<view class="button flex-align-center flex-justify-center">
{{ button.name }}
<view @tap.stop>
<switch v-if="button.isSwitch" :checked="data.huabeiInfo.isOverdue" @change="button.click"
<switch v-if="button.isSwitch" :checked="data.huabeiInfo[button.key]" @change="button.click"
style="transform: scale(0.7);"></switch>
</view>
</view>
@ -44,11 +45,13 @@
<view v-if="huabeiInfo.styleType == 1" class="current-month">{{ huabeiInfo.mouth }}月应还()</view>
<view v-else class="current-month">{{ huabeiInfo.mouth }}月账单累计中()</view>
<view class="money-box flex-align-center">
<text class="money alipay-font">{{ numberUtil.formatMoneyWithThousand(huabeiInfo.money) }}</text>
<text class="money alipay-font" style="font-size: 48rpx;" v-if="data.huabeiInfo.isPayOff">已还清</text>
<text class="money alipay-font" v-else>{{ numberUtil.formatMoneyWithThousand(huabeiInfo.money) }}</text>
<uni-icons type="right" size="18" color="#B9D6FF"></uni-icons>
</view>
<!-- 样式一 按钮样式 -->
<view v-if="huabeiInfo.styleType == 1 || !huabeiInfo.styleType" class="style-1 button-group">
<view v-if="(huabeiInfo.styleType == 1 || !huabeiInfo.styleType) && !huabeiInfo.isPayOff"
class="style-1 button-group">
<view class="button-item second-button" :class="{ 'ios-button': $system == 'iOS' }">立即还款</view>
<view v-if="!huabeiInfo.isInstallment" class="button-item primary-button"
:class="{ 'ios-button': $system == 'iOS' }">
@ -58,17 +61,20 @@
</view>
</view>
<!-- 样式二 纯气泡样式 -->
<view v-if="huabeiInfo.styleType == 2" class="style-2 bubble-container">
<view v-if="(huabeiInfo.styleType == 2) && !huabeiInfo.isPayOff" class="style-2 bubble-container">
<view class="bubble-box">
<view class="arrow"></view>
<text class="text">{{ huabeiInfo.descText }}</text>
</view>
</view>
<!-- 样式三 气泡带箭头样式 -->
<view v-if="huabeiInfo.styleType == 3" class="style-3 bubble-container">
<view v-if="huabeiInfo.styleType == 3 || huabeiInfo.isPayOff" class="style-3 bubble-container">
<view class="bubble-box">
<view class="arrow"></view>
<text class="text flex-align-center">{{ huabeiInfo.descText }}
<text class="text flex-align-center">
<text style="line-height: 18px;">{{ huabeiInfo.isPayOff ? huabeiInfo.showDescText :
huabeiInfo.descText
}}</text>
<uni-icons type="right" size="18" color="#B9D6FF"></uni-icons>
</text>
</view>
@ -81,7 +87,8 @@
<view class="info-item">
<view class="label">总计额度</view>
<view class="value">{{
numberUtil.formatMoneyWithThousand(Number(huabeiInfo.totalAmount) - Number(huabeiInfo.money))
huabeiInfo.isPayOff ? numberUtil.formatMoneyWithThousand(Number(huabeiInfo.totalAmount)) :
numberUtil.formatMoneyWithThousand(Number(huabeiInfo.totalAmount) - Number(huabeiInfo.money))
}}可用
</view>
</view>
@ -201,6 +208,8 @@ import {
const instance = getCurrentInstance();
const { proxy } = instance;
const navBarRef = ref(null)
const buttonGroup = [{
name: "编辑花呗数据",
@ -221,6 +230,7 @@ const buttonGroup = [{
}, {
name: "花呗逾期",
isSwitch: true,
key: 'isOverdue',
click: () => {
data.huabeiInfo.isOverdue = !data.huabeiInfo.isOverdue
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
@ -229,6 +239,20 @@ const buttonGroup = [{
url: '/pages/ant-credit-pay/overdue-payment/overdue-payment'
})
}
navBarRef.value.closeTopPopup()
}
}, {
name: "还清花呗",
isSwitch: true,
key: 'isPayOff',
click: () => {
data.huabeiInfo.isPayOff = !data.huabeiInfo.isPayOff
// uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
if (data.huabeiInfo.isPayOff) {
data.huabeiInfo.showDescText = `${Number(data.huabeiInfo.mouth) + 1}月已累计0.00,查看消费详情报告`
}
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
navBarRef.value.closeTopPopup()
}
}]
@ -704,7 +728,7 @@ const goBack = () => {
justify-content: center;
.text {
color: #ffffff;
color: #B9D6FF;
font-size: 26rpx;
line-height: 1.2;
}
@ -738,7 +762,7 @@ const goBack = () => {
}
.value {
color: #ffffff;
color: #B9D6FF;
font-size: 26rpx;
line-height: 36rpx;
}

View File

@ -206,8 +206,8 @@
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark :dark="data.dark" source="uni_alipay_huabei" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_huabei')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>

View File

@ -1,14 +1,14 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark :dark="data.dark" source="uni_alipay_balance" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_balance')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="container" :style="{ height: data.windowHeight + 'px' }">
<view class="bg-container"></view>
<NavBar class="nav-bar" isRightIcon title="" tipLayerType="balance-tip" isTipLayer tipLayerText="修改余额"
<nav-bar class="nav-bar" isRightIcon title="" tipLayerType="balance-tip" isTipLayer tipLayerText="修改余额"
:bgColor="data.navBar.bgColor" :buttonGroup="buttonGroup" @button-click="clickTitlePopupButton">
<template v-slot:left>
<view class="nav-bar-left" @click="util.goBack()">
@ -21,7 +21,7 @@
<image class="nav-icon" src="/static/image/nav-bar/more-white.png" mode=""></image>
</view>
</template>
</NavBar>
</nav-bar>
<scroll-view class="scroll-view" :style="{ height: (data.windowHeight - 44 - data.statusBarHeight) + 'px' }"
scroll-y="true">
<view class="h100 w100 flex-between" style="flex-direction: column;">
@ -111,7 +111,7 @@
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar'
//import NavBar from '@/components/nav-bar/nav-bar'
import BalanceList from '@/components/balance-list/balance-list.vue'
import { fastEntranceList } from '@/static/json/initial.json'
import {
@ -211,7 +211,7 @@ onLoad(async () => {
})
onShow(() => {
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F0F3F8')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
@ -515,11 +515,12 @@ const onMenuScroll = (e) => {
}
.nav-text {
font-size: 18px;
font-size: 36rpx;
margin-right: 4px;
color: #FFFFFF;
height: 24px;
line-height: 24px;
font-weight: 500;
}
::v-deep .uni-navbar__header-btns-left {

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,10 @@
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure">{{ billData.name }}</text>
<input class="text-input" type="text" v-model="billData.name" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
<input class="text-input" type="text" :focus="data.focusState.name" v-model="billData.name"
@blur="data.focusState.name = false" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png" @click="triggerFocus('name')">
</image>
</view>
<!-- 金额 -->
<view class="money-box flex-align-center">
@ -39,8 +41,10 @@
<text class="text-measure font-w500" style="font-size: 64rpx;">{{ billData.money }}</text>
<input class="text-input font-w500" style="font-size: 64rpx;" type="digit"
v-model="billData.money" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
:focus="data.focusState.money" v-model="billData.money"
@blur="data.focusState.money = false" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="triggerFocus('money')"></image>
</view>
</view>
@ -52,9 +56,13 @@
<!-- 详情信息列表 -->
<view class="detail-info-container">
<template v-for="item in billData.itemInfoList" :key="item.id">
<view class="info-item-box" v-if="item.key != 'paymentReward'">
<view class="item-label">
{{ item.label }}
<view class="info-item-box"
v-if="item.key != 'paymentReward' && (item.key != 'transferNote' || billData.merchantOption.isTransferNote)">
<view class="item-label" :class="{ 'switchable-label': item.key === 'createTime' }"
@click="toggleCreateTimeLabel(item)">
<text>{{ item.label }}</text>
<image v-if="item.key === 'createTime'" class="switch-icon"
src="/static/image/bill/add-bill/edit.png"></image>
</view>
<view v-if="item.type != 'link'" class="info-item-input" @click="onClickItemInfo(item)">
<!-- 隐藏的text用于测量宽度 -->
@ -66,7 +74,8 @@
<input
v-if="(item.type == 'text' || item.type == 'digit' || item.type == 'number') && !(data.type == 1 && item.key == 'payMethod')"
:style="{ color: item.textColor ? item.textColor : '#1a1a1a' }" class="text-input"
:type="item.type" :focus="item.focus" v-model="item.value" @click.stop />
:type="item.type" :focus="item.focus" v-model="item.value" @click.stop
@blur="item.focus = false" />
</view>
<image v-if="item.type != 'link' && !(data.type == 1 && item.key == 'payMethod')"
class="edit-image" src="/static/image/bill/add-bill/edit.png"
@ -97,7 +106,7 @@
<view class="right-input-box flex-align-center">
<!-- <text class="right-text">{{ item.value.number }}</text> -->
<input class="right-text" :focus="item.focus" type="text" v-model="item.value.quantity"
@click.stop />
@blur="item.focus = false" @click.stop />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="onClickItemInfo(item, 'foucs')">
</image>
@ -111,9 +120,11 @@
<view class="info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure">{{ billData.balance }}</text>
<input class="text-input" type="digit" v-model="billData.balance" />
<input class="text-input" type="digit" :focus="data.focusState.balance"
v-model="billData.balance" @blur="data.focusState.balance = false" />
</view>
<image class="edit-image" src="/static/image/bill/add-bill/edit.png">
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="triggerFocus('balance')">
</image>
</view>
</view>
@ -122,7 +133,8 @@
<view class="switch-option-container">
<template v-for="option in data.switchOptions" :key="option.id">
<view v-if="option.showTypeIds ? option.showTypeIds.includes(billData.selectId) : true"
class="border-bottom" :class="{ 'no-border-bottom': isNoBorderBottom(option) }">
class="border-bottom"
:class="{ 'no-border-bottom': isNoBorderBottom(option) || option.isMarginBottom, 'margin-bottom-16rpx': option.isMarginBottom }">
<view class="switch-option">
<view class="switch-option-text">{{ option.name }}</view>
<switch v-if="option.isSwitch" color="#1676FE" :checked="billData.merchantOption[option.key]"
@ -172,6 +184,7 @@
<image class="edit-image" src="/static/image/bill/add-bill/edit.png">
</image>
</view>
</template>
</view>
@ -187,8 +200,12 @@
billData.merchantOption.serviceDetailInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
v-model="billData.merchantOption.serviceDetailInfo.text" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
:focus="data.focusState.serviceDetail"
v-model="billData.merchantOption.serviceDetailInfo.text"
@blur="data.focusState.serviceDetail = false" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="triggerFocus('serviceDetail')">
</image>
</view>
</view>
<uni-data-select class="select" style="flex: none;width: 80px;"
@ -207,8 +224,11 @@
billData.merchantOption.recommendServiceInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
v-model="billData.merchantOption.recommendServiceInfo.text" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
:focus="data.focusState.recommendService"
v-model="billData.merchantOption.recommendServiceInfo.text"
@blur="data.focusState.recommendService = false" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="triggerFocus('recommendService')"></image>
</view>
</view>
<text class="right-text">去看看</text>
@ -224,12 +244,34 @@
billData.merchantOption.serviceRecommendInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
v-model="billData.merchantOption.serviceRecommendInfo.text" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"></image>
:focus="data.focusState.serviceRecommend"
v-model="billData.merchantOption.serviceRecommendInfo.text"
@blur="data.focusState.serviceRecommend = false" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="triggerFocus('serviceRecommend')"></image>
</view>
</view>
<image class="right-icon" src="/static/image/common/right-grey.png" />
</view>
<view class="bill-analysis-box"
v-if="option.key == 'isShowBillAnalysis' && billData.merchantOption.isShowBillAnalysis">
<view class="bill-analysis-text-box">
<view class="flex-1 over-hidden">
<view class="service-detail-info info-item-input">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure" style="font-size: 22rpx;">{{
billData.merchantOption.billAnalysisInfo.text }}</text>
<input class="text-input" maxlength="20" style="font-size: 22rpx;" type="text"
:focus="data.focusState.billAnalysis"
v-model="billData.merchantOption.billAnalysisInfo.text"
@blur="data.focusState.billAnalysis = false" />
<image class="edit-image" src="/static/image/bill/add-bill/edit.png"
@click="triggerFocus('billAnalysis')"></image>
</view>
</view>
</view>
</view>
</view>
</template>
@ -307,6 +349,41 @@ const timepopup = ref(null)
//
const billManagementPopupRef = ref(null)
//
const nameInputRef = ref(null)
const moneyInputRef = ref(null)
const balanceInputRef = ref(null)
const serviceDetailInputRef = ref(null)
const recommendServiceInputRef = ref(null)
const serviceRecommendInputRef = ref(null)
const billAnalysisInputRef = ref(null)
/**
* 通用聚焦函数
* @param {Object} inputRef 目标输入框的 ref
*/
const onFocusShortcut = (inputRef) => {
if (!inputRef) return
nextTick(() => {
// uni-app input focus focus()
// ref
if (inputRef.value && typeof inputRef.value.focus === 'function') {
inputRef.value.focus()
}
})
}
/**
* 触发指定输入框聚焦响应式变量方式
* @param {String} key focusState 中的键名
*/
const triggerFocus = (key) => {
data.focusState[key] = false
nextTick(() => {
data.focusState[key] = true
})
}
const {
addBill,
getBillList,
@ -333,82 +410,97 @@ const serviceDetailRightTextList = [{
}
]
// switch
const switchOptions = [{
"id": 7,
"name": "账单分类",
"isSwitch": false,
key: 'billClassify',
},
{
"id": 8,
"name": "标签",
"isSwitch": false,
key: 'tag',
},
{
"id": 9,
"name": "备注",
"isSwitch": false,
key: 'note',
},
{
"id": 1,
"name": "服务详情",
"isSwitch": true,
showTypeIds: [3, 4, 10],
key: 'serviceDetail',
},
{
"id": 2,
"name": "推荐服务",
"isSwitch": true,
showTypeIds: [4],
key: 'recommendService',
},
{
"id": 3,
"name": "服务推荐",
"isSwitch": true,
showTypeIds: [6, 7],
key: 'serviceRecommend',
},
{
"id": 4,
"name": "退款",
"isSwitch": true,
showTypeIds: [12, 13],
key: 'refund',
change: (value) => {
if (billData.value.selectId == 12 || billData.value.selectId == 13) {
if (value) {
billData.value.orderStatus = "已全额退款"
} else {
billData.value.orderStatus = "交易成功"
const switchOptions = [
{
"id": 11,
"name": "转账备注",
"isSwitch": true,
showTypeIds: [7],
key: 'isTransferNote',
isMarginBottom: true
}, {
"id": 7,
"name": "账单分类",
"isSwitch": false,
key: 'billClassify',
},
{
"id": 8,
"name": "标签",
"isSwitch": false,
key: 'tag',
},
{
"id": 9,
"name": "备注",
"isSwitch": false,
key: 'note',
},
{
"id": 1,
"name": "服务详情",
"isSwitch": true,
showTypeIds: [3, 4, 10],
key: 'serviceDetail',
},
{
"id": 2,
"name": "推荐服务",
"isSwitch": true,
showTypeIds: [4],
key: 'recommendService',
},
{
"id": 3,
"name": "服务推荐",
"isSwitch": true,
showTypeIds: [6, 7],
key: 'serviceRecommend',
},
{
"id": 4,
"name": "退款",
"isSwitch": true,
showTypeIds: [12, 13],
key: 'refund',
change: (value) => {
if (billData.value.selectId == 12 || billData.value.selectId == 13) {
if (value) {
billData.value.orderStatus = "已全额退款"
} else {
billData.value.orderStatus = "交易成功"
}
}
}
},
{
"id": 5,
"name": "支付奖励",
"isSwitch": true,
showTypeIds: [1, 3, 4, 13],
key: 'payReward',
},
{
"id": 6,
"name": "计入收支",
"isSwitch": true,
showTypeIds: [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13],
key: 'countInAndOut',
},
{
"id": 7,
"name": "商家订单号转换条形码",
"isSwitch": true,
showTypeIds: [1, 2, 3],
key: 'isShowBarcode',
},
{
"id": 10,
"name": "账单分析",
"isSwitch": true,
showTypeIds: [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13],
key: 'isShowBillAnalysis',
}
},
{
"id": 5,
"name": "支付奖励",
"isSwitch": true,
showTypeIds: [1, 3, 4, 13],
key: 'payReward',
},
{
"id": 6,
"name": "计入收支",
"isSwitch": true,
showTypeIds: [1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13],
key: 'countInAndOut',
},
{
"id": 7,
"name": "商家订单号转换条形码",
"isSwitch": true,
showTypeIds: [1, 2, 3],
key: 'isShowBarcode',
}
]
@ -460,6 +552,7 @@ const data = reactive({
merchantOption: {
serviceDetail: false,
isShowBarcode: false,
isTransferNote: false,
serviceDetailInfo: {
imgUrl: '',
rightText: 0,
@ -481,6 +574,10 @@ const data = reactive({
note: {
isImage: false,
text: ''
},
isShowBillAnalysis: false,
billAnalysisInfo: {
text: '本笔登上月收入榜,看看分析吧'
}
}
},
@ -492,7 +589,17 @@ const data = reactive({
tagInputValue: "",
showTagInput: false,
// switch
switchOptions: switchOptions
switchOptions: switchOptions,
//
focusState: {
name: false,
money: false,
balance: false,
serviceDetail: false,
recommendService: false,
serviceRecommend: false,
billAnalysis: false
}
})
let {
@ -882,6 +989,15 @@ const onRightClick = async () => {
}, 500)
}
/**
* 切换创建时间/支付时间标签
*/
const toggleCreateTimeLabel = (item) => {
if (item.key === 'createTime') {
item.label = item.label === '创建时间' ? '支付时间' : '创建时间'
}
}
// itemInfo
const onClickItemInfo = async (item, action) => {
console.log(item)
@ -955,7 +1071,7 @@ const uploadImage = () => {
*/
const saveAndDisplayImage = (file) => {
// #ifdef H5
return Promise.resolve(file)
// return Promise.resolve(file)
// #endif
// #ifndef H5
@ -1230,6 +1346,22 @@ page {
font-size: 26rpx;
color: var(--text-secondary);
}
.switchable-label {
display: inline-flex;
align-items: center;
padding: 4rpx 10rpx 4rpx 10rpx;
margin-left: -10rpx;
border-radius: 8rpx;
border: 1px dashed #d9d9d9;
background-color: #fcfcfc;
.switch-icon {
width: 18rpx;
height: 18rpx;
margin-left: 6rpx;
}
}
}
.info-item-input {
@ -1279,11 +1411,11 @@ page {
.switch-option-container {
margin: 16rpx 24rpx;
background-color: #ffffff;
border-radius: 16rpx 16rpx 16rpx 16rpx;
.border-bottom {
border-bottom: 1px solid #efefef
border-bottom: 1px solid #efefef;
background-color: #ffffff;
}
.no-border-bottom {
@ -1468,4 +1600,24 @@ page {
}
.bill-analysis-box {
border-top: 0.5px solid #E4E4E4;
padding: 14rpx 22rpx;
.bill-analysis-text-box {
color: #B7971B;
font-size: 26rpx;
background-color: #FBF8F1;
height: 76rpx;
display: flex;
align-items: center;
border-radius: 16rpx;
padding: 0 24rpx;
}
}
.margin-bottom-16rpx {
margin-bottom: 16rpx;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark :dark="data.dark" source="uni_alipay_bill_details" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_bill_details')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -29,7 +29,8 @@
<!-- 金额 -->
<view class=" money info-item-input alipay-font" style="height: 77rpx;">
<!-- 隐藏的text用于测量宽度 -->
<text class="text-measure font-w500" style="font-size: 64rpx;">{{ billData.money }}</text>
<text class="text-measure font-w500" style="font-size: 64rpx;">{{
numberUtil.formatMoneyWithThousand(billData.money) }}</text>
</view>
</view>
@ -41,7 +42,9 @@
<!-- 详情信息列表 -->
<view class="detail-info-container">
<template v-for="item in visibleItemInfoList" :key="item.id">
<view class="info-item-box" :class="{ 'bottom-border': item.key == 'relatedRecord' }">
<view class="info-item-box"
v-if="item.key != 'transferNote' || billData.merchantOption.isTransferNote"
:class="{ 'bottom-border': item.key == 'relatedRecord' }">
<view class="item-label">
{{ item.label }}
</view>
@ -171,8 +174,9 @@
<view class="bill-management">
<view class="title">账单管理</view>
<view class="top-box" style="background-color: #FBF8F1;">
<text style="font-size: 24rpx;color: #B7971B;">本笔登上月收入榜看看分析吧</text>
<view v-if="billData.merchantOption.isShowBillAnalysis" class="top-box" style="background-color: #FBF8F1;">
<text style="font-size: 24rpx;color: #B7971B;">{{
billData.merchantOption.billAnalysisInfo.text || '本笔登上月收入榜,看看分析吧' }}</text>
<uni-icons type="right" size="10" color="#B7971B"></uni-icons>
</view>
<view class="bill-classification bill-management-item">
@ -253,7 +257,8 @@ const {
} = addBillJson
import {
util,
randomUtil
randomUtil,
numberUtil
} from '@/utils/common.js'
import {

View File

@ -1,8 +1,8 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark :dark="data.dark" source="uni_alipay_bill" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_bill')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -49,9 +49,9 @@
<view class="income-ande-outCome flex-between">
<view class="flex">
<view class="item"><text>支出</text><text class="money wx-font-regular">{{
Number(currentMonthData.outCome).toFixed(2) }}</text></view>
numberUtil.formatMoneyWithThousand(currentMonthData.outCome) }}</text></view>
<view class="item"><text>收入</text><text class="money wx-font-regular">{{
Number(currentMonthData.inCome).toFixed(2) }}</text></view>
numberUtil.formatMoneyWithThousand(currentMonthData.inCome) }}</text></view>
</view>
<view class="">
@ -83,13 +83,13 @@
<view class="outCome item">
<text class="title">支出</text>
<text class="amount alipay-font"><text class="font-11 wx-font-regular"></text>{{
Number(item.outCome).toFixed(2)
numberUtil.formatMoneyWithThousand(item.outCome)
}}</text>
</view>
<view class="income item">
<text class="title">收入</text>
<text class="amount alipay-font"><text class="font-11 wx-font-regular"></text>{{
Number(item.inCome).toFixed(2)
numberUtil.formatMoneyWithThousand(item.inCome)
}}</text>
</view>
</view>
@ -129,7 +129,8 @@
<script setup>
import {
dateUtil,
util
util,
numberUtil
} from '@/utils/common.js'
import navBar from '@/components/nav-bar/nav-bar.vue'
import BalanceList from '@/components/balance-list/balance-list.vue'
@ -283,7 +284,7 @@ const getBillDataList = () => {
const createTimeItem = item.itemInfoList.find(info => info.key == 'createTime')
const createTime = createTimeItem ? createTimeItem.value : new Date()
const date = new Date(createTime)
const date = dateUtil.parseSafe(createTime)
const year = date.getFullYear() + ''
const month = (date.getMonth() + 1) + ''
@ -341,7 +342,7 @@ const getBillDataList = () => {
//
groupList.forEach(group => {
group.list.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
group.list.sort((a, b) => dateUtil.parseSafe(b.timestamp) - dateUtil.parseSafe(a.timestamp))
})
data.billList = groupList
@ -623,6 +624,13 @@ page {
overflow: hidden;
overflow-x: scroll;
&::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
color: transparent;
}
.filter-item {
background-color: #ffffff;
color: var(--text-primary);

110
pages/call-log/call.vue Normal file
View File

@ -0,0 +1,110 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_other_call" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_call')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="container">
<!-- 自定义头部导航栏 -->
<ZdyNavbar tipLayerText="新增记录" :isTipLayer="true" :type="data.type" :scrollTop="data.scrollTop" @click="open" />
<ZdyHeader :type="data.type" />
<callList :type="data.type" ref="callLogList"></callList>
<tabbar :type="data.type" />
<image v-if="data.type != 'ios'" :src="`/static/image/call/${data.type}BtnImg.png`" mode="" class="btnImg"
:class="['btnImg_' + data.type]"></image>
</view>
</template>
<script setup>
import ZdyNavbar from "@/components/call-log/nav-bar/nav-bar.vue"
import ZdyHeader from "@/components/call-log/header/header.vue"
import callList from "@/components/call-log/list/list.vue"
import tabbar from "@/components/call-log/tabbar/tabbar.vue"
import { util } from "@/utils/common.js"
import { ref, reactive, watch, nextTick, getCurrentInstance } from "vue";
import { onLoad, onShow, onReady, onPageScroll, onReachBottom } from "@dcloudio/uni-app";
const { appContext, proxy } = getCurrentInstance();
const data = reactive({
type: 'vivo',
scrollTop: 0
})
let callLogList = ref();
onLoad((option) => {
data.type = option.type
})
onReady(() => {
})
onShow(() => {
try {
if (plus.os.name === 'Android') {
let colorTabbar = "#FAFAFA"
if (data.type == 'xiaomi' || data.type == 'oppo') {
colorTabbar = "#FFFFFF"
}
console.log(colorTabbar);
util.setAndroidSystemBarColor(colorTabbar)
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
}
} catch (error) {
console.log("修改导航条颜色失败", error);
}
})
onPageScroll((e) => {
// console.log(e)
data.scrollTop = e.scrollTop
})
onReachBottom(() => {
})
function open() {
// console.log(callLogList.value)
callLogList.value.openAddModal()
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.btnImg {
position: fixed;
}
.btnImg_vivo {
width: 80px;
height: 96px;
left: 40px;
bottom: 130px;
}
.btnImg_huawei {
width: 54px;
height: 54px;
left: calc(50% - 27px);
bottom: 65px;
}
.btnImg_oppo {
width: 58px;
height: 58px;
right: 20px;
bottom: 100px;
}
.btnImg_xiaomi {
width: 55px;
height: 55px;
right: 34px;
bottom: 100px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,176 @@
<template>
<view class="page">
<!-- 顶部折叠头部 -->
<view
class="header"
:style="{ height: headerHeight + 'px' }"
>
<!-- 背景 -->
<view class="header-bg"></view>
<!-- 内容层 -->
<view
class="header-content"
:style="{
transform: `translateY(${contentY}px)`
}"
>
<!-- 头像单独动画 -->
<image
class="avatar"
src="/static/avatar.png"
:style="{
transform: `scale(${avatarScale}) translateY(${avatarY}px)`
}"
/>
<!-- 文本 -->
<view class="info">
<view class="name">张三</view>
<view class="desc">iOS通讯录详情页</view>
</view>
</view>
</view>
<!-- 内容重点不会被遮挡 -->
<view class="content">
<view class="card" v-for="i in 30" :key="i">
详情内容 {{ i }}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
scrollY: 0,
headerMax: 260,
headerMin: 90,
avatarScale: 1,
avatarY: 0,
contentY: 0
};
},
computed: {
headerHeight() {
const y = this.scrollY;
// 🔥
return Math.max(this.headerMin, this.headerMax - y);
}
},
onPageScroll(e) {
const y = e.scrollTop;
this.scrollY = y;
// =========================
// 1. 0~1
// =========================
const max = 160;
let p = Math.min(y / max, 1);
// =========================
// 2.
// =========================
this.avatarScale = 1 - p * 0.4; // 1 0.6
// =========================
// 3.
// =========================
this.avatarY = -p * 20;
// =========================
// 4.
// =========================
this.contentY = -p * 10;
}
};
</script>
<style lang="scss">
.page {
background: #f5f6f7;
}
/* =====================
header核心
===================== */
.header {
position: fixed;
top: 0;
left: 0;
width: 100%;
overflow: hidden;
z-index: 10;
}
/* 背景 */
.header-bg {
position: absolute;
width: 100%;
height: 100%;
background: linear-gradient(180deg, #3b82f6, #1e40af);
}
/* 内容区 */
.header-content {
position: absolute;
bottom: 20px;
left: 30px;
display: flex;
align-items: center;
}
/* 头像 */
.avatar {
width: 140rpx;
height: 140rpx;
border-radius: 50%;
margin-right: 20rpx;
border: 4rpx solid rgba(255,255,255,0.6);
transform-origin: center;
}
/* 文本 */
.info {
color: #fff;
}
.name {
font-size: 40rpx;
font-weight: bold;
}
.desc {
font-size: 24rpx;
opacity: 0.85;
margin-top: 6rpx;
}
/* =====================
内容关键防遮挡
===================== */
.content {
padding-top: 260px; /* ⭐ 解决你说的遮挡问题 */
padding: 20rpx;
}
.card {
background: #fff;
margin-bottom: 20rpx;
padding: 30rpx;
border-radius: 16rpx;
box-shadow: 0 10rpx 30rpx rgba(0,0,0,0.05);
}
</style>

View File

@ -0,0 +1,178 @@
<template>
<view>
<NavBar :title="data.navBar.title" :bgColor="data.navBar.bgColor" />
<view class="list-container">
<view class="item" v-for="item in source" :key="item.id"
:style="{ background: `linear-gradient( -270deg, ${item.color.bgColor} 0%, #FFFFFF 70%), #FFFFFF` }">
<view class="content flex flex-align-center">
<image class="logo" :src="`/static/image/common/phone/${item.icon}.png`" mode=""></image>
<view class="name flex-1">{{ item.name }}机型</view>
<view class="right-button" :style="{ background: item.color.buttonColor }"
@click="goPage(data.type == 'message' ? item.messageUrl : item.callUrl)">立即进入</view>
</view>
<view class="line" :style="{ background: item.color.lineColor }"></view>
</view>
</view>
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import {
ref,
reactive,
getCurrentInstance
} from 'vue'
import {
onLoad
} from "@dcloudio/uni-app";
import {
util
} from '@/utils/common.js';
const {
appContext,
proxy
} = getCurrentInstance();
//
const source = ref([{
name: '苹果',
color: {
bgColor: '#F3EAFF',
lineColor: '#B78EF5',
buttonColor: '#BA8DFF',
},
icon: 'iphone',
messageUrl: "/pages/message/list-index?phone=iphone",
callUrl: "/pages/call-log/call?type=ios"
},
{
name: '华为',
color: {
bgColor: '#FFE9E9',
lineColor: '#FF6969',
buttonColor: '#FB6767',
},
icon: 'huawei',
messageUrl: "/pages/message/list-index?phone=huawei",
callUrl: "/pages/call-log/call?type=huawei"
},
{
name: '小米',
color: {
bgColor: '#FFF0DD',
lineColor: '#FFA143',
buttonColor: '#FFA64D',
},
icon: 'mi',
messageUrl: "/pages/message/list-index?phone=mi",
callUrl: "/pages/call-log/call?type=xiaomi"
},
{
name: 'oppo',
color: {
bgColor: '#E0FFD9',
lineColor: '#56B745',
buttonColor: '#5DCD49',
},
icon: 'oppo',
messageUrl: "/pages/message/list-index?phone=oppo",
callUrl: "/pages/call-log/call?type=oppo"
}, {
name: 'vivo',
color: {
bgColor: '#D4F4FF',
lineColor: '#52C2FF',
buttonColor: '#50C1FE',
},
icon: 'vivo',
messageUrl: "/pages/message/list-index?phone=vivo",
callUrl: "/pages/call-log/call?type=vivo"
},
])
const data = reactive({
navBar: {
title: '选择机型',
bgColor: '#F0F4F9',
},
type: "message"
})
onLoad((options) => {
if (options.type) {
data.type = options.type
}
proxy.$apiUserEvent('all', {
type: 'event',
key: data.type == 'message' ? 'message' : 'call-log',
prefix: '.uni.other.',
value: data.type == 'message' ? '短信' : "通话"
})
})
function goPage(url) {
if (url) {
util.goPage(url)
} else {
uni.showToast({
title: '开发中',
icon: 'none'
})
}
}
</script>
<style>
@import '@/common/main.css';
</style>
<style lang="less">
.list-container {
background-color: #F0F4F9;
padding: 24rpx 32rpx;
}
.item {
width: 100%;
height: 188rpx;
border-radius: 24rpx;
margin-bottom: 24rpx;
padding: 40rpx 36rpx 24rpx 28rpx;
.content {
.logo {
width: 96rpx;
height: 96rpx;
flex-shrink: 0;
}
.name {
margin-left: 32rpx;
color: #1A1A1A;
font-size: 36rpx;
font-weight: 500;
}
.right-button {
width: 140rpx;
height: 64rpx;
border-radius: 16rpx;
color: #ffffff;
font-size: 28rpx;
line-height: 64rpx !important;
text-align: center;
// display: flex;
// align-items: center;
// justify-content: center;
}
}
.line {
width: 100rpx;
height: 8rpx;
filter: blur(5px);
opacity: 0.5;
margin-top: 4rpx;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,134 +1,151 @@
<!-- pages/webview/webview.vue -->
<template>
<view class="webview-container">
<navBar class="nav-bar" :title="pageTitle" :bgColor="data.navBar.bgColor" isBack></navBar>
<navBar class="nav-bar" :title="pageTitle" :bgColor="data.navBar.bgColor" isBack>
<template v-if="isClose" v-slot:right>
<image style="width:22px;height: 22px;" src='/static/image/close.png' @click="onConfirm()"></image>
</template>
</navBar>
<web-view :src="url" :webview-styles="webviewStyles"></web-view>
</view>
</template>
<script setup>
//
// import ZdyNavbar from "@/components/navbar/navbar.vue"
import navBar from '@/components/nav-bar/nav-bar.vue'
import { util } from '@/utils/common.js'
//
// import ZdyNavbar from "@/components/navbar/navbar.vue"
import navBar from '@/components/nav-bar/nav-bar.vue'
import {
util
} from '@/utils/common.js'
import {
ref,
reactive,
watch,
nextTick,
getCurrentInstance,
onMounted,
onBeforeUnmount,
toRefs,
computed
} from "vue";
import {
onLoad,
onReady,
onShow
} from '@dcloudio/uni-app'
import {
ref,
reactive,
watch,
nextTick,
getCurrentInstance,
onMounted,
onBeforeUnmount,
toRefs,
computed
} from "vue";
import {
onLoad,
onReady,
onShow
} from '@dcloudio/uni-app'
const data = reactive({
navBar: {
title: "",
bgColor: '#ffffff',
},
dark: "dark",
//
systemBarHeight: "0",
})
const data = reactive({
navBar: {
title: "",
bgColor: '#ffffff',
},
dark: "dark",
//
systemBarHeight: "0",
isClose: false
})
let {
systemBarHeight
} = toRefs(data)
let {
systemBarHeight,
isClose
} = toRefs(data)
const url = ref('')
const pageTitle = ref('')
const url = ref('')
const pageTitle = ref('')
onLoad((options) => {
uni.getSystemInfo({
success: res => {
systemBarHeight.value = res.statusBarHeight; //
onLoad((options) => {
if (options.isClose) {
isClose.value = options.isClose
}
uni.getSystemInfo({
success: res => {
systemBarHeight.value = res.statusBarHeight; //
}
})
if (options.url) {
url.value = decodeURIComponent(options.url)
// const videoExps = [/\.mp4$/i, /\.m3u8$/i, /\.flv$/i, /\.avi$/i, /\.mov$/i, /\.wmv$/i, /\.webm$/i,
// /\.mkv$/i
// ];
// const isVideo = videoExps.some(exp => exp.test(url.value))
// console.log(isVideo)
// if (isVideo) {
// plus.navigator.setStatusBarStyle("light");
// } else {
// plus.navigator.setStatusBarStyle("dark");
// }
}
console.log("options参数", options);
if (options.title) {
console.log("标题", options.title);
pageTitle.value = decodeURIComponent(options.title)
}
})
if (options.url) {
url.value = decodeURIComponent(options.url)
// const videoExps = [/\.mp4$/i, /\.m3u8$/i, /\.flv$/i, /\.avi$/i, /\.mov$/i, /\.wmv$/i, /\.webm$/i,
// /\.mkv$/i
// ];
// const isVideo = videoExps.some(exp => exp.test(url.value))
// console.log(isVideo)
// if (isVideo) {
// plus.navigator.setStatusBarStyle("light");
// } else {
// plus.navigator.setStatusBarStyle("dark");
// }
onShow(() => {
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500);
// #endif
})
const onConfirm = () => {
uni.reLaunch({
url: '/pages/index/index'
})
}
console.log("options参数", options);
if (options.title) {
console.log("标题", options.title);
pageTitle.value = decodeURIComponent(options.title)
const goBack = () => {
uni.navigateBack()
}
})
onShow(() => {
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500);
// #endif
})
const isVideo = computed(() => {
if (!url.value) return false
const videoExps = [/\.mp4$/i, /\.m3u8$/i, /\.flv$/i, /\.avi$/i, /\.mov$/i, /\.wmv$/i, /\.webm$/i,
/\.mkv$/i
];
return videoExps.some(exp => exp.test(url.value));
})
const goBack = () => {
uni.navigateBack()
}
const isVideo = computed(() => {
if (!url.value) return false
const videoExps = [/\.mp4$/i, /\.m3u8$/i, /\.flv$/i, /\.avi$/i, /\.mov$/i, /\.wmv$/i, /\.webm$/i,
/\.mkv$/i
];
return videoExps.some(exp => exp.test(url.value));
})
const webviewStyles = computed(() => {
return {
top: `${Number(systemBarHeight.value) + 40}px`,
bottom: '0px',
'padding-bottom': '20px'
}
})
const webviewStyles = computed(() => {
return {
top: `${Number(systemBarHeight.value) + 40}px`,
bottom: '0px',
'padding-bottom': '20px'
}
})
</script>
<style scoped>
.webview-container {
flex: 1;
display: flex;
flex-direction: column;
}
.webview-container {
flex: 1;
display: flex;
flex-direction: column;
}
.header {
padding: 20rpx;
background: #fff;
border-bottom: 1rpx solid #eee;
}
.header {
padding: 20rpx;
background: #fff;
border-bottom: 1rpx solid #eee;
}
.title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
}
.title {
font-size: 32rpx;
font-weight: bold;
text-align: center;
}
.video-web {
position: fixed;
width: 100%;
background-color: #000;
}
.video-web {
position: fixed;
width: 100%;
background-color: #000;
}
</style>
<style>
page {
background-color: #ffffff;
}
page {
background-color: #ffffff;
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark dark="light" source="uni_alipay_finance" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_finance')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -193,7 +193,7 @@
<!-- 底部导航栏 -->
<view class="bottom-box"
:class="`navigation-menu-${financeInfo.navigationMenuStyle}`, { 'ios-bottom-box': $system == 'iOS' }">
:class="`navigation-menu-${financeInfo.navigationMenuStyle}` + ($system == 'iOS' ? ' ios-bottom-box' : '')">
<view class="bottom-item" v-for="item in navigationMenu" :key="item.name" @click="clickBottomItem(item)">
<image
:src="`/static/image/finance-management/navigation-menu/${financeInfo.navigationMenuStyle}/${item.icon}.png`">

View File

@ -1,9 +1,9 @@
<template>
<view class="container" :style="{ height: data.windowHeight + 'px' }">
<image class="index-bg-img" :style="{ width: windowWidth + 'px' }" src="/static/image/index/index-bg.png"
mode="widthFix">
<image v-if="isReady" class="index-bg-img" :style="{ width: windowWidth + 'px' }"
src="/static/image/index/index-bg.png" mode="widthFix">
</image>
<view class="nav-bar-box" :style="{ backgroundColor: data.navBarBgColor }">
<view v-if="isReady" class="nav-bar-box" :style="{ backgroundColor: data.navBarBgColor }">
<view class="status-box" :style="{ height: statusBarHeight + 'px' }"></view>
<view class="nav-box">
<view class="left-box" @click="exit" @tap="exit" @touchstart.stop="exit">
@ -15,14 +15,13 @@
</view>
</view>
<view class="content-box" :style="{ height: windowHeight + 'px' }">
<view v-if="isReady" class="content-box" :style="{ height: windowHeight + 'px' }">
<scroll-view scroll-y="true" class="scroll-view"
:style="{ height: (windowHeight - statusBarHeight - 44) + 'px', marginTop: (statusBarHeight + 44) + 'px' }"
@scroll="handleScroll">
<view>
<!-- iOS专用透明点击区域覆盖导航栏左上角 -->
<view @click="exit" @tap="exit" :style="{
<view @click="exit" :style="{
position: 'fixed',
top: statusBarHeight + 'px',
left: '0px',
@ -40,7 +39,7 @@
:src="`/static/image/index/${userInfo.vip > 1 ? (userInfo.vip == 3 ? 'lifetime-vip-bg' : 'vip-bg') : 'no-vip-bg'}.png`"
mode=""></image>
<view class="user-info-box" :style="{ width: (windowWidth - 32) + 'px' }">
<image class="user-avatar" :src="userInfo.avater"></image>
<image v-if="userInfo.avater" class="user-avatar" :src="userInfo.avater"></image>
<view class="user-info">
<view class="name-box">
<text class="phone-text">ID{{ userInfo.user_id }}</text>
@ -80,7 +79,7 @@
<view class="group-box">
<image class="title-img" src="/static/image/index/shipingjiaocheng.png"></image>
<view class="video-help-box">
<view class="video-help-item" :style="{ width: (windowWidth - 50) / 4 + 'px' }"
<view class="video-help-item" :style="{ width: (windowWidth - 32) / 4 + 'px' }"
v-for="item in videoHelpList" :key="item.id" @click="clickVideoHelp(item)">
<image class="video-help-img" :src="item.icon"></image>
<text class="video-help-title">{{ item.text }}</text>
@ -106,20 +105,22 @@
</view>
</view>
<view class="activity-box">
<!-- <view class="activity-box">
<image class="alipay-year-bill" :style="{ width: (windowWidth - 32) + 'px' }"
src="/static/image/index/alipay-year-bill.png" mode="widthFix"
@click="util.goPage(`/pages/common/alipay-annual-bill/alipay-annual-bill`)"></image>
</view>
</view> -->
<view class="group-box">
<image class="title-img" src="/static/image/index/qita.png"></image>
<view class="video-help-box">
<view class="video-help-item" :style="{ width: (windowWidth - 50) / 4 + 'px' }"
v-for="item in otherList" :key="item.id" @click="clickMenu(item)">
<image class="video-help-img" :src="item.icon"></image>
<text class="video-help-title">{{ item.name }}</text>
</view>
<template v-for="item in otherList" :key="item.id">
<view class="video-help-item" :style="{ width: (windowWidth - 32) / 4 + 'px' }"
@click="clickMenu(item)" v-if="isShowBankIdCard(item)">
<image class="video-help-img" :src="item.icon"></image>
<text class="video-help-title">{{ item.name }}</text>
</view>
</template>
</view>
</view>
</view>
@ -132,7 +133,6 @@
</view>
</scroll-view>
</view>
</view>
</template>
@ -158,9 +158,18 @@ import {
onLoad,
onShow,
onHide,
onUnload
onUnload,
onReady
} from '@dcloudio/uni-app';
onReady(() => {
setTimeout(() => {
// #ifdef APP-PLUS
plus.navigator.closeSplashscreen();
// #endif
}, 500);
});
// 内部埋点方法
const apiUserEvent = async (type, adminData) => {
let uni_version = uni.getStorageSync("version")
@ -189,7 +198,7 @@ const goRechargePage = () => {
}
})
uni.navigateTo({
url: '/pages/common/recharge/index'
url: '/pages/common/recharge/index?source=uni_alipay_index'
});
}
@ -218,23 +227,32 @@ const menuList = [{
name: "花呗",
isHot: false,
path: "/pages/ant-credit-pay/index"
}, {
icon: "zhuanzhang",
name: "转账模拟",
isHot: false,
path: "/pages/balance/transfer/transfer"
}, {
icon: "nianduzhangdan",
name: "年度账单",
isHot: false,
path: "/pages/common/alipay-annual-bill/alipay-annual-bill"
},
]
const otherList = [{
icon: "/static/image/index/qita/jipiao.png",
name: "机票",
path: "/pages/other/tickets-app/index"
path: "/pages/other/tickets-app/index?type=airTicket"
},
{
icon: "/static/image/index/qita/huochepiao.png",
name: "火车票",
path: "/pages/other/train-tickets/12306-tickets/12306-tickets"
// path: "/pages/other/train-tickets/12306-tickets/12306-tickets"
path: "/pages/other/tickets-app/index?type=trainTicket"
},
{
icon: "/static/image/index/qita/gongzidan.png",
name: "工资",
name: "工资",
path: "/pages/other/splash/splash"
},
{
@ -242,22 +260,76 @@ const otherList = [{
name: "视频群聊",
path: "/pages/other/video-group-chat/video-group-chat"
},
{
icon: "/static/image/index/qita/card.png",
name: "身份证",
path: "/pages/other/card/card"
},
{
icon: "/static/image/index/qita/message.png",
name: "短信",
path: "/pages/common/call-and-message-entry/call-and-message-entry?type=message"
},
{
icon: "/static/image/index/qita/call.png",
name: "通话",
path: "/pages/common/call-and-message-entry/call-and-message-entry?type=call"
},
{
icon: "/static/image/index/qita/ranking.png",
name: "从夯倒拉排名",
path: "/pages/other/ranking/ranking"
},
{
icon: "/static/image/index/qita/gouwu.png",
name: "购物",
path: "/pages/shopping/index"
},
{
icon: "/static/image/other/certificate/certificate.png",
name: "证书",
path: "/pages/other/certificate/index"
},
{
icon: "/static/image/other/silkBanner/silkBanner.png",
name: "锦旗",
path: "/pages/other/silkBanner/silkBanner"
},
{
icon: "/static/image/index/qita/about-iphone.png",
name: "关于本机",
path: "/pages/other/about-this-iphone/about-this-iphone"
},
{
icon: "/static/image/other/bank/bank.png",
name: "银行卡",
path: "/pages/other/bank/index"
},
{
icon: "/static/image/index/qita/game.png",
name: "王者主页",
path: "/pages/other/game/honor-of-kings"
},
]
const data = reactive({
isReady: false,
navBarBgColor: 'transparent',
statusBarHeight: 0,
windowWidth: 0,
windowHeight: 0,
windowWidth: 390, // 防0导致初次Weex负数高度白屏崩溃
windowHeight: 844, // 防0导致初次Weex负数高度白屏崩溃
userInfo: {},
videoHelpList: [],
noticeInfo: {},
vision: "",
platform: '', // 添加平台信息,
qqgroup: {}
qqgroup: {},
idcard: false,
bank: false
})
const {
isReady,
statusBarHeight,
windowWidth,
windowHeight,
@ -289,24 +361,60 @@ onLoad(async () => {
data.vision = uni.getStorageSync('version')
})
const updateSystemSize = () => {
try {
const systemInfo = uni.getSystemInfoSync();
// 防止安卓宿主中首次热更新进入短暂状态下获取宽高均为0导致的白屏bug
if (systemInfo.windowHeight > 0 && systemInfo.windowWidth > 0) {
data.statusBarHeight = systemInfo.statusBarHeight;
data.windowWidth = systemInfo.windowWidth;
data.windowHeight = systemInfo.windowHeight;
// 针对部分小米/底层ROM的顽疾
// 强制延迟挂载超重DOM让其宿主完全完成测绘后再放入节点避免Weex渲染主线程卡死白屏
setTimeout(() => {
data.isReady = true;
}, 300);
} else {
// 未获取到正常宽高,延迟再次进行重试
setTimeout(updateSystemSize, 100);
}
} catch (error) {
setTimeout(updateSystemSize, 100);
}
}
onShow(() => {
// 启动时获取数据
fetchUserData()
// 每次显示时刷新数据
setUserData()
// 获取系统信息
const systemInfo = uni.getSystemInfoSync();
data.statusBarHeight = systemInfo.statusBarHeight;
data.windowWidth = systemInfo.windowWidth;
data.windowHeight = systemInfo.windowHeight;
// 循环检测更新真实屏幕宽高
updateSystemSize();
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#F0F4F9')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// util.setAndroidSystemBarColor('#F0F4F9');
// plus.navigator.setStatusBarStyle("dark");
}, 800);
// #endif
})
function isShowBankIdCard(item) {
if (item.name == '银行卡' || item.name == '身份证') {
console.log(item.name != '银行卡' || item.name != '身份证')
console.log(item.name == '银行卡' && data.bank)
console.log(item.name == '银行卡' && data.bank)
}
if (item.name != '银行卡' && item.name != '身份证') {
return true
} else if (item.name == '银行卡' && data.bank) {
return true
} else if (item.name == '身份证' && data.idcard) {
return true
} else {
return false
}
}
/**
* 获取用户数据(从服务器)
*/
@ -330,11 +438,17 @@ const fetchUserData = async () => {
// 处理用户配置结果
if (configResult.status === 'fulfilled') {
console.log('用户配置获取成功')
console.log('用户配置获取成功', configResult)
} else {
console.error('获取用户配置失败:', configResult.reason)
}
if (configResult.value.config['client.uniapp.bank'] || false) {
data.bank = true
}
if (configResult.value.config['client.uniapp.idcard'] || false) {
data.idcard = true
}
// 刷新页面数据
setUserData()
@ -387,7 +501,7 @@ const setUserData = () => {
const userInfoData = storage.get("userInfo")
data.userInfo = userInfoData || {
user_id: '加载中...',
avater: '/static/default-avatar.png',
avater: '',
vip: 0,
vip_expire: ''
}
@ -461,7 +575,7 @@ const clickNotice = () => {
console.log("点击公告", noticeInfo.value)
if (!noticeInfo.value.url) return
const url = noticeInfo.value.url + `&uni_id=${userInfo.value.user_id}`
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${noticeInfo.value.title}`)
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${noticeInfo.value.title}&isClose=${true}`)
}
/**
@ -830,7 +944,7 @@ onUnload(() => {
}
.group-box {
margin: 32rpx;
margin: 16px;
margin-bottom: 0;
}
@ -843,7 +957,7 @@ onUnload(() => {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
justify-content: flex-start;
background-color: #FFFFFF;
padding: 24rpx 0;
border-radius: 24rpx;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,234 @@
[
{
"id": 12365458895222,
"unRead": true,
"unReadNumber": 2,
"noNotice": true,
"img": "",
"title": "京东快递",
"content": "【京东快递】您的快递已送达博朗郡二期...",
"chatList": [
{
"id": "uuid-001-a1",
"time": "2026-03-15 10:24",
"content": "<p>【京东快递】快递尾号29398已放在博朗郡二期惠美家便利店快递员电话15320547739。</p>",
"isMe": false,
"simKa": 1
},
{
"id": "uuid-001-a2",
"time": "2026-03-16 08:31",
"content": "<p>【京东快递】您的包裹已签收,感谢使用京东快递,期待再次为您服务。</p>",
"isMe": false,
"simKa": 1
}
],
"time": "2026-03-16 08:31:00"
},
{
"id": 12365458895223,
"unRead": false,
"unReadNumber": 0,
"noNotice": false,
"img": "",
"title": "招商银行",
"content": "【招商银行】账户变动提醒",
"chatList": [
{
"id": "uuid-002-b1",
"time": "2026-03-15 12:00",
"content": "<p>【招商银行】您尾号8888的账户于03月15日11:58收入人民币90000.00元余额965600.50元。</p>",
"isMe": false,
"simKa": 1
}
],
"time": "2026-03-15 12:00:00"
},
{
"id": 12365458895224,
"unRead": true,
"unReadNumber": 1,
"noNotice": false,
"img": "",
"title": "中国移动",
"content": "【中国移动】流量使用周报",
"chatList": [
{
"id": "uuid-003-c1",
"time": "2026-03-14 09:00",
"content": "<p>【中国移动】截至14日09时您本月已使用通用流量18.5GB剩余3.2GB。</p>",
"isMe": false,
"simKa": 1
},
{
"id": "uuid-003-c2",
"time": "2026-03-14 18:30",
"content": "<p>【中国移动】流量包订购成功您已成功领取5GB周末流量包立即生效。</p>",
"isMe": false,
"simKa: 1"
}
],
"time": "2026-03-14 18:30:00"
},
{
"id": 12365458895225,
"unRead": false,
"unReadNumber": 0,
"noNotice": false,
"img": "",
"title": "验证码中心",
"content": "【抖音】您的验证码是 882931",
"chatList": [
{
"id": "uuid-004-d1",
"time": "2026-03-16 01:10",
"content": "<p>【抖音】验证码 882931用于手机绑定。5分钟内有效请勿泄露给他人。</p>",
"isMe": false,
"simKa": 1
}
],
"time": "2026-03-16 01:10:00"
},
{
"id": 12365458895226,
"unRead": false,
"unReadNumber": 0,
"noNotice": true,
"img": "",
"title": "美团外卖",
"content": "【美团】订单配送通知",
"chatList": [
{
"id": "uuid-005-e1",
"time": "2026-03-15 18:30",
"content": "<p>【美团】商家已接单,骑手正在赶往商家,请耐心等待。</p>",
"isMe": false,
"simKa": 2
},
{
"id": "uuid-005-e2",
"time": "2026-03-15 19:15",
"content": "<p>【美团】骑手已送达!祝您用餐愉快,给个五星好评吧!</p>",
"isMe": false,
"simKa": 2
}
],
"time": "2026-03-15 19:15:00"
},
{
"id": 12365458895227,
"unRead": true,
"unReadNumber": 3,
"noNotice": false,
"img": "",
"title": "建设银行",
"content": "【建设银行】还款提醒",
"chatList": [
{
"id": "uuid-006-f1",
"time": "2026-03-10 10:00",
"content": "<p>【建设银行】您03月信用卡账单已出应还款额为¥3,200.00。</p>",
"isMe": false,
"simKa": 1
},
{
"id": "uuid-006-f2",
"time": "2026-03-13 14:00",
"content": "<p>【建设银行】温馨提醒您的账单将于3天后到期请确保扣款账户余额充足。</p>",
"isMe": false,
"simKa": 1
},
{
"id": "uuid-006-f3",
"time": "2026-03-16 09:00",
"content": "<p>【建设银行】扣款成功。感谢您使用建设银行信用卡。</p>",
"isMe": false,
"simKa": 1
}
],
"time": "2026-03-16 09:00:00"
},
{
"id": 12365458895228,
"unRead": false,
"unReadNumber": 0,
"noNotice": false,
"img": "",
"title": "12306",
"content": "【12306】购票成功通知",
"chatList": [
{
"id": "uuid-007-g1",
"time": "2026-03-11 11:00",
"content": "<p>【12306】订单EG12345678支付成功。北京南-上海虹桥03月20日14:00开。</p>",
"isMe": false,
"simKa": 2
}
],
"time": "2026-03-11 11:00:00"
},
{
"id": 12365458895229,
"unRead": false,
"unReadNumber": 0,
"noNotice": true,
"img": "",
"title": "腾讯科技",
"content": "【腾讯科技】安全提醒",
"chatList": [
{
"id": "uuid-008-h1",
"time": "2026-03-13 22:30",
"content": "<p>【腾讯科技】您的QQ账号在异地登录登录地点广州。如非本人操作请及时改密。</p>",
"isMe": false,
"simKa": 1
}
],
"time": "2026-03-13 22:30:00"
},
{
"id": 12365458895230,
"unRead": true,
"unReadNumber": 1,
"noNotice": false,
"img": "",
"title": "菜鸟驿站",
"content": "【菜鸟驿站】取件通知",
"chatList": [
{
"id": "uuid-009-i1",
"time": "2026-03-16 10:15",
"content": "<p>【菜鸟驿站】您的中通包裹已到达。凭取件码 8-2-4002 领取,地址:博朗郡东门。</p>",
"isMe": false,
"simKa: 1"
}
],
"time": "2026-03-16 10:15:00"
},
{
"id": 12365458895231,
"unRead": false,
"unReadNumber": 0,
"noNotice": false,
"img": "",
"title": "阿里云",
"content": "【阿里云】资源包即将到期",
"chatList": [
{
"id": "uuid-010-j1",
"time": "2026-03-12 09:00",
"content": "<p>【阿里云】尊敬的用户您的OSS存储包将于7天后到期请及时续费。</p>",
"isMe": false,
"simKa": 1
},
{
"id": "uuid-010-j2",
"time": "2026-03-15 15:00",
"content": "<p>【阿里云】续费成功。您的OSS存储包有效期已延长至2027年03月。</p>",
"isMe": false,
"simKa": 1
}
],
"time": "2026-03-15 15:00:00"
}
]

View File

@ -0,0 +1,776 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_other_message" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_message')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view>
<MessageNavBar :phone="data.phone" :isScroll="data.isScroll" @add="openAddPopup" @setSim="setSim"
:noticeCount="allNoticeCount" @setNoticeCount="setNoticeCount">
<view v-if="data.phone == 'huawei'" class="huawei-notice">
<view class="img-box">
<image class="img" src="/static/image/phone-message/huawei/notice.png" mode="aspectFill"></image>
<view class="dot" v-if="data.noticeCount > 0">{{ data.noticeCount > 99 ? '99+' : data.noticeCount }}
</view>
</view>
<view class="text">通知信息</view>
<image class="right-img" src="/static/image/phone-message/huawei/right.png" mode="aspectFill"></image>
</view>
<MessageList :phone="data.phone" :list="defaultList" @item-click="itemClick" @delete-item="deleteItem"
@edit-item="editItem">
</MessageList>
</MessageNavBar>
<!-- 添加短信弹窗 -->
<view v-if="showAddPopup" class="add-mask" @tap="closeAddPopup">
<view class="add-popup" @tap.stop>
<view class="add-header">{{ editingItem ? '编辑短信' : '新建短信' }}</view>
<view class="add-body">
<view class="add-row">
<text class="add-label">头像URL</text>
<view style="position: relative;">
<view class="image-box" :class="addForm.imgShape" style="width: 84rpx;height: 84rpx;"
@tap="selectImage">
<image v-if="addForm.img" class="image w100 h100" :src="addForm.img" mode="aspectFill">
</image>
<image v-else class="image w100 h100" src="/static/image/phone-message/add.png"
mode="aspectFill">
</image>
</view>
<view v-if="addForm.img" class="delete-img-icon" @tap.stop="deleteImage">
×
</view>
</view>
<view v-if="data.phone == 'iphone'"
style="flex: 1;display: flex;align-items: center;justify-content: flex-end;">
<uni-data-checkbox style="display: flex;justify-content: flex-end;"
v-model="addForm.imgShape" :localdata="shape"></uni-data-checkbox>
</view>
</view>
<view class="add-row">
<text class="add-label required">联系人</text>
<input class="add-input" v-model="addForm.title" placeholder="请输入名称或号码" />
</view>
<view class="add-row" v-if="data.phone == 'oppo'">
<text class="add-label">地区</text>
<input class="add-input" v-model="addForm.area" placeholder="请输入地区" />
</view>
<view class="add-row between" v-if="data.phone == 'iphone'">
<text class="add-label">取消通知</text>
<switch :checked="addForm.noNotice" @change="addForm.noNotice = !addForm.noNotice" />
</view>
<view class="add-row between">
<text class="add-label">是否未读</text>
<switch :checked="addForm.unRead" @change="addForm.unRead = !addForm.unRead" />
</view>
<view class="add-row"
v-if="addForm.unRead && (data.phone == 'oppo' || data.phone == 'huawei' || data.phone == 'iphone')">
<text class="add-label">未读数量</text>
<input class="add-input" type="number" v-model="addForm.unReadNumber" placeholder="请输入未读数量" />
</view>
<view class="add-row" v-if="addForm.chatList.length == 0 && editingItem">
<text class="add-label">消息时间</text>
<view class="time-picker-group">
<picker mode="date" :value="addForm.date" @change="onAddDateChange">
<view class="time-picker-item">
<text>{{ addForm.date || '选择日期' }}</text>
</view>
</picker>
<picker mode="time" :value="addForm.timeOfDay" @change="onAddTimeChange">
<view class="time-picker-item">
<text>{{ addForm.timeOfDay || '选择时刻' }}</text>
</view>
</picker>
</view>
</view>
</view>
<view class="add-footer">
<view class="add-btn cancel" @tap="closeAddPopup">取消</view>
<view class="add-btn confirm" @tap="confirmAdd">确定</view>
</view>
</view>
</view>
<!-- 设置运营商弹窗 -->
<view v-if="showSimPopup" class="add-mask" @tap="closeSimPopup">
<view class="add-popup" @tap.stop>
<view class="add-header">设置卡1卡2运营商</view>
<view class="add-body">
<view class="add-row">
<text class="add-label">卡1运营商</text>
<input class="add-input" v-model="simForm.sim1" placeholder="请输入卡1运营商名称" />
</view>
<view class="add-row">
<text class="add-label">卡2运营商</text>
<input class="add-input" v-model="simForm.sim2" placeholder="请输入卡2运营商名称" />
</view>
</view>
<view class="add-footer">
<view class="add-btn cancel" @tap="closeSimPopup">取消</view>
<view class="add-btn confirm" @tap="confirmSim">确定</view>
</view>
</view>
</view>
<!-- 设置通知信息未读数弹窗 -->
<view v-if="showNoticePopup" class="add-mask" @tap="closeNoticePopup">
<view class="add-popup" @tap.stop>
<view class="add-header">设置通知信息</view>
<view class="add-body">
<view class="add-row">
<text class="add-label">未读数量</text>
<input class="add-input" type="number" v-model="noticeForm.count" placeholder="请输入通知信息数量" />
</view>
</view>
<view class="add-footer">
<view class="add-btn cancel" @tap="closeNoticePopup">取消</view>
<view class="add-btn confirm" @tap="confirmNotice">确定</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import MessageNavBar from '@/components/message/list/message-nav-bar.vue'
import MessageList from '@/components/message/list/list.vue'
import defaultData from './defaultData.json'
import {
ref,
reactive,
computed
} from 'vue'
import {
onLoad,
onShow,
onPageScroll
} from "@dcloudio/uni-app";
import {
stringUtil,
util
} from '@/utils/common.js';
const defaultList = ref(defaultData)
const data = reactive({
navBar: {
title: '信息',
bgColor: '#FFFFFF',
},
phone: 'iphone',
isScroll: false,
noticeCount: 256
})
const shape = [{
text: '圆形',
value: 'circle'
}, {
text: '方形',
value: 'square',
}]
const STORAGE_KEY = 'message_list'
const SIM_STORAGE_KEY = 'sim_info'
const NOTICE_COUNT_KEY = 'huawei_notice_count'
const allNoticeCount = computed(() => {
let count = 0
defaultList.value.forEach(element => {
if (element.unRead) {
count += element.unReadNumber
}
});
return Number(data.noticeCount) + Number(count)
})
onLoad((options) => {
if (options.phone) {
data.phone = options.phone
}
})
onShow(() => {
// #ifdef APP-PLUS
if (data.phone == 'oppo') {
util.setAndroidSystemBarColor('#FAFAFA')
} else {
util.setAndroidSystemBarColor('#ffffff')
}
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// #endif
//
try {
const cached = uni.getStorageSync(STORAGE_KEY)
if (cached) {
defaultList.value = JSON.parse(cached)
}
//
const cachedNotice = uni.getStorageSync(NOTICE_COUNT_KEY)
if (cachedNotice !== '') {
data.noticeCount = Number(cachedNotice)
}
} catch (e) { console.error(e) }
})
onPageScroll((e) => {
if (e.scrollTop > 60) {
data.isScroll = true
} else {
data.isScroll = false
}
})
/**
* 将图片保存到本地持久化存储
* @param {string} tempFilePath 临时文件路径
* @returns {Promise<string>} 持久化后的文件路径
*/
const saveImageToLocal = (tempFilePath) => {
return new Promise((resolve) => {
//
if (!tempFilePath || tempFilePath.startsWith('_doc') || tempFilePath.startsWith('/static')) {
return resolve(tempFilePath)
}
uni.saveFile({
tempFilePath: tempFilePath,
success: (res) => {
console.log('图片持久化成功:', res.savedFilePath)
resolve(res.savedFilePath)
},
fail: (err) => {
console.error('图片持久化失败:', err)
resolve(tempFilePath) // 退使
}
})
})
}
/**
* 删除本地持久化文件
* @param {string} filePath 文件路径
*/
const removeLocalFile = (filePath) => {
if (filePath && filePath.startsWith('_doc')) {
uni.removeSavedFile({
filePath: filePath,
success: () => {
console.log('本地文件删除成功:', filePath)
},
fail: (err) => {
console.warn('本地文件删除失败:', filePath, err)
}
})
}
}
/**
* 删除元素
* @param item
*/
const deleteItem = (item) => {
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success: (res) => {
if (res.confirm) {
const idx = defaultList.value.findIndex(i => i.id === item.id)
if (idx > -1) {
//
if (item.img) {
removeLocalFile(item.img)
}
//
if (item.chatList && item.chatList.length > 0) {
item.chatList.forEach(msg => {
if (msg.type === 'image' && msg.imgUrl && msg.imgUrl.includes('_doc/')) {
removeLocalFile(msg.imgUrl)
}
})
}
defaultList.value.splice(idx, 1)
uni.setStorageSync(STORAGE_KEY, JSON.stringify(defaultList.value))
}
}
}
})
}
/**
* 选择图片
*/
const selectImage = () => {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
addForm.img = res.tempFilePaths[0]
}
})
}
const deleteImage = () => {
addForm.img = ''
}
const itemClick = (item) => {
util.goPage(`/pages/message/chat-page/chat-page?phone=${data.phone}&id=${item.id}`)
}
// ===== =====
const showAddPopup = ref(false)
const addForm = reactive({
title: '',
img: '',
content: '',
date: '',
timeOfDay: ''
})
// null==
const editingItem = ref(null)
const openAddPopup = () => {
//
const now = new Date()
const pad = v => String(v).padStart(2, '0')
editingItem.value = null
addForm.title = ''
addForm.img = ''
addForm.content = ''
addForm.area = ''
addForm.unRead = false
addForm.unReadNumber = 1
addForm.noNotice = false
addForm.chatList = []
addForm.imgShape = 'circle'
addForm.date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
addForm.timeOfDay = `${pad(now.getHours())}:${pad(now.getMinutes())}`
showAddPopup.value = true
}
const closeAddPopup = () => {
showAddPopup.value = false
}
const onAddDateChange = (e) => {
addForm.date = e.detail.value
}
const onAddTimeChange = (e) => {
addForm.timeOfDay = e.detail.value
}
/**
* 编辑元素回填表单并打开弹窗
*/
const editItem = (item) => {
editingItem.value = item
const lastMsg = item.chatList && item.chatList.length ? item.chatList[item.chatList.length - 1] : null
const timeStr = lastMsg ? lastMsg.time : (item.time || '')
const parts = timeStr.split(' ')
addForm.title = item.title || ''
addForm.img = item.img || ''
addForm.imgShape = item.imgShape || 'circle'
addForm.content = ''
addForm.area = item.area || ''
addForm.unRead = !!item.unRead
addForm.unReadNumber = item.unReadNumber || 0
addForm.noNotice = item.noNotice || false
addForm.date = parts[0] || ''
addForm.timeOfDay = parts[1] || ''
addForm.chatList = item.chatList || []
showAddPopup.value = true
}
const confirmAdd = async () => {
if (!addForm.title.trim()) {
uni.showToast({ title: '请输入联系人名称', icon: 'none' })
return
}
const time = `${addForm.date} ${addForm.timeOfDay}`.trim()
//
let finalImgPath = addForm.img
if (editingItem.value) {
//
if (addForm.img !== editingItem.value.img) {
finalImgPath = await saveImageToLocal(addForm.img)
removeLocalFile(editingItem.value.img)
}
} else {
//
finalImgPath = await saveImageToLocal(addForm.img)
}
if (editingItem.value) {
// ===== =====
const idx = defaultList.value.findIndex(i => i.id === editingItem.value.id)
if (idx > -1) {
defaultList.value[idx].title = addForm.title.trim()
defaultList.value[idx].img = finalImgPath
defaultList.value[idx].unRead = addForm.unRead
defaultList.value[idx].unReadNumber = addForm.unReadNumber
defaultList.value[idx].noNotice = addForm.noNotice
defaultList.value[idx].imgShape = addForm.imgShape
defaultList.value[idx].area = addForm.area
defaultList.value[idx].time = time
}
} else {
// ===== =====
const newItem = {
id: Date.now(),
unRead: addForm.unRead,
unReadNumber: addForm.unReadNumber,
noNotice: addForm.noNotice,
img: finalImgPath,
title: addForm.title.trim(),
imgShape: addForm.imgShape,
area: addForm.area,
chatList: addForm.content.trim() ? [
{
id: stringUtil.uuid(),
time: time,
content: `<p>${addForm.content.trim()}</p>`,
isMe: false
}
] : [],
time: time
}
defaultList.value.unshift(newItem)
}
//
defaultList.value.sort((a, b) => {
const timeA = new Date(((a.chatList && a.chatList.length ? a.chatList[a.chatList.length - 1].time : null) || a.time || '').replace(/-/g, '/')).getTime()
const timeB = new Date(((b.chatList && b.chatList.length ? b.chatList[b.chatList.length - 1].time : null) || b.time || '').replace(/-/g, '/')).getTime()
return timeB - timeA
})
try {
uni.setStorageSync(STORAGE_KEY, JSON.stringify(defaultList.value))
} catch (e) { console.error(e) }
closeAddPopup()
}
// ===== =====
const showSimPopup = ref(false)
const simForm = reactive({
sim1: '',
sim2: ''
})
const setSim = () => {
try {
const cached = uni.getStorageSync(SIM_STORAGE_KEY)
const simInfo = cached ? JSON.parse(cached) : { sim1: '中国电信', sim2: '中国移动' }
simForm.sim1 = simInfo.sim1
simForm.sim2 = simInfo.sim2
} catch (e) {
simForm.sim1 = '中国电信'
simForm.sim2 = '中国移动'
}
showSimPopup.value = true
}
const closeSimPopup = () => {
showSimPopup.value = false
}
const confirmSim = () => {
try {
uni.setStorageSync(SIM_STORAGE_KEY, JSON.stringify({
sim1: simForm.sim1.trim() || '联通',
sim2: simForm.sim2.trim() || '移动'
}))
uni.showToast({ title: '保存成功', icon: 'success' })
} catch (e) { console.error(e) }
closeSimPopup()
}
// ===== =====
const showNoticePopup = ref(false)
const noticeForm = reactive({
count: 256
})
const setNoticeCount = () => {
noticeForm.count = data.noticeCount
showNoticePopup.value = true
}
const closeNoticePopup = () => {
showNoticePopup.value = false
}
const confirmNotice = () => {
data.noticeCount = Number(noticeForm.count) || 0
try {
uni.setStorageSync(NOTICE_COUNT_KEY, data.noticeCount)
uni.showToast({ title: '保存成功', icon: 'success' })
} catch (e) { console.error(e) }
closeNoticePopup()
}
</script>
<style>
@import '@/common/main.css';
page {
background-color: #FFFFFF;
}
</style>
<style lang="less">
::v-deep .uni-navbar__header-btns {
width: 100px !important;
flex: 1;
}
.iphone-style {
.mg-r-30 {
margin-right: 60rpx;
}
.mg-r-5 {
margin-right: 10rpx;
}
.left-icon {
width: 40rpx;
height: 40rpx;
}
.right-icon {
width: 48rpx;
height: 48rpx;
}
.left-text {
font-size: 32rpx;
color: #0278E2;
margin-left: 10rpx;
}
.center-text {
font-size: 32rpx;
color: #1a1a1a;
}
}
.mi-style {
.right-icon {
width: 40rpx;
height: 40rpx;
margin-right: 30rpx;
}
}
/* ===== 添加短信弹窗 ===== */
.add-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.5);
z-index: 999;
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}
.add-popup {
width: 88%;
background-color: #ffffff;
border-radius: 24rpx;
overflow: hidden;
}
.add-header {
padding: 36rpx 40rpx 20rpx;
font-size: 34rpx;
font-weight: bold;
color: #1A1A1A;
text-align: center;
}
.add-body {
padding: 8rpx 0;
}
.add-row {
display: flex;
align-items: center;
padding: 20rpx 32rpx;
gap: 16rpx;
.image-box {
border-radius: 50%;
overflow: hidden;
}
.circle {
border-radius: 50%;
}
.square {
border-radius: 16rpx;
}
}
.between {
justify-content: space-between;
}
.required {
position: relative;
}
.required::before {
position: absolute;
left: -10px;
content: '*';
top: 0;
color: #EA0000;
}
.add-label {
font-size: 28rpx;
color: #1A1A1A;
width: 150rpx;
flex-shrink: 0;
}
.add-input {
flex: 1;
height: 70rpx;
background-color: #F6F6F6;
border-radius: 14rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #1A1A1A;
::v-deep .uni-input {
color: #aaaaaa;
}
}
.add-footer {
padding: 32rpx 24rpx;
display: flex;
}
.add-btn {
flex: 1;
height: 90rpx;
line-height: 90rpx;
text-align: center;
font-size: 32rpx;
background-color: #F1F1F1;
margin: 0 16rpx;
border-radius: 12rpx;
}
.add-btn.cancel {
color: #767676;
}
.add-btn.confirm {
color: #fff;
background-color: #1777FF;
}
.time-picker-group {
display: flex;
flex-direction: row;
gap: 16rpx;
flex: 1;
}
.time-picker-item {
flex: 1;
height: 70rpx;
line-height: 70rpx;
background-color: #F8F8F8;
border-radius: 8rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #333333;
text-align: center;
}
.huawei-notice {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32rpx;
height: 124rpx;
.img-box {
position: relative;
width: 76rpx;
height: 76rpx;
flex-shrink: 0;
.img {
width: 100%;
height: 100%;
}
.dot {
position: absolute;
top: -12rpx;
right: -8rpx;
height: 28rpx;
line-height: 30rpx;
padding: 0 10rpx;
background-color: #EA0000;
border-radius: 16rpx;
font-size: 20rpx;
color: #fff;
}
}
.text {
flex: 1;
font-size: 30rpx;
color: #1A1A1A;
margin: 0 32rpx;
font-weight: 500;
}
.right-img {
width: 28rpx;
height: 28rpx;
flex-shrink: 0;
}
}
.delete-img-icon {
position: absolute;
top: -12rpx;
right: -12rpx;
width: 32rpx;
height: 32rpx;
background-color: red;
color: #fff;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
line-height: 28rpx;
padding-bottom: 2rpx;
z-index: 2;
}
</style>

View File

@ -0,0 +1,423 @@
<template>
<view>
<!-- Custom Nav Bar -->
<view class="custom-nav-bar" :class="{ 'android-nav': isAndroid }">
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<view class="nav-content">
<view class="left-icon" @click="back">
<image class="back-icon" src="/static/image/common/iphone-back.png" />
</view>
<view class="title">关于本机</view>
<view class="right-placeholder"></view>
</view>
</view>
<!-- Placeholder to push content down -->
<view class="nav-placeholder" :style="{ height: statusBarHeight + 74 + 'px' }"></view>
<view class="info-container">
<block v-for="(group, groupIndex) in listData" :key="groupIndex">
<!-- Group Header (Optional) -->
<view v-if="group.groupTitle" class="group-header">
<text class="header-text">{{ group.groupTitle }}</text>
</view>
<!-- Group Items -->
<view class="info-group">
<view class="info-item" v-for="(item, itemIndex) in group.items" :key="item.id"
:class="{ 'no-border': itemIndex === group.items.length - 1 }"
@click="handleEdit(groupIndex, itemIndex)">
<text class="item-label">{{ item.label }}</text>
<view class="item-value-box">
<text class="item-value" v-if="item.value !== undefined">{{ item.value }}</text>
<!-- <view class="chevron"></view> -->
<image class="chevron" v-if="item.hasArrow"
src="/static/image/common/about-iphone-right.png">
</image>
</view>
</view>
</view>
</block>
</view>
<!-- Custom Modal -->
<view class="custom-modal-mask" v-if="isModalVisible" @click="closeModal">
<view class="custom-modal" @click.stop>
<view class="modal-header">
<text class="modal-title">{{ modalTitle }}</text>
</view>
<view class="modal-body">
<input class="modal-input" v-model="modalInputValue" :placeholder="modalPlaceholder"
cursor-spacing="20" />
</view>
<view class="modal-footer">
<view class="modal-btn cancel-btn" @click="closeModal">取消</view>
<view class="modal-btn confirm-btn" @click="confirmModal">确定</view>
</view>
</view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_shopping_about_iphone" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_shopping_about_iphone')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</template>
<script setup>
import { ref, onMounted, getCurrentInstance } from 'vue';
import { util } from '@/utils/common.js'
import { onShow, onLoad, onPageScroll } from '@dcloudio/uni-app'
const { proxy } = getCurrentInstance();
const CACHE_KEY = 'about_iphone_data';
// Removed scroll logic, styling is now fully CSS-driven
const defaultData = [
{
groupTitle: '',
items: [
{ id: 'name', label: '名称', value: '胡英俊的iPhone', hasArrow: true },
{ id: 'iosVersion', label: 'iOS版本', value: '26.4.2', hasArrow: true },
{ id: 'modelName', label: '型号名称', value: 'iPhone 17 Pro Max', hasArrow: false },
{ id: 'modelNumber', label: '型号', value: 'MG8W4CH/A', hasArrow: false },
{ id: 'serialNumber', label: '序列号', value: 'J5YQ2R5F8R', hasArrow: false }
]
},
{
groupTitle: '',
items: [
{ id: 'warranty', label: '有限保修', value: '到期日期2026/12/13', hasArrow: true }
]
},
{
groupTitle: '',
items: [
{ id: 'songs', label: '歌曲', value: '0', hasArrow: false },
{ id: 'videos', label: '视频', value: '456', hasArrow: false },
{ id: 'photos', label: '照片', value: '6,568', hasArrow: false },
{ id: 'apps', label: '应用程序', value: '64', hasArrow: false },
{ id: 'capacity', label: '总容量', value: '2 TB', hasArrow: false },
{ id: 'available', label: '可用容量', value: '1.56 TB', hasArrow: false }
]
},
{
groupTitle: '',
items: [
{ id: 'wifi', label: '无线局域网地址', value: '87:5F:4D:48:DF:36', hasArrow: false },
{ id: 'bluetooth', label: '蓝牙', value: '87:5F:4D:2B:DF:68', hasArrow: false },
{ id: 'firmware', label: '调制解调器固件', value: '1.55.25', hasArrow: false },
{ id: 'seid', label: 'SEID', value: '', hasArrow: true },
{ id: 'carrierLock', label: '运营商锁', value: '无SIM卡限制', hasArrow: false }
]
},
{
groupTitle: '主号',
items: [
{ id: 'primaryNetwork', label: '网络', value: '中国移动全球通', hasArrow: false },
{ id: 'primaryCarrier', label: '运营商', value: '中国移动 69.0', hasArrow: false },
{ id: 'primaryIMEI', label: 'IMEI', value: '35 756481 6466451 3', hasArrow: false },
{ id: 'primaryICCID', label: 'ICCID', value: '45632144856512347896', hasArrow: false }
]
},
{
groupTitle: '个人',
items: [
{ id: 'personalNetwork', label: '网络', value: '中国电信', hasArrow: false },
{ id: 'personalCarrier', label: '运营商', value: '中国电信 69.0', hasArrow: false },
{ id: 'personalIMEI', label: 'IMEI', value: '35 756481 6466451 3', hasArrow: false },
{ id: 'personalICCID', label: 'ICCID', value: '45632144856512347896', hasArrow: false }
]
},
{
groupTitle: '',
items: [
{ id: 'trustSettings', label: '证书信任设置', value: '', hasArrow: true }
]
}
];
const listData = ref([]);
const statusBarHeight = ref(0);
const isAndroid = ref(false);
onShow(() => {
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#F2F1F6')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500);
// #endif
})
onLoad(() => {
proxy.$apiUserEvent('all', {
type: 'event',
key: 'ios-about-phone',
prefix: '.uni.other.',
value: '设置-关于本机'
})
})
onMounted(() => {
const systemInfo = uni.getSystemInfoSync();
statusBarHeight.value = systemInfo.statusBarHeight || 0;
if (systemInfo.osName === 'android' || systemInfo.platform === 'android') {
isAndroid.value = true;
}
const cached = uni.getStorageSync(CACHE_KEY);
if (cached) {
try {
listData.value = JSON.parse(cached);
} catch (e) {
listData.value = JSON.parse(JSON.stringify(defaultData));
}
} else {
listData.value = JSON.parse(JSON.stringify(defaultData));
}
});
const back = () => {
uni.navigateBack();
};
const isModalVisible = ref(false);
const modalTitle = ref('');
const modalPlaceholder = ref('');
const modalInputValue = ref('');
let currentEditGroupIndex = -1;
let currentEditItemIndex = -1;
const handleEdit = (groupIndex, itemIndex) => {
const item = listData.value[groupIndex].items[itemIndex];
currentEditGroupIndex = groupIndex;
currentEditItemIndex = itemIndex;
modalTitle.value = `修改${item.label}`;
modalPlaceholder.value = `请输入新的${item.label}`;
modalInputValue.value = item.value || '';
isModalVisible.value = true;
};
const closeModal = () => {
isModalVisible.value = false;
};
const confirmModal = () => {
if (currentEditGroupIndex !== -1 && currentEditItemIndex !== -1) {
listData.value[currentEditGroupIndex].items[currentEditItemIndex].value = modalInputValue.value;
uni.setStorageSync(CACHE_KEY, JSON.stringify(listData.value));
}
isModalVisible.value = false;
};
</script>
<style lang="less" scoped>
.custom-nav-bar {
opacity: .97;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 999;
background: linear-gradient(180deg, rgba(242, 242, 246, 0.5) 0%, rgba(242, 242, 246, 0) 100%);
backdrop-filter: saturate(220%) blur(14px);
-webkit-backdrop-filter: saturate(220%) blur(14px);
-webkit-mask-image: linear-gradient(180deg, rgba(242, 242, 246, 0.5) 0%, rgba(242, 242, 246, 0) 100%);
mask-image: linear-gradient(to bottom, rgba(0, 0, 0, 1) calc(100% - 50rpx), rgba(0, 0, 0, 0) 100%);
.nav-content {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 26rpx 50rpx;
}
.left-icon,
.right-placeholder {
width: 86rpx;
display: flex;
align-items: center;
}
.title {
flex: 1;
text-align: center;
font-size: 32rpx;
font-weight: 500;
color: #1A1A1A;
}
}
/* 安卓手机降级:取消复杂的渐变、滤镜与遮罩,直接使用纯色 */
.custom-nav-bar.android-nav {
opacity: 1 !important;
background: #F2F1F6 !important;
backdrop-filter: none !important;
-webkit-backdrop-filter: none !important;
mask-image: none !important;
-webkit-mask-image: none !important;
.nav-content {
padding-bottom: 10rpx !important;
}
}
.nav-placeholder {
width: 100%;
padding-top: 146rpx;
}
/* .blur-nav removed */
.back-icon {
box-shadow: 0rpx 0rpx 20rpx 0rpx #E1E1E6;
border: 2rpx solid #FFFFFF;
border-radius: 50%;
width: 86rpx;
height: 86rpx;
}
.info-container {
padding: 30rpx;
padding-top: 0;
padding-bottom: 60rpx;
}
.info-group {
background-color: #FFFFFF;
border-radius: 48rpx;
margin-bottom: 64rpx;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-left: 30rpx;
margin-right: 30rpx;
border-bottom: 0.5px solid #E4E4E4;
line-height: 32rpx;
height: 100rpx;
}
.info-item.no-border {
border-bottom: none;
}
.item-label {
font-size: 32rpx;
color: #1A1A1A;
}
.item-value-box {
display: flex;
align-items: center;
}
.item-value {
font-size: 32rpx;
color: #8A8A8A;
font-weight: 400;
}
.chevron {
width: 24rpx;
height: 24rpx;
margin-left: 12rpx;
}
.group-header {
padding: 0 0 10rpx 30rpx;
}
.header-text {
font-size: 32rpx;
line-height: 32rpx;
margin-bottom: 16rpx;
color: #85858A;
font-weight: 500;
}
.custom-modal-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.4);
z-index: 999;
display: flex;
justify-content: center;
align-items: center;
}
.custom-modal {
width: 600rpx;
background-color: #FFFFFF;
border-radius: 48rpx;
display: flex;
flex-direction: column;
overflow: hidden;
}
.modal-header {
padding: 40rpx 30rpx 30rpx;
text-align: center;
}
.modal-title {
font-size: 34rpx;
color: #1A1A1A;
font-weight: 500;
}
.modal-body {
padding: 0 40rpx 40rpx;
}
.modal-input {
background-color: #F2F1F6;
height: 80rpx;
border-radius: 20rpx;
padding: 0 20rpx;
font-size: 32rpx;
color: #1A1A1A;
}
.modal-footer {
display: flex;
border-top: 0.5px solid #E4E4E4;
height: 100rpx;
}
.modal-btn {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 32rpx;
}
.cancel-btn {
color: #8A8A8A;
border-right: 0.5px solid #E4E4E4;
}
.confirm-btn {
color: #007AFF;
font-weight: 500;
}
</style>
<style>
page {
background-color: #F2F1F6;
}
</style>

View File

@ -1,8 +1,9 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark dark="light" source="uni_alipay_other_airTickets_ctrip" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_airTickets_ctrip')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -270,7 +271,7 @@
<text class="codefun-shrink-0 codefun-self-start font_13 text_47">出行人</text>
<view class="codefun-flex-col codefun-flex-1 codefun-relative group_16 ml-41">
<text class="codefun-self-start text_23" @click="goEdit"><text
style="margin-right: 6px;" v-for="value in airTicketsInfo.passengersInfo">{{
style="margin-right: 6px;" v-for="(value, index) in airTicketsInfo.passengersInfo" :key="index">{{
value.name }}</text></text>
<view class="codefun-flex-row codefun-items-center codefun-self-stretch codefun-mt-8">
<text class="font_14 text_49">查看证件航司预订号修改姓名/证件</text>
@ -366,7 +367,7 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue';
import { reactive, ref, computed, toRefs } from 'vue';
import { reactive, computed, toRefs } from 'vue';
import { onShow, onPageScroll } from '@dcloudio/uni-app';
import { util } from '@/utils/common.js';
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';

View File

@ -86,10 +86,10 @@
<text class="label">到达机场</text>
<input class="input" v-model="ticketsInfo.flightInfo.endAirport" />
</view>
<!-- <view class="form-item">
<view class="form-item">
<text class="label">时长</text>
<input class="input" v-model="ticketsInfo.flightInfo.duration" placeholder="例: 2时5分" />
</view> -->
</view>
<view class="form-item">
<text class="label">机型</text>
<input class="input" v-model="ticketsInfo.flightInfo.aircraftType" />

View File

@ -1,8 +1,9 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark dark="light" source="uni_alipay_other_airTickets_fliggy" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_airTickets_fliggy')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -204,8 +205,8 @@
<text class="label" :class="{ 'empty': index !== 0 }">乘机人</text>
<view class="info" @click="goEdit">
<text class="name">{{ item.name }}</text>
<text class="id-card">{{ item.idType }}{{ item.idType === '身份证' ?
stringUtil.maskIdCard(item.idNumber) : (item.idType === '护照' ?
<text class="id-card">{{ item.idType }}{{ item.idType.includes('身份证') ?
showFristAndLastNumber(item.idNumber) : (item.idType.includes('护照') ?
stringUtil.maskPassport(item.idNumber) : item.idNumber) }}</text>
<view class="ticket-row">
<text class="ticket-no">票号{{ item.ticketNo }}</text>
@ -298,11 +299,11 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue';
import { ref, onMounted, reactive, toRefs, computed } from 'vue';
import { onMounted, reactive, toRefs, computed } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
import airlineJson from '@/static/json/air-line.json';
import { util, numberUtil, stringUtil } from '@/utils/common.js';
import { util, stringUtil } from '@/utils/common.js';
const buttonGroup = [{
name: "编辑机票信息",
@ -371,6 +372,17 @@ const deepMerge = (target, source) => {
return target;
};
const showFristAndLastNumber = (str) => {
if (!str) {
return '';
}
const len = str.length;
if (len <= 2) {
return str;
}
return str.slice(0, 1) + '*'.repeat(len - 2) + str.slice(-1);
}
onShow(() => {
const stored = uni.getStorageSync(data.STORAGE_KEY) || "";
if (stored) {

View File

@ -1,8 +1,9 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark dark="light" source="uni_alipay_other_airTickets_qunar" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_airTickets_qunar')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -198,8 +199,8 @@
<view class="row-content"
@click="util.goPage('/pages/other/air-tickets/edit/edit')">
<view class="p-name">{{ p.name }}</view>
<view class="p-sub">{{ p.idType }}: {{ p.idType === '身份证' ?
stringUtil.maskIdCard(p.idNumber) : (p.idType === '护照' ?
<view class="p-sub">{{ p.idType }}: {{ p.idType.includes('身份证') ?
stringUtil.maskIdCard(p.idNumber) : (p.idType.includes('护照') ?
stringUtil.maskPassport(p.idNumber) : p.idNumber) }}</view>
<view class="p-sub">
<text>票号:{{ p.ticketNo }}</text>
@ -375,7 +376,7 @@ const data = reactive({
ticketData: JSON.parse(JSON.stringify(defualtData))
})
let { navBar, ticketData } = toRefs(data);
let { ticketData } = toRefs(data);
//
const ofCheckInTime = computed(() => {

1264
pages/other/bank/gsyh.vue Normal file

File diff suppressed because it is too large Load Diff

171
pages/other/bank/index.vue Normal file
View File

@ -0,0 +1,171 @@
<template>
<view>
<NavBar :title="data.navBar.title" :bgColor="data.navBar.bgColor" />
<view class="list-container">
<view class="item" v-for="item in source" :key="item.id" @click="goPage(item.callUrl,item.name)"
:style="{ background: ` #FFFFFF` }">
<view class="content flex flex-align-center">
<image class="logo" :src="`/static/image/other/bank/${item.icon}.png`" mode=""></image>
<view class="name flex-1">{{ item.name }}银行</view>
<view class="right-button">
<uni-icons type="right" size="16"></uni-icons>
</view>
</view>
<!-- <view class="line" :style="{ background: item.color.lineColor }"></view> -->
</view>
</view>
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import {
ref,
reactive,
getCurrentInstance
} from 'vue'
import {
onLoad
} from "@dcloudio/uni-app";
import {
util
} from '@/utils/common.js';
const {
appContext,
proxy
} = getCurrentInstance();
//
const source = ref([{
name: '招商',
color: {
bgColor: '#F3EAFF',
lineColor: '#B78EF5',
buttonColor: '#BA8DFF',
},
icon: 'zsyh',
callUrl: "/pages/other/bank/zsyh"
},
{
name: '工商',
color: {
bgColor: '#FFE9E9',
lineColor: '#FF6969',
buttonColor: '#FB6767',
},
icon: 'gsyh',
callUrl: "/pages/other/bank/gsyh"
},
{
name: '农业',
color: {
bgColor: '#FFF0DD',
lineColor: '#FFA143',
buttonColor: '#FFA64D',
},
icon: 'nyyh',
callUrl: "/pages/other/bank/nyyh"
},
{
name: '建设',
color: {
bgColor: '#E0FFD9',
lineColor: '#56B745',
buttonColor: '#5DCD49',
},
icon: 'jsyh',
callUrl: "/pages/other/bank/jsyh"
}
])
const data = reactive({
navBar: {
title: '选择机型',
bgColor: '#F0F4F9',
},
type: "message"
})
onLoad((options) => {
if (options.type) {
data.type = options.type
}
proxy.$apiUserEvent('all', {
type: 'event',
key: 'bank',
prefix: '.uni.other.',
value:'银行卡首页'
})
})
function goPage(url,name) {
if (url) {
proxy.$apiUserEvent('all', {
type: 'event',
key: 'bank',
prefix: '.uni.other.',
value:name+'银行'
})
util.goPage(url)
} else {
uni.showToast({
title: '开发中',
icon: 'none'
})
}
}
</script>
<style>
@import '@/common/main.css';
</style>
<style lang="less">
.list-container {
background-color: #F0F4F9;
padding: 24rpx 32rpx;
}
.item {
width: 100%;
height: 188rpx;
border-radius: 24rpx;
margin-bottom: 24rpx;
padding: 40rpx 36rpx 24rpx 28rpx;
.content {
.logo {
width: 96rpx;
height: 96rpx;
flex-shrink: 0;
}
.name {
margin-left: 32rpx;
color: #1A1A1A;
font-size: 36rpx;
font-weight: 500;
}
.right-button {
width: 140rpx;
height: 64rpx;
border-radius: 16rpx;
color: #ffffff;
font-size: 28rpx;
line-height: 64rpx !important;
text-align: right;
// display: flex;
// align-items: center;
// justify-content: center;
}
}
.line {
width: 100rpx;
height: 8rpx;
filter: blur(5px);
opacity: 0.5;
margin-top: 4rpx;
}
}
</style>

1752
pages/other/bank/jsyh.vue Normal file

File diff suppressed because it is too large Load Diff

1206
pages/other/bank/nyyh.vue Normal file

File diff suppressed because it is too large Load Diff

1505
pages/other/bank/zsyh.vue Normal file

File diff suppressed because it is too large Load Diff

664
pages/other/card/card.vue Normal file
View File

@ -0,0 +1,664 @@
<template>
<view class="container">
<!-- 自定义头部导航栏 -->
<ZdyNavbar @right-click="edit" isRightButton rightButtonText="编辑" :title="data.navbar.title"
:bgColor="data.navbar.bgColor" :isBack="true" />
<view style="display: flex;align-items: center;background: #fff;border-radius: 6px;margin: 10px;padding: 4px;color: #767676;">
<image src="/static/image/other/notice.png" style="width: 16px;height: 16px;margin-right: 10rpx;margin-left: 10rpx;"></image>
此功能不具备真实性仅供娱乐
</view>
<image :src="data.code" mode="widthFix" style="width: 100vw;" @click="previewImage"></image>
<view class="button-container">
<button class="btn-save-image" @click="saveImage">保存图片</button>
</view>
<view style="width: 0px;height: 0;overflow: hidden;" v-if="data.shuaxing">
<l-painter isCanvasToTempFilePath @success="successImage" @progress="progress"
:css="`width:${data.width}px;height:${data.height }px;background-color:#fff;`">
<l-painter-view
:css="`margin-top:109px;margin-left:75px;position: relative;width:666px;height:406px;background-image: url('/static/image/other/card/cardBGImg.png');background-size:6660px 406px;`">
<!-- 头部年月 -->
<l-painter-view :css="`position: absolute;left:122px;top:54px;`">
<l-painter-text :css="data.textCss+data.textCssLeft" :text="data.form.name" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:122px;top:102px;`">
<l-painter-text :css="data.textCss2+data.textCssLeft" :text="data.form.gender" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:262px;top:102px;`">
<l-painter-text :css="data.textCss2+data.textCssLeft" :text="data.form.nation" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:122px;top:152px;`">
<l-painter-text v-for="(item,index) in data.form.year.toString()" :css="data.textCss3+data.textCssLeft"
:text="item" :key="index" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:192px;top:152px;`">
<l-painter-text :css="data.textCss2+data.textCssCenter" :text="data.form.month.toString()" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:254px;top:152px;`">
<l-painter-text :css="data.textCss2+data.textCssCenter" :text="data.form.day.toString()" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:122px;top:200px;width:274px;`">
<l-painter-text v-for="(item,index) in data.form.address" :css="data.textCss3+data.textCssLeft" :text="item"
:key="index" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:223px;top:322px;`">
<l-painter-text v-for="(item,index) in data.form.idNumber" :css="data.textCss4+data.textCssLeft" :text="item"
:key="index" />
</l-painter-view>
<l-painter-view :css="`position: absolute;right:48px;top:62px;`">
<l-painter-image v-if="data.form.photo" :src="data.form.photo" css="width: 196px; height: 230px;" />
<l-painter-view v-else css="width: 196px; height: 230px;background-color:#fff;">
</l-painter-view>
</l-painter-view>
<l-painter-view v-if="$isVip()" :css="`position: absolute;left:535px;bottom:-78px;`">
<l-painter-image src="/static/image/other/card/shuiyin2.png" css="width: 170px;height: 50px;" />
</l-painter-view>
</l-painter-view>
</l-painter>
</view>
<!-- 编辑弹窗 -->
<view v-if="showEditPopup" class="popup-overlay">
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">编辑身份证</text>
<text class="popup-close" @click="closeEditPopup">×</text>
</view>
<view class="popup-body">
<view class="form-row">
<text class="form-label">姓名</text>
<input class="form-input" v-model="editForm.name" type="text" />
</view>
<view class="form-row">
<text class="form-label">性别</text>
<input class="form-input" v-model="editForm.gender" type="text" />
</view>
<view class="form-row">
<text class="form-label">民族</text>
<input class="form-input" v-model="editForm.nation" type="text" />
</view>
<view class="form-row">
<text class="form-label">出生日期</text>
<input class="form-input" v-model="editForm.year" type="number" style="width: 100px;" />
<text class="form-separator">-</text>
<input class="form-input" v-model="editForm.month" type="number" style="width: 80px;" />
<text class="form-separator">-</text>
<input class="form-input" v-model="editForm.day" type="number" style="width: 80px;" />
</view>
<view class="form-row">
<text class="form-label">住址</text>
<input class="form-input" v-model="editForm.address" type="text" />
</view>
<view class="form-row">
<text class="form-label">身份证号</text>
<input class="form-input" v-model="editForm.idNumber" type="text" />
</view>
<view class="form-row">
<text class="form-label">照片</text>
<view class="upload-container">
<image v-if="editForm.photo" :src="editForm.photo" class="upload-image" @click="chooseImage" />
<view v-else class="upload-btn" @click="chooseImage">
<text class="upload-text">点击上传照片</text>
</view>
</view>
</view>
</view>
<view class="popup-footer">
<button class="btn-cancel" @click="closeEditPopup">取消</button>
<button class="btn-save" @click="saveEditForm">保存</button>
</view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_card')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</view>
</template>
<script setup>
//
import ZdyNavbar from "@/components/nav-bar/nav-bar.vue"
import {
ref,
reactive,
watch,
nextTick,
getCurrentInstance
} from "vue";
import {
onLoad,
onShow,
onUnload,
onReady,
onPullDownRefresh,
onReachBottom
} from "@dcloudio/uni-app";
const {
appContext,
proxy
} = getCurrentInstance();
const data = reactive({
shuaxing:true,
navbar: {
title: "身份证",
bgColor: '#EDEDED',
},
width: 800,
height: 600,
code: '',
form: {
name: '某某',
gender: '男',
nation: '汉',
year: 2000,
month: 1,
day: 1,
address: '翻斗大街翻斗花园二号楼1001室',
idNumber: '110000200001010000',
photo: '',
},
textCss: 'word-spacing: 20px;font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;color:#3D3D3D;font-size:24px; mix-blend-mode: overlay;',
textCss2: 'word-spacing: 20px;font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;text-align: left;color:#3D3D3D;font-size:22px;mix-blend-mode: overlay;',
textCss3: 'word-spacing: 20px;letter-spacing: 10px;margin-right:2px;font-family: "SimHei", "Microsoft YaHei", sans-serif;text-align: left;color:#3D3D3D;font-size:22px;mix-blend-mode: overlay;',
textCss4: 'word-spacing: 20px;margin-right:3px;letter-spacing: 10px;font-family: "card", "Microsoft YaHei", sans-serif;text-align: left;color:#1a1a1a;font-size:28px;mix-blend-mode: overlay;display:flex;',
textCssLeft: 'text-align: left;',
textCssCenter: 'text-align: center;'
})
//
const showEditPopup = ref(false);
const editForm = ref({});
onLoad((option) => {
uni.showLoading({
title: "生成中"
})
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'idcard',
prefix: '.uni.other.',
value: "身份证"
})
let formdata=uni.getStorageSync("cardForm")
if(formdata){
data.form=formdata
}
uni.$on("editFormPhoto",(info)=>{
data.code = ""
editForm.value.photo = info;
data.form.photo = info;
})
})
onUnload(() => {
uni.$off('editFormPhoto')
})
onReady(() => {
})
onShow(() => {})
onPullDownRefresh(() => {
setTimeout(() => {
uni.stopPullDownRefresh();
}, 1000);
})
onReachBottom(() => {
})
function successImage(e) {
data.code = e
uni.hideLoading()
}
function progress(e) {
// console.log(e)
}
//
function edit() {
console.log(data.form)
//
editForm.value = JSON.parse(JSON.stringify(data.form));
//
showEditPopup.value = true;
}
//
function closeEditPopup() {
showEditPopup.value = false;
}
//
function saveEditForm() {
uni.showLoading({
title: "生成中"
})
data.shuaxing=false
//
data.form = editForm.value
data.form.photo=editForm.value.photo
setTimeout(()=>{
data.shuaxing=true
},100)
uni.setStorageSync("cardForm",data.form)
//
showEditPopup.value = false;
}
//
function chooseImage() {
uni.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
// crop:{
// width:'196px',
// height:"230px"
// },
success: (res) => {
uni.navigateTo({
url:'/pages/other/qf-image/qf-image?src='+res.tempFilePaths[0]
})
},
fail: (err) => {
console.log('选择图片失败', err);
}
});
}
/**
* 将本地图片路径通过 Canvas 转换为 File 对象
* @param {string} localPath - 本地图片路径如从 uni.chooseImage 获取的 tempFilePath
* @param {Object} options - 可选参数
* @param {string} options.format - 输出格式 'image/png' 'image/jpeg'默认 'image/png'
* @param {number} options.quality - 图片质量 jpeg 有效0~1默认 0.92
* @param {number} options.maxWidth - 最大宽度等比缩放不填则使用原图尺寸
* @param {number} options.maxHeight - 最大高度等比缩放不填则使用原图尺寸
* @returns {Promise<File>} 返回一个 Promiseresolve File 对象
*/
function convertLocalImageToFile(localPath) {
return new Promise((resolve, reject) => {
// 1. Base64 Image
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.file((file) => {
const reader = new plus.io.FileReader();
reader.onload = (e) => {
const base64Data = e.target.result; // data:image/jpeg;base64,/9j/...
resolve(e.target.result)
};
reader.onerror = (err) => reject(err);
reader.readAsDataURL(file); // DataURL
}, reject);
}, reject);
});
}
//
function previewImage() {
if (data.code) {
uni.previewImage({
urls: [data.code],
current: 0
});
}
}
//
function saveImage() {
if (data.code) {
console.log(data.code)
uni.showLoading({
title: '保存中...'
});
try {
// base64
if (data.code.startsWith('data:image')) {
// base64
console.log('开始处理base64图片');
uni.base64ToTempFile({
base64: data.code.split(',')[1], // base64
success: (res) => {
console.log('base64转换成功', res);
if (res.tempFilePath) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
console.log('保存图片成功');
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.log('保存图片失败', err);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
} else {
console.log('base64转换失败无临时文件路径');
uni.hideLoading();
uni.showToast({
title: '转换失败',
icon: 'none'
});
}
},
fail: (err) => {
console.log('base64转换失败', err);
uni.hideLoading();
uni.showToast({
title: '转换失败',
icon: 'none'
});
}
});
} else if (data.code.startsWith('_') || data.code.includes('temp') || data.code.includes('cache') || data
.code.includes('_doc') || data.code.includes('_tmp')) {
//
console.log('开始处理本地临时文件');
uni.saveImageToPhotosAlbum({
filePath: data.code,
success: () => {
console.log('保存本地文件成功');
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.log('保存本地文件失败', err);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
} else {
//
console.log('开始处理网络图片');
uni.downloadFile({
url: data.code,
success: (res) => {
console.log('下载图片成功', res);
if (res.statusCode === 200 && res.tempFilePath) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
console.log('保存图片成功');
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.log('保存图片失败', err);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
} else {
console.log('下载图片失败,状态码:', res.statusCode);
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (err) => {
console.log('下载图片失败', err);
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
});
}
} catch (error) {
console.log('保存图片异常', error);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
} else {
uni.showToast({
title: '暂无图片可保存',
icon: 'none'
});
}
}
</script>
<style lang="scss" >
page {
background-color: #ededed;
}
@font-face {
font-family: "card";
src: url("/static/font/card.ttf");
}
* {
box-sizing: content-box;
}
.aadadad {
text-align: center;
}
.heiti {
font-family: "SimHei", "Microsoft YaHei", sans-serif;
}
/* 弹窗样式 */
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.popup-content {
background-color: #fff;
border-radius: 10px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.popup-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
.popup-close {
font-size: 48rpx;
color: #999;
cursor: pointer;
}
.popup-body {
padding: 20rpx;
}
.form-section {
margin-bottom: 30rpx;
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
}
.section-title {
font-size: 28rpx;
font-weight: bold;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.form-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.form-label {
width: 200rpx;
font-size: 26rpx;
color: #333;
}
.form-input {
flex: 1;
padding: 15rpx;
border: 1rpx solid #ddd;
border-radius: 4rpx;
font-size: 26rpx;
}
.form-separator {
margin: 0 10rpx;
font-size: 26rpx;
color: #333;
}
.form-total {
margin-top: 10rpx;
padding-top: 10rpx;
border-top: 1rpx dashed #ddd;
}
.form-total-input {
background-color: #f9f9f9;
color: #ff6b35;
font-weight: bold;
}
/* 上传图片样式 */
.upload-container {
flex: 1;
position: relative;
}
.upload-btn {
width: 120rpx;
height: 160rpx;
border-radius: 8rpx;
border: 1rpx dashed #ddd;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
}
.upload-text {
font-size: 22rpx;
color: #999;
text-align: center;
}
.upload-image {
width: 98rpx;
height: 115rpx;
border-radius: 1rpx;
object-fit: cover;
border: 1px dashed #ddd;
}
.popup-footer {
display: flex;
justify-content: space-between;
padding: 20rpx;
border-top: 1rpx solid #eee;
}
.btn-cancel,
.btn-save {
width: 48%;
padding: 20rpx;
border-radius: 4rpx;
font-size: 28rpx;
}
.btn-cancel {
background-color: #f5f5f5;
color: #333;
border: 1rpx solid #ddd;
}
.btn-save {
background-color: #187AFF;
color: #fff;
border: none;
}
/* 保存图片按钮 */
.button-container {
display: flex;
justify-content: center;
padding: 30rpx 0;
}
.btn-save-image {
background: linear-gradient(90deg, #187AFF 0%, #3295FC 100%);
color: #fff;
border: none;
padding: 18rpx 60rpx;
border-radius: 60rpx;
font-size: 26rpx;
font-weight: bold;
box-shadow: 0 3rpx 10rpx rgba(7, 66, 193, 0.3);
transition: all 0.3s ease;
text-align: center;
min-width: 160rpx;
}
.btn-save-image:hover {
transform: translateY(-2rpx);
box-shadow: 0 4rpx 12rpx rgba(7, 72, 193, 0.4);
}
.btn-save-image:active {
transform: translateY(0);
box-shadow: 0 2rpx 6rpx rgba(7, 140, 193, 0.3);
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,163 @@
<template>
<view class="container">
<ZdyNavbar :title="data.navbar.title" :bgColor="data.navbar.bgColor" :isAdd="true" :isSearch="$system=='Android'">
</ZdyNavbar>
<view class="card-grid">
<view class="card" v-for="(item, index) in cardList" :key="index">
<view class="title">
{{item.title}}
</view>
<image :src="item.img" class="card-img" />
<button class="card-btn" @click="create(index)">立即制作</button>
</view>
</view>
</view>
</template>
<script setup>
import ZdyNavbar from "@/components/nav-bar/nav-bar.vue"
import {
ref,
reactive,
getCurrentInstance
} from 'vue'
import {
onLoad,
onShow,
onUnload,
onReady,
onPullDownRefresh,
onReachBottom
} from "@dcloudio/uni-app";
const {
appContext,
proxy
} = getCurrentInstance();
const data = reactive({
navbar: {
title: '证件选择',
bgColor: ''
}
})
const cardList = ref([
{
title: '身份证',
img: '/static/image/other/certificate/certificate1.png',
},
{
title: '毕业证',
img: '/static/image/other/certificate/certificate2.png',
},
{
title: '情侣证',
img: '/static/image/other/certificate/certificate3.png',
},
{
title: '分手证',
img: '/static/image/other/certificate/certificate4.png',
},
{
title: '奖状',
img: '/static/image/other/certificate/certificate5.png',
},
{
title: '颜值证',
img: '/static/image/other/certificate/certificate6.png',
},
])
onLoad(()=>{
proxy.$apiUserEvent('all', {
type: 'event',
key: 'certificate',
prefix: '.uni.other.',
value:'证书首页'
})
const config = uni.getStorageSync('config').config
const themeConfig = config?.['client.uniapp.idcard']
if (!themeConfig) {
cardList.value.shift()
}
})
function create(index){
if(index){
uni.navigateTo({
url:'/pages/other/certificate/graduate?styleType='+(index-1)
})
}else{
uni.navigateTo({
url:'/pages/other/card/card'
})
}
}
</script>
<style scoped>
.container {
padding: 20rpx;
background-color: #F0F4F9;
min-height: 100vh;
}
.card-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.card {
width: 48%;
background-color: #fff;
border-radius: 20rpx;
padding: 20rpx;
margin-bottom: 30rpx;
align-items: center;
/* box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.05); */
display: flex;
align-items: center;
flex-direction: column;
}
.title{
font-size: 14px;
color: #1A1A1A;
line-height: 14px;
font-weight: bold;
padding-top: 10rpx;
}
.card-img {
width: 130px;
height: 80px;
/* border-radius: 8px 8px 8px 8px; */
margin-top: 21px;
margin-bottom: 18px;
}
.card-btn {
width: 68px;
height: 28px;
background: linear-gradient(327deg, #1777FF 0%, #17CDFF 100%);
border-radius: 8px 8px 8px 8px;
font-size: 12px;
color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
}
button::after{
border: none;
}
button{
padding: 0;
margin: 0;
border: none;
}
</style>

View File

@ -0,0 +1,828 @@
<template>
<view class="honor-of-kings">
<nav-bar title="王者主页" bgColor="#F5F5F5" isRightButton :rightButtonText="rightButtonText"
@right-click="onRightClick">
</nav-bar>
<view style="padding: 8px;box-sizing: border-box;">
<view class="painter-container" @click="handlePreview"
:style="`width:calc(100vw - 16px) ; height: ${posterContainerHeight}px; overflow: hidden; position: relative; transform: translateZ(0);`">
<!-- 运用 js 算出的无误差纯数字 scale 来实现高清画板的等比缩小彻底兼容所有老旧内核不再被截断 -->
<view
:style="`width: 750px; transform: scale(${posterScaleRatio}); transform-origin: left top; position: absolute; left: 0; top: 0;`">
<l-painter isCanvasToTempFilePath @success="onPainterSuccess" :css="`width:750px;`">
<l-painter-view :css="`width: 750.0px; position: relative;`">
<!-- 直接使用主背景图撑开父容器移除不必要的 opacity: 0 占位图避免在大屏渲染时产生黑块 -->
<l-painter-image :src="`/static/image/other/game/wangzhe/style-${honorData.type}.jpg`"
css="width: 750.0px; display: block;"></l-painter-image>
<!-- 头像 (纯百分比绝对定位完美适配所有大屏分辨率) -->
<l-painter-image :src="honorData.avatar"
css="position: absolute; left: 117.0px; top: 76.8px; width: 66px; height: 66px; border-radius: 200px; object-fit: cover; display: block;"></l-painter-image>
<!-- 头像框 (压在头像上方) -->
<l-painter-image
:src="`/static/image/other/game/wangzhe/avatar-frame-${honorData.type}.png`"
css="position: absolute; left: 0; top: 0; width: 183px; height: 141px"></l-painter-image>
<!-- 性别图标 -->
<l-painter-image :src="`/static/image/other/game/wangzhe/${honorData.gender}.png`"
:css="`position: absolute; left: ${honorData.type == 3 ? '217.5px' : '202.5px'}; top: 87.0px; width: 11.8px; height: 11.7px;`"></l-painter-image>
<!-- 昵称渐变图片 -->
<l-painter-image v-if="nicknameImage" :src="nicknameImage"
:css="`position: absolute; left: ${honorData.type == 3 ? '230.3px' : '215.3px'}; top: ${$system == 'iOS' ? '83px' : '85.6px'}; width: ${honorData.nickname.length * 14.9}px;`"></l-painter-image>
<!-- 收到花束 -->
<l-painter-text :text="honorData.receivedFlowers"
css="position: absolute; left: 358.5px; top: 131.4px; font-size: 9px; color: #DAF2FF;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
<!-- 热度数 -->
<l-painter-text :text="honorData.popularityCount"
css="position: absolute; left: 386.3px; top: 131.4px; font-size: 9px; color: #DAF2FF;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
<!-- 点赞数 -->
<l-painter-text :text="honorData.likeCount"
css="position: absolute; left: 414.0px; top: 131.4px; font-size: 9px; color: #DAF2FF;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
<!-- 荣誉一 -->
<l-painter-text :text="honorData.honor1"
css="position: absolute; left: 247.5px; top: 145.6px; font-size: 9px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 荣誉二 -->
<l-painter-text :text="honorData.honor2"
css="position: absolute; left: 465.6px; top: 173.6px; font-size: 8px; color: #71B1D3;transform: scale(0.85),translateX(-50%);"></l-painter-text>
<!-- 巅峰万强 -->
<l-painter-text v-if="honorData.type == 4" :text="honorData.peakStrong"
css="position: absolute; left: 257.3px; top: 192.8px; font-size: 8px; color: #E9F5FB;transform: translateX(-50%);"></l-painter-text>
<!-- 对战场次 -->
<l-painter-text :text="honorData.matchCount"
css="position: absolute; left: 138.0px; top: 242.3px; font-size: 12px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 对战被赞 -->
<l-painter-text :text="honorData.matchLikeCount"
css="position: absolute; left: 199.5px; top: 242.3px; font-size: 12px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 图鉴等级 -->
<l-painter-text :text="honorData.pokedexLevel"
css="position: absolute; left: 319.5px; top: 238.8px; font-size:9px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 皮肤数 -->
<l-painter-text :text="honorData.skinCount"
css="position: absolute; left: 377.3px; top: 238.8px; font-size:9px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 游戏天数 -->
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2" :text="honorData.gameDays"
css="position: absolute; left: 202.1px; top: 282.2px; font-size:10px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 峡谷对战局数 -->
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2"
:text="honorData.riftMatchCount"
css="position: absolute; left: 322.2px; top: 282.2px; font-size:10px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 大乱斗对战局数 -->
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2"
:text="honorData.aramMatchCount"
css="position: absolute; left: 379.9px; top: 282.2px; font-size:10px; color: #71B1D3;transform: translateX(-50%);"></l-painter-text>
<!-- 亲密度 -->
<l-painter-text v-if="honorData.type == 3 || honorData.type == 4" :text="honorData.intimacy"
css="position: absolute; left: 348.0px; top: 296.2px; font-size:10px; color: #71B1D3;transform: translateX(-50%);font-family: WangZheFont2;"></l-painter-text>
<!-- 水印 -->
<l-painter-image v-if="$isVip()" src="/static/image/other/shuiying.png"
:css="`position: absolute; left: 18.8px; bottom: 17.1px; width: 145.38px;height:42.76px`"></l-painter-image>
</l-painter-view>
</l-painter>
</view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_wangzhe')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="save-action">
<button class="save-btn" @click="handleSave">保存</button>
</view>
<!-- 主题选择区域 -->
<view class="theme-selector">
<scroll-view scroll-x="true" class="theme-scroll" :show-scrollbar="false">
<view class="theme-list">
<view class="theme-item" v-for="(item, index) in ['样式一', '样式二', '样式三', '样式四']" :key="index + 1"
:class="{ active: honorData.type == index + 1 }" @click="handleChangeTheme(index + 1)">
<text class="theme-text">{{ item }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 数据编辑弹窗 -->
<uni-popup ref="editPopup" type="center">
<view class="edit-popup-content">
<view class="popup-header">
<text class="title">编辑主页数据</text>
</view>
<scroll-view scroll-y class="popup-scroll">
<!-- <view class="form-item">
<text class="label">样式类型</text>
<input class="input" type="number" v-model="tempData.type" placeholder="1-4" />
</view> -->
<view class="form-item avatar-form-item">
<text class="label">头像</text>
<view class="avatar-uploader">
<view class="avatar-preview" v-if="tempData.avatar">
<image class="preview-img" :src="tempData.avatar" mode="aspectFill"></image>
<view class="delete-icon" @click="handleDeleteAvatar">
<text>×</text>
</view>
</view>
<view class="upload-btn" v-else @click="handleChooseAvatar">
<text class="plus">+</text>
</view>
</view>
</view>
<view class="form-item">
<text class="label">昵称</text>
<input class="input" type="text" v-model="tempData.nickname" />
</view>
<view class="form-item">
<text class="label">性别</text>
<view class="radio-group">
<view class="radio-item" @click="tempData.gender = 'man'">
<view class="radio-icon" :class="{ active: tempData.gender === 'man' }"></view>
<text class="radio-text" :class="{ active: tempData.gender === 'man' }"></text>
</view>
<view class="radio-item" @click="tempData.gender = 'female'">
<view class="radio-icon" :class="{ active: tempData.gender === 'female' }"></view>
<text class="radio-text" :class="{ active: tempData.gender === 'female' }"></text>
</view>
</view>
</view>
<view class="form-item">
<text class="label">荣誉一</text>
<input class="input" type="text" v-model="tempData.honor1" />
</view>
<view class="form-item">
<text class="label">荣誉二</text>
<input class="input" type="text" v-model="tempData.honor2" />
</view>
<view class="form-item">
<text class="label">巅峰万强</text>
<input class="input" type="text" v-model="tempData.peakStrong" />
</view>
<view class="form-item">
<text class="label">收到花束</text>
<input class="input" type="text" v-model="tempData.receivedFlowers" />
</view>
<view class="form-item">
<text class="label">热度</text>
<input class="input" type="text" v-model="tempData.popularityCount" />
</view>
<view class="form-item">
<text class="label">点赞数</text>
<input class="input" type="text" v-model="tempData.likeCount" />
</view>
<view class="form-item">
<text class="label">对战场次</text>
<input class="input" type="number" v-model="tempData.matchCount" />
</view>
<view class="form-item">
<text class="label">对战被赞</text>
<input class="input" type="number" v-model="tempData.matchLikeCount" />
</view>
<view class="form-item">
<text class="label">图鉴等级</text>
<input class="input" type="number" v-model="tempData.pokedexLevel" />
</view>
<view class="form-item">
<text class="label">皮肤数</text>
<input class="input" type="number" v-model="tempData.skinCount" />
</view>
<view class="form-item">
<text class="label">游戏天数</text>
<input class="input" type="number" v-model="tempData.gameDays" />
</view>
<view class="form-item">
<text class="label">峡谷对战局数</text>
<input class="input" type="number" v-model="tempData.riftMatchCount" />
</view>
<view class="form-item">
<text class="label">大乱斗局数</text>
<input class="input" type="number" v-model="tempData.aramMatchCount" />
</view>
<view class="form-item">
<text class="label">亲密度</text>
<input class="input" type="number" v-model="tempData.intimacy" />
</view>
</scroll-view>
<view class="popup-footer">
<button class="cancel-btn" @click="closeEditPopup">取消</button>
<button class="confirm-btn" @click="confirmEdit">确定</button>
</view>
</view>
</uni-popup>
<!-- 隐藏的画布用于原生绘制带有纯正渐变色的文本再转成图片给到海报插件 -->
<view style="position: absolute; left: -9999px; top: -9999px; width: 0; height: 0; overflow: hidden;">
<canvas canvas-id="gradientTextCanvas" id="gradientTextCanvas" style="width: 300px; height: 60px;"></canvas>
</view>
<!-- 横向全屏放大预览层 -->
<view class="preview-overlay" v-if="showPreview" @click="showPreview = false">
<image class="preview-image" :src="finalPosterPath" mode="aspectFill"></image>
</view>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, getCurrentInstance } from 'vue'
import { onLoad, onReady } from '@dcloudio/uni-app'
const rightButtonText = ref("编辑");
const instance = getCurrentInstance();
const { proxy } = getCurrentInstance();
const nicknameImage = ref('');
const showPreview = ref(false); //
const isMountedReady = ref(false);
const isFontLoaded = ref(false);
// css calc()
const sysInfo = uni.getSystemInfoSync();
const windowWidth = sysInfo.windowWidth || 375;
const posterScaleRatio = windowWidth / 750;
const posterContainerHeight = 342 * posterScaleRatio;
//
const honorData = reactive({
type: 1, // 1.2.3.4
avatar: '/static/image/shopping/pdd/avatars/avatars1.jpg', //
nickname: '甜喵小贝', //
gender: 'female', //
region: '重庆市两江新区', //
honor1: '国服第一小乔', // 1
honor2: '888级大师收藏家·珍耀无双', // 2
matchCount: 58965, //
matchLikeCount: 9746, //
gameDays: 3127, //
pokedexLevel: 104, //
skinCount: 444, //
riftMatchCount: 7368, //
aramMatchCount: 5193, //
receivedFlowers: 520, //
popularityCount: '6.0W', //
likeCount: 4883, //
intimacy: '25147',//
peakStrong: '5632'
})
onLoad(() => {
const cachedData = uni.getStorageSync('wangzheHonorData');
if (cachedData) {
Object.assign(honorData, cachedData);
}
//
proxy.$apiUserEvent('all', {
type: 'click',
key: 'wangzhe',
prefix: '.uni.other.',
value: "王者主页"
})
const config = uni.getStorageSync('config')
console.log("---config---", config);
let fontConfig = config.config['client.uniapp.font'];
try {
if (typeof fontConfig === 'string') {
fontConfig = JSON.parse(fontConfig);
}
} catch (e) {
console.error('字体配置解析失败', e);
}
console.log("字体地址信息", fontConfig?.wangzhe);
// Font loading logic
const fontUrl = fontConfig?.wangzhe;
const fontUrl2 = fontConfig?.wangzhe2;
let loadedCount = 0;
const totalFonts = 2;
const checkAllLoaded = () => {
loadedCount++;
if (loadedCount >= totalFonts) {
isFontLoaded.value = true;
if (isMountedReady.value) drawGradientText();
}
};
const loadSingleFont = (url, name, storageKey, onComplete) => {
if (!url) {
console.warn(`未获取到 ${name} 字体地址,回退使用系统默认字体`);
onComplete && onComplete();
return;
}
const doLoad = (path) => {
uni.loadFontFace({
family: name,
source: `url("${path}")`,
success() {
console.log(`${name} 字体加载成功`);
onComplete && onComplete();
},
fail(err) {
console.error(`${name} 字体加载失败`, err);
onComplete && onComplete();
}
});
};
// #ifdef H5
// H5 URL
doLoad(url);
// #endif
// #ifndef H5
// H5 使
const savedPath = uni.getStorageSync(storageKey);
if (savedPath) {
doLoad(savedPath);
} else {
uni.downloadFile({
url: url,
success: (res) => {
if (res.statusCode === 200) {
uni.saveFile({
tempFilePath: res.tempFilePath,
success: (saveRes) => {
const saved = saveRes.savedFilePath;
uni.setStorageSync(storageKey, saved);
console.log(`${name} 字体保存路径`, saved);
doLoad(saved);
},
fail: (err) => {
console.error(`保存 ${name} 文件失败`, err);
// Fallback:
doLoad(res.tempFilePath);
}
});
} else {
onComplete && onComplete();
}
},
fail: (err) => {
console.error(`下载 ${name} 字体失败`, err);
onComplete && onComplete();
}
});
}
// #endif
};
loadSingleFont(fontUrl, 'WangZheFont', 'wangzhe_font_path', checkAllLoaded);
loadSingleFont(fontUrl2, 'WangZheFont2', 'wangzhe_font2_path', checkAllLoaded);
})
const finalPosterPath = ref('');
const editPopup = ref(null);
const tempData = reactive({});
const newAvatars = ref([]);
function handleChangeTheme(typeIndex) {
//
finalPosterPath.value = '';
honorData.type = typeIndex;
uni.setStorageSync('wangzheHonorData', honorData);
}
function onRightClick() {
Object.assign(tempData, honorData);
newAvatars.value = [];
editPopup.value.open();
}
const handleChooseAvatar = () => {
uni.chooseImage({
count: 1,
crop: {
quality: 100,
width: 400,
height: 400
},
success: (res) => {
const tempPath = res.tempFilePaths[0];
// #ifndef H5
uni.saveFile({
tempFilePath: tempPath,
success: (saveRes) => {
tempData.avatar = saveRes.savedFilePath;
newAvatars.value.push(saveRes.savedFilePath);
}
});
// #endif
// #ifdef H5
tempData.avatar = tempPath;
// #endif
}
});
};
const handleDeleteAvatar = () => {
tempData.avatar = '';
};
function closeEditPopup() {
// #ifndef H5
newAvatars.value.forEach(path => {
uni.removeSavedFile({ filePath: path });
});
// #endif
editPopup.value.close();
}
function confirmEdit() {
const isNicknameChanged = honorData.nickname !== tempData.nickname;
// #ifndef H5
//
newAvatars.value.forEach(path => {
if (path !== tempData.avatar) {
uni.removeSavedFile({ filePath: path });
}
});
//
if (honorData.avatar !== tempData.avatar && honorData.avatar && !honorData.avatar.startsWith('/static/')) {
uni.removeSavedFile({ filePath: honorData.avatar });
}
// #endif
Object.assign(honorData, tempData);
uni.setStorageSync('wangzheHonorData', honorData);
editPopup.value.close();
//
finalPosterPath.value = '';
//
if (isNicknameChanged && isFontLoaded.value) {
drawGradientText();
}
}
//
const handlePreview = () => {
if (!finalPosterPath.value) {
uni.showToast({ title: '海报仍在生成中,请稍候', icon: 'none' });
return;
}
showPreview.value = true;
}
//
const onPainterSuccess = (path) => {
finalPosterPath.value = path;
}
//
const handleSave = () => {
if (!finalPosterPath.value) {
uni.showToast({ title: '图片仍在生成中,请稍候', icon: 'none' })
return
}
uni.saveImageToPhotosAlbum({
filePath: finalPosterPath.value,
success: () => {
uni.showToast({ title: '保存成功', icon: 'success' })
},
fail: () => {
uni.showToast({ title: '保存失败', icon: 'none' })
}
})
}
onMounted(() => {
isMountedReady.value = true;
if (isFontLoaded.value) {
drawGradientText();
}
})
// Canvas
const drawGradientText = () => {
//
const fontSize = 40;
const textLen = honorData.nickname.length;
//
const canvasWidth = fontSize * textLen;
const ctx = uni.createCanvasContext('gradientTextCanvas', instance.proxy);
ctx.clearRect(0, 0, 300, 60);
// 使 WangZheFont
ctx.font = `${fontSize}px "WangZheFont", sans-serif`;
// 线
const grd = ctx.createLinearGradient(0, 0, canvasWidth, 0);
grd.addColorStop(0, '#F9D577');
grd.addColorStop(1, '#FFF5C4');
ctx.setFillStyle(grd);
ctx.setTextBaseline('top');
//
ctx.fillText(honorData.nickname, 0, 0);
ctx.draw(false, () => {
//
setTimeout(() => {
uni.canvasToTempFilePath({
canvasId: 'gradientTextCanvas',
x: 0,
y: 0,
width: canvasWidth,
height: fontSize + 10,
destWidth: canvasWidth * 2, // 2
destHeight: (fontSize + 10) * 2,
success: (res) => {
nicknameImage.value = res.tempFilePath;
}
}, instance.proxy)
}, 200)
})
}
</script>
<style lang="less" scoped>
.preview-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #000;
z-index: 99999;
/* 抛弃 flex 布局,防止 width: 100vh 被父级限制而发生坍缩 */
}
.preview-overlay .preview-image {
position: absolute;
top: 50%;
left: 50%;
width: 100vh;
height: 100vw;
/* 先拉回自身中心点,再围绕中心点旋转 90 度 */
transform: translate(-50%, -50%) rotate(90deg);
}
.honor-of-kings {
width: 100%;
overflow-x: hidden;
}
.painter-container {
/* 强制画板缩放到设备屏幕宽度,避免物理 px 超出屏幕 */
padding: 8px;
zoom: calc(100vw / 798);
width: 100%;
max-width: 1000px;
/* 限制PC/iPad端的最大宽度 */
margin: 0 auto;
}
.save-action {
margin-top: 60rpx;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 220rpx;
/* 留出底部主题固定栏的安全空间 */
.save-btn {
margin-top: 60rpx;
width: 316rpx;
background: #1777FF;
color: #fff;
border-radius: 56rpx;
}
}
.theme-selector {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
padding-top: 20rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
z-index: 99;
.theme-scroll {
width: 100%;
white-space: nowrap;
::-webkit-scrollbar {
display: none;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
}
.theme-list {
display: inline-block;
padding: 0 20rpx;
.theme-item {
display: inline-flex;
vertical-align: top;
margin: 0 10rpx;
padding: 0 40rpx;
height: 84rpx;
border-radius: 12rpx;
background-color: #FFFFFF;
justify-content: center;
align-items: center;
border: 2rpx solid transparent;
box-sizing: border-box;
transition: all 0.3s;
&.active {
background-color: #fff;
border-color: #3B7BFF;
.theme-text {
color: #3B7BFF;
font-weight: bold;
}
}
.theme-text {
font-size: 28rpx;
color: #666;
}
}
}
}
.edit-popup-content {
background-color: #fff;
border-radius: 20rpx;
width: 85vw;
.popup-header {
display: flex;
justify-content: center;
align-items: center;
padding: 40rpx 0 20rpx 0;
.title {
font-size: 34rpx;
font-weight: bold;
color: #333;
}
}
.popup-scroll {
height: 55vh;
padding: 20rpx 40rpx;
box-sizing: border-box;
}
.form-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
padding-bottom: 16rpx;
.label {
width: 200rpx;
font-size: 28rpx;
color: #333;
}
.avatar-uploader {
display: flex;
align-items: center;
.upload-btn {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #999DA7;
display: flex;
justify-content: center;
align-items: center;
.plus {
font-size: 60rpx;
color: #fff;
font-weight: 300;
margin-top: -6rpx;
}
}
.avatar-preview {
position: relative;
width: 100rpx;
height: 100rpx;
.preview-img {
width: 100%;
height: 100%;
border-radius: 50%;
}
.delete-icon {
position: absolute;
top: -4rpx;
right: -4rpx;
width: 32rpx;
height: 32rpx;
background-color: red;
color: #fff;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
line-height: 28rpx;
}
}
}
.input {
flex: 1;
font-size: 28rpx;
background-color: #F7F7F7;
border-radius: 12rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
color: #333;
}
.radio-group {
display: flex;
align-items: center;
gap: 40rpx;
.radio-item {
display: flex;
align-items: center;
cursor: pointer;
.radio-icon {
width: 28rpx;
height: 28rpx;
border-radius: 50%;
border: 2rpx solid #d9d9d9;
margin-right: 12rpx;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
&.active {
border-color: #3B7BFF;
&::after {
content: '';
width: 14rpx;
height: 14rpx;
border-radius: 50%;
background-color: #3B7BFF;
}
}
}
.radio-text {
font-size: 28rpx;
color: #666;
&.active {
color: #3B7BFF;
}
}
}
}
}
.popup-footer {
display: flex;
justify-content: space-between;
padding: 20rpx 40rpx 40rpx;
button {
width: 46%;
height: 76rpx;
line-height: 76rpx;
font-size: 30rpx;
border-radius: 12rpx;
margin: 0;
&::after {
border: none;
}
}
.cancel-btn {
background-color: #F4F4F4;
color: #666;
}
.confirm-btn {
background-color: #3B7BFF;
color: #fff;
}
}
}
</style>

View File

@ -0,0 +1,110 @@
<template>
<view>
<qf-image-cropper :src="data.src" :width="data.width" :height="data.height" :radius="0" @crop="handleCrop"
:reverseRotatable="true"></qf-image-cropper>
</view>
</template>
<script setup>
import {
ref,
reactive,
watch,
nextTick,
getCurrentInstance
} from "vue";
import {
onLoad,
onShow,
onReady,
onPullDownRefresh,
onReachBottom
} from "@dcloudio/uni-app";
const {
appContext,
proxy
} = getCurrentInstance();
const data = reactive({
src: "",
width:196,
height:230,
isCard:true
})
onLoad((option) => {
console.log(option)
data.src = option.src
if(option.width){
data.width=option.width*2
data.isCard=false
}
if(option.height){
data.height=option.height*2
data.isCard=false
}
})
function handleCrop(e) {
if(data.isCard){
uni.showLoading({
title:"抠图中"
})
convertLocalImageToFile(e.tempFilePath).then(file => {
proxy.$imageUpload(file.split(',', 2)[1]).then(resimage => {
uni.hideLoading()
// editForm.value.photo = decodeURI(resimage.data);
uni.$emit("editFormPhoto", decodeURI(resimage.data))
uni.navigateBack()
}).catch(err => {
uni.hideLoading()
console.log(err.data.message)
uni.showToast({
icon: "none",
title: "图片不是人像或者过大"
})
})
}).catch(err => {
uni.hideLoading()
})
}else{
uni.saveFile({
tempFilePath: e.tempFilePath,
success: function(res) {
console.log(res)
uni.$emit("editFormPhoto", decodeURI(res.savedFilePath))
uni.navigateBack()
// res.avatar = res.savedFilePath
}
});
}
}
/**
* 将本地图片路径通过 Canvas 转换为 File 对象
* @param {string} localPath - 本地图片路径如从 uni.chooseImage 获取的 tempFilePath
* @param {Object} options - 可选参数
* @param {string} options.format - 输出格式 'image/png' 'image/jpeg'默认 'image/png'
* @param {number} options.quality - 图片质量 jpeg 有效0~1默认 0.92
* @param {number} options.maxWidth - 最大宽度等比缩放不填则使用原图尺寸
* @param {number} options.maxHeight - 最大高度等比缩放不填则使用原图尺寸
* @returns {Promise<File>} 返回一个 Promiseresolve File 对象
*/
function convertLocalImageToFile(localPath) {
return new Promise((resolve, reject) => {
// 1. Base64 Image
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.file((file) => {
const reader = new plus.io.FileReader();
reader.onload = (e) => {
const base64Data = e.target.result; // data:image/jpeg;base64,/9j/...
resolve(e.target.result)
};
reader.onerror = (err) => reject(err);
reader.readAsDataURL(file); // DataURL
}, reject);
}, reject);
});
}
</script>

View File

@ -0,0 +1,790 @@
<template>
<view class="ranking-page">
<nav-bar title="从夯到拉" bgColor="#F5F5F5" isRightButton :rightButtonText="rightButtonText"
@right-click="onRightClick">
</nav-bar>
<view class="content-container">
<view :class="{ 'skin-box': type == 'skin', 'table-box': type != 'skin' }"
:style="type == 'skin' ? { backgroundColor: skinBgColor } : {}">
<template v-if="type == 'skin'">
<view v-if="!isEdit" class="title">{{ title }}</view>
<input v-else class="title title-input" v-model="title" maxlength="12" placeholder="请输入排名标题" />
<image class="img happy" src="/static/image/other/ranking/happy.png"></image>
<image class="img sad" src="/static/image/other/ranking/sad.png"></image>
</template>
<image v-if="$isVip()" class="watermark" :class="{}" src="/static/image/other/card/shuiyin2.png"
mode="heightFix">
</image>
<view class="ranking-table">
<view v-for="(item, index) in tierList" :key="index" class="ranking-row">
<view class="label-box" :style="{ backgroundColor: item.bgColor }"
@click="changeRowBgColor(index)">
<!-- <text class="label-text" :class="{ 'with-stroke': item.hasStroke }"
:style="{ color: item.textColor }">
{{ item.label }}
</text> -->
<image :style="`height:60rpx;width:160rpx;`"
:src="`/static/image/other/ranking/${type == 'skin' ? item.img + '_skin' : item.img}.png`">
</image>
</view>
<view class="items-box ranking-item-list" :id="'row-' + index">
<view v-for="(img, imgIdx) in item.images" :key="imgIdx" class="item-wrapper"
:class="{ 'is-dragging': drag.tierIndex === index && drag.imgIdx === imgIdx }"
:style="getDragStyle(index, imgIdx)" @touchstart="onTouchStart($event, index, imgIdx)"
@touchmove.stop.prevent="onTouchMove" @touchend="onTouchEnd">
<image :src="img" mode="aspectFill" class="item-img"></image>
<view v-if="isEdit" class="del-btn" @click.stop="deleteImage(index, imgIdx)">×</view>
</view>
<view v-if="isEdit && item.images.length < 4" class="add-btn" @click="chooseImage(index)">
<image class="add-icon" src="/static/image/common/add.png"></image>
</view>
</view>
</view>
</view>
</view>
<view class="save-action">
<button v-if="!isEdit" class="save-btn" @click="handleSave">保存图片</button>
<!-- 编辑模式下的全局颜色控制器 -->
<view v-else-if="type == 'skin'" class="bottom-edit-actions">
<view class="color-picker-wrapper">
<view class="color-picker">
<view v-for="color in ['#FFDFDF', '#DFF0FF', '#E0FFDF', '#FDF5C8', '#F3DFFF']" :key="color"
class="color-dot" :style="{ backgroundColor: color }" @click="skinBgColor = color">
</view>
</view>
<input v-if="false" class="hex-input" v-model="skinBgColor" placeholder="#HEX" maxlength="7" />
</view>
<view class="spectrum-picker" @touchstart="handleHueTouch" @touchmove.stop.prevent="handleHueTouch">
<view class="spectrum-bar"></view>
<view class="slider-thumb" :style="{ left: (skinHue / 360 * 100) + '%' }"></view>
</view>
<text class="edit-tip">点击左侧分类标签可切换背景色</text>
</view>
</view>
</view>
<view class="bottom-tabs">
<view class="tab-item" :class="{ active: type == 'base' }" @click="switchType('base')">基础</view>
<view class="tab-item" :class="{ active: type == 'skin' }" @click="switchType('skin')">皮肤</view>
</view>
<view class="painter-container" v-if="isSnapshot">
<l-painter isCanvasToTempFilePath @success="onPainterSuccess"
:css="`width:750rpx; padding: 40rpx; background-color:${type == 'skin' ? skinBgColor : '#F8F8F8'};`">
<l-painter-view
:css="`width: 100%; display: flex; flex-direction: column; position: relative; ${type == 'skin' ? 'padding-top: 120rpx;padding-bottom: 120rpx;' : ''}`">
<template v-if="type == 'skin'">
<l-painter-text :text="title"
css="position: absolute; top: 26rpx; left: 50%; transform: translateX(-50%); font-size: 36rpx; font-weight: bold; color: #333;" />
<l-painter-image src="/static/image/other/ranking/happy.png"
css="position: absolute;top: 58rpx;right: 24rpx; width: 96rpx; height: 96rpx;" />
</template>
<l-painter-view css="border: 3rpx solid #333; background-color: #fff; width: 100%;">
<l-painter-view v-for="(item, index) in tierList" :key="index"
:css="`display: flex; min-height: 148rpx; border-bottom: ${index === tierList.length - 1 ? 'none' : '3rpx solid #333'};`">
<l-painter-view
:css="`width: 176rpx; min-height: 148rpx; background-color: ${item.bgColor}; display: flex; align-items: center; justify-content: center; border-right: 3rpx solid #333;`">
<!-- <l-painter-text :text="item.label"
:css="`font-size: 52rpx; font-weight: bold; color: ${item.textColor}; ${item.hasStroke ? 'text-shadow: 2rpx 2rpx 0 #000, -2rpx -2rpx 0 #000, 2rpx -2rpx 0 #000, -2rpx 2rpx 0 #000, 0 2rpx 0 #000, 0 -2rpx 0 #000, 2rpx 0 0 #000, -2rpx 0 0 #000;' : ''}`" /> -->
<l-painter-image
:src="`/static/image/other/ranking/${type == 'skin' ? item.img + '_skin' : item.img}.png`"
:css="`height: 60rpx; width: 160rpx;`" mode="heightFix" />
</l-painter-view>
<l-painter-view
:css="`flex: 1; display: flex; align-items: center;height: 148rpx;width:490rpx;padding:0 12rpx;`">
<l-painter-image v-for="(img, imgIdx) in item.images" :key="imgIdx" :src="img"
css="width: 105rpx; height: 105rpx; margin: 0 8rpx; object-fit: cover;"
mode="aspectFill" />
</l-painter-view>
</l-painter-view>
</l-painter-view>
<l-painter-view v-if="$isVip()"
:css="type == 'skin' ? `position: absolute;bottom: 32rpx;right: 14rpx;` : `position: absolute;top: 50%;right: 50%;transform: translate(50%, -50%);`">
<l-painter-image src="/static/image/other/card/shuiyin2.png"
css="width: 194rpx;height: 56rpx;" />
</l-painter-view>
<l-painter-image v-if="type == 'skin'" src="/static/image/other/ranking/sad.png"
css="position: absolute;bottom: 76rpx;left: 116rpx; width: 96rpx; height: 96rpx;" />
</l-painter-view>
</l-painter>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_ranking')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</template>
<script setup>
import {
ref,
reactive,
getCurrentInstance
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
const {
proxy
} = getCurrentInstance();
const type = ref("base");
const title = ref("xx排名");
const skinBgColor = ref("#FFDFDF");
const skinHue = ref(0); //
const isSnapshot = ref(false);
const isEdit = ref(false);
const rightButtonText = ref("编辑");
const tierList = ref([{
label: '夯',
bgColor: '#D5171C',
img: 'hang',
textColor: '#FFFFFF',
hasStroke: true,
images: [],
width: 60,
},
{
label: '顶级',
bgColor: '#FF6A0B',
img: 'dingji',
textColor: '#FFFFFF',
hasStroke: true,
images: [],
width: 104,
},
{
label: '人上人',
bgColor: '#FFF06A',
img: 'renshangren',
textColor: '#FFFFFF',
hasStroke: true,
images: [],
width: 156,
},
{
label: 'NPC',
bgColor: '#FDF5C8',
img: 'npc',
textColor: '#FFFFFF',
hasStroke: true,
images: [],
width: 110,
},
{
label: '拉完了',
bgColor: '#FFFFFF',
img: 'lawanle',
textColor: '#FFFFFF',
hasStroke: true,
images: [],
width: 156,
}
]);
// --- ---
onLoad(() => {
//
proxy.$apiUserEvent('all', {
type: 'click',
key: 'ranking',
value: "从夯倒拉排名"
})
//
const cache = uni.getStorageSync('ranking_config_data');
if (cache) {
title.value = cache.title || title.value;
type.value = cache.type || type.value;
skinBgColor.value = cache.skinBgColor || skinBgColor.value;
skinHue.value = cache.skinHue ?? skinHue.value;
if (cache.tierList) tierList.value = cache.tierList;
console.log(tierList.value);
}
});
// --- ---
const drag = reactive({
tierIndex: -1,
imgIdx: -1,
startX: 0,
startY: 0,
moveX: 0,
moveY: 0
});
//
const getDragStyle = (tIdx, iIdx) => {
if (drag.tierIndex === tIdx && drag.imgIdx === iIdx) {
return {
transform: `translate(${drag.moveX}px, ${drag.moveY}px)`,
zIndex: 99,
transition: 'none'
};
}
return {
transition: 'transform 0.3s ease'
};
};
const onTouchStart = (e, tIdx, iIdx) => {
if (!isEdit.value) return;
const touch = e.touches[0];
drag.tierIndex = tIdx;
drag.imgIdx = iIdx;
drag.startX = touch.clientX;
drag.startY = touch.clientY;
drag.moveX = 0;
drag.moveY = 0;
};
const onTouchMove = (e) => {
if (drag.tierIndex === -1) return;
const touch = e.touches[0];
drag.moveX = touch.clientX - drag.startX;
drag.moveY = touch.clientY - drag.startY;
};
const onTouchEnd = () => {
if (drag.tierIndex === -1) return;
const {
tierIndex,
imgIdx,
moveX
} = drag;
const rowData = tierList.value[tierIndex].images;
// item (100rpx + 12rpx 56px)
const itemWidth = 56;
const offset = Math.round(moveX / itemWidth);
let newIndex = imgIdx + offset;
//
newIndex = Math.max(0, Math.min(newIndex, rowData.length - 1));
if (newIndex !== imgIdx) {
const temp = rowData.splice(imgIdx, 1)[0];
rowData.splice(newIndex, 0, temp);
}
//
drag.tierIndex = -1;
drag.imgIdx = -1;
};
// --- ---
const chooseImage = (index) => {
const currentCount = tierList.value[index].images.length;
uni.chooseImage({
count: 4 - currentCount,
sizeType: ['compressed'],
success: (res) => {
tierList.value[index].images.push(...res.tempFilePaths);
}
});
};
const deleteImage = (tIdx, iIdx) => {
const path = tierList.value[tIdx].images[iIdx];
removeLocalFile(path); //
tierList.value[tIdx].images.splice(iIdx, 1);
};
const onRightClick = async () => {
if (isEdit.value) {
uni.showLoading({
title: '正在持久化图片...',
mask: true
});
// 1.
for (let tierIdx = 0; tierIdx < tierList.value.length; tierIdx++) {
const tier = tierList.value[tierIdx];
for (let i = 0; i < tier.images.length; i++) {
const path = tier.images[i];
// static _doc/usr
const isStatic = path.startsWith('/static/') || path.startsWith('static/');
const isPersistent = path.indexOf('_doc/') !== -1 || path.indexOf('usr/') !== -1;
if (!isStatic && !isPersistent) {
console.log('检测到待持久化图片:', path);
const newPath = await saveImageToLocal(path);
if (newPath) {
tier.images[i] = newPath;
console.log('持久化成功,新路径:', newPath);
}
}
}
}
// 2.
uni.setStorageSync('ranking_config_data', {
title: title.value,
type: type.value,
skinBgColor: skinBgColor.value,
skinHue: skinHue.value,
tierList: tierList.value
});
uni.hideLoading();
uni.showToast({
title: '已保存',
icon: 'none'
});
}
isEdit.value = !isEdit.value;
rightButtonText.value = isEdit.value ? "确定" : "编辑";
};
/**
* 将临时图片保存到本地永久目录
*/
const saveImageToLocal = (tempFilePath) => {
return new Promise((resolve) => {
//
if (tempFilePath.indexOf('_doc/') !== -1 || tempFilePath.indexOf('usr/') !== -1) {
return resolve(tempFilePath);
}
uni.saveFile({
tempFilePath: tempFilePath,
success: (res) => {
resolve(res.savedFilePath);
},
fail: (err) => {
console.error('本地持久化保存失败:', err);
// H5 saveFile App/
resolve(null);
}
});
});
};
/**
* 物理删除本地已保存的文件
*/
const removeLocalFile = (path) => {
if (!path) return;
//
if (path.indexOf('_doc/') !== -1 || path.indexOf('usr/') !== -1) {
uni.removeSavedFile({
filePath: path,
success: () => console.log('文件物理删除成功:', path),
fail: (err) => console.log('文件删除失败:', err)
});
}
};
const handleSave = () => {
uni.showLoading({
title: '生成中...',
mask: true
});
isSnapshot.value = true;
};
const onPainterSuccess = (path) => {
const done = () => {
isSnapshot.value = false;
uni.hideLoading();
};
if (!path) return done();
uni.saveImageToPhotosAlbum({
filePath: path,
success: () => uni.showToast({
title: '保存成功'
}),
fail: () => uni.showToast({
title: '保存失败',
icon: 'none'
}),
complete: done
});
};
const switchType = (val) => {
type.value = val;
if (val == 'skin') {
tierList.value[0].bgColor = '#F3575B'
tierList.value[1].bgColor = '#FF9B5B'
tierList.value[2].bgColor = '#FFF59E'
tierList.value[3].bgColor = '#FFFBE1'
tierList.value[4].bgColor = '#FFFFFF'
} else {
tierList.value[0].bgColor = '#D5171C'
tierList.value[1].bgColor = '#FF6A0B'
tierList.value[2].bgColor = '#FFF06A'
tierList.value[3].bgColor = '#FDF5C8'
tierList.value[4].bgColor = '#FFFFFF'
}
// type便
const cache = uni.getStorageSync('ranking_config_data') || {};
cache.type = val;
uni.setStorageSync('ranking_config_data', cache);
}
/**
* 循环切换行背景色 (编辑模式下)
*/
const changeRowBgColor = (index) => {
if (!isEdit.value) return;
const colors = ['#D5171C', '#FF6A0B', '#FFF06A', '#FDF5C8', '#FFFFFF', '#1777FF', '#333333'];
const curr = tierList.value[index].bgColor;
let nextIdx = (colors.indexOf(curr.toUpperCase()) + 1) % colors.length;
if (nextIdx === -1) nextIdx = 0;
tierList.value[index].bgColor = colors[nextIdx];
// ()
const darkColors = ['#D5171C', '#1777FF', '#333333'];
tierList.value[index].textColor = darkColors.includes(colors[nextIdx]) ? '#FFFFFF' : '#FFFFFF';
};
/**
* 处理色域滑动
*/
const handleHueTouch = (e) => {
const touch = e.touches[0];
uni.createSelectorQuery().select('.spectrum-picker').boundingClientRect(rect => {
if (rect) {
const x = Math.max(0, Math.min(touch.clientX - rect.left, rect.width));
skinHue.value = Math.round((x / rect.width) * 360);
// 1. HSL
const h = skinHue.value;
const s = 70;
const l = 90;
// 2. 16
skinBgColor.value = hslToHex(h, s, l);
}
}).exec();
};
/**
* HSL Hex 工具函数
*/
function hslToHex(h, s, l) {
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}`.toUpperCase();
}
</script>
<style lang="less" scoped>
.ranking-page {
min-height: 100vh;
background-color: #F8F8F8;
padding-bottom: 200rpx;
}
.content-container {
position: relative;
padding: 12rpx 24rpx;
}
.table-box {
position: relative;
.watermark {
position: absolute;
height: 56rpx;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 99;
}
}
.ranking-table {
border: 3rpx solid #333;
background-color: #fff;
}
.ranking-row {
display: flex;
min-height: 148rpx;
border-bottom: 3rpx solid #333;
&:last-child {
border-bottom: none;
}
}
.label-box {
width: 176rpx;
display: flex;
align-items: center;
justify-content: center;
border-right: 3rpx solid #333;
}
.label-text {
font-size: 48rpx;
font-weight: bold;
&.with-stroke {
text-shadow: 2rpx 2rpx 0 #000, -2rpx -2rpx 0 #000, 2rpx -2rpx 0 #000, -2rpx 2rpx 0 #000;
}
}
.items-box {
flex: 1;
display: flex;
align-items: center;
padding: 14rpx 12rpx;
flex-wrap: wrap;
}
.item-wrapper {
width: 105rpx;
height: 105rpx;
position: relative;
margin: 0 6rpx;
touch-action: none;
/* 关键:禁止浏览器默认触摸行为 */
&.is-dragging {
opacity: 0.7;
scale: 1.1;
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.2);
}
}
.item-img {
width: 100%;
height: 100%;
border-radius: 4rpx;
}
.del-btn {
position: absolute;
top: -12rpx;
right: -12rpx;
width: 36rpx;
height: 36rpx;
background: #ff4d4f;
color: #fff;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 24rpx;
z-index: 10;
}
.add-btn {
width: 100rpx;
height: 100rpx;
background: #eee;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4rpx;
.add-icon {
width: 40rpx;
height: 40rpx;
}
}
.save-btn {
margin-top: 60rpx;
width: 400rpx;
background: #1777FF;
color: #fff;
border-radius: 50rpx;
}
.bottom-tabs {
position: fixed;
bottom: 40rpx;
bottom: calc(32rpx + env(safe-area-inset-bottom));
bottom: calc(32rpx + constant(safe-area-inset-bottom));
width: 100%;
display: flex;
justify-content: center;
.tab-item {
width: 180rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: #fff;
margin: 0 20rpx;
border-radius: 10rpx;
&.active {
border: 4rpx solid #1777FF;
color: #1777FF;
}
}
}
.skin-box {
position: relative;
padding: 120rpx 12rpx;
background-color: #FFDFDF;
.watermark {
height: 56rpx !important;
position: absolute;
right: 14rpx !important;
bottom: 32rpx !important;
}
.title {
width: 260px;
position: absolute;
top: 30rpx;
left: 50%;
transform: translateX(-50%);
font-weight: bold;
text-align: center;
font-size: 36rpx;
color: #333;
}
.title-input {
min-width: 260px;
background: rgba(255, 255, 255, 0.5);
border-radius: 8rpx;
padding: 4rpx 12rpx;
border: 1rpx dashed #1777FF;
}
.title-input {
background: rgba(255, 255, 255, 0.5);
border-radius: 8rpx;
padding: 4rpx 12rpx;
width: 300rpx;
border: 1rpx dashed #1777FF;
}
.img {
position: absolute;
width: 90rpx;
height: 90rpx;
}
.happy {
top: 40rpx;
right: 30rpx;
}
.sad {
bottom: 60rpx;
left: 100rpx;
}
}
.save-action {
margin-top: 60rpx;
display: flex;
flex-direction: column;
align-items: center;
padding-bottom: 60rpx;
}
.bottom-edit-actions {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.edit-tip {
font-size: 22rpx;
color: #999;
margin-top: 10rpx;
}
.color-picker {
display: flex;
gap: 16rpx;
background: #fff;
padding: 12rpx 20rpx;
border-radius: 40rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.color-picker-wrapper {
display: flex;
align-items: center;
margin: 12rpx 0;
}
.hex-input {
width: 150rpx;
height: 56rpx;
background: #fff;
border-radius: 28rpx;
font-size: 24rpx;
text-align: center;
color: #333;
border: 1rpx solid #eee;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}
.spectrum-picker {
width: 600rpx;
height: 32rpx;
background: #fff;
border-radius: 16rpx;
padding: 4rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
position: relative;
margin: 12rpx 0;
}
.slider-thumb {
position: absolute;
top: 50%;
width: 38rpx;
height: 38rpx;
background: #fff;
border-radius: 50%;
transform: translate(-50%, -50%);
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.3);
border: 2rpx solid #fff;
pointer-events: none;
}
.spectrum-bar {
width: 100%;
height: 100%;
border-radius: 12rpx;
background: linear-gradient(to right, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%);
}
.color-dot {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 2rpx solid #fff;
box-shadow: 0 0 4rpx rgba(0, 0, 0, 0.2);
margin: 0 10rpx;
}
.painter-container {
position: fixed;
left: -9999rpx;
}
</style>

View File

@ -0,0 +1,136 @@
<script setup>
import { ref } from 'vue';
const list = ref([
{ label: '澶?, bgColor: '#BE0012', textColor: '#FFFFFF', hasShadow: true },
{ label: '椤剁骇', bgColor: '#F17415', textColor: '#FFFFFF', hasShadow: true },
{ label: '浜轰笂浜?, bgColor: '#F9E962', textColor: '#FFFFFF', hasShadow: true },
{ label: 'NPC', bgColor: '#FDF5C0', textColor: '#555555', hasShadow: false },
{ label: '鎷夊畬浜?, bgColor: '#FFFFFF', textColor: '#555555', hasShadow: false }
]);
const handleAdd = (index) => {
console.log('娣诲姞椤?, index);
};
</script>
<template>
<view class="ranking-container">
<view class="ranking-table">
<view v-for="(item, index) in list" :key="index" class="ranking-row">
<!-- 鏍囩鍒?-->
<view class="label-col" :style="{ backgroundColor: item.bgColor }">
<text class="label-text" :class="{ 'with-shadow': item.hasShadow }" :style="{ color: item.textColor }">
{{ item.label }}
</text>
</view>
<!-- 鍐呭鍒?-->
<view class="content-col">
<!-- 浠呭湪绗竴琛屽睍绀烘紨绀哄浘鐗?-->
<template v-if="index === 0">
<view class="item-box">
<image src="/static/images/food.jpg" mode="aspectFill" class="item-img" />
</view>
</template>
<!-- 娣诲姞鎸夐挳 -->
<view class="add-btn" @tap="handleAdd(index)">
<text class="plus-icon">+</text>
</view>
</view>
</view>
</view>
</view>
</template>
<style lang="less">
.ranking-container {
padding: 30rpx 20rpx;
background-color: #F8F8F8;
min-height: 100vh;
}
.ranking-table {
border: 2rpx solid #333;
background-color: #fff;
display: flex;
flex-direction: column;
}
.ranking-row {
display: flex;
min-height: 180rpx;
border-bottom: 2rpx solid #333;
&:last-child {
border-bottom: none;
}
}
.label-col {
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
border-right: 2rpx solid #333;
padding: 0 10rpx;
box-sizing: border-box;
}
.label-text {
font-size: 52rpx;
font-weight: bold;
text-align: center;
line-height: 1.2;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
&.with-shadow {
/* 寮哄姏榛戣壊鎻忚竟锛屾ā鎷?:1鏁堟灉 */
text-shadow:
2rpx 2rpx 0 #000,
-2rpx -2rpx 0 #000,
2rpx -2rpx 0 #000,
-2rpx 2rpx 0 #000,
0 2rpx 0 #000,
0 -2rpx 0 #000,
2rpx 0 0 #000,
-2rpx 0 0 #000;
}
}
.content-col {
flex: 1;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
padding: 20rpx;
gap: 20rpx;
}
.item-box {
width: 140rpx;
height: 140rpx;
background-color: #eee;
border: 1rpx solid #ddd;
}
.item-img {
width: 100%;
height: 100%;
}
.add-btn {
width: 140rpx;
height: 140rpx;
background-color: #F2F2F2;
display: flex;
align-items: center;
justify-content: center;
.plus-icon {
font-size: 70rpx;
color: #DBDBDB;
font-weight: 200;
}
}
</style>

View File

@ -0,0 +1,816 @@
<template>
<view class="container">
<!-- 自定义头部导航栏 -->
<ZdyNavbar @right-click="edit" isRightButton rightButtonText="编辑" :title="data.navbar.title"
:bgColor="data.navbar.bgColor" :isBack="true" />
<view class="footer-box">
<view class="btn" v-for="(item, index) in data.typeList" :key="index" :class="{ active: data.type == index }"
@click="setType(index)">
样式一
</view>
</view>
<view
style="display: flex;align-items: center;background: #fff;border-radius: 6px;margin: 10px;padding: 4px;color: #767676;">
<image src="/static/image/other/notice.png"
style="width: 16px;height: 16px;margin-right: 10rpx;margin-left: 10rpx;"></image>
此功能不具备真实性仅供娱乐
</view>
<image :src="data.code" mode="widthFix" style="width:calc( 100vw - 24px);margin:0 12px;" @click="previewImage">
</image>
<view class="button-container">
<button class="btn-save-image" @click="saveImage">保存图片</button>
</view>
<view style="width: 0px;height: 0;overflow: hidden;" v-if="data.shuaxing">
<l-painter isCanvasToTempFilePath @success="successImage" @progress="progress" css="width:351px;height:460px;"
v-if="data.type == 0">
<l-painter-view
:css="`width:351px;height:460px;background-image: url('/static/image/other/silkBanner/banner1.png');position: relative;`">
<!-- 落款日期 -->
<l-painter-view :css="`position: absolute;left:85px;bottom:94px;`">
<l-painter-text :css="data.textFont+data.textCss" :text="data.banner.dateText" />
</l-painter-view>
<!-- 敬赠落款 -->
<l-painter-view :css="`position: absolute;left:100px;bottom:94px;`">
<l-painter-text :css="data.textFont+data.textCss" :text="data.banner.donorLine" />
</l-painter-view>
<!-- 受赠对象 -->
<l-painter-view :css="`position: absolute;top:86px;right:74px;`">
<l-painter-text :css="data.textFont+data.textCss" :text="data.banner.recipientLine" />
</l-painter-view>
<!-- 中心大字 -->
<l-painter-view :css="`position: absolute;top:86px;left:171px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:112px;left:124px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:112px;right:116px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:155px;left:107px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:155px;right:101px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:198px;left:141px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:198px;right:126px;`">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:112px;left:158px;`">
<l-painter-text :css="data.textFont+data.textCss2+'width:50px;text-align: center;'" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view :css="`position: absolute;bottom:165px;left:10px;`">
<l-painter-text :css="data.textFont+data.textCss3" :text="data.banner.phraseTop" />
</l-painter-view>
<l-painter-view :css="`position: absolute;bottom:118px;left:10px;`">
<l-painter-text :css="data.textFont+data.textCss3" :text="data.banner.phraseBottom" />
</l-painter-view>
<l-painter-view v-if="$isVip()" :css="`position: absolute;left:117px;bottom:127px;`">
<l-painter-image src="/static/image/other/card/shuiyin2.png" css="width: 97px;height: 28px;" />
</l-painter-view>
</l-painter-view>
</l-painter>
<l-painter isCanvasToTempFilePath @success="successImage" @progress="progress" css="width:351px;height:460px;"
v-else-if="data.type == 1">
<l-painter-view
:css="`width:351px;height:460px;background-image: url('/static/image/other/silkBanner/banner2.png');position: relative;`">
<!-- 落款日期 -->
<l-painter-view :css="`position: absolute;left:85px;bottom:94px;`">
<l-painter-text :css="data.textFont+data.textCss" :text="data.banner.dateText" />
</l-painter-view>
<!-- 敬赠落款 -->
<l-painter-view :css="`position: absolute;left:100px;bottom:94px;`">
<l-painter-text :css="data.textFont+data.textCss" :text="data.banner.donorLine" />
</l-painter-view>
<!-- 受赠对象 -->
<l-painter-view :css="`position: absolute;top:86px;right:74px;`">
<l-painter-text :css="data.textFont+data.textCss" :text="data.banner.recipientLine" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:102px;right:131px;`">
<l-painter-text :css="data.textFont+data.textCss4" :text="data.banner.phraseTop" />
</l-painter-view>
<l-painter-view :css="`position: absolute;top:102px;left:142px;`">
<l-painter-text :css="data.textFont+data.textCss4" :text="data.banner.phraseBottom" />
</l-painter-view>
<l-painter-view v-if="$isVip()" :css="`position: absolute;left:117px;bottom:127px;`">
<l-painter-image src="/static/image/other/card/shuiyin2.png" css="width: 97px;height: 28px;" />
</l-painter-view>
</l-painter-view>
</l-painter>
</view>
<!-- 编辑弹窗 -->
<view v-if="showEditPopup" class="popup-overlay">
<view class="popup-content">
<view class="popup-header">
<text class="popup-title">编辑锦旗</text>
<text class="popup-close" @click="closeEditPopup">×</text>
</view>
<view class="popup-body">
<view class="form-row">
<text class="form-label">落款日期</text>
<input class="form-input" v-model="editForm.dateText" type="text" placeholder="如:二零二六年四月" />
</view>
<view class="form-row">
<text class="form-label">敬赠落款</text>
<input class="form-input" v-model="editForm.donorLine" type="text" placeholder="如:张三敬赠" />
</view>
<view class="form-row">
<text class="form-label">受赠对象</text>
<input class="form-input" v-model="editForm.recipientLine" type="text" placeholder="如:赠:某某团队" />
</view>
<view class="form-row" v-if="data.type == 0">
<text class="form-label">中心大字</text>
<input class="form-input" v-model="editForm.centerChar" type="text" maxlength="1" placeholder="单字,如:牛" />
</view>
<view class="form-row">
<text class="form-label">上句赞语</text>
<input class="form-input" v-model="editForm.phraseTop" type="text" />
</view>
<view class="form-row">
<text class="form-label">下句赞语</text>
<input class="form-input" v-model="editForm.phraseBottom" type="text" />
</view>
</view>
<view class="popup-footer">
<button class="btn-cancel" @click="closeEditPopup">取消</button>
<button class="btn-save" @click="saveEditForm">保存</button>
</view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_silkBanner')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</view>
</template>
<script setup>
//
import ZdyNavbar from "@/components/nav-bar/nav-bar.vue"
import {
ref,
reactive,
computed,
getCurrentInstance
} from "vue";
import {
onLoad,
onShow,
onReady,
onPullDownRefresh,
onReachBottom
} from "@dcloudio/uni-app";
const {
appContext,
proxy
} = getCurrentInstance();
function defaultBanner(type) {
const banners = {
0: {
dateText: '二零二六年四月',
donorLine: '安娜敬赠',
recipientLine: '赠:良子大胃袋',
centerChar: '牛',
phraseTop: '焖子大王',
phraseBottom: '靠谱'
},
1: {
dateText: '二零二六年五月',
donorLine: '小狗敬赠',
recipientLine: '赠:大小姐',
phraseTop: '我勒个豆',
phraseBottom: '太有实力'
}
// type
}
// type type 1
return banners[type] || banners[1]
}
function mergeBanner(raw, type) {
return Object.assign(defaultBanner(type), raw && typeof raw === 'object' ? raw : {})
}
const data = reactive({
isShow:false,
type: 0,
banner: mergeBanner(null, 0),
shuaxing: false,
navbar: {
title: "锦旗",
bgColor: '#EDEDED',
},
width: 800,
height: 600,
code: '',
textCss: 'text-align: center;font-weight: 400;word-spacing: 50px;letter-spacing: 20px;word-spacing: 20px;width:10px;color:#FFDD6D;font-size:10px; mix-blend-mode: overlay;',
textCss1: 'font-weight: 400;word-spacing: 50px;letter-spacing: 20px;word-spacing: 20px;color:#FFDD6D;font-size:20px; mix-blend-mode: overlay;',
textCss2: 'font-weight: 400;word-spacing: 50px;letter-spacing: 20px;word-spacing: 20px;color:#FFDD6D;font-size:80px; mix-blend-mode: overlay;',
textCss3: 'text-align: center;width:351px;font-weight: 400;word-spacing: 50px;letter-spacing: 20px;word-spacing: 20px;color:#FFDD6D;font-size:30px; mix-blend-mode: overlay;',
textCss4: 'text-align: center;width:20px;font-weight: 400;word-spacing: 50px;letter-spacing: 20px;word-spacing: 20px;color:#FFDD6D;font-size:40px; mix-blend-mode: overlay;',
textFont: 'font-family:"certificate2";',
typeList: ['样式1', '样式2']
})
//
const showEditPopup = ref(false);
const editForm = ref({});
onLoad((option) => {
uni.showLoading({
title: "生成中"
})
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'silkBanner',
prefix: '.uni.other.',
value: "锦旗"
})
data.banner = mergeBanner(uni.getStorageSync("silkBanner" + data.type), data.type)
const config = uni.getStorageSync('config')
console.log("---config---", config);
const font = config.config['client.uniapp.font']
console.log("字体地址信息", font.certificate2);
// Font loading logic
const fontUrl = font.certificate2;
const fontName = 'certificate2';
const loadFont = (path) => {
uni.loadFontFace({
family: fontName,
source: `url("${path}")`,
success() {
data.isShow=true
data.shuaxing=true
console.log('字体加载成功');
},
fail(err) {
data.isShow=true
data.shuaxing=true
console.error('字体加载失败', err);
}
});
};
// #ifdef H5
// H5 URL
loadFont(fontUrl);
// #endif
// #ifndef H5
// H5 使
const savedFontPath = uni.getStorageSync(' ');
if (savedFontPath) {
loadFont(savedFontPath);
} else {
uni.downloadFile({
url: fontUrl,
success: (res) => {
if (res.statusCode === 200) {
uni.saveFile({
tempFilePath: res.tempFilePath,
success: (saveRes) => {
const savedPath = saveRes.savedFilePath;
uni.setStorageSync('certificate2_font_path', savedPath);
console.log("字体保存路径", savedPath);
loadFont(savedPath);
},
fail: (err) => {
console.error('保存文件失败', err);
// Fallback:
loadFont(res.tempFilePath);
}
});
}
},
fail: (err) => {
console.error('下载字体失败', err);
}
});
}
// #endif
})
onReady(() => {
})
onShow(() => {})
onPullDownRefresh(() => {
setTimeout(() => {
uni.stopPullDownRefresh();
}, 1000);
})
onReachBottom(() => {
})
function setType(index) {
uni.showLoading({
title: "生成中"
})
data.type = index
data.banner = mergeBanner(uni.getStorageSync("silkBanner" + data.type), data.type)
}
function successImage(e) {
data.code = e
uni.hideLoading()
}
function progress(e) {
// console.log(e)
}
//
function edit() {
editForm.value = mergeBanner(JSON.parse(JSON.stringify(data.banner)), data.type)
showEditPopup.value = true;
}
//
function closeEditPopup() {
showEditPopup.value = false;
}
//
function saveEditForm() {
uni.showLoading({
title: "生成中"
})
data.shuaxing = false
data.banner = mergeBanner(editForm.value, data.type)
setTimeout(() => {
data.shuaxing = true
}, 100)
uni.setStorageSync("silkBanner" + data.type, data.banner)
//
showEditPopup.value = false;
}
/**
* 将本地图片路径通过 Canvas 转换为 File 对象
* @param {string} localPath - 本地图片路径如从 uni.chooseImage 获取的 tempFilePath
* @param {Object} options - 可选参数
* @param {string} options.format - 输出格式 'image/png' 'image/jpeg'默认 'image/png'
* @param {number} options.quality - 图片质量 jpeg 有效0~1默认 0.92
* @param {number} options.maxWidth - 最大宽度等比缩放不填则使用原图尺寸
* @param {number} options.maxHeight - 最大高度等比缩放不填则使用原图尺寸
* @returns {Promise<File>} 返回一个 Promiseresolve File 对象
*/
function convertLocalImageToFile(localPath) {
return new Promise((resolve, reject) => {
// 1. Base64 Image
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.file((file) => {
const reader = new plus.io.FileReader();
reader.onload = (e) => {
const base64Data = e.target.result; // data:image/jpeg;base64,/9j/...
resolve(e.target.result)
};
reader.onerror = (err) => reject(err);
reader.readAsDataURL(file); // DataURL
}, reject);
}, reject);
});
}
//
function previewImage() {
if (data.code) {
uni.previewImage({
urls: [data.code],
current: 0
});
}
}
//
function saveImage() {
if (data.code) {
console.log(data.code)
uni.showLoading({
title: '保存中...'
});
try {
// base64
if (data.code.startsWith('data:image')) {
// base64
console.log('开始处理base64图片');
uni.base64ToTempFile({
base64: data.code.split(',')[1], // base64
success: (res) => {
console.log('base64转换成功', res);
if (res.tempFilePath) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
console.log('保存图片成功');
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.log('保存图片失败', err);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
} else {
console.log('base64转换失败无临时文件路径');
uni.hideLoading();
uni.showToast({
title: '转换失败',
icon: 'none'
});
}
},
fail: (err) => {
console.log('base64转换失败', err);
uni.hideLoading();
uni.showToast({
title: '转换失败',
icon: 'none'
});
}
});
} else if (data.code.startsWith('_') || data.code.includes('temp') || data.code.includes('cache') || data
.code.includes('_doc') || data.code.includes('_tmp')) {
//
console.log('开始处理本地临时文件');
uni.saveImageToPhotosAlbum({
filePath: data.code,
success: () => {
console.log('保存本地文件成功');
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.log('保存本地文件失败', err);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
} else {
//
console.log('开始处理网络图片');
uni.downloadFile({
url: data.code,
success: (res) => {
console.log('下载图片成功', res);
if (res.statusCode === 200 && res.tempFilePath) {
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
console.log('保存图片成功');
uni.hideLoading();
uni.showToast({
title: '保存成功',
icon: 'success'
});
},
fail: (err) => {
console.log('保存图片失败', err);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
});
} else {
console.log('下载图片失败,状态码:', res.statusCode);
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
},
fail: (err) => {
console.log('下载图片失败', err);
uni.hideLoading();
uni.showToast({
title: '下载失败',
icon: 'none'
});
}
});
}
} catch (error) {
console.log('保存图片异常', error);
uni.hideLoading();
uni.showToast({
title: '保存失败',
icon: 'none'
});
}
} else {
uni.showToast({
title: '暂无图片可保存',
icon: 'none'
});
}
}
</script>
<style lang="scss">
page {
background-color: #ededed;
}
.box {
width: 750rpx;
height: 500rpx;
background-image: url('/static/image/other/certificate/graduate/graduate1.png');
background-repeat: no-repeat;
background-size: 100% 100%;
position: relative;
.title {
position: absolute;
width: 288rpx;
height: 36rpx;
display: flex;
justify-content: center;
align-items: center;
left: 92rpx;
top: 134rpx;
image {
height: 36rpx;
}
}
.icon {
position: absolute;
width: 108rpx;
height: 108rpx;
display: flex;
justify-content: center;
align-items: center;
right: 82rpx;
bottom: 124rpx;
image {
height: 108rpx;
}
}
}
@font-face {
font-family: "card";
src: url("/static/font/card.ttf");
}
* {
box-sizing: content-box;
}
.aadadad {
text-align: center;
}
.heiti {
font-family: "SimHei", "Microsoft YaHei", sans-serif;
}
/* 弹窗样式 */
.popup-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.popup-content {
background-color: #fff;
border-radius: 10px;
width: 90%;
max-width: 500px;
max-height: 80vh;
overflow-y: auto;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.popup-title {
font-size: 32rpx;
font-weight: 400;
color: #333;
}
.popup-close {
font-size: 48rpx;
color: #999;
cursor: pointer;
}
.popup-body {
padding: 20rpx;
}
.form-section {
margin-bottom: 30rpx;
padding: 20rpx;
background-color: #f5f5f5;
border-radius: 8rpx;
}
.section-title {
font-size: 28rpx;
font-weight: 400;
color: #333;
margin-bottom: 20rpx;
display: block;
}
.form-row {
display: flex;
align-items: center;
margin-bottom: 20rpx;
}
.form-label {
width: 200rpx;
font-size: 26rpx;
color: #333;
}
.form-input {
flex: 1;
padding: 15rpx;
border: 1rpx solid #ddd;
border-radius: 4rpx;
font-size: 26rpx;
}
.form-separator {
margin: 0 10rpx;
font-size: 26rpx;
color: #333;
}
.form-total {
margin-top: 10rpx;
padding-top: 10rpx;
border-top: 1rpx dashed #ddd;
}
.form-total-input {
background-color: #f9f9f9;
color: #ff6b35;
font-weight: 400;
}
/* 上传图片样式 */
.upload-container {
flex: 1;
position: relative;
}
.upload-btn {
width: 120rpx;
height: 160rpx;
border-radius: 8rpx;
border: 1rpx dashed #ddd;
display: flex;
justify-content: center;
align-items: center;
background-color: #f5f5f5;
}
.upload-text {
font-size: 22rpx;
color: #999;
text-align: center;
}
.upload-image {
width: 98rpx;
height: 115rpx;
border-radius: 1rpx;
object-fit: cover;
border: 1px dashed #ddd;
}
.popup-footer {
display: flex;
justify-content: space-between;
padding: 20rpx;
border-top: 1rpx solid #eee;
}
.btn-cancel,
.btn-save {
width: calc(48% - 40rpx);
padding: 20rpx;
border-radius: 4rpx;
font-size: 28rpx;
}
.btn-cancel {
background-color: #f5f5f5;
color: #333;
// border: 1rpx solid #ddd;
}
.btn-save {
background-color: #187AFF;
color: #fff;
border: none;
}
/* 保存图片按钮 */
.button-container {
display: flex;
justify-content: center;
padding: 30rpx 0;
}
.btn-save-image {
background: linear-gradient(90deg, #187AFF 0%, #3295FC 100%);
color: #fff;
border: none;
padding: 18rpx 60rpx;
border-radius: 50rpx;
font-size: 26rpx;
font-weight: 400;
box-shadow: 0 3rpx 10rpx rgba(7, 66, 193, 0.3);
transition: all 0.3s ease;
text-align: center;
min-width: 160rpx;
}
.btn-save-image:hover {
transform: translateY(-2rpx);
box-shadow: 0 4rpx 12rpx rgba(7, 72, 193, 0.4);
}
.btn-save-image:active {
transform: translateY(0);
box-shadow: 0 2rpx 6rpx rgba(7, 140, 193, 0.3);
}
.footer-box {
position: fixed;
left: 0;
bottom: 16px;
width: 100vw;
display: flex;
justify-content: center;
padding-bottom: constant(safe-area-inset-bottom); // IOS<11.2
padding-bottom: env(safe-area-inset-bottom); // IOS>11.2
.btn {
font-size: 18px;
color: #767676;
width: 180rpx;
height: 80rpx;
line-height: 80rpx;
background: #FFFFFF;
border-radius: 8px 8px 8px 8px;
background: #FFFFFF;
text-align: center;
margin: 0 24rpx;
border: 2px solid #FFFFFF;
color: #767676;
}
.active {
color: #1777FF;
border: 2px solid #1777FF;
}
}
</style>

View File

@ -8,7 +8,7 @@
<view class="button-container">
<button class="btn-save-image" @click="saveImage">保存图片</button>
</view>
<l-painter isCanvasToTempFilePath @success="data.code = $event" hidden
<l-painter isCanvasToTempFilePath @success="successImage" @progress="progress" hidden
:css="`width:${data.width}px;height:${data.width / 4 * 3}px;`">
<l-painter-view
:css="`position: relative;width:${data.width}px;height:${data.width / 4 * 3}px;background-image: url('/static/image/other/gzd.png');`">
@ -170,6 +170,13 @@
</view>
</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_gongzhitiao')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
</view>
</template>
@ -228,7 +235,21 @@ const data = reactive({
//
const showEditPopup = ref(false);
const editForm = ref({});
//
function successImage(e){
data.code=e
}
function progress(e){
if(e<0.03){
uni.showLoading({
title:"生成中"
})
}
if(e==1){
uni.hideLoading()
}
console.log(e)
}
//
function edit() {
console.log(data.form)

View File

@ -1,93 +1,140 @@
<template>
<view class="container">
<NavBar title="选择机票" bgColor="#F0F4F9" isBack></NavBar>
<NavBar :title="`选择${type == 'airTicket' ? '机票' : '火车票'}`" bgColor="#F0F4F9" isBack></NavBar>
<view class="content">
<view class="app-card" v-for="(item, index) in appList" :key="index" @click="handleItemClick(item)">
<!-- Background Watermark -->
<image class="watermark" :src="item.bgImage" mode="heightFix"></image>
<template v-for="(item, index) in appList" :key="index">
<view class="app-card"
v-if="(type == 'airTicket' && item.airPath) || (type == 'trainTicket' && item.trainPath)"
@click="handleItemClick(item)">
<!-- Background Watermark -->
<image class="watermark" :src="item.bgImage" mode="heightFix"></image>
<!-- Front Content -->
<view class="card-left">
<view class="logo-box">
<image class="logo" :src="item.logo" mode="aspectFit"></image>
<!-- Hot Tag for Fliggy -->
<view v-if="item.isHot" class="hot-tag">
<image style="width: 72rpx;height:32rpx" src="/static/image/index/hot-icon.png"
mode="aspectFit"></image>
<!-- Front Content -->
<view class="card-left">
<view class="logo-box">
<image class="logo" :src="item.logo" mode="aspectFit"></image>
<!-- Hot Tag for Fliggy -->
<view v-if="item.isHot" class="hot-tag">
<image style="width: 72rpx;height:32rpx" src="/static/image/index/hot-icon.png"
mode="aspectFit"></image>
</view>
</view>
<text class="app-name">{{ item.name }}</text>
</view>
<text class="app-name">{{ item.name }}</text>
</view>
<uni-icons type="right" size="18" color="#CCCCCC"></uni-icons>
</view>
<uni-icons type="right" size="18" color="#CCCCCC"></uni-icons>
</view>
</template>
</view>
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue';
import { reactive, toRefs, getCurrentInstance } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { util } from '@/utils/common.js'; // Assuming util exists for navigation, similar to previous tasks
import {
reactive,
toRefs,
getCurrentInstance,
ref
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app';
import {
util
} from '@/utils/common.js'; // Assuming util exists for navigation, similar to previous tasks
const {
appContext,
proxy
} = getCurrentInstance();
let type = ref('airTicket')
const appList = [
{
name: '携程APP',
logo: '/static/image/other/tickets-app/trip-com-logo.png',
bgImage: '/static/image/other/tickets-app/trip-com-bg.png',
airPath: '/pages/other/air-tickets/ctrip-air-tickets/ctrip-air-tickets',
trainPath: "/pages/other/train-tickets/ctrip-train-tickets/ctrip-train-tickets",
isHot: false
},
{
name: '铁路12306',
logo: '/static/image/other/tickets-app/12306-logo.png',
bgImage: '/static/image/other/tickets-app/12306-bg.png',
airPath: '',
trainPath: "/pages/other/train-tickets/12306-tickets/12306-tickets",
isHot: false
}, {
name: '去哪儿APP',
logo: '/static/image/other/tickets-app/qvnar-logo.png',
bgImage: '/static/image/other/tickets-app/qvnar-bg.png',
path: '/pages/other/air-tickets/qunar-air-tickets/qunar-air-tickets',
airPath: '/pages/other/air-tickets/qunar-air-tickets/qunar-air-tickets',
trainPath: "/pages/other/train-tickets/qunar-train-tickets/qunar-train-tickets",
isHot: false
},
{
name: '飞猪APP',
logo: '/static/image/other/tickets-app/fliggy-logo.png',
bgImage: '/static/image/other/tickets-app/fliggy-bg.png',
path: '/pages/other/air-tickets/fliggy-air-tickets/fliggy-air-tickets',
airPath: '/pages/other/air-tickets/fliggy-air-tickets/fliggy-air-tickets',
trainPath: "/pages/other/train-tickets/fliggy-train-tickets/fliggy-train-tickets",
isHot: true
},
{
name: '携程APP',
logo: '/static/image/other/tickets-app/trip-com-logo.png',
bgImage: '/static/image/other/tickets-app/trip-com-bg.png',
path: '/pages/other/air-tickets/ctrip-air-tickets/ctrip-air-tickets',
isHot: false
},
// {
// name: '12306',
// logo: '/static/image/other/tickets-app/12306-logo.png',
// bgImage: '/static/image/other/tickets-app/12306-bg.png',
// path: '/pages/other/train-tickets/12306-tickets/12306-tickets',
// isHot: false
// }
]
onLoad((option) => {
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'ticket',
prefix: '.uni.other.',
value: "机票"
})
})
const handleItemClick = (item) => {
if (item.path) {
util.goPage(item.path)
const appType = option.type
if (appType == "trainTicket") {
type.value = "trainTicket"
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'train_ticket',
prefix: '.uni.other.',
value: "高铁票"
})
} else {
uni.showToast({
title: '开发中',
icon: 'none'
type.value = "airTicket"
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'ticket',
prefix: '.uni.other.',
value: "机票"
})
}
}
})
/**
* 跳转火车票/飞机票页面
*/
const handleItemClick = (item) => {
if (type.value == 'trainTicket') {
if (item.trainPath && item.trainPath != "开发中") {
util.goPage(item.trainPath)
} else {
uni.showToast({
title: '开发中',
icon: 'none'
})
}
} else {
if (item.airPath) {
util.goPage(item.airPath)
} else {
uni.showToast({
title: '开发中',
icon: 'none'
})
}
}
}
</script>
<style lang="less" scoped>
.container {

View File

@ -1,8 +1,8 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark dark="light" source="uni_alipay_other_tickets_12306" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_tickets_12306')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -24,8 +24,8 @@
<view class="order-info-box">
<view class="order-number-row">
<text class="order-label">订单号:{{ ticketsInfo.orderInfo.orderNo }}</text>
<image class="copy-btn" @click="copyOrderNo"
src="/static/image/other/train-tickets/12306-tickets/copy-button.png"></image>
<image class="copy-btn" src="/static/image/other/train-tickets/12306-tickets/copy-button.png">
</image>
</view>
<text class="order-time">下单时间:{{ ticketsInfo.orderInfo.orderTime }}</text>
</view>
@ -201,8 +201,7 @@ import {
ref,
reactive,
toRefs,
computed,
getCurrentInstance
computed
} from 'vue';
import {
onLoad,
@ -210,11 +209,6 @@ import {
} from '@dcloudio/uni-app';
import { util } from '@/utils/common.js';
const {
appContext,
proxy
} = getCurrentInstance();
const buttonGroup = [{
name: "编辑车票信息",
click: () => {
@ -226,24 +220,7 @@ function goEdit() {
util.goPage('/pages/other/train-tickets/edit/edit')
}
const ticketType = [
{
label: '成人票',
value: '1'
},
{
label: '儿童票',
value: '2'
},
{
label: '学生票',
value: '3'
},
{
label: '残疾军人票',
value: '4'
}
]
const statusBarHeight = ref(20);
const data = reactive({
@ -364,14 +341,6 @@ const calculateNightCount = () => {
onLoad(() => {
const sys = uni.getSystemInfoSync();
statusBarHeight.value = sys.statusBarHeight || 20;
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'train_ticket',
prefix: '.uni.other.',
value: "高铁票"
})
});
onShow(() => {
@ -393,22 +362,6 @@ onShow(() => {
}
});
const goBack = () => {
uni.navigateBack();
};
const copyOrderNo = () => {
uni.setClipboardData({
data: orderInfo.value.orderNo,
success: () => {
uni.showToast({
title: '复制成功',
icon: 'none'
});
}
});
};
const handleAction = (action) => {
uni.showToast({
title: `点击了${action}`,

File diff suppressed because it is too large Load Diff

View File

@ -14,13 +14,21 @@
<text class="label">订单号</text>
<input class="input" v-model="ticketsInfo.orderInfo.orderNo" />
</view>
<picker mode="date" fields="day" :value="getPickerDate(ticketsInfo.orderInfo.orderTime)"
@change="onOrderTimeChange">
<picker v-if="app == '12306'" mode="date" fields="day"
:value="getPickerDate(ticketsInfo.orderInfo.orderTime)" @change="onOrderTimeChange">
<view class="form-item">
<text class="label">下单时间</text>
<view class="input">{{ ticketsInfo.orderInfo.orderTime }}</view>
</view>
</picker>
<view v-if="app != '12306' && app != 'fliggy'" class="form-item">
<text class="label">订单总价</text>
<input class="input" type="number" v-model="ticketsInfo.orderInfo.price" />
</view>
<view v-if="app == 'fliggy'" class="form-item">
<text class="label">已购服务</text>
<input class="input" v-model="ticketsInfo.serviceText" />
</view>
</view>
</view>
@ -71,10 +79,18 @@
<view class="input">{{ ticketsInfo.ticketInfo.arrivalTime }}</view>
</view>
</picker>
<!-- <view class="form-item">
<view class="form-item">
<text class="label">历时</text>
<input class="input" v-model="ticketsInfo.ticketInfo.duration" disabled />
</view> -->
<input class="input" v-model="ticketsInfo.ticketInfo.duration" />
</view>
<view v-if="app == 'ctrip'" class="form-item">
<text class="label">火车名称</text>
<input class="input" v-model="ticketsInfo.ticketInfo.trainName" />
</view>
<view v-if="app == 'fliggy'" class="form-item">
<text class="label">车票状态</text>
<input class="input" v-model="ticketsInfo.ticketInfo.status" />
</view>
</view>
</view>
@ -95,7 +111,8 @@
<text class="label">姓名</text>
<input class="input" v-model="passenger.name" />
</view>
<picker :range="ticketType" range-key="label" @change="(e) => onTicketTypeChange(e, index)">
<picker v-if="app != 'fliggy'" :range="ticketType" range-key="label"
@change="(e) => onTicketTypeChange(e, index)">
<view class="form-item" style="border-bottom: 1rpx solid #F5F5F5;">
<text class="label">票种</text>
<view class="input">{{ passenger.type }}</view>
@ -116,13 +133,21 @@
<view class="form-item">
<text class="label">票价</text>
<input class="input" v-model="passenger.price" />
<input class="input" type="number" v-model="passenger.price" @input="onPriceInput" />
</view>
<view class="form-item">
<view v-if="app == 'ctrip'" class="form-item">
<text class="label">积分</text>
<input class="input" type="number" v-model="passenger.points" />
</view>
<view v-if="app != 'ctrip'" class="form-item">
<text class="label">证件类型</text>
<input class="input" v-model="passenger.idType" />
</view>
<view class="form-item">
<view v-if="app != 'ctrip'" class="form-item">
<text class="label">证件号</text>
<input class="input" v-model="passenger.idNumber" />
</view>
<view v-if="app != 'qunar'" class="form-item">
<text class="label">是否本人</text>
<switch :checked="passenger.isMe" @change="(e) => passenger.isMe = e.detail.value" />
</view>
@ -136,16 +161,20 @@
</view>
<!-- 酒店广告 -->
<view class="section-container">
<view v-if="app == '12306' || app == 'ctrip'" class="section-container">
<view class="section-header" @click="toggleSection('hotelInfo')">
<text class="section-title">酒店广告</text>
<text class="section-title">{{ app == '12306' ? '酒店广告' : '返现任务' }}</text>
<uni-icons :type="collapsed.hotelInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
</view>
<view class="card" v-show="!collapsed.hotelInfo">
<view class="form-item">
<view v-if="app == '12306'" class="form-item">
<text class="label">城市</text>
<input class="input" v-model="ticketsInfo.hotelInfo.city" />
</view>
<view v-if="app == 'ctrip'" class="form-item">
<text class="label">返现金额</text>
<input class="input" type="number" v-model="ticketsInfo.hotelInfo.cashback" />
</view>
<uni-datetime-picker type="daterange" v-model="hotelDateRange" :border="false">
<view class="form-item">
<text class="label">入住/离店日期</text>
@ -163,12 +192,19 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import { reactive, toRefs, onMounted, computed } from 'vue';
import {
reactive,
toRefs,
onMounted,
computed
} from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const defaultData = {
"orderInfo": {
"orderNo": "EJ66223536",
"orderTime": "2026.01.01"
"orderTime": "2026.01.01",
"price": "4440"
},
"ticketInfo": {
"departureTime": "01-01 09:19",
@ -179,45 +215,47 @@ const defaultData = {
"duration": "4时45分",
"date": "2026.01.01",
"weekDay": "星期四",
"gate": "6B"
"gate": "6B",
"trainName": "复兴号"
},
"passengerList": [
{
"name": "张元英",
"type": "成人票",
"seatType": "商务座",
"carriage": "01",
"seatNo": "03C",
"idType": "外国护照KR",
"price": "2110",
"status": "已支付",
"isMe": true
}
],
"passengerList": [{
"name": "张元英",
"type": "成人票",
"seatType": "商务座",
"carriage": "01",
"seatNo": "03C",
"idType": "外国护照KR",
"idNumber": "123456789012345678",
"price": "2110",
"status": "已支付",
"isMe": true,
"points": 9632
}],
"hotelInfo": {
"city": "上海",
"cashback": "25",
"startDay": "01-01",
"endDay": "01-02"
}
},
serviceText: "多人连坐 ¥10x2份"
}
//
const ticketType = [
{
label: '成人票',
value: '1'
},
{
label: '儿童票',
value: '2'
},
{
label: '学生票',
value: '3'
},
{
label: '残疾军人票',
value: '4'
}
const ticketType = [{
label: '成人票',
value: '1'
},
{
label: '儿童票',
value: '2'
},
{
label: '学生票',
value: '3'
},
{
label: '残疾军人票',
value: '4'
}
]
const data = reactive({
@ -227,11 +265,28 @@ const data = reactive({
ticketInfo: false, // Default open ticket info as it is most likely to be edited
passengerList: false,
hotelInfo: true
}
},
app: '12306',
STORAGE_KEY: 'ticketsInfo'
})
let { app } = toRefs(data)
const { ticketsInfo, collapsed } = toRefs(data)
const {
ticketsInfo,
collapsed
} = toRefs(data)
onLoad((options) => {
console.log("options", options)
if (options.app) {
data.app = options.app
}
if (options.storageKey) {
data.STORAGE_KEY = options.storageKey
}
})
const ticketYear = computed(() => {
const dateStr = data.ticketsInfo.ticketInfo.date;
@ -242,14 +297,17 @@ const ticketYear = computed(() => {
})
/**
* 获取酒店日期范围
*/
const hotelDateRange = computed({
get() {
const year = ticketYear.value;
const start = data.ticketsInfo.hotelInfo.startDay ? `${year}-${data.ticketsInfo.hotelInfo.startDay}` : '';
const end = data.ticketsInfo.hotelInfo.endDay ? `${year}-${data.ticketsInfo.hotelInfo.endDay}` : '';
const start = data.ticketsInfo.hotelInfo.startDay ?
`${year}-${data.ticketsInfo.hotelInfo.startDay}` : '';
const end = data.ticketsInfo.hotelInfo.endDay ? `${year}-${data.ticketsInfo.hotelInfo.endDay}` :
'';
if (start && end) {
return [start, end];
}
@ -264,12 +322,26 @@ const hotelDateRange = computed({
})
onMounted(() => {
const stored = uni.getStorageSync('ticketsInfo')
const stored = uni.getStorageSync(data.STORAGE_KEY)
if (stored) {
Object.assign(data.ticketsInfo, stored)
}
updateDuration();
})
/**
* 更新总价
*/
const onPriceInput = () => {
setTimeout(() => {
let total = 0;
data.ticketsInfo.passengerList.forEach(item => {
const price = Number(item.price) || 0;
total += price;
});
data.ticketsInfo.orderInfo.price = total.toString();
}, 50);
}
/**
* 确认
*/
@ -305,7 +377,7 @@ const handleRightButtonClick = () => {
}
}
uni.setStorageSync('ticketsInfo', data.ticketsInfo)
uni.setStorageSync(data.STORAGE_KEY, data.ticketsInfo)
uni.navigateBack()
}
@ -327,7 +399,8 @@ const removePassenger = (index) => {
content: '确定要删除该乘客吗?',
success: (res) => {
if (res.confirm) {
data.ticketsInfo.passengerList.splice(index, 1)
data.ticketsInfo.passengerList.splice(index, 1);
onPriceInput();
}
}
})
@ -347,9 +420,11 @@ const addPassenger = () => {
idType: '中国居民身份证',
price: oldPassenger.price || '0',
status: '已支付',
isMe: false
isMe: false,
points: oldPassenger.points || '2898',
}
data.ticketsInfo.passengerList.push(newPassenger)
data.ticketsInfo.passengerList.push(newPassenger);
onPriceInput();
}
/**
@ -432,8 +507,12 @@ const departureTimeHHMM = computed(() => {
* 获取出发时间选择器范围
*/
const departureTimeRange = computed(() => {
const hours = Array.from({ length: 24 }, (_, i) => i < 10 ? '0' + i : '' + i);
const minutes = Array.from({ length: 60 }, (_, i) => i < 10 ? '0' + i : '' + i);
const hours = Array.from({
length: 24
}, (_, i) => i < 10 ? '0' + i : '' + i);
const minutes = Array.from({
length: 60
}, (_, i) => i < 10 ? '0' + i : '' + i);
return [hours, minutes];
})
@ -470,8 +549,12 @@ const arrivalRange = computed(() => {
} else {
dates.push('MM-DD');
}
const hours = Array.from({ length: 24 }, (_, i) => i < 10 ? '0' + i : '' + i);
const minutes = Array.from({ length: 60 }, (_, i) => i < 10 ? '0' + i : '' + i);
const hours = Array.from({
length: 24
}, (_, i) => i < 10 ? '0' + i : '' + i);
const minutes = Array.from({
length: 60
}, (_, i) => i < 10 ? '0' + i : '' + i);
return [dates, hours, minutes];
})
@ -754,4 +837,4 @@ page {
.placeholder {
height: 60rpx;
}
</style>
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,279 @@
<template>
<view class="box">
<view class="codefun-flex-row codefun-justify-between codefun-items-center section_5 codefun-mt-8">
<view class="codefun-flex-row codefun-items-center">
<image class="codefun-shrink-0 image_11" src="/static/image/other/train-tickets/qunar/plan.png" />
<text class="font_3 text_31 codefun-ml-4">抢票方案详情</text>
</view>
<image class="image_4 image_12" src="/static/image/other/train-tickets/qunar/right.png" />
</view>
<view class="codefun-flex-row codefun-items-center section_6 codefun-mt-8">
<view class="codefun-flex-row codefun-flex-1">
<image class="codefun-shrink-0 image_13" src="/static/image/other/train-tickets/qunar/child.png" />
<view class="codefun-flex-col codefun-items-start codefun-flex-1 group_14 codefun-ml-10">
<text class="font_9 text_32">免费携带儿童申报</text>
<text class="font_5 text_34 codefun-mt-8">每名成人可免费携带1名6岁以下儿童</text>
</view>
</view>
<view
class="codefun-flex-col codefun-justify-start codefun-items-center codefun-shrink-0 text-wrapper_4 ml-15 codefun-justify-center">
<text class="font_9 text_33">去申报</text>
</view>
</view>
<view class="codefun-flex-col codefun-justify-start section_7 codefun-mt-8">
<view class="codefun-flex-col group_15">
<text class="codefun-self-start text_35">专属服务</text>
<view
class="codefun-flex-row codefun-justify-between codefun-items-center codefun-self-stretch group_16">
<view class="codefun-flex-row codefun-items-baseline">
<text class="codefun-shrink-0 font_3 text_36">全能抢票套餐</text>
<text class="font_19 codefun-ml-24">20元接送站代金券 x2份</text>
</view>
<image class="image_7" src="/static/image/other/train-tickets/qunar/right.png" />
</view>
<view v-for="(item, index) in services" :key="index"
class="codefun-flex-row codefun-justify-between codefun-items-center codefun-self-end service-item-right">
<text class="font_19">{{ item }}</text>
<image class="image_7" src="/static/image/other/train-tickets/qunar/right.png" />
</view>
</view>
</view>
<view class="codefun-flex-col section_8 codefun-mt-8">
<text class="codefun-self-start font_12 text_41">你可能遇到的问题</text>
<view class="codefun-flex-col codefun-self-stretch mt-25">
<view v-for="(question, index) in questions" :key="index"
class="codefun-flex-row codefun-justify-between codefun-items-center faq-item"
:class="{ 'mt-34': index > 0 }">
<view class="codefun-flex-row codefun-items-center codefun-flex-1">
<view class="codefun-flex-col codefun-items-center indicator-wrap">
<view class="codefun-shrink-0 section_9"></view>
</view>
<text class="font_3" style="margin-left: 14rpx;">{{ question.text }}</text>
</view>
<image class="image_7 faq-arrow" src="/static/image/other/train-tickets/qunar/right.png" />
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const services = ref([
'腾讯视频vip月卡 x2份',
'加速包 x2份',
'24小时专人抢票 x2份'
]);
const questions = ref([
{ text: '为什么会退票失败' },
{ text: '退款什么时候到账' },
{ text: '已在车站或12306办理退票退款何时到账' },
{ text: '如何退票' }
]);
</script>
<style>
@import '/common/global.css';
page {
background-color: #F2F5F9;
}
</style>
<style lang="less" scoped>
.mt-25 {
margin-top: 50rpx;
}
.box {
// padding: 0 16rpx;
.section_5 {
padding: 24rpx 28rpx 23.5rpx;
overflow: hidden;
border-radius: 20rpx;
background-color: #ffffff;
.image_11 {
width: 32rpx;
height: 32rpx;
}
.font_3 {
font-size: 26rpx;
line-height: 25.92rpx;
color: #363636;
}
.text_31 {
font-size: 28rpx;
line-height: 26.04rpx;
}
.image_4 {
width: 28rpx;
height: 28rpx;
}
.image_12 {
margin-right: 8rpx;
}
}
.section_6 {
padding: 24rpx 32rpx;
background-color: #ffffff;
background-size: 100% 100%;
background-repeat: no-repeat;
border-radius: 20rpx;
.image_13 {
width: 72rpx;
height: 72rpx;
}
.group_14 {
margin-top: 6.4rpx;
.text_32 {
font-size: 28rpx;
line-height: 26.12rpx;
}
.font_5 {
font-size: 26rpx;
line-height: 21.14rpx;
color: #666666;
}
.text_34 {
color: #999999;
font-size: 24rpx;
line-height: 22.48rpx;
}
}
.text-wrapper_4 {
background-image: linear-gradient(106.4deg, #ff912b -19.9%, #fc6a11 100.3%);
border-radius: 32rpx;
width: 140rpx;
height: 64rpx;
.text_33 {
color: #ffffff;
}
}
.font_9 {
font-size: 26rpx;
line-height: 25.92rpx;
font-weight: 700;
color: #363636;
}
}
.section_7 {
padding: 46rpx 0 48rpx;
background-color: #ffffff;
border-radius: 20rpx;
.group_15 {
margin-left: 30rpx;
margin-right: 36rpx;
overflow: hidden;
.text_35 {
color: #363636;
font-size: 30rpx;
font-weight: 700;
line-height: 27.7rpx;
}
.group_16 {
margin-top: 34.76rpx;
.font_3 {
font-size: 26rpx;
line-height: 25.92rpx;
color: #363636;
}
.text_36 {
line-height: 24.24rpx;
}
}
.font_19 {
font-size: 26rpx;
line-height: 28rpx;
color: #666666;
}
.image_7 {
width: 20rpx;
height: 20rpx;
}
.service-item-right {
margin-top: 24rpx;
width: 450rpx;
}
}
}
.section_8 {
padding: 36.12rpx 20.54rpx 46.56rpx 28.54rpx;
background-image: linear-gradient(180deg, #f1fbfb -21.5%, #ffffff 67.4%);
border-radius: 20rpx;
border: solid 4rpx #ffffff;
.font_12 {
font-size: 32rpx;
line-height: 29.6rpx;
color: #363636;
}
.text_41 {
font-size: 34rpx;
font-weight: 700;
line-height: 31.44rpx;
}
.section_9 {
background-color: #02c8e7;
border-radius: 50%;
width: 8rpx;
height: 8rpx;
}
.indicator-wrap {
width: 20rpx;
display: flex;
align-items: center;
}
.font_3 {
font-size: 26rpx;
line-height: 25.92rpx;
color: #363636;
}
.image_7 {
width: 20rpx;
height: 20rpx;
}
.faq-arrow {
margin-right: 15rpx;
}
.mt-34 {
margin-top: 34rpx;
}
.group_21 {
line-height: 24.62rpx;
}
}
}
</style>

View File

@ -0,0 +1,678 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_other_tickets_qunar" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_tickets_qunar')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<view class="codefun-flex-col section">
<NavBar :bgColor="data.navBar.bgColor" :buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton"
tipLayerType="qunar-train-tickets-tip" isTipLayer tipLayerText="修改车票信息">
<template v-slot:right>
<view class="codefun-flex-col group_2">
<view class="codefun-flex-row group_3">
<image class="image_4" src="/static/image/other/train-tickets/qunar/tuigaishuoming.png" />
<image class="image_4 ml-25" src="/static/image/other/train-tickets/qunar/kefu.png" />
</view>
<view class="codefun-flex-row codefun-mt-4">
<text class="font text">退改说明</text>
<text class="font text_2 ml-11">客服</text>
</view>
</view>
</template>
</NavBar>
<view class="codefun-flex-col">
<view class="codefun-flex-row codefun-justify-between codefun-items-center group_4">
<view class="codefun-flex-row codefun-items-center">
<image class="codefun-shrink-0 image_5" src="/static/image/other/train-tickets/qunar/success.png" />
<text class="text_5 ml-7">出票完成</text>
<view
class="codefun-flex-col codefun-justify-start codefun-items-center codefun-shrink-0 text-wrapper ml-7 codefun-justify-center">
<text class="font_5 text_6">订返程</text>
</view>
</view>
<view class="codefun-flex-col section_2">
<view class="codefun-self-start group_5">
<text class="font_2 text_3">¥</text>
<text class="font_2 text_4">{{ trainTickets.orderInfo.price }}</text>
</view>
<image class="codefun-shrink-0 codefun-self-end image_7 image_8"
src="/static/image/other/train-tickets/qunar/right.png" />
<text class="codefun-self-start text_7">支付明细</text>
</view>
</view>
</view>
</view>
<view class="main-box">
<view class="codefun-flex-col section_3">
<view class="codefun-flex-col view">
<view class="codefun-flex-row codefun-items-baseline codefun-self-stretch codefun-justify-between">
<text class="font_5 text_8">取票号:{{ trainTickets.orderInfo.orderNo }}</text>
<view class="group_9 ml-25">
<text class="font_6">{{ trainTickets.ticketInfo.gate }}</text>
</view>
</view>
<view class="codefun-flex-row equal-division codefun-justify-between">
<view class="codefun-flex-col group_10 group_28">
<text class="codefun-self-stretch font_3 text_16">{{
formatDateTime(trainTickets.ticketInfo.departureTime,
trainTickets.ticketInfo.date) }}</text>
<text class="codefun-self-start font_8 mt-7">{{
trainTickets.ticketInfo.departureTime.split(' ')[1]
}}</text>
</view>
<view class="codefun-flex-col codefun-items-center group_10 group_29">
<text class="font_5 text_17">{{ trainTickets.ticketInfo.duration }}</text>
<image class="image_9 mt-11" src="/static/image/other/train-tickets/qunar/jingtingzhan.png" />
</view>
<view class="codefun-flex-col group_10 group_26">
<text class="codefun-self-start font_3 text_18">{{
formatDateTime(trainTickets.ticketInfo.arrivalTime,
trainTickets.ticketInfo.date) }}</text>
<text class="codefun-self-end font_8 mt-7">{{ trainTickets.ticketInfo.arrivalTime.split(' ')[1]
}}</text>
</view>
</view>
<view class="codefun-flex-row codefun-justify-center codefun-self-stretch codefun-relative group_11">
<view class="codefun-flex-row codefun-items-center pos">
<text class="font_9 text_19">{{ trainTickets.ticketInfo.departureStation }}</text>
<image class="codefun-shrink-0 image_4 ml-3"
src="/static/image/other/train-tickets/qunar/location.png" />
</view>
<view class="codefun-flex-row codefun-items-center">
<text class="font_19 text_21">{{ trainTickets.ticketInfo.trainNo }}</text>
<image class="codefun-shrink-0 image_10 codefun-ml-2"
src="/static/image/other/train-tickets/qunar/info.png" />
</view>
<view class="codefun-flex-row codefun-items-center pos_2">
<image class="codefun-shrink-0 image_4"
src="/static/image/other/train-tickets/qunar/location.png" />
<text class="font_9 text_20 codefun-ml-4">{{ trainTickets.ticketInfo.arrivalStation }}</text>
</view>
</view>
</view>
<view class="codefun-flex-col group_12">
<view class="codefun-flex-col section_1" v-for="item in trainTickets.passengerList" :key="item.id">
<view class="codefun-flex-col">
<view class="codefun-flex-row codefun-justify-between codefun-items-center">
<view class="codefun-flex-row codefun-items-baseline">
<text class="font_10">{{ item.name }}</text>
<text class="font_11 text_22 codefun-ml-10">{{ item.idType }}</text>
</view>
<view class="codefun-flex-row">
<view
class="codefun-flex-col codefun-justify-start codefun-items-center codefun-shrink-0 text-wrapper_8">
<text v-if="computeSeatNo(item.seatNo)" class="font_4 text_23">{{
computeSeatNo(item.seatNo) }}</text>
</view>
<text class="font_12 text_51 codefun-ml-6">{{ item.carriage }}{{ item.seatNo }}</text>
</view>
</view>
<view class="codefun-flex-row codefun-justify-between codefun-mt-8">
<text class="codefun-self-start font_13">{{ item.idType.includes('身份证') ?
stringUtil.maskIdCard(item.idNumber) : (item.idType.includes('护照') ?
stringUtil.maskPassport(item.idNumber) : item.idNumber) }}</text>
<text class="codefun-self-center font_14 text_25">{{ item.seatType }}</text>
</view>
</view>
<view class="codefun-flex-row codefun-justify-between codefun-items-center group_27 mt-19">
<text class="font_15 text_50">出票成功</text>
<view class="codefun-flex-row">
<view class="codefun-flex-col codefun-justify-center codefun-items-center action-btn">
<text class="font_16">分享</text>
</view>
<view
class="codefun-flex-col codefun-justify-center codefun-items-center action-btn codefun-ml-6">
<text class="font_16">改签</text>
</view>
<view
class="codefun-flex-col codefun-justify-center codefun-items-center action-btn codefun-ml-6">
<text class="font_16">退票</text>
</view>
</view>
</view>
</view>
</view>
</view>
<Box />
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue';
import Box from './components/box.vue';
import { reactive, toRefs } from 'vue';
import { onShow, onPageScroll } from '@dcloudio/uni-app';
import { util, stringUtil } from '@/utils/common.js';
const buttonGroup = [{
name: "编辑机票信息",
click: () => {
goEdit()
}
}]
function goEdit() {
util.goPage(`/pages/other/train-tickets/edit/edit?app=qunar&storageKey=${data.STORAGE_KEY}`)
}
const data = reactive({
navBar: {
bgColor: 'transparent'
},
trainTickets: {
"orderInfo": {
"orderNo": "EJ66223536",
"price": "4440"
},
"ticketInfo": {
"departureTime": "01-01 09:19",
"departureStation": "北京南",
"arrivalTime": "01-01 14:04",
"arrivalStation": "上海虹桥",
"trainNo": "G905",
"duration": "4时45分",
"date": "2026.01.01",
"weekDay": "星期四",
"gate": "6A、7A进站检票口,6B、7B进站检票",
"trainName": "复兴号"
},
"passengerList": [
{
"name": "张元英",
"type": "成人票",
"seatType": "商务座",
"carriage": "01",
"seatNo": "03C",
"idType": "外国护照KR",
"idNumber": "KR123456789",
"price": "2110",
"status": "已支付",
"isMe": true,
"points": 9632
}
]
},
STORAGE_KEY: 'qunarTrainTickets'
})
let { trainTickets } = toRefs(data)
onShow(() => {
const stored = uni.getStorageSync(data.STORAGE_KEY)
if (stored) {
Object.assign(data.trainTickets, stored)
}
})
onPageScroll((e) => {
data.navBar.bgColor = e.scrollTop > 40 ? '#F2F5F9' : 'transparent';
})
const computeSeatNo = (seatNo) => {
if (seatNo.includes('C') || seatNo.includes('F')) {
return '靠窗';
} else if (seatNo.includes('A') || seatNo.includes('D')) {
return '过道';
}
return '';
}
const formatDateTime = (timeStr, dateStr) => {
if (!timeStr) return '';
const parts = timeStr.split(' ');
const md = parts[0];
if (!md) return '';
const mdParts = md.split('-');
let year, month, day;
if (mdParts.length === 3) {
year = mdParts[0];
month = mdParts[1];
day = mdParts[2];
} else if (mdParts.length === 2) {
month = mdParts[0];
day = mdParts[1];
year = new Date().getFullYear();
if (dateStr) {
year = dateStr.split('.')[0];
}
} else {
return '';
}
const dateObj = new Date(Number(year), Number(month) - 1, Number(day));
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
const week = isNaN(dateObj.getTime()) ? '' : weekDays[dateObj.getDay()];
return `${month}${day}${week}`;
}
</script>
<style>
@import '/common/global.css';
page {
background-color: #F2F5F9;
}
</style>
<style lang="less" scoped>
::v-deep .uni-navbar__header-btns-right {
width: auto !important;
}
.ml-11 {
margin-left: 22rpx;
}
.ml-7 {
margin-left: 14rpx;
}
.ml-25 {
margin-left: 50rpx;
}
.mt-7 {
margin-top: 14rpx;
}
.mt-11 {
margin-top: 22rpx;
}
.ml-3 {
margin-left: 6rpx;
}
.mt-19 {
margin-top: 38rpx;
}
.section {
background: linear-gradient(180deg, #CDF2F6 0%, #F2F5F9 100%);
.image_5 {
width: 44rpx;
height: 44rpx;
}
.group_2 {
.group_3 {
padding-left: 23.26rpx;
padding-right: 4.64rpx;
.image_4 {
width: 28rpx;
height: 28rpx;
}
}
.font {
font-size: 18rpx;
line-height: 16.88rpx;
color: #363636;
}
.text {
line-height: 16.68rpx;
}
.text_2 {
line-height: 16.74rpx;
}
}
.group_4 {
padding-left: 20rpx;
padding-bottom: 30rpx;
.text_5 {
color: #363636;
font-size: 44rpx;
font-weight: 700;
line-height: 41.18rpx;
}
.text-wrapper {
border-radius: 25rpx;
width: 106rpx;
height: 24px;
border: solid 0.5px #28c2dd;
.text_6 {
color: #28c2dd;
font-size: 24rpx;
line-height: 50rpx;
}
}
.section_2 {
padding: 19.76rpx 16.2rpx 13.54rpx;
background: linear-gradient(270deg, rgba(255, 255, 255, 0) 0%, #FFFFFF 100%);
border-radius: 16rpx 0rpx 0rpx 16rpx;
width: 160rpx;
height: 94rpx;
.group_5 {
line-height: 20.46rpx;
.font_2 {
font-size: 26rpx;
line-height: 21.14rpx;
color: #fb8517;
}
.text_3 {
font-size: 28rpx;
line-height: 19.9rpx;
}
.text_4 {
font-size: 28rpx;
line-height: 20.46rpx;
}
}
.image_7 {
width: 20rpx;
height: 20rpx;
}
.image_8 {
margin-right: 11.8rpx;
}
.text_7 {
margin-left: 2.52rpx;
margin-top: 3.84rpx;
color: #363636;
font-size: 20rpx;
line-height: 18.62rpx;
}
}
}
}
.main-box {
padding: 10rpx 16rpx;
padding-bottom: 10rpx;
padding-bottom: calc(10rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(10rpx + env(safe-area-inset-bottom));
.ml-25 {
margin-left: 50rpx;
}
.section_3 {
padding: 20rpx 18rpx 0;
background-color: #ffffff;
border-radius: 20rpx;
.view {
margin: 0 14rpx;
padding-top: 2.96rpx;
.font_5 {
font-size: 26rpx;
line-height: 21.14rpx;
color: #666666;
}
.text_8 {
font-size: 24rpx;
}
.group_9 {
line-height: 21.76rpx;
height: 21.76rpx;
overflow: hidden;
overflow-x: auto;
flex: 1;
text-align: right;
/* 隐藏横向滚动条,但保留滑动效果 */
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
.font_6 {
white-space: nowrap;
font-size: 22rpx;
line-height: 24rpx;
font-weight: 500;
color: #4FB26E;
}
}
.equal-division {
align-self: flex-start;
margin-top: 21rpx;
width: 100%;
.group_10 {
flex-shrink: 0;
.font_3 {
font-size: 26rpx;
line-height: 25.92rpx;
color: #363636;
}
.text_16 {
font-size: 28rpx;
line-height: 24rpx;
}
.font_8 {
font-size: 52rpx;
color: #363636;
}
.text_17 {
font-size: 24rpx;
line-height: 22.04rpx;
}
.image_9 {
width: 234rpx;
height: 44rpx;
}
.text_18 {
font-size: 28rpx;
line-height: 24rpx;
text-align: right;
width: 100%;
}
}
.group_28 {
padding: 16rpx 0;
width: 200rpx;
}
.group_29 {
padding: 18.84rpx 0 3.54rpx;
}
.group_26 {
padding: 16rpx 0;
width: 200rpx;
}
}
.group_11 {
.pos {
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
.text_19 {
font-size: 28rpx;
line-height: 25.96rpx;
}
}
.font_19 {
font-size: 26rpx;
line-height: 28rpx;
color: #666666;
}
.text_21 {
font-size: 24rpx;
line-height: 17.54rpx;
}
.image_10 {
width: 30rpx;
height: 30rpx;
}
.pos_2 {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
.text_20 {
font-size: 28rpx;
line-height: 25.82rpx;
}
}
.image_4 {
width: 28rpx;
height: 28rpx;
}
.font_9 {
font-size: 26rpx;
line-height: 25.92rpx;
font-weight: 700;
color: #363636;
}
}
}
.group_12 {
padding: 38rpx 0 0;
.section_1 {
padding: 28rpx 10.58rpx 22rpx 19.1rpx;
background-color: #f9fbfc;
border-radius: 16rpx;
margin-bottom: 24rpx;
.text_22 {
font-size: 24rpx;
line-height: 22.22rpx;
}
.text-wrapper_8 {
background-color: #9db2bd;
border-radius: 8rpx;
width: 56rpx;
height: 32rpx;
.text_23 {
line-height: 32rpx;
}
}
.text_51 {
margin-top: 2.92rpx;
}
.text_25 {
margin-right: 4.38rpx;
font-size: 28rpx;
}
.group_27 {
padding: 0 8.98rpx;
.text_50 {
font-size: 28rpx;
}
}
}
.font_10 {
font-size: 26rpx;
line-height: 23.74rpx;
font-weight: 700;
color: #363636;
}
.font_11 {
font-size: 26rpx;
line-height: 21.14rpx;
font-weight: 500;
color: #999999;
}
.font_4 {
font-size: 20rpx;
color: #ffffff;
}
.font_12 {
font-size: 32rpx;
line-height: 29.6rpx;
color: #363636;
font-weight: 500;
}
.font_13 {
font-size: 26rpx;
line-height: 19rpx;
font-weight: 500;
color: #999999;
}
.font_14 {
font-size: 26rpx;
line-height: 25.92rpx;
color: #545356;
}
.font_15 {
font-size: 26rpx;
line-height: 25.92rpx;
font-weight: 700;
color: #f5882c;
}
.font_16 {
font-size: 26rpx;
line-height: 21.14rpx;
font-weight: 700;
color: #363636;
}
.action-btn {
background-color: #ffffff;
border-radius: 30rpx;
width: 104rpx;
height: 60rpx;
border: solid 1rpx #e0e4e3;
text {
font-size: 24rpx;
}
}
}
}
}
</style>

View File

@ -1,8 +1,8 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<watermark dark="light" source="uni_alipay_other_groupChat" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_groupChat')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
@ -64,39 +64,43 @@
<view class="control-buttons">
<!-- 麦克风 -->
<view class="control-item">
<view class="control-btn" :class="{ active: videoData.micOn }" @click="changeInfo('micOn')">
<image class="control-icon"
:src="videoData.micOn ? '/static/image/other/video-call/mic-on.png' : '/static/image/other/video-call/mic-off.png'">
</image>
</view>
<image class="control-btn" :class="{ active: videoData.micOn }" @click="changeInfo('micOn')"
:src="videoData.micOn ? '/static/image/other/video-call/mic-on.png' : '/static/image/other/video-call/mic-off.png'">
</image>
<!-- <view class="" :class="{ active: videoData.micOn }" >
</view> -->
<text class="control-label">{{ videoData.micOn ? '麦克风已开' : '麦克风已关' }}</text>
</view>
<!-- 扬声器 -->
<view class="control-item">
<view class="control-btn" :class="{ active: videoData.speakerOn }" @click="changeInfo('speakerOn')">
<image class="control-icon"
:src="videoData.speakerOn ? '/static/image/other/video-call/speaker-on.png' : '/static/image/other/video-call/speaker-off.png'">
</image>
</view>
<image class="control-btn" :class="{ active: videoData.speakerOn }" @click="changeInfo('speakerOn')"
:src="videoData.speakerOn ? '/static/image/other/video-call/speaker-on.png' : '/static/image/other/video-call/speaker-off.png'">
</image>
<!-- <view class="" :class="{ active: videoData.speakerOn }" @click="changeInfo('speakerOn')">
</view> -->
<text class="control-label">{{ videoData.speakerOn ? '扬声器已开' : '扬声器已关' }}</text>
</view>
<!-- 摄像头 -->
<view class="control-item">
<view class="control-btn" :class="{ active: videoData.cameraOn }" @click="changeInfo('cameraOn')">
<image class="control-icon"
:src="videoData.cameraOn ? '/static/image/other/video-call/camera-on.png' : '/static/image/other/video-call/camera-off.png'">
</image>
</view>
<image class="control-btn" :class="{ active: videoData.cameraOn }" @click="changeInfo('cameraOn')"
:src="videoData.cameraOn ? '/static/image/other/video-call/camera-on.png' : '/static/image/other/video-call/camera-off.png'">
</image>
<!-- <view class="control-btn" :class="{ active: videoData.cameraOn }" @click="changeInfo('cameraOn')">
</view> -->
<text class="control-label">{{ videoData.cameraOn ? '摄像头已开' : '摄像头已关' }}</text>
</view>
</view>
<!-- 挂断按钮 -->
<view class="hangup-btn" @click="hangup">
<image class="hangup-icon" src="/static/image/other/video-call/hangup.png"></image>
</view>
<image class="hangup-btn" @click="hangup" src="/static/image/other/video-call/hangup.png"></image>
<!-- <view class="hangup-btn" @click="hangup">
</view> -->
</view>
</view>
@ -142,11 +146,10 @@
<script setup>
import NavBar from "@/components/nav-bar/nav-bar.vue"
import { ref, toRefs, onMounted, onUnmounted, reactive, computed, getCurrentInstance } from 'vue'
import { toRefs, onMounted, onUnmounted, reactive, computed, getCurrentInstance } from 'vue'
import { onLoad, onShow, onHide } from '@dcloudio/uni-app'
import { util } from '@/utils/common.js'
const {
appContext,
proxy
} = getCurrentInstance();
@ -681,7 +684,7 @@ const removeFile = (filePath) => {
// 使 uni.removeSavedFile ( uni.saveFile )
uni.removeSavedFile({
filePath: filePath,
success: (res) => {
success: () => {
console.log('✅ uni.removeSavedFile 删除成功:', filePath)
},
fail: (err) => {
@ -718,7 +721,7 @@ const handleTouchStart = (event, index) => {
}
// -
const handleTouchMove = (event, index) => {
const handleTouchMove = (event) => {
if (!data.dragState.isDragging) return
const touch = event.touches[0]
@ -731,7 +734,7 @@ const handleTouchMove = (event, index) => {
}
// -
const handleTouchEnd = (event, index) => {
const handleTouchEnd = () => {
//
if (data.dragState.longPressTimer) {
clearTimeout(data.dragState.longPressTimer)

179
pages/shopping/index.vue Normal file
View File

@ -0,0 +1,179 @@
<template>
<view class="container">
<nav-bar title="购物平台" bgColor="#F0F4F9"></nav-bar>
<view class="menu-container">
<view class="card" v-for="(item, index) in menuList" :key="index"
:style="{ background: `linear-gradient(to bottom, ${item.bgColor[0]} 0%, ${item.bgColor[1]} 80%)` }">
<text class="card-title">{{ item.name }}</text>
<view class="icon-wrapper">
<view class="icon-shadow" :style="{ background: item.shadowColor }"></view>
<image class="icon-img" :src="`/static/image/shopping/${item.icon}.png`" mode="aspectFit"></image>
</view>
<view class="btn" :style="{ background: item.btnBg }" @click="goTo(item.url)">立即进入</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
reactive,
getCurrentInstance
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app'
const {
proxy
} = getCurrentInstance();
const data = reactive({
phone: ''
})
onLoad((options) => {
if (options.phone) {
data.phone = options.phone
}
proxy.$apiUserEvent('all', {
type: 'event',
key: 'shopping',
prefix: '.uni.other.',
value: '购物'
})
})
const menuList = ref([{
name: "京东",
icon: "jingdong",
bgColor: ["#FFE0DF", "#FFFFFF"],
btnBg: "#FF4848",
shadowColor: "#FF4848",
url: "/pages/shopping/jingdong/list-index"
}, {
name: "淘宝",
icon: "taobao",
bgColor: ["#FFF0DA", "#FFFFFF"],
btnBg: "#FF953C",
shadowColor: "#FF953C",
url: "/pages/shopping/taobao/list-index"
}, {
name: "拼多多",
icon: "pinduoduo",
bgColor: ["#FFE0DF", "#FFFFFF"],
btnBg: "#FF4848",
shadowColor: "#FF4848",
url: "/pages/shopping/pdd/list-index"
}, {
name: "快手",
icon: "kuaishou",
bgColor: ["#FFF0DA", "#FFFFFF"],
btnBg: "#FF953C",
shadowColor: "#FF953C",
url: ""
}, {
name: "抖音",
icon: "douyin",
bgColor: ["#FAE5FF", "#FFFFFF"],
btnBg: "#393939",
shadowColor: "#D15CFF",
url: ""
}, {
name: "得物",
icon: "dewu",
bgColor: ["#FAE5FF", "#FFFFFF"],
btnBg: "#393939",
shadowColor: "#D15CFF",
url: ""
}]);
const goTo = (url) => {
if (url) {
uni.navigateTo({
url: url
})
} else {
uni.showToast({
title: "开发中",
icon: "none"
})
}
}
</script>
<style>
page {
background-color: #F0F4F9;
}
</style>
<style lang="less" scoped>
.container {
min-height: 100vh;
}
.menu-container {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 24rpx;
}
.card {
width: calc(50% - 14rpx);
border-radius: 28rpx 28rpx 28rpx 28rpx;
margin-bottom: 24rpx;
display: flex;
flex-direction: column;
align-items: center;
padding-top: 48rpx;
padding-bottom: 40rpx;
box-sizing: border-box;
border: 1px solid #ffffff;
}
.card-title {
font-size: 28rpx;
color: #1A1A1A;
font-weight: 500;
}
.icon-wrapper {
margin-top: 40rpx;
margin-bottom: 62rpx;
position: relative;
width: 116rpx;
height: 116rpx;
display: flex;
justify-content: center;
align-items: center;
}
.icon-img {
width: 116rpx;
height: 116rpx;
z-index: 2;
}
.icon-shadow {
position: absolute;
bottom: -10rpx;
width: 100rpx;
height: 8rpx;
border-radius: 50%;
filter: blur(5px);
z-index: 1;
opacity: 0.6;
}
.btn {
padding: 0 20rpx;
height: 56rpx;
line-height: 56rpx;
border-radius: 16rpx 16rpx 16rpx 16rpx;
color: #fff;
font-size: 24rpx;
text-align: center;
font-weight: 500;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,555 @@
<template>
<view class="list-card-container" v-if="item" @click="handleCardClick(item.id)" @touchstart="handleTouchStart"
@touchmove="handleTouchMove" @touchend="handleTouchEnd" @touchcancel="handleTouchEnd">
<!-- 头部店铺信息和状态 -->
<view class="header-box flex-between flex-align-center">
<view class="shop-info flex-align-center">
<view class="shop-logo-placeholder flex-center" v-if="item.shop.logo">
<image class="brand-img" :src="item.shop.logo">
</image>
</view>
<text class="shop-name">{{ item.shop?.name }}</text>
<image v-if="item.shop?.hasBrandAuth" class="brand-img"
src="/static/image/shopping/pdd/pinpairenzheng.png">
</image>
<image class="flag-img" v-if="item.shop?.hasFlagship" src="/static/image/shopping/pdd/qijiandian.png">
</image>
<image v-if="item.shop?.has618" style="margin-left: 8rpx;height: 28rpx;" class="flagship-tag"
src="/static/image/shopping/pdd/618.png" mode="heightFix"></image>
<uni-icons style="margin-top: 4rpx;" type="right" color="#999999" size="12"></uni-icons>
</view>
<view class="status-text" v-if="item.orderType === 'wait_pay'">待支付</view>
<view class="status-text" v-if="item.orderType === 'wait_recv'">待收货</view>
<view class="status-text" v-if="item.orderType === 'received'">交易成功</view>
</view>
<!-- 商品主要内容 -->
<view class="product-box flex" v-for="(product, pIndex) in item.products" :key="pIndex">
<view class="product-img-box">
<image class="product-img" :src="product.img" mode="aspectFill"></image>
</view>
<view class="product-content flex-1">
<view class="title-box">
<text class="brand-tag" v-if="item.shop?.hasBrandAuth">品牌</text>
<text class="title-text">{{ product.title }}</text>
</view>
<view class="spec-text text-ellipsis">{{ product.spec }}</view>
<view class="tags-box flex-align-center">
<view class="green-tag" v-for="(tag, index) in product.serviceTags" :key="index">{{ tag }}</view>
</view>
</view>
<view class="price-box flex-column align-end">
<text class="price">¥
<text style="font-size: 30rpx;">{{ Number(product.price).toFixed(1).split('.')[0] + '.' }}</text>
<text style="font-size: 22rpx;">{{ Number(product.price).toFixed(1).split('.')[1] }}</text>
</text>
<text class="count">x{{ product.count }}</text>
</view>
</view>
<view>
<!-- 价格汇总 -->
<view class="summary-box flex-between flex-align-center">
<view class="avatar-group flex-align-center">
<image class="avatar" v-for="(avatar, idx) in item.avatars" :key="idx" :src="avatar"
mode="aspectFill">
</image>
</view>
<view class="total-price flex-align-center">
<text class="label">{{ item.orderType === 'wait_pay' ? '应付' : '实付' }}:</text>
<text class="symbol">¥</text>
<text class="amount">{{ item.payment.totalAmount }}</text>
<text class="freight">{{ item.payment.freightDesc }}</text>
</view>
</view>
<!-- 按钮组 -->
<view class="action-box flex-end flex-align-center">
<template v-if="item.orderType === 'wait_pay'">
<view class="btn plain">取消订单</view>
<view class="btn primary">去支付</view>
</template>
<template v-if="item.orderType === 'wait_recv'">
<text class="more-btn">更多</text>
<view class="btn plain">申请退款</view>
<view class="btn plain">追加评价</view>
<view class="btn primary with-badge">
确认收货
<!-- <view class="badge" v-if="item.collectCount">{{ item.collectCount }}人收藏</view> -->
</view>
</template>
<template v-if="item.orderType === 'received'">
<text class="more-btn">更多</text>
<view class="btn plain">申请退款</view>
<view class="btn plain">追加评价</view>
<view class="btn primary with-badge">
再次拼单
<!-- <view class="badge" v-if="item.collectCount">{{ item.collectCount }}人收藏</view> -->
</view>
</template>
</view>
<!-- 物流状态栏 -->
<view class="delivery-box flex-align-center" v-if="item.deliveryStatus && item.orderType !== 'received'">
<view class="truck-icon flex-center">
<image style="width:32rpx;height: 32rpx;" src="/static/image/shopping/pdd/yunshuzhong.png"></image>
</view>
<view class="delivery-content text-ellipsis flex-1">
<template v-if="item.orderType === 'wait_pay'">
<text class="green-text">{{ item.deliveryStatus }}</text>
<text class="green-text">{{ item.statusDesc }}</text>
</template>
<template v-if="item.orderType === 'wait_recv'">
<text class="green-text">{{ item.deliveryStatus }}</text>
<text class="gray-text">{{ item.logistics.desc }}</text>
</template>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue';
const props = defineProps({
item: {
type: Object,
default: () => ({})
}
});
const emit = defineEmits(['longpress']);
let longPressTimer = null;
let isLongPressTriggered = false;
let startX = 0;
let startY = 0;
const handleTouchStart = (e) => {
isLongPressTriggered = false;
const touch = e.touches[0] || e.changedTouches[0];
if (touch) {
startX = touch.clientX;
startY = touch.clientY;
}
longPressTimer = setTimeout(() => {
isLongPressTriggered = true;
uni.vibrateShort();
emit('longpress', e);
}, 1200); // 1200ms
};
const handleTouchMove = (e) => {
if (longPressTimer) {
const touch = e.touches[0] || e.changedTouches[0];
if (touch) {
const moveX = touch.clientX;
const moveY = touch.clientY;
// 15px
if (Math.abs(moveX - startX) > 15 || Math.abs(moveY - startY) > 15) {
clearTimeout(longPressTimer);
longPressTimer = null;
}
}
}
};
const handleTouchEnd = () => {
if (longPressTimer) {
clearTimeout(longPressTimer);
longPressTimer = null;
}
};
const toDetail = (id) => {
uni.navigateTo({
url: `/pages/shopping/pdd/order-detail/order-detail?id=${id}`
});
};
const handleCardClick = (id) => {
if (isLongPressTriggered) return;
toDetail(id);
};
</script>
<style lang="less" scoped>
.list-card-container {
background-color: #ffffff;
margin-bottom: 16rpx;
padding: 22rpx 0;
box-sizing: border-box;
padding-bottom: 4rpx;
}
.flex-between {
display: flex;
justify-content: space-between;
}
.flex-align-center {
display: flex;
align-items: center;
}
.flex-end {
display: flex;
justify-content: flex-end;
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.flex {
display: flex;
}
.flex-column {
display: flex;
flex-direction: column;
}
.flex-1 {
flex: 1;
}
.text-ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
/* Header */
.header-box {
padding: 0 24rpx;
margin-bottom: 24rpx;
.shop-logo-placeholder {
flex-shrink: 0;
width: 40rpx;
height: 40rpx;
border-radius: 4rpx;
margin-right: 6rpx;
overflow: hidden;
}
.shop-name {
font-weight: 400;
font-size: 28rpx;
color: #1A1A1A;
line-height: 24rpx;
// margin-right: 8rpx;
}
.brand-img {
width: 94rpx;
height: 28rpx;
margin-left: 8rpx;
}
.flag-img {
width: 74rpx;
height: 28rpx;
margin-left: 8rpx;
}
.status-text {
font-weight: 400;
font-size: 28rpx;
color: #DF2E26;
line-height: 28rpx;
}
}
/* Product Info */
.product-box {
padding: 0 24rpx;
padding-bottom: 14rpx;
// border-bottom: 1rpx solid #EFEFEF;
.product-img-box {
width: 148rpx;
height: 148rpx;
margin-right: 14rpx;
border-radius: 4rpx;
overflow: hidden;
background-color: #f5f5f5;
flex-shrink: 0;
.product-img {
width: 100%;
height: 100%;
}
}
.product-content {
width: 0;
.title-box {
width: 100%;
font-size: 26rpx;
color: #1A1A1A;
line-height: 36rpx;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
.brand-tag {
background-color: #252525;
color: #fff;
font-size: 22rpx;
line-height: 22rpx;
padding: 2rpx 8rpx;
border-radius: 4rpx;
margin-right: 8rpx;
position: relative;
top: -2rpx;
}
.title-text {}
}
.spec-text {
font-size: 26rpx;
color: #999999;
line-height: 26rpx;
margin-top: 6rpx;
margin-bottom: 14rpx;
}
.tags-box {
/* Overriding image style */
.green-tag {
display: inline-flex;
align-items: center;
padding: 0 4rpx;
border-radius: 4rpx;
margin: 0 4rpx;
font-weight: 400;
height: 30rpx;
color: #fff;
background-color: #2BB40C;
font-size: 22rpx;
line-height: 22rpx;
border: none;
}
}
}
.price-box {
margin-left: 10rpx;
flex-shrink: 0;
flex-direction: column;
align-items: flex-end;
.price {
font-size: 24rpx;
color: #999;
}
.count {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
}
.align-end {
align-items: flex-end;
}
}
}
/* Summary */
.summary-box {
position: relative;
margin-bottom: 16rpx;
padding: 0 16rpx;
padding-bottom: 14rpx;
padding-top: 10rpx;
&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
background-color: #EFEFEF;
}
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
background-color: #EFEFEF;
}
.avatar-group {
position: relative;
width: 110rpx;
height: 44rpx;
.avatar {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
border: 2rpx solid #FFFFFF;
position: absolute;
}
.avatar:nth-child(1) {
left: 0;
z-index: 4;
}
.avatar:nth-child(2) {
left: 34rpx;
z-index: 3;
}
.avatar:nth-child(3) {
left: 68rpx;
z-index: 2;
}
.avatar:nth-child(4) {
left: 102rpx;
z-index: 1;
}
}
.total-price {
display: flex;
align-items: baseline;
font-size: 24rpx;
color: #1A1A1A;
.label {
margin-right: 4rpx;
}
.symbol {
font-size: 24rpx;
margin-right: 4rpx;
}
.amount {
font-size: 34rpx;
}
.freight {
font-size: 26rpx;
margin-left: 8rpx;
}
}
}
/* Action Buttons */
.action-box {
margin-bottom: 16rpx;
padding: 0 16rpx;
.more-btn {
font-weight: 400;
font-size: 28rpx;
color: #1A1A1A;
line-height: 28rpx;
margin-right: 46rpx;
}
.btn {
min-width: 138rpx;
height: 58rpx;
border-radius: 8rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 28rpx;
margin-left: 14rpx;
padding: 0 12rpx;
box-sizing: border-box;
&.plain {
border: 1rpx solid #9E9E9E;
color: #1A1A1A;
background-color: #fff;
}
&.primary {
border: 1rpx solid #DF2E26;
background-color: #DF2E26;
color: #fff;
}
&.with-badge {
position: relative;
.badge {
position: absolute;
top: -18rpx;
right: -12rpx;
background-color: #FFE669;
color: #E01B13;
font-size: 18rpx;
padding: 2rpx 10rpx;
border-radius: 20rpx;
white-space: nowrap;
transform: scale(0.9);
z-index: 2;
}
}
}
}
/* Delivery Info */
.delivery-box {
background-color: #F5F5F5;
padding: 10rpx 16rpx;
border-radius: 4rpx;
margin: 0 16rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 16rpx;
.truck-icon {
margin-right: 6rpx;
}
.delivery-content {
display: flex;
align-items: center;
font-size: 24rpx;
white-space: nowrap;
.green-text {
color: #2BB40C;
}
.gray-text {
flex: 1;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: 100px;
color: #595959;
margin-left: 10rpx;
}
}
}
</style>

View File

@ -0,0 +1,650 @@
{
// "购物订单分类字段"
"shoppingClassfiy": {
"wait_pay": {
// type: 1,
"id": 1,
"orderType": "wait_pay",
"waitPayTime": "23:59:27",
"shop": {
"name": "",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true,
"has618": false
},
"products": [
{
"img": "",
"title": "",
"price": 0,
"count": 0,
"spec": "",
"serviceTags": []
}
],
"avatars": [],
"activePayIndex": 2,
"address": {
"name": "张三",
"phone": "13888888888",
"province": "上海市浦东新区汤臣一品1号楼",
"detail": ""
},
"payment": {
"totalAmount": "",
"discountText": "已优惠10元",
"freightDesc": "(免运费)"
},
"totalAmount": 0,
"freightText": "",
"collectCount": 0,
"deliveryStatus": "",
"statusDesc": "预计后天送达",
"logistics": {
"desc": "",
"time": ""
},
"orderInfo": [
{
"label": "订单编号:",
"value": "",
"hasCopy": true
},
{
"label": "下单时间:",
"value": ""
}
]
},
"wait_recv": {
// type: 2,
"id": 2,
"orderType": "wait_recv",
"shop": {
"name": "",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true,
"has618": false
},
"products": [
{
"img": "",
"title": "",
"price": 0,
"count": 0,
"spec": "",
"serviceTags": []
}
],
"avatars": [],
"address": {
"name": "张三",
"phone": "13888888888",
"province": "上海市浦东新区汤臣一品1号楼",
"detail": ""
},
"payment": {
"totalAmount": "",
"discountText": "",
"freightDesc": "(免运费)"
},
"totalAmount": 0,
"freightText": "",
"collectCount": 0,
"deliveryStatus": "运输中 预计明天送达",
"statusDesc": "商家已早于承诺时间发货",
"logistics": {
"desc": "申通快递:【南京市】快件已发往 上海市浦东新区",
"time": "今天 06:25:21"
},
"orderInfo": [
{
"label": "订单编号:",
"value": "260403-03369586325654",
"hasCopy": true
},
{
"label": "商品快照:",
"value": "发生交易争议时,可作为判断依据",
"hasArrow": true
},
{
"label": "支付方式:",
"value": "支付宝"
},
{
"label": "物流公司:",
"value": "申通快递"
},
{
"label": "快递单号:",
"value": "777445412223333326"
},
{
"label": "下单时间:",
"value": "2026-03-31 08:55:30"
},
{
"label": "拼单时间:",
"value": "2026-03-31 08:55:30",
"hasAvatars": true,
"hasArrow": true
},
{
"label": "发货时间:",
"value": "2026-03-31 11:11:52"
}
]
},
"received": {
// type: 3,
"id": 3,
"orderType": "received",
"shop": {
"name": "",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true,
"has618": false
},
"products": [
{
"img": "",
"title": "",
"price": 0,
"count": 0,
"spec": "",
"serviceTags": []
}
],
"avatars": [],
"address": {
"name": "张三",
"phone": "13888888888",
"province": "上海市浦东新区汤臣一品1号楼",
"detail": ""
},
"payment": {
"totalAmount": "",
"discountText": "",
"freightDesc": "(免运费)"
},
"totalAmount": 0,
"freightText": "",
"collectCount": 558,
"deliveryStatus": "",
"statusDesc": "商家已早于承诺时间发货",
"logistics": {
"desc": "申通快递:【上海市】订单已签收 上海市浦东新区汤臣一品",
"time": "今天 10:25:21"
},
"orderInfo": [
{
"label": "订单编号:",
"value": "260403-03369586325654",
"hasCopy": true
},
{
"label": "商品快照:",
"value": "发生交易争议时,可作为判断依据",
"hasArrow": true
},
{
"label": "支付方式:",
"value": "支付宝"
},
{
"label": "物流公司:",
"value": "申通快递"
},
{
"label": "快递单号:",
"value": "77735698745526"
},
{
"label": "下单时间:",
"value": "2026-03-31 08:55:30"
},
{
"label": "拼单时间:",
"value": "2026-03-31 08:55:30",
"hasAvatars": true,
"hasArrow": true
},
{
"label": "发货时间:",
"value": "2026-03-31 11:11:52"
}
]
}
},
// "购物类型"
"shoppingType": [
{
"label": "等待付款",
"key": "wait_pay"
},
{
"label": "待收货",
"key": "wait_recv"
},
{
"label": "完成-已签收",
"key": "received"
}
],
// "购物随机产品列表"
"shoppingProductList": [
{
"payment": {
"totalAmount": "35.9",
"discountText": "立省 6.59",
"freightDesc": "(免运费)"
},
"shop": {
"name": "多多数码旗舰店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/xianka.png",
"name": "",
"title": "华硕 TUF 5090显卡",
"spec": "5090旗舰算力 水冷夜神OC",
"serviceTags": [
"不支持7天无理由退货"
],
"price": "39849.00",
"count": "1"
}
]
},
{
"payment": {
"totalAmount": "35.9",
"discountText": "立省 6.59",
"freightDesc": "(免运费)"
},
"shop": {
"name": "Apple 产品旗舰店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/iphone.png",
"name": "Apple iPhone 17 Pro",
"title": "Apple/苹果 iPhone 17 Pro Max 1TB 星宇橙色",
"spec": "星宇橙色全网通5G",
"serviceTags": [
"免费上门退换",
"七天无理由退货"
],
"price": "12999.00",
"count": "1"
}
]
},
{
"payment": {
"totalAmount": "35.9",
"discountText": "立省 6.59",
"freightDesc": "(免运费)"
},
"shop": {
"name": "路易威登官方旗舰店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/lv.png",
"name": "LV",
"title": "路易威登 经典老花手提包",
"spec": "经典老花;手提包",
"serviceTags": [
"次日达",
"七天无理由退货"
],
"price": "23979.25",
"count": "1"
}
]
},
{
"payment": {
"totalAmount": "35.9",
"discountText": "立省 6.59",
"freightDesc": "(免运费)"
},
"shop": {
"name": "索尼SONY数码官方旗舰店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/sony.png",
"name": "Sony",
"title": "索尼SONYAlpha 7 IV 全画幅微单数码相机",
"spec": "单机身不含镜头约3300万有效像素",
"serviceTags": [
"厂家发货",
"七天无理由退货"
],
"price": "15499.00",
"count": "1"
}
]
},
{
"payment": {
"totalAmount": "35.9",
"discountText": "立省 6.59",
"freightDesc": "(免运费)"
},
"shop": {
"name": "五粮液自营店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/wuliangye.png",
"name": "五粮液",
"title": "五粮液 普五 第八代 浓香型白酒 52度 500ml*2瓶",
"spec": "500ml*2礼盒装浓香型",
"serviceTags": [
"七天无理由退货",
"假一赔十"
],
"price": "1099.00",
"count": "2"
}
]
},
{
"payment": {
"totalAmount": "35.9",
"discountText": "立省 6.59",
"freightDesc": "(免运费)"
},
"shop": {
"name": "戴森Dyson官方旗舰店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/dyson_v12.png",
"name": "戴森",
"title": "戴森(Dyson) V12 Detect Slim 无绳吸尘器",
"spec": "轻量版;激光探测;轻量吸尘器",
"serviceTags": [
"厂家发货",
"七天无理由退货"
],
"price": "3299.00",
"count": "1"
}
]
}
],
//
"defaultDataList": [
{
"id": "1780046536767",
"orderType": "received",
"shop": {
"name": "五粮液自营店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/wuliangye.png",
"title": "五粮液 普五 第八代 浓香型白酒 52度 500ml*2瓶",
"price": "1099.00",
"count": "2",
"spec": "500ml*2礼盒装浓香型",
"serviceTags": [
"七天无理由退货",
"假一赔十"
],
"name": "五粮液"
}
],
"avatars": [
"/static/image/shopping/pdd/avatars/avatars4.jpg",
"/static/image/shopping/pdd/avatars/avatars1.jpg",
"/static/image/shopping/pdd/avatars/avatars3.jpg",
"/static/image/shopping/pdd/avatars/avatars2.jpg"
],
"address": {
"name": "张三",
"phone": "13888888888",
"province": "上海市浦东新区汤臣一品1号楼",
"detail": ""
},
"payment": {
"totalAmount": 2198,
"discountText": "已优惠10元",
"freightDesc": "(免运费)"
},
"totalAmount": 0,
"freightText": "",
"collectCount": 558,
"deliveryStatus": "",
"statusDesc": "商家已早于承诺时间发货",
"logistics": {
"desc": "申通快递:【上海市】订单已签收 上海市浦东新区汤臣一品",
"time": "昨天 18:35:17"
},
"orderInfo": [
{
"label": "订单编号:",
"value": "265333-63850934463838",
"hasCopy": true
},
{
"label": "商品快照:",
"value": "发生交易争议时,可作为判断依据",
"hasArrow": true
},
{
"label": "支付方式:",
"value": "支付宝"
},
{
"label": "物流公司:",
"value": "申通快递"
},
{
"label": "快递单号:",
"value": "7726047284494359"
},
{
"label": "下单时间:",
"value": "2026-05-29 01:56:29"
},
{
"label": "拼单时间:",
"value": "2026-05-29 01:57:19",
"hasAvatars": true,
"hasArrow": true,
"avatars": []
},
{
"label": "发货时间:",
"value": "2026-05-29 20:57:19"
}
],
"waitPayTime": "22:55:05",
"activePayIndex": 2
},
{
"id": "1780046527800",
"orderType": "wait_recv",
"shop": {
"name": "索尼SONY数码官方旗舰店",
"logo": "",
"hasBrandAuth": false,
"hasFlagship": false,
"has618": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/sony.png",
"title": "索尼SONYAlpha 7 IV 全画幅微单数码相机",
"price": "15499.00",
"count": "1",
"spec": "单机身不含镜头约3300万有效像素",
"serviceTags": [
"厂家发货",
"七天无理由退货"
],
"name": "Sony"
}
],
"avatars": [
"/static/image/shopping/pdd/avatars/avatars4.jpg",
"/static/image/shopping/pdd/avatars/avatars1.jpg",
"/static/image/shopping/pdd/avatars/avatars2.jpg",
"/static/image/shopping/pdd/avatars/avatars3.jpg"
],
"address": {
"name": "张三",
"phone": "13888888888",
"province": "上海市浦东新区汤臣一品1号楼",
"detail": ""
},
"payment": {
"totalAmount": 15499,
"discountText": "已优惠10元",
"freightDesc": "(免运费)"
},
"totalAmount": 0,
"freightText": "",
"collectCount": 0,
"deliveryStatus": "运输中 预计明天送达",
"statusDesc": "商家已早于承诺时间发货",
"logistics": {
"desc": "顺丰快递:【南京市】快件已发往 上海市浦东新区",
"time": "今天 06:25:21"
},
"orderInfo": [
{
"label": "订单编号:",
"value": "262022-17081287841024",
"hasCopy": true
},
{
"label": "商品快照:",
"value": "发生交易争议时,可作为判断依据",
"hasArrow": true
},
{
"label": "支付方式:",
"value": "支付宝"
},
{
"label": "物流公司:",
"value": "申通快递"
},
{
"label": "快递单号:",
"value": "777445412223333326"
},
{
"label": "下单时间:",
"value": "2026-05-28 17:05:56"
},
{
"label": "拼单时间:",
"value": "2026-03-31 08:55:30",
"hasAvatars": true,
"hasArrow": true,
"avatars": []
},
{
"label": "发货时间:",
"value": "2026-03-31 11:11:52"
}
],
"waitPayTime": "01:45:17",
"activePayIndex": 2
},
{
"id": "1780046506911",
"orderType": "wait_pay",
"waitPayTime": "23:49:16",
"shop": {
"name": "Apple 产品旗舰店",
"logo": "",
"hasBrandAuth": true,
"hasFlagship": true
},
"products": [
{
"img": "/static/image/shopping/jingdong/product/shopping/iphone.png",
"title": "Apple/苹果 iPhone 17 Pro Max 1TB 星宇橙色",
"price": "12999.00",
"count": "1",
"spec": "星宇橙色全网通5G",
"serviceTags": [
"免费上门退换",
"七天无理由退货"
],
"name": "Apple iPhone 17 Pro"
}
],
"avatars": [
"/static/image/shopping/pdd/avatars/avatars1.jpg",
"/static/image/shopping/pdd/avatars/avatars2.jpg",
"/static/image/shopping/pdd/avatars/avatars4.jpg"
],
"activePayIndex": 2,
"address": {
"name": "张三",
"phone": "13888888888",
"province": "上海市浦东新区汤臣一品1号楼",
"detail": ""
},
"payment": {
"totalAmount": 12999,
"discountText": "已优惠10元",
"freightDesc": "(免运费)"
},
"totalAmount": 0,
"freightText": "",
"collectCount": 0,
"deliveryStatus": "",
"statusDesc": "预计后天送达",
"logistics": {
"desc": "",
"time": "今天 16:14:06"
},
"orderInfo": [
{
"label": "订单编号:",
"value": "267726-97143795493977",
"hasCopy": true
},
{
"label": "下单时间:",
"value": "2026-05-29 05:52:46"
}
]
}
]
}

View File

@ -0,0 +1,894 @@
<template>
<view class="page-container flex-column">
<view class="nav-bar">
<nav-bar v-if="!selectedImage" :buttonGroup="buttonGroup" @button-click="util.clickTitlePopupButton"
tipLayerType="pdd-shopping-list" isTipLayer tipLayerText="添加订单信息">
<!-- 使用作用域插槽自定义按钮渲染特别是switch的checked绑定 -->
<template #button="{ button }">
<view class="button flex-align-center flex-justify-center">
{{ button.name }}
<view @tap.stop>
<switch v-if="button.isSwitch" :checked="couponInfo[button.key]" @change="button.click"
style="transform: scale(0.7);"></switch>
</view>
</view>
</template>
<template v-slot:left>
<image style="width: 48rpx;height: 48rpx;" src="/static/image/shopping/pdd/back.png"
@click="util.goBack()">
</image>
</template>
<template v-slot:center>
<view class="title-box flex-center flex-column">
<view class="title">我的订单</view>
<view class="flex-center text">
<image style="width: 30rpx;height: 30rpx;" src="/static/image/shopping/pdd/safety.png">
</image>
<text>专属客服 ·闪电退换 ·售后无忧</text>
</view>
</view>
</template>
<template v-slot:right>
<image style="width: 48rpx;height: 48rpx;" src="/static/image/shopping/pdd/search.png"></image>
</template>
</nav-bar>
<nav-bar v-else title="拼图" bgColor="#EFEFEF" noBack @back="closeImage" isRightButton
@right-click="confirmImage">
</nav-bar>
</view>
<view class="tab-list flex-align-center">
<view class="tab-item" :class="{ active: tab.id == activeTab }" v-for="(tab, index) in tabList" :key="index"
@click="activeTab = tab.id">
<text>{{ tab.name }}</text>
</view>
</view>
<scroll-view v-if="activeTab === 4" class="tab2-box" scroll-x="true" :show-scrollbar="false">
<view class="tab2-content flex-align-center">
<view class="tab2-item flex-center" :class="{ active: activeTab2 === item.id }"
v-for="(item, index) in displayTab2List" :key="index" @click="handleTab2Click(item)">
<text>{{ item.name }} {{ item.count }}</text>
</view>
</view>
</scroll-view>
<scroll-view class="flex-1" style="height: 100px;" scroll-y="true" :scroll-into-view="scrollIntoId"
scroll-with-animation>
<view v-if="couponInfo.show618Coupon" class="flex-align-center coupon-banner">
<image style="width: 32rpx;height: 28rpx;margin-right: 10rpx;"
src="/static/image/shopping/pdd/coupon-logo.png">
</image>
<view class="flex-align-center flex-1">
<text class="red">618</text>
<text>降价正式开始再送</text>
<text class="red">{{ couponInfo.coupon618Amount || '188' }}</text>
<text>优惠券</text>
</view>
<uni-icons type="right" size="14" color="#B7B7B7"></uni-icons>
</view>
<image v-if="couponInfo.hasCoupon" style="width: 100%;height: 300rpx;"
:src="couponInfo.couponPath || '/static/image/shopping/pdd/coupon1.png'" mode="widthFix"></image>
<list-card v-for="(item, index) in filteredOrderList" :key="index" :item="item"
@longpress="handleLongPress($event, item)"></list-card>
<view v-if="filteredOrderList.length == 0" class="null-data-box flex-align-center">
<text>没找到订单试试</text>
<text style="color: #2686E4;">查看全部</text>
<text></text>
<text style="color: #2686E4;">切换账号</text>
</view>
<view class="bottom-fixed" style="height: 1000rpx;">
<view v-if="!selectedImage" class="upload-screenshot-box" @touchstart="handleTouchStart"
@touchend="handleTouchEnd">
<view v-if="!screenshotImage" class="upload-screenshot-content">
<image class="upload-screenshot" src="/static/image/common/upload-screenshot.png"
mode="aspectFit">
</image>
<view class="upload-screenshot-text">长按替换截图</view>
</view>
<view v-else class="screenshot-preview-box">
<image class="screenshot-preview" :src="screenshotImage" mode="widthFix"></image>
</view>
<!-- <image v-else class="w100 h100" :src="screenshotImage" mode="widthFix"></image> -->
</view>
<view v-else class="upload-screenshot-box scroll-image-box">
<scroll-view class="image-box" style="width: 100%;height: 100%;" scroll-y :show-scrollbar="false"
@scroll="onImageScroll">
<image class="crop-image-target" style="width:100%;" :src="selectedImage" mode="widthFix">
</image>
</scroll-view>
<view class="dashed-line-box">
<view class="dashed-line-text">我是分割线</view>
</view>
</view>
<canvas canvas-id="crop-canvas"
style="position: fixed; left: -9999px; width: 750rpx; height: 100vh; pointer-events: none;"></canvas>
<view id="bottom-anchor" style="height: 1px;"></view>
</view>
</scroll-view>
<!-- 上下文菜单和遮罩 -->
<view class="menu-mask" v-if="showMenu" @click="showMenu = false"></view>
<view class="context-menu flex-column" v-if="showMenu" :style="{ left: menuX + 'px', top: menuY + 'px' }">
<view class="menu-item" @click="handleEdit">编辑</view>
<view class="menu-item text-red" @click="handleDelete">删除</view>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_shopping_pdd" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_shopping_pdd')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
</liu-drag-button>
</view>
<!-- 优惠券弹窗 -->
<uni-popup ref="couponPopup" type="center">
<view class="popup-content"
style="width: 620rpx; background-color: #fff; border-radius: 24rpx; padding: 40rpx 30rpx; box-sizing: border-box;">
<view class="flex-align-center flex-between" style="margin-bottom: 30rpx;">
<text style="font-size: 34rpx; font-weight: 500; color: #1A1A1A;">618活动券展示</text>
<switch :checked="tempCouponInfo.show618Coupon"
@change="(e) => tempCouponInfo.show618Coupon = e.detail.value" color="#E02E24"
style="transform: scale(0.8);" />
</view>
<view v-if="tempCouponInfo.show618Coupon" class="flex-align-center flex-between"
style="margin-bottom: 30rpx;">
<text style="font-size: 30rpx; color: #1A1A1A;">设置618优惠券额度</text>
<input type="text" v-model="tempCouponInfo.coupon618Amount" placeholder="如 188"
style="text-align: right; border: 1px solid #ddd; border-radius: 8rpx; padding: 4rpx 16rpx; width: 140rpx; font-size: 28rpx;" />
</view>
<view style="height: 1px; background-color: #f5f5f5; margin-bottom: 30rpx;"></view>
<view class="flex-align-center flex-between" style="margin-bottom: 30rpx;">
<text style="font-size: 34rpx; font-weight: 500; color: #1A1A1A;">普通优惠券展示</text>
<switch :checked="tempCouponInfo.hasCoupon"
@change="(e) => tempCouponInfo.hasCoupon = e.detail.value" color="#E02E24"
style="transform: scale(0.8);" />
</view>
<scroll-view scroll-y v-if="tempCouponInfo.hasCoupon" class="coupon-options flex-column"
style="max-height: 55vh;">
<view class="coupon-item"
v-for="(img, idx) in ['/static/image/shopping/pdd/coupon1.png', '/static/image/shopping/pdd/coupon2.png', '/static/image/shopping/pdd/coupon3.png']"
:key="idx" @click="tempCouponInfo.couponPath = img"
:style="{ border: tempCouponInfo.couponPath === img ? '4rpx solid #E02E24' : '4rpx solid transparent', marginBottom: '20rpx', borderRadius: '16rpx', overflow: 'hidden', padding: '4rpx' }">
<image :src="img" style="width: 100%; display: block; border-radius: 12rpx;" mode="widthFix">
</image>
</view>
</scroll-view>
<view class="flex-center" style="margin-top: 40rpx;">
<view class="btn primary flex-center"
style="width: 100%; border-radius: 40rpx; padding: 22rpx 0; text-align: center; background-color: #E02E24; color: #fff; font-size: 32rpx; font-weight: 500;"
@click="saveCouponInfo">确认保存</view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, computed, getCurrentInstance } from 'vue';
import { onShow, onLoad } from '@dcloudio/uni-app';
import ListCard from './components/list-card/list-card.vue';
import { defaultDataList } from '@/pages/shopping/pdd/json/order.json';
import { util } from '@/utils/common.js';
const { proxy } = getCurrentInstance();
const activeTab = ref(0);
const scrollIntoId = ref('');
//
const couponInfo = ref({
show618Coupon: true,
coupon618Amount: '188',
hasCoupon: true,
couponPath: '/static/image/shopping/pdd/coupon1.png'
});
const tempCouponInfo = ref({
show618Coupon: true,
coupon618Amount: '188',
hasCoupon: true,
couponPath: '/static/image/shopping/pdd/coupon1.png'
});
const couponPopup = ref(null);
const openCouponPopup = () => {
tempCouponInfo.value = JSON.parse(JSON.stringify(couponInfo.value));
couponPopup.value.open();
};
const saveCouponInfo = () => {
couponInfo.value = JSON.parse(JSON.stringify(tempCouponInfo.value));
uni.setStorageSync('pddCouponInfo', couponInfo.value);
couponPopup.value.close();
uni.showToast({ title: '保存成功', icon: 'success' });
};
//
const buttonGroup = [
{
name: "新增购物订单",
click: () => {
uni.navigateTo({
url: '/pages/shopping/pdd/add-order/add-order'
});
}
}, {
name: "切换推荐商品图片",
click: () => {
scrollIntoId.value = '';
setTimeout(() => {
scrollIntoId.value = 'bottom-anchor';
}, 50);
setTimeout(() => {
chooseImage();
}, 400);
}
}, {
name: "设置优惠券",
click: () => {
openCouponPopup();
}
}
]
onLoad(() => {
proxy.$apiUserEvent('all', {
type: 'event',
key: 'shopping',
prefix: '.uni.other.',
value: '拼多多'
})
})
const tabList = ref([
{
name: '全部',
id: 0,
value: 'all'
},
{
name: '待付款',
id: 1,
value: 'wait_pay'
},
{
name: '拼团中',
id: 2,
value: 'grouping'
},
{
name: '打包中',
id: 3,
value: 'packing'
},
{
name: '待收货',
id: 4,
value: 'wait_recv'
},
{
name: '评价',
id: 5,
value: 'received'
},
]);
const activeTab2 = ref(0);
const tab2List = ref([
{ name: '全部', count: 1, id: 0 },
{ name: '已签收', count: 0, id: 1 },
{ name: '待取件', count: 0, id: 2 },
{ name: '派件中', count: 0, id: 3 },
{ name: '运输中', count: 1, id: 4 },
{ name: '其他', count: 0, id: 5 },
]);
const displayTab2List = computed(() => {
let sum = 0;
tab2List.value.forEach(item => {
if (item.id !== 0) {
sum += Number(item.count || 0);
}
});
return tab2List.value.map(item => {
if (item.id === 0) {
return { ...item, count: sum };
}
return item;
});
});
const saveTab2List = () => {
uni.setStorageSync('pdd_tab2List', tab2List.value);
uni.showToast({
title: '修改已保存',
icon: 'success'
});
};
const handleTab2Click = (item) => {
if (item.id !== 0) {
//
uni.showModal({
title: `修改[${item.name}]数量`,
editable: true,
placeholderText: '请输入数量',
success: (res) => {
if (res.confirm && res.content) {
const num = parseInt(res.content);
if (!isNaN(num) && num >= 0) {
const target = tab2List.value.find(t => t.id === item.id);
if (target) {
target.count = num;
saveTab2List();
}
}
}
}
});
} else {
//
activeTab2.value = item.id;
}
};
const orderList = ref([]);
const showMenu = ref(false);
const menuX = ref(0);
const menuY = ref(0);
const currentEditItem = ref(null);
const handleLongPress = (e, item) => {
const touch = e.touches && e.touches[0] ? e.touches[0] : (e.changedTouches && e.changedTouches[0] ? e.changedTouches[0] : e);
menuX.value = touch.clientX || touch.pageX || 100;
menuY.value = touch.clientY || touch.pageY || 100;
currentEditItem.value = item;
showMenu.value = true;
};
const handleEdit = () => {
showMenu.value = false;
if (currentEditItem.value) {
uni.navigateTo({
url: `/pages/shopping/pdd/add-order/add-order?isEdit=true&id=${currentEditItem.value.id}`
});
}
};
const removeLocalFile = (filePath) => {
if (!filePath) return;
if (filePath.startsWith('http') || filePath.startsWith('/static')) return;
uni.removeSavedFile({
filePath,
fail: () => { }
});
};
const handleDelete = () => {
showMenu.value = false;
if (currentEditItem.value) {
uni.showModal({
title: '提示',
content: '确定要删除该订单吗?',
success: (res) => {
if (res.confirm) {
const index = orderList.value.findIndex(item => item.id === currentEditItem.value.id);
if (index > -1) {
const deletedOrder = orderList.value[index];
//
if (deletedOrder.shop && deletedOrder.shop.logo) {
removeLocalFile(deletedOrder.shop.logo);
}
if (deletedOrder.products) {
deletedOrder.products.forEach(p => {
if (p.img) removeLocalFile(p.img);
if (p.image) removeLocalFile(p.image);
});
}
if (deletedOrder.avatars) {
deletedOrder.avatars.forEach(av => removeLocalFile(av));
}
orderList.value.splice(index, 1);
//
uni.setStorageSync('pddOrderList', orderList.value);
uni.showToast({ title: '删除成功', icon: 'success' });
}
}
}
});
}
};
const getOrderTime = (order) => {
if (order.orderInfo) {
const timeInfo = order.orderInfo.find(info => info.label && info.label.includes('下单时间'));
if (timeInfo && timeInfo.value) {
const timestamp = new Date(timeInfo.value.replace(/-/g, '/')).getTime();
if (!isNaN(timestamp)) {
return timestamp;
}
}
}
return Number(order.id) || 0;
};
const filteredOrderList = computed(() => {
const currentTab = tabList.value.find(t => t.id === activeTab.value);
let list = orderList.value;
if (currentTab && currentTab.value !== 'all') {
list = list.filter(item => item.orderType === currentTab.value);
}
return list.slice().sort((a, b) => getOrderTime(b) - getOrderTime(a));
});
onShow(() => {
const pddCouponInfo = uni.getStorageSync('pddCouponInfo');
if (pddCouponInfo) {
couponInfo.value = pddCouponInfo;
}
const isInitialized = uni.getStorageSync('isPddOrderListInitialized');
if (!isInitialized) {
uni.setStorageSync('pddOrderList', defaultDataList);
uni.setStorageSync('isPddOrderListInitialized', true);
orderList.value = defaultDataList;
} else {
const cacheList = uni.getStorageSync('pddOrderList');
orderList.value = cacheList || [];
}
// ()
let savedScreenshot = uni.getStorageSync('pdd_screenshot');
if (!savedScreenshot) {
savedScreenshot = '/static/image/shopping/pdd/pdd-bottom-product.png';
uni.setStorageSync('pdd_screenshot', savedScreenshot);
}
screenshotImage.value = savedScreenshot;
//
const cachedTab2 = uni.getStorageSync('pdd_tab2List');
if (cachedTab2) {
tab2List.value = cachedTab2;
}
// #ifdef APP-PLUS
util.setAndroidSystemBarColor('#FFFFFF')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500);
// #endif
});
//
const screenshotImage = ref('');
const selectedImage = ref('');
const scrollTop = ref(0);
let longPressTimer = null;
// -
const chooseImage = () => {
if (selectedImage.value) return;
uni.chooseImage({
count: 1,
sourceType: ['album'],
success: (res) => {
selectedImage.value = res.tempFilePaths[0];
}
});
};
//
const handleTouchStart = (e) => {
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.platform === 'ios' && systemInfo.safeAreaInsets?.bottom) {
const clientY = e.touches[0].clientY;
const windowHeight = systemInfo.windowHeight;
if (clientY > windowHeight - systemInfo.safeAreaInsets.bottom) {
return;
}
}
longPressTimer = setTimeout(() => {
uni.vibrateShort();
chooseImage();
}, 1200);
};
const handleTouchEnd = () => {
if (longPressTimer) {
clearTimeout(longPressTimer);
longPressTimer = null;
}
};
//
const onImageScroll = (e) => {
scrollTop.value = e.detail.scrollTop;
};
//
const confirmImage = () => {
uni.showLoading({
title: '处理中...'
})
const query = uni.createSelectorQuery().in(proxy)
//
query.select('.image-box').boundingClientRect()
query.select('.crop-image-target').boundingClientRect()
query.exec(res => {
if (!res[0] || !res[1]) {
uni.hideLoading()
return
}
console.log('rects', res)
const container = res[0] //
const image = res[1] //
// ( / /?)
// canvas
// canvas drawImage : img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight
//
uni.getImageInfo({
src: selectedImage.value,
success: (imgInfo) => {
const scale = imgInfo.width / image.width // 图片 原始宽 / 渲染宽
const sTop = scrollTop.value * scale // Y
const sHeight = container.height * scale //
// widthFix
const sWidth = imgInfo.width
// (使)
// canvasContext使 pixelRatio
// uni-app canvas-id (px)
// canvas
const canvasW = container.width
const canvasH = container.height
const ctx = uni.createCanvasContext('crop-canvas', proxy)
//
ctx.clearRect(0, 0, canvasW, canvasH)
//
// drawImage(imageResource, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
ctx.drawImage(
imgInfo.path,
0, sTop, sWidth, sHeight, //
0, 0, canvasW, canvasH //
)
ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'crop-canvas',
width: canvasW,
height: canvasH,
destWidth: sWidth, // 使,
destHeight: sHeight, // 使,
success: (res) => {
console.log('crop success (temp)', res
.tempFilePath)
//
uni.saveFile({
tempFilePath: res.tempFilePath,
success: (saveRes) => {
console.log('save success (saved)', saveRes.savedFilePath)
screenshotImage.value = saveRes.savedFilePath
selectedImage.value = '' //
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
}, 200)
//
uni.setStorageSync('pdd_screenshot', screenshotImage.value)
uni.hideLoading()
},
fail: (err) => {
console.error('saveFile fail', err)
uni.hideLoading()
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
})
},
fail: (err) => {
console.error(err)
uni.hideLoading()
uni.showToast({
title: '裁剪失败',
icon: 'none'
})
}
}, proxy)
})
},
fail: () => {
uni.hideLoading()
uni.showToast({
title: '图片加载失败',
icon: 'none'
})
}
})
})
}
//
const closeImage = () => {
selectedImage.value = '';
};
</script>
<style lang="less" scoped>
.page-container {
height: 100vh;
}
.nav-bar {
border-bottom: 1rpx solid #D2D2D2;
}
.title-box {
display: flex;
flex-direction: column;
.title {
font-weight: 400;
font-size: 30rpx;
color: #1A1A1A;
line-height: 30rpx;
}
.text {
font-weight: 400;
font-size: 24rpx;
color: #16B800;
line-height: 24rpx;
margin-top: 12rpx;
}
}
.tab-list {
height: 80rpx;
background-color: #ffff;
border-bottom: 0.5px solid #D2D2D2;
.tab-item {
font-weight: 400;
font-size: 30rpx;
color: #1A1A1A;
line-height: 80rpx;
white-space: nowrap;
margin: 0 22rpx;
&.active {
color: #E01B13;
font-weight: 500;
box-shadow: inset 0 -4rpx 0 0 #E01B13;
}
}
}
.tab2-box {
height: 78rpx;
background-color: #ffff;
border-bottom: 0.5px solid #D2D2D2;
width: 100%;
white-space: nowrap;
:deep(.uni-scroll-view::-webkit-scrollbar) {
display: none !important;
width: 0 !important;
height: 0 !important;
-webkit-appearance: none;
background: transparent;
}
.tab2-content {
display: inline-flex;
padding: 0 24rpx;
height: 100%;
}
.tab2-item {
flex-shrink: 0;
height: 52rpx;
padding: 0 16rpx;
background-color: #F8F8F8;
border-radius: 26rpx;
margin-right: 20rpx;
font-size: 26rpx;
color: #1A1A1A;
white-space: nowrap;
&.active {
background-color: #FCEFEF;
font-weight: 500;
color: #E01B13;
}
}
}
.null-data-box {
justify-content: center;
font-size: 28rpx;
color: #999999;
line-height: 28rpx;
padding: 42rpx 0;
}
.menu-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 998;
}
.context-menu {
position: fixed;
z-index: 999;
background-color: #fff;
border-radius: 8rpx;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
width: 200rpx;
overflow: hidden;
.menu-item {
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 28rpx;
color: #333;
border-bottom: 1rpx solid #F5F5F5;
&:last-child {
border-bottom: none;
}
&.text-red {
color: #DF2E26;
}
}
}
.bottom-fixed {
display: flex;
flex-direction: column;
.upload-screenshot-box {
width: 100%;
flex: 1;
min-height: 540rpx;
background-color: #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
position: relative;
.upload-screenshot {
width: 92rpx;
height: 92rpx;
}
.upload-screenshot-content {
width: 100%;
height: 100%;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
.upload-screenshot-text {
font-size: 36rpx;
color: #1777FF;
line-height: 50rpx;
margin-top: 16rpx;
}
}
.screenshot-preview-box {
width: 100%;
height: 100%;
display: flex;
align-items: flex-start;
// flex-direction: column;
justify-content: center;
.screenshot-preview {
width: 100%;
height: 100%;
}
}
.scroll-image-box {
width: 100%;
min-height: 0; // flex
// overflow: hidden; // scroll-view
position: relative;
}
.dashed-line-box {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border: 4rpx dashed #ffffff;
pointer-events: none;
.dashed-line-text {
height: 44rpx;
line-height: 44rpx;
width: 180rpx;
padding: 0 20rpx;
border-radius: 8rpx;
color: #1777FF;
font-size: 24rpx;
font-weight: 500;
background-color: #fff;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
}
}
.mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 999;
.mask-icon {
position: absolute;
top: 50%;
right: 52rpx;
transform: translateY(-25%);
width: 360rpx;
height: 360rpx;
}
}
}
}
.coupon-banner {
background-color: #FEF5B2;
padding: 20rpx 22rpx;
font-weight: 400;
font-size: 26rpx;
color: #1A1A1A;
line-height: 26rpx;
.red {
color: #DF2E26;
}
}
</style>
<style>
@import '@/common/main.css';
page {
background-color: #F5F5F5;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,858 @@
<template>
<view class="card-wrapper">
<view class="card-container" v-for="(item, index) in list" :key="index" @click.stop="clickItem(item)"
@touchstart="handleTouchStart($event, item)" @touchmove="handleTouchMove" @touchend="handleTouchEnd">
<!-- 顶部店铺和状态 -->
<view class="card-header">
<view class="shop-info flex-align-center">
<image class="taobao-logo" :class="{ shangou: item.orderType === 'waimai' }"
:src="`/static/image/shopping/taobao/${item.orderType === 'waimai' ? 'shangou' : item.shopType}.png`"
mode="heightFix">
</image>
<text class="shop-name flex-align-center"><text
:class="{ 'waimai-shop-name': item.orderType === 'waimai' }">{{ item.shopName || '淘宝店铺'
}}</text> <text v-if="item.orderType === 'waimai'" style="color: #7C8495;"
class="waimai-shop-name">{{
item.location }}</text></text>
<uni-icons style="margin-left: 4rpx;" type="right" size="12" color="#8C8C8C"></uni-icons>
<!-- <text class="iconfont icon-arrow-right arrow-icon">></text> -->
</view>
<view class="order-status" :class="[item.statusType, item.orderType]">
{{ item.statusText || getStatusText(item.statusType) }}
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list" @tap="goToDetail(item)">
<template v-if="item.orderType === 'waimai'">
<!-- 外卖单个商品 -->
<!-- :style="{ width: imgHeights[index + '-0'] ? imgHeights[index + '-0'] + 'px' : '', height: imgHeights[index + '-0'] ? imgHeights[index + '-0'] + 'px' : '' }" -->
<view v-if="item.goodsList.length === 1" class="goods-item">
<image class="goods-img" :src="item.goodsList[0].image || '/static/default-goods.png'"
mode="aspectFill">
</image>
<view class="goods-info" :id="'goods-info-' + index + '-0'">
<view class="goods-title-row">
<view class="good-info-box" :id="'title-box-' + index + '-0'">
<text class="goods-title">{{ getTruncatedTitle(item, index, item.goodsList[0], 0)
}}</text>
<view class="goods-spec" style="margin-top: 18rpx; color: #999;">默认</view>
<view class="goods-tags" v-if="item.goodsList[0].tags || item.goodsList[0].tag"
style="margin-top: 18rpx;">
<text class="tag">{{ item.goodsList[0].tags || item.goodsList[0].tag }}</text>
</view>
</view>
<view class="goods-price-box">
<text class="price-symbol wx-font-medium"></text>
<text class="price-val wx-font-medium">{{ item.goodsList[0].actualPrice ||
item.goodsList[0].price }}</text>
<text class="goods-count">x{{ item.goodsList[0].count || 1 }}</text>
</view>
</view>
</view>
</view>
<!-- 外卖多个商品 -->
<view v-else class="waimai-multi-goods flex-align-center"
style="margin-bottom: 18rpx; height: 136rpx;">
<scroll-view scroll-x class="waimai-images"
style="flex: 1; white-space: nowrap; overflow: hidden; margin-right: 16rpx; height: 100%;">
<view v-for="(goods, gIdx) in item.goodsList" :key="gIdx"
style="display: inline-block; position: relative; margin-right: 16rpx; width: 136rpx; height: 136rpx;">
<image :src="goods.image || '/static/default-goods.png'" mode="aspectFill"
style="width: 100%; height: 100%; border-radius: 12rpx; background-color: #F5F5F5;">
</image>
<text
style="position: absolute; bottom: 0; left: 0; background: rgba(0,0,0,0.4); color: #fff; font-size: 20rpx; padding: 2rpx 8rpx; border-radius: 0 12rpx 0 12rpx;">x{{
goods.count || 1 }}</text>
</view>
</scroll-view>
<view class="waimai-total-count"
style="font-size: 24rpx; color: #7C8495; flex-shrink: 0; font-weight: 400;">{{
getTotalCount(item.goodsList) }}</view>
</view>
</template>
<template v-else>
<view class="goods-item" v-for="(goods, gIdx) in item.goodsList" :key="gIdx">
<!-- :style="{ width: imgHeights[index + '-' + gIdx] ? imgHeights[index + '-' + gIdx] + 'px' : '', height: imgHeights[index + '-' + gIdx] ? imgHeights[index + '-' + gIdx] + 'px' : '' }" -->
<image class="goods-img"
:class="{ refunding: item.statusType === 'refunding' && currentFilter == 4 }"
:src="goods.image || '/static/default-goods.png'" mode="aspectFill">
</image>
<view class="goods-info" :id="'goods-info-' + index + '-' + gIdx">
<view class="goods-title-row">
<view class="good-info-box" :id="'title-box-' + index + '-' + gIdx">
<text class="goods-title">{{ getTruncatedTitle(item, index, goods, gIdx) }}</text>
<view class="goods-spec" v-if="goods.specs">{{ goods.specs }}</view>
</view>
<view class="goods-price-box">
<text class="price-symbol wx-font-medium"></text>
<text class="price-val wx-font-medium">{{ goods.price }}</text>
<text class="goods-count">x{{ goods.count || 1 }}</text>
</view>
</view>
<view class="goods-tags" v-if="goods.tags && currentFilter != 4">
<text class="tag">{{ goods.tags }}</text>
</view>
<!-- 单个商品的售后状态 -->
<view class="goods-evaluation" v-if="item.statusType == 'success'">
<view class="left">
<image style="width: 24rpx;height: 24rpx;margin-right: 8rpx;"
src='/static/image/shopping/taobao/pingjia.png'>
</image>
<text>评价将帮助70人</text>
</view>
<view class="right">
<image style="width: 24rpx;height: 24rpx;margin-right: 8rpx;"
src='/static/image/shopping/taobao/chaping.png'>
</image>
<text>差评</text>
<image style="width: 24rpx;height: 24rpx;margin-right: 8rpx;"
src='/static/image/shopping/taobao/zhongping.png'>
</image>
<text>中评</text>
<image style="width: 24rpx;height: 24rpx;margin-right: 8rpx;"
src='/static/image/shopping/taobao/haoping.png'>
</image>
<text>好评</text>
</view>
</view>
<!-- 单个商品的退款状态 -->
<view class="goods-evaluation" v-if="item.statusType == 'refunding' && currentFilter != 4">
<view class="left">
<image style="width: 24rpx;height: 24rpx;margin-right: 8rpx;"
src='/static/image/shopping/taobao/tuikuanchenggong.png'>
</image>
<text>退款成功</text>
</view>
<view class="desc">
{{ item.statusDesc }}
</view>
<uni-icons type="right" size="12" color="#7B7E8C"></uni-icons>
</view>
</view>
</view>
</template>
</view>
<!-- 各种提示信息 -->
<view v-if="item.orderType === 'waimai' && item.statusType === 'wait_recv'" class="tips-box waimai-delivery"
style="background-color: #F4F5F7; border-radius: 12rpx; padding: 16rpx 24rpx; margin-top: 16rpx; display: flex; align-items: center; height: auto;">
<image style="width: 36rpx; height: 36rpx; margin-right: 16rpx; flex-shrink: 0;"
src="/static/image/shopping/taobao/daocha.png"></image>
<view style="flex: 1; overflow: hidden;">
<view
style="font-size: 26rpx; color: #1A1A1A; font-weight: 500; display: flex; align-items: center;">
预计 <text style="color: #FF7824; margin: 0 8rpx;">{{ item.deliveryTime || '14:15-14:45' }}</text>
送达</view>
<view style="font-size: 22rpx; color: #999; margin-top: 6rpx;">下单时间 {{ item.orderInfo?.[8]?.value }}
</view>
</view>
<uni-icons type="right" size="12" color="#7C8495"></uni-icons>
</view>
<view v-else-if="item.statusType == 'wait_pay' && item.orderType != 'waimai'" class="tips-box daifukuan">
<image class="tip-icon" src="/static/image/shopping/taobao/daifukuan.png"></image>
<text class="text">订单即将关闭建议尽快付款</text>
</view>
<view v-else-if="item.statusType == 'wait_send'" class="tips-box daifahuo">
<image class="tip-icon" src="/static/image/shopping/taobao/daifahuo.png"></image>
<text class="title">待发货</text>
<text class="text">{{ item.statusDesc }}</text>
</view>
<view v-else-if="item.statusType == 'wait_recv'" class="tips-box yunshuzhong">
<image class="tip-icon"
:src="`/static/image/shopping/taobao/${item.isSignFor ? 'yiqianshou' : 'yunshuzhong'}.png`">
</image>
<text class="title">{{ item.isSignFor ? '已签收' : '运输中' }}</text>
<text class="text flex-1">{{ item.statusDesc2 || item.statusDesc1 }}</text>
<uni-icons v-if="item.statusDesc2" type="right" size="14" color="#7B7E8C"></uni-icons>
</view>
<view v-else-if="item.statusType == 'refunding' && props.currentFilter == 4" class="tips-box yunshuzhong">
<text class="title">退款成功</text>
<text class="text">{{ item.statusDesc }}</text>
</view>
<!-- 价格合计 -->
<view class="order-total">
<text class="total-label" :style="item.orderType === 'waimai' ? 'color: #7B7E8C;' : ''">{{
getTotalCount(item.goodsList) }}{{ item.orderType === 'waimai' ? ' (含包装/配送费)' : '商品' }}</text>
<text class="total-label" style="margin-left: 8rpx;">实付款</text>
<text class="total-symbol wx-font-medium"></text>
<text class="total-price wx-font-medium">{{ Number(
item.priceDetail?.actualPay || item.totalPrice || 0).toFixed(2).split(".")[0] }}</text>
<text class="total-price wx-font-medium" style="font-size: 28rpx;">.{{ Number(
item.priceDetail?.actualPay || item.totalPrice || 0).toFixed(2).split(".")[1] }}</text>
</view>
<!-- 底部按钮 -->
<view class="card-footer" v-if="getButtons(item.statusType, item.orderType).buttons.length > 0">
<view class="more-text" v-if="getButtons(item.statusType, item.orderType).moreText?.isShow"
:style="{ color: getButtons(item.statusType, item.orderType).moreText?.color || '#1A1A1A' }">
{{ getButtons(item.statusType, item.orderType).moreText?.text || '更多' }}
</view>
<view class="btn" :class="{ 'btn-primary': btn.primary }"
v-for="(btn, bIdx) in getButtons(item.statusType, item.orderType).buttons" :key="bIdx"
@tap.stop="handleAction(btn.action, item)" style="position: relative;">
<text v-if="btn.tag"
style="position: absolute; top: -18rpx; left: 50%; transform: translateX(-50%); background-color: #FF7824; color: #fff; font-size: 16rpx; padding: 2rpx 8rpx; border-radius: 16rpx; border-bottom-left-radius: 4rpx; white-space: nowrap;">{{
btn.tag }}</text>
{{ btn.text }}
</view>
</view>
</view>
</view>
</template>
<script setup>
import { defineProps, defineEmits, ref, watch, nextTick, getCurrentInstance, onMounted } from 'vue';
const props = defineProps({
list: {
type: Array,
default: () => []
},
currentFilter: {
type: String,
default: ''
}
});
const emit = defineEmits(['action', 'detail', 'click-item', 'longpress']);
const instance = getCurrentInstance();
const imgHeights = ref({});
const titleMap = ref({}); // key: 'itemIdx-goodsIdx'
const longPressTimer = ref(null);
const touchStartPos = ref({ x: 0, y: 0 });
/**
* shopping-card.vue 对齐的字符权重截断算法
*/
const calcFitTitle = (originalTitle, containerWidth) => {
const fontSize = uni.upx2px(26);
let currentWidth = 0;
let cutIndex = 0;
let found = false;
for (let i = 0; i < originalTitle.length; i++) {
const char = originalTitle[i];
const charCode = char.charCodeAt(0);
let weight = 1;
if (charCode <= 255) {
if (/[A-Z]/.test(char)) weight = 0.7;
else if (/[a-z]/.test(char)) weight = 0.5;
else if (/[0-9]/.test(char)) weight = 0.55;
else if (char === ' ') weight = 0.25;
else weight = 0.5;
}
currentWidth += weight * fontSize;
if (currentWidth > containerWidth - uni.upx2px(4)) {
cutIndex = i;
found = true;
break;
}
}
return found ? originalTitle.slice(0, cutIndex - 2) + '...' : originalTitle;
};
/**
* 供模板调用返回截取后的标题
*/
const getTruncatedTitle = (item, index, goods, gIdx) => {
const key = index + '-' + gIdx;
return titleMap.value[key] !== undefined ? titleMap.value[key] : (goods.title || '');
};
const calculateHeights = () => {
nextTick(() => {
setTimeout(() => {
if (!instance || !instance.proxy) return;
if (!props.list || props.list.length === 0) return;
const heightQuery = uni.createSelectorQuery().in(instance.proxy);
const titleQuery = uni.createSelectorQuery().in(instance.proxy);
let keys = [];
props.list.forEach((item, index) => {
if (!item.goodsList) return;
item.goodsList.forEach((goods, gIdx) => {
const key = index + '-' + gIdx;
keys.push(key);
heightQuery.select('#goods-info-' + key).boundingClientRect();
titleQuery.select('#title-box-' + key).boundingClientRect();
});
});
if (keys.length > 0) {
//
heightQuery.exec((res) => {
if (res && res.length > 0) {
res.forEach((data, i) => {
if (data && data.height) {
imgHeights.value[keys[i]] = data.height;
}
});
}
});
//
titleQuery.exec((res) => {
if (res && res.length > 0) {
res.forEach((data, i) => {
if (data && data.width) {
const [li, gi] = keys[i].split('-').map(Number);
const goods = props.list[li]?.goodsList?.[gi];
if (goods && goods.title) {
titleMap.value[keys[i]] = calcFitTitle(goods.title, data.width);
}
}
});
}
});
}
}, 300); //
});
};
watch(() => props.list, () => {
calculateHeights();
}, { deep: true, immediate: true });
//
// shopping-card.vue onMounted
onMounted(() => {
setTimeout(calculateHeights, 500);
});
//
const getStatusText = (status) => {
const map = {
'wait_pay': '待付款',
'wait_send': '待发货',
'wait_recv': '卖家已发货',
'success': '交易成功',
'closed': '交易关闭',
'refunding': '交易关闭'
};
if (status == 'refunding' && props.currentFilter == 4) {
map[status] = '退款'
}
return map[status] || status;
};
//
const getButtons = (status, orderType) => {
if (orderType === 'waimai') {
const waimaiMap = {
'wait_pay': {
moreText: { isShow: false },
buttons: [
{ text: '继续付款', action: 'pay', primary: true }
]
},
'wait_recv': {
moreText: { isShow: false },
buttons: [
{ text: '查看订单', action: 'view_order' },
{ text: '查看物流', action: 'view_logistics' }
]
},
'success': {
moreText: { isShow: false },
buttons: [
{ text: '删除订单', action: 'delete' },
{ text: '评价', action: 'comment' },
{ text: '再买一单', action: 'buy_again', primary: false }
]
},
'closed': {
moreText: { isShow: false },
buttons: [
{ text: '删除订单', action: 'delete' },
{ text: '再买一单', action: 'buy_again', primary: false }
]
}
};
return waimaiMap[status] || { moreText: { isShow: false }, buttons: [] };
}
const map = {
'wait_pay': {
moreText: {
text: "",
isShow: true,
color: "#1a1a1a"
},
buttons: [
{ text: '修改地址', action: 'edit_address' },
{ text: '找朋友付', action: 'cancel' },
{ text: '继续付款', action: 'pay', primary: true }
]
},
'wait_send': {
moreText: {
text: "",
isShow: false,
color: "#1a1a1a"
},
buttons: [
{ text: '催发货', action: 'remind' },
{ text: '查看物流', action: 'view_logistics' },
{ text: '修改地址', action: 'edit_address' }
]
},
'wait_recv': {
moreText: {
text: "",
isShow: true,
color: "#1a1a1a"
},
buttons: [
{ text: '加入购物车', action: 'add_cart' },
{ text: '查看物流', action: 'view_logistics' },
{ text: '确认收货', action: 'confirm', primary: true }
]
},
'success': {
moreText: {
text: "",
isShow: true,
color: "#1a1a1a"
},
buttons: [
{ text: '加入购物车', action: 'add_cart' },
{ text: '再买一单', action: 'buy_again' },
{ text: '评价', action: 'comment', primary: true }
]
},
'closed': {
moreText: {
text: "",
isShow: false,
color: "#1a1a1a"
},
buttons: [
{ text: '删除订单', action: 'delete' },
{ text: '加入购物车', action: 'add_cart' },
{ text: '再买一单', action: 'buy_again', primary: true }
]
},
'refunding': {
moreText: {
text: "",
isShow: true,
color: "#7C8495"
},
buttons: [
{ text: '加入购物车', action: 'add_cart' },
{ text: '钱款去向', action: 'query_money' },
{ text: '联系商家', action: 'contact_merchant', primary: true }
]
}
};
return map[status] || [];
};
const clickItem = (item) => {
emit('click-item', item);
}
const handleTouchStart = (e, item) => {
const touch = e.touches[0];
touchStartPos.value = { x: touch.clientX, y: touch.clientY };
//
if (longPressTimer.value) clearTimeout(longPressTimer.value);
// 800ms ()
longPressTimer.value = setTimeout(() => {
//
uni.vibrateShort();
emit('longpress', {
item,
x: touch.clientX,
y: touch.clientY
});
longPressTimer.value = null;
}, 800);
}
const handleTouchMove = (e) => {
const touch = e.touches[0];
const offsetX = Math.abs(touch.clientX - touchStartPos.value.x);
const offsetY = Math.abs(touch.clientY - touchStartPos.value.y);
// 10px
if (offsetX > 10 || offsetY > 10) {
if (longPressTimer.value) {
clearTimeout(longPressTimer.value);
longPressTimer.value = null;
}
}
}
const handleTouchEnd = () => {
if (longPressTimer.value) {
clearTimeout(longPressTimer.value);
longPressTimer.value = null;
}
}
const getTotalCount = (goodsList) => {
if (!goodsList || !goodsList.length) return 0;
return goodsList.reduce((sum, item) => sum + (Number(item.count) || 1), 0);
};
const handleAction = (action, item) => {
emit('action', { action, item });
};
const goToDetail = (item) => {
emit('detail', item);
};
</script>
<style lang="less" scoped>
.card-wrapper {
width: 100%;
}
.card-container {
margin: 16rpx 0;
background-color: #FFFFFF;
padding: 24rpx;
box-sizing: border-box;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.shop-info {
display: flex;
align-items: center;
.taobao-logo {
height: 28rpx;
margin-right: 6rpx;
&.shangou {
height: 36rpx;
}
}
.shop-name {
font-size: 28rpx;
line-height: 28rpx;
color: #333333;
font-weight: bold;
}
.waimai-shop-name {
display: inline-block;
max-width: 220rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.arrow-icon {
font-size: 24rpx;
color: #999;
margin-left: 8rpx;
}
}
.order-status {
font-size: 26rpx;
color: #FF7824;
font-weight: 500;
}
}
.goods-list {
.goods-item {
display: flex;
align-items: flex-start;
margin-bottom: 18rpx;
.goods-img {
height: 92px;
width: 92px;
min-width: 178rpx;
min-height: 178rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
flex-shrink: 0;
&.refunding {
width: 74px;
height: 74px;
min-width: 142rpx;
min-height: 142rpx;
}
}
.goods-info {
flex: 1;
margin-left: 24rpx;
display: flex;
flex-direction: column;
.goods-title-row {
display: flex;
justify-content: space-between;
.good-info-box {
flex: 1;
width: 100px;
overflow: hidden;
text-overflow: ellipsis;
// margin-right: 8px;
}
.goods-title {
font-size: 26rpx;
color: #00032A;
line-height: 36rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
margin-right: 18rpx;
font-weight: 500;
}
.goods-price-box {
text-align: right;
.price-symbol {
font-size: 30rpx;
color: #1A1A1A;
font-weight: 500;
}
.price-val {
font-size: 30rpx;
color: #1A1A1A;
font-weight: 500;
}
.goods-count {
display: block;
font-size: 22rpx;
line-height: 22rpx;
color: #999999;
margin-top: 14rpx;
}
}
}
.goods-spec {
font-size: 22rpx;
line-height: 22rpx;
color: #7C8495;
border-radius: 8rpx;
align-self: flex-start;
margin-top: 22rpx;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
width: 100%;
}
.goods-tags {
display: flex;
flex-wrap: wrap;
margin-top: 16rpx;
.tag {
font-size: 24rpx;
line-height: 24rpx;
color: #35A156;
}
}
.goods-refund-status {
font-size: 24rpx;
color: #FF5000;
margin-top: 12rpx;
}
.goods-evaluation {
display: flex;
align-items: center;
justify-content: space-between;
background-color: #F4F5F7;
border-radius: 8rpx 8rpx 8rpx 8rpx;
height: 46rpx;
padding: 0 12rpx;
margin-top: 20rpx;
.left {
display: flex;
align-items: center;
text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.right {
display: flex;
align-items: center;
text {
color: #CACFD4;
font-size: 20rpx;
margin-right: 8rpx;
}
}
text {
font-size: 24rpx;
color: #1A1A1A;
font-weight: 500;
line-height: 24rpx;
}
.desc {
flex: 1;
width: 100px;
font-size: 24rpx;
color: #7C8495;
line-height: 24rpx;
margin-left: 20rpx;
}
}
}
}
}
.tips-box {
background-color: #F4F5F7;
border-radius: 12rpx;
padding: 20rpx 24rpx;
margin-top: 16rpx;
height: 68rpx;
display: flex;
align-items: center;
.title {
flex-shrink: 0;
color: #1A1A1A;
margin-left: 8rpx;
margin-right: 20rpx;
font-weight: 500;
white-space: nowrap;
}
.tip-icon {
flex-shrink: 0;
width: 30rpx;
height: 30rpx;
margin-right: 12rpx;
}
.text {
font-size: 26rpx;
color: #7C8495;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&.daifukuan {
.tip-icon {
width: 30rpx;
height: 30rpx;
margin-right: 12rpx;
}
.text {
font-size: 26rpx;
color: #F37120;
font-weight: 500;
}
}
&.daifahuo {}
}
.order-total {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 10rpx;
margin-bottom: 24rpx;
.total-label {
font-size: 24rpx;
color: #1A1A1A;
margin-right: 8rpx;
}
.total-symbol {
font-size: 28rpx;
color: #1A1A1A;
font-weight: 500;
}
.total-price {
font-size: 32rpx;
color: #1A1A1A;
font-weight: 500;
}
}
.card-footer {
position: relative;
display: flex;
justify-content: flex-end;
align-items: center;
.more-text {
position: absolute;
left: 0;
top: 50%;
font-size: 26rpx;
line-height: 26rpx;
color: #1A1A1A;
transform: translateY(-50%);
}
.btn {
padding: 0 24rpx;
height: 64rpx;
line-height: 64rpx;
font-size: 26rpx;
color: #1A1A1A;
margin-left: 16rpx;
background-color: #F4F5F7;
border-radius: 12rpx;
&.btn-primary {
color: #F37120;
background-color: #FFECE5;
}
}
}
}
</style>
<style>
@import '@/common/main.css';
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

BIN
static/font/card.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 646 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

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