Compare commits

...

236 Commits

Author SHA1 Message Date
小李 a7849b78a7 三角洲 2026-07-28 14:54:13 +08:00
小李 5d4c0d30e0 优化 2026-07-25 17:56:39 +08:00
小李 bd3a2e9cce 安卓沉浸适配 2026-07-25 17:20:57 +08:00
小李 d9459b1bd0 接口加密 2026-07-20 10:10:55 +08:00
tangxinyue 738f058281 1.携程火车票已出行状态
2.解决和平精英,无畏锲约选择图片后为关闭选项问题
3.转账,支付成功页面优化导航
2026-07-16 16:30:19 +08:00
tangxinyue a215b9b263 编辑数据后重新刷新海报 2026-07-10 13:51:02 +08:00
tangxinyue deca26c218 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-07-10 11:51:44 +08:00
tangxinyue ca06240e86 充值来源 2026-07-10 11:50:57 +08:00
小李 9172ced172 版本号 2026-07-10 11:50:35 +08:00
小李 8c96536d11 工资条底部样式 2026-07-10 10:07:13 +08:00
tangxinyue 6242e47490 优化游戏界面 2026-07-09 14:43:11 +08:00
tangxinyue 2c28dd5cfd Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-07-08 14:41:11 +08:00
tangxinyue 80ae3c1e1e 完成和平精英 无畏契约游戏界面 2026-07-08 14:40:48 +08:00
小李 c933a2c663 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-07-08 10:37:55 +08:00
小李 007f0c7238 工资单新增样式,广东高考理科 2026-07-08 10:37:45 +08:00
tangxinyue 1b65430bd2 修改支付成功页面 2026-07-07 09:14:19 +08:00
tangxinyue a2ab06de2b 完成支付成功页面 2026-07-01 17:35:22 +08:00
小李 3467d370ca 优化 2026-06-30 16:25:07 +08:00
tangxinyue 125e6694d6 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-30 16:17:22 +08:00
tangxinyue f842ab9d6a 更改图片 2026-06-30 16:17:01 +08:00
小李 b47742884b 图片下载 2026-06-30 14:57:16 +08:00
小李 7bfa56cf5b Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-30 14:11:26 +08:00
小李 597ace78aa 图片优化 2026-06-30 14:11:18 +08:00
tangxinyue 2f57b85609 高铁票出发时间改不了年月 2026-06-30 13:43:09 +08:00
小李 ccdab994a8 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-06-26 15:00:42 +08:00
小李 d985f0dd25 优化 2026-06-26 14:59:44 +08:00
tangxinyue cce9d63ef1 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-26 14:58:59 +08:00
tangxinyue 8ce81540d9 短信华为机型时间高亮 2026-06-26 14:58:30 +08:00
小李 76a25d329b Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-26 11:00:13 +08:00
小李 8791695869 兑换码 2026-06-26 11:00:05 +08:00
tangxinyue c7496e73e1 优化转账页面 2026-06-26 10:42:09 +08:00
tangxinyue 3b2396ec5c 1.解决华为机型短信发送消息过长输入框变形问题 2026-06-23 14:25:19 +08:00
tangxinyue b90c0c6774 优化视频聊天页 2026-06-18 17:17:59 +08:00
tangxinyue fe48026d19 优化视频通话 2026-06-18 15:00:42 +08:00
小李 476acc7571 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-18 14:00:52 +08:00
tangxinyue c625b3472f Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-18 13:42:54 +08:00
tangxinyue 7aec27d81b 微信视频通话 2026-06-18 13:42:30 +08:00
小李 bc0c917efa 工资条和高考开关控制 2026-06-18 10:24:37 +08:00
小李 bc53f9552e Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-18 09:44:42 +08:00
小李 d047299489 工商银行优化 2026-06-18 09:44:20 +08:00
tangxinyue 82e922eab0 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts:
#	main.js
2026-06-17 17:04:45 +08:00
tangxinyue b3f3f33c90 完成三角洲 视频单聊通话 修改微信支付页面 2026-06-17 17:04:02 +08:00
小李 906293b350 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-16 17:22:27 +08:00
小李 ee086eec01 充值页面配置 2026-06-16 17:22:21 +08:00
tangxinyue 77c3590e5b Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-15 13:45:31 +08:00
tangxinyue 1f71a0240d 修改淘宝交易成功 2026-06-15 13:44:40 +08:00
小李 c6a3e2072d 优化重庆 2026-06-12 17:16:59 +08:00
小李 86281a76b6 优化 2026-06-12 17:06:40 +08:00
小李 e7705b1162 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-12 16:51:25 +08:00
小李 4e16330877 优化 2026-06-12 16:51:19 +08:00
tangxinyue dbc666a90d 修改退款图片展示宽高 2026-06-12 16:49:12 +08:00
tangxinyue 4d15da3c52 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-12 16:32:59 +08:00
tangxinyue 9e3926548e 修改淘宝退款样式 2026-06-12 16:32:35 +08:00
小李 f67ac5b73a Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-12 16:16:45 +08:00
小李 d234c1a48a 优化高考 2026-06-12 16:16:40 +08:00
tangxinyue 4b1d7c168d 替换小宝转账banner图片 2026-06-12 16:08:42 +08:00
tangxinyue 34a6810681 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-12 15:39:49 +08:00
tangxinyue 8657afdbd7 修改王者荣耀裁剪图片方式 2026-06-12 15:39:01 +08:00
小李 e1775a67e3 Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1 2026-06-12 14:49:32 +08:00
小李 ab1b4b48c8 高考 2026-06-12 14:49:22 +08:00
tangxinyue 9c5f267b5f 修改淘宝退款页面样式间距 2026-06-12 14:48:13 +08:00
tangxinyue d54877fb85 修改转账页面,可切换图片区域样式,编辑优惠券内容 2026-06-11 11:12:00 +08:00
tangxinyue b1f41cfcbb 完成微信支付页面 2026-06-09 17:41:28 +08:00
tangxinyue 503a4e1194 修改淘宝退款详情页,变更支付宝首页布局,新增视频教程页 2026-06-09 15:48:23 +08:00
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
847 changed files with 84990 additions and 4882 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'
}
}

View File

@ -9,6 +9,10 @@
"customPlaygroundType" : "local",
"playground" : "custom",
"type" : "uni-app:app-android"
},
{
"playground" : "standard",
"type" : "uni-app:app-harmony"
}
]
}

2
.husky/pre-commit Normal file
View File

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

371
App.vue
View File

@ -1,194 +1,227 @@
<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) {
uni.setStorageSync('AppUser', {})
uni.setStorageSync('userInfo', {})
// 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('开始监听宿主消息')
if (typeof uni.onNativeEventReceive === 'function') {
uni.onNativeEventReceive((event, data) => {
if (event) {
console.log('接收到宿主消息:', event, data)
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 {
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
let storageData={}
if(uni.getStorageSync('isTest')){
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,
}
} else if (event == 'wx_pay_result' || event == 'ios_pay_result') {
this.globalData.recentNativeEvent = event
this.globalData.recentNativeData = data
// #endif
},
encrypt: extraData['encrypt'],
decrypt: extraData['decrypt']
}
}else{
if (extraData['safeBottom']) {
this.$store.state["safeBottomAz"]=Number(extraData['safeBottom'])||0
}
storageData = {
host: extraData.host,
header: {
// #ifdef APP-PLUS
"header": {
"version": this.$version,
}
// #endif
}
}
}
//
Object.keys(storageData).forEach(key => {
uni.setStorageSync(key, storageData[key])
})
if (extraData['isCombo']) {
uni.setStorageSync('isCombo', extraData['isCombo'])
}
if (extraData['paytype']) {
uni.setStorageSync('isCombo', extraData['paytype'] == 'combo' ? 'ok' : '')
}
},
/**
* 初始化开发环境配置也作为默认配置
*/
initDevelopmentConfig() {
console.log('初始化默认配置')
//
const devConfig = {
host: "https://flaunt.batiao8.com/",
header: {
"x-token": "55e2481e-cbd2-4d2d-82d3-b47e191fcfc0"
},
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

@ -1,20 +1,10 @@
<!-- eslint-disable -->
<template>
<view
class="player-wrapper"
:id="videoWrapperId"
:parentId="id"
:randomNum="randomNum"
:change:randomNum="domVideoPlayer.randomNumChange"
:viewportProps="viewportProps"
:change:viewportProps="domVideoPlayer.viewportChange"
:videoSrc="videoSrc"
:change:videoSrc="domVideoPlayer.initVideoPlayer"
:command="eventCommand"
:change:command="domVideoPlayer.triggerCommand"
:func="renderFunc"
:change:func="domVideoPlayer.triggerFunc"
/>
<view class="player-wrapper" :id="videoWrapperId" :parentId="id" :randomNum="randomNum"
:change:randomNum="domVideoPlayer.randomNumChange" :viewportProps="viewportProps"
:change:viewportProps="domVideoPlayer.viewportChange" :videoSrc="videoSrc"
:change:videoSrc="domVideoPlayer.initVideoPlayer" :command="eventCommand"
:change:command="domVideoPlayer.triggerCommand" :func="renderFunc" :change:func="domVideoPlayer.triggerFunc" />
</template>
<script>
@ -158,6 +148,14 @@ export default {
params: { sec, isDelay }
}
}
},
// renderjs video
// v-if ""
beforeUnmount() {
this.renderFunc = {
name: 'removeHandler',
params: null
}
}
}
</script>
@ -173,7 +171,11 @@ export default {
loadingEl: null,
//
delayFunc: null,
renderProps: {}
renderProps: {},
// pause()
intentionalPause: false,
//
lastAutoRecoverTime: 0
}
},
computed: {
@ -215,7 +217,9 @@ export default {
videoEl.muted = muted
videoEl.playbackRate = playbackRate
videoEl.id = this.playerId
// videoEl.setAttribute('x5-video-player-type', 'h5')
// X5 H5
videoEl.setAttribute('x5-video-player-type', 'h5')
videoEl.setAttribute('x5-video-player-fullscreen', 'false')
videoEl.setAttribute('preload', 'auto')
videoEl.setAttribute('playsinline', true)
videoEl.setAttribute('webkit-playsinline', true)
@ -315,6 +319,18 @@ export default {
key: 'playing',
value: false
})
// 2
const { loop, autoplay } = this.renderProps
const now = Date.now()
if (loop && autoplay && this.videoEl && !this.intentionalPause && (now - this.lastAutoRecoverTime > 2000)) {
this.lastAutoRecoverTime = now
setTimeout(() => {
if (this.videoEl && this.videoEl.paused && !this.intentionalPause) {
this.videoEl.play().catch(() => {})
}
}, 200)
}
}
this.videoEl.removeEventListener('pause', pauseHandler)
this.videoEl.addEventListener('pause', pauseHandler)
@ -367,7 +383,13 @@ export default {
this.videoEl.addEventListener('loadedmetadata', loadedMetadataHandler)
//
let lastTime = 0;
const timeupdateHandler = (e) => {
const now = Date.now();
// 500ms renderjs
if (now - lastTime < 500) return;
lastTime = now;
const currentTime = e.target.currentTime
this.$ownerInstance.callMethod('eventEmit', {
event: 'timeupdate',
@ -457,6 +479,12 @@ export default {
},
triggerCommand(eventType) {
if (eventType) {
// /
if (eventType === 'pause') {
this.intentionalPause = true
} else if (eventType === 'play') {
this.intentionalPause = false
}
this.$ownerInstance.callMethod('resetEventCommand')
this.videoEl && this.videoEl[eventType]()
}
@ -519,6 +547,16 @@ export default {
randomNumChange(val) {
this.num = val
}
},
// renderjs video
// v-if
beforeUnmount() {
if (this.videoEl) {
this.videoEl.pause()
this.videoEl.src = ''
this.videoEl.load()
this.videoEl = null
}
}
}
</script>
@ -529,5 +567,7 @@ export default {
height: 100%;
padding: 0;
position: relative;
/* 开启硬件加速,将视频提升为独立合成层 */
transform: translateZ(0);
}
</style>

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" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<image :src="`/static/image/call/${type}TabbarImg${index+1}.png`" mode=""></image>
<text>{{item}}</text>
</view>
</view>
<view class="footer footerZhangwei" :class="['footer_'+type]" :style="{'margin-top':$store.state.safeBottomAz+'px'}">
</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,596 @@
<template>
<view :class="`${phone}-style`">
<!-- 导航样式 -->
<view class="nav-bar-box" :class="{ 'border-nav-bar-box': isScroll }">
<NavBar :isBack="false" :bgColor="isScroll ? navBarBgColor : '#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" :style="{'margin-top':$store.state.safeBottomAz+'px'}"></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 " :style="{height: (74+$store.state.safeBottomAz)+'px'}">
<view class="flex" :style="{height: '148rpx'}">
<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>
</view>
</template>
</view>
</template>
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import {
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 navBarBgColor = computed(() => {
return props.phone === 'iphone' ? '#F8F8F8' : '#FFFFFF';
})
const data = reactive({
navBar: {
title: '信息',
},
})
//
const showInfo = computed(() => {
let text, placeholder, secondText, bottomLtext, bottomRtext
switch (props.phone) {
case "iphone":
text = "信息"
placeholder = '搜索'
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 {
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

@ -0,0 +1,97 @@
<template>
<text
class="custom-icon"
:class="iconClass"
:style="{ color: color, fontSize: size + 'px' }"
@click="$emit('click')"
></text>
</template>
<script>
export default {
name: "my-icon",
props: {
// 'icon-home'
name: {
type: String,
default: ""
},
//
color: {
type: String,
default: "#333333"
},
// rpx
size: {
type: [Number, String],
default: 16
}
},
computed: {
iconClass() {
// 'custom-icon' font-family
const name = `icon-${this.name}`;
return name;
}
}
}
</script>
<style scoped>
/* 1. 引入并定义字体名称 */
@font-face {
font-family: 'myIconFont';
/* 微信小程序若本地引用报错,可换成 base64 字符串或网络绝对路径 */
src: url('@/static/font/iconfont.ttf') format('truetype');
}
/* 2. 定义基础样式 */
.custom-icon {
font-family: "myIconFont" !important;
font-size: inherit;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
display: inline-block;
}
.icon-sanjiaozhou-maohao:before {
content: "\e630";
}
.icon-sanjiaozhou-fengexian:before {
content: "\e631";
}
.icon-sanjiaozhou-douhao:before {
content: "\e626";
}
.icon-sanjiaozhou-0:before {
content: "\e629";
}
.icon-sanjiaozhou-1:before {
content: "\e627";
}
.icon-sanjiaozhou-2:before {
content: "\e62b";
}
.icon-sanjiaozhou-3:before {
content: "\e62c";
}
.icon-a-sanjiaozhou-4:before {
content: "\e62f";
}
.icon-sanjiaozhou-5:before {
content: "\e62d";
}
.icon-sanjiaozhou-6:before {
content: "\e628";
}
.icon-sanjiaozhou-7:before {
content: "\e625";
}
.icon-sanjiaozhou-8:before {
content: "\e62a";
}
.icon-sanjiaozhou-9:before {
content: "\e62e";
}
</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,41 @@
<template>
<view class="safeBottomBox">
<view class="safeBottomFiexd" :style="{ height: safeBottomAz + 'px', backgroundColor: bgColor }">
</view>
<view class="safeBottomZhangwei" :style="{ height: safeBottomAz + 'px' }">
</view>
</view>
</template>
<script setup>
import { computed, getCurrentInstance } from 'vue'
const props = defineProps({
bgColor: {
type: String,
default: '#EDEDED'
}
})
const instance = getCurrentInstance()
const safeBottomAz = computed(() => {
const store = instance?.appContext?.config?.globalProperties?.$store
const height = store?.state?.safeBottomAz
return Number(height) || 0
})
</script>
<style lang="scss" scoped>
.safeBottomBox {
width: 100vw;
position: relative;
z-index: 999;
overflow: hidden;
.safeBottomFiexd {
width: 100vw;
position: fixed;
left: 0;
bottom: 0;
}
}
</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;

View File

@ -0,0 +1,29 @@
## 自定义水印添加
***
一款声明版权立场的组件
该组件参考了 [315500033@qq.com](https://ext.dcloud.net.cn/plugin?id=362) 大神的组件 修改后符合自己的编码习惯 让水印有更多的展示方式
### 使用方式
#### 在 ``script`` 中引入组件:
```javascript
import wmWatermark from '../../components/wm-watermark.vue';
export default {
components: {wmWatermark}
}
```
#### 在 ``template`` 中使用组件:
```html
<wm-watermark text="这是水印信息"></wm-watermark>
```
### **属性说明**
| 属性 | 类型 | 默认值 | 说明 |
| :---: | :---: | :---: | :--------: |
| text | String | 无 | 设置水印文字 注:(文字优先图片) |
| imgUrl | String | 无 | 设置水印图片链接 注:(文字优先图片) |
| opacity | Number,String | 0.6 | 设置显示透明度 |
| num | Number | 20 | 设置水印数量 |

View File

@ -0,0 +1,72 @@
<template>
<!-- 水印 -->
<view class="watermark">
<block v-for="(item,index) in num" :key="index">
<view class="watermark-text" v-if="text != ''" :style="{opacity:opacity}">{{text}}</view>
<image class="watermark-img" :src="imgUrl" mode="aspectFill" v-if="imgUrl !='' && text ==''" :style="{opacity:opacity}"></image>
</block>
</view>
</template>
<script>
export default {
name:'wm-watermark',
props:{
text:{ //
type:String,
default:''
},
imgUrl:{ //
type:String,
default:''
},
opacity:{ //
type:[Number,String],
default:0.6
},
num:{ //
type:Number,
default:30
}
},
data() {
return {
};
}
}
</script>
<style lang="scss">
.watermark{
position: fixed;
width: 1200upx;
height: 100%;
top:0;
left: 0;
bottom: 0;
right: 0;
pointer-events: none;
z-index: 9999999;
display: flex;
flex-wrap: wrap;
overflow: hidden;
transform: rotate(-33deg) translateX(-20px);
.watermark-text{
width: 350upx;
height: 80upx;
display: flex;
justify-content: center;
align-items: center;
// color: red;
color: rgba(0,0,0,0.1);
font-size: 18upx;
white-space: nowrap;
}
.watermark-img{
width: 375upx;
height: 375upx;
z-index: 1;
}
}
</style>

BIN
eslint-output.json Normal file

Binary file not shown.

76
main.js
View File

@ -16,20 +16,78 @@ 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() || {}
}
const windowInfo = uni.getWindowInfo();
app.config.globalProperties.$store = {
state: {
safeBottom: windowInfo.safeAreaInsets?.bottom || 0,
safeBottomAz: 0
}
}
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
app.config.globalProperties.$systemInfo = systemInfo
uni.setStorageSync('version', '1.0.2.sp4')
app.config.globalProperties.$version = uni.getStorageSync('version')
// #ifdef APP-PLUS&&!APP-HARMONY
app.config.globalProperties.$system = plus.os.name;
// #endif
app.config.globalProperties.$systemInfo = systemInfo;
uni.setStorageSync('version', '1.0.7.sp6');
app.config.globalProperties.$version = uni.getStorageSync('version');
// 环境development→测试production→正式跟构建环境走无需手改
const isTest = process.env.NODE_ENV === 'development';
app.config.globalProperties.$isTest = isTest;
// #ifdef APP-PLUS&&!APP-HARMONY
const version = plus.runtime.version;
// #endif
const compareVersion = (v1, v2) => {
const a = String(v1).split('.')
const b = String(v2).split('.')
const len = Math.max(a.length, b.length)
for (let i = 0; i < len; i++) {
const n1 = parseInt(a[i] || '0', 10)
const n2 = parseInt(b[i] || '0', 10)
if (n1 > n2) return 1
if (n1 < n2) return -1
}
return 0
}
//安卓和ios
// #ifdef APP-PLUS && !APP-HARMONY
if (plus.os.name == 'iOS') {
if (compareVersion(version, '1.9') >= 0 && !isTest) {
uni.setStorageSync('isTest', false)
} else {
uni.setStorageSync('isTest', true)
}
} else {
if (compareVersion(version, '2.2.0') >= 0 && !isTest) {
uni.setStorageSync('isTest', false)
} else {
uni.setStorageSync('isTest', true)
}
}
// #endif
//鸿蒙
// #ifdef APP-PLUS && APP-HARMONY
if (compareVersion(version, '2.1.7') >= 0&&!isTest) {
uni.setStorageSync('isTest', false)
} else {
uni.setStorageSync('isTest', true)
}
// #endif
//h5环境
// #ifndef APP-PLUS
uni.setStorageSync('isTest', true)
// #endif
app.use(globalMethods);
return {
app

View File

@ -1,37 +1,64 @@
{
"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" //
},
"bundleName" : "cn.zuom8.jsloction",
"signingConfigs" : {
"default" : {
"certpath" : "C:\\Users\\Administrator\\Desktop\\jisuloca\\test\\debug.cer",
"keyAlias" : "__uni__1be0b2f",
"keyPassword" : "000000189B31358AF1B1A6FD1CB2D1011E32DF02389A1F92604ECEB091A66654CDCDF6DB4DD18BC3",
"profile" : "C:\\Users\\Administrator\\Desktop\\jisuloca\\test\\jsloction_debugDebug.p7b",
"signAlg" : "SHA256withECDSA",
"storeFile" : "C:\\Users\\Administrator\\Desktop\\jisuloca\\hmos.p12",
"storePassword" : "000000185660236954705CE2E46C7A2FA76565A93989A8DDD5810CC2760ED8D2B479EDD9C488B982"
}
}
}
},
"app-plus" : {
"darkmode" : false,
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : false,
"delay" : 0
},
"optimization": {
"subPackages": true
"compatible" : {
"runtimeVersion": "4.75",
"compilerVersion": "5.14",
"ignoreVersion" : 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 +77,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"
}
}
}

2315
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,40 @@
{
"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": {
"@batiao/batiao-sdk-uniapp-unimp": "1.0.374",
"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

@ -10,6 +10,150 @@
}
],
"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": [
@ -27,6 +171,27 @@
"navigationBarTitleText": "快速入口页面",
"navigationStyle": "custom"
}
},
{
"path": "transfer/transfer",
"style": {
"navigationBarTitleText": "转账模拟",
"navigationStyle": "custom"
}
},
{
"path": "pay-successful/pay-successful",
"style": {
"navigationBarTitleText": "支付成功",
"navigationStyle": "custom"
}
},
{
"path": "pay-successful/edit",
"style": {
"navigationBarTitleText": "修改支付成功",
"navigationStyle": "custom"
}
}
]
},
@ -170,6 +335,197 @@
"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"
}
},
{
"path": "game/sanjiaozhou",
"style": {
"navigationBarTitleText": "三角洲",
"navigationStyle": "custom"
}
},
{
"path" : "game/hepingjingying",
"style" :
{
"navigationBarTitleText": "和平精英",
"navigationStyle": "custom"
}
},
{
"path" : "game/wuweiqiyue",
"style" :
{
"navigationBarTitleText": "无畏契约",
"navigationStyle": "custom"
}
},
{
"path": "game/index",
"style": {
"navigationBarTitleText": "游戏首页",
"navigationStyle": "custom"
}
},
{
"path": "wx-payment-success/wx-payment-success",
"style": {
"navigationBarTitleText": "微信支付成功",
"navigationStyle": "custom"
}
},
{
"path": "score/index",
"style": {
"navigationBarTitleText": "高考分数",
"navigationStyle": "custom"
}
},
{
"path": "score/center",
"style": {
"navigationBarTitleText": "查询中心",
"navigationStyle": "custom"
}
},
{
"path": "score/chongqing",
"style": {
"navigationBarTitleText": "重庆",
"navigationStyle": "custom"
}
},
{
"path": "score/hebei",
"style": {
"navigationBarTitleText": "河北",
"navigationStyle": "custom"
}
},
{
"path": "score/guandong",
"style": {
"navigationBarTitleText": "广东",
"navigationStyle": "custom"
}
},
{
"path": "video-chat/video-chat",
"style": {
"navigationBarTitleText": "视频单聊",
"navigationStyle": "custom"
}
}
]
},
@ -203,6 +559,20 @@
"navigationBarTitleText": "充值页",
"navigationStyle": "custom"
}
},
{
"path": "call-and-message-entry/call-and-message-entry",
"style": {
"navigationBarTitleText": "呼叫和短信入口",
"navigationStyle": "custom"
}
},
{
"path": "shipinjiaocheng/shipinjiaocheng",
"style": {
"navigationBarTitleText": "视频教程",
"navigationStyle": "custom"
}
}
]
}
@ -216,7 +586,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')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark :dark="data.dark" source="uni_alipay_huabei" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_huabei')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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>
@ -111,6 +118,7 @@
</view>
<canvas canvas-id="crop-canvas"
style="position: fixed; left: -9999px; width: 750rpx; height: 100vh; pointer-events: none;"></canvas>
<ZdySafeBottom bgColor="#fff"/>
<!-- 编辑弹窗 -->
<uni-popup ref="popup" type="center" :mask-click="false">
<view class="popup-content">
@ -183,6 +191,7 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar'
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
import {
numberUtil,
util
@ -201,6 +210,8 @@ import {
const instance = getCurrentInstance();
const { proxy } = instance;
const navBarRef = ref(null)
const buttonGroup = [{
name: "编辑花呗数据",
@ -221,6 +232,7 @@ const buttonGroup = [{
}, {
name: "花呗逾期",
isSwitch: true,
key: 'isOverdue',
click: () => {
data.huabeiInfo.isOverdue = !data.huabeiInfo.isOverdue
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
@ -229,6 +241,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()
}
}]
@ -316,7 +342,7 @@ onLoad((option) => {
})
onShow(() => {
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
@ -439,7 +465,9 @@ const confirmImage = () => {
data.huabeiInfo.image = saveRes.savedFilePath
selectedImage.value = '' //
setTimeout(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("light");
// #endif
}, 200)
//
uni.setStorageSync(data.huabeiInfoStorageKey, data.huabeiInfo)
@ -500,7 +528,9 @@ const chooseImage = () => {
selectedImage.value = res.tempFilePaths[0]
data.showMask = true
setTimeout(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("dark");
// #endif
}, 500)
}
})
@ -537,7 +567,9 @@ const handleTouchEnd = () => {
const closeImage = () => {
selectedImage.value = ''
data.showMask = false
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("light");
// #endif
return false
}
@ -704,7 +736,7 @@ const goBack = () => {
justify-content: center;
.text {
color: #ffffff;
color: #B9D6FF;
font-size: 26rpx;
line-height: 1.2;
}
@ -738,7 +770,7 @@ const goBack = () => {
}
.value {
color: #ffffff;
color: #B9D6FF;
font-size: 26rpx;
line-height: 36rpx;
}

View File

@ -1,4 +1,4 @@
<template>
<template>
<view class="page-container">
<view class="main-container">
@ -206,9 +206,9 @@
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark :dark="data.dark" source="uni_alipay_huabei" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_huabei')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
</template>
@ -407,18 +407,22 @@ onPageScroll((e) => {
data.navBar.bgColor = '#F6F6F6'
data.navBar.textColor = '#1a1a1a'
data.isTop = false
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("dark");
// #endif
} else {
//
data.navBar.bgColor = '#717E91'
data.navBar.textColor = '#fff'
data.isTop = true
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("light");
// #endif
}
})
onShow(() => {
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F6F6F6')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");

View File

@ -1,14 +1,14 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark :dark="data.dark" source="uni_alipay_balance" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_balance')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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;">
@ -58,7 +58,7 @@
<scroll-view class="menu-scroll-view" scroll-x="true" @scroll="onMenuScroll"
:show-scrollbar="false">
<view class="menu-content">
<view class="item menu-icon-box" v-for="item in menuList">
<view class="item menu-icon-box" v-for="(item, index) in menuList" :key="index" >
<image class="menu-icon" :src="`/static/image/balance/menu-icon/${item.label}.png`"
mode="">
</image>
@ -89,6 +89,8 @@
<view class="blue-text">我的客服</view>
<view class="footer-text">余额升级服务由支付宝和网商银行提供</view>
</view>
<safeBottom bgColor="#F0F3F8"/>
</view>
</scroll-view>
@ -111,8 +113,10 @@
</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 ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
import { fastEntranceList } from '@/static/json/initial.json'
import {
util,
@ -211,7 +215,7 @@ onLoad(async () => {
})
onShow(() => {
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F0F3F8')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
@ -515,11 +519,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 {

View File

@ -0,0 +1,153 @@
{
"randomData": [
{
"title": "充值满减券",
"tag": "特惠充",
"desc": "满50元可用",
"price": "1",
"butText": "去看看",
"imgUrl": "/static/image/balance/pay-successful/chongzhi.png"
},
{
"title": "芭芭农场肥料",
"tag": "芭芭农场",
"desc": "现实翻倍",
"price": "",
"butText": "去看看",
"imgUrl": "/static/image/balance/pay-successful/88nongchang.png"
},
{
"title": "心相印抽纸优惠券",
"tag": "分期生活",
"desc": "满一元可用",
"price": "1",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/xinxiangyin.png"
},
{
"title": "蜜雪冰城全场券",
"tag": "外卖福利",
"desc": "满10元可用",
"price": "3",
"butText": "去领取",
"imgUrl": "/static/image/balance/pay-successful/mixue.png"
},
{
"title": "充值满减券",
"tag": "中国石化",
"desc": "满50元可用",
"price": "3",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/shiyou.png"
},
{
"title": "零售购物券",
"tag": "淘宝闪购",
"desc": "满29元可用",
"price": "8",
"butText": "去领取",
"imgUrl": "/static/image/balance/pay-successful/shangou.png"
},
{
"title": "公交乘车券",
"tag": "公交地铁",
"desc": "享3笔0.3乘车优惠",
"price": "0.8",
"butText": "去看看",
"imgUrl": "/static/image/balance/pay-successful/chengche.png"
},
{
"title": "电燃费优惠红包",
"tag": "生活缴费",
"desc": "缴电燃气费立减",
"price": "0.5",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/shuidian.png"
}
],
"defaultData": {
"user": "小王",
"money": "88888.88",
"payMethod": "招商银行卡(8888)",
"bannerTime": "07月12日12:22",
"styleType": "style3",
"style1": {
"couponlist": [
{
"title": "充值满减券",
"tag": "中国石化",
"desc": "满50元可用",
"price": "3",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/shiyou.png"
}
],
"bannerUrl": "/static/image/balance/transfer/bottom-banner-1.png"
},
"style2": {
"couponlist": [
{
"title": "蜜雪冰城全场券",
"tag": "外卖福利",
"desc": "满10元可用",
"price": "3",
"butText": "去领取",
"imgUrl": "/static/image/balance/pay-successful/mixue.png"
},
{
"title": "充值满减券",
"tag": "中国石化",
"desc": "满50元可用",
"price": "3",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/shiyou.png"
},
{
"title": "零售购物券",
"tag": "淘宝闪购",
"desc": "满29元可用",
"price": "8",
"butText": "去领取",
"imgUrl": "/static/image/balance/pay-successful/shangou.png"
},
{
"title": "公交乘车券",
"tag": "公交地铁",
"desc": "享3笔0.3乘车优惠",
"price": "0.8",
"butText": "去看看",
"imgUrl": "/static/image/balance/pay-successful/chengche.png"
}
]
},
"style3": {
"couponlist": [
{
"title": "充值满减券",
"tag": "中国石化",
"desc": "满50元可用",
"price": "3",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/shiyou.png"
},
{
"title": "心相印抽纸优惠券",
"tag": "分期生活",
"desc": "满一元可用",
"price": "1",
"butText": "免费领",
"imgUrl": "/static/image/balance/pay-successful/xinxiangyin.png"
},
{
"title": "蜜雪冰城全场券",
"tag": "外卖福利",
"desc": "满10元可用",
"price": "3",
"butText": "去领取",
"imgUrl": "/static/image/balance/pay-successful/mixue.png"
}
],
"bannerTime": "07月12日12:22"
}
}
}

View File

@ -0,0 +1,691 @@
<template>
<view class="container">
<nav-bar bgColor="#F5F5F5" :isBack="true" title="编辑账单详情" isRightButton @right-click="saveData"></nav-bar>
<view class="tabs">
<view class="tab-item" :class="{ active: payData.styleType == 'style1' }"
@click="payData.styleType = 'style1'">样式1
</view>
<view class="tab-item" :class="{ active: payData.styleType == 'style2' }"
@click="payData.styleType = 'style2'">样式2
</view>
<view class="tab-item" :class="{ active: payData.styleType == 'style3' }"
@click="payData.styleType = 'style3'">样式3
</view>
</view>
<view class="main-content">
<!-- 基础信息卡片 -->
<view class="card basic-card">
<view class="amount-wrap">
<text class="symbol">¥</text>
<auto-width-input class="alipay-font" type="digit" v-model="payData.money" fontSize="88rpx"
fontWeight="500" color="#1A1A1A" :showEdit="true"></auto-width-input>
</view>
<view class="info-row">
<view class="info-item">
<auto-width-input v-model="payData.user" fontSize="28rpx" color="#333"
:showEdit="true"></auto-width-input>
</view>
<view class="info-item" style="justify-content: flex-end;">
<text class="value-symbol">¥ </text>
<auto-width-input type="digit" v-model="payData.money" fontSize="28rpx" color="#333"
:showEdit="true"></auto-width-input>
</view>
</view>
<view class="info-row" style="margin-top: 16rpx;">
<view class="info-item">
<text class="label">付款方式</text>
</view>
<view class="info-item" style="justify-content: flex-end;">
<auto-width-input v-model="payData.payMethod" fontSize="28rpx" color="#333"
:showEdit="true"></auto-width-input>
</view>
</view>
</view>
<!-- 样式1 -->
<template v-if="payData.styleType == 'style1'">
<view class="card list-card">
<view class="card-title" style="display: flex; justify-content: space-between;">
支付有礼
<image style="width: 24px;height: 24px;" src="/static/image/common/random-dice.png"
mode="widthFix" @click="randomCoupon('style1')"></image>
</view>
<!-- 券1 -->
<view class="list-item" v-if="payData.style1.couponlist && payData.style1.couponlist[0]">
<view class="left-icon-wrap" @click="chooseImage('style1_coupon_0')">
<image v-if="payData.style1.couponlist[0].imgUrl" class="item-img"
:src="payData.style1.couponlist[0].imgUrl" mode="aspectFill"></image>
<image v-else class="edit-icon-center" src="/static/image/common/edit.png"></image>
</view>
<view class="item-content">
<view class="row-flex" style="flex-wrap: wrap;">
<view class="row-flex" style="margin-right: 12rpx;"
v-if="payData.style1.couponlist[0].price !== undefined">
<auto-width-input v-model="payData.style1.couponlist[0].price" fontSize="28rpx"
color="#E6372D" fontWeight="500"></auto-width-input>
<text class="main-title" style="color: #E6372D; width: auto;"></text>
<image class="edit-icon" src="/static/image/common/edit.png"
style="margin-left: 8rpx; width: 24rpx; height: 24rpx;"></image>
</view>
<view class="row-flex">
<auto-width-input v-model="payData.style1.couponlist[0].title" fontSize="28rpx"
color="#333" fontWeight="500" :showEdit="true"></auto-width-input>
</view>
</view>
<view class="row-flex mt">
<view class="tag-red" style="margin-right: 12rpx; display: flex; align-items: center;"
v-if="payData.style1.couponlist[0].tag !== undefined">
<auto-width-input v-model="payData.style1.couponlist[0].tag" fontSize="20rpx"
color="#E6372D" :showEdit="true"></auto-width-input>
</view>
<view class="row-flex" v-if="payData.style1.couponlist[0].desc !== undefined">
<auto-width-input v-model="payData.style1.couponlist[0].desc" fontSize="24rpx"
color="#999" :showEdit="true"></auto-width-input>
</view>
</view>
</view>
<view class="right-btn btn-get"
style="display: flex; align-items: center; justify-content: center;"
@click="toggleBtnText(payData.style1.couponlist[0])">
<text style="font-size: 24rpx; color: #fff; font-weight: 500;">{{
payData.style1.couponlist[0].butText || '免费领' }}</text>
</view>
</view>
</view>
<!-- 飞猪广告 -->
<view class="card fliggy-card">
<view class="banner-wrap" @click="toggleBanner">
<image class="banner-img"
:src="payData.style1.bannerUrl || '/static/image/balance/transfer/bottom-banner-1.png'"
mode="widthFix"></image>
<view class="guanggao-tag">
<image style="width: 70rpx;height: 32rpx;"
src="/static/image/balance/transfer/guanggao.png"></image>
</view>
<view class="yaoyiyao-container">
<view class="yaoyiyao-box">
<image class="img" src="/static/image/balance/transfer/yaoyiyao.png" mode="widthFix">
</image>
<text class="text">摇动或点击查看详情</text>
</view>
</view>
</view>
<view class="fliggy-footer"
v-if="payData.style1.bannerUrl == '/static/image/balance/transfer/bottom-banner-1.png'">
<view class="logo-box">
<image class="logo" src="/static/image/balance/transfer/feizhu.png" mode="aspectFit">
</image>
</view>
<view class="banner-info">
<view class="text-content">
<text class="b-title">飞猪旅行</text>
<text class="b-desc">飞猪出行 超多折扣</text>
</view>
<view class="b-btn">领优惠</view>
</view>
</view>
<view class="fliggy-footer"
v-if="payData.style1.bannerUrl == '/static/image/balance/transfer/bottom-banner-2.png'">
<view class="logo-box">
<image class="logo" src="/static/image/balance/transfer/taobaoshangou.png" mode="aspectFit">
</image>
</view>
<view class="banner-info" style="padding: 0;">
<view class="text-content">
<text class="b-title">淘宝闪购</text>
<text class="b-desc">每天领大额外面红包</text>
</view>
<view class="b-btn">去领取</view>
</view>
</view>
</view>
</template>
<!-- 样式2 -->
<template v-if="payData.styleType == 'style2'">
<view class="card list-card">
<view class="card-title" style="display: flex; justify-content: space-between;">支付有礼
<image style="width: 24px;height: 24px;" src="/static/image/common/random-dice.png"
mode="widthFix" @click="randomCoupon('style2')"></image>
</view>
<view class="list-item" v-for="(item, index) in payData.style2.couponlist" :key="index">
<view class="left-icon-wrap" @click="chooseImage('style2_coupon_' + index)">
<image v-if="item.imgUrl" class="item-img" :src="item.imgUrl" mode="aspectFill"></image>
<image v-else class="edit-icon-center" src="/static/image/common/edit.png"></image>
</view>
<view class="item-content">
<view class="row-flex" style="flex-wrap: wrap;">
<view class="row-flex" style="margin-right: 12rpx;" v-if="item.price !== undefined">
<auto-width-input v-model="item.price" fontSize="28rpx"
:color="item.price.includes('g') ? '#333' : '#E6372D'"
fontWeight="500"></auto-width-input>
<text class="main-title"
:style="{ color: item.price.includes('g') ? '#333' : '#E6372D', width: 'auto' }">{{
item.price.includes('g') ? '' : '元' }}</text>
<image class="edit-icon" src="/static/image/common/edit.png"
style="margin-left: 8rpx; width: 24rpx; height: 24rpx;"></image>
</view>
<view class="row-flex">
<auto-width-input v-model="item.title" fontSize="28rpx" color="#333"
fontWeight="500" :showEdit="true"></auto-width-input>
</view>
</view>
<view class="row-flex mt">
<view class="tag-red" :class="{ 'tag-orange': item.tag == '芭芭农场' }"
style="margin-right: 12rpx; display: flex; align-items: center;"
v-if="item.tag !== undefined">
<auto-width-input v-model="item.tag" fontSize="20rpx"
:color="item.tag == '芭芭农场' ? '#EF673C' : '#E6372D'"
:showEdit="true"></auto-width-input>
</view>
<view class="row-flex" v-if="item.desc !== undefined">
<auto-width-input v-model="item.desc" fontSize="24rpx" color="#999"
:showEdit="true"></auto-width-input>
</view>
</view>
</view>
<view class="right-btn btn-get" :class="{ 'btn-look': item.butText == '去看看' }"
style="display: flex; align-items: center; justify-content: center;"
@click="toggleBtnText(item)">
<text style="font-size: 24rpx; color: #fff; font-weight: 500;">{{ item.butText || '免费领'
}}</text>
</view>
</view>
</view>
</template>
<!-- 样式3 -->
<template v-if="payData.styleType == 'style3'">
<view class="card list-card" style="padding-bottom: 24rpx;">
<view class="flex-between" style="margin-bottom: 24rpx;">
<text class="card-title" style="margin-bottom: 0;">碰一下立减</text>
<view class="row-flex">
<auto-width-input v-model="payData.style3.bannerTime" fontSize="24rpx"
color="#666"></auto-width-input>
<text class="time-text" style="width: auto;">后过期</text>
<image class="edit-icon" src="/static/image/common/edit.png"
style="margin-left: 8rpx; width: 24rpx; height: 24rpx;"></image>
</view>
</view>
<view class="banner-box">
<text class="banner-time">{{ payData.style3.bannerTime }}后过期</text>
<image src="/static/image/balance/pay-successful/pengyipengbanner.png"
style="width: 100%; border-radius: 12rpx; display: block;" mode="widthFix"></image>
</view>
</view>
<view class="card-title" style="display: flex; justify-content: flex-end;padding: 0 16px;">
<image style="width: 24px;height: 24px;" src="/static/image/common/random-dice.png" mode="widthFix"
@click="randomCoupon('style3')">
</image>
</view>
<view class="card list-card" style="margin-top: 24rpx;">
<view class="list-item" v-for="(item, index) in payData.style3.couponlist" :key="index">
<view class="left-icon-wrap" @click="chooseImage('style3_coupon_' + index)">
<image v-if="item.imgUrl" class="item-img" :src="item.imgUrl" mode="aspectFill"></image>
<image v-else class="edit-icon-center" src="/static/image/common/edit.png"></image>
</view>
<view class="item-content" style="flex: 1;">
<view class="row-flex">
<auto-width-input v-model="item.title" fontSize="28rpx" color="#333" fontWeight="500"
:showEdit="true"></auto-width-input>
</view>
<view class="row-flex mt">
<view class="tag-red" style="margin-right: 12rpx; display: flex; align-items: center;"
v-if="item.tag !== undefined">
<auto-width-input v-model="item.tag" fontSize="20rpx" color="#E6372D"
:showEdit="true"></auto-width-input>
</view>
<view class="row-flex" v-if="item.desc !== undefined">
<auto-width-input v-model="item.desc" fontSize="24rpx" color="#999"
:showEdit="true"></auto-width-input>
</view>
</view>
</view>
<view class="row-flex" style="align-items: baseline;" v-if="item.price !== undefined">
<auto-width-input v-model="item.price" fontSize="40rpx" color="#E6372D" fontWeight="500"
:showEdit="true"></auto-width-input>
</view>
</view>
</view>
</template>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { defaultData, randomData } from './data.json';
import { util } from '@/utils/common.js';
import autoWidthInput from '@/components/common/auto-width-input.vue';
const payData = ref(JSON.parse(JSON.stringify(defaultData)));
const PAY_STORAGE_KEY = 'alipay_pay_successful_data';
const randomCoupon = (styleName) => {
// #ifdef APP-PLUS
const oldList = payData.value[styleName].couponlist || [];
oldList.forEach(item => {
if (item.imgUrl && !item.imgUrl.startsWith('/static') && !item.imgUrl.startsWith('http')) {
uni.removeSavedFile({ filePath: item.imgUrl });
}
});
// #endif
const count = payData.value[styleName].couponlist.length;
const shuffled = [...randomData].sort(() => 0.5 - Math.random());
const selected = shuffled.slice(0, count);
payData.value[styleName].couponlist = JSON.parse(JSON.stringify(selected));
};
const loadStorageData = () => {
const savedData = uni.getStorageSync(PAY_STORAGE_KEY);
if (savedData) {
payData.value = Object.assign({}, payData.value, savedData);
}
};
onLoad(() => {
loadStorageData();
});
const saveData = () => {
uni.setStorageSync(PAY_STORAGE_KEY, payData.value);
uni.navigateBack();
};
const chooseImage = (type) => {
uni.chooseImage({
count: 1,
success: (res) => {
const tempPath = res.tempFilePaths[0];
let oldPath = '';
if (type === 'style1_coupon_0') {
oldPath = payData.value.style1.couponlist[0].imgUrl;
} else if (type.startsWith('style2_coupon_')) {
const idx = parseInt(type.split('_')[2]);
oldPath = payData.value.style2.couponlist[idx].imgUrl;
} else if (type.startsWith('style3_coupon_')) {
const idx = parseInt(type.split('_')[2]);
oldPath = payData.value.style3.couponlist[idx].imgUrl;
}
const updateImgUrl = (path) => {
if (type === 'style1_coupon_0') {
payData.value.style1.couponlist[0].imgUrl = path;
} else if (type.startsWith('style2_coupon_')) {
const idx = parseInt(type.split('_')[2]);
payData.value.style2.couponlist[idx].imgUrl = path;
} else if (type.startsWith('style3_coupon_')) {
const idx = parseInt(type.split('_')[2]);
payData.value.style3.couponlist[idx].imgUrl = path;
}
};
// #ifdef APP-PLUS
if (oldPath && !oldPath.startsWith('/static') && !oldPath.startsWith('http')) {
//
uni.removeSavedFile({ filePath: oldPath });
}
uni.saveFile({
tempFilePath: tempPath,
success: (saveRes) => {
updateImgUrl(saveRes.savedFilePath);
}
});
// #endif
// #ifndef APP-PLUS
updateImgUrl(tempPath);
// #endif
}
});
};
const toggleBtnText = (item) => {
const textList = ['免费领', '去看看', '去领取'];
const currentIndex = textList.indexOf(item.butText);
if (currentIndex > -1) {
item.butText = textList[(currentIndex + 1) % textList.length];
} else {
item.butText = '免费领';
}
};
const toggleBanner = () => {
const current = payData.value.style1.bannerUrl || '/static/image/balance/transfer/bottom-banner-1.png';
if (current.includes('bottom-banner-1.png')) {
payData.value.style1.bannerUrl = '/static/image/balance/transfer/bottom-banner-2.png';
} else {
payData.value.style1.bannerUrl = '/static/image/balance/transfer/bottom-banner-1.png';
}
};
</script>
<style lang="less" scoped>
@import "@/common/main.css";
.container {
min-height: 100vh;
background-color: #F5F5F5;
padding-bottom: 60rpx;
}
.confirm-btn {
background-color: #3884FD;
color: #fff;
font-size: 26rpx;
padding: 10rpx 32rpx;
border-radius: 30rpx;
}
.tabs {
display: flex;
justify-content: center;
gap: 20rpx;
margin: 24rpx 0;
}
.tab-item {
padding: 12rpx 40rpx;
background-color: #fff;
color: #333;
font-size: 28rpx;
border-radius: 8rpx;
border: 1px solid #fff;
}
.tab-item.active {
color: #3884FD;
border-color: #3884FD;
}
.main-content {
padding: 0 0;
}
.card {
background: #fff;
border-radius: 16rpx;
margin: 24rpx;
padding: 32rpx;
}
.basic-card {
display: flex;
flex-direction: column;
align-items: center;
}
.amount-wrap {
display: flex;
align-items: baseline;
justify-content: center;
color: #1A1A1A;
margin-top: 10rpx;
margin-bottom: 50rpx;
.symbol {
font-size: 60rpx;
margin-right: 8rpx;
}
}
.info-row {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
.info-item {
display: flex;
align-items: center;
.label {
font-size: 28rpx;
color: #333;
width: 200rpx;
}
.value-symbol {
font-size: 28rpx;
color: #333;
}
}
.edit-icon {
width: 24rpx;
height: 24rpx;
margin-left: 10rpx;
}
.edit-icon-center {
width: 28rpx;
height: 28rpx;
}
.item-img {
width: 100%;
height: 100%;
border-radius: 50%;
}
.left-icon-wrap {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
background-color: #F5F5F5;
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
}
.list-card {
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #1A1A1A;
margin-bottom: 32rpx;
}
.list-item {
display: flex;
align-items: center;
margin-bottom: 40rpx;
&:last-child {
margin-bottom: 0;
}
}
}
.item-content {
flex: 1;
}
.row-flex {
display: flex;
align-items: center;
}
.mt {
margin-top: 8rpx;
}
.main-title {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
.sub-title {
font-size: 24rpx;
color: #999;
}
.tag-red {
font-size: 20rpx;
color: #E6372D;
background: #FFF1F0;
padding: 4rpx 10rpx;
border-radius: 6rpx;
}
.tag-orange {
color: #EF673C;
background: #FFF2F1;
}
.right-btn {
padding: 10rpx 28rpx;
border-radius: 26rpx;
font-size: 24rpx;
font-weight: 500;
}
.btn-get {
background-color: #E6372D;
color: #fff;
}
.btn-look {
background-color: #E6372D;
color: #fff;
}
.fliggy-card {
padding: 0;
overflow: hidden;
.banner-wrap {
position: relative;
.banner-img {
width: 100%;
display: block;
}
.guanggao-tag {
position: absolute;
top: 16rpx;
right: 16rpx;
}
.yaoyiyao-container {
position: absolute;
bottom: 20rpx;
width: 100%;
display: flex;
justify-content: center;
.yaoyiyao-box {
background: rgba(0, 0, 0, 0.4);
padding: 10rpx 24rpx;
border-radius: 30rpx;
display: flex;
align-items: center;
.img {
width: 24rpx;
margin-right: 10rpx;
}
.text {
color: #fff;
font-size: 24rpx;
}
}
}
}
.fliggy-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx;
.logo-box {
width: 76rpx;
height: 76rpx;
margin-right: 20rpx;
display: flex;
justify-content: center;
align-items: center;
background: #fff;
border-radius: 50%;
.logo {
width: 100%;
height: 100%;
}
}
.banner-info {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
.text-content {
display: flex;
flex-direction: column;
.b-title {
font-size: 28rpx;
color: #333;
font-weight: bold;
}
.b-desc {
font-size: 24rpx;
color: #999;
margin-top: 6rpx;
}
}
.b-btn {
background-color: #E6372D;
color: #fff;
padding: 10rpx 28rpx;
border-radius: 26rpx;
font-size: 24rpx;
}
}
}
}
.flex-between {
display: flex;
justify-content: space-between;
align-items: center;
}
.time-text {
font-size: 24rpx;
color: #666;
}
.banner-box {
position: relative;
width: 100%;
.banner-time {
position: absolute;
z-index: 10;
left: 170rpx;
top: 68rpx;
font-size: 20rpx;
color: #C70B0C;
}
}
</style>

View File

@ -0,0 +1,769 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_pay-successful" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_pay-successful')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="container">
<!-- 蓝色背景区域 -->
<view class="header-section">
<nav-bar ref="navBarRef" bgColor="transparent" :isBack="false" :buttonGroup="buttonGroup"
tipLayerType="pay-successful-tip" isTipLayer tipLayerText="修改支付数据" @button-click="handleButtonClick">
<template v-slot:right>
<view class="nav-link w100"
style="display: flex; align-items: center; justify-content: flex-end; white-space: nowrap; padding: 0;">
<text @click="util.goBack()" style="margin-right: 20rpx;">回首页</text>
</view>
</template>
<template v-slot:center>
<view class="status-box">
<image class="image" src="/static/image/balance/transfer/success.png"></image>
<text class="status-text">支付成功</text>
</view>
</template>
</nav-bar>
<!-- 转账状态信息 -->
<view class="success-info">
<view class="amount-box" @click="goEdit">
<text class="symbol">¥</text>
<text class="amount alipay-font">{{ Number(payData.money).toFixed(2) }}</text>
</view>
<!-- 收款方/付款方式 -->
<view class="details-box">
<view class="detail-item">
<text class="label">{{ payData.user }}</text>
<text class="value">{{ Number(payData.money).toFixed(2) }}</text>
</view>
<view class="detail-item">
<text class="label">付款方式</text>
<text class="value">{{ payData.payMethod }}</text>
</view>
</view>
</view>
</view>
<view class="main-content" :style="{ top: `${190 + statusBarHeight}px` }">
<!-- 样式1 -->
<template v-if="payData.styleType == 'style1'">
<view class="pay-success-card">
<image class="title-img" style="width: 106rpx;height: 28rpx;"
src="/static/image/balance/pay-successful/zhifuyouli.png" mode="widthFix"></image>
<view class="banner-footer" style="padding: 0 16rpx;">
<view class="logo-box">
<image class="logo" src="/static/image/balance/pay-successful/zhifufuli.png"
mode="aspectFit">
</image>
</view>
<view class="banner-info" style="border-top: none;">
<view class="text-content">
<text class="b-title">领取<text style="color: #EA4833;">今日</text>支付福利</text>
<text class="b-desc">领优惠支付更省钱</text>
</view>
<view class="b-btn">去看看</view>
</view>
</view>
<view class="banner-footer" style="padding: 0 16rpx;">
<view class="logo-box">
<image class="logo" :src="payData.style1.couponlist[0].imgUrl" mode="aspectFit">
</image>
</view>
<view class="banner-info">
<view class="text-content">
<text class="b-title">
<text
v-if="payData.style1.couponlist[0].price && payData.style1.couponlist[0].price > 0"
style="color: #EA4833;">{{ payData.style1.couponlist[0].price }}</text>
{{ payData.style1.couponlist[0].title }}
</text>
<text class="b-desc">
<text class="b-desc-tag">{{ payData.style1.couponlist[0].tag }}</text>
{{ payData.style1.couponlist[0].desc }}
</text>
</view>
<view class="b-btn">{{ payData.style1.couponlist[0].butText }}</view>
</view>
</view>
</view>
<view class="content-section">
<!-- 飞猪广告 -->
<view class="card fliggy-card">
<image class="banner-img" :src="payData.style1.bannerUrl" mode="widthFix" @click="toggleBanner">
</image>
<view class="guanggao-tag">
<image style="width: 70rpx;height: 32rpx;"
src="/static/image/balance/transfer/guanggao.png">
</image>
</view>
<view class="yaoyiyao-container">
<view class="yaoyiyao-box">
<image class="img" src="/static/image/balance/transfer/yaoyiyao.png" mode="widthFix">
</image>
<text class="text">摇动或点击查看详情</text>
</view>
</view>
<view class="banner-footer"
v-if="payData.style1.bannerUrl == '/static/image/balance/transfer/bottom-banner-1.png'">
<view class="logo-box">
<image class="logo" src="/static/image/balance/transfer/feizhu.png" mode="aspectFit">
</image>
</view>
<view class="banner-info" style="padding: 0;">
<view class="text-content">
<text class="b-title">飞猪旅行</text>
<text class="b-desc">飞猪出行 超多折扣</text>
</view>
<view class="b-btn">领优惠</view>
</view>
</view>
<view class="banner-footer"
v-if="payData.style1.bannerUrl == '/static/image/balance/transfer/bottom-banner-2.png'">
<view class="logo-box">
<image class="logo" src="/static/image/balance/transfer/taobaoshangou.png"
mode="aspectFit">
</image>
</view>
<view class="banner-info" style="padding: 0;">
<view class="text-content">
<text class="b-title">淘宝闪购</text>
<text class="b-desc">每天领大额外面红包</text>
</view>
<view class="b-btn">去领取</view>
</view>
</view>
</view>
</view>
</template>
<!-- 样式2 -->
<template v-if="payData.styleType == 'style2'">
<view class="pay-success-card">
<image class="title-img" style="width: 106rpx;height: 28rpx;"
src="/static/image/balance/pay-successful/zhifuyouli.png" mode="widthFix"></image>
<view class="banner-footer" style="padding: 0 16rpx;">
<view class="logo-box">
<image class="logo" src="/static/image/balance/pay-successful/zhifufuli.png"
mode="aspectFit">
</image>
</view>
<view class="banner-info" style="border-top: none;">
<view class="text-content">
<text class="b-title">领取<text style="color: #EA4833;">今日</text>支付福利</text>
<text class="b-desc">领优惠支付更省钱</text>
</view>
<view class="b-btn">去看看</view>
</view>
</view>
<view class="banner-footer" style="padding: 0 16rpx;"
v-for="item, index in payData.style2.couponlist" :key="index">
<view class="logo-box">
<image class="logo" :src="item.imgUrl" mode="aspectFit">
</image>
</view>
<view class="banner-info">
<view class="text-content">
<text class="b-title">
<text v-if="item.price" style="color: #EA4833;">{{ item.price }}</text>{{
item.title
}}</text>
<text class="b-desc">
<text class="b-desc-tag">{{ item.tag }}</text>
{{ item.desc }}</text>
</view>
<view class="b-btn">{{ payData.style1.couponlist[0].butText }}</view>
</view>
</view>
</view>
</template>
<template v-if="payData.styleType == 'style3'">
<view style="padding: 24rpx 20rpx;margin-bottom: 24rpx;" class="pay-success-card">
<text class="title">支付有礼</text>
<view class="banner-box">
<text class="banner-time">{{ payData.style3.bannerTime }}后过期</text>
<image src="/static/image/balance/pay-successful/pengyipengbanner.png" style="width: 100%;"
mode="widthFix"></image>
</view>
</view>
<view class="promo-card" v-for="item, index in payData.style3.couponlist" :key="index">
<view class="promo-left-box">
<image class="img" :src="item.imgUrl" mode="aspectFill"></image>
</view>
<view class="promo-right">
<view class="promo-content">
<text class="promo-title">{{ item.title }}</text>
<view class="promo-tags">
<text class="tag">{{ item.tag }}</text>
<text class="tag-desc">{{ item.desc }}</text>
</view>
</view>
<view class="promo-right-box">
<view class="price-box">
<text class="p-num alipay-font">{{ item.price }}</text>
<text class="p-unit"></text>
</view>
<view class="free-btn">{{ item.butText }}</view>
</view>
</view>
</view>
</template>
</view>
<!-- 底部操作区 -->
<view class="footer-section" :style="{'padding-bottom':($store.state.safeBottomAz+24)+'px'}">
<view class="finish-btn" @click="goBack">完成</view>
</view>
<ZdySafeBottom bgColor="#f5f5f5"/>
</view>
</template>
<script setup>
import {
ref,
onMounted,
reactive,
getCurrentInstance
} from 'vue';
import {
onShow,
onLoad
} from '@dcloudio/uni-app';
import {
util
} from '@/utils/common.js';
import {
defaultData
} from './data.json';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
const {
appContext,
proxy
} = getCurrentInstance();
const statusBarHeight = ref(0);
const PAY_STORAGE_KEY = 'alipay_pay_successful_data';
const payData = ref(defaultData);
const bannerImages = [
'/static/image/balance/transfer/bottom-banner-1.png',
'/static/image/balance/transfer/bottom-banner-2.png'
];
//
const loadStorageData = () => {
const savedData = uni.getStorageSync(PAY_STORAGE_KEY);
if (savedData) {
payData.value = savedData;
}
};
loadStorageData();
const navBarRef = ref(null);
const buttonGroup = ref([{
name: '编辑支付数据',
action: 'edit'
}]);
const handleButtonClick = (button) => {
if (button.action === 'edit') {
goEdit()
}
};
const goEdit = () => {
uni.navigateTo({
url: '/pages/balance/pay-successful/edit'
});
}
const toggleBanner = () => {
const currentIndex = bannerImages.indexOf(payData.value.style1.bannerUrl);
const nextIndex = (currentIndex + 1) % bannerImages.length;
payData.value.style1.bannerUrl = bannerImages[nextIndex];
uni.setStorageSync(PAY_STORAGE_KEY, payData.value);
};
onLoad(() => {
//
proxy.$apiUserEvent('all', {
type: 'click',
key: 'pay-successful',
value: "支付成功"
})
})
onMounted(() => {
const systemInfo = uni.getSystemInfoSync();
if (systemInfo.statusBarHeight) {
statusBarHeight.value = systemInfo.statusBarHeight;
}
});
onShow(() => {
loadStorageData();
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F5F5F5')
uni.setNavigationBarColor({
animation: { //
duration: 100,
timingFunc: 'easeIn'
}
})
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
}, 500);
// #endif
})
const goBack = () => {
uni.navigateBack();
};
</script>
<style lang="less" scoped>
@import "@/common/main.css";
.container {
min-height: 100vh;
background-color: #F5F5F5;
}
.header-section {
height: 860rpx;
background: linear-gradient(181deg, #3884FD 60%, rgba(30, 128, 253, 0.5) 81.75%, rgba(30, 128, 253, 0) 100%);
::v-deep .nav-bar-container {
position: relative !important;
}
::v-deep .placeholder-box {
display: none !important;
background: transparent;
}
::v-deep .tipLayer {
position: absolute;
}
::v-deep .uni-navbar__header-btns {
width: 100px !important;
}
}
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40rpx;
}
.nav-link {
padding: 0 30rpx;
text-align: right;
font-size: 28rpx;
line-height: 28rpx;
color: #fff;
}
.success-info {
display: flex;
flex-direction: column;
align-items: center;
}
.pay-success-card {
margin: 24rpx;
margin-top: 16rpx;
background-color: #FFFFFF;
border-radius: 16rpx;
margin-bottom: 0;
.title-img {
width: 106rpx;
height: 28rpx;
margin-top: 22rpx;
margin-left: 16rpx;
}
.title {
font-weight: 700;
font-size: 24rpx;
color: #252525;
line-height: 24rpx;
}
.banner-box {
position: relative;
width: 660rpx;
height: 166rpx;
margin-top: 18rpx;
.banner-time {
position: absolute;
z-index: 10;
left: 156rpx;
top: 68rpx;
font-weight: 400;
font-size: 20rpx;
color: #C70B0C;
line-height: 20rpx;
}
}
}
.status-box {
display: flex;
align-items: center;
margin-top: 10rpx;
height: 34rpx;
.image {
width: 34rpx;
height: 34rpx;
margin-right: 12rpx;
}
.status-text {
font-size: 34rpx;
color: #FFFFFF;
font-weight: 500;
line-height: 34rpx;
}
}
.amount-box {
display: flex;
align-items: baseline;
color: #fff;
margin-top: 20rpx;
margin-bottom: 60rpx;
.symbol {
font-size: 68rpx;
margin-right: 4rpx;
}
.amount {
font-size: 92rpx;
line-height: 92rpx;
font-weight: 500;
}
}
.details-box {
width: 100%;
padding: 0 46rpx;
.detail-item {
display: flex;
justify-content: space-between;
margin-bottom: 20rpx;
height: 26rpx;
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
text {
font-size: 26rpx;
color: rgba(255, 255, 255, 0.9);
line-height: 26rpx;
}
}
}
.content-section {
padding: 24rpx;
padding-bottom: 16rpx;
// background-color: #F5F5F5;
border-radius: 30rpx 30rpx 0 0;
}
.card {
background-color: #fff;
border-radius: 20rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.02);
}
.fliggy-card {
overflow: hidden;
position: relative;
.banner-img {
width: 100%;
}
.guanggao-tag {
position: absolute;
top: 16rpx;
right: 16rpx;
}
.yaoyiyao-container {
width: 100%;
display: flex;
justify-content: center;
margin-top: -80rpx;
padding-bottom: 16rpx;
.yaoyiyao-box {
padding: 12rpx 18rpx;
display: flex;
align-items: center;
background-color: #000000;
border-radius: 28rpx;
opacity: 0.5;
.img {
width: 32rpx;
height: 32rpx;
margin-right: 14rpx;
}
.text {
color: #FFFFFF;
font-size: 24rpx;
line-height: 24rpx;
}
}
}
}
.banner-footer {
padding: 24rpx;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
.logo-box {
width: 76rpx;
height: 76rpx;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20rpx;
.logo {
width: 76rpx;
height: 76rpx;
}
}
.banner-info {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1rpx solid #F6F6F6;
padding: 30rpx 0;
.text-content {
display: flex;
flex-direction: column;
}
.b-title {
font-size: 28rpx;
line-height: 28rpx;
color: #1A1A1A;
font-weight: 500;
}
.b-desc {
margin-top: 12rpx;
font-size: 24rpx;
line-height: 24rpx;
color: #969696;
.b-desc-tag {
font-size: 24rpx;
color: #E4711F;
background-color: #FDF6F3;
line-height: 24rpx;
padding: 2px 4px;
}
}
}
.b-btn {
padding: 0 20rpx;
background-color: #FF3A31;
color: #fff;
font-size: 26rpx;
height: 52rpx;
line-height: 52rpx;
border-radius: 8rpx;
}
&::after {
content: '';
position: absolute;
background: url('/static/image/balance/pay-successful/more.png');
top: 16rpx;
right: 20rpx;
width: 24rpx;
height: 12rpx;
background-size: contain;
background-repeat: no-repeat;
}
}
.footer-section {
padding: 0px 40rpx 0;
margin-top: 50rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: 0;
display: flex;
justify-content: center;
padding-bottom: 24rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
.finish-btn {
height: 84rpx;
width: 390rpx;
line-height: 84rpx;
text-align: center;
border: 1px solid #2172CA;
border-radius: 42rpx;
color: #2172CA;
font-size: 32rpx;
background-color: transparent;
}
}
.main-content {
position: absolute;
left: 0;
top: 190px;
width: 100%;
}
.promo-card {
// padding: 30rpx;
margin: 16rpx;
margin-bottom: 10rpx;
display: flex;
justify-content: space-between;
align-items: center;
.promo-left-box {
display: flex;
align-items: center;
padding: 34rpx;
background-image: url("/static/image/balance/transfer/card-bg-2-left.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
height: 144rpx;
margin-right: -1px;
.img {
width: 70rpx;
height: 70rpx;
border-radius: 50%;
}
}
.promo-right {
height: 144rpx;
background-image: url("/static/image/balance/transfer/card-bg-2-right.png");
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: center;
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx 32rpx 24rpx;
.promo-title {
font-size: 28rpx;
line-height: 28rpx;
font-weight: 500;
color: #1A1A1A;
}
.promo-tags {
display: flex;
align-items: center;
margin-top: 8rpx;
.tag {
font-size: 24rpx;
line-height: 34rpx;
height: 34rpx;
color: #EF673C;
background-color: #FFF2F1;
border-radius: 4rpx;
margin-right: 12rpx;
padding: 0 12rpx;
}
.tag-desc {
font-size: 24rpx;
line-height: 24rpx;
color: #969696;
}
}
}
.promo-right-box {
display: flex;
flex-direction: column;
align-items: center;
.price-box {
color: #F93950;
.p-num {
font-size: 34rpx;
line-height: 24rpx;
font-weight: 500;
}
.p-unit {
font-size: 24rpx;
line-height: 24rpx;
margin-left: 2rpx;
}
}
.free-btn {
height: 52rpx;
line-height: 52rpx;
padding: 0 24rpx;
background-color: #FA3A49;
color: #fff;
font-size: 24rpx;
border-radius: 26rpx;
}
}
}
</style>

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 {
@ -502,7 +609,7 @@ let {
} = toRefs(data)
onShow(() => {
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F5F5F5')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
@ -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,9 +1,9 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark :dark="data.dark" source="uni_alipay_bill_details" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_bill_details')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view style="overflow: hidden;overflow-y: scroll; height: 100vh;">
@ -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">
@ -237,6 +241,8 @@
</view>
</view>
<ZdySafeBottom bgColor="#F5F5F5"/>
</view>
@ -247,13 +253,15 @@
import navBar from '@/components/nav-bar/nav-bar.vue'
import billManagementPopup from '@/components/bill-management-popup/bill-management-popup.vue'
import addBillJson from '@/static/json/add-bill.json'
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
const {
billBottomIconList,
classifyTabBar
} = addBillJson
import {
util,
randomUtil
randomUtil,
numberUtil
} from '@/utils/common.js'
import {
@ -359,7 +367,7 @@ const visibleItemInfoList = computed(() => {
onShow(() => {
getBillData(data.billId)
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F5F5F5')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");

View File

@ -1,9 +1,9 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark :dark="data.dark" source="uni_alipay_bill" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_bill')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view style="overflow: hidden;height: 100vh;;">
@ -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>
@ -112,6 +112,7 @@
<view class="bill-list">
<BalanceList :isBalance="false" :list="item.list" @longPress="onLongPress" @onBill="billClick" />
</view>
<ZdySafeBottom bgColor="#F5F5F5"/>
</view>
</scroll-view>
@ -129,10 +130,12 @@
<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'
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
import {
reactive,
@ -246,7 +249,7 @@ onShow(() => {
//
updateStatusBarHeight()
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#F5F5F5')
uni.setNavigationBarColor({
animation: { //
@ -283,7 +286,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 +344,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 +626,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);

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

@ -0,0 +1,112 @@
<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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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]" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}"></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(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
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);
}
// #endif
})
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

@ -1,9 +1,9 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="container">
@ -20,7 +20,7 @@
</view>
</view>
</view>
<image ref="bgImage" class="bg-image" src="@/static/image/alipay-bill/main-bg.png" mode="widthFix"></image>
<image ref="bgImage" class="bg-image" :src="billBgImage" mode="widthFix"></image>
<image class="eye-image" src="@/static/image/alipay-bill/hide.png" mode="widthFix"></image>
<image class="find-more" src="@/static/image/alipay-bill/find-more.png" mode=""></image>
@ -32,18 +32,18 @@
<view class="frist-money money-type-item">
<view class="type-input-box">
<!-- <{{ moneyFrist.type }}>支出最多 -->
<<view class="percentage-input-box">
&lt;<view class="percentage-input-box">
<text class="ghost-text">{{ moneyFrist.type }}</text>
<input class="percentage-input" type="text" v-model="moneyFrist.type"
@input="inputPercentage($event, 'moneyFrist')" maxlength="4"></input>
</view>>支出最多
@input="inputPercentage($event, 'moneyFrist')" maxlength="4" />
</view>&gt;支出最多
</view>
<view class="money">
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyFrist.percentage }}</text>
<input class="percentage-input" type="digit" v-model="moneyFrist.percentage"
@input="inputPercentage($event, 'moneyFrist')"></input>
@input="inputPercentage($event, 'moneyFrist')" />
</view>
<view>%</view>
</view>
@ -54,7 +54,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneySecond.type }}</text>
<input class="percentage-input" type="text" v-model="moneySecond.type"
@input="inputPercentage($event, 'moneySecond')"></input>
@input="inputPercentage($event, 'moneySecond')" />
</view>
</view>
<view class="money">
@ -62,7 +62,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneySecond.percentage }}</text>
<input class="percentage-input" type="digit" v-model="moneySecond.percentage"
@input="inputPercentage($event, 'moneySecond')"></input>
@input="inputPercentage($event, 'moneySecond')" />
</view>
<view>%</view>
</view>
@ -73,7 +73,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyThird.type }}</text>
<input class="percentage-input" type="text" v-model="moneyThird.type"
@input="inputPercentage($event, 'moneyThird')"></input>
@input="inputPercentage($event, 'moneyThird')" />
</view>
</view>
<view class="money">
@ -81,7 +81,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyThird.percentage }}</text>
<input class="percentage-input" type="digit" v-model="moneyThird.percentage"
@input="inputPercentage($event, 'moneyThird')"></input>
@input="inputPercentage($event, 'moneyThird')" />
</view>
<view>%</view>
</view>
@ -92,7 +92,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyFourth.type }}</text>
<input class="percentage-input" type="text" v-model="moneyFourth.type"
@input="inputPercentage($event, 'moneyFourth')"></input>
@input="inputPercentage($event, 'moneyFourth')" />
</view>
</view>
<view class="money">
@ -100,7 +100,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyFourth.percentage }}</text>
<input class="percentage-input" type="digit" v-model="moneyFourth.percentage"
@input="inputPercentage($event, 'moneyFourth')"></input>
@input="inputPercentage($event, 'moneyFourth')" />
</view>
<view>%</view>
</view>
@ -111,7 +111,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyFifth.type }}</text>
<input class="percentage-input" type="text" v-model="moneyFifth.type"
@input="inputPercentage($event, 'moneyFifth')"></input>
@input="inputPercentage($event, 'moneyFifth')" />
</view>
</view>
<view class="money">
@ -119,7 +119,7 @@
<view class="percentage-input-box">
<text class="ghost-text">{{ moneyFifth.percentage }}</text>
<input class="percentage-input" type="digit" v-model="moneyFifth.percentage"
@input="inputPercentage($event, 'moneyFifth')"></input>
@input="inputPercentage($event, 'moneyFifth')" />
</view>
<view>%</view>
</view>
@ -156,7 +156,7 @@
<!-- <view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view> -->
</template>
@ -169,10 +169,9 @@ import {
getCurrentInstance
} from 'vue';
import {
onLoad,
onShow,
onUnload
onLoad
} from '@dcloudio/uni-app';
import { imgLocal } from '@/utils/common.js';
const statusBarHeight = ref(0);
const dialogPopup = ref(null);
const timeDialogPopup = ref(null);
@ -203,7 +202,7 @@ const data = reactive({
type: '服饰装扮',
percentage: "83"
},
navBarHeight: 0
billBgImage: ''
})
let {
@ -214,11 +213,11 @@ let {
moneyFourth,
moneyFifth,
time,
navBarHeight
billBgImage
} = toRefs(data)
onLoad(() => {
onLoad(async () => {
//
proxy.$apiUserEvent('all', {
type: 'click',
@ -228,6 +227,9 @@ onLoad(() => {
time.value = getYesterday();
statusBarHeight.value = uni.getSystemInfoSync().statusBarHeight;
data.billBgImage = await imgLocal.getLocalImage('alipay-bill/main-bg.png');
console.log("data.billBgImage", data.billBgImage)
const config = uni.getStorageSync('config')
console.log("---config---", config);
const font = config.config['client.uniapp.font']

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

@ -0,0 +1,83 @@
<template>
<view>
<nav-bar title="视频教程" bg-color="transparent">
<template v-slot:statusBar>
<view class="top-bg-box">
<image style="width: 100%;" src="/static/image/common/shipinjiaocheng/top-bg.png" mode="widthFix">
</image>
</view>
</template>
</nav-bar>
<view class="video-list">
<view class="video-item" 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 flex-1">{{ item.text }}</text>
<uni-icons type="forward" size="18" color="#AEAEAE"></uni-icons>
</view>
</view>
<ZdySafeBottom bgColor="#f0f3f8"/>
</view>
</template>
<script setup>
import { ref } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { storage } from "../../../utils/storage";
import {
util
} from '@/utils/common.js'
const videoHelpList = ref([]);
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
onLoad(() => {
const configData = storage.get("config")
videoHelpList.value = configData.config['client.uniapp.alipay.video_help'] || []
})
/**
* 点击视频教程
* @param item
*/
const clickVideoHelp = (item) => {
const url = item.url
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${item.text}`)
}
</script>
<style lang="less">
.video-list {
background-color: #FFFFFF;
border-radius: 32rpx;
margin: 0 32rpx;
.video-item {
display: flex;
align-items: center;
padding: 20rpx 32rpx;
border-bottom: 1rpx solid #E4E4E4;
&:last-child {
border-bottom: none;
}
.video-help-title {
width: 100%;
font-size: 32rpx;
color: #1A1A1A;
line-height: 42rpx;
margin-left: 16rpx;
}
}
.video-help-img {
flex-shrink: 0;
width: 72rpx;
height: 72rpx;
}
}
</style>

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&&!APP-HARMONY
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,9 +1,9 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark :dark="data.dark" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark dark="light" source="uni_alipay_finance" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_finance')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="container flex flex-column" @touchstart="handleTouchStart" @touchmove.stop.prevent="handleTouchMove"
@ -163,7 +163,7 @@
</view>
</view>
<view v-if="!selectedImage" class="content flex-1 flex-column flex" @touchstart="clickHandleTouchStart"
<view v-if="!selectedImage" class="content flex-1 flex-column flex" @touchstart="clickHandleTouchStart" :style="{'padding-bottom':$store.state.safeBottomAz+'px'}"
@touchend="clickHandleTouchEnd">
<view v-if="financeInfo.bgImage" class="w100 h100" style="margin-top: -1px;">
<image class="w100 h100" :src="financeInfo.bgImage" mode="widthFix">
@ -188,13 +188,13 @@
</view>
<canvas canvas-id="crop-canvas"
style="position: fixed; left: -9999px; width: 750rpx; height: 100vh; pointer-events: none;"></canvas>
</view>
<!-- 底部导航栏 -->
<view class="bottom-box"
:class="`navigation-menu-${financeInfo.navigationMenuStyle}`, { 'ios-bottom-box': $system == 'iOS' }">
<view class="bottom-item" v-for="item in navigationMenu" :key="item.name" @click="clickBottomItem(item)">
: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)" :style="{'padding-bottom':$store.state.safeBottomAz+'px'}">
<image
:src="`/static/image/finance-management/navigation-menu/${financeInfo.navigationMenuStyle}/${item.icon}.png`">
</image>
@ -244,6 +244,7 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import { imgLocal } from '@/utils/common.js';
import {
ref,
toRefs,
@ -263,6 +264,7 @@ import {
navigationMenu,
myAssets
} from '@/static/json/fortune.json'
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
import {
numberUtil,
util
@ -298,7 +300,7 @@ const buttonGroup = [{
}
}]
const defualtData = {
let defualtData = {
benefitsText: '尊享礼遇18000元/年',
cardHolderName: 'XiaoMing',
vipLevel: "v3",
@ -355,13 +357,16 @@ onReady(() => {
}).exec()
})
onShow(() => {
onShow(async () => {
defualtData.bgImage = await imgLocal.getLocalImage('finance-management/bg-style/style-1.png');
console.log(defualtData.bgImage)
financeInfo.value = {
...defualtData,
...uni.getStorageSync('financeInfo')
}
console.log("financeInfo.value", financeInfo.value)
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor(financeInfo.value.navigationMenuStyle == 'style-1' ? '#ffffff' : '#F8F8F8')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
@ -580,7 +585,9 @@ const chooseImage = () => {
selectedImage.value = res.tempFilePaths[0]
data.showMask = true
setTimeout(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("dark");
// #endif
}, 500)
}
})
@ -659,7 +666,9 @@ const confirmImage = () => {
financeInfo.value.bgImage = saveRes.savedFilePath
selectedImage.value = '' //
setTimeout(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("light");
// #endif
}, 200)
//
uni.setStorageSync("financeInfo", financeInfo.value)
@ -708,7 +717,9 @@ const clickHandleTouchEnd = () => {
const closeImage = () => {
selectedImage.value = ''
data.showMask = false
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.setStatusBarStyle("light");
// #endif
return false
}
@ -746,7 +757,7 @@ const closeStyleDialog = () => {
const confirmStyleDialog = (type) => {
financeInfo.value.navigationMenuStyle = type
uni.setStorageSync('financeInfo', financeInfo.value)
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor(financeInfo.value.navigationMenuStyle == 'style-1' ? '#ffffff' : '#F8F8F8')
// #endif
stylePopup.value.close()

View File

@ -2,13 +2,12 @@
<view>
<NavBar title="选择理财图片样式" bgColor="#F5F5F5" isRightButton @right-click="handleRightButtonClick"></NavBar>
<view class="conatiner flex flex-justify-between">
<view v-for="index in 6" :key="index" class="cover-item flex flex-column flex-align-center"
<view v-for="(item, index) in bgList" :key="index" class="cover-item flex flex-column flex-align-center"
@click="data.selectedImage = index">
<view class="image-box" :class="{ 'active': index == data.selectedImage }">
<image class="w100 h100" :src="`/static/image/finance-management/covers/cover-${index}.png`"
mode="widthFix"></image>
<image class="w100 h100" :src="item" mode="widthFix"></image>
</view>
<view class="text">样式{{ index }}</view>
<view class="text">样式{{ index + 1 }}</view>
</view>
</view>
</view>
@ -17,6 +16,8 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue'
import { ref, reactive, toRefs, onMounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import { imgLocal } from '@/utils/common.js';
const defualtData = {
benefitsText: '尊享礼遇18000元/年',
@ -82,6 +83,14 @@ const data = reactive({
})
const { bgImage, selectedImage, financeInfo } = toRefs(data)
const bgList = ref([])
onLoad(async () => {
for (let i = 1; i <= 6; i++) {
const imgPath = await imgLocal.getLocalImage(`finance-management/covers/cover-${i}.png`)
bgList.value.push(imgPath)
}
})
onMounted(() => {
financeInfo.value = {
@ -90,11 +99,12 @@ onMounted(() => {
}
})
const handleRightButtonClick = () => {
const handleRightButtonClick = async () => {
const financeInfoData = {
...financeInfo.value
}
financeInfoData.bgImage = `/static/image/finance-management/bg-style/style-${data.selectedImage}.png`
// financeInfoData.bgImage = `finance-management/bg-style/style-${data.selectedImage}.png`
financeInfoData.bgImage = await imgLocal.getLocalImage(`finance-management/bg-style/style-${data.selectedImage + 1}.png`)
uni.setStorageSync('financeInfo', financeInfoData)
uni.navigateBack()
}

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>
@ -77,16 +76,16 @@
</view>
</view>
<view class="group-box">
<!-- <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>
</view>
</view>
</view>
</view> -->
<view class="group-box">
<image class="title-img" src="/static/image/index/monixiaobao.png"></image>
@ -108,31 +107,36 @@
<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>
src="/static/image/index/shipinjiaocheng-banner.png" mode="widthFix"
@click="util.goPage(`/pages/common/shipinjiaocheng/shipinjiaocheng`)"></image>
</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 - 36) / 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>
<!-- <view style="border: 1px solid red; text-align: center;padding: 10px;" @click="exit">退出模拟器</view> -->
<view class="footer-box" :class="{ 'ios-padding-bottom': platform === 'ios' }">
<text class="vision-text">版本:{{ vision }}</text>
<text class="vision-text margin-l-6" v-if="qqgroup.enable">{{ qqgroup.text }}</text>
<text class="vision-text" @click="copyNumber(qqgroup.number)">{{ qqgroup.number }}</text>
</view>
<view v-if="platform != 'ios'" :style="{'height':'40px','background':'#F0F4F9'}">
</view>
<view v-if="platform != 'ios'" class="footer-box-safeBottom" :style="{'height':'40px','background':'#F0F4F9',width:data.windowWidth+'px'}">
</view>
</scroll-view>
</view>
</view>
</template>
@ -158,8 +162,18 @@ import {
onLoad,
onShow,
onHide,
onUnload
onUnload,
onReady
} from '@dcloudio/uni-app';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
onReady(() => {
setTimeout(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.navigator.closeSplashscreen();
// #endif
}, 500);
});
// 内部埋点方法
const apiUserEvent = async (type, adminData) => {
@ -189,7 +203,7 @@ const goRechargePage = () => {
}
})
uni.navigateTo({
url: '/pages/common/recharge/index'
url: '/pages/common/recharge/index?source=uni_alipay_index'
});
}
@ -218,46 +232,131 @@ const menuList = [{
name: "花呗",
isHot: false,
path: "/pages/ant-credit-pay/index"
}, {
icon: "zhuanzhang",
name: "转账模拟",
isHot: false,
path: "/pages/balance/transfer/transfer"
}, {
icon: "zhifuchenggong",
name: "支付成功",
isHot: false,
path: "/pages/balance/pay-successful/pay-successful"
}, {
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: "工资单",
path: "/pages/other/splash/splash"
icon: "/static/image/index/qita/gouwu.png",
name: "购物",
path: "/pages/shopping/index"
},
{
icon: "/static/image/other/bank/bank.png",
name: "银行卡",
path: "/pages/other/bank/index"
},
{
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/shipinqunliao.png",
name: "视频群聊",
path: "/pages/other/video-group-chat/video-group-chat"
},
{
icon: "/static/image/index/qita/danliao.png",
name: "视频聊天",
path: "/pages/other/video-chat/video-chat"
},
{
icon: "/static/image/index/qita/zhifuchenggong-wx.png",
name: "微信支付",
path: "/pages/other/wx-payment-success/wx-payment-success"
},
{
icon: "/static/image/index/qita/gongzidan.png",
name: "工资条",
path: "/pages/other/splash/splash"
},
{
icon: "/static/image/index/qita/card.png",
name: "身份证",
path: "/pages/other/card/card"
},
{
icon: "/static/image/index/qita/about-iphone.png",
name: "关于本机",
path: "/pages/other/about-this-iphone/about-this-iphone"
},
{
icon: "/static/image/index/qita/game.png",
name: "游戏",
path: "/pages/other/game/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/ranking.png",
name: "从夯倒拉排名",
path: "/pages/other/ranking/ranking"
},
{
icon: "/static/image/other/score/gaokao.png",
name: "高考",
path: "/pages/other/score/index"
},
]
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,
payslip: false,
score: false,
})
const {
isReady,
statusBarHeight,
windowWidth,
windowHeight,
@ -289,24 +388,58 @@ 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 != '身份证' && item.name != '工资条' && item.name != '高考') {
return true
} else if (item.name == '银行卡' && data.bank) {
return true
} else if (item.name == '身份证' && data.idcard) {
return true
} else if (item.name == '工资条' && data.payslip) {
return true
} else if (item.name == '高考' && data.score) {
return true
} else {
return false
}
}
/**
* 获取用户数据(从服务器)
*/
@ -330,11 +463,23 @@ 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
}
if (configResult.value.config['client.uniapp.score'] || false) {
data.score = true
}
if (configResult.value.config['client.uniapp.payslip'] || false) {
data.payslip = true
}
// 刷新页面数据
setUserData()
@ -387,7 +532,7 @@ const setUserData = () => {
const userInfoData = storage.get("userInfo")
data.userInfo = userInfoData || {
user_id: '加载中...',
avater: '/static/default-avatar.png',
avater: '',
vip: 0,
vip_expire: ''
}
@ -445,15 +590,6 @@ const openVip = () => {
goRechargePage()
}
/**
* 点击视频教程
* @param item
*/
const clickVideoHelp = (item) => {
const url = item.url
util.goPage(`/pages/common/webview/webview?url=${encodeURIComponent(url)}&title=${item.text}`)
}
/**
* 点击公告
*/
@ -461,7 +597,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}`)
}
/**
@ -499,7 +635,9 @@ const exit = () => {
console.log('宿主App回传的数据' + ret);
});
} else {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.runtime.quit();
// #endif
}
}
@ -633,6 +771,11 @@ onUnload(() => {
})
</script>
<style>
.footer-box-safeBottom{
position: fixed;
left: 0;
bottom: 0;
}
.container {
background-color: #F0F4F9;
}
@ -830,7 +973,7 @@ onUnload(() => {
}
.group-box {
margin: 32rpx;
margin: 16px;
margin-bottom: 0;
}
@ -843,7 +986,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;
@ -887,7 +1030,7 @@ onUnload(() => {
display: flex;
flex-direction: column;
justify-content: flex-end;
margin-bottom: 32rpx;
margin-bottom: 10rpx;
}
.menu-item {
@ -924,6 +1067,7 @@ onUnload(() => {
.activity-box {
margin: 0 32rpx;
margin-top: 20rpx;
}
.footer-box {
@ -933,6 +1077,7 @@ onUnload(() => {
justify-content: center;
margin-top: 40rpx;
margin-bottom: 10rpx;
padding-bottom: 20px;
}
.vision-text {

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,778 @@
<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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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>
<ZdySafeBottom v-if="data.phone!='oppo'&&data.phone!='xiaomi'" bgColor="#fff"/>
<!-- 添加短信弹窗 -->
<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 ZdySafeBottom from '@/components/safeBottom/safeBottom.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&&!APP-HARMONY
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,425 @@
<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>
<ZdySafeBottom bgColor="#f0f3f8"/>
</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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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();
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
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&&!APP-HARMONY
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,9 +1,10 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark dark="light" source="uni_alipay_other_airTickets_ctrip" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_airTickets_ctrip')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="codefun-flex-col page" :style="{ '--font-weight-bold': $system == 'iOS' ? 500 : 700 }">
@ -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>
@ -308,8 +309,8 @@
<image class="image_19 pos_15" src="/static/image/other/air-tickets/ctrip-air/connect-icon.png" />
</view>
</view>
<view class="codefun-flex-col codefun-mt-8">
<view class="codefun-flex-col section_12">
<view class="codefun-flex-col codefun-mt-8" :style="{'padding-bottom':$store.state.safeBottomAz+'px'}">
<view class="codefun-flex-col section_12" >
<text class="codefun-self-start font_6 text_52">出行服务</text>
<view class="codefun-self-stretch grid_2 mt-19">
<view class="codefun-flex-col codefun-items-center grid-item_4">
@ -343,7 +344,7 @@
<text class="font_8 text_54">更多服务</text>
<image class="image_11 codefun-ml-6" src="/static/image/other/air-tickets/ctrip-air/more.png" />
</view>
<view class="codefun-flex-col codefun-justify-start section_14">
<view class="codefun-flex-col codefun-justify-start section_14" :style="{'padding-bottom':$store.state.safeBottomAz+'px'}">
<view class="codefun-flex-row equal-division_3">
<view
class="codefun-flex-col codefun-justify-start codefun-items-center text-wrapper_5 text-wrapper_1">
@ -366,12 +367,14 @@
<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';
import airlineJson from '@/static/json/air-line.json';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
const buttonGroup = [{
name: "编辑机票信息",
click: () => {
@ -425,7 +428,7 @@ onShow(() => {
if (stored) {
Object.assign(data.airTicketsInfo, stored);
}
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");

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,9 +1,10 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark dark="light" source="uni_alipay_other_airTickets_fliggy" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_airTickets_fliggy')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="page-container">
@ -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>
@ -278,10 +279,13 @@
<!-- Note: User image shows floating bottom bar but cut off. Assuming standard action buttons -->
</view>
<view class="safe-area-inset-bottom"></view> <!-- Spacer -->
<view class="" :style="{'height':$store.state.safeBottomAz+'px'}">
</view>
</scroll-view>
<view class="footer-bar">
<view class="flex flex-justify-between">
<view class="flex flex-justify-between" :style="{'padding-bottom':$store.state.safeBottomAz+'px'}">
<view class="flex-1 flex flex-align-center flex-justify-center">
<image class="footer-icon" src="/static/image/other/air-tickets/fliggy-air/zhibo.png" />
<text class="text">联系飞猪</text>
@ -298,11 +302,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 +375,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) {
@ -380,7 +395,7 @@ onShow(() => {
const mergedData = deepMerge(baseData, stored);
Object.assign(data.airTickrtInfo, mergedData);
}
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");

View File

@ -1,9 +1,10 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark dark="light" source="uni_alipay_other_airTickets_qunar" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_airTickets_qunar')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="page-container">
@ -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>
@ -333,16 +334,19 @@
</view>
<!-- Branding -->
<view class="branding-area">
<view class="branding-area" >
<image class="image" src="/static/image/other/air-tickets/qunar/branding-area-image.png"
mode="widthFix">
</image>
</view>
<view class="" :style="{'height':$store.state.safeBottomAz+'px'}">
</view>
</view>
</view>
<!-- Bottom Fixed -->
<view class="bottom-bar">
<view class="bottom-bar" :style="{'padding-bottom':$store.state.safeBottomAz+'px'}">
<button class="invoice-btn">开具发票</button>
</view>
</view>
@ -375,7 +379,7 @@ const data = reactive({
ticketData: JSON.parse(JSON.stringify(defualtData))
})
let { navBar, ticketData } = toRefs(data);
let { ticketData } = toRefs(data);
//
const ofCheckInTime = computed(() => {
@ -403,7 +407,7 @@ onShow(() => {
Object.assign(data.ticketData, stored);
}
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");

1184
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>

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,732 @@
<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 && data.bgImg">
<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;`">
<!-- 背景图 image 替代 background-image避免鸿蒙本地路径不生效 -->
<l-painter-image :src="data.bgImg" css="width:666px;height:406px;position:absolute;left:0;top:0;object-fit:fill;" />
<!-- 姓名 -->
<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>
<!-- 出生年逐字绝对定位避免鸿蒙下多 text 竖排/重叠 -->
<l-painter-text
v-for="(item, index) in String(data.form.year)"
:key="'year-' + index"
:css="data.textCssDigit + `position:absolute;left:${122 + index * 18}px;top:152px;`"
:text="item"
/>
<!-- / -->
<l-painter-view :css="`position: absolute;left:216px;top:152px;`">
<l-painter-text :css="data.textCssMonthDay" :text="String(data.form.month)" />
</l-painter-view>
<l-painter-view :css="`position: absolute;left:278px;top:152px;`">
<l-painter-text :css="data.textCssMonthDay" :text="String(data.form.day)" />
</l-painter-view>
<!-- 住址 -->
<l-painter-text
v-for="(item, index) in addressChars"
:key="'addr-' + index"
:css="data.textCssDigit + `position:absolute;left:${item.left}px;top:${item.top}px;`"
:text="item.text"
/>
<!-- 身份证号 -->
<l-painter-text
v-for="(item, index) in String(data.form.idNumber || '')"
:key="'id-' + index"
:css="data.textCssId + `position:absolute;left:${223 + index * 20}px;top:322px;`"
:text="item"
/>
<!-- 照片right 在鸿蒙易失效改为 left -->
<l-painter-view :css="`position: absolute;left:422px;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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
</view>
</template>
<script setup>
//
import ZdyNavbar from "@/components/nav-bar/nav-bar.vue"
import { imgLocal } from '@/utils/common.js';
import {
ref,
reactive,
computed,
watch,
nextTick,
getCurrentInstance
} from "vue";
import {
onLoad,
onShow,
onUnload,
onReady,
onPullDownRefresh,
onReachBottom
} from "@dcloudio/uni-app";
const {
appContext,
proxy
} = getCurrentInstance();
const data = reactive({
shuaxing: false,
navbar: {
title: "身份证",
bgColor: '#EDEDED',
},
width: 800,
height: 600,
code: '',
bgImg: '',
form: {
name: '某某',
gender: '男',
nation: '汉',
year: 2000,
month: 1,
day: 1,
address: '翻斗大街翻斗花园二号楼1001室',
idNumber: '110000200001010000',
photo: '',
},
textCss: 'font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;color:#3D3D3D;font-size:24px;',
textCss2: 'font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;text-align: left;color:#3D3D3D;font-size:22px;',
// ///
textCssDigit: 'font-family: "SimHei", "Microsoft YaHei", sans-serif;width:22px;color:#3D3D3D;font-size:22px;',
textCssMonthDay: 'font-family: "SimHei", "Microsoft YaHei", sans-serif;width:40px;text-align:center;color:#3D3D3D;font-size:22px;',
//
textCssId: 'font-family: "card", "Microsoft YaHei", sans-serif;width:22px;color:#1a1a1a;font-size:28px;',
textCssLeft: 'text-align: left;',
textCssCenter: 'text-align: center;'
})
/** 住址逐字坐标(固定字宽换行,避免鸿蒙流式布局异常) */
const addressChars = computed(() => {
const chars = String(data.form.address || '').split('')
const startLeft = 122
const startTop = 200
const step = 26
const lineHeight = 32
const perLine = 10
return chars.map((text, index) => ({
text,
left: startLeft + (index % perLine) * step,
top: startTop + Math.floor(index / perLine) * lineHeight
}))
})
function loadCardFont() {
return new Promise((resolve) => {
uni.loadFontFace({
family: 'card',
source: 'url("/static/font/card.ttf")',
success: () => resolve(true),
fail: (err) => {
console.warn('身份证号码字体加载失败', err)
resolve(false)
}
})
})
}
async function loadCardBg() {
try {
const localPath = await imgLocal.getLocalImage('other/card/cardBGImg.png')
return localPath || ''
} catch (e) {
console.error('身份证背景图加载失败', e)
return ''
}
}
//
const showEditPopup = ref(false);
const editForm = ref({});
onLoad(async(option) => {
uni.showLoading({
title: "生成中"
})
await loadCardFont()
data.bgImg = await loadCardBg()
data.shuaxing = !!data.bgImg
if (!data.bgImg) {
uni.hideLoading()
uni.showToast({ title: '背景图加载失败', icon: 'none' })
}
//
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) => {
// #ifdef APP-PLUS&&!APP-HARMONY
// 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);
// #endif
// #ifndef APP-PLUS
reject(new Error('plus.io not supported'))
// #endif
});
}
//
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,637 @@
<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: 822px; transform: scale(${posterScaleRatio}); transform-origin: left top; position: absolute; left: 0; top: 0;`">
<l-painter v-if="bgImage" isCanvasToTempFilePath @success="onPainterSuccess" :css="`width:822px;`">
<l-painter-view :css="`width: 822px; position: relative;`">
<!-- 直接使用主背景图撑开父容器移除不必要的 opacity: 0 占位图避免在大屏渲染时产生黑块 -->
<l-painter-image :src="bgImage" css="width: 822px; display: block;"></l-painter-image>
<!-- 人物图片 -->
<l-painter-image :src="hePingData.imgUrl" :css="characterStyle"></l-painter-image>
<template v-if="hePingData.type == 2">
<!-- 头像 -->
<l-painter-image :src="hePingData.avatar"
css="position:absolute;left:50px;bottom:13px;width:36px;height:36px"></l-painter-image>
<!-- 头像框 -->
<l-painter-image src="/static/image/other/game/heping/touxiang.png"
css="position:absolute;left:0;bottom:0;width:100px;height:60px"></l-painter-image>
<!-- 昵称 -->
<l-painter-text :text="hePingData.nickname"
css="position: absolute; left: 102px; bottom: 15px; font-size: 10px; color: #EFF9FF;"></l-painter-text>
</template>
<!-- 水印 -->
<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_game_heping')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="save-action">
<button class="save-btn" @click="handleSave">保存</button>
</view>
<!-- 主题选择区域 -->
<view class="theme-selector" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<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: hePingData.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 avatar-form-item">
<text class="label">人物图片</text>
<view class=" avatar-uploader img-box" @click="handleChooseImgUrl">
<view class="avatar-preview" v-if="tempData.imgUrl">
<image class="preview-img" :src="tempData.imgUrl" mode="widthFix"></image>
<view class="delete-icon" @click.stop="handleDeleteImgUrl">
<text>×</text>
</view>
</view>
<view class="upload-btn" v-else>
<text class="plus">+</text>
</view>
</view>
</view>
<view class="form-item avatar-form-item" v-if="tempData.type == 2">
<text class="label">头像</text>
<view class="avatar-uploader" @click="handleChooseAvatar">
<view class="avatar-preview" v-if="tempData.avatar">
<image class="preview-img" :src="tempData.avatar" mode="aspectFill"></image>
<view class="delete-icon" @click.stop="handleDeleteAvatar">
<text>×</text>
</view>
</view>
<view class="upload-btn" v-else @click="handleChooseAvatar">
<text class="plus">+</text>
</view>
</view>
</view>
<view class="form-item" v-if="tempData.type == 2">
<text class="label">昵称</text>
<input class="input" type="text" v-model="tempData.nickname" />
</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 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, computed, watch, onMounted, getCurrentInstance } from 'vue'
import { onLoad, onReady, onUnload } from '@dcloudio/uni-app'
import { imgLocal } from '@/utils/common.js';
import NavBar from '@/components/nav-bar/nav-bar.vue';
const instance = getCurrentInstance();
const { proxy } = getCurrentInstance();
//
const hePingData = reactive({
type: 1, // 1.2
avatar: '/static/image/shopping/pdd/avatars/avatars1.jpg', //
imgUrl: '/static/image/other/game/heping/default.png', //
nickname: '甜喵小贝', //
})
const characterImgWidth = ref(300); //
const characterStyle = computed(() => {
if (hePingData.type == 1) {
return `position: absolute; left: 50%; bottom:43%; transform: translate(-50%,50%); height: 300px; width: 300px;`;
} else {
return `position: absolute; left: 300px; bottom: 50%;transform: translate(-50%,50%); height: 300px; width: 300px;`;
}
});
//
watch(() => hePingData.imgUrl, (newUrl) => {
if (!newUrl) return;
uni.getImageInfo({
src: newUrl,
success: (res) => {
// 300px
const calcWidth = (300 / res.height) * res.width;
characterImgWidth.value = Math.round(calcWidth);
},
fail: () => {
characterImgWidth.value = 300; //
}
});
}, { immediate: true });
const bgImage = ref('')
// css calc()
const sysInfo = uni.getSystemInfoSync();
const windowWidth = sysInfo.windowWidth || 375;
// 16px padding 822
const posterScaleRatio = (windowWidth - 16) / 822;
const posterContainerHeight = 374 * posterScaleRatio;
onLoad(async () => {
const cachedData = uni.getStorageSync('hePingData');
if (cachedData) {
Object.assign(hePingData, cachedData);
// .jpg .png 404
if (hePingData.imgUrl === '/static/image/other/game/heping/default.jpg') {
hePingData.imgUrl = '/static/image/other/game/heping/default.png';
uni.setStorageSync('hePingData', hePingData);
}
}
try {
bgImage.value = await imgLocal.getLocalImage(`other/game/heping/bg-${hePingData.type}.jpg`);
} catch (e) {
console.error('获取背景图失败:', e);
uni.showToast({ title: '背景图加载失败', icon: 'none' });
}
uni.$on('editFormPhoto', (info) => {
if (currentEditImgField.value) {
tempData[currentEditImgField.value] = info;
}
// #ifndef H5
if (info && !info.startsWith('/static/')) {
newAvatars.value.push(info);
}
// #endif
});
//
if (proxy && proxy.$apiUserEvent) {
proxy.$apiUserEvent('all', {
type: 'click',
key: 'heping',
prefix: '.uni.other.',
value: "和平精英"
});
}
})
onUnload(() => {
uni.$off('editFormPhoto');
})
const rightButtonText = ref("编辑");
const finalPosterPath = ref('');
const showPreview = ref(false); //
const editPopup = ref(null);
const tempData = reactive({});
const newAvatars = ref([]);
const currentEditImgField = ref('');
function onRightClick() {
Object.assign(tempData, JSON.parse(JSON.stringify(hePingData)));
newAvatars.value = [];
editPopup.value.open();
}
const handleChooseAvatar = () => {
currentEditImgField.value = 'avatar';
uni.chooseImage({
count: 1,
success: (res) => {
uni.navigateTo({
url: `/pages/other/qf-image/qf-image?src=${res.tempFilePaths[0]}&width=200&height=200`
});
}
});
};
const handleChooseImgUrl = () => {
currentEditImgField.value = 'imgUrl';
uni.chooseImage({
count: 1,
success: (res) => {
if (res.tempFilePaths[0]) {
// qf-image 300x300 (150) isMatting=1
uni.navigateTo({
url: `/pages/other/qf-image/qf-image?src=${res.tempFilePaths[0]}&width=150&height=150&isMatting=1`
});
}
}
});
};
const handleDeleteAvatar = () => {
tempData.avatar = '';
};
const handleDeleteImgUrl = () => {
tempData.imgUrl = '';
};
function closeEditPopup() {
// #ifndef H5
newAvatars.value.forEach(path => {
uni.removeSavedFile({ filePath: path });
});
// #endif
editPopup.value.close();
}
function confirmEdit() {
// #ifndef H5
//
newAvatars.value.forEach(path => {
if (path !== tempData.avatar && path !== tempData.imgUrl) {
uni.removeSavedFile({ filePath: path });
}
});
//
if (hePingData.avatar !== tempData.avatar && hePingData.avatar && !hePingData.avatar.startsWith('/static/')) {
uni.removeSavedFile({ filePath: hePingData.avatar });
}
if (hePingData.imgUrl !== tempData.imgUrl && hePingData.imgUrl && !hePingData.imgUrl.startsWith('/static/')) {
uni.removeSavedFile({ filePath: hePingData.imgUrl });
}
// #endif
Object.assign(hePingData, tempData);
uni.setStorageSync('hePingData', hePingData);
editPopup.value.close();
//
finalPosterPath.value = '';
let tempBg = bgImage.value;
bgImage.value = ''; //
setTimeout(() => {
bgImage.value = tempBg;
}, 50);
}
async function handleChangeTheme(typeIndex) {
//
finalPosterPath.value = '';
bgImage.value = ''; //
hePingData.type = typeIndex;
uni.setStorageSync('hePingData', hePingData);
try {
bgImage.value = await imgLocal.getLocalImage(`other/game/heping/bg-${typeIndex}.jpg`);
} catch (e) {
console.error('切换主题加载背景图失败:', e);
uni.showToast({ title: '背景加载失败', icon: 'none' });
}
}
//
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' })
}
})
}
/**
* 将本地图片路径通过 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) => {
// #ifdef APP-PLUS&&!APP-HARMONY
// 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);
// #endif
// #ifndef APP-PLUS
reject(new Error('plus.io not supported'))
// #endif
});
}
</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-flex;
width: 100%;
justify-content: center;
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 {
max-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;
}
}
}
.img-box {
width: 200rpx !important;
.avatar-preview {
width: 200rpx !important;
height: auto !important;
}
.preview-img {
width: 100%;
border-radius: 0 !important;
}
}
.input {
flex: 1;
font-size: 28rpx;
background-color: #F7F7F7;
border-radius: 12rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
color: #333;
}
}
.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,829 @@
<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="headerImage"
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: 338.5px; top: 131.4px; width: 40px; text-align: center; font-size: 9px; color: #DAF2FF;font-family: WangZheFont2;"></l-painter-text>
<!-- 热度数 -->
<l-painter-text :text="honorData.popularityCount"
css="position: absolute; left: 366.3px; top: 131.4px; width: 40px; text-align: center; font-size: 9px; color: #DAF2FF;font-family: WangZheFont2;"></l-painter-text>
<!-- 点赞数 -->
<l-painter-text :text="honorData.likeCount"
css="position: absolute; left: 394.0px; top: 131.4px; width: 40px; text-align: center; font-size: 9px; color: #DAF2FF;font-family: WangZheFont2;"></l-painter-text>
<!-- 荣誉一 -->
<l-painter-text :text="honorData.honor1"
css="position: absolute; left: 207.5px; top: 145.6px; width: 80px; text-align: center; font-size: 9px; color: #71B1D3;"></l-painter-text>
<!-- 荣誉二文案较长需足够宽度避免换行下移 -->
<l-painter-text :text="honorData.honor2"
css="position: absolute; left: 300.6px; top: 149px; width: 180px; text-align: center; font-size: 7px; line-height: 1em; color: #71B1D3;"></l-painter-text>
<!-- 巅峰万强 -->
<l-painter-text v-if="honorData.type == 4" :text="honorData.peakStrong"
css="position: absolute; left: 227.3px; top: 192.8px; width: 60px; text-align: center; font-size: 8px; color: #E9F5FB;"></l-painter-text>
<!-- 对战场次 -->
<l-painter-text :text="honorData.matchCount"
css="position: absolute; left: 113.0px; top: 242.3px; width: 50px; text-align: center; font-size: 12px; color: #71B1D3;"></l-painter-text>
<!-- 对战被赞 -->
<l-painter-text :text="honorData.matchLikeCount"
css="position: absolute; left: 174.5px; top: 242.3px; width: 50px; text-align: center; font-size: 12px; color: #71B1D3;"></l-painter-text>
<!-- 图鉴等级 -->
<l-painter-text :text="honorData.pokedexLevel"
css="position: absolute; left: 299.5px; top: 238.8px; width: 40px; text-align: center; font-size:9px; color: #71B1D3;"></l-painter-text>
<!-- 皮肤数 -->
<l-painter-text :text="honorData.skinCount"
css="position: absolute; left: 357.3px; top: 238.8px; width: 40px; text-align: center; font-size:9px; color: #71B1D3;"></l-painter-text>
<!-- 游戏天数 -->
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2" :text="honorData.gameDays"
css="position: absolute; left: 177.1px; top: 282.2px; width: 50px; text-align: center; font-size:10px; color: #71B1D3;"></l-painter-text>
<!-- 峡谷对战局数 -->
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2"
:text="honorData.riftMatchCount"
css="position: absolute; left: 297.2px; top: 282.2px; width: 50px; text-align: center; font-size:10px; color: #71B1D3;"></l-painter-text>
<!-- 大乱斗对战局数 -->
<l-painter-text v-if="honorData.type == 1 || honorData.type == 2"
:text="honorData.aramMatchCount"
css="position: absolute; left: 354.9px; top: 282.2px; width: 50px; text-align: center; font-size:10px; color: #71B1D3;"></l-painter-text>
<!-- 亲密度 -->
<l-painter-text v-if="honorData.type == 3 || honorData.type == 4" :text="honorData.intimacy"
css="position: absolute; left: 323.0px; top: 296.2px; width: 50px; text-align: center; font-size:10px; color: #71B1D3;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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="save-action">
<button class="save-btn" @click="handleSave">保存</button>
</view>
<!-- 主题选择区域 -->
<view class="theme-selector" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<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" @click="handleChooseAvatar">
<view class="avatar-preview" v-if="tempData.avatar">
<image class="preview-img" :src="tempData.avatar" mode="aspectFill"></image>
<view class="delete-icon">
<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, onUnload } from '@dcloudio/uni-app'
import { imgLocal } from '@/utils/common.js';
const rightButtonText = ref("编辑");
const instance = getCurrentInstance();
const { proxy } = getCurrentInstance();
const nicknameImage = ref('');
const showPreview = ref(false); //
const isMountedReady = ref(false);
const isFontLoaded = ref(false);
const headerImage = ref('')
// 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(async () => {
const cachedData = uni.getStorageSync('wangzheHonorData');
if (cachedData) {
Object.assign(honorData, cachedData);
}
headerImage.value = await imgLocal.getLocalImage(`other/game/wangzhe/avatar-frame-${honorData.type}.png`)
uni.$on('editFormPhoto', (info) => {
tempData.avatar = info;
// #ifndef H5
if (info && !info.startsWith('/static/')) {
newAvatars.value.push(info);
}
// #endif
});
//
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);
})
onUnload(() => {
uni.$off('editFormPhoto');
})
const finalPosterPath = ref('');
const editPopup = ref(null);
const tempData = reactive({});
const newAvatars = ref([]);
async function handleChangeTheme(typeIndex) {
//
finalPosterPath.value = '';
honorData.type = typeIndex;
uni.setStorageSync('wangzheHonorData', honorData);
headerImage.value = await imgLocal.getLocalImage(`other/game/wangzhe/avatar-frame-${typeIndex}.png`)
}
function onRightClick() {
Object.assign(tempData, honorData);
newAvatars.value = [];
editPopup.value.open();
}
const handleChooseAvatar = () => {
uni.chooseImage({
count: 1,
success: (res) => {
uni.navigateTo({
url: `/pages/other/qf-image/qf-image?src=${res.tempFilePaths[0]}&width=200&height=200`
});
}
});
};
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>

150
pages/other/game/index.vue Normal file
View File

@ -0,0 +1,150 @@
<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: #ffffff ">
<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/other/game/${item.icon}.png`" mode="aspectFit"></image>
</view>
<view class="btn" style="background: linear-gradient( 327deg, #1777FF 0%, #17CDFF 100%);"
@click="goTo(item.url)">立即进入</view>
</view>
</view>
</view>
</template>
<script setup>
import {
ref,
getCurrentInstance
} from 'vue';
import {
onLoad
} from '@dcloudio/uni-app'
const {
proxy
} = getCurrentInstance();
onLoad(() => {
proxy.$apiUserEvent('all', {
type: 'event',
key: 'game',
prefix: '.uni.other.',
value: '游戏'
})
})
const menuList = ref([{
name: "王者荣耀",
icon: "wangzhe",
shadowColor: "#FF604F",
url: "/pages/other/game/honor-of-kings"
}, {
name: "三角洲",
icon: "sanjiaozhou",
shadowColor: "#12C96A",
url: "/pages/other/game/sanjiaozhou"
}, {
name: "和平精英",
icon: "hepingjingying",
shadowColor: "#4992F2",
url: "/pages/other/game/hepingjingying"
}, {
name: "无畏契约",
icon: "wuweiqiyue",
shadowColor: "#FF3333",
url: "/pages/other/game/wuweiqiyue"
}]);
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>

View File

@ -0,0 +1,819 @@
<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/sanjiaozhou/style-${data.type}.jpg`"
css="width: 750.0px; display: block;"></l-painter-image>
<!-- 顶部信息栏绝对定位避免鸿蒙 inline-block 竖排 -->
<!-- 地图 -->
<l-painter-image src="/static/image/other/game/sanjiaozhou/line.png"
css="position:absolute;left:73px;top:113px;height:22px;width:1px;"></l-painter-image>
<l-painter-text
css="position:absolute;left:78px;top:113px;color:#CDCDCD;font-size:6px;line-height:1em;"
:text="data.map.level"></l-painter-text>
<l-painter-text
css="position:absolute;left:78px;top:123px;color:#CDCDCD;font-size:10px;line-height:1em;"
:text="data.map.name"></l-painter-text>
<!-- 撤离点 -->
<l-painter-image src="/static/image/other/game/sanjiaozhou/line.png"
css="position:absolute;left:155px;top:113px;height:22px;width:1px;"></l-painter-image>
<l-painter-text
css="position:absolute;left:160px;top:113px;color:#CDCDCD;font-size:6px;line-height:1em;"
text="撤离点"></l-painter-text>
<l-painter-text
css="position:absolute;left:160px;top:123px;color:#CDCDCD;font-size:10px;line-height:1em;"
:text="data.extractionPoint"></l-painter-text>
<!-- 时长 -->
<l-painter-image src="/static/image/other/game/sanjiaozhou/line.png"
css="position:absolute;left:237px;top:113px;height:22px;width:1px;"></l-painter-image>
<l-painter-text
css="position:absolute;left:242px;top:113px;color:#CDCDCD;font-size:6px;line-height:1em;"
text="时长"></l-painter-text>
<l-painter-text v-for="(item, idx) in durationChars" :key="'durationNum_' + idx"
:css="item.css" :text="item.text"></l-painter-text>
<!-- 对局时间 -->
<l-painter-image src="/static/image/other/game/sanjiaozhou/line.png"
css="position:absolute;left:319px;top:113px;height:22px;width:1px;"></l-painter-image>
<l-painter-text
css="position:absolute;left:324px;top:113px;color:#CDCDCD;font-size:6px;line-height:1em;"
text="对局时间"></l-painter-text>
<l-painter-text v-for="(item, idx) in matchTimeChars" :key="'matchTimeNum_' + idx"
:css="item.css" :text="item.text"></l-painter-text>
<!-- 本局收获 -->
<l-painter-text v-for="(item, idx) in harvestChars" :key="'harvestNum_' + idx"
:css="item.css" :text="item.text"></l-painter-text>
<!-- 击败干员 -->
<l-painter-text v-for="(item, idx) in defeatChars" :key="'bigNum_' + idx"
:css="item.css" :text="item.text"></l-painter-text>
<!-- 高光击败 -->
<template v-for="(item, index) in data.defeatInfo" :key="'kill_' + index">
<l-painter-image src="/static/image/other/game/sanjiaozhou/kill.png"
:css="`position:absolute;left:73px;top:${295 + index * 16}px;width:12px;height:12px;`"></l-painter-image>
<l-painter-text
:css="`position:absolute;left:87px;top:${297 + index * 16}px;color:#DBE5E6;font-size:9px;line-height:1em;`"
:text="`${item.name} [${item.rank}]`"></l-painter-text>
</template>
<!-- 玩家名称鸿蒙不支持 rotate保持位置 -->
<l-painter-text
css="color:#CDCDCD;font-size:10px;line-height:1em;position:absolute;left:424px;top:310px;transform: rotate(12deg);"
:text="data.name"></l-painter-text>
<l-painter-text
css="color:#CDCDCD;font-size:7px;line-height:1em;position:absolute;left:422px;top:321px;opacity:0.6;transform: rotate(12deg);"
text="信守不渝"></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_sanjiaozhou')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="save-action">
<button class="save-btn" @click="handleSave">保存</button>
</view>
<!-- 主题选择区域 -->
<view class="theme-selector" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<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: data.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="text" v-model="tempData.name" />
</view>
<view class="form-item">
<text class="label">地图级别</text>
<view class="button-group">
<view class="button-item" :class="{ active: tempData.map.level === '普通' }"
@click="tempData.map.level = '普通'">普通</view>
<view class="button-item" :class="{ active: tempData.map.level === '机密' }"
@click="tempData.map.level = '机密'">机密</view>
<view class="button-item" :class="{ active: tempData.map.level === '绝密' }"
@click="tempData.map.level = '绝密'">绝密</view>
</view>
</view>
<view class="form-item">
<text class="label">地图名称</text>
<input class="input" type="text" v-model="tempData.map.name" />
</view>
<view class="form-item">
<text class="label">撤离点</text>
<input class="input" type="text" v-model="tempData.extractionPoint" />
</view>
<view class="form-item">
<text class="label">对局时间</text>
<picker mode="multiSelector" :range="matchTimeRange" :value="matchTimeIndex"
@change="onMatchTimeChange" class="input"
style="height: 36px; line-height: 36px; display: flex; align-items: center;">
<text>{{ tempData.matchTime || '请选择时间' }}</text>
</picker>
</view>
<view class="form-item">
<text class="label">时长</text>
<picker mode="multiSelector" :range="durationRange" :value="durationIndex"
@change="onDurationChange" class="input"
style="height: 36px; line-height: 36px; display: flex; align-items: center;">
<text>{{ tempData.duration || '请选择时长' }}</text>
</picker>
</view>
<view class="form-item">
<text class="label">本局收获</text>
<input class="input" type="text" v-model="tempData.harvest" />
</view>
<view class="form-item">
<text class="label">击败干员</text>
<input class="input" type="text" v-model="tempData.defeat" @input="onDefeatChange" />
</view>
<view style="border: 1px dashed #007aff;padding: 10px;border-radius: 8px;"
v-if="tempData.defeatInfo && tempData.defeatInfo.length > 0">
<view v-for="(item, index) in tempData.defeatInfo" :key="'defeat_' + index"
style="margin-bottom: 10px;">
<view
style="display: flex; justify-content: flex-end; gap: 15px; margin-bottom: 5px; font-size: 12px; height: 16px;"
v-if="tempData.defeatInfo.length > 1">
<text v-if="index > 0" @click="moveDefeatUp(index)"
style="color: #007aff; cursor: pointer;"> 上移</text>
<text v-if="index < tempData.defeatInfo.length - 1" @click="moveDefeatDown(index)"
style="color: #007aff; cursor: pointer;"> 下移</text>
</view>
<view class="form-item">
<text class="label">被击败者{{ index + 1 }}昵称</text>
<input class="input" type="text" v-model="item.name" />
</view>
<view class="form-item" style="margin-bottom: 0;">
<text class="label">被击败者{{ index + 1 }}段位</text>
<input class="input" type="text" v-model="item.rank" />
</view>
</view>
</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 class="preview-overlay" v-if="showPreview" @click="showPreview = false">
<image class="preview-image" :src="finalPosterPath" mode="aspectFit"></image>
</view>
</view>
</template>
<script setup>
import { ref, reactive, computed, onMounted, getCurrentInstance } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
const rightButtonText = ref("编辑");
const { proxy } = getCurrentInstance();
const showPreview = ref(false); //
const isMountedReady = ref(false);
// css calc()
const sysInfo = uni.getSystemInfoSync();
const windowWidth = sysInfo.windowWidth || 375;
const posterScaleRatio = windowWidth / 750;
const posterContainerHeight = 430 * posterScaleRatio;
//
const data = reactive({
type: 1, // 1.2.3.4
name: '大坝猛攻哥',
map: {
name: '零号大坝',
level: '机密'
},
extractionPoint: '工业电梯',
duration: "00:25:19",
matchTime: '02-32 21:02',
harvest: '13526559',
defeat: '1',
defeatInfo: [
{
name: '大坝贪吃鼠',
rank: "黑鹰II"
},
]
})
// my-icon iconfont Unicode
const digitIconMap = {
'0': '\ue629', '1': '\ue627', '2': '\ue62b', '3': '\ue62c', '4': '\ue62f',
'5': '\ue62d', '6': '\ue628', '7': '\ue625', '8': '\ue62a', '9': '\ue62e',
',': '\ue626', ':': '\ue630',
};
const getDefeatIconText = (defeatStr) => {
if (!defeatStr && defeatStr !== 0) return '';
return String(defeatStr).split('').map(char => digitIconMap[char] || char).join('');
};
/** 时长/对局时间:按步进绝对定位(鸿蒙 inline-block 不横排) */
function layoutTimeChars(str, startLeft, startTop) {
let x = startLeft
return String(str || '').split('').map((char) => {
const fontSize = char === '-' ? 12 : (char === ':' ? 4 : 10)
const step = char === ':' ? 4 : (char === '-' ? 10 : (char === ' ' ? 6 : 8))
const left = x
x += step
return {
text: getDefeatIconText(char),
css: `position:absolute;left:${left}px;top:${startTop}px;color:#CDCDCD;font-size:${fontSize}px;line-height:1em;font-family:myIconFont;`
}
})
}
/** 本局收获数字 */
function layoutHarvestChars(str, startLeft, startTop) {
const formatted = String(str || '').replace(/\B(?=(\d{3})+(?!\d))/g, ',')
let x = startLeft
return formatted.split('').map((char) => {
const fontSize = char === ',' ? 6 : 22
const step = char === ',' ? 8 : 19
const top = char === ',' ? startTop + 3 : startTop
const left = x
x += step
return {
text: getDefeatIconText(char),
css: `position:absolute;left:${left}px;top:${top}px;color:#DBE5E6;font-size:${fontSize}px;line-height:1em;font-family:myIconFont;`
}
})
}
/** 击败干员数字 */
function layoutDefeatChars(str, startLeft, startTop) {
let x = startLeft
return String(str || '').split('').map((char) => {
const left = x
x += 20
return {
text: getDefeatIconText(char),
css: `position:absolute;left:${left}px;top:${startTop}px;color:#DBE5E6;font-size:22px;line-height:1em;font-family:myIconFont;`
}
})
}
const durationChars = computed(() => layoutTimeChars(data.duration, 242, 123))
const matchTimeChars = computed(() => layoutTimeChars(data.matchTime, 324, 123))
const harvestChars = computed(() => layoutHarvestChars(data.harvest, 73, 182))
const defeatChars = computed(() => layoutDefeatChars(data.defeat, 73, 250))
onLoad(() => {
const cachedData = uni.getStorageSync('sanjiaozhouData');
if (cachedData) {
Object.assign(data, cachedData);
}
uni.$on('editFormPhoto', (info) => {
tempData.avatar = info;
// #ifndef H5
if (info && !info.startsWith('/static/')) {
newAvatars.value.push(info);
}
// #endif
});
//
proxy.$apiUserEvent('all', {
type: 'click',
key: 'sanjiaozhou',
prefix: '.uni.other.',
value: "三角洲结算页"
})
//
uni.loadFontFace({
family: 'myIconFont',
source: `url("/static/font/iconfont.ttf")`,
success() {
console.log('myIconFont 字体加载成功');
},
fail(err) {
console.error('myIconFont 字体加载失败', err);
}
});
});
onUnload(() => {
uni.$off('editFormPhoto');
})
const finalPosterPath = ref('');
const editPopup = ref(null);
const tempData = reactive({
map: {},
defeatInfo: [{}]
});
const newAvatars = ref([]);
const onDefeatChange = (e) => {
const val = parseInt(e.detail.value) || 0;
const maxAllowed = Math.min(3, Math.max(0, val));
if (tempData.defeatInfo.length < maxAllowed) {
const toAdd = maxAllowed - tempData.defeatInfo.length;
for (let i = 0; i < toAdd; i++) {
tempData.defeatInfo.push({ name: '', rank: '' });
}
} else if (tempData.defeatInfo.length > maxAllowed) {
tempData.defeatInfo.splice(maxAllowed);
}
};
const moveDefeatUp = (index) => {
if (index > 0) {
const item = tempData.defeatInfo.splice(index, 1)[0];
tempData.defeatInfo.splice(index - 1, 0, item);
}
};
const moveDefeatDown = (index) => {
if (index < tempData.defeatInfo.length - 1) {
const item = tempData.defeatInfo.splice(index, 1)[0];
tempData.defeatInfo.splice(index + 1, 0, item);
}
};
//
const durationRange = ref([
Array.from({ length: 100 }, (_, i) => (i < 10 ? '0' + i : i + '')),
Array.from({ length: 60 }, (_, i) => (i < 10 ? '0' + i : i + '')),
Array.from({ length: 60 }, (_, i) => (i < 10 ? '0' + i : i + ''))
]);
const durationIndex = ref([0, 0, 0]);
const onDurationChange = (e) => {
const val = e.detail.value;
durationIndex.value = val;
tempData.duration = `${durationRange.value[0][val[0]]}:${durationRange.value[1][val[1]]}:${durationRange.value[2][val[2]]}`;
};
//
const matchTimeRange = ref([
Array.from({ length: 12 }, (_, i) => (i + 1 < 10 ? '0' + (i + 1) : (i + 1) + '')),
Array.from({ length: 31 }, (_, i) => (i + 1 < 10 ? '0' + (i + 1) : (i + 1) + '')),
Array.from({ length: 24 }, (_, i) => (i < 10 ? '0' + i : i + '')),
Array.from({ length: 60 }, (_, i) => (i < 10 ? '0' + i : i + ''))
]);
const matchTimeIndex = ref([0, 0, 0, 0]);
const onMatchTimeChange = (e) => {
const val = e.detail.value;
matchTimeIndex.value = val;
tempData.matchTime = `${matchTimeRange.value[0][val[0]]}-${matchTimeRange.value[1][val[1]]} ${matchTimeRange.value[2][val[2]]}:${matchTimeRange.value[3][val[3]]}`;
};
const initPickerIndexes = () => {
if (tempData.duration) {
const parts = tempData.duration.split(':');
if (parts.length === 3) {
durationIndex.value = [parseInt(parts[0]) || 0, parseInt(parts[1]) || 0, parseInt(parts[2]) || 0];
}
}
if (tempData.matchTime) {
const spaceSplit = tempData.matchTime.split(' ');
if (spaceSplit.length === 2) {
const dateParts = spaceSplit[0].split('-');
const timeParts = spaceSplit[1].split(':');
if (dateParts.length === 2 && timeParts.length === 2) {
matchTimeIndex.value = [(parseInt(dateParts[0]) || 1) - 1, (parseInt(dateParts[1]) || 1) - 1, parseInt(timeParts[0]) || 0, parseInt(timeParts[1]) || 0];
}
}
}
};
function handleChangeTheme(typeIndex) {
//
finalPosterPath.value = '';
data.type = typeIndex;
uni.setStorageSync('sanjiaozhouData', data);
}
function onRightClick() {
Object.assign(tempData, JSON.parse(JSON.stringify(data)));
initPickerIndexes();
newAvatars.value = [];
editPopup.value.open();
}
function closeEditPopup() {
// #ifndef H5
newAvatars.value.forEach(path => {
uni.removeSavedFile({ filePath: path });
});
// #endif
editPopup.value.close();
}
function confirmEdit() {
// #ifndef H5
//
newAvatars.value.forEach(path => {
if (path !== tempData.avatar) {
uni.removeSavedFile({ filePath: path });
}
});
//
if (data.avatar !== tempData.avatar && data.avatar && !data.avatar.startsWith('/static/')) {
uni.removeSavedFile({ filePath: data.avatar });
}
// #endif
Object.assign(data, tempData);
uni.setStorageSync('sanjiaozhouData', data);
editPopup.value.close();
//
finalPosterPath.value = '';
}
//
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;
})
</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;
}
}
}
}
.button-group {
display: flex;
align-items: center;
flex-wrap: nowrap;
.button-item {
padding: 4rpx 16rpx;
border-radius: 30rpx;
font-size: 26rpx;
margin-right: 16rpx;
color: #666;
background-color: #F7F7F7;
border: 2rpx solid transparent;
white-space: nowrap;
cursor: pointer;
transition: all 0.2s;
&.active {
color: #3B7BFF;
background-color: rgba(59, 123, 255, 0.1);
border-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,727 @@
<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: 822px; transform: scale(${posterScaleRatio}); transform-origin: left top; position: absolute; left: 0; top: 0;`">
<l-painter v-if="bgImage" isCanvasToTempFilePath @success="onPainterSuccess" :css="`width:822px;`">
<l-painter-view :css="`width: 822px; position: relative;`">
<!-- 直接使用主背景图撑开父容器移除不必要的 opacity: 0 占位图避免在大屏渲染时产生黑块 -->
<l-painter-image :src="bgImage" css="width: 822px; display: block;"></l-painter-image>
<!-- 人物图片 -->
<l-painter-image :src="wuweiData.imgUrl" :css="characterStyle"></l-painter-image>
<template v-if="wuweiData.type == 1">
<!-- 击败 -->
<l-painter-text :text="wuweiData.killCount"
:css="`${style1TextCommonStyle}; left:61px;`"></l-painter-text>
<!-- 精准射击 -->
<l-painter-text :text="wuweiData.shooting"
:css="`${style1TextCommonStyle}; left:131px;`"></l-painter-text>
<!-- 输出伤害 -->
<l-painter-text :text="wuweiData.output"
:css="`${style1TextCommonStyle}; left:201px;`"></l-painter-text>
<!-- 获得资金 -->
<l-painter-text :text="wuweiData.harvest"
:css="`${style1TextCommonStyle}; left:272px;`"></l-painter-text>
<!-- 三连击败 -->
<l-painter-text :text="wuweiData.threeKill"
:css="`${style1TextCommonStyle}; left:342px;`"></l-painter-text>
</template>
<template v-if="wuweiData.type == 2">
<!-- 击败 -->
<l-painter-text :text="wuweiData.killCount"
css="position: absolute; left: 70px; bottom: 122px; width:40px; text-align:center; font-size: 18px;font-weight: 700;color: #FFFFFF;line-height: 18px;"></l-painter-text>
<!-- 头像 -->
<l-painter-image :src="wuweiData.avatar"
css="position:absolute;left:65.5px;bottom:68.5px;width:42px;height:42px"></l-painter-image>
<!-- 昵称 -->
<l-painter-text :text="truncatedNickname"
css="position: absolute; left: 119px; bottom: 82px; font-size: 13px; font-family: Arial, sans-serif; color: #FFFFFF; width: 117px;"></l-painter-text>
<!-- 阵败 -->
<l-painter-text :text="wuweiData.defeatsCount"
css="position: absolute; left: 132.5px; bottom: 122px; width: 40px; text-align: center; font-size: 18px;font-weight: 700;color: #FFFFFF;line-height: 18px;"></l-painter-text>
<!-- 助攻 -->
<l-painter-text :text="wuweiData.assistCount"
css="position: absolute; left: 195px; bottom: 122px; width: 40px; text-align: center; font-size: 18px;font-weight: 700;color: #FFFFFF;line-height: 18px;"></l-painter-text>
<!-- 战斗分 -->
<l-painter-text :text="`平均战斗评分:${wuweiData.battlePoints}`"
css="position: absolute; left: 80px; bottom: 153px; width: 140px; text-align: center; font-size:10px;color: #FFFFFF;line-height: 10px;"></l-painter-text>
</template>
<!-- 水印 -->
<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_game_wuweiqiyue')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="save-action">
<button class="save-btn" @click="handleSave">保存</button>
</view>
<!-- 主题选择区域 -->
<view class="theme-selector" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<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: wuweiData.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 avatar-form-item">
<text class="label">人物图片</text>
<view class=" avatar-uploader img-box" @click="handleChooseImgUrl">
<view class="avatar-preview" v-if="tempData.imgUrl">
<image class="preview-img" :src="tempData.imgUrl" mode="widthFix"></image>
<view class="delete-icon" @click.stop="handleDeleteImgUrl">
<text>×</text>
</view>
</view>
<view class="upload-btn" v-else>
<text class="plus">+</text>
</view>
</view>
</view>
<view class="form-item avatar-form-item" v-if="tempData.type == 2">
<text class="label">头像</text>
<view class="avatar-uploader" @click="handleChooseAvatar">
<view class="avatar-preview" v-if="tempData.avatar">
<image class="preview-img" :src="tempData.avatar" mode="aspectFill"></image>
<view class="delete-icon" @click.stop="handleDeleteAvatar">
<text>×</text>
</view>
</view>
<view class="upload-btn" v-else @click="handleChooseAvatar">
<text class="plus">+</text>
</view>
</view>
</view>
<view class="form-item" v-if="tempData.type == 2">
<text class="label">昵称</text>
<input class="input" type="text" v-model="tempData.nickname" />
</view>
<view class="form-item">
<text class="label">击败</text>
<input class="input" type="number" v-model="tempData.killCount" />
</view>
<view class="form-item" v-if="tempData.type == 1">
<text class="label">精准射击</text>
<input class="input" type="number" v-model="tempData.shooting" />
</view>
<view class="form-item" v-if="tempData.type == 1">
<text class="label">输出伤害</text>
<input class="input" type="number" v-model="tempData.output" />
</view>
<view class="form-item" v-if="tempData.type == 1">
<text class="label">获得资金</text>
<input class="input" type="number" v-model="tempData.harvest" />
</view>
<view class="form-item" v-if="tempData.type == 1">
<text class="label">三连击败</text>
<input class="input" type="number" v-model="tempData.threeKill" />
</view>
<view class="form-item" v-if="tempData.type == 2">
<text class="label">阵败</text>
<input class="input" type="number" v-model="tempData.defeatsCount" />
</view>
<view class="form-item" v-if="tempData.type == 2">
<text class="label">助攻</text>
<input class="input" type="number" v-model="tempData.assistCount" />
</view>
<view class="form-item" v-if="tempData.type == 2">
<text class="label">战斗评分</text>
<input class="input" type="number" v-model="tempData.battlePoints" />
</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 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, computed, watch, onMounted, getCurrentInstance } from 'vue'
import { onLoad, onReady, onUnload } from '@dcloudio/uni-app'
import { imgLocal } from '@/utils/common.js';
import NavBar from '@/components/nav-bar/nav-bar.vue';
const instance = getCurrentInstance();
const { proxy } = getCurrentInstance();
//
const wuweiData = reactive({
type: 1, // 1.2
avatar: '/static/image/shopping/pdd/avatars/avatars1.jpg', //
imgUrl: '/static/image/other/game/heping/default.png', //
nickname: '甜喵小贝', //
killCount: 24,
defeatsCount: 2,
assistCount: 12,
battlePoints: 3854,
shooting: 7,
output: 4109,
harvest: 41750,
threeKill: 5
})
const characterImgHeight = ref(350); //
const characterStyle = computed(() => {
if (wuweiData.type == 1) {
return `position: absolute; right: 77px; bottom: 0; width: 300px; height: 350px;`;
} else {
return `position: absolute; right: 210px; bottom: 0; width: 300px; height: 350px;`;
}
});
const style1TextCommonStyle = `position: absolute; bottom:79px; width:40px; text-align:center; font-size: 16px;color: #FFFFFF;line-height: 16px;`;
// line-clamp
const truncatedNickname = computed(() => {
let name = wuweiData.nickname || '';
let width = 0;
let result = '';
let max = 117 - 15; // 15px "..."
for (let i = 0; i < name.length; i++) {
let charCode = name.charCodeAt(i);
if (charCode >= 0 && charCode <= 128) {
width += 7.5; //
} else {
width += 13; // font-size: 13px
}
if (width > max) {
return result + '...';
}
result += name[i];
}
return name;
});
//
watch(() => wuweiData.imgUrl, (newUrl) => {
if (!newUrl) return;
uni.getImageInfo({
src: newUrl,
success: (res) => {
// 300px
const calcHeight = (300 / res.width) * res.height;
characterImgHeight.value = Math.round(calcHeight);
},
fail: () => {
characterImgHeight.value = 350; //
}
});
}, { immediate: true });
const bgImage = ref('')
// css calc()
const sysInfo = uni.getSystemInfoSync();
const windowWidth = sysInfo.windowWidth || 375;
// 16px padding 822
const posterScaleRatio = (windowWidth - 16) / 822;
const posterContainerHeight = 374 * posterScaleRatio;
onLoad(async () => {
const cachedData = uni.getStorageSync('wuweiData');
if (cachedData) {
Object.assign(wuweiData, cachedData);
// .jpg .png 404
if (wuweiData.imgUrl === '/static/image/other/game/heping/default.jpg') {
wuweiData.imgUrl = '/static/image/other/game/heping/default.png';
uni.setStorageSync('wuweiData', wuweiData);
}
}
try {
bgImage.value = await imgLocal.getLocalImage(`other/game/wuwei/bg-${wuweiData.type}.jpg`);
} catch (e) {
console.error('获取背景图失败:', e);
uni.showToast({ title: '背景图加载失败', icon: 'none' });
}
uni.$on('editFormPhoto', (info) => {
if (currentEditImgField.value) {
tempData[currentEditImgField.value] = info;
}
// #ifndef H5
if (info && !info.startsWith('/static/')) {
newAvatars.value.push(info);
}
// #endif
});
//
if (proxy && proxy.$apiUserEvent) {
proxy.$apiUserEvent('all', {
type: 'click',
key: 'wuwei',
prefix: '.uni.other.',
value: "无畏契约"
});
}
})
onUnload(() => {
uni.$off('editFormPhoto');
})
const rightButtonText = ref("编辑");
const finalPosterPath = ref('');
const showPreview = ref(false); //
const editPopup = ref(null);
const tempData = reactive({});
const newAvatars = ref([]);
const currentEditImgField = ref('');
function onRightClick() {
Object.assign(tempData, JSON.parse(JSON.stringify(wuweiData)));
newAvatars.value = [];
editPopup.value.open();
}
const handleChooseAvatar = () => {
currentEditImgField.value = 'avatar';
uni.chooseImage({
count: 1,
success: (res) => {
uni.navigateTo({
url: `/pages/other/qf-image/qf-image?src=${res.tempFilePaths[0]}&width=200&height=200`
});
}
});
};
const handleChooseImgUrl = () => {
currentEditImgField.value = 'imgUrl';
uni.chooseImage({
count: 1,
success: (res) => {
if (res.tempFilePaths[0]) {
// qf-image 300x350 (150x175) isMatting=1
uni.navigateTo({
url: `/pages/other/qf-image/qf-image?src=${res.tempFilePaths[0]}&width=150&height=175&isMatting=1`
});
}
}
});
};
const handleDeleteAvatar = () => {
tempData.avatar = '';
};
const handleDeleteImgUrl = () => {
tempData.imgUrl = '';
};
function closeEditPopup() {
// #ifndef H5
newAvatars.value.forEach(path => {
uni.removeSavedFile({ filePath: path });
});
// #endif
editPopup.value.close();
}
function confirmEdit() {
// #ifndef H5
//
newAvatars.value.forEach(path => {
if (path !== tempData.avatar && path !== tempData.imgUrl) {
uni.removeSavedFile({ filePath: path });
}
});
//
if (wuweiData.avatar !== tempData.avatar && wuweiData.avatar && !wuweiData.avatar.startsWith('/static/')) {
uni.removeSavedFile({ filePath: wuweiData.avatar });
}
if (wuweiData.imgUrl !== tempData.imgUrl && wuweiData.imgUrl && !wuweiData.imgUrl.startsWith('/static/')) {
uni.removeSavedFile({ filePath: wuweiData.imgUrl });
}
// #endif
Object.assign(wuweiData, tempData);
uni.setStorageSync('wuweiData', wuweiData);
editPopup.value.close();
//
finalPosterPath.value = '';
let tempBg = bgImage.value;
bgImage.value = ''; //
setTimeout(() => {
bgImage.value = tempBg;
}, 50);
}
async function handleChangeTheme(typeIndex) {
//
finalPosterPath.value = '';
bgImage.value = ''; //
wuweiData.type = typeIndex;
uni.setStorageSync('wuweiData', wuweiData);
try {
bgImage.value = await imgLocal.getLocalImage(`other/game/wuwei/bg-${typeIndex}.jpg`);
} catch (e) {
console.error('切换主题加载背景图失败:', e);
uni.showToast({ title: '背景加载失败', icon: 'none' });
}
}
//
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' })
}
})
}
/**
* 将本地图片路径通过 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) => {
// #ifdef APP-PLUS&&!APP-HARMONY
// 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);
// #endif
// #ifndef APP-PLUS
reject(new Error('plus.io not supported'))
// #endif
});
}
</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-flex;
width: 100%;
justify-content: center;
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 {
max-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;
}
}
}
.img-box {
width: 200rpx !important;
.avatar-preview {
width: 200rpx !important;
height: auto !important;
}
.preview-img {
width: 100%;
border-radius: 0 !important;
}
}
.input {
flex: 1;
font-size: 28rpx;
background-color: #F7F7F7;
border-radius: 12rpx;
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
color: #333;
}
}
.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,117 @@
<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
}
if (option.isMatting === '1') {
data.isCard = true; //
}
})
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) => {
// #ifdef APP-PLUS&&!APP-HARMONY
// 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);
// #endif
// #ifndef APP-PLUS
reject(new Error('plus.io not supported'))
// #endif
});
}
</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" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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,983 @@
<script setup>
import { reactive, ref, onMounted, computed } from 'vue';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
const CACHE_KEY = 'score_center_info';
const data = reactive({
form: {
examYear: '2016',
idNumber: '***********1234',
name: '肖战',
chinese: '113',
math: '123',
english: '116',
comprehensive: '195'
}
});
const total = computed(() => {
const sum = Number(data.form.chinese || 0) + Number(data.form.math || 0) + Number(data.form.english || 0) + Number(data.form.comprehensive || 0);
return sum || '';
});
const editDialog = reactive({
show: false,
data: {}
});
onMounted(() => {
loadCache();
});
function loadCache() {
const cache = uni.getStorageSync(CACHE_KEY);
if (cache) {
data.form = { ...data.form, ...cache };
}
}
function saveCache() {
uni.setStorageSync(CACHE_KEY, data.form);
}
function openEditDialog() {
editDialog.data = JSON.parse(JSON.stringify(data.form));
editDialog.show = true;
}
function saveEdit() {
data.form = { ...data.form, ...editDialog.data };
saveCache();
editDialog.show = false;
uni.showToast({
title: '保存成功',
icon: 'success'
});
}
const editTotal = computed(() => {
const sum = Number(editDialog.data.chinese || 0) + Number(editDialog.data.math || 0) + Number(editDialog.data.english || 0) + Number(editDialog.data.comprehensive || 0);
return sum || '';
});
function back(){
uni.navigateBack()
}
</script>
<template>
<view class="div">
<view v-if="$isVip()" >
<watermark dark="light" source="uni_alipay_other_score" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_score')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="headerBox" :style="{'padding-top':$systemInfo.statusBarHeight*2+'rpx'}">
<view class="header" >
<view class="left" @click="back()">
<image src="/static/image/other/score/center/home.png" mode=""></image>
</view>
<view class="center">
查询专区
</view>
<view class="right">
<image src="/static/image/other/score/center/rightBtn.png" mode=""></image>
</view>
</view>
</view>
<view class="" :style="{height:$systemInfo.statusBarHeight*2+88+'rpx'}">
</view>
<view class="flexcontainer" @click="openEditDialog">
<view class="rectangle_23375">
<text class="text_2"> 普通高考成绩查询 </text>
</view>
<view class="rectangle_23377">
<view class="flexcontainer_1"><text class="text_3"> 考试年份: </text><text class="text_4"> {{data.form.examYear}} </text></view>
<view class="div_424"></view>
<view class="flexcontainer_2"><text class="text_5"> 身份证号: </text><text class="text_6"> {{data.form.idNumber}} </text></view>
<view class="div_425"></view>
<view class="flexcontainer_3"><view class="text_7"> <text>:</text> </view><text class="text_8"> {{data.form.name}} </text></view>
<view class="div_426"></view>
<text class="text_9"> 科目成绩: </text>
<view class="group_48319">
<text class="text_10"> 语文 </text>
<view class="div_427"></view>
<text class="text_11"> {{data.form.chinese}} </text>
</view>
<view class="group_48320">
<text class="text_12"> 数学 </text>
<view class="div_427_1"></view>
<text class="text_13"> {{data.form.math}} </text>
</view>
<view class="group_48321">
<text class="text_14"> 外语 </text>
<view class="div_427_2"></view>
<text class="text_15"> {{data.form.english}} </text>
</view>
<view class="group_48322">
<text class="text_16"> 综合 </text>
<view class="div_427_3"></view>
<text class="text_17"> {{data.form.comprehensive}} </text>
</view>
<view class="group_48323">
<text class="text_18"> 总分 </text>
<view class="div_427_4"></view>
<text class="text_19"> {{total}} </text>
</view>
</view>
</view>
<view class="group_48324">
<text class="text_20"> 返回查询页 </text>
</view>
<view class="home">
<view class="home_indicator"></view>
</view>
<safeBottom bgColor="#f4f7fb" />
<!-- 编辑弹窗 -->
<view v-if="editDialog.show" class="editDialog">
<view class="editDialog_bg">
<view class="editDialog_header">
<text class="title">编辑成绩信息</text>
</view>
<view class="editDialog_body">
<view class="formItem">
<text>考试年份</text>
<input v-model="editDialog.data.examYear" type="digit" placeholder="如: 2016" />
</view>
<view class="formItem">
<text>身份证号</text>
<input v-model="editDialog.data.idNumber" placeholder="如: ***********1234" />
</view>
<view class="formItem">
<text>姓名</text>
<input v-model="editDialog.data.name" placeholder="请输入姓名" />
</view>
<view class="formItem">
<text>语文</text>
<input v-model="editDialog.data.chinese" type="digit" placeholder="请输入语文成绩" />
</view>
<view class="formItem">
<text>数学</text>
<input v-model="editDialog.data.math" type="digit" placeholder="请输入数学成绩" />
</view>
<view class="formItem">
<text>外语</text>
<input v-model="editDialog.data.english" type="digit" placeholder="请输入外语成绩" />
</view>
<view class="formItem">
<text>综合</text>
<input v-model="editDialog.data.comprehensive" type="digit" placeholder="请输入综合成绩" />
</view>
<view class="formItem">
<text>总分</text>
<text style="flex:1;text-align:right;font-size:28rpx;color:#333;">{{editTotal}}</text>
</view>
</view>
<view class="popup-footer">
<view class="btn-cancel" @click="editDialog.show=false">取消</view>
<view class="btn-save" @click="saveEdit">保存</view>
</view>
</view>
</view>
</view>
</template>
<style lang="scss">
* {
box-sizing: border-box;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-col {
display: flex;
flex-direction: column;
}
.justify-start {
display: flex;
justify-content: flex-start;
}
.justify-center {
display: flex;
justify-content: center;
}
.justify-end {
display: flex;
justify-content: flex-end;
}
.justify-between {
display: flex;
justify-content: space-between;
}
.items-start {
display: flex;
align-items: flex-start;
}
.items-end {
display: flex;
align-items: flex-end;
}
.items-center {
display: flex;
align-items: center;
}
.no-shrink {
flex-shrink: 0;
}
/* 编辑弹窗样式 */
.editDialog {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.editDialog_bg {
width: 680rpx;
max-height: 80vh;
background-color: #ffffff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
.editDialog_header {
padding: 30rpx;
border-bottom: 1rpx solid #eee;
text-align: center;
.title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
}
.editDialog_body {
padding: 20rpx 30rpx;
overflow-y: auto;
max-height: 60vh;
}
.formItem {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
text {
width: 200rpx;
font-size: 28rpx;
color: #666;
flex-shrink: 0;
}
input {
flex: 1;
font-size: 28rpx;
color: #333;
text-align: right;
}
}
.popup-footer {
display: flex;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
gap: 20rpx;
.btn-cancel,
.btn-save {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 10rpx;
font-size: 28rpx;
}
.btn-cancel {
background-color: #f5f5f5;
color: #666;
}
.btn-save {
background-color: #187AFF;
color: #fff;
}
}
</style>
<style lang="scss" scoped>
/* 查询中心 */
.div {
position: relative;
width: 750rpx;
// height: 1624rpx;
overflow: hidden;
background-color: #f4f7fb;
display: flex;
flex-direction: column;
align-items: flex-start;
isolation: isolate;
.headerBox{
position: fixed;
left: 0;
top: 0;
background-color: #fff;
z-index: 999 !important;
width: 100%;
}
.header{
min-height: 88rpx;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.center{
font-weight: bold;
font-size: 17px;
color: #1A1A1A;
}
.left,.right{
width: 200rpx;
display: flex;
align-items: center;
}
.left{
margin-left: 30rpx;
image{
width: 42rpx;
height: 42rpx;
}
}
.right{
margin-right: 30rpx;
image{
width: 200rpx;
height: 66rpx;
}
}
}
/* flexContainer */
.flexcontainer {
position: relative;
display: flex;
width: 750rpx;
height: 1222rpx;
flex-direction: column;
align-items: flex-start;
isolation: isolate;
z-index: 2;
margin-top: 0;
margin-left: 0;
/* 矩形 23375 */
.rectangle_23375 {
position: relative;
width: 750rpx;
height: 260rpx;
background-color: #1a92ef;
margin-left: auto;
margin-right: auto;
display: flex;
flex-direction: column;
align-items: flex-start;
z-index: 0;
margin-top: 0;
border-radius: 0 0 24rpx 24rpx;
/* 消息类型设置 */
.text_2 {
position: relative;
text-align: center;
font-size: 36rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 50rpx;
color: #ffffff;
white-space: pre;
height: 50rpx;
margin-top: 24rpx;
margin-bottom: -7rpx;
width: max-content;
margin-left: auto;
margin-right: auto;
}
}
/* 矩形 23377 */
.rectangle_23377 {
position: relative;
width: 688rpx;
height: 1056rpx;
border-radius: 20rpx;
background-color: #ffffff;
margin-left: auto;
margin-right: auto;
display: flex;
flex-direction: column;
align-items: flex-start;
isolation: isolate;
z-index: 2;
margin-top: -94rpx;
/* flexContainer */
.flexcontainer_1 {
position: relative;
display: flex;
width: 344rpx;
height: 32rpx;
flex-direction: row;
align-items: flex-start;
z-index: 0;
margin-top: 32rpx;
margin-left: 30rpx;
/* 考试年份: */
.text_3 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: 0.34em;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 0;
margin-bottom: -6rpx;
position: relative;
margin-left: 0;
}
/* 2016 年 */
.text_4 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #333333;
white-space: pre;
height: 44rpx;
margin-top: 0;
margin-bottom: -6rpx;
position: relative;
margin-left: 42rpx;
}
}
/* 直线 424 */
.div_424 {
width: 626rpx;
height: 0;
border: 1rpx solid #f1f1f1;
z-index: 4;
position: relative;
margin-top: 46rpx;
margin-left: 30rpx;
}
/* flexContainer */
.flexcontainer_2 {
position: relative;
display: flex;
width: 482rpx;
height: 32rpx;
flex-direction: row;
align-items: flex-start;
z-index: 1;
margin-top: 32rpx;
margin-left: 30rpx;
/* 身份证号: */
.text_5 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: 0.34em;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 0;
margin-bottom: -6rpx;
position: relative;
margin-left: 0;
}
/* ***********1234 */
.text_6 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #333333;
white-space: pre;
height: 44rpx;
margin-top: 0;
margin-bottom: -6rpx;
position: relative;
margin-left: 42rpx;
}
}
/* 直线 425 */
.div_425 {
width: 626rpx;
height: 0;
border: 1rpx solid #f1f1f1;
z-index: 5;
position: relative;
margin-top: 46rpx;
margin-left: 30rpx;
}
/* flexContainer */
.flexcontainer_3 {
position: relative;
display: flex;
// width: 288rpx;
height: 32rpx;
flex-direction: row;
align-items: flex-start;
z-index: 3;
margin-top: 34rpx;
margin-left: 30rpx;
/* 姓 名: */
.text_7 {
width: 210rpx;
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: 0.34em;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 0;
margin-bottom: -6rpx;
position: relative;
margin-left: 0;
display: flex;
justify-content: space-between;
}
/* 肖战 */
.text_8 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #333333;
white-space: pre;
height: 44rpx;
margin-top: 0;
margin-bottom: -6rpx;
position: relative;
margin-left: 64rpx;
}
}
/* 直线 426 */
.div_426 {
width: 626rpx;
height: 0;
border: 1rpx solid #f1f1f1;
z-index: 6;
position: relative;
margin-top: 46rpx;
margin-left: 30rpx;
}
/* 科目成绩: */
.text_9 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: 0.34em;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 32rpx;
margin-bottom: -6rpx;
z-index: 2;
position: relative;
margin-left: 30rpx;
}
/* 组 48319 */
.group_48319 {
width: 626rpx;
height: 90rpx;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
z-index: 7;
margin-top: 48rpx;
margin-left: 30rpx;
border-radius: 16rpx;
background-color: #f4f7f9;
isolation: isolate;
/* 语文 */
.text_10 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 0;
position: relative;
margin-left: 128rpx;
}
/* 直线 427 */
.div_427 {
width: 40rpx;
height: 0;
transform: rotate(90deg);
border: 1rpx solid #e5e8ea;
z-index: 2;
position: relative;
margin-left: 84rpx;
margin-top: 44rpx;
}
/* 113 */
.text_11 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: -0.04em;
color: #0087cf;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 1;
position: relative;
margin-left: 122rpx;
}
}
/* 组 48320 */
.group_48320 {
width: 626rpx;
height: 90rpx;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
z-index: 9;
margin-top: 32rpx;
margin-left: 30rpx;
border-radius: 16rpx;
background-color: #f4f7f9;
isolation: isolate;
/* 数学 */
.text_12 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 0;
position: relative;
margin-left: 128rpx;
}
/* 直线 427 */
.div_427_1 {
width: 40rpx;
height: 0;
transform: rotate(90deg);
border: 1rpx solid #e5e8ea;
z-index: 2;
position: relative;
margin-left: 84rpx;
margin-top: 44rpx;
}
/* 123 */
.text_13 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: -0.04em;
color: #0087cf;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 1;
position: relative;
margin-left: 122rpx;
}
}
/* 组 48321 */
.group_48321 {
width: 626rpx;
height: 90rpx;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
z-index: 8;
margin-top: 32rpx;
margin-left: 30rpx;
border-radius: 16rpx;
background-color: #f4f7f9;
isolation: isolate;
/* 外语 */
.text_14 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 0;
position: relative;
margin-left: 128rpx;
}
/* 直线 427 */
.div_427_2 {
width: 40rpx;
height: 0;
transform: rotate(90deg);
border: 1rpx solid #e5e8ea;
z-index: 2;
position: relative;
margin-left: 84rpx;
margin-top: 44rpx;
}
/* 116 */
.text_15 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: -0.04em;
color: #0087cf;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 1;
position: relative;
margin-left: 122rpx;
}
}
/* 组 48322 */
.group_48322 {
width: 626rpx;
height: 90rpx;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
z-index: 10;
margin-top: 32rpx;
margin-left: 30rpx;
border-radius: 16rpx;
background-color: #f4f7f9;
isolation: isolate;
/* 综合 */
.text_16 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 0;
position: relative;
margin-left: 128rpx;
}
/* 直线 427 */
.div_427_3 {
width: 40rpx;
height: 0;
transform: rotate(90deg);
border: 1rpx solid #e5e8ea;
z-index: 2;
position: relative;
margin-left: 84rpx;
margin-top: 44rpx;
}
/* 195 */
.text_17 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: -0.04em;
color: #0087cf;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 1;
position: relative;
margin-left: 122rpx;
}
}
/* 组 48323 */
.group_48323 {
width: 626rpx;
height: 90rpx;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-start;
z-index: 11;
margin-top: 32rpx;
margin-left: 30rpx;
border-radius: 16rpx;
background-color: #f4f7f9;
isolation: isolate;
/* 总分 */
.text_18 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
color: #6f6f6f;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 0;
position: relative;
margin-left: 128rpx;
}
/* 直线 427 */
.div_427_4 {
width: 40rpx;
height: 0;
transform: rotate(90deg);
border: 1rpx solid #e5e8ea;
z-index: 2;
position: relative;
margin-left: 84rpx;
margin-top: 44rpx;
}
/* 549 */
.text_19 {
font-size: 32rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 44rpx;
letter-spacing: -0.04em;
color: #0087cf;
white-space: pre;
height: 44rpx;
margin-top: 26rpx;
margin-bottom: -6rpx;
z-index: 1;
position: relative;
margin-left: 122rpx;
}
}
}
}
/* 组 48324 */
.group_48324 {
width: 688rpx;
height: 90rpx;
position: relative;
font-size: 28rpx;
font-family: 'PingFang SC';
font-weight: 400;
line-height: 40rpx;
color: #ffffff;
white-space: pre;
display: flex;
flex-direction: column;
align-items: flex-start;
z-index: 3;
margin-top: 48rpx;
margin-left: 32rpx;
border-radius: 298rpx;
background-color: #1689f9;
/* 返回查询页 */
.text_20 {
height: 40rpx;
margin-top: 32rpx;
margin-bottom: -6rpx;
position: relative;
margin-left: 274rpx;
}
}
/* home */
.home {
position: absolute;
width: 750rpx;
height: 68rpx;
top: auto;
left: 0;
background-color: rgba(255, 255, 255, 0);
right: 0;
margin-left: auto;
margin-right: auto;
bottom: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
.home_indicator {}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,622 @@
<script setup>
import { imgLocal } from '@/utils/common.js';
import wmWatermark from "@/components/wm-watermark/wm-watermark.vue"
import DateTimePicker from '@/components/dengrq-datetime-picker/dateTimePicker/index.vue';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
import {
reactive,
ref,
onMounted,
computed
} from 'vue';
const CACHE_KEY = 'score_guandong_info';
const data = reactive({
bgImg:'',
form: {
subject: '文科',
avatar: '',
name: '大王',
idNumber: '',
examNumber: '123456789',
chinese: '116',
math: '104',
english: '104',
history: '104',
politics: '104',
geography: '104',
total: '104',
undergraduateTotal: '104',
ranking: '104',
technicalTotal: '104',
technicalRanking: '1455',
time: '2025-6-25'
}
});
const total = computed(() => {
const sum = Number(data.form.chinese || 0) + Number(data.form.math || 0) + Number(data.form.english || 0) +
Number(data.form.history || 0) + Number(data.form.politics || 0) + Number(data.form.geography || 0);
return sum || '';
});
function getSubjectLabels(subject) {
const isScience = subject === '理科'
return {
subject3: isScience ? '物理' : '历史',
subject4: isScience ? '化学' : '思想政治',
subject5: isScience ? '生物学' : '地理',
trackName: isScience ? '物理' : '历史',
}
}
const subjectLabels = computed(() => getSubjectLabels(data.form.subject || '文科'))
const editDialog = reactive({
show: false,
data: {}
});
const editSubjectLabels = computed(() => getSubjectLabels(editDialog.data.subject || '文科'))
onMounted(async() => {
data.bgImg= await imgLocal.getLocalImage('other/score/guandong/guandongbg.png');
loadCache();
});
function loadCache() {
const cache = uni.getStorageSync(CACHE_KEY);
if (cache) {
data.form = {
...data.form,
...cache,
subject: cache.subject || '文科'
};
}
}
function saveCache() {
uni.setStorageSync(CACHE_KEY, data.form);
}
function openEditDialog() {
editDialog.data = JSON.parse(JSON.stringify(data.form));
if (!editDialog.data.subject) {
editDialog.data.subject = '文科';
}
editDialog.show = true;
}
function saveEdit() {
data.form = {
...data.form,
...editDialog.data
};
saveCache();
editDialog.show = false;
uni.showToast({
title: '保存成功',
icon: 'success'
});
}
const editTotal = computed(() => {
const sum = Number(editDialog.data.chinese || 0) + Number(editDialog.data.math || 0) + Number(editDialog.data
.english || 0) + Number(editDialog.data.history || 0) + Number(editDialog.data.politics || 0) + Number(
editDialog.data.geography || 0);
return sum || '';
});
function back(){
uni.navigateBack()
}
function onChangeStartDate(e){
editDialog.data.time=e
console.log(e)
}
function getTime(times,status){
let list=times.split('-')
if(status){
return list[0]+'年'
}else{
return list[0]+'年'+list[1]+'月'
}
}
</script>
<template>
<wmWatermark :text="'广东省教育考试院'+data.form.time"/>
<view v-if="$isVip()" >
<watermark dark="light" source="uni_alipay_other_score" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_score')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="container">
<view class="headerBox" :style="{'padding-top':$systemInfo.statusBarHeight*2+'rpx'}">
<view class="header" >
<view class="left" @click="back()">
<image src="/static/image/other/score/guandong/leftIcon.png" mode=""></image>
</view>
<view class="center">
考试成绩结果
</view>
<view class="right">
<image src="/static/image/other/score/guandong/rightIcon.png" mode=""></image>
</view>
</view>
</view>
<view class="" :style="{height:$systemInfo.statusBarHeight*2+88+'rpx'}">
</view>
<view class="content">
<view class="title">
{{ getTime(data.form.time,0) }}
</view>
<view class="dec">
{{ getTime(data.form.time,1) }}广东省普通高等学校招生全国统一考试
</view>
</view>
<image class="bgimg" :src="data.bgImg" mode="widthFix"></image>
<view class="list" @click="openEditDialog">
<view class="item">
<view class="text1">
姓名
</view>
<view class="text2">
{{data.form.name}}
</view>
</view>
<view class="item">
<view class="text1">
考生号
</view>
<view class="text2">
{{data.form.examNumber}}
</view>
</view>
<view class="item">
<view class="text1">
语文
</view>
<view class="text2">
{{data.form.chinese}}
</view>
</view>
<view class="item">
<view class="text1">
数学
</view>
<view class="text2">
{{data.form.math}}
</view>
</view>
<view class="item">
<view class="text1">
英语
</view>
<view class="text2">
{{data.form.english}}
</view>
</view>
<view class="item">
<view class="text1">
{{ subjectLabels.subject3 }}
</view>
<view class="text2">
{{data.form.history}}
</view>
</view>
<view class="item">
<view class="text1">
{{ subjectLabels.subject4 }}
</view>
<view class="text2">
{{data.form.politics}}
</view>
</view>
<view class="item">
<view class="text1">
{{ subjectLabels.subject5 }}
</view>
<view class="text2">
{{data.form.geography}}
</view>
</view>
<view class="item">
<view class="text1">
普通类 ({{ subjectLabels.trackName }})总分
</view>
<view class="text2">
{{data.form.total}}
</view>
</view>
<view class="item">
<view class="text1">
本科普通类 ({{ subjectLabels.trackName }})总分 (含加分)
</view>
<view class="text2">
{{data.form.undergraduateTotal}}
</view>
</view>
<view class="item">
<view class="text1">
排位
</view>
<view class="text2">
{{data.form.ranking}}
</view>
</view>
<view class="item">
<view class="text1">
专科普通类 ({{ subjectLabels.trackName }})总分 (含加分)
</view>
<view class="text2">
{{data.form.technicalTotal}}
</view>
</view>
<view class="item">
<view class="text1">
排位
</view>
<view class="text2">
{{data.form.technicalRanking}}
</view>
</view>
</view>
</view>
<safeBottom bgColor="#F3F8FB" />
<!-- 编辑弹窗 -->
<view v-if="editDialog.show" class="editDialog">
<view class="editDialog_bg">
<view class="editDialog_header">
<text class="title">编辑成绩信息</text>
</view>
<view class="editDialog_body">
<view class="formItem">
<text>科目</text>
<view class="subject-options">
<view class="subject-option" :class="{ active: editDialog.data.subject === '文科' }"
@click="editDialog.data.subject = '文科'">文科</view>
<view class="subject-option" :class="{ active: editDialog.data.subject === '理科' }"
@click="editDialog.data.subject = '理科'">理科</view>
</view>
</view>
<view class="formItem">
<text>姓名</text>
<input v-model="editDialog.data.name" placeholder="请输入姓名" />
</view>
<view class="formItem">
<text>证件号</text>
<input v-model="editDialog.data.idNumber" placeholder="请输入证件号" />
</view>
<view class="formItem">
<text>考生号</text>
<input v-model="editDialog.data.examNumber" placeholder="请输入考生号" />
</view>
<view class="formItem">
<text>语文</text>
<input v-model="editDialog.data.chinese" type="digit" placeholder="请输入语文成绩" />
</view>
<view class="formItem">
<text>数学</text>
<input v-model="editDialog.data.math" type="digit" placeholder="请输入数学成绩" />
</view>
<view class="formItem">
<text>英语</text>
<input v-model="editDialog.data.english" type="digit" placeholder="请输入英语成绩" />
</view>
<view class="formItem">
<text>{{ editSubjectLabels.subject3 }}</text>
<input v-model="editDialog.data.history" type="digit" :placeholder="'请输入' + editSubjectLabels.subject3 + '成绩'" />
</view>
<view class="formItem">
<text>{{ editSubjectLabels.subject4 }}</text>
<input v-model="editDialog.data.politics" type="digit" :placeholder="'请输入' + editSubjectLabels.subject4 + '成绩'" />
</view>
<view class="formItem">
<text>{{ editSubjectLabels.subject5 }}</text>
<input v-model="editDialog.data.geography" type="digit" :placeholder="'请输入' + editSubjectLabels.subject5 + '成绩'" />
</view>
<view class="formItem">
<text>普通类 ({{ editSubjectLabels.trackName }})总分</text>
<text style="flex:1;text-align:right;font-size:28rpx;color:#333;">{{editTotal}}</text>
</view>
<view class="formItem">
<text>本科普通类 ({{ editSubjectLabels.trackName }})总分 (含加分)</text>
<input v-model="editDialog.data.undergraduateTotal" type="digit" placeholder="请输入本科总分" />
</view>
<view class="formItem">
<text>排位</text>
<input v-model="editDialog.data.ranking" type="digit" placeholder="请输入排位" />
</view>
<view class="formItem">
<text>专科普通类 ({{ editSubjectLabels.trackName }})总分 (含加分)</text>
<input v-model="editDialog.data.technicalTotal" type="digit" placeholder="请输入专科总分" />
</view>
<view class="formItem">
<text>排位</text>
<input v-model="editDialog.data.technicalRanking" type="digit" placeholder="请输入排位" />
</view>
<view class="formItem">
<text>时间</text>
<view style="width:100%;">
<DateTimePicker :defaultDate="editDialog.data.time" :mode="1" @onChange="onChangeStartDate" />
</view>
</view>
</view>
<view class="popup-footer">
<view class="btn-cancel" @click="editDialog.show=false">取消</view>
<view class="btn-save" @click="saveEdit">保存</view>
</view>
</view>
</view>
</template>
<style lang="scss">
page{
background-color: #F3F8FB;
}
* {
box-sizing: border-box;
}
.flex-row {
display: flex;
flex-direction: row;
}
.flex-col {
display: flex;
flex-direction: column;
}
.justify-start {
display: flex;
justify-content: flex-start;
}
.justify-center {
display: flex;
justify-content: center;
}
.justify-end {
display: flex;
justify-content: flex-end;
}
.justify-between {
display: flex;
justify-content: space-between;
}
.items-start {
display: flex;
align-items: flex-start;
}
.items-end {
display: flex;
align-items: flex-end;
}
.items-center {
display: flex;
align-items: center;
}
.no-shrink {
flex-shrink: 0;
}
/* 编辑弹窗样式 */
.editDialog {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.editDialog_bg {
width: 680rpx;
max-height: 80vh;
background-color: #ffffff;
border-radius: 20rpx;
overflow: hidden;
display: flex;
flex-direction: column;
}
.editDialog_header {
padding: 30rpx;
border-bottom: 1rpx solid #eee;
text-align: center;
.title {
font-size: 32rpx;
font-weight: bold;
color: #333;
}
}
.editDialog_body {
padding: 20rpx 30rpx;
overflow-y: auto;
max-height: 60vh;
}
.formItem {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
text {
width: 200rpx;
font-size: 28rpx;
color: #666;
flex-shrink: 0;
}
input {
flex: 1;
font-size: 28rpx;
color: #333;
text-align: right;
}
.subject-options {
flex: 1;
display: flex;
justify-content: flex-end;
gap: 20rpx;
}
.subject-option {
padding: 8rpx 32rpx;
font-size: 26rpx;
color: #666;
border: 1rpx solid #ddd;
border-radius: 8rpx;
&.active {
color: #187AFF;
border-color: #187AFF;
background-color: rgba(24, 122, 255, 0.08);
}
}
}
.popup-footer {
display: flex;
padding: 20rpx 30rpx;
border-top: 1rpx solid #eee;
gap: 20rpx;
.btn-cancel,
.btn-save {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 10rpx;
font-size: 28rpx;
}
.btn-cancel {
background-color: #f5f5f5;
color: #666;
}
.btn-save {
background-color: #187AFF;
color: #fff;
}
}
</style>
<style lang="scss" scoped>
.headerBox{
z-index: 999;
position: fixed;
left: 0;
top: 0;
width: 100%;
}
.header{
width: 100%;
height: 88rpx;
display: flex;
justify-content: space-between;
align-items: center;
.left{
width: 216rpx;
height: 88rpx;
image{
width: 36rpx;
height: 36rpx;
margin-left: 30rpx;
margin-top:46rpx;
}
}
.center{
font-weight: 400;
font-size: 19px;
color: #FFFFFF;
padding-top: 30rpx;
}
.right{
width: 216rpx;
display: flex;
align-items: center;
image{
width: 200rpx;
height: 64rpx;
}
}
}
.bgimg{
position: fixed;
width: 100%;
left: 0;
top: 0;
}
.content{
position: relative;
z-index: 9;
padding-left: 40rpx;
margin-top: 100rpx;
.title{
font-weight: 400;
font-size: 25px;
color: #FFFFFF;
}
.dec{
font-weight: 400;
font-size: 16px;
color: #FFFFFF;
white-space: nowrap;
}
}
.list{
position: relative;
z-index: 9;
margin: 72rpx 40rpx 0;
background: #FFFFFF;
box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.1);
border-radius: 12px 12px 12px 12px;
padding-left: 32rpx;
.item{
height: 72rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1rpx solid #F2F2F2;
.text1{
font-weight: 400;
font-size: 12px;
color: #898D96;
}
.text2{
font-weight: 400;
font-size: 12px;
color: #1A1A1A;
margin-right: 28rpx;
}
}
}
</style>

1464
pages/other/score/hebei.vue Normal file

File diff suppressed because it is too large Load Diff

180
pages/other/score/index.vue Normal file
View File

@ -0,0 +1,180 @@
<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-image': `url('/static/image/other/score/${item.icon}bg.png')` }">
<view class="content flex flex-align-center">
<image class="logo" :src="`/static/image/other/score/${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: 'icon1',
callUrl: "/pages/other/score/center"
},
{
name: '广东省',
color: {
bgColor: '#FFE9E9',
lineColor: '#FF6969',
buttonColor: '#FB6767',
},
icon: 'guandong',
callUrl: "/pages/other/score/guandong"
},
{
name: '重庆市',
color: {
bgColor: '#FFF0DD',
lineColor: '#FFA143',
buttonColor: '#FFA64D',
},
icon: 'chongqing',
callUrl: "/pages/other/score/chongqing"
},
{
name: '河北省',
color: {
bgColor: '#E0FFD9',
lineColor: '#56B745',
buttonColor: '#5DCD49',
},
icon: 'hebei',
callUrl: "/pages/other/score/hebei"
}
])
const data = reactive({
navBar: {
title: '高考成绩',
bgColor: '#F0F4F9',
},
type: "message"
})
onLoad((options) => {
if (options.type) {
data.type = options.type
}
proxy.$apiUserEvent('all', {
type: 'event',
key: 'score',
prefix: '.uni.other.',
value:'高考查询'
})
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#ffffff')
// 便
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// #endif
})
function goPage(url,name) {
if (url) {
proxy.$apiUserEvent('all', {
type: 'event',
key: 'score',
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: 160rpx;
border-radius: 24rpx;
margin-bottom: 24rpx;
padding: 40rpx 36rpx 24rpx 28rpx;
background-repeat: no-repeat;
background-size: 100% 100%;
.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>

View File

@ -0,0 +1,869 @@
<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" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}" v-for="(item, index) in data.typeList" :key="index" :class="{ active: data.type == index }"
@click="setType(index)">
{{item}}
</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" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}">
<button class="btn-save-image" @click="saveImage">保存图片</button>
</view>
<view style="width: 0px;height: 0;overflow: hidden;" v-if="data.shuaxing && currentBgImg">
<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;position: relative;">
<l-painter-image :src="data.bgImg1" css="width:351px;height:460px;position:absolute;left:0;top:0;object-fit:fill;" />
<!-- 落款日期 -->
<l-painter-view css="position: absolute;left:82px;bottom:100px;">
<l-painter-text :css="data.textFont+data.textCss" :text="toVerticalText(data.banner.dateText)" />
</l-painter-view>
<!-- 敬赠落款 -->
<l-painter-view css="position: absolute;left:100px;bottom:100px;">
<l-painter-text :css="data.textFont+data.textCss" :text="toVerticalText(data.banner.donorLine)" />
</l-painter-view>
<!-- 受赠对象画布宽351 right:72 left:267避免鸿蒙 right 定位失效 -->
<l-painter-view css="position: absolute;top:80px;left:267px;">
<l-painter-text :css="data.textFont+data.textCss" :text="toVerticalText(data.banner.recipientLine)" />
</l-painter-view>
<!-- 中心大字装饰 right 均改为 left字宽按 20px -->
<l-painter-view css="position: absolute;top:80px;left:168px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view css="position: absolute;top:108px;left:120px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view css="position: absolute;top:108px;left:219px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view css="position: absolute;top:150px;left:102px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view css="position: absolute;top:150px;left:235px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view css="position: absolute;top:192px;left:136px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<l-painter-view css="position: absolute;top:192px;left:211px;">
<l-painter-text :css="data.textFont+data.textCss1" :text="data.banner.centerChar" />
</l-painter-view>
<!-- 中心主字 -->
<l-painter-view css="position: absolute;top:105px;left:135px;">
<l-painter-text :css="data.textFont+data.textCss2" :text="data.banner.centerChar" />
</l-painter-view>
<!-- 上句赞语 -->
<l-painter-view css="position: absolute;bottom:160px;left:0;">
<l-painter-text :css="data.textFont+data.textCss3" :text="data.banner.phraseTop" />
</l-painter-view>
<!-- 下句赞语 -->
<l-painter-view css="position: absolute;bottom:118px;left:0;">
<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:127px;bottom:132px;">
<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;position: relative;">
<l-painter-image :src="data.bgImg2" css="width:351px;height:460px;position:absolute;left:0;top:0;object-fit:fill;" />
<!-- 落款日期 -->
<l-painter-view css="position: absolute;left:82px;bottom:100px;">
<l-painter-text :css="data.textFont+data.textCss" :text="toVerticalText(data.banner.dateText)" />
</l-painter-view>
<!-- 敬赠落款 -->
<l-painter-view css="position: absolute;left:100px;bottom:100px;">
<l-painter-text :css="data.textFont+data.textCss" :text="toVerticalText(data.banner.donorLine)" />
</l-painter-view>
<!-- 受赠对象 -->
<l-painter-view css="position: absolute;top:80px;left:267px;">
<l-painter-text :css="data.textFont+data.textCss" :text="toVerticalText(data.banner.recipientLine)" />
</l-painter-view>
<!-- 上句赞语右列 -->
<l-painter-view css="position: absolute;top:95px;left:188px;">
<l-painter-text :css="data.textFont+data.textCss4" :text="toVerticalText(data.banner.phraseTop)" />
</l-painter-view>
<!-- 下句赞语左列 -->
<l-painter-view css="position: absolute;top:95px;left:118px;">
<l-painter-text :css="data.textFont+data.textCss4" :text="toVerticalText(data.banner.phraseBottom)" />
</l-painter-view>
<l-painter-view v-if="$isVip()" css="position: absolute;left:127px;bottom:132px;">
<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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
</view>
</template>
<script setup>
//
import ZdyNavbar from "@/components/nav-bar/nav-bar.vue"
import { imgLocal } from '@/utils/common.js';
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 : {})
}
/** 竖排:用换行代替窄 width 自动折行(鸿蒙 canvas measureText 下窄宽不换行) */
function toVerticalText(text) {
return String(text ?? '').split('').join('\n')
}
async function loadBannerBg(path, fallback) {
try {
const localPath = await imgLocal.getLocalImage(path)
return localPath || fallback
} catch (e) {
console.error('背景图加载失败,使用本地兜底', path, e)
return fallback
}
}
function refreshPainter() {
data.shuaxing = false
setTimeout(() => {
data.shuaxing = true
}, 80)
}
const data = reactive({
isShow:false,
type: 0,
bgImg1:'',
bgImg2:'',
banner: mergeBanner(null, 0),
shuaxing: false,
navbar: {
title: "锦旗",
bgColor: '#EDEDED',
},
width: 800,
height: 600,
code: '',
textCss: 'text-align:center;font-weight:400;width:12px;color:#FFDD6D;font-size:12px;line-height:1.15em;',
textCss1: 'text-align:center;font-weight:400;width:20px;color:#FFDD6D;font-size:20px;line-height:1em;',
textCss2: 'text-align:center;font-weight:400;width:82px;color:#FFDD6D;font-size:78px;line-height:1em;',
textCss3: 'text-align:center;width:351px;font-weight:400;letter-spacing:16px;color:#FFDD6D;font-size:28px;line-height:1em;',
textCss4: 'text-align:center;width:44px;font-weight:400;color:#FFDD6D;font-size:40px;line-height:1.15em;',
textFont: 'font-family:"certificate2";',
typeList: ['样式1', '样式2']
})
const currentBgImg = computed(() => data.type === 0 ? data.bgImg1 : data.bgImg2)
//
const showEditPopup = ref(false);
const editForm = ref({});
onLoad(async(option) => {
uni.showLoading({
title: "生成中"
})
data.bgImg1 = await loadBannerBg('other/silkBanner/banner1.png', '/static/image/other/silkBanner/banner1.png')
data.bgImg2 = await loadBannerBg('other/silkBanner/banner2.png', '/static/image/other/silkBanner/banner2.png')
//
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']
const startPaint = () => {
data.isShow = true
data.shuaxing = true
}
if (!font?.certificate2) {
console.warn('字体配置缺失,直接生成锦旗')
startPaint()
return
}
console.log("字体地址信息", font.certificate2);
const fontUrl = font.certificate2;
const fontName = 'certificate2';
const loadFont = (path) => {
uni.loadFontFace({
family: fontName,
source: `url("${path}")`,
success() {
startPaint()
console.log('字体加载成功');
},
fail(err) {
startPaint()
console.error('字体加载失败', err);
}
});
};
// #ifdef H5
loadFont(fontUrl);
// #endif
// #ifndef H5
const savedFontPath = uni.getStorageSync('certificate2_font_path');
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);
loadFont(res.tempFilePath);
}
});
} else {
startPaint()
}
},
fail: (err) => {
console.error('下载字体失败', err);
startPaint()
}
});
}
// #endif
})
onReady(() => {
})
onShow(() => {})
onPullDownRefresh(() => {
setTimeout(() => {
uni.stopPullDownRefresh();
}, 1000);
})
onReachBottom(() => {
})
function setType(index) {
if (data.type === index) return
uni.showLoading({
title: "生成中"
})
data.type = index
data.banner = mergeBanner(uni.getStorageSync("silkBanner" + data.type), data.type)
data.code = ''
refreshPainter()
}
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.banner = mergeBanner(editForm.value, data.type)
data.code = ''
refreshPainter()
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) => {
// #ifdef APP-PLUS&&!APP-HARMONY
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
entry.file((file) => {
const reader = new plus.io.FileReader();
reader.onload = (e) => {
resolve(e.target.result)
};
reader.onerror = (err) => reject(err);
reader.readAsDataURL(file);
}, reject);
}, reject);
// #endif
// #ifndef APP-PLUS
reject(new Error('plus.io not supported'))
// #endif
});
}
//
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;
position: fixed;
left: 0;
bottom: 86px;
width: 100vw;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
z-index: 10;
}
.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);
}
.container {
padding-bottom: 220rpx;
}
.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
z-index: 10;
.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>

File diff suppressed because it is too large Load Diff

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,9 +1,9 @@
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark dark="light" source="uni_alipay_other_tickets_12306" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_tickets_12306')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="container">
@ -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>
@ -144,8 +144,7 @@
<!-- 分享区 -->
<view class="share-section">
<text class="share-tips">美好旅途快与朋友一起分享吧~</text>
<image style="width: 158rpx;height: 50rpx;"
src="/static/image/other/train-tickets/12306-tickets/share.png">
<image style="width: 158rpx;height: 50rpx;" src="/static/image/other/train-tickets/12306-tickets/share.png">
</image>
</view>
@ -157,11 +156,9 @@
<!-- 广告Banner -->
<view class="ad-banner">
<image class="bg-image" src="/static/image/other/train-tickets/12306-tickets/hotel-bg.png"
mode="widthFix">
<image class="bg-image" src="/static/image/other/train-tickets/12306-tickets/hotel-bg.png" mode="widthFix">
</image>
<image class="bg-image" style="opacity: 0;position: relative;"
src="/static/image/other/train-tickets/12306-tickets/hotel-bg.png" mode="widthFix">
<image class="bg-image" style="opacity: 0;position: relative;" src="/static/image/other/train-tickets/12306-tickets/hotel-bg.png" mode="widthFix">
</image>
<view class="hotel-ad">
<view class="hotel-search-row">
@ -190,6 +187,7 @@
src="/static/image/other/train-tickets/12306-tickets/bottom-bg.png" mode="widthFix">
</image>
</view>
<ZdySafeBottom bgColor="#fff"/>
</view>
</scroll-view>
</view>
@ -197,12 +195,13 @@
<script setup>
import NavBar from '@/components/nav-bar/nav-bar.vue';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
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(() => {
@ -379,7 +348,7 @@ onShow(() => {
Object.assign(data.ticketsInfo, uni.getStorageSync('ticketsInfo'))
calculateNightCount()
}
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#ffffff')
setTimeout(() => {
plus.navigator.setStatusBarStyle("light");
@ -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,39 @@ const hotelDateRange = computed({
})
onMounted(() => {
const stored = uni.getStorageSync('ticketsInfo')
const stored = uni.getStorageSync(data.STORAGE_KEY)
if (stored) {
Object.assign(data.ticketsInfo, stored)
}
//
const ticketDate = data.ticketsInfo.ticketInfo.date;
if (ticketDate) {
const parts = ticketDate.split('.');
if (parts.length === 3) {
const mmdd = `${parts[1]}-${parts[2]}`;
const oldDepTime = data.ticketsInfo.ticketInfo.departureTime || '';
const timePart = oldDepTime.split(' ')[1] || '00:00';
data.ticketsInfo.ticketInfo.departureTime = `${mmdd} ${timePart}`;
}
}
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 +390,7 @@ const handleRightButtonClick = () => {
}
}
uni.setStorageSync('ticketsInfo', data.ticketsInfo)
uni.setStorageSync(data.STORAGE_KEY, data.ticketsInfo)
uni.navigateBack()
}
@ -327,7 +412,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 +433,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();
}
/**
@ -407,6 +495,15 @@ const onTicketDateChange = (e) => {
const dateObj = new Date(val.replace(/-/g, '/')); // Compatible
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
data.ticketsInfo.ticketInfo.weekDay = days[dateObj.getDay()];
//
const parts = val.split('-');
if (parts.length === 3) {
const mmdd = `${parts[1]}-${parts[2]}`;
const oldDepTime = data.ticketsInfo.ticketInfo.departureTime || '';
const timePart = oldDepTime.split(' ')[1] || '00:00';
data.ticketsInfo.ticketInfo.departureTime = `${mmdd} ${timePart}`;
}
}
}
@ -432,8 +529,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 +571,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 +859,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,683 @@
<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')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</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 class="" style="width: 100%;overflow: hidden;">
<ZdySafeBottom bgColor="#f0f3f8" />
</view>
</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';
import ZdySafeBottom from '@/components/safeBottom/safeBottom.vue'
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;
width: 100vw;
overflow: hidden;
}
</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>

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
<template>
<template>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
<watermark dark="light" source="uni_alipay_other_groupChat" />
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark', 'uni_alipay_other_groupChat')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
<view class="container">
@ -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" :style="{'margin-bottom':$store.state.safeBottomAz+'px'}"></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();
@ -265,7 +268,10 @@ onLoad(() => {
...videoData,
iconType: videoData.onMic ? videoData.iconType : 1,
videoList: videoData.videoList.map((item) => {
item.videoUrl = item.savedVideoUrl
// #ifdef APP-PLUS&&!APP-HARMONY
item.videoUrl = plus.io.convertLocalFileSystemURL(item.savedVideoUrl)
// #endif
return item
})
}
@ -290,7 +296,7 @@ onShow(() => {
}
startTimer()
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#232323')
// 便
statusBarTimer = setTimeout(() => {
@ -523,7 +529,10 @@ const chooseVideo = () => {
camera: 'back',
success: async (res) => {
console.log('选择视频成功:', res)
const videoUrl = plus.io.convertLocalFileSystemURL(res.tempFilePath)
let videoUrl = res.tempFilePath
// #ifdef APP-PLUS&&!APP-HARMONY
videoUrl = plus.io.convertLocalFileSystemURL(res.tempFilePath)
// #endif
const savedVideoUrl = await new Promise((resolve, reject) => {
uni.saveFile({
tempFilePath: videoUrl,
@ -661,7 +670,7 @@ const removeFile = (filePath) => {
console.log('🗑️ 准备删除文件:', filePath)
// #ifdef APP-PLUS
// #ifdef APP-PLUS&&!APP-HARMONY
// 使 plus.io ( file:// _downloads )
if (filePath.startsWith('file://') || filePath.includes('_downloads') || filePath.includes('saved_video_')) {
plus.io.resolveLocalFileSystemURL(filePath, (entry) => {
@ -681,7 +690,7 @@ const removeFile = (filePath) => {
// 使 uni.removeSavedFile ( uni.saveFile )
uni.removeSavedFile({
filePath: filePath,
success: (res) => {
success: () => {
console.log('✅ uni.removeSavedFile 删除成功:', filePath)
},
fail: (err) => {
@ -718,7 +727,7 @@ const handleTouchStart = (event, index) => {
}
// -
const handleTouchMove = (event, index) => {
const handleTouchMove = (event) => {
if (!data.dragState.isDragging) return
const touch = event.touches[0]
@ -731,7 +740,7 @@ const handleTouchMove = (event, index) => {
}
// -
const handleTouchEnd = (event, index) => {
const handleTouchEnd = () => {
//
if (data.dragState.longPressTimer) {
clearTimeout(data.dragState.longPressTimer)
@ -1024,7 +1033,6 @@ const stopTimer = () => {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
background-color: #4a4a4a;
display: flex;
align-items: center;
justify-content: center;

View File

@ -0,0 +1,361 @@
<template>
<view class="container">
<nav-bar bgColor="transparent" :isBack="false">
<template v-slot:center>
<view class="flex-cneter title-box flex" style="margin-top: 24rpx;">
<image class="wx-logo" src="/static/image/other/payment-success/payment-success.png"></image>
<text class="title">支付成功</text>
</view>
</template>
</nav-bar>
<view class="content-box flex-column flex-cneter" @click="editPaymentInfo">
<image v-if="paymetInfo.userImg" class="user-img" :src="paymetInfo.userImg" mode="aspectFill"></image>
<text class="product-name">{{ paymetInfo.productName }}</text>
<view class="money flex wx-font-medium" :style="{ 'margin-top': paymetInfo.userImg ? '78rpx' : '46rpx' }">
<image class="symbol" src="/static/image/other/payment-success/symbol-yuan.png"></image>{{
Number(paymetInfo.money).toFixed(2) }}
</view>
<image v-if="paymetInfo.isAd" class="ad-img" src="/static/image/other/payment-success/ad-img.png"
mode="widthFix">
</image>
</view>
<view class="finish-butn" @click="util.goBack()">完成</view>
<!-- 修改支付信息弹窗 -->
<uni-popup ref="editPaymentInfoPopup" type="center">
<view class="edit-popup">
<view class="title">修改支付信息</view>
<view class="input-item flex-align-center">
<text class="label">用户头像</text>
<view class="avatar-uploader flex-1">
<view class="avatar-preview" v-if="paymetInfo.userImg">
<image class="preview-img" :src="paymetInfo.userImg" mode="aspectFill"></image>
<view class="delete-icon" @click="handleDeleteAvatar">×</view>
</view>
<view class="upload-btn" v-else @click="handleChooseAvatar">
<text class="plus">+</text>
</view>
</view>
</view>
<view class="input-item flex-align-center">
<text class="label"><text style="color: #FF3B30;">*</text>商品名称</text>
<input class="flex-1" type="text" v-model="paymetInfo.productName" placeholder="请输入商品名称" />
</view>
<view class="input-item flex-align-center">
<text class="label"><text style="color: #FF3B30;">*</text>支付金额</text>
<input class="flex-1" type="digit" v-model="paymetInfo.money" placeholder="请输入支付金额" />
</view>
<view class="input-item flex-align-center">
<text class="label">展示广告</text>
<switch class="flex-1" :checked="paymetInfo.isAd" @change="paymetInfo.isAd = $event.detail.value"
color="#16B264" style="transform:scale(0.8);transform-origin:left center;" />
</view>
<view class="btn-group flex">
<view class="btn cancel flex-1" @click="editPaymentInfoPopup.close()">取消</view>
<view style="width: 30rpx;"></view>
<view class="btn confirm flex-1" @click="confirmEditPaymentInfo">确定</view>
</view>
</view>
</uni-popup>
</view>
<!-- 水印 -->
<view v-if="$isVip()">
<watermark dark="light" source="uni_alipay_other_wx_pay_success" />
<liu-drag-button :canDocking="false"
@clickBtn="$goRechargePage('watermark', 'uni_alipay_other_wx_pay_success')">
<image src='/static/lottie/watermark.gif' mode="widthFix" style="width:94px;height:74px;" ></image>
</liu-drag-button>
</view>
</template>
<script setup>
import { ref, getCurrentInstance } from 'vue';
import { onShow, onLoad } from '@dcloudio/uni-app';
import { util } from '@/utils/common';
import { storage } from '@/utils/storage';
const {
proxy
} = getCurrentInstance();
const editPaymentInfoPopup = ref(null);
const editPaymentInfo = () => {
editPaymentInfoPopup.value.open('center');
}
const handleChooseAvatar = () => {
uni.chooseImage({
count: 1,
success: (res) => {
const tempFilePath = res.tempFilePaths[0];
// #ifndef H5
uni.saveFile({
tempFilePath: tempFilePath,
success: (saveRes) => {
if (paymetInfo.value.userImg && !paymetInfo.value.userImg.startsWith('/static/')) {
uni.removeSavedFile({ filePath: paymetInfo.value.userImg });
}
paymetInfo.value.userImg = saveRes.savedFilePath;
}
});
// #endif
// #ifdef H5
paymetInfo.value.userImg = tempFilePath;
// #endif
}
});
};
const handleDeleteAvatar = () => {
// #ifndef H5
if (paymetInfo.value.userImg && !paymetInfo.value.userImg.startsWith('/static/')) {
uni.removeSavedFile({ filePath: paymetInfo.value.userImg });
}
// #endif
paymetInfo.value.userImg = '';
};
const confirmEditPaymentInfo = () => {
editPaymentInfoPopup.value.close();
storage.set('wx_payment_success_info', paymetInfo.value);
}
const buttonGroup = [
{
name: "修改支付信息",
click: () => {
editPaymentInfo()
}
}
]
const paymetInfo = ref({
userImg: '',
productName: '王者荣耀',
money: 298,
isAd: false,
});
onShow(() => {
// #ifdef APP-PLUS&&!APP-HARMONY
util.setAndroidSystemBarColor('#FFFFFF')
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// #endif
})
onLoad(async () => {
const cachedInfo = storage.get('wx_payment_success_info');
if (cachedInfo) {
paymetInfo.value = cachedInfo;
} else {
paymetInfo.value = { productName: '王者荣耀', money: 168 };
}
//
proxy.$apiUserEvent('all', {
type: 'event',
key: 'wx_pay_success',
prefix: '.uni.other.',
value: "微信支付成功"
})
})
</script>
<style lang="less" scoped>
.title-box {
.wx-logo {
width: 40rpx;
height: 36rpx;
}
.title {
font-size: 32rpx;
color: #16B264;
line-height: 32rpx;
font-weight: 500;
margin-left: 10rpx;
}
}
.content-box {
margin-top: 162rpx;
align-items: center;
.user-img {
width: 94rpx;
height: 94rpx;
border-radius: 50%;
background-color: #D8D8D8;
transform: translateY(-50%);
}
.product-name {
font-size: 32rpx;
color: #1A1A1A;
line-height: 32rpx;
text-align: center;
}
.money {
align-items: center;
justify-content: center;
margin-top: 46rpx;
font-weight: 500;
font-size: 88rpx;
color: #1A1A1A;
line-height: 88rpx;
.symbol {
width: 32rpx;
height: 46rpx;
margin-right: 20rpx;
}
}
.ad-img {
margin-top: 100rpx;
width: 654rpx;
}
}
.finish-butn {
position: fixed;
left: 50%;
transform: translateX(-50%);
width: 348rpx;
height: 92rpx;
background-color: #F1F1F1;
color: #1A1A1A;
line-height: 92rpx;
font-size: 28rpx;
font-weight: 500;
border-radius: 12rpx;
text-align: center;
margin: 0 auto;
bottom: 170rpx;
bottom: calc(170rpx + env(safe-area-inset-bottom));
bottom: calc(170rpx + constant(safe-area-inset-bottom));
}
.edit-popup {
width: 600rpx;
background-color: #FFFFFF;
border-radius: 24rpx;
padding: 40rpx;
box-sizing: border-box;
.title {
font-size: 32rpx;
color: #1A1A1A;
font-weight: 500;
text-align: center;
margin-bottom: 40rpx;
}
.input-item {
margin-bottom: 30rpx;
.label {
font-size: 28rpx;
color: #1A1A1A;
width: 140rpx;
}
input {
height: 72rpx;
background-color: #F8F8F8;
border-radius: 12rpx;
padding: 0 20rpx;
font-size: 28rpx;
color: #1A1A1A;
}
.avatar-uploader {
display: flex;
align-items: center;
.upload-btn {
width: 100rpx;
height: 100rpx;
border-radius: 50%;
background-color: #F8F8F8;
display: flex;
justify-content: center;
align-items: center;
border: 2rpx dashed #CCCCCC;
.plus {
font-size: 60rpx;
color: #999;
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;
}
}
}
}
.btn-group {
margin-top: 50rpx;
justify-content: space-between;
.btn {
height: 80rpx;
border-radius: 12rpx;
text-align: center;
line-height: 80rpx;
font-size: 30rpx;
&.cancel {
background-color: #F1F1F1;
color: #333333;
}
&.confirm {
background-color: #3b82f6;
color: #FFFFFF;
}
}
}
}
</style>
<style>
@import '/common/main.css';
page {
background-color: #FFFFFF;
}
</style>

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

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