Compare commits

..

387 Commits

Author SHA1 Message Date
isummer e12779d654 修复容器部署失败问题,重新编写本地部署文档 2 years ago
isummer ac590563a7 修复超级签共享用户下载文件失败问题 2 years ago
isummer 8fc1d353c9 管理后台增加重置密码功能 2 years ago
isummer 35b64d74de 修复flyapps容器构建失败问题,优化部署文档 2 years ago
isummer a064988f56 修复flyapps容器构建失败问题,优化部署文档 2 years ago
isummer adff361c11 修复非苹果设备导致余额不足问题 2 years ago
isummer 419e39eaaa 修复非苹果设备导致余额不足问题 2 years ago
isummer 9d9cb3f2cb Merge remote-tracking branch 'origin/master' 2 years ago
isummer dd802dccbb 优化代码 2 years ago
isummer 1262bff671 优化代码 2 years ago
isummer 289737ed00 修复异常,优化代码 2 years ago
isummer dc7b491534 修复新苹果,新应用无法设置专属签名问题 2 years ago
isummer eed0f5194c 增加苹果设备类型区分,优化苹果设备注册量及展示 2 years ago
isummer e0599585be 修复前端应用超级签名展示异常问题 2 years ago
isummer 42451f2cb0 修复超级签权限问题 2 years ago
isummer 646162593e 增加打包时间 2 years ago
isummer 200494fcf3 增加打包时间 2 years ago
isummer fe5a7605f0 增加打包时间 2 years ago
isummer f70351d8e5 修复容器启动异常问题 2 years ago
isummer 9e2f40383a 优化代码,增加邀请码配置,修改极验弹窗模式 2 years ago
isummer debe95791e 优化容器部署 2 years ago
isummer a3887825c9 更新依赖包,支持python39环境 2 years ago
isummer 8a8f9ef7fe 升级python环境为python39 2 years ago
isummer 8e325e6cde 优化容器启停异常 2 years ago
isummer 92961cfa61 flyapps-cli增加容器访问 2 years ago
isummer 8c25aab63c flyapps-cli增加容器访问 2 years ago
isummer 0cc5084390 修复授权绑定设备添加重复问题 2 years ago
isummer 4d98981485 容器部署支持管理后台访问 2 years ago
isummer a7a1472533 苹果应用授权码支持添加绑定设备 2 years ago
isummer 332d12bddf 容器部署优化 2 years ago
isummer 6439824468 优化docker部署方式 2 years ago
isummer 42478f21d9 1.修复授权码为一次,超级签苹果包下载失败问题 2 years ago
isummer 2c67305f8a 增加通过命令给用户充值次数 2 years ago
isummer 79037d5248 修复应用上传超级签检测问题 2 years ago
isummer 525ec097d7 更新readme 2 years ago
isummer a35ca0b087 更新配置文件信息 2 years ago
isummer d9ac93ccc1 no message 2 years ago
isummer 1ff3da6896 修复管理页面预览展示 2 years ago
isummer cbad06e95d 应用时间更新修复 2 years ago
isummer b093d7133b 页面底部信息配置迁移 2 years ago
isummer 7aa0471ba9 增加应用当天下载次数展示 2 years ago
isummer 2224ce27c7 优化代码 2 years ago
isummer d8b124c1a7 用户下载页私有配置功能 2 years ago
isummer d1a5d69ac8 增加客户端独立部署功能 2 years ago
isummer 8a8d2f2400 增加预览下载页路由模式 2 years ago
isummer 49832ca702 修复新注册用户 默认域名绑定问题 2 years ago
isummer 7075bc7ed6 修改存储添加失败问题 2 years ago
MMXX 7d70f381a1 geetest 增加代理访问 2 years ago
MMXX 15a506450c 增加管理后台应用当天下载次数展示 2 years ago
isummer 93c13fa776 日志增加function名称 2 years ago
MMXX c8f02465d0 修复异常注册问题 2 years ago
isummer 2e240735dc 优化下载次数展示 2 years ago
I夏 ec908f0dea 代码优化,增加缓存函数提前失效参数 3 years ago
I夏 47abaf61f5 订单展示格式化 3 years ago
I夏 31fe8664a9 修复注册赠送下载数异常 3 years ago
nineven f4ca8d48f9 修復转换异常问题 3 years ago
nineven e2b9b12613 修復转换异常问题 3 years ago
nineven f772d65765 存储扩容功能 3 years ago
nineven 3ad5e03504 存储扩容功能 3 years ago
nineven 5f967a9b52 修复存储切换下载token过期问题 3 years ago
nineven e7858a840e 服务器需要部署阿里云,服务器和oss存储内网互通 3 years ago
nineven b191cbae6b 优化代码,增加存储迁移锁解除功能 3 years ago
nineven c36ab08f35 优化存储逻辑和展示页面 3 years ago
nineven a5b73c3695 增加存储共享功能,增加应用历史版本设置,修复设备共享查询问题,优化存储切换逻辑 3 years ago
nineven 0c6b61327a 修复域名绑定获取最小错误返回 3 years ago
nineven 92c0345341 优化代码展示 3 years ago
nineven 25c2ea4c7a 优化代码展示 3 years ago
nineven dfcf213bed 优化代码 3 years ago
nineven 2b29e0e3bd 优化代码 3 years ago
nineven a9bbdd40bd 修复读模式获取开发者发送失败通知 3 years ago
nineven 97349e758f 代码更新 3 years ago
nineven 156bf67c1c 优化代码 3 years ago
nineven 1d75c8ed23 优化异常注册逻辑,修复设置设备状态返回异常问题 3 years ago
nineven ef243feb9f 优化查询状态 3 years ago
nineven 58472fb9e4 修改文件下载失败问题 3 years ago
nineven 6c25f6a932 优化签名开发者查询逻辑 3 years ago
nineven 2d21ee9150 优化签名开发者查询逻辑 3 years ago
nineven 04ba947219 修复链接存在密码情况下,跳转密码丢失问题 3 years ago
nineven 884d6a47e8 大意失荆州啊 3 years ago
nineven 0a64689902 修复 bytesio 导致uwsgi 不识别问题 3 years ago
nineven 5ca5b0eb34 优化错误信息展示 3 years ago
nineven 3bbabdafdb 1.增加异常设备和设备黑名单功能 3 years ago
nineven 4cd0dc7586 设备异常 3 years ago
nineven 5eb947843e 优化啦 3 years ago
nineven 17ab6e6efd 修复前端页面时间显示问题 3 years ago
nineven 94bc5cc1db 优化代码 3 years ago
nineven 584f50ba20 优化页面 3 years ago
nineven df38944eab 优化页面 3 years ago
nineven fe880b27b7 优化下载方式 3 years ago
nineven 8316a52595 下载授权码和设备绑定功能,下载页面优化 3 years ago
nineven 747d2b3dfa 优化通知提醒,增加批量设置开发者账户自动检测,定时任务修改为 【0,1,3,7】自动提醒,同步设备支持条件 3 years ago
nineven 04ae8cccc4 增加应用下载授权token, 增加超级签名多重安全验证,优化本地下载逻辑 3 years ago
nineven 3b8134aafd 优化代码 3 years ago
nineven bb9bdf8891 邮件通知模板优化 3 years ago
nineven bc0cee937d 修复定时任务多线程数据共享异常 3 years ago
nineven d1a41a179b 优化代码,增加苹果签名错误操作记录 3 years ago
nineven 3ff322849a 优化设备错误展示 3 years ago
nineven 4184f2ad1c 问题修复 3 years ago
nineven 22a9b8d816 优化代码 3 years ago
nineven 2390397f1b 微信web登录跳转优化 3 years ago
nineven ae9dfae6a3 微信web登录跳转修改,定时通知逻辑优化 3 years ago
nineven d7e3186f9d 修复配置名重复添加失败问题 3 years ago
nineven 2bfd694f23 重要,微信登录 大版本更新,上一个版本更新 3 years ago
nineven 63e09c1925 增加微信登录多账户功能 3 years ago
nineven 391e4987de 优化消息提醒 3 years ago
nineven 88745550d4 增加消息提醒功能,下载余额,签名余额不足自动检测,发送邮件或微信通知功能 3 years ago
nineven 59e73fa83e 增加消息提醒功能,下载余额,签名余额不足自动检测,发送邮件或微信通知功能 3 years ago
nineven 5eca01278b 优化代码,增加余额不足微信模板消息 3 years ago
nineven 5edf7cd65a 优化代码,增加证书信息展示功能,增加包重签功能 3 years ago
nineven cf9d9eeaf8 优化代码 3 years ago
nineven c4a4db2c7e 优化定时任务设备消耗展示 3 years ago
nineven 24a8307164 优化设备消耗展示 3 years ago
nineven 05669b8d3e 苹果设备状态异常,导致注册失败【苹果设备状态还可能为 INELIGIBLE 和 PROCESSING】,当设备存在该状态,则该开发者将无法注册新设备号,需要禁用该开发者 3 years ago
nineven 32c3058d57 优化账户激活检测代码 3 years ago
nineven 4bbae93933 优化代码 3 years ago
nineven f94184551a 优化代码 3 years ago
nineven dd73441bdd 优化代码 3 years ago
nineven 4e7927a332 优化代码,修复定时任务检测证书获取异常并清理问题 3 years ago
MMXX fc1963c8a9 优化代码 3 years ago
MMXX d15b583804 优化代码 3 years ago
MMXX d930603919 优化代码 3 years ago
MMXX 9b9be3fcf9 版本更新 3 years ago
MMXX 378912944f 优化代码 3 years ago
MMXX 75c7d365a3 优化代码 3 years ago
MMXX 1dec3b7de2 Merge branch 'xsign' of https://github.com/nineaiyu/FlyApps into xsign 3 years ago
MMXX eeba5b409a 优化代码展示 3 years ago
nineven 684a407f97 优化代码,增加设备同步详细日志【有时候会删除该开发者下所有签名数据,很诡异】 3 years ago
nineven 5901d63f3e 优化代码,增加设备同步详细日志【有时候会删除该开发者下所有签名数据,很诡异】 3 years ago
MMXX cf14dcb0a5 代码优化,苹果开发者账户展示优化 3 years ago
MMXX e780a2597e 增加共享设备一键清理功能 3 years ago
MMXX 02f908e3de 增加共享设备一键清理功能 3 years ago
MMXX 7ceb9c83c1 优化代码,修改共享撤回问题 3 years ago
MMXX 77d97fd1d2 优化签名包下载逻辑 3 years ago
nineven 98fe1d5f61 优化代码 3 years ago
nineven fb77c251c3 优化代码 3 years ago
nineven c765658fb5 微信支付,微信web登录功能优化 3 years ago
nineven dd1fb29ba0 优化微信登录和获取任务长轮询请求方式 3 years ago
nineven d571b399d7 代码优化 3 years ago
nineven 115d1c95df 信号优化 3 years ago
nineven e54f6c1286 验证码优化 3 years ago
nineven 75fecbca10 超级签名重签功能 3 years ago
nineven 96295132d9 应用上传黑白名单 3 years ago
nineven ec327598a2 优化代码结构 3 years ago
nineven 81db86b9ef 优化代码结构 3 years ago
nineven 6076659bf1 Merge branch 'master' into xsign 3 years ago
nineven 4b90ce682b 问题修复 3 years ago
nineven f00135a6a4 xsign 模块 3 years ago
nineven 8ce3b14475 Merge branch 'master' into xsign 3 years ago
nineven 25d09e8cf1 修复小问题 3 years ago
nineven c6e78a6eab 优化代码 3 years ago
nineven e9c6cadeb6 重新迁移数据文件 3 years ago
nineven 153f800b91 代码结构优化 3 years ago
nineven 5e380fa09e 优化代码 3 years ago
nineven 42bab0739b 优化代码目录结构 3 years ago
nineven 518fc3101e 优化配置文件读取,支持文件数据库配置模板变量使用 3 years ago
nineven 875a3a4305 设备转移之后,匹配异常问题 3 years ago
nineven 6051f443c1 优化代码,配置文件内部支持template语法,修复新设备注册异常的问题 3 years ago
nineven c4fd9aa793 修复json配置读取失败的问题 3 years ago
nineven 9ba02e449d 大版本更新,签名逻辑更改 3 years ago
nineven cb841b5cf7 大版本更新,签名逻辑更改 3 years ago
nineven 9353abc8a9 配置文件迁移更新 3 years ago
nineven 06bace9f66 配置文件迁移更新 3 years ago
nineven ce31dd5621 优化域名绑定功能 3 years ago
nineven 91e042ea1d 優化代碼 3 years ago
nineven f02eb026de 开发者账号支持自定义自动检测 3 years ago
nineven 44cfd03cee 设置配置缓存过期时间30天 3 years ago
nineven 14f60d4f8d 部分配置信息迁移到数据库,方便直接修改数据库生效 3 years ago
nineven 614db12422 设备共享逻辑及前端页面书写 3 years ago
nineven 10ab90a104 修复清理开发者脏数据失败问题 3 years ago
nineven f5ffc3632f 签名账户设备转移功能 3 years ago
nineven adc71de160 优化上传p8key文件 3 years ago
nineven 6f959fa4a9 优化上传p8key文件 3 years ago
nineven 3d2ce3b73d 优化添加苹果开发者账号逻辑 3 years ago
nineven 192d4cadc0 优化代码 3 years ago
nineven 9f2fea094d 优化代码 3 years ago
nineven 32bdc4ba9e 优化代码 3 years ago
nineven fdd0abb07e 增加添加开发者账号检测功能 3 years ago
nineven a580dddd83 优化下载页 3 years ago
nineven 2f32b71b2b 优化下载页 3 years ago
MMXX 03a6202ee2 优化后台管理api 3 years ago
MMXX 7e15211fd2 清理签名临时数据 3 years ago
MMXX ae58a71ee4 修复签名异常导致死锁问题 3 years ago
MMXX b270d8e5c6 修复域名配置异常问题 3 years ago
MMXX 32ca967d07 优化代码,修改cors逻辑 3 years ago
MMXX 878183dbce 修改日志存储时间 3 years ago
MMXX bebc8417eb 优化应用域名展示 3 years ago
MMXX de511bf90c 状态展示优化 3 years ago
MMXX a8c1df58ee 优化代码,增加设备删除控制,增加设备被封状态 3 years ago
MMXX ef335e118a 优化代码 3 years ago
MMXX 03f66df7b9 优化代码,修复多个开发者,下载不是最新签名包问题,增加批量修改开发者状态 3 years ago
MMXX 4d4ee64a65 优化显示异常提示 3 years ago
MMXX 04ccfe77a3 优化代码,增加苹果账户备注查询功能 3 years ago
MMXX d085d86a26 苹果超级签名优化 3 years ago
MMXX 8a60be6f4f 优化代码 3 years ago
MMXX 869d5ee7bb 优化代码 3 years ago
MMXX 9572382a9e 优化代码 3 years ago
MMXX 7ad5bc8161 优化签名代码逻辑 3 years ago
MMXX 59d7d905d3 优化代码 3 years ago
MMXX c361d740bf 优化代码 3 years ago
MMXX 17c1f0612a 优化缓存代码 3 years ago
MMXX 2b5325ff32 优化缓存代码 3 years ago
MMXX 09257733f8 更新苹果api接口 3 years ago
MMXX 5dd1dd0681 后端更新下载码逻辑 3 years ago
MMXX 979b6f0386 更新前端页面 3 years ago
MMXX e8e6ea962a 修复已知问题 3 years ago
MMXX c50005e7c7 修复激活问题 3 years ago
MMXX 465aea9744 修复页面udid展示不全问题 3 years ago
MMXX 7c2a29b86d 优化代码 3 years ago
MMXX f8418f6414 增加签名应用限额 3 years ago
youngS def6c9dfc8 优化代码 3 years ago
youngS 205505cb0e 优化超级签 获取开发者账户逻辑,增加微信删除必须输入密码限制 3 years ago
youngS 106ae636a2 优化代码 3 years ago
youngS 2e96183c06 修复展示问题 3 years ago
youngS 216bc367c4 优化代码 3 years ago
youngS e559267de3 增加苹果开发者备注显示信息 3 years ago
youngS f04c4411c7 修复已知问题,修正 专属账户,其他应用禁止使用 3 years ago
youngS a60a7d6011 优化代码 3 years ago
youngS d3fc7327a6 增加应用专属开发者功能 3 years ago
youngS 4decc14bf8 优化代码 3 years ago
nineven d657c709f6 优化代码 3 years ago
nineven 73d783b97d 优化代码 3 years ago
nineven b18dfa7a14 优化代码 3 years ago
youngS 0d26e5ac7b 优化代码 3 years ago
youngS bda9cf994d 优化代码 3 years ago
youngS 9d0d36ca63 优化api请求时间 3 years ago
youngS 26e6efeaf4 优化代码,增加苹果api访问代理 3 years ago
youngS 49c35275b6 修复设备消耗查询问题 3 years ago
youngS 92ba4fc258 增加下载码展示 3 years ago
youngS c8a69acb84 增加下载码展示 3 years ago
youngS 7932dde3b4 修复bug 3 years ago
youngS 55f5f65e15 修复已知问题,增加设备消化统计 3 years ago
youngS fd43dcfea4 优化代码 3 years ago
youngS 4c41efa46f 修改 查询开发者策略 按照最小注册设备数进行查找 3 years ago
youngS d3e0c53b8f 修复数据存储异常问题 3 years ago
youngS bd77da66b7 修复自定义id名 3 years ago
youngS ebed9b1aa4 修复只有一个证书激活失败的问题 3 years ago
youngS 0c5c4a6ce7 修复异常问题 3 years ago
youngS b3899d4a97 修复异常问题 3 years ago
youngS 27b8a21556 数据库优化 3 years ago
youngS bbf9664fc7 修复异常问题 3 years ago
youngS 86467ef2fd 优化展示 3 years ago
youngS 2aedb458ee 优化前端展示 3 years ago
youngS 2e491db16c 代码bug修复 3 years ago
youngS 8a59b8d329 代码bug修复 3 years ago
youngS fce8a09d5e 优化代码 3 years ago
fxC 29328d293b 修复mac环境下启动服务失败 3 years ago
fxC 2ec7fb05eb 修复已知问题 3 years ago
youngS 341cf21367 签名优化 3 years ago
youngS 6412e36d0b 优化代码 3 years ago
youngS 8bf925e9df 优化代码 3 years ago
youngS d550d20998 管理后台增加超级签充值功能,前端页面增加udid展示 3 years ago
youngS c170086609 签名余额限制,开发者账户加锁,修复账单数量统计错误问题 3 years ago
youngS bd8c8d00cc 超级签名并发优化,签名余额还未限制,下个版本限制 3 years ago
youngS a2c5d1bd3f 超级签名并发优化,签名余额还未限制,下个版本限制 3 years ago
youngS 169347ca16 下载页举报按钮优化 3 years ago
youngS 372455bcb3 下载页举报按钮优化 3 years ago
youngS d943b5527d 修复已知问题 3 years ago
youngS 5316ed83c1 优化日志保存记录 3 years ago
youngS c173262a0b 修复微信绑定bug 3 years ago
fxC 5be7115e91 兼容老API接口 3 years ago
youngS f30aafb5d9 优化代码 3 years ago
fxC 34971b792a fix some bug 3 years ago
youngS f49c067015 前端代码格式化 3 years ago
youngS 002c90d5e3 优化代码,修改已知问题 3 years ago
youngS 4c29d32d67 下载页面增加举报功能 3 years ago
youngS 819c9a7017 管理后台增加举报信息 3 years ago
youngS 479abff102 下载页面增加举报功能 3 years ago
youngS 20ac8eadda 优化代码 3 years ago
youngS 1cc05645ec 优化代码 3 years ago
youngS 2ee4352544 证书导出功能优化 3 years ago
youngS 7702fa5209 开发者keyid和存储secert加密存储处理 3 years ago
youngS 50da6044a4 增加设备消化账单 3 years ago
youngS 7d781de388 优化代码 3 years ago
youngS ab52639caf 优化代码 3 years ago
youngS 5232ba5fe6 优化代码 3 years ago
youngS 47861511fc 优化单用户签名流程 3 years ago
fxC 0a20226dd7 reformat code and import 3 years ago
fxC cb4fc0b827 no message 3 years ago
youngS 674bf66827 共有池 3 years ago
fxC abd2db398d 一些优化 3 years ago
youngS 9d3b5c582e 共有池 3 years ago
youngS abbe709d27 共有池 3 years ago
fxC b3637c6844 签名共有池实现 3 years ago
youngS 9b7590146a 优化下载页跳转 3 years ago
youngS 7309eaf9b5 修复绑定异常问题 3 years ago
youngS edd603b8e9 下载域名权限问题 3 years ago
youngS 9b69021337 优化cli.py 上传脚本 3 years ago
youngS e7d4df5941 修复新应用短连接重复异常上传问题 3 years ago
youngS b772d2921e 优化代码下载 3 years ago
youngS dd5d0ed0ed 优化代码下载 3 years ago
youngS dc2fa091cb 增加自定义广告功能 3 years ago
youngS d34073301b 增加自定义广告功能 3 years ago
youngS fac7eccc05 优化代码下载 3 years ago
youngS 4533e04850 修复域名下载问题 3 years ago
youngS 0e2578738f 优化docker部署 3 years ago
youngS 08c6b7b87f 优化代码 3 years ago
youngS 7c5ab20dd9 优化uwsgi 启动服务 3 years ago
youngS 1a9cae8cb7 优化uwsgi 启动服务 3 years ago
youngS e34ca43cfc 签名逻辑优化 3 years ago
youngS 75d5a5df76 增加状态描述文件下载状态 3 years ago
youngS ac8614b54c 服务启动方式被manage.py托管 3 years ago
youngS 1e1385da84 服务启动方式被manage.py托管 3 years ago
youngS 0bac3e55f4 优化微信消息推送模板,管理后台增加微信绑定功能 3 years ago
youngS 820e1df84e 优化代码 3 years ago
youngS 08ffa72eab 管理后台增加应用类型展示 3 years ago
youngS 6861c65b83 管理后台增加应用原包下载功能 3 years ago
youngS 439db9788a 管理后台增加应用原包下载功能 3 years ago
youngS 2feda8800c 管理后台增加域名功能 3 years ago
youngS b10a40d177 下载域名统一管理 3 years ago
youngS 3325814c03 增加域名访问权限控制,用户绑定的域名只能允许本用户应用访问 3 years ago
youngS f6918011ca 增加域名下载码绑定功能,权限大于1的可以使用该功能 3 years ago
youngS d7a31c62b3 优化代码 3 years ago
youngS d154381895 优化代码,增加cli.py 上传进度条,修复签名相关问题 3 years ago
youngS 13a67be0ff 修复超级签名,老包未及时清理问题 3 years ago
youngS b815950f7d 优化代码 3 years ago
youngS 4316398d82 修复存储迁移失败的问题 3 years ago
youngS 749d72b513 优化代码展示 3 years ago
youngS bfb3738d69 优化代码,修复新用户注册一直绑定同一个系统域名问题 3 years ago
youngS 3c80635d5f 增加微信登录开启关闭配置 3 years ago
youngS ed05083bbe 修改异常bug 3 years ago
youngS 31677fb1fd 优化代码 3 years ago
youngS cf1267deb2 增加用户名控制登录 3 years ago
youngS 5988e0e1a9 描述文件bug 3 years ago
youngS 81f1f75fb1 描述文件bug 3 years ago
youngS 08e7eb3476 优化代码 3 years ago
youngS 0148905071 优化获取token逻辑 3 years ago
youngS bab6e083bf 优化代码 3 years ago
youngS adaf7af866 优化代码,增加查询udid的服务器逻辑 3 years ago
youngS e8931df00f 优化描述文件签名逻辑,修复清理临时文件bug 3 years ago
youngS 1dc6e84ba7 下载页面增加udid展示 3 years ago
youngS cac1703957 优化开发者账户同步逻辑 3 years ago
youngS dfcf878416 优化代码,修复bug 3 years ago
youngS 45978aae73 修复设备id不存在返回异常 3 years ago
youngS e84f47170a 修改bug 3 years ago
youngS 69c699aebb 修改只能注册两个用户bug 3 years ago
youngS 19709c7da3 优化获取微信token代码 3 years ago
youngS dc8de286c5 优化获取微信token代码 3 years ago
youngS 9b8412304f 增加微信公众号绑定扫码登录功能 3 years ago
youngS d6ce64c3b6 优化苹果开发者激活检测方式 3 years ago
youngS 4f0c8a302c 优化下载次数统计代码 3 years ago
youngS 3ade1efadb 优化页面展示 3 years ago
youngS 90dceee1c5 修复开发者账户认证键值异常 3 years ago
youngS d460da0024 优化重置密码逻辑 3 years ago
youngS 10297e1616 修复清理临时文件失败的问题 3 years ago
youngS 37df9f4a5d 支持短信找回密码功能 3 years ago
youngS a8e8402eff 修复本地文件连接异常问题 3 years ago
youngS dd218fe745 优化上传失败 3 years ago
youngS 42cb1bfc88 增加js版本展示 3 years ago
youngS d8b02a74ab 优化阿里云oss上传失败重试 3 years ago
youngS 7599e5f3a2 代码优化 3 years ago
youngS 4501b5dc2a 前端页面增加应用上传速度展示 3 years ago
youngS 58e7f8fa6a 实名认证照片存放到服务器默认存储 3 years ago
youngS 436e26af37 前端页面增加批量应用上传功能 3 years ago
youngS cb3738f4de 管理后台 存储和超级签权限修改 3 years ago
youngS aa83de23b5 优化代码 3 years ago
youngS 5169c8a477 超级签自定义名字不能为空 3 years ago
youngS 83509a79de 增加容器部署 3 years ago
youngS 9abdc5f5ae 下载页展示优化 3 years ago
youngS a95a2fb22f 修复超级签密码访问未校验问题,增加post接收速率限制 3 years ago
youngS 1bd0f08a4b 优化代码 3 years ago
youngS cdf778d3f3 苹果开发者过期时间限制 3 years ago
youngS bb5d5829df 优化代码 3 years ago
youngS a5a79867f1 cdn NGINX获取真实ip 3 years ago
youngS 8c1ca01532 优化代码 3 years ago
youngS 4e3af4de35 修复解除合并,未清理cache问题 3 years ago
youngS 586136729a 优化下载页展示 3 years ago
youngS 3c4e5aa6e3 修改下载页面,第三方应用不自动跳转问题 3 years ago
youngS cad1a77f8f 增加服务操作脚本 3 years ago
youngS f7f0896893 优化下载页展示 3 years ago
youngS f3349ecc5b 修复当miniOSversion不存在引发的异常 3 years ago
youngS 7fba632390 修复当miniOSversion不存在引发的异常 3 years ago
youngS aa33e5640b 优化代码 3 years ago
youngS 45d813cca3 优化代码 3 years ago
youngS 95b4b45da8 修复开发者不存在导致签名失败bug 3 years ago
youngS 00032239b0 优化证书检测 3 years ago
youngS 86d5623437 修改p12 证书导入逻辑 3 years ago
youngS d4e4e9b278 修改p12 证书导入逻辑 3 years ago
youngS a6f0cb0174 增加支持p12 证书导入 3 years ago
youngS b40f93a16c 增加支持p12 证书导入 3 years ago
youngS e6f8b2f628 修改超级签方式,isign 部分包无法签名,使用zsign 进行签名 3 years ago
youngS ed841bc8fb 优化实名认证逻辑 4 years ago
youngS a59958e1af 管理后台增加超级签开发展示 4 years ago
youngS 5835b4d6e1 优化代码 4 years ago
youngS 5bac330551 优化代码 4 years ago
youngS 1cc239698b 优化代码,增加超级签密钥证书获取教程 4 years ago
youngS 56cd35565f 超级签默认启动 4 years ago
youngS 30db4882b4 修改前端页面 4 years ago
  1. 5
      .gitignore
  2. 207
      README.md
  3. 102
      doc/docker.md
  4. BIN
      doc/images/img_1.png
  5. BIN
      doc/images/img_10.png
  6. BIN
      doc/images/img_11.png
  7. BIN
      doc/images/img_2.png
  8. BIN
      doc/images/img_3.png
  9. BIN
      doc/images/img_4.png
  10. BIN
      doc/images/img_5.png
  11. BIN
      doc/images/img_6.png
  12. BIN
      doc/images/img_7.png
  13. BIN
      doc/images/img_8.png
  14. BIN
      doc/images/img_9.png
  15. 166
      doc/local.md
  16. 67
      docker/build/docker-compose.yml
  17. 31
      docker/flyapps/docker-compose.only.yml
  18. 48
      docker/flyapps/docker-compose.yml
  19. 7
      docker/init/clean.sh
  20. 10
      docker/init/download.images
  21. 24
      docker/init/init.sh
  22. 1
      docker/init/timezone
  23. 43
      docker/mariadb/docker-compose.yml
  24. 108
      docker/mariadb/server.cnf
  25. 25
      docker/redis/docker-compose.yml
  26. 6
      docker/scripts/build.sh
  27. 9
      docker/scripts/start_all.sh
  28. 10
      docker/scripts/stop_all.sh
  29. 3
      fir_admin/.env.development
  30. 2
      fir_admin/.env.production
  31. 40
      fir_admin/src/api/app.js
  32. 54
      fir_admin/src/api/developer.js
  33. 10
      fir_admin/src/api/devices.js
  34. 31
      fir_admin/src/api/domain.js
  35. 26
      fir_admin/src/api/order.js
  36. 31
      fir_admin/src/api/report.js
  37. 13
      fir_admin/src/api/storage.js
  38. 8
      fir_admin/src/api/user.js
  39. 31
      fir_admin/src/api/wxbind.js
  40. 153
      fir_admin/src/router/index.js
  41. 2
      fir_admin/src/store/modules/user.js
  42. 15
      fir_admin/src/views/appinfos/AppDetail.vue
  43. 20
      fir_admin/src/views/appinfos/AppReleaseDetail.vue
  44. 41
      fir_admin/src/views/appinfos/AppReleaseList.vue
  45. 48
      fir_admin/src/views/appinfos/list.vue
  46. 4
      fir_admin/src/views/authentication/list.vue
  47. 146
      fir_admin/src/views/domain/Detail.vue
  48. 188
      fir_admin/src/views/domain/list.vue
  49. 2
      fir_admin/src/views/login/index.vue
  50. 15
      fir_admin/src/views/order/Detail.vue
  51. 38
      fir_admin/src/views/order/list.vue
  52. 158
      fir_admin/src/views/report/Detail.vue
  53. 227
      fir_admin/src/views/report/list.vue
  54. 15
      fir_admin/src/views/storage/Detail.vue
  55. 29
      fir_admin/src/views/storage/list.vue
  56. 183
      fir_admin/src/views/supersign/Detail.vue
  57. 117
      fir_admin/src/views/supersign/bill/add.vue
  58. 202
      fir_admin/src/views/supersign/bill/list.vue
  59. 193
      fir_admin/src/views/supersign/developer/Detail.vue
  60. 218
      fir_admin/src/views/supersign/developer/list.vue
  61. 98
      fir_admin/src/views/supersign/devices/list.vue
  62. 3
      fir_admin/src/views/supersign/index.vue
  63. 127
      fir_admin/src/views/userinfos/UserDetail.vue
  64. 36
      fir_admin/src/views/userinfos/list.vue
  65. 157
      fir_admin/src/views/wxbind/Detail.vue
  66. 192
      fir_admin/src/views/wxbind/list.vue
  67. 48
      fir_admin/yarn.lock
  68. 47
      fir_client/README.md
  69. 11281
      fir_client/package-lock.json
  70. 4
      fir_client/package.json
  71. 16
      fir_client/public/config.js
  72. BIN
      fir_client/public/favicon.ico
  73. 7
      fir_client/public/index.html
  74. 8
      fir_client/public/short.html
  75. 154
      fir_client/src/App.vue
  76. 128
      fir_client/src/Download.vue
  77. BIN
      fir_client/src/assets/github.png
  78. BIN
      fir_client/src/assets/imgs/circle.png
  79. BIN
      fir_client/src/assets/sign/help/sign_help_0.png
  80. BIN
      fir_client/src/assets/sign/help/sign_help_1.png
  81. BIN
      fir_client/src/assets/sign/help/sign_help_2.png
  82. BIN
      fir_client/src/assets/sign/help/sign_help_3.png
  83. BIN
      fir_client/src/assets/sign/help/sign_help_4.png
  84. BIN
      fir_client/src/assets/sign/help/sign_help_5.png
  85. 227
      fir_client/src/components/FirBase.vue
  86. 3322
      fir_client/src/components/FirDownload.vue
  87. 87
      fir_client/src/components/FirFooter.vue
  88. 364
      fir_client/src/components/FirHeader.vue
  89. 760
      fir_client/src/components/FirLogin.vue
  90. 755
      fir_client/src/components/FirRegist.vue
  91. 473
      fir_client/src/components/FirResetPwd.vue
  92. 3451
      fir_client/src/components/ShortDownload.vue
  93. 756
      fir_client/src/components/apps/FirAppInfosBase.vue
  94. 528
      fir_client/src/components/apps/FirAppInfosbaseinfo.vue
  95. 696
      fir_client/src/components/apps/FirAppInfoscombo.vue
  96. 96
      fir_client/src/components/apps/FirAppInfosdevices.vue
  97. 1022
      fir_client/src/components/apps/FirAppInfossecurity.vue
  98. 485
      fir_client/src/components/apps/FirAppInfossupersign.vue
  99. 916
      fir_client/src/components/apps/FirAppInfostimeline.vue
  100. 3190
      fir_client/src/components/apps/FirApps.vue
  101. Some files were not shown because too many files have changed in this diff Show More

5
.gitignore vendored

@ -9,3 +9,8 @@ fir_client/yarn.lock
fir_download/package-lock.json
fir_client/src/assets/down_left.png
fir_client/src/assets/down_right.png
mailhtml/build/
mailhtml/node_modules/
mailhtml/.idea/
fir_client/dist_index/
fir_client/dist_short/

@ -1,144 +1,63 @@
##### 从git上面下载源码
```
cd /data/
git clone https://github.com/nineaiyu/FlyApps
```
#### 超级签名依赖部署
##### fastlane [相关文档](https://github.com/fastlane/fastlane/blob/master/spaceship/docs/DeveloperPortal.md)
```
# fastlane
#1 gem ruby安装
yum install gem -y
yum install ruby-devel -y
yum install gcc gcc-c++ -y
#gem修改为国内镜像
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/
gem sources -l
#https://gems.ruby-china.com
#确保只有 gems.ruby-china.com
#2 更新rubby
gpg2 --keyserver hkp://pool.sks-keyservers.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
#如果脚本执行失败,可以这样
# echo 199.232.68.133 raw.githubusercontent.com >> /etc/hosts
curl -L get.rvm.io | bash -s stable
source /etc/profile.d/rvm.sh
rvm list known
rvm install 2.7
#3 安装fastlane
gem install fastlane
gem install pry
# space 好像有点问题,需要把这个注释掉
vim /usr/local/rvm/gems/ruby-2.7.0/gems/fastlane-2.146.1/spaceship/lib/spaceship/base.rb +298
#v.gsub!("\n", "\n\t") # to align nested elements #注释掉,要不然会报错
```
##### isign
```
# python2.7
yum update python -y
pip install -U setuptools pip
wget https://github.com/nineaiyu/isign/archive/v1.8.0.tar.gz
tar xvf v1.8.0.tar.gz
cd isign-1.8.0/
python setup.py build
python setup.py install
```
### FlyApp 服务器搭建 [python3 环境]
##### 搭建python env 环境
```
yum install python36 python36-devel redis mariadb-server mariadb-devel -y
python3 -m venv py3
source py3/bin/activate
```
###### 安装pip包
```
cd FlyApps/fir_ser/
pip install -r requirements.txt
```
###### 配置数据库
```
在 settings.py 配置redis 和 mysql 数据库
```
###### 迁移数据库
```
#如果是mysql,需要做一下配置,如果是sqlite,需要升级sqlite
#记得根据配置创建数据库
#create database flyapp default character set utf8 COLLATE utf8_general_ci;
#grant all on flyapp.* to flyuser@'127.0.0.1' identified by 'flypwd00oo.1';
python manage.py makemigrations
python manage.py migrate
```
### FlyApp web端搭建
###### npm编译环境
```
yum install npm
npm install -g n
n latest
npm install -g yarn
```
###### 编译web端
```
cd FlyApps/fir_client/
vim src/restful/index.js #修改api接口地址
yarn install
yarn build
```
###### 编译下载页面
```
cd FlyApps/fir_download/
vim src/restful/download.js #修改api接口地址
yarn install
yarn build
```
###### web目录操作
```
# web目录: /www/wwwroot/fly.dvcloud.xin/
cd /www/wwwroot/fly.dvcloud.xin/
cp -a /data/FlyApps/fir_client/dist/* .
cp -a /data/FlyApps/fir_download/dist/* .
```
##### 更新sqllite
```
tar xvf sqlite-autoconf-3310100.tar.gz
cd sqlite-autoconf-3310100
./configure --prefix=/usr/local/sqlite
make -j4
make install
rm -rf /usr/bin/sqlite3
ln -s /usr/local/sqlite/bin/sqlite3 /usr/bin/sqlite3
ll /usr/bin/sqlite3
echo "/usr/local/sqlite/lib" > /etc/ld.so.conf.d/sqlite3.conf
ldconfig
```
### 用与应用分发,苹果超级签名
#### 部署前准备
- 备案域名【至少需要一个域名,以下可通过子域名部署】
- API域名
- 前端web域名
- 下载页域名
- 下载页域名可配置多个
- 存储域名(使用阿里云oss存储)
- ssl证书
- API域名证书
- 存储域名证书(使用阿里云oss存储)
- 前端web域名证书(可选)
- Centos8Stream 服务器
- 如果使用oss存储,则带宽为1M,若使用本地存储,则带宽越大越好
- 如果使用超级签,最低配置为2cpu 4G内存,若干不使用签名,则1cpu2G就行
- 阿里云短信或极光短信服务【可选一个,主要用与注册,重置密码】
- 阿里云短信
- 极光短信
- 邮箱服务【可选,用与注册,重置密码,通知信息】
- 阿里云OSS存储【可选】
- [sts授权配置](https://help.aliyun.com/document_detail/100624.html)
- 阿里云CDN【可选,用与加速访问】
- 极验验证【可选,滑动验证服务】
- 微信公众号【可选,用与微信扫描登录】
- 阿里云支付【可选,用与购买下载次数】
- 微信支付【可选,用与购买下载次数】
#### 自用搭建建议
- 阿里云服务器需要1cpu 2G内存,无需系统盘,如果使用超级签,可以适当增加配置
- 需要阿里云OSS存储和阿里云CDN,并且OSS存储和阿里云服务器部署同一个地区
- 可以申请一个极验进行滑动验证,或者开启验证码验证
- 阿里云备案域名:api和前端可以使用一个域名,下载页单独域名
#### 部署必备资料
- 域名证书
- web域名和证书
- api域名和证书
- 下载页域名(可配置证书)
- 存储域名和证书
- 本地存储,则该域名和证书可以和api域名证书一致
- 阿里云oss存储
- 开启cdn,需要新域名和证书
- 不开启,无需域名和证书
- Centos8Stream 服务器
## 部署方式
### 1.[Docker 部署](./doc/docker.md) 【推荐】
### 2.[本地部署](./doc/local.md)
### 功能预览
![img_1.png](./doc/images/img_1.png)
![img_2.png](./doc/images/img_2.png)
![img_3.png](./doc/images/img_3.png)
![img_4.png](./doc/images/img_4.png)
![img_5.png](./doc/images/img_5.png)
![img_6.png](./doc/images/img_6.png)
![img_7.png](./doc/images/img_7.png)
![img_8.png](./doc/images/img_8.png)
![img_9.png](./doc/images/img_9.png)
![img_10.png](./doc/images/img_10.png)
![img_11.png](./doc/images/img_11.png)

@ -0,0 +1,102 @@
### Docker最小化部署前准备,如果使用超级签和云存储,需要使用阿里云的服务器
系统|Centos 8 Stream 纯净系统 [2cpu 4G内存]
----|----
域名|```app.hehelucky.cn```
域名SSL证书| nginx 格式证书 [阿里云和腾讯云都可以申请免费ssl证书]
数据路径| ```/data```
### 开始部署
##### 1.从git上面下载源码
```
cd /data/
dnf install git -y
git clone https://github.com/nineaiyu/flyapps
```
## 注意,下面展示的相对路径,都是以```/data/flyapps```为相对目录
#### 2.docker环境安装
```
cd /data/flyapps/docker/init
sh init.sh
```
#### 3.配置域名和证书,如果有cdn或者oss,也要进行配置
a.将域名证书通过```sftp软件```或者```rz命令```复制到```/data/flyapps/nginx.conf.d```目录中
b.将域名证书分别重命名为 ```域名.pem``` 和 ```域名.key``` .本次使用的域名是 ```app.hehelucky.cn``` ,因此,证书名称类似如下
```shell script
nginx.conf.d/app.hehelucky.cn.key
nginx.conf.d/app.hehelucky.cn.pem
```
c.修改NGINX配置文件```nginx.conf.d/flyapps-vhost.conf```,将```server_name```字段修改为域名,将```ssl_certificate```和
```ssl_certificate_key```修改为对应证书路径,修改之后,对应字段如下
```shell
server_name app.hehelucky.cn;
ssl_certificate /etc/nginx/conf.d/app.hehelucky.cn.pem;
ssl_certificate_key /etc/nginx/conf.d/app.hehelucky.cn.key;
```
#### 4.配置api服务需要修改api和web域名,如果有需求,还可以配置 短信,邮箱,geetest,存储等信息
##### fir_ser配置文件 ```fir_ser/config.py```
```python
class DOMAINCONF(object):
API_DOMAIN = "https://app.hehelucky.cn" # 用与开启本地存储,上传应用配置
WEB_DOMAIN = "https://app.hehelucky.cn" # 用于超级签跳转配置,该域名一般为前端页面域名
```
##### fir_client配置文件 ```fir_client/vue.confjg.js```
```javascript
const pro_base_env = {
baseUrl: '/', //该选项可以填写web-api的域名,类似 https://api.xxx.com/
index_static: '/', //若配置cdn等加速,可以填写cdn加速域名
baseShortUrl: '/', //该选项可以填写short-api的域名,也可以和web-api域名一样,类似 https://api.xxx.com/
short_static: '/short/', //若配置cdn等加速,可以填写cdn加速域名
version: version,
};
```
##### 构建静态资源
```
cd /data/flyapps/docker/scripts
sh build.sh
```
##### 启动所有服务
```
cd /data/flyapps/docker/scripts
sh start_all.sh
```
##### 关闭所有服务
```
cd /data/flyapps/docker/scripts
sh stop_all.sh
```
##### 根据提示创建默认管理用户,用与访问管理后台
```shell
docker exec -it flyapps python manage.py createsuperuser
```
##### 测试访问
- 在浏览器输入自己配置的域名 https://app.hehelucky.cn/ 进行访问
- 管理后台访问 https://app.hehelucky.cn:3448/ 进行访问
##### 如果要使用本地的数据库和redis,api服务使用容器,需要修改文件```/data/flyapps/docker/flyapps/docker-compose.yml```
```shell
external_links:
- mariadb:mariadb
- redis:redis
```
修改为
```shell
extra_hosts:
- "mariadb:172.31.31.1"
- "redis:172.31.31.1"
```
并将```nginx:```下面的配置注释掉
同时还需要修改mariadb和redis授权

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

@ -0,0 +1,166 @@
### 本地部署前准备
系统 | Centos 8 Stream [2cpu 4G内存]
----|----
域名|```app.hehelucky.cn```
域名SSL证书| nginx 格式证书 [阿里云和腾讯云都可以申请免费ssl证书]
数据路径| ```/data```
##### 从git上面下载源码
```shell
cd /data/
dnf install git gcc zip unzip -y
git clone https://github.com/nineaiyu/flyapps
```
### flyapps 本地部署 [python3 环境]
##### 搭建python env 环境
```shell
dnf install python39 python39-devel redis mariadb-server mariadb-devel -y
python3.9 -m venv py39
source py39/bin/activate
```
###### 安装pip包
```shell
cd /data/flyapps/fir_ser/
pip install -U setuptools pip -i https://pypi.tuna.tsinghua.edu.cn/simple/
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple/
```
#### 配置数据库
##### 启动mariadb数据库
```shell
systemctl restart mariadb
systemctl enable mariadb
```
##### 创建mysql数据库
```shell
mysql
```
执行下面mysql命令
```mariadb
create database flyapps default character set utf8 COLLATE utf8_general_ci;
grant all on flyapps.* to flyuser@'127.0.0.1' identified by 'KGzKjZpWBp4R4RSa';
```
##### 启动redis数据库
```shell
echo 'requirepass nineven' >> /etc/redis.conf # 配置授权密码
systemctl restart redis
systemctl enable redis
```
### flyapps zsign ipa重签名工具安装
```shell
dnf install openssl-devel -y
dnf install gcc-c++ gcc -y
cd /data/flyapps/fir_ser/
tar xvf zsign-1.1.2.tar.gz
cd zsign-1.1.2/
g++ *.cpp common/*.cpp -lcrypto -O3 -std=c++11 -o /usr/bin/zsign
```
#### 配置api服务需要修改api和web域名,如果有需求,还可以配置 短信,邮箱,geetest,存储等信息
##### fir_ser配置文件 ```fir_ser/config.py```
```python
class DOMAINCONF(object):
API_DOMAIN = "https://app.hehelucky.cn" # 用与开启本地存储,上传应用配置
WEB_DOMAIN = "https://app.hehelucky.cn" # 用于超级签跳转配置,该域名一般为前端页面域名
```
##### fir_client配置文件 ```fir_client/vue.confjg.js```
```javascript
const pro_base_env = {
baseUrl: '/', //该选项可以填写web-api的域名,类似 https://api.xxx.com/
index_static: '/', //若配置cdn等加速,可以填写cdn加速域名
baseShortUrl: '/', //该选项可以填写short-api的域名,也可以和web-api域名一样,类似 https://api.xxx.com/
short_static: '/short/', //若配置cdn等加速,可以填写cdn加速域名
version: version,
};
```
### flyapps web端搭建
###### npm编译环境
```shell
dnf install npm -y
npm install -g n
n 12.13 # 安装12版本的node或者14版本 ,最新版本会有问题
npm install -g yarn
```
###### 编译web端和下载页
```shell
cd /data/flyapps/fir_client/
vim vue.config.js #修改api接口地址
# pro_base_env 正式环境信息
# dev_base_env 开发环境信息
yarn install
yarn build index # 前端打包,输出目录 dist_index
yarn build short # 下载页打包,输出目录 dist_short
```
###### 编译web管理后台
```shell
cd /data/flyapps/fir_admin/
yarn install
yarn build:prod # 下载页打包,输出目录 dist
```
### nginx配置
##### 安装nginx
```shell
dnf install nginx -y
```
##### 将域名ssl证书解压到```/etc/nginx/conf.d/``` 目录中,并重命名为```域名.pem```,```域名.key```格式
##### 拷贝虚拟主机配置文件
```shell
cp /data/flyapps/nginx.conf.d/flyapps-vhost.conf /etc/nginx/conf.d/
```
1.创建web目录,并将打包好的web复制该目录
```shell
mkdir -pv /data/{fir_client/short,fir_admin}
cp -a /data/flyapps/fir_client/dist_index/* /data/fir_client/
cp -a /data/flyapps/fir_client/dist_short/* /data/fir_client/short/
cp -a /data/flyapps/fir_admin/dist/* /data/fir_admin/
chown nginx.nginx -R /data/{fir_client,fir_admin}
```
2.启动nginx服务
```shell
nginx -t
systemctl restart nginx
systemctl enable nginx
```
##### 添加redis和mariadb uwsgi本地解析
```shell
echo '127.0.0.1 mariadb redis flyapps' >> /etc/hosts
```
###### 迁移数据库
```shell
cd /data/flyapps/fir_ser/
python manage.py makemigrations
python manage.py migrate
```
#### 启动api服务
```shell
echo 'net.core.somaxconn=1024' >> /etc/sysctl.conf
sysctl -p
cd /data/flyapps/fir_ser/
python manage.py start all -u nginx -usm 1 -d
```
##### 根据提示创建默认管理用户,用与访问管理后台
```shell
python manage.py createsuperuser
```
- 需要输入用户名,邮箱和密码
- 用户名和密码用与登录管理后台
- 邮箱和密码用与登录前端web

@ -0,0 +1,67 @@
version: '3.2'
services:
buildflyapps:
image: 'flyapps'
build:
context: ../../fir_ser
dockerfile: Dockerfile
command:
- echo
- success
buildclient:
container_name: buildclient
image: 'node:14.17.3'
working_dir: /fir_client
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_client:/fir_client
- ../../data/web/:/web
command:
- bash
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build index && yarn build short && ([ ! -e /web/fir_client ] && \cp -a dist_index /web/fir_client || \cp -a dist_index/* /web/fir_client/) && ([ ! -e /web/fir_client/short ] && \cp -a dist_short /web/fir_client/short || \cp -a dist_short/* /web/fir_client/short/) && echo build success
buildindex:
container_name: buildindex
image: 'node:14.17.3'
working_dir: /fir_client
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_client:/fir_client
- ../../data/web/:/web
command:
- bash
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build index && ([ ! -e /web/fir_index ] && \cp -a dist_index /web/fir_index || \cp -a dist_index/* /web/fir_index/) && echo build success
buildshort:
container_name: buildshort
image: 'node:14.17.3'
working_dir: /fir_client
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_client:/fir_client
- ../../data/web/:/web
command:
- sh
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build short && ([ ! -e /web/fir_short ] && \cp -a dist_short /web/fir_short || \cp -a dist_short/* /web/fir_short/) && echo build success
buildadmin:
container_name: buildadmin
image: 'node:14.17.3'
working_dir: /fir_admin
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_admin:/fir_admin
- ../../data/web/:/web
command:
- sh
- -c
- yarn config set registry https://registry.npm.taobao.org && yarn install && yarn build:prod && ([ ! -e /web/fir_admin ] && \cp -a dist /web/fir_admin || \cp -a dist/* /web/fir_admin/) && echo build success

@ -0,0 +1,31 @@
version: '3'
networks:
flyapps:
external: true
name: flyapps
services:
flyapps:
container_name: flyapps
restart: always
image: 'nineven/flyapps'
working_dir: /data/fir_ser
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_ser:/data/fir_ser
- ../../data/flyapps/files:/data/fir_ser/files
- ../../data/logs/flyapps/:/data/fir_ser/logs
- ../../data/flyapps/supersign/:/data/fir_ser/supersign
- ../../nginx.conf.d:/data/cert
networks:
flyapps:
ipv4_address: 172.31.31.100
external_links:
- mariadb:mariadb
- redis:redis
sysctls:
- net.core.somaxconn=4096
extra_hosts:
- "mariadb:172.31.31.1"
- "redis:172.31.31.1"

@ -0,0 +1,48 @@
version: '3'
networks:
flyapps:
external: true
name: flyapps
services:
flyapps:
container_name: flyapps
restart: always
image: 'nineven/flyapps'
working_dir: /data/fir_ser
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../fir_ser:/data/fir_ser
- ../../data/flyapps/files:/data/fir_ser/files
- ../../data/logs/flyapps/:/data/fir_ser/logs
- ../../data/flyapps/supersign/:/data/fir_ser/supersign
- ../../nginx.conf.d:/data/cert
networks:
flyapps:
ipv4_address: 172.31.31.100
external_links:
- mariadb:mariadb
- redis:redis
sysctls:
- net.core.somaxconn=4096
nginx:
container_name: nginx
restart: always
image: 'nginx:1.21.3'
volumes:
- /etc/localtime:/etc/localtime:ro
- ../../data/web:/data
- ../../nginx.conf.d:/etc/nginx/conf.d
- ../../data/logs/nginx:/var/log/nginx
networks:
flyapps:
ipv4_address: 172.31.31.200
ports:
- 80:80
- 443:443
- 3448:3448
external_links:
- flyapps:flyapps
depends_on:
- flyapps

@ -0,0 +1,7 @@
#!/bin/bash
#
#
for i in nginx flyapps mariadb redis buildclient buildshort buildadmin ;do echo $i;docker rm -f $i;done
docker network rm flyapps

@ -0,0 +1,10 @@
#!/bin/bash
#
#
docker pull 'bitnami/mariadb:10.7.3'
docker pull 'bitnami/redis:6.2.7'
docker pull 'nginx:1.21.3'
docker pull 'nineven/flyapps:latest'
docker pull 'node:14.17.3'

@ -0,0 +1,24 @@
#!/bin/bash
#
#
which dockerd
if [ $? -ne 0 ];then
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
dnf install docker-ce -y
fi
#which docker-compose
#if [ $? -ne 0 ];then
# curl -L https://get.daocloud.io/docker/compose/releases/download/v2.5.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
#fi
data_path="$(dirname $(dirname `pwd`))/data"
mkdir -pv ${data_path}/{flyapps/files,web,mariadb,redis,logs/{mariadb,nginx,flyapps}}
\cp $(dirname $(dirname `pwd`))/fir_ser/files/head_img.jpeg ${data_path}/flyapps/files/
chown 1001.1001 -R ${data_path}/{flyapps,web,mariadb,redis,logs/{mariadb,nginx,flyapps}}
chown 101.101 -R ${data_path}/{flyapps,logs/flyapps}
systemctl start docker && docker network create flyapps --driver bridge --subnet=172.31.31.0/24 --gateway=172.31.31.1
systemctl enable docker
systemctl status docker

@ -0,0 +1 @@
Asia/Shanghai

@ -0,0 +1,43 @@
version: '3.2'
networks:
flyapps:
external: true
name: flyapps
services:
mariadb:
image: bitnami/mariadb:10.7.3
container_name: mariadb
restart: always
environment:
- MARIADB_ROOT_PASSWORD=rootIPD.xx2.19
- MARIADB_DATABASE=flyapps
- MARIADB_USER=flyuser
- MARIADB_PASSWORD=KGzKjZpWBp4R4RSa
#- ALLOW_EMPTY_PASSWORD=yes
- MARIADB_ENABLE_SLOW_QUERY=1
- MARIADB_LONG_QUERY_TIME=3
- MARIADB_SKIP_TEST_DB=yes
- MARIADB_EXTRA_FLAGS=--max-connect-errors=3000 --max_connections=30000
ports:
- 3306:3306
networks:
flyapps:
ipv4_address: 172.31.31.90
volumes:
- ../init/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ../../data/mariadb:/bitnami/mariadb/data
- ../../data/logs/mariadb:/data/logs/mariadb
- ./server.cnf:/opt/bitnami/mariadb/conf/my_custom.cnf:ro
# adminer:
# image: adminer
# ports:
# - 8080:8080
# networks:
# - fungame

@ -0,0 +1,108 @@
#
# These groups are read by MariaDB server.
# Use it for options that only the server (but not clients) should see
#
# See the examples of server my.cnf files in /usr/share/mysql/
#
# this is only for the mysqld standalone daemon
[mysqld]
binlog_cache_size = 192K
thread_stack = 384K
join_buffer_size = 4096K
query_cache_type = 1
max_heap_table_size = 1024M
default_storage_engine = InnoDB
performance_schema_max_table_instances = 400
table_definition_cache = 400
skip-external-locking
key_buffer_size = 512M
max_allowed_packet = 1G
table_open_cache = 1024
sort_buffer_size = 2048K
net_buffer_length = 4K
read_buffer_size = 2048K
read_rnd_buffer_size = 1024K
myisam_sort_buffer_size = 16M
thread_cache_size = 192
query_cache_size = 256M
tmp_table_size = 1024M
sql-mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
max_connections = 30000
max_connect_errors = 1000
open_files_limit = 65535
expire_logs_days = 10
#log_queries_not_using_indexes=on
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
init_connect='SET NAMES utf8mb4'
#character-set-server=utf8
skip_name_resolve
event_scheduler=1
#skip-grant-tables
#innodb_data_home_dir = /var/lib/mysql/
#innodb_data_file_path = ibdata1:10M:autoextend
#innodb_log_group_home_dir = /var/lib/mysql/
#innodb_buffer_pool_size = 1024M
#innodb_log_file_size = 128M
#innodb_log_buffer_size = 32M
#innodb_flush_log_at_trx_commit = 1
#innodb_lock_wait_timeout = 50
#innodb_max_dirty_pages_pct = 90
#innodb_read_io_threads = 4
#innodb_write_io_threads = 4
#
# Allow server to accept connections on all interfaces.
#
#bind-address=0.0.0.0
#
# this is only for embedded server
# This group is only read by MariaDB servers, not by MySQL.
# If you use the same .cnf file for MySQL and MariaDB,
# you can put MariaDB-only options here
[mariadb]
#autoset_open_files_limit
#enable_slow_query_log
#audit
plugin_load_add=server_audit
server_audit_logging=on
server_audit_events=connect,query
server_audit=force_plus_permanent
server_audit_events=QUERY_DDL,QUERY_DML,CONNECT
server_audit_output_type=file
server_audit_file_rotate_now=on
server_audit_file_rotations=9
server_audit_file_rotate_size=1G
server_audit_file_path=/data/logs/mariadb
log_error=/data/logs/mariadb/mariadb.err.log
log_output=FILE
slow_query_log
long_query_time=3
slow_query_log_file=/data/logs/mariadb/mariadb-slow.log
log_queries_not_using_indexes=ON #Logging Queries That Don't Use Indexes
#server_id=2
log-bin=/data/logs/mariadb/mysql-bin
# This group is only read by MariaDB-10.5 servers.
# If you use the same .cnf file for MariaDB of different versions,
# use this group for options that older servers don't understand

@ -0,0 +1,25 @@
version: '3.2'
networks:
flyapps:
external: true
name: flyapps
services:
redis:
image: 'bitnami/redis:6.2.7'
container_name: redis
restart: always
volumes:
- ../init/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
- ../../data/redis:/bitnami/redis/data
environment:
#- REDIS_REPLICATION_MODE=master
#- ALLOW_EMPTY_PASSWORD=yes
- REDIS_PASSWORD=nineven
networks:
- flyapps
#ports:
# - 6379:6379

@ -0,0 +1,6 @@
#!/bin/bash
#
#
cd ../build/
docker compose up buildclient buildadmin

@ -0,0 +1,9 @@
#!/bin/bash
#
#
cd ../mariadb/ && docker compose up -d
cd ../redis/ && docker compose up -d
cd ../flyapps/ && docker compose up -d
docker logs -f flyapps

@ -0,0 +1,10 @@
#!/bin/bash
#
#
cd ../flyapps/ && docker compose down
cd ../redis/ && docker compose down
cd ../mariadb/ && docker compose down

@ -2,4 +2,5 @@
ENV = 'development'
# base api
VUE_APP_BASE_API = 'https://app.hehelucky.cn/api/v3/fir/server'
VUE_APP_BASE_API = 'https://flyapps.cn/api/v3/fir/server'
#VUE_APP_BASE_API = 'https://app.hehelucky.cn/api/v3/fir/server'

@ -2,5 +2,5 @@
ENV = 'production'
# base api
VUE_APP_BASE_API = '/prod-api'
VUE_APP_BASE_API = '/api/v3/fir/server'

@ -1,6 +1,6 @@
import request from '@/utils/request'
export function getAppInfos(query) {
export function getAppList(query) {
return request({
url: '/app/info',
method: 'get',
@ -8,23 +8,29 @@ export function getAppInfos(query) {
})
}
export function updateAppInfo(data) {
export function getAppInfos(pk) {
return request({
url: '/app/info',
url: '/app/info/' + pk,
method: 'get'
})
}
export function updateAppInfo(pk, data) {
return request({
url: '/app/info/' + pk,
method: 'put',
data
})
}
export function deleteApp(data) {
export function deleteApp(pk) {
return request({
url: '/app/info',
method: 'delete',
data
url: '/app/info/' + pk,
method: 'delete'
})
}
export function getAppReleaseInfos(query) {
export function getAppReleaseList(query) {
return request({
url: '/app/release/info',
method: 'get',
@ -32,10 +38,24 @@ export function getAppReleaseInfos(query) {
})
}
export function updateReleaseAppInfo(data) {
export function getAppReleaseInfos(pk) {
return request({
url: '/app/release/info',
url: '/app/release/info/' + pk,
method: 'get'
})
}
export function updateReleaseAppInfo(pk, data) {
return request({
url: '/app/release/info/' + pk,
method: 'put',
data
})
}
export function downloadAppReleaseInfos(data) {
return request({
url: '/app/release/info',
method: 'post',
data
})
}

@ -0,0 +1,54 @@
import request from '@/utils/request'
export function getDeveloperList(query) {
return request({
url: '/developer/info',
method: 'get',
params: query
})
}
export function getDeveloperInfo(pk) {
return request({
url: '/developer/info/' + pk,
method: 'get'
})
}
export function updatedeveloperInfo(pk, data) {
return request({
url: '/developer/info/' + pk,
method: 'put',
data
})
}
export function getBillList(query) {
return request({
url: '/bill/info',
method: 'get',
params: query
})
}
export function addBillInfo(data) {
return request({
url: '/bill/info',
method: 'post',
data
})
}
export function delBillInfo(pk) {
return request({
url: '/bill/info/' + pk,
method: 'delete'
})
}
export function getUserBillInfo(query) {
return request({
url: '/bill/userinfo',
method: 'get',
params: query
})
}

@ -0,0 +1,10 @@
import request from '@/utils/request'
export function getDevicesList(query) {
return request({
url: '/devices/info',
method: 'get',
params: query
})
}

@ -0,0 +1,31 @@
import request from '@/utils/request'
export function getDomainList(query) {
return request({
url: '/domain/info',
method: 'get',
params: query
})
}
export function getDomainInfos(pk) {
return request({
url: '/domain/info/' + pk,
method: 'get'
})
}
export function updateDomainInfo(pk, data) {
return request({
url: '/domain/info/' + pk,
method: 'put',
data
})
}
export function deleteDomain(pk) {
return request({
url: '/domain/info/' + pk,
method: 'delete'
})
}

@ -1,6 +1,6 @@
import request from '@/utils/request'
export function getOrderInfo(query) {
export function getOrderList(query) {
return request({
url: '/order/info',
method: 'get',
@ -8,11 +8,31 @@ export function getOrderInfo(query) {
})
}
export function updateOrderInfo(data) {
export function getOrderInfo(pk) {
return request({
url: '/order/info',
url: '/order/info/' + pk,
method: 'get'
})
}
export function updateOrderInfo(pk, data) {
return request({
url: '/order/info/' + pk,
method: 'put',
data
})
}
export function deleteOrderInfo(pk) {
return request({
url: '/order/info/' + pk,
method: 'delete'
})
}
export function createOrderInfo(data) {
return request({
url: '/order/info',
method: 'post',
data
})
}

@ -0,0 +1,31 @@
import request from '@/utils/request'
export function getAppReportList(query) {
return request({
url: '/report/info',
method: 'get',
params: query
})
}
export function getAppReportIfo(pk) {
return request({
url: '/report/info/' + pk,
method: 'get'
})
}
export function updateAppReportIfo(pk, data) {
return request({
url: '/report/info/' + pk,
method: 'put',
data
})
}
export function deleteAppReportIfo(pk) {
return request({
url: '/report/info/' + pk,
method: 'delete'
})
}

@ -1,6 +1,6 @@
import request from '@/utils/request'
export function getStorageInfo(query) {
export function getStorageList(query) {
return request({
url: '/storage/info',
method: 'get',
@ -8,9 +8,16 @@ export function getStorageInfo(query) {
})
}
export function updateStorageInfo(data) {
export function getStorageInfo(pk) {
return request({
url: '/storage/info',
url: '/storage/info/' + pk,
method: 'get'
})
}
export function updateStorageInfo(pk, data) {
return request({
url: '/storage/info/' + pk,
method: 'put',
data
})

@ -53,3 +53,11 @@ export function updateCertificationInfo(data) {
data
})
}
export function resetPassword(data) {
return request({
url: '/user/info',
method: 'post',
data
})
}

@ -0,0 +1,31 @@
import request from '@/utils/request'
export function getWxBindList(query) {
return request({
url: '/wxbind/info',
method: 'get',
params: query
})
}
export function getWxBindInfos(pk) {
return request({
url: '/wxbind/info/' + pk,
method: 'get'
})
}
export function updateWxBindInfo(pk,data) {
return request({
url: '/wxbind/info/' + pk,
method: 'put',
data
})
}
export function deleteWxBind(pk) {
return request({
url: '/wxbind/info/' + pk,
method: 'delete'
})
}

@ -3,6 +3,8 @@ import Router from 'vue-router'
Vue.use(Router)
const celery_flower = process.env.VUE_APP_BASE_API.split('/')
/* Layout */
import Layout from '@/layout'
@ -137,18 +139,6 @@ export const constantRoutes = [
hidden: true
}
]
},
{
path: '/sign',
component: Layout,
children: [
{
path: 'index',
name: 'index',
component: () => import('@/views/form/index'),
meta: { title: '超级签名', icon: 'form' }
}
]
}, {
path: '/authentication',
component: Layout,
@ -167,7 +157,8 @@ export const constantRoutes = [
hidden: true
}
]
}, {
},
{
path: '/order',
component: Layout,
children: [
@ -186,6 +177,131 @@ export const constantRoutes = [
}
]
},
{
path: '/domain',
component: Layout,
children: [
{
path: 'list',
name: 'domain_info_list',
component: () => import('@/views/domain/list'),
meta: { title: '下载域名管理', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/domain/Detail'),
name: 'domain_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/domain/list' },
hidden: true
}
]
},
{
path: '/report',
component: Layout,
children: [
{
path: 'list',
name: 'report_info_list',
component: () => import('@/views/report/list'),
meta: { title: '应用举报信息', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/report/Detail'),
name: 'report_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/report/list' },
hidden: true
}
]
},
{
path: '/wxbind',
component: Layout,
children: [
{
path: 'list',
name: 'wxbind_info_list',
component: () => import('@/views/wxbind/list'),
meta: { title: '微信绑定管理', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/wxbind/Detail'),
name: 'wxbind_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/wxbind/list' },
hidden: true
}
]
},
{
path: '/supersign',
component: Layout,
redirect: '/supersign/developer',
name: '超级签名',
meta: {
title: '超级签名',
icon: 'nested'
},
children: [
{
path: 'developer',
name: 'developer',
redirect: '/supersign/developer/list',
component: () => import('@/views/supersign/index'),
children: [
{
path: 'list',
name: 'developer_user_info_list',
component: () => import('@/views/supersign/developer/list'),
meta: { title: '苹果开发者', icon: 'form' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/supersign/developer/Detail'),
name: 'developer_user_info_edit',
meta: { title: '编辑信息', noCache: true, activeMenu: '/supersign/list' },
hidden: true
}
]
},
{
path: 'devices',
name: 'devices',
redirect: '/supersign/devices/list',
component: () => import('@/views/supersign/index'),
children: [
{
path: 'list',
name: 'devices_info_list',
component: () => import('@/views/supersign/devices/list'),
meta: { title: '设备消耗', icon: 'form' }
}
]
},
{
path: 'bill',
name: 'bill',
redirect: '/supersign/bill/list',
component: () => import('@/views/supersign/index'),
meta: { title: '设备账单', icon: 'form' },
children: [
{
path: 'list',
name: 'bill_info_list',
component: () => import('@/views/supersign/bill/list'),
meta: { title: '消耗账单', icon: 'form' }
},
{
path: 'add',
name: 'bill_info_add',
component: () => import('@/views/supersign/bill/add'),
meta: { title: '设备充值', icon: 'form' }
}
]
}
]
},
{
path: '/settings',
component: Layout,
@ -231,7 +347,6 @@ export const constantRoutes = [
}
]
},
{
path: '/nested',
component: Layout,
@ -300,6 +415,16 @@ export const constantRoutes = [
}
]
},
{
path: 'celery-flower-link',
component: Layout,
children: [
{
path: celery_flower[0] + '//' + celery_flower[2] + '/flower',
meta: { title: '任务监控', icon: 'link' }
}
]
},
// 404 page must be placed at the end !!!
{ path: '*', redirect: '/404', hidden: true }

@ -1,7 +1,7 @@
import { login, logout, getInfo } from '@/api/user'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { resetRouter } from '@/router'
const Base64 = require('js-base64').Base64;
const Base64 = require('js-base64').Base64
const getDefaultState = () => {
return {

@ -264,29 +264,30 @@ export default {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
is_edit: false,
id: ''
}
},
computed: {
},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
}
},
methods: {
fetchData(id) {
getAppInfos({ id: id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
getAppInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateAppInfo(this.postForm).then(response => {
updateAppInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {

@ -102,7 +102,7 @@
<el-form-item label="ios内测版 udid" label-width="200px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.udid" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
<el-input v-if="postForm.udid" :value="JSON.stringify(postForm.udid)" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
</el-col>
</el-row>
</el-form-item>
@ -161,28 +161,28 @@ export default {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
is_edit: false,
id: ''
}
},
computed: {
},
created() {
const id = this.$route.params && this.$route.params.id
const app_id = this.$route.params && this.$route.params.app_id
this.fetchData(id, app_id)
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id, app_id) {
getAppReleaseInfos({ id: id, app_id: app_id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
fetchData(id) {
getAppReleaseInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateReleaseAppInfo(this.postForm).then(response => {
updateReleaseAppInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {

@ -2,7 +2,7 @@
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.release_id" placeholder="release_id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -67,13 +67,16 @@
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="200" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'app_release_info_edit',params:{app_id:listQuery.app_id,id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="info" size="mini" @click="download_bin(scope.row)">
下载源文件
</el-button>
</template>
</el-table-column>
</el-table>
@ -83,7 +86,7 @@
</template>
<script>
import { getAppReleaseInfos } from '@/api/app'
import { downloadAppReleaseInfos, getAppReleaseList } from '@/api/app'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
@ -130,12 +133,11 @@ export default {
page: 1,
limit: 10,
app_id: undefined,
download_token: undefined,
release_id: undefined,
sort: '-created_time'
ordering: '-created_time'
},
sortOptions,
type_choices: [],
status_choices: []
sortOptions
}
},
created() {
@ -143,19 +145,30 @@ export default {
this.fetchData()
},
methods: {
download_bin(app_info) {
downloadAppReleaseInfos(
{
'token': app_info.download_token,
'release_id': app_info.release_id,
'app_id': app_info.app_id
}
).then(res => {
if (res.code === 1000) {
window.location.href = res.data.download_url
} else {
this.$message.error(res.msg)
}
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getAppReleaseInfos(this.listQuery).then(response => {
this.list = response.data
if (this.list && this.list.length > 0) {
this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.total
getAppReleaseList(this.listQuery).then(response => {
this.list = response.data.results
this.total = response.data.count
this.listLoading = false
})
}

@ -4,7 +4,7 @@
<el-input v-model="listQuery.bundle_id" placeholder="Bundle_Id" style="width: 250px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.name" placeholder="应用名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.short" placeholder="短连接" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.domain_name" placeholder="应用专属访问域名" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<!-- <el-input v-model="listQuery.domain_name" placeholder="应用专属访问域名" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />-->
<el-select v-if="type_choices" v-model="listQuery.type" placeholder="应用类型" clearable class="filter-item" style="width: 120px" @change="handleFilter">
<el-option v-for="item in type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
@ -12,7 +12,7 @@
<el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -40,7 +40,11 @@
</el-table-column>
<el-table-column label="应用名称">
<template slot-scope="scope">
{{ scope.row.name }}
<el-tooltip content="点击访问下载页">
<el-link :underline="false" @click="go_download_url(scope.row)">
{{ scope.row.name }}
</el-link>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="Bundle_Id" align="center">
@ -71,16 +75,26 @@
{{ scope.row.short }}
</template>
</el-table-column>
<el-table-column label="下载次数" width="100" align="center">
<el-table-column label="下载次数" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.count_hits }}
</template>
</el-table-column>
<el-table-column label="当天下载次数" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.today_hits_count }}
</template>
</el-table-column>
<el-table-column label="应用大小" width="80" align="center">
<template slot-scope="scope">
{{ scope.row.master_release.binary_size }}
</template>
</el-table-column>
<el-table-column label="应用类型" width="80" align="center">
<template slot-scope="scope">
{{ scope.row |appTypeNameFilter }}
</template>
</el-table-column>
<el-table-column label="历史版本数" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'app_release_info_list',params:{app_id:scope.row.id}}">
@ -121,13 +135,13 @@
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
<pagination v-show="count>0" :total="count" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import { getAppInfos, deleteApp } from '@/api/app'
import { deleteApp, getAppList } from '@/api/app'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
@ -157,7 +171,10 @@ export default {
return statusMap[status]
},
appStatusNameFilter(row) {
return baseFilter(row.status,row.status_choices)
return baseFilter(row.status, row.status_choices)
},
appTypeNameFilter(row) {
return baseFilter(row.type, row.type_choices)
},
appStatusFilter(status) {
const statusMap = {
@ -172,13 +189,13 @@ export default {
return {
list: null,
listLoading: true,
total: 0,
count: 0,
listQuery: {
page: 1,
limit: 10,
name: undefined,
bundle_id: undefined,
sort: '-count_hits',
ordering: '-count_hits',
type: undefined,
domain_name: undefined,
user_id: undefined
@ -189,9 +206,14 @@ export default {
}
},
created() {
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.listQuery.id = this.$route.params && this.$route.params.app_id
this.fetchData()
},
methods: {
go_download_url(app_info) {
window.open(app_info.preview_url, '_blank', '')
},
deleteApp(app_id) {
this.$confirm('此操作将永久删除该应用, 是否继续?', '提示', {
confirmButtonText: '确定',
@ -199,7 +221,7 @@ export default {
type: 'warning'
}).then(() => {
this.listLoading = true
deleteApp({ id: app_id }).then(response => {
deleteApp(app_id).then(() => {
this.$message.success('删除成功')
this.fetchData()
this.listLoading = false
@ -217,13 +239,13 @@ export default {
},
fetchData() {
this.listLoading = true
getAppInfos(this.listQuery).then(response => {
this.list = response.data
getAppList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.type_choices = this.list[0].type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.total
this.count = response.data.count
this.listLoading = false
})
}

@ -86,9 +86,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>

@ -0,0 +1,146 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="postForm.app_info&& postForm.app_info.app_id" label="应用ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="postForm.app_info&& postForm.app_info.app_id" label="应用名称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_info.name" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="域名类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.domain_type" class="filter-item" disabled>
<el-option v-for="item in postForm.domain_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="绑定的域名" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.domain_name" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="绑定状态" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.is_enable" class="filter-item">
<el-option v-for="item in domain_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="CnameID" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.cname_id" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" :disabled="postForm.id === postForm.used_id" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getDomainInfos, updateDomainInfo } from '@/api/domain'
const domain_state_choices = [
{ id: false, name: '继续绑定' },
{ id: true, name: '绑定成功' }
]
const defaultForm = {
user_id: undefined,
app_info: undefined,
domain_type: undefined,
domain_name: undefined,
cname_id: undefined,
is_enable: undefined,
domain_type_choices: undefined,
created_time: undefined
}
export default {
name: 'OrderDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
domain_state_choices,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getDomainInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateDomainInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,188 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.domain_name" placeholder="下载域名" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_name" placeholder="应用名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.domain_type" placeholder="下载域名类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in domain_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.is_enable" placeholder="域名绑定状态" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in domain_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.user_id}}">
<el-link type="primary"> {{ scope.row.user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="下载域名" min-width="280px" align="center">
<template slot-scope="scope">
{{ scope.row.domain_name }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="下载域名类型" width="160" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.domain_type===2" :to="{name: 'app_info_list',params:{user_id:scope.row.user_id,app_id:scope.row.app_id}}">
<el-tag :type="scope.row.domain_type | domainStatusFilter">{{ scope.row| domainLableFilter }}</el-tag>
</router-link>
<el-tag v-else :type="scope.row.domain_type | domainStatusFilter">{{ scope.row| domainLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="绑定状态" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_enable | statusFilter">{{ scope.row| statusLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'domain_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="remove_domain(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import { deleteDomain, getDomainList } from '@/api/domain'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
const domain_state_choices = [
{ id: false, name: '继续绑定' },
{ id: true, name: '绑定成功' }
]
export default {
name: 'DomainInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
if (time) {
return time.split('T')[0]
}
},
domainStatusFilter(status) {
const statusMap = {
'1': 'success',
'0': 'gray',
'2': 'info' }
return statusMap[status]
},
domainLableFilter(row) {
if (row.domain_type === 2) {
return '应用' + row.app_info.name + '域名'
}
return baseFilter(row.domain_type, row.domain_type_choices)
},
statusLableFilter(row) {
return baseFilter(row.is_enable, domain_state_choices)
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
user_id: undefined,
ordering: '-created_time',
domain_name: undefined,
app_name: undefined,
domain_type: undefined,
is_enable: undefined
},
sortOptions,
domain_type_choices: [],
domain_state_choices
}
},
created() {
this.fetchData()
}, mounted() {
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
console.log(this.$router)
},
methods: {
remove_domain(domain_id) {
deleteDomain(domain_id).then(response => {
this.fetchData()
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getDomainList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.domain_type_choices = this.list[0].domain_type_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -66,7 +66,7 @@ export default {
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
if (value.length < 3) {
callback(new Error('The password can not be less than 6 digits'))
} else {
callback()

@ -142,26 +142,27 @@ export default {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
is_edit: false,
id: ''
}
},
computed: {},
created() {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getOrderInfo({ id: id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
getOrderInfo(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateOrderInfo(this.postForm).then(response => {
updateOrderInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {

@ -14,7 +14,7 @@
<el-select v-model="listQuery.order_type" placeholder="订单类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in order_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -59,7 +59,7 @@
</el-table-column>
<el-table-column label="实付金额" align="center" width="80px">
<template slot-scope="scope">
{{ scope.row.actual_amount }}
{{ scope.row.actual_amount/100 }}
</template>
</el-table-column>
<el-table-column label="购买数量" align="center" width="90px">
@ -105,7 +105,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
<el-button type="danger" size="mini" @click="remove_order_info(scope.row)">
删除
</el-button>
</template>
@ -117,14 +117,18 @@
</template>
<script>
import { getOrderInfo } from '@/api/order'
import { deleteOrderInfo, getOrderList } from '@/api/order'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'reviewed_time' },
{ label: '创建时间 Descending', key: '-reviewed_time' },
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' },
{ label: '付款时间 Ascending', key: 'pay_time' },
{ label: '付款时间 Descending', key: '-pay_time' },
{ label: '付款金额 Ascending', key: 'actual_amount' },
{ label: '付款金额 Descending', key: '-actual_amount' }
]
export default {
@ -188,7 +192,7 @@ export default {
page: 1,
limit: 10,
user_id: undefined,
sort: '-created_time',
ordering: '-created_time',
payment_type: undefined,
payment_name: undefined,
payment_number: undefined,
@ -205,25 +209,35 @@ export default {
created() {
this.fetchData()
}, mounted() {
if (this.$route.query.user_id) {
this.listQuery.user_id = this.$route.query.user_id
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
},
methods: {
remove_order_info(order_info) {
deleteOrderInfo(order_info.id).then(response => {
this.list = response.data
if (response.code === 1000) {
this.fetchData()
} else {
this.$message.error('操作失败了 ' + response.msg)
}
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getOrderInfo(this.listQuery).then(response => {
this.list = response.data
getOrderList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.payment_type_choices = this.list[0].payment_type_choices
this.status_choices = this.list[0].status_choices
this.order_type_choices = this.list[0].order_type_choices
}
this.total = response.total
this.total = response.data.count
this.listLoading = false
})
}

@ -0,0 +1,158 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="应用ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.app_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="应用名称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.app_name" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.report_type" class="filter-item" disabled>
<el-option v-for="item in postForm.report_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报原因">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.report_reason" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" disabled />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="举报者姓名" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.username" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报联系方式" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.email" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="举报者IP地址" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.remote_addr" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="处理状态" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.status" class="filter-item">
<el-option v-for="item in postForm.status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="处理备注" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.description" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" :disabled="postForm.id === postForm.used_id" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getAppReportIfo, updateAppReportIfo } from '@/api/report'
const defaultForm = {
app_id: undefined,
app_name: undefined,
bundle_id: undefined,
created_time: undefined,
description: undefined,
email: undefined,
remote_addr: undefined,
report_reason: undefined,
status: undefined,
report_type: undefined,
username: undefined
}
export default {
name: 'ReportDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getAppReportIfo(this.id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateAppReportIfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,227 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.app_id" placeholder="应用ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_name" placeholder="应用名称" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.bundle_id" placeholder="Bundle_Id" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.remote_addr" placeholder="远端地址" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.email" placeholder="联系信息" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.report_type" placeholder="举报类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in report_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.status" placeholder="处理状态" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in status_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="应用ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'app_info_edit',params:{id:scope.row.app_id}}">
<el-link type="primary"> {{ scope.row.app_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="应用名称" width="80px">
<template slot-scope="scope">
{{ scope.row.app_name }}
</template>
</el-table-column>
<el-table-column label="Bundle_Id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.bundle_id }}</span>
</template>
</el-table-column>
<el-table-column label="联系信息" align="center">
<template slot-scope="scope">
<span>{{ scope.row.email }}</span>
</template>
</el-table-column>
<el-table-column label="联系姓名" align="center">
<template slot-scope="scope">
<span>{{ scope.row.username }}</span>
</template>
</el-table-column>
<el-table-column label="举报者IP" align="center">
<template slot-scope="scope">
<span>{{ scope.row.remote_addr }}</span>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="举报类型" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.report_type | payStatusFilter">{{ scope.row| payLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="处理状态" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.status | certStatusFilter">{{ scope.row| statusLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="提交时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'report_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="remove_order_info(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import { deleteAppReportIfo, getAppReportList } from '@/api/report'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'OrderInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
if (time) {
return time.split('T')[0]
}
},
payStatusFilter(status) {
const statusMap = {
'0': 'gray',
'1': 'success' }
return statusMap[status]
},
certStatusFilter(status) {
const statusMap = {
'0': 'success',
'1': 'info',
'2': 'gray' }
return statusMap[status]
},
payLableFilter(row) {
return baseFilter(row.report_type, row.report_type_choices)
},
statusLableFilter(row) {
return baseFilter(row.status, row.status_choices)
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
},
appStatusNameFilter(row) {
for (const r of row.status_choices) {
if (r.id === row.status) {
return r.name
}
}
},
appStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
app_id: undefined,
ordering: '-created_time',
report_type: undefined,
app_name: undefined,
bundle_id: undefined,
email: undefined,
status: undefined,
remote_addr: undefined,
username: undefined
},
sortOptions,
report_type_choices: [],
status_choices: []
}
},
created() {
this.fetchData()
}, mounted() {
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
},
methods: {
remove_order_info(order_info) {
deleteAppReportIfo(order_info.id).then(response => {
this.list = response.data
if (response.code === 1000) {
this.fetchData()
} else {
this.$message.error('操作失败了 ' + response.msg)
}
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getAppReportList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.report_type_choices = this.list[0].report_type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -147,26 +147,27 @@ export default {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
is_edit: false,
id: ''
}
},
computed: {},
created() {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getStorageInfo({ id: id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
getStorageInfo(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateStorageInfo(this.postForm).then(response => {
updateStorageInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {

@ -9,7 +9,7 @@
<el-select v-model="listQuery.storage_type" placeholder="存储类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in storage_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -83,9 +83,7 @@
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
@ -95,15 +93,15 @@
</template>
<script>
import { getStorageInfo } from '@/api/storage'
import { getStorageList } from '@/api/storage'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'reviewed_time' },
{ label: '创建时间 Descending', key: '-reviewed_time' },
{ label: '更新时间 Ascending', key: 'created_time' },
{ label: '更新时间 Descending', key: '-created_time' }
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' },
{ label: '更新时间 Ascending', key: 'updated_time' },
{ label: '更新时间 Descending', key: '-updated_time' }
]
export default {
@ -162,21 +160,21 @@ export default {
page: 1,
limit: 10,
name: undefined,
sort: '-created_time',
bucket_name: undefined,
access_key: undefined,
storage_type: undefined,
domain_name: undefined,
user_id: undefined,
used_id: undefined
ordering: '-created_time'
},
sortOptions,
storage_choices: []
}
},
created() {
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.fetchData()
},mounted() {
}, mounted() {
if (this.$route.query.user_id) {
this.listQuery.user_id = this.$route.query.user_id
}
@ -188,12 +186,13 @@ export default {
},
fetchData() {
this.listLoading = true
getStorageInfo(this.listQuery).then(response => {
this.list = response.data
getStorageList(this.listQuery).then(response => {
this.list = response.data.results
console.log(this.list)
if (this.list && this.list.length > 0) {
this.storage_choices = this.list[0].storage_choices
}
this.total = response.total
this.total = response.data.count
this.listLoading = false
})
}

@ -1,183 +0,0 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储名称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.name" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.storage_type" class="filter-item" disabled>
<el-option v-for="item in postForm.storage_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="更新时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.updated_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储描述信息">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.description" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="存储访问access_key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.access_key" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储访问secret_key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.secret_key" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="存储空间bucket_name" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.bucket_name" />
</el-col>
</el-row>
</el-form-item>
<div v-if="postForm.storage_type===2">
<el-form-item label="阿里云sts_role_arn" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.sts_role_arn" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="阿里云endpoint" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.endpoint" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="阿里云下载授权方式" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.download_auth_type" class="filter-item" style="width: 100%">
<el-option v-for="item in postForm.download_auth_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item v-if="postForm.download_auth_type===2" label="阿里云cnd_auth_key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.cnd_auth_key" />
</el-col>
</el-row>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getStorageInfo, updateStorageInfo } from '@/api/storage'
const defaultForm = {
user_id: undefined,
name: undefined,
storage_type: undefined,
access_key: undefined,
secret_key: undefined,
bucket_name: undefined,
domain_name: undefined,
created_time: undefined,
description: undefined,
sts_role_arn: undefined,
endpoint: undefined,
download_auth_type: undefined,
cnd_auth_key: undefined,
updated_time: undefined,
download_auth_type_choices: []
}
export default {
name: 'StorageDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false
}
},
computed: {},
created() {
const id = this.$route.params && this.$route.params.id
this.fetchData(id)
},
methods: {
fetchData(id) {
getStorageInfo({ id: id }).then(response => {
if (response.data.length === 1) {
this.postForm = response.data[0]
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateStorageInfo(this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,117 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.user_id" placeholder="邮箱,手机号,或者uid" />
</el-col>
<el-col :span="4">
<el-button @click="check_balance('user_id')">查询校验</el-button>
</el-col>
</el-row>
<el-row v-if="user_info.private_balance_info.all_balance+user_info.public_balance_info.all_balance">
<el-col>
<el-link :underline="false"> 公有池数量 {{ user_info.public_balance_info.all_balance }} 已经使用数量 {{ user_info.public_balance_info.used_balance }}</el-link>
</el-col>
<el-col>
<el-link :underline="false"> 私有池数量 {{ user_info.private_balance_info.all_balance }} 已经使用数量 {{ user_info.private_balance_info.used_balance }}</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="目标用户">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.to_user_id" placeholder="邮箱,手机号,或者uid" />
</el-col>
<el-col :span="4">
<el-button @click="check_balance('to_user_id')">查询校验</el-button>
</el-col>
</el-row>
<el-row v-if="to_user_info.private_balance_info.all_balance+to_user_info.public_balance_info.all_balance">
<el-col>
<el-link :underline="false"> 公有池数量 {{ to_user_info.public_balance_info.all_balance }} 已经使用数量 {{ to_user_info.public_balance_info.used_balance }}</el-link>
</el-col>
<el-col>
<el-link :underline="false"> 私有池数量 {{ to_user_info.private_balance_info.all_balance }} 已经使用数量 {{ to_user_info.private_balance_info.used_balance }}</el-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="转移数量">
<el-row :gutter="12">
<el-col :span="16">
<el-input-number v-model="postForm.number" :min="0" :max="10000" label="可使用设备数" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button type="primary" @click="addbill">确定</el-button>
</el-col>
</div>
</template>
<script>
import waves from '@/directive/waves'
import { addBillInfo, getUserBillInfo } from '@/api/developer'
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'BillAddInfo',
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
postForm: {
to_user_id: undefined,
user_id: undefined,
number: undefined
},
user_info: { public_balance_info: { all_balance: undefined }, private_balance_info: { used_balance: undefined }},
to_user_info: { public_balance_info: { all_balance: undefined }, private_balance_info: { used_balance: undefined }},
action_choices: undefined,
sortOptions
}
},
created() {
},
methods: {
check_balance(act) {
let user_id = this.postForm.user_id
if (act === 'to_user_id') {
user_id = this.postForm.to_user_id
}
getUserBillInfo({ user_id: user_id }).then(response => {
if (act === 'user_id') {
this.user_info = response
} else if (act === 'to_user_id') {
this.to_user_info = response
}
console.log(this.user_info)
})
},
addbill() {
addBillInfo(this.postForm).then(response => {
this.$message.success('充值成功')
})
}
}
}
</script>

@ -0,0 +1,202 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.to_user_id" placeholder="目标用户" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.udid" placeholder="设备UDID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.app_id" placeholder="应用id" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.action" placeholder="充值类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in action_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.user_id}}">
<el-link type="primary"> {{ scope.row.user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="目标用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.to_user_id" :to="{name: 'user_info_edit',params:{id:scope.row.to_user_id}}">
<el-link type="primary"> {{ scope.row.to_user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="应用ID" width="100" align="center">
<template slot-scope="scope">
<router-link v-if="scope.row.app_id" :to="{name: 'app_info_edit',params:{id:scope.row.app_id}}">
<el-link type="primary"> {{ scope.row.app_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="UDID" align="center">
<template slot-scope="scope">
{{ scope.row.udid }}
</template>
</el-table-column>
<el-table-column label="客户端IP" align="center" >
<template slot-scope="scope">
{{ scope.row.remote_addr }}
</template>
</el-table-column>
<el-table-column label="账单类型" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.action }}
</template>
</el-table-column>
<el-table-column label="备注" align="center">
<template slot-scope="scope">
{{ scope.row.description }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button type="danger" size="mini" @click="delbill(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves'
import { delBillInfo, getBillList } from '@/api/developer'
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'BillInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
},
certStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray',
'3': 'info'
}
return statusMap[status]
},
certLableFilter(row) {
for (const v of row.auth_type_choices) {
if (v.id === row.auth_type) {
return v.name
}
}
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
},
appStatusNameFilter(row) {
for (const r of row.status_choices) {
if (r.id === row.status) {
return r.name
}
}
},
appStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
ordering: '-created_time',
action: undefined,
app_id: undefined,
to_user_id: undefined,
user_id: undefined,
udid: undefined
},
action_choices: undefined,
sortOptions
}
},
created() {
this.listQuery.issuer_id = this.$route.params && this.$route.params.issuer_id
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.fetchData()
},
methods: {
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
delbill(id) {
delBillInfo(id).then(response => {
this.$message.success('删除成功')
this.fetchData()
this.listLoading = false
})
},
fetchData() {
this.listLoading = true
getBillList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.action_choices = this.list[0].action_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -0,0 +1,193 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="issuer_id">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.issuer_id" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="可使用设备数">
<el-row :gutter="12">
<el-col :span="16">
<el-input-number v-model="postForm.usable_number" :min="0" :max="100" label="可使用设备数" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="已消耗设备数">
<el-row :gutter="12">
<el-col :span="16">
<el-input-number v-model="postForm.use_number" :min="0" :max="100" label="已消耗设备数" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="账户类型">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.auth_type" class="filter-item" disabled>
<el-option v-for="item in postForm.auth_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="更新时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.updated_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="描述信息">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.description" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="账户状态" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-tooltip :content="postForm.is_actived|userStatusFilter" placement="top">
<el-switch
v-model="postForm.is_actived"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="true"
:inactive-value="false"
/>
</el-tooltip>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="证书过期时间" prop="timestamp" label-width="160px">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.cert_expire_time" type="datetime" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="private_key_id" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.private_key_id" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="p8key" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.p8key" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="certid" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.certid" />
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getDeveloperInfo, updatedeveloperInfo } from '@/api/developer'
const defaultForm = {
user_id: undefined,
is_actived: undefined,
issuer_id: undefined,
private_key_id: undefined,
p8key: undefined,
certid: undefined,
created_time: undefined,
description: undefined,
usable_number: undefined,
use_number: undefined,
auth_type: undefined,
cert_expire_time: undefined,
updated_time: undefined,
auth_type_choices: []
}
export default {
name: 'DeveloperDetail',
components: { }, filters: {
userStatusFilter(status) {
const statusMap = {
true: '激活,允许使用',
false: '禁用,禁止使用'
}
return statusMap[status]
}
},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getDeveloperInfo(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updatedeveloperInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
.editor-container {
position: relative;
height: 100%;
}
</style>

@ -0,0 +1,218 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.issuer_id" placeholder="issuer_id" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.private_key_id" placeholder="private_key_id" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.certid" placeholder="证书ID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.description" placeholder="备注" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.auth_type" placeholder="账户类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in auth_type_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.user_id}}">
<el-link type="primary"> {{ scope.row.user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="issuer_id" width="300">
<template slot-scope="scope">
{{ scope.row.issuer_id }}
</template>
</el-table-column>
<el-table-column label="private_key_id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.private_key_id }}</span>
</template>
</el-table-column>
<el-table-column label="certid" align="center">
<template slot-scope="scope">
{{ scope.row.certid }}
</template>
</el-table-column>
<el-table-column label="可用设备数" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.usable_number }}
</template>
</el-table-column>
<el-table-column label="消耗设备数" align="center" width="100">
<template slot-scope="scope">
<router-link :to="{name: 'devices_info_list',params:{issuer_id:scope.row.issuer_id}}">
<el-link type="primary"> {{ scope.row.use_number }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="账户类型" width="130" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.auth_type | certStatusFilter">{{ scope.row| certLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="是否激活" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_actived | statusFilter">{{ scope.row |StatusNameFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="证书过期时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.updated_time">
<span>{{ scope.row.cert_expire_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'developer_user_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<!-- <el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">-->
<!-- 删除-->
<!-- </el-button>-->
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import { getDeveloperList } from '@/api/developer'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' },
{ label: '更新时间 Ascending', key: 'updated_time' },
{ label: '更新时间 Descending', key: '-updated_time' },
{ label: '证书过期时间 Ascending', key: 'cert_expire_time' },
{ label: '证书过期时间 Descending', key: '-cert_expire_time' }
]
export default {
name: 'StorageInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
return time.split('T')[0]
},
certStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray',
'3': 'info'
}
return statusMap[status]
},
certLableFilter(row) {
for (const v of row.auth_type_choices) {
if (v.id === row.auth_type) {
return v.name
}
}
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
},
StatusNameFilter(row) {
for (const r of row.status_choices) {
if (r.id === row.status) {
return r.name
}
}
},
appStatusFilter(status) {
const statusMap = {
'0': 'danger',
'1': 'success',
'2': 'gray'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
ordering: '-created_time',
issuer_id: undefined,
private_key_id: undefined,
certid: undefined,
description: undefined,
user_id: undefined,
auth_type: undefined
},
sortOptions,
auth_type_choices: [],
status_choices: []
}
},
created() {
this.fetchData()
},
methods: {
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getDeveloperList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.auth_type_choices = this.list[0].auth_type_choices
this.status_choices = this.list[0].status_choices
}
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -2,14 +2,13 @@
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.bucket_name" placeholder="bucket_name" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.access_key" placeholder="access_key" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.name" placeholder="存储名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.domain_name" placeholder="下载域名" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.storage_type" placeholder="存储类型" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in storage_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.sort" style="width: 140px" class="filter-item" @change="handleFilter">
<el-input v-model="listQuery.issuer_id" placeholder="开发者issuer_id" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.udid" placeholder="设备UDID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.name" placeholder="应用名称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.bundle_id" placeholder="BundleID" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.short" placeholder="短连接" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
@ -37,54 +36,52 @@
</router-link>
</template>
</el-table-column>
<el-table-column label="存储名称">
<el-table-column label="应用ID" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.name }}
<router-link :to="{name: 'app_info_edit',params:{id:scope.row.app_id}}">
<el-link type="primary"> {{ scope.row.app_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="存储访问access_key" align="center">
<el-table-column label="开发者issuer_id" align="center">
<template slot-scope="scope">
<span>{{ scope.row.access_key }}</span>
<router-link :to="{name: 'developer_user_info_edit',params:{id:scope.row.developer_pk}}">
<el-link type="primary"> {{ scope.row.developer_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="存储空间bucket_name" align="center">
<el-table-column label="应用名称" align="center" >
<template slot-scope="scope">
{{ scope.row.bucket_name }}
{{ scope.row.bundle_name }}
</template>
</el-table-column>
<el-table-column label="下载域名" align="center">
<el-table-column label="短连接" align="center" width="100">
<template slot-scope="scope">
{{ scope.row.domain_name }}
{{ scope.row.short }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="存储类型" width="95" align="center">
<el-table-column label="BundleID" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.storage_type | certStatusFilter">{{ scope.row| certLableFilter }}</el-tag>
{{ scope.row.bundle_id }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<el-table-column label="设备UDID" align="center" width="350">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
{{ scope.row.device_udid }}
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="更新时间" width="120">
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.updated_time">
<span>{{ scope.row.updated_time|formatTime }}</span>
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'storage_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="deleteApp(scope.row.id)">
删除
</el-button>
@ -97,19 +94,17 @@
</template>
<script>
import { getStorageInfo } from '@/api/storage'
import { getDevicesList } from '@/api/devices'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'reviewed_time' },
{ label: '创建时间 Descending', key: '-reviewed_time' },
{ label: '更新时间 Ascending', key: 'created_time' },
{ label: '更新时间 Descending', key: '-created_time' }
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
export default {
name: 'StorageInfo',
name: 'DevicesInfo',
components: { Pagination },
directives: { waves },
filters: {
@ -126,8 +121,8 @@ export default {
return statusMap[status]
},
certLableFilter(row) {
for (const v of row.storage_choices) {
if (v.id === row.storage_type) {
for (const v of row.auth_type_choices) {
if (v.id === row.auth_type) {
return v.name
}
}
@ -163,19 +158,20 @@ export default {
listQuery: {
page: 1,
limit: 10,
ordering: '-created_time',
issuer_id: undefined,
short: undefined,
bundle_id: undefined,
name: undefined,
sort: '-created_time',
bucket_name: undefined,
access_key: undefined,
storage_type: undefined,
domain_name: undefined,
user_id: undefined
user_id: undefined,
udid: undefined
},
sortOptions,
storage_choices: []
sortOptions
}
},
created() {
this.listQuery.issuer_id = this.$route.params && this.$route.params.issuer_id
this.listQuery.user_id = this.$route.params && this.$route.params.user_id
this.fetchData()
},
methods: {
@ -185,12 +181,12 @@ export default {
},
fetchData() {
this.listLoading = true
getStorageInfo(this.listQuery).then(response => {
this.list = response.data
getDevicesList(this.listQuery).then(response => {
this.list = response.data.results
if (this.list && this.list.length > 0) {
this.storage_choices = this.list[0].storage_choices
this.auth_type_choices = this.list[0].auth_type_choices
}
this.total = response.total
this.total = response.data.count
this.listLoading = false
})
}

@ -0,0 +1,3 @@
<template>
<router-view />
</template>

@ -6,21 +6,21 @@
<el-form-item label="UID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.uid" disabled />
<el-input :value="postForm.uid" disabled/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="账号" prop="username">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.username" />
<el-input v-model="postForm.username"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="昵称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.first_name" />
<el-input v-model="postForm.first_name"/>
</el-col>
</el-row>
</el-form-item>
@ -28,7 +28,9 @@
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.gender" class="filter-item" placeholder="Please select">
<el-option v-for="item in postForm.gender_choices" :key="item.id" :label="item.name" :value="item.id" />
<el-option v-for="item in postForm.gender_choices" :key="item.id" :label="item.name"
:value="item.id"
/>
</el-select>
</el-col>
</el-row>
@ -37,7 +39,7 @@
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.role" class="filter-item" placeholder="Please select">
<el-option v-for="item in postForm.role_choices" :key="item.id" :label="item.name" :value="item.id" />
<el-option v-for="item in postForm.role_choices" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-col>
</el-row>
@ -46,7 +48,7 @@
<el-form-item label="邮箱" prop="email">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.email" />
<el-input v-model="postForm.email"/>
</el-col>
</el-row>
</el-form-item>
@ -54,35 +56,37 @@
<el-form-item label="手机" prop="mobile">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.mobile" />
<el-input v-model="postForm.mobile"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="Q Q" prop="number">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.qq" />
<el-input v-model="postForm.qq"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="职位">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.job" />
<el-input v-model="postForm.job"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="公司">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.company" />
<el-input v-model="postForm.company"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="备注">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.memo" :autosize="{ minRows: 4, maxRows: 6}" type="textarea" placeholder="Please input" />
<el-input v-model="postForm.memo" :autosize="{ minRows: 4, maxRows: 6}" type="textarea"
placeholder="Please input"
/>
</el-col>
</el-row>
</el-form-item>
@ -91,7 +95,9 @@
<el-form-item label="用户头像">
<el-row :gutter="12">
<el-col :span="16">
<el-image :src="postForm.head_img" :preview-src-list="[postForm.head_img]" fit="contain" style="width: 100px; height: 100px" />
<el-image :src="postForm.head_img" :preview-src-list="[postForm.head_img]" fit="contain"
style="width: 100px; height: 100px"
/>
</el-col>
</el-row>
</el-form-item>
@ -177,10 +183,16 @@
<el-form-item label="实名认证">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.certification" class="filter-item" placeholder="Please select" :disabled="postForm.certification === -1">
<el-option v-for="item in postForm.certification_status_choices" :key="item.id" :label="item.name" :value="item.id" />
<el-select v-model="postForm.certification" class="filter-item" placeholder="Please select"
:disabled="postForm.certification === -1"
>
<el-option v-for="item in postForm.certification_status_choices" :key="item.id" :label="item.name"
:value="item.id"
/>
</el-select>
<router-link v-if="postForm.certification_id" :to="{name: 'user_authentication_info_edit',params:{id:postForm.certification_id.id}}">
<router-link v-if="postForm.certification_id"
:to="{name: 'user_authentication_info_edit',params:{id:postForm.certification_id.id}}"
>
<el-button type="primary">
审核认证信息
</el-button>
@ -194,38 +206,49 @@
<el-form-item label="下载域名" prop="domain_name">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.domain_name" />
<router-link v-if="postForm.id" :to="{name: 'domain_info_list',params:{user_id:postForm.id}}">
<el-button type="primary">查看下载域名绑定信息</el-button>
</router-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="下载次数" prop="download_times">
<el-row :gutter="12">
<el-col :span="8">
<el-input :value="postForm.download_times" disabled />
<el-input :value="postForm.download_times" disabled/>
</el-col>
<el-col :span="8">
<el-button>后台充值</el-button>
<router-link v-if="postForm.id" :to="{name: 'order_info_list',params:{user_id:postForm.id}}">
<el-button type="primary">查看充值订单信息</el-button>
</router-link>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="注册时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.date_joined" type="datetime" disabled />
<el-date-picker :value="postForm.date_joined" type="datetime" disabled/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="应用版本历史记录">
<el-row :gutter="20">
<el-col :span="8">
<el-input v-model="postForm.history_release_limit" />
<el-input v-model="postForm.history_release_limit"/>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-col :span="8">
<el-button @click="add_amount_download_times(postForm.id)">下载次数充值</el-button>
</el-col>
<el-col :span="8">
<el-button @click="reset_password(postForm.id)">重置密码</el-button>
</el-col>
<el-button v-if="!is_edit" type="primary" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
@ -237,9 +260,9 @@
<script>
import { validURL } from '@/utils/validate'
import { getUserInfos, updateUserInfo } from '@/api/user'
import { getStorageInfo,changeStorageInfo } from '@/api/storage'
import { CommentDropdown, PlatformDropdown, SourceUrlDropdown } from './Dropdown'
import { getUserInfos, resetPassword, updateUserInfo } from '@/api/user'
import { createOrderInfo } from '@/api/order'
import { changeStorageInfo, getStorageList } from '@/api/storage'
const defaultForm = {
email: undefined,
@ -260,6 +283,7 @@ const defaultForm = {
date_joined: undefined,
storage_active: undefined,
storage: undefined,
certification_id: undefined,
supersign_active: undefined,
role_choices: [],
gender_choices: [],
@ -268,7 +292,7 @@ const defaultForm = {
export default {
name: 'UserDetail',
components: { CommentDropdown, PlatformDropdown, SourceUrlDropdown }, filters: {
components: {}, filters: {
userStatusFilter(status) {
const statusMap = {
true: '激活,允许登录',
@ -332,8 +356,7 @@ export default {
storage_selection: []
}
},
computed: {
},
computed: {},
created() {
if (this.isEdit) {
const id = this.$route.params && this.$route.params.id
@ -341,17 +364,61 @@ export default {
}
},
methods: {
add_amount_download_times(user_id) {
this.$prompt('请输入要充值的下载次数', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
createOrderInfo({ id: user_id, amount: parseInt(value) }).then(res => {
if (res.code === 1000) {
this.$message.success('下载次数充值成功')
this.fetchData(user_id)
}
})
}).catch((e) => {
console.log(e)
this.$message({
type: 'info',
message: '取消充值'
})
})
}, reset_password(user_id) {
this.$prompt('请输入新密码,密码至少6位', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消'
}).then(({ value }) => {
resetPassword({ user_id: user_id, password: value }).then(res => {
if (res.code === 1000) {
this.$message.success('密码重置成功')
this.fetchData(user_id)
} else {
this.$message.error(res.msg)
}
})
}).catch((e) => {
console.log(e)
this.$message({
type: 'info',
message: '取消充值'
})
})
},
changeStorageData(force) {
changeStorageInfo({id: this.postForm.id, use_storage_id: this.postForm.storage,force: force}).then(response => {
changeStorageInfo({
id: this.postForm.id,
use_storage_id: this.postForm.storage,
force: force
}).then(response => {
this.$message.success('存储数据迁移并设置刷新成功')
}).catch(err => {
console.log(err)
})
},
fetchStorageData(user_id) {
getStorageInfo({ user_id: user_id }).then(response => {
if (response.storage_selection) {
this.storage_selection = response.storage_selection
getStorageList({ user_id: user_id }).then(response => {
if (response.data.storage_selection) {
this.storage_selection = response.data.storage_selection
}
}).catch(err => {
console.log(err)

@ -34,12 +34,12 @@
<el-image :src="scope.row.head_img" :preview-src-list="[scope.row.head_img]" fit="contain" style="width: 80px; height: 80px" />
</template>
</el-table-column>
<el-table-column label="登录名">
<el-table-column label="登录名" align="center">
<template slot-scope="scope">
{{ scope.row.username }}
</template>
</el-table-column>
<el-table-column label="昵称">
<el-table-column label="昵称" align="center">
<template slot-scope="scope">
{{ scope.row.first_name }}
</template>
@ -49,7 +49,7 @@
<span>{{ scope.row.mobile }}</span>
</template>
</el-table-column>
<el-table-column label="邮箱" width="210" align="center">
<el-table-column label="邮箱" width="200" align="center">
<template slot-scope="scope">
{{ scope.row.email }}
</template>
@ -60,21 +60,39 @@
{{ scope.row.download_times }}
</template>
</el-table-column>
<el-table-column label="当天苹果|安卓下载次数" width="100" align="center">
<template slot-scope="scope">
{{ scope.row.today_hits_count.ios_today_hits_count }} | {{ scope.row.today_hits_count.android_today_hits_count }}
</template>
</el-table-column>
<el-table-column label="应用数量" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'app_info_list',params:{user_id:scope.row.id}}">
<el-link type="primary"> {{ scope.row.app_count }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="是否激活" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.is_active | statusFilter">{{ scope.row.is_active }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="私有存储" width="80" align="center">
<el-table-column class-name="status-col" label="私有存储权限" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.storage_active | statusFilter">{{ scope.row.storage_active }}</el-tag>
<router-link v-if="scope.row.storage_active" :to="{name: 'storage_info_list',params:{user_id:scope.row.id}}">
<el-link type="primary"> <el-tag :type="scope.row.storage_active | statusFilter">{{ scope.row.storage_active }}</el-tag></el-link>
</router-link>
<el-tag v-else :type="scope.row.storage_active | statusFilter">{{ scope.row.storage_active }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="超级签" width="80" align="center">
<el-table-column class-name="status-col" label="超级签权限" width="80" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.supersign_active | statusFilter">{{ scope.row.supersign_active }}</el-tag>
<router-link v-if="scope.row.supersign_active" :to="{name: 'devices_info_list',params:{user_id:scope.row.id}}">
<el-link type="primary"> <el-tag :type="scope.row.supersign_active | statusFilter">{{ scope.row.supersign_active }}</el-tag></el-link>
</router-link>
<el-tag v-else :type="scope.row.supersign_active | statusFilter">{{ scope.row.supersign_active }}</el-tag>
</template>
</el-table-column>
<el-table-column class-name="status-col" label="实名认证" width="95" align="center">
@ -91,7 +109,7 @@
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width">
<el-table-column label="操作" align="center" width="110" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
@ -144,7 +162,7 @@ export default {
return statusMap[status]
},
certLableFilter(row) {
return baseFilter(row.certification,row.certification_status_choices)
return baseFilter(row.certification, row.certification_status_choices)
}
},
data() {

@ -0,0 +1,157 @@
<template>
<div class="app-container">
<el-form ref="postForm" :model="postForm" label-width="100px" :disabled="!is_edit">
<el-row>
<el-col :span="12">
<el-form-item label="用户ID">
<el-row :gutter="12">
<el-col :span="16">
<el-input :value="postForm.user_id" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="微信openid">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.openid" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="微信昵称">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.nickname" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="创建时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.created_time" disabled type="datetime" />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="订阅时间" prop="timestamp">
<el-row :gutter="20">
<el-col :span="8">
<el-date-picker :value="postForm.subscribe_time*1000" disabled type="datetime" />
</el-col>
</el-row>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="用户头像" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-image
:preview-src-list="[postForm.head_img_url]"
:src="postForm.head_img_url"
fit="contain"
style="width: 100px; height: 100px"
/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="扫码登录" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.enable_login" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="消息通知" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-input v-model="postForm.enable_notify" disabled />
</el-col>
</el-row>
</el-form-item>
<el-form-item label="订阅状态" label-width="160px">
<el-row :gutter="12">
<el-col :span="16">
<el-select v-model="postForm.subscribe" class="filter-item" disabled>
<el-option v-for="item in wxbind_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-col>
</el-row>
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-col :span="9" style="float: right">
<el-button v-if="!is_edit" type="primary" :disabled="postForm.id === postForm.used_id" @click="is_edit=true">修改</el-button>
<div v-else>
<el-button type="primary" @click="is_edit=false">取消</el-button>
<el-button type="primary" @click="updateData">保存修改</el-button>
</div>
</el-col>
</div>
</template>
<script>
import { getWxBindInfos, updateWxBindInfo } from '@/api/wxbind'
const wxbind_state_choices = [
{ id: false, name: '未订阅' },
{ id: true, name: '已订阅' }
]
const defaultForm = {
user_id: undefined,
address: undefined,
created_time: undefined,
head_img_url: undefined,
id: undefined,
nickname: undefined,
openid: undefined,
subscribe: undefined,
subscribe_time: undefined,
enable_login: undefined,
enable_notify: undefined
}
export default {
name: 'OrderDetail',
components: { }, filters: {},
data() {
return {
postForm: Object.assign({}, defaultForm),
loading: false,
is_edit: false,
wxbind_state_choices,
id: ''
}
},
computed: {},
created() {
this.id = this.$route.params && this.$route.params.id
this.fetchData(this.id)
},
methods: {
fetchData(id) {
getWxBindInfos(id).then(response => {
if (response.data) {
this.postForm = response.data
}
}).catch(err => {
console.log(err)
})
},
updateData() {
updateWxBindInfo(this.id, this.postForm).then(response => {
this.$message.success('更新成功')
this.postForm = response.data
}).catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped>
</style>

@ -0,0 +1,192 @@
<template>
<div class="app-container">
<div class="filter-container">
<el-input v-model="listQuery.user_id" placeholder="用户ID" style="width: 140px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.openid" placeholder="微信openid" style="width: 300px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-input v-model="listQuery.nickname" placeholder="微信昵称" style="width: 200px;" class="filter-item" clearable @keyup.enter.native="handleFilter" />
<el-select v-model="listQuery.subscribe" placeholder="玩家是否订阅" clearable class="filter-item" style="width: 140px" @change="handleFilter">
<el-option v-for="item in wxbind_state_choices" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<el-select v-model="listQuery.ordering" style="width: 140px" class="filter-item" @change="handleFilter">
<el-option v-for="item in sortOptions" :key="item.key" :label="item.label" :value="item.key" />
</el-select>
<el-button v-waves class="filter-item" type="primary" icon="el-icon-search" @click="handleFilter">
Search
</el-button>
</div>
<el-table
v-loading="listLoading"
:data="list"
element-loading-text="Loading"
border
fit
highlight-current-row
stripe
>
<el-table-column align="center" label="ID" width="90">
<template slot-scope="scope">
{{ scope.row.id }}
</template>
</el-table-column>
<el-table-column label="用户ID" width="100" align="center">
<template slot-scope="scope">
<router-link :to="{name: 'user_info_edit',params:{id:scope.row.user_id}}">
<el-link type="primary"> {{ scope.row.user_id }}</el-link>
</router-link>
</template>
</el-table-column>
<el-table-column label="头像" align="center">
<template slot-scope="scope">
<el-image :src="scope.row.head_img_url" :preview-src-list="[scope.row.head_img_url]" fit="contain" style="width: 80px; height: 80px" />
</template>
</el-table-column>
<el-table-column label="微信昵称" min-width="180px" align="center">
<template slot-scope="scope">
{{ scope.row.nickname }}
</template>
</el-table-column>
<el-table-column label="微信openid" min-width="280px" align="center">
<template slot-scope="scope">
{{ scope.row.openid }}
</template>
</el-table-column>
<el-table-column label="允许扫码登录" min-width="100px" align="center">
<template slot-scope="scope">
{{ scope.row.enable_login }}
</template>
</el-table-column>
<el-table-column label="允许消息通知" min-width="100px" align="center">
<template slot-scope="scope">
{{ scope.row.enable_notify }}
</template>
</el-table-column>
<el-table-column class-name="status-col" label="订阅状态" width="95" align="center">
<template slot-scope="scope">
<el-tag :type="scope.row.subscribe | statusFilter">{{ scope.row| statusLableFilter }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="created_time" label="创建时间" width="120">
<template slot-scope="scope">
<i class="el-icon-time" />
<el-tooltip :content="scope.row.created_time">
<span>{{ scope.row.created_time|formatTime }}</span>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<router-link :to="{name: 'wxbind_info_edit',params:{id:scope.row.id}}">
<el-button type="primary" size="mini">
查看编辑
</el-button>
</router-link>
<el-button type="danger" size="mini" @click="remove_wxbind(scope.row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="listQuery.page" :limit.sync="listQuery.limit" @pagination="fetchData" />
</div>
</template>
<script>
import { deleteWxBind, getWxBindList } from '@/api/wxbind'
import { baseFilter } from '@/utils'
import Pagination from '@/components/Pagination' // secondary package based on el-pagination
import waves from '@/directive/waves' // waves directive
const sortOptions = [
{ label: '创建时间 Ascending', key: 'created_time' },
{ label: '创建时间 Descending', key: '-created_time' }
]
const wxbind_state_choices = [
{ id: false, name: '未订阅' },
{ id: true, name: '已订阅' }
]
export default {
name: 'WxBindInfo',
components: { Pagination },
directives: { waves },
filters: {
formatTime(time) {
if (time) {
return time.split('T')[0]
}
},
wxbindStatusFilter(status) {
const statusMap = {
'1': 'success',
'0': 'gray',
'2': 'info' }
return statusMap[status]
},
wxbindLableFilter(row) {
if (row.wxbind_type === 2) {
return '应用' + row.app_info.name + '域名'
}
return baseFilter(row.wxbind_type, row.wxbind_type_choices)
},
statusLableFilter(row) {
return baseFilter(row.subscribe, wxbind_state_choices)
},
statusFilter(status) {
const statusMap = {
true: 'success',
false: 'danger'
}
return statusMap[status]
}
},
data() {
return {
list: null,
listLoading: true,
total: 0,
listQuery: {
page: 1,
limit: 10,
user_id: undefined,
ordering: '-created_time',
openid: undefined,
nickname: undefined,
subscribe: undefined
},
sortOptions,
wxbind_state_choices
}
},
created() {
this.fetchData()
}, mounted() {
if (this.$route.params.user_id) {
this.listQuery.user_id = this.$route.params.user_id
}
console.log(this.$router)
},
methods: {
remove_wxbind(wxbind_id) {
deleteWxBind(wxbind_id).then(response => {
this.list = response.data
this.total = response.total
this.listLoading = false
})
},
handleFilter() {
this.listQuery.page = 1
this.fetchData()
},
fetchData() {
this.listLoading = true
getWxBindList(this.listQuery).then(response => {
this.list = response.data.results
this.total = response.data.count
this.listLoading = false
})
}
}
}
</script>

@ -2508,15 +2508,15 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.5.4:
version "4.16.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
version "4.16.3"
resolved "https://registry.npm.taobao.org/browserslist/download/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717"
integrity sha1-NAqkaUDX24eHSFZ8XeokpI3fNxc=
dependencies:
caniuse-lite "^1.0.30001219"
colorette "^1.2.2"
electron-to-chromium "^1.3.723"
caniuse-lite "^1.0.30001181"
colorette "^1.2.1"
electron-to-chromium "^1.3.649"
escalade "^3.1.1"
node-releases "^1.1.71"
node-releases "^1.1.70"
bs-logger@0.x:
version "0.2.6"
@ -2718,10 +2718,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001219:
version "1.0.30001234"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001234.tgz#8fc2e709e3b0679d7af7f073a1c661155c39b975"
integrity sha512-a3gjUVKkmwLdNysa1xkUAwN2VfJUJyVW47rsi3aCbkRCtbHAfo+rOsCqVw29G6coQ8gzAPb5XBXwiGHwme3isA==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000957, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181:
version "1.0.30001208"
resolved "https://registry.npm.taobao.org/caniuse-lite/download/caniuse-lite-1.0.30001208.tgz?cache=0&sync_timestamp=1617866642507&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcaniuse-lite%2Fdownload%2Fcaniuse-lite-1.0.30001208.tgz#a999014a35cebd4f98c405930a057a0d75352eb9"
integrity sha1-qZkBSjXOvU+YxAWTCgV6DXU1Lrk=
capture-exit@^2.0.0:
version "2.0.0"
@ -3024,10 +3024,10 @@ color@^3.0.0:
color-convert "^1.9.1"
color-string "^1.5.4"
colorette@^1.2.1, colorette@^1.2.2:
colorette@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
resolved "https://registry.npm.taobao.org/colorette/download/colorette-1.2.2.tgz?cache=0&sync_timestamp=1614259647923&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcolorette%2Fdownload%2Fcolorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha1-y8x51emcrqLb8Q6zom/Ys+as+pQ=
combined-stream@^1.0.6, combined-stream@~1.0.6:
version "1.0.8"
@ -3922,10 +3922,10 @@ ejs@^2.6.1:
resolved "https://registry.npm.taobao.org/ejs/download/ejs-2.7.4.tgz?cache=0&sync_timestamp=1612644037163&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fejs%2Fdownload%2Fejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba"
integrity sha1-SGYSh1c9zFPjZsehrlLDoSDuybo=
electron-to-chromium@^1.3.723:
version "1.3.748"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.748.tgz#16638a8130f407ae5bf2fc168f2173574deb36c5"
integrity sha512-fmIKfYALVeEybk/L2ucdgt7jN3JsbGtg3K9pmF/MRWgkeADBI1VSAa5IzdG2gZwTxsnsrFtdMpOTSM5mrBRKVQ==
electron-to-chromium@^1.3.649:
version "1.3.712"
resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.712.tgz?cache=0&sync_timestamp=1618020179238&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.712.tgz#ae467ffe5f95961c6d41ceefe858fc36eb53b38f"
integrity sha1-rkZ//l+VlhxtQc7v6Fj8NutTs48=
element-ui@2.13.2:
version "2.13.2"
@ -4057,8 +4057,8 @@ es-to-primitive@^1.2.1:
escalade@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
resolved "https://registry.npm.taobao.org/escalade/download/escalade-3.1.1.tgz?cache=0&sync_timestamp=1602567261690&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fescalade%2Fdownload%2Fescalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha1-2M/ccACWXFoBdLSoLqpcBVJ0LkA=
escape-html@~1.0.3:
version "1.0.3"
@ -7322,10 +7322,10 @@ node-notifier@^5.4.2:
shellwords "^0.1.1"
which "^1.3.0"
node-releases@^1.1.71:
version "1.1.72"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
node-releases@^1.1.70:
version "1.1.71"
resolved "https://registry.npm.taobao.org/node-releases/download/node-releases-1.1.71.tgz?cache=0&sync_timestamp=1614110416015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-releases%2Fdownload%2Fnode-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb"
integrity sha1-yxM0sXmJaxyJ7P3UtyX7e738fbs=
nopt@^5.0.0:
version "5.0.0"

@ -39,5 +39,48 @@ yarn lint
```
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).
## deploy nginx
#### short deploy nginx
```shell
location / {
try_files $uri $uri/ /short.html;
}
```
#### index.html
```html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="refresh" content="3;URL=https://flyapps.cn">
<title>FLY分发平台</title>
<style>
.container {
width: 60%;
margin: 10% auto 0;
background-color: #f0f0f0;
padding: 2% 5%;
border-radius: 10px
}
ul {
padding-left: 20px;
}
ul li {
line-height: 2.3
}
a {
color: #20a53a
}
</style>
</head>
<body>
<div class="container">
<h3>这是分发平台下载默认页,正在跳转 <a href="https://flyapps.cn">首页</a></h3>
</div>
</body>
</html>
```

File diff suppressed because it is too large Load Diff

@ -11,7 +11,7 @@
"dependencies": {
"ali-oss": "^6.5.1",
"app-info-parser": "^0.3.9",
"axios": "^0.19.0",
"axios": "^0.22.0",
"core-js": "^3.4.4",
"element-ui": "^2.13.0",
"js-base64": "^2.5.2",
@ -20,6 +20,7 @@
"vue": "^2.6.10",
"vue-clipboard2": "^0.3.1",
"vue-cookies": "^1.7.0",
"vue-cropper": "^0.5.7",
"vue-lazyload": "^1.3.3",
"vue-qr": "^2.3.0",
"vue-router": "^3.1.3",
@ -35,7 +36,6 @@
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.0.0",
"html-webpack-plugin": "^5.3.1",
"image-webpack-loader": "^7.0.1",
"script-ext-html-webpack-plugin": "^2.1.5",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-template-compiler": "^2.6.10",

@ -1,16 +0,0 @@
window.g = {
baseUrl: 'https://app.hehelucky.cn',
PaymentQuestionMsg:'如对充值订单有疑问,请联系 nineven@qq.com',
footer: {
copyright: 'Copyright © 2017-2020 第九系艾文 版权所有.',
ipcBeiAn: {
url: 'https://beian.miit.gov.cn',
text: '豫ICP备15004336号-3',
},
gongAnBeiAn: {
url: 'http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=41142202000049',
text: '豫公网安备 41142202000049号',
},
}
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

@ -2,10 +2,9 @@
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<script src="<%= BASE_URL %>config.js"></script>
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width,initial-scale=1.0" name="viewport">
<link href="<%= BASE_URL %>favicon.ico" rel="icon">
<script src="<%= BASE_URL %>gt.js"></script>
<title>FLY分发平台</title>
</head>

@ -2,10 +2,10 @@
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<script src="<%= BASE_URL %>config.js"></script>
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta content="width=device-width,initial-scale=1.0" name="viewport">
<link href="<%= BASE_URL %>favicon.ico" rel="icon">
<script src="<%= BASE_URL %>gt.js"></script>
<title>应用下载</title>
</head>
<body>

@ -1,87 +1,87 @@
<template>
<div id="app" ref="app">
<router-view></router-view>
</div>
<div id="app" ref="app">
<router-view/>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {}
},
}
export default {
name: 'app',
data() {
return {}
},
}
</script>
<style>
body {
background-color: #F6F6F6;
}
html, body {
height: 100%;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
.canvas {
position: fixed;
left: -0px;
top: 58px;
height: 100%;
z-index: -2;
width: 100%;
}
body {
background-color: #F6F6F6;
}
html, body {
height: 100%;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
.canvas {
position: fixed;
left: -0px;
top: 58px;
height: 100%;
z-index: -2;
width: 100%;
}
</style>

@ -1,75 +1,75 @@
<template>
<div id="download" ref="download">
<router-view></router-view>
</div>
<div id="download" ref="download">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'download',
data() {
return {}
},
}
export default {
name: 'download',
data() {
return {}
},
}
</script>
<style>
body {
background-color: #F6F6F6;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
body {
background-color: #F6F6F6;
}
@font-face {
font-family: "iconfont";
src: url('./assets/icon/iconfont.eot?t=1583637102320'); /* IE9 */
src: url('./assets/icon/iconfont.eot?t=1583637102320#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'),
url('./assets/icon/iconfont.woff?t=1583637102320') format('woff'),
url('./assets/icon/iconfont.ttf?t=1583637102320') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ url('./assets/icon/iconfont.svg?t=1583637102320#iconfont') format('svg'); /* iOS 4.1- */
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-android:before {
content: "\e61d";
}
.icon-android1:before {
content: "\e616";
}
.icon-android2:before {
content: "\e62a";
}
.icon-ios:before {
content: "\e60c";
}
.icon-IOS:before {
content: "\e68c";
}
.icon-ios1:before {
content: "\e601";
}
.icon-ios2:before {
content: "\e64b";
}
.icon-Android:before {
content: "\e6d4";
}
.icon-android3:before {
content: "\e625";
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

@ -1,119 +1,132 @@
<template>
<div ref="appbase">
<el-dialog
title="绑定下载页域名"
:close-on-click-modal="false"
:close-on-press-escape="false"
:visible.sync="bind_domain_sure"
width="666px">
<bind-domain transitionName="bind-user-domain"/>
</el-dialog>
<canvas ref="canvas" class="canvas" @mousemove="canvas_move" @mouseleave="canvas_leave"/>
<el-container>
<div style="margin: -5px 20px" v-if="$store.state.show_domain_msg">
<el-alert
center
type="error"
:closable="false"
effect="dark">
<div slot="title">
<span :underline="false"
class="domian-tip-bar">应用分发请绑定您自己的域名平台分发域名可能因不可违因素更换将导致您的应用无法访问</span>
<el-button size="medium" @click="bind_domain_sure=true">立即绑定</el-button>
</div>
</el-alert>
</div>
<el-header>
<FirHeader/>
</el-header>
<div class="pbody">
<router-view/>
</div>
<el-footer>
<el-divider/>
<FirFooter/>
</el-footer>
<el-tooltip placement="top" content="回到顶部">
<back-to-top :custom-style="myBackToTopStyle" :visibility-height="300" :back-position="50"
transition-name="fade"/>
</el-tooltip>
</el-container>
</div>
<div ref="appbase">
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:title="bind_domain_title"
:visible.sync="bind_domain_sure"
width="666px">
<bind-domain v-if="bind_domain_sure" :domain_type="bind_domain_type"
:transitionName="`bind-user-domain`+ bind_domain_type"/>
</el-dialog>
<canvas ref="canvas" class="canvas" @mouseleave="canvas_leave" @mousemove="canvas_move"/>
<el-container>
<div v-if="$store.state.show_domain_msg" style="margin: -5px 20px">
<el-alert
:closable="false"
center
effect="dark"
type="error">
<div slot="title">
<span class="domain-tip-bar">应用分发请绑定您自己的域名平台分发域名可能因不可违因素更换将导致您的应用无法访问</span>
<el-button size="small" @click="bind_domain_type=1,bind_domain_sure=true">立即绑定</el-button>
</div>
</el-alert>
</div>
<el-header>
<FirHeader/>
</el-header>
<div class="pbody">
<router-view/>
</div>
<el-footer>
<el-divider/>
<FirFooter/>
</el-footer>
<el-tooltip content="回到顶部" placement="top">
<back-to-top :back-position="50" :custom-style="myBackToTopStyle" :visibility-height="300"
transition-name="fade"/>
</el-tooltip>
</el-container>
</div>
</template>
<script>
import FirHeader from "@/components/FirHeader";
import FirFooter from "@/components/FirFooter";
import BackToTop from "@/components/base/BackToTop";
import BindDomain from "@/components/base/BindDomain";
import {show_beautpic,} from "@/utils";
import FirHeader from "@/components/FirHeader";
import FirFooter from "@/components/FirFooter";
import BackToTop from "@/components/base/BackToTop";
import BindDomain from "@/components/base/BindDomain";
import {show_beautpic,} from "@/utils";
export default {
name: "FirBase",
components: {FirFooter, FirHeader, BackToTop, BindDomain},
data() {
return {
mousePosition: {},
bind_domain_sure: false,
active: 1,
domain_name: '',
bind_status: false,
domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}],
myBackToTopStyle: {
right: '80px',
bottom: '100px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px',
background: '#e7eaf1'
}
}
},
mounted() {
let canvas = this.$refs.canvas;
show_beautpic(this, canvas, 200);
}, watch: {
'$store.state.userinfo': function () {
if (this.$store.state.userinfo.domain_name) {
this.$store.dispatch("dodomainshow", false);
} else {
this.$store.dispatch("dodomainshow", true);
}
},
'$store.state.domain_action': function () {
if (this.$store.state.domain_action) {
this.$store.dispatch("dodomainaction", false);
this.bind_domain_sure = true;
}
},
}
, methods: {
canvas_move(e) {
this.mousePosition.x = e.pageX;
this.mousePosition.y = e.pageY;
},
// eslint-disable-next-line no-unused-vars
canvas_leave(e) {
let canvas = this.$refs.canvas;
this.mousePosition.x = canvas.width / 2;
this.mousePosition.y = canvas.height / 2;
},
}
export default {
name: "FirBase",
components: {FirFooter, FirHeader, BackToTop, BindDomain},
data() {
return {
mousePosition: {},
bind_domain_sure: false,
bind_domain_title: '绑定下载页域名',
bind_domain_type: 1,
active: 1,
domain_name: '',
bind_status: false,
domain_tData: [{'type': 'CNAME', 'host': 'xxx', 'dns': 'demo.xxx.cn'}],
myBackToTopStyle: {
right: '80px',
bottom: '100px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px',
background: '#e7eaf1'
}
}
},
mounted() {
let canvas = this.$refs.canvas;
show_beautpic(this, canvas, 200);
}, watch: {
'$store.state.userinfo': function () {
if (this.$store.state.userinfo.domain_name) {
this.$store.dispatch("dodomainshow", false);
} else {
this.$store.dispatch("dodomainshow", true);
}
},
'$store.state.domain_action': function () {
if (this.$store.state.domain_action) {
if (this.$store.state.domain_action === 2) {
this.bind_domain_title = '绑定下载码域名';
this.bind_domain_type = 0
} else {
this.bind_domain_title = '绑定下载页域名';
this.bind_domain_type = 1
}
this.$store.dispatch("dodomainaction", 0);
this.bind_domain_sure = true;
}
},
}
, methods: {
canvas_move(e) {
this.mousePosition.x = e.pageX;
this.mousePosition.y = e.pageY;
},
// eslint-disable-next-line no-unused-vars
canvas_leave(e) {
let canvas = this.$refs.canvas;
this.mousePosition.x = canvas.width / 2;
this.mousePosition.y = canvas.height / 2;
},
}
}
</script>
<style scoped>
.pbody {
padding-top: 30px;
}
.pbody {
padding-top: 30px;
}
.domian-tip-bar {
line-height: 43px;
color: #fff;
font-weight: 500;
font-size: medium;
margin-right: 20px;
}
.domain-tip-bar {
line-height: 43px;
color: #fff;
font-weight: 500;
font-size: medium;
margin-right: 20px;
}
/deep/ .el-alert {
padding: 2px 16px;
}
</style>

File diff suppressed because it is too large Load Diff

@ -1,64 +1,51 @@
<template>
<div class="ifooter">
<div class="ifooter">
<div style="width: 300px; margin: 0 auto; padding: 5px 0;">
<el-link :underline="false"> {{ footer.copyright }}</el-link>
</div>
<div style="width: 300px; margin: 0 auto; padding: 5px 0;">
<a style="display: inline-block; text-decoration: none; height: 20px; line-height: 20px;"
:href="footer.gongAnBeiAn.url"
target="_blank" rel="noopener">
<img style="float: left;" src="@/assets/beianlogo.png" alt=""/>
<el-link :underline="false"
style="float: left; height: 20px; line-height: 20px; margin: 0 0 0 5px; color: #939393;">
{{ footer.gongAnBeiAn.text }}
</el-link>
</a>
<div style="width: 300px; margin: 0 auto; padding: 5px 0;">
<el-link :underline="false"> {{ footer.copyright }}</el-link>
</div>
<div style="width: 300px; margin: 0 auto; padding: 5px 0;">
<a :href="footer.gongAnBeiAn.url"
rel="noopener"
style="display: inline-block; text-decoration: none; height: 20px; line-height: 20px;" target="_blank">
<img alt="" src="@/assets/beianlogo.png" style="float: left;"/>
<el-link :underline="false"
style="float: left; height: 20px; line-height: 20px; margin: 0 0 0 5px; color: #939393;">
{{ footer.gongAnBeiAn.text }}
</el-link>
</a>
</div>
</div>
<div>
<div :underline="false" style="width: 200px; margin: 0 auto; padding: 5px 0;">
<el-link :underline="false" :href="footer.ipcBeiAn.url" target="_blank"
rel="nofollow noopener">{{ footer.ipcBeiAn.text }}
</el-link>
</div>
</div>
<div>
<div style="width: 200px; margin: 0 auto; padding: 5px 0;">
<el-link :href="footer.ipcBeiAn.url" :underline="false" rel="nofollow noopener"
target="_blank">{{ footer.ipcBeiAn.text }}
</el-link>
</div>
</div>
</div>
</template>
<script>
export default {
name: "FirFooter",
data() {
return {
footer: {
copyright: '',
ipcBeiAn: {
url: '',
text: '',
},
gongAnBeiAn: {
url: '',
text: '',
},
}
}
}, methods: {}, created() {
}, mounted() {
this.footer = window.g.footer;
}
const footer = process.env.base_env.footer
export default {
name: "FirFooter",
data() {
return {
footer
}
}
}
</script>
<style scoped>
.ifooter {
margin: 20px auto 100px;
width: 1166px;
text-align: center;
font-size: 0;
border-radius: 10px;
}
.ifooter {
margin: 20px auto 100px;
width: 1166px;
text-align: center;
font-size: 0;
border-radius: 10px;
}
</style>

@ -1,187 +1,213 @@
<template>
<div style="background-color: rgba(162,193,230,0.18);">
<el-container class="navbar-wrapper">
<el-dialog
title="API Token"
:visible.sync="dialogVisible"
width="30%"
center
>
<span>可用于调用公开 API可用于登录 fly-cli请勿泄露您的 token</span>
<el-main>
<el-tooltip content="点击复制到剪贴板">
<el-link type="primary" v-if="token" :underline="false" v-clipboard:copy="token"
v-clipboard:success="copy_success">{{ token }}
</el-link>
</el-tooltip>
</el-main>
<span slot="footer" class="dialog-footer">
<div style="background-color: rgba(162,193,230,0.18);">
<el-container class="navbar-wrapper">
<el-dialog
:visible.sync="dialogVisible"
center
title="API Token"
width="30%"
>
<span>可用于调用公开 API可用于登录 fly-cli请勿泄露您的 token</span>
<el-main>
<el-tooltip content="点击复制到剪贴板">
<el-link v-if="token" v-clipboard:copy="token" v-clipboard:success="copy_success" :underline="false"
type="primary">{{ token }}
</el-link>
</el-tooltip>
</el-main>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="maketoken">重新生成</el-button>
</span>
</el-dialog>
<el-row :gutter="20">
<el-col :span="14" style="padding-top: 36px;margin-left: 60px">
<el-breadcrumb separator-class="el-icon-arrow-right" style="height: 60px;font-size: 20px">
<el-breadcrumb-item :to="{ name:'FirIndex' }">首页</el-breadcrumb-item>
<el-breadcrumb-item :to="{ name:'FirApps'}">我的应用</el-breadcrumb-item>
<el-breadcrumb-item v-if="this.route_info.label" :to="{ name:route_info.name}">{{
this.route_info.label }}
</el-breadcrumb-item>
<el-breadcrumb-item v-if="$store.state.currentapp.name"
:to="{name: 'FirAppInfostimeline', params: {id: $store.state.currentapp.app_id}}">
{{ $store.state.currentapp.name}}
</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
<el-col :span="2" :push="2" style="padding-top: 16px;">
<div class="block">
<el-avatar :size="66" :src="$store.state.userinfo.head_img"></el-avatar>
</div>
</el-col>
<el-col :span="4" :push="2" style="padding-top: 16px">
<el-dropdown style="padding-top: 12px;" @command="handleCommand">
<el-button type="primary" plain round>
{{$store.state.userinfo.first_name }}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userinfo">个人资料</el-dropdown-item>
<el-dropdown-item command="apitoken">API token</el-dropdown-item>
<el-dropdown-item command="setdomian">设置域名</el-dropdown-item>
<el-dropdown-item command="storage" v-if="$store.state.userinfo.storage_active">存储管理
</el-dropdown-item>
<el-dropdown-item command="supersign" v-if="$store.state.userinfo.supersign_active">超级签名
</el-dropdown-item>
<el-dropdown-item command="myorder">订单信息</el-dropdown-item>
<el-dropdown-item command="exit">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
</el-container>
</div>
</el-dialog>
<el-row :gutter="20">
<el-col :span="14" style="padding-top: 36px;margin-left: 60px">
<el-breadcrumb separator-class="el-icon-arrow-right" style="height: 60px;font-size: 20px">
<el-breadcrumb-item :to="{ name:'FirIndex' }">首页</el-breadcrumb-item>
<el-breadcrumb-item :to="{ name:'FirApps'}">我的应用</el-breadcrumb-item>
<el-breadcrumb-item v-if="this.route_info.label" :to="{ name:route_info.name}">{{
this.route_info.label
}}
</el-breadcrumb-item>
<el-breadcrumb-item v-if="$store.state.currentapp.name"
:to="{name: 'FirAppInfostimeline', params: {id: $store.state.currentapp.app_id}}">
{{ $store.state.currentapp.name }}
</el-breadcrumb-item>
</el-breadcrumb>
</el-col>
<el-col :push="2" :span="2" style="padding-top: 16px;">
<div class="block">
<el-avatar :size="66" :src="$store.state.userinfo.head_img"></el-avatar>
</div>
</el-col>
<el-col :push="2" :span="4" style="padding-top: 16px">
<el-dropdown style="padding-top: 12px;" @command="handleCommand">
<el-button plain round type="primary">
{{ $store.state.userinfo.first_name }}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="userinfo">个人资料</el-dropdown-item>
<el-dropdown-item command="apitoken">API token</el-dropdown-item>
<el-dropdown-item command="setdomian">设置域名</el-dropdown-item>
<el-dropdown-item command="download">下载配置</el-dropdown-item>
<el-dropdown-item command="setnotify">消息中心</el-dropdown-item>
<el-dropdown-item v-if="$store.state.userinfo.role>1" command="setadvert">宣传广告
</el-dropdown-item>
<el-dropdown-item v-if="$store.state.userinfo.storage_active" command="storage">存储管理
</el-dropdown-item>
<el-dropdown-item v-if="$store.state.userinfo.supersign_active" command="supersign">超级签名
</el-dropdown-item>
<el-dropdown-item command="myorder">订单信息</el-dropdown-item>
<el-dropdown-item command="contact">联系我们</el-dropdown-item>
<el-dropdown-item command="qrcode">下载码</el-dropdown-item>
<el-dropdown-item command="exit">退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
</el-container>
</div>
</template>
<script>
import {apitoken, logout} from '@/restful'
export default {
name: "FirHeader",
data() {
return {
current_user: {},
appName: '',
token: '',
dialogVisible: false,
route_info: {'name': '', 'label': ''},
}
}, methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
maketoken() {
apitoken(data => {
if (data.code === 1000) {
this.token = data.data.token;
this.$message({
type: 'success',
message: '重新生成成功!'
});
} else {
this.$message.error("失败了 " + data.msg)
}
}, {methods: 'PUT', token: this.token});
},
handleCommand(command) {
if (command === 'userinfo') {
this.$router.push({name: 'FirUserProfileInfo'})
} else if (command === 'storage') {
this.$router.push({name: 'FirUserStorage', params: {act: "change"}})
} else if (command === 'supersign') {
this.$store.dispatch('doucurrentapp', {});
this.$router.push({"name": 'FirSuperSignBase', params: {act: "iosdeveloper"}})
} else if (command === 'apitoken') {
apitoken(data => {
if (data.code === 1000) {
this.token = data.data.token;
this.dialogVisible = true;
} else {
this.dialogVisible = false;
this.$message.error("获取失败了 " + data.msg)
}
}, {methods: 'GET', token: this.token});
} else if (command === 'setdomian') {
this.$store.dispatch("dodomainaction", true);
} else if (command === 'myorder') {
this.$router.push({"name": 'FirUserOrders'})
} else if (command === 'exit') {
logout(data => {
if (data.code === 1000) {
this.$message.success("退出成功");
this.$cookies.remove("token");
this.$cookies.remove("auth_token");
this.$cookies.remove("username");
this.$cookies.remove("first_name");
this.$store.dispatch('doucurrentapp', {});
this.$store.dispatch('doUserinfo', {});
this.$router.push({name: 'FirLogin'});
} else {
this.$message.error("退出失败")
}
}, {})
}
},
init_route_name() {
if (this.$route.meta) {
this.route_info.label = this.$route.meta.label;
this.route_info.name = this.$route.name;
}
},
}, created() {
this.appName = this.$route.params.id
}, watch: {
$route: function () {
this.appName = this.$route.params.id;
this.init_route_name();
}
}, mounted() {
this.init_route_name();
}
import {apitoken, logout} from '@/restful'
export default {
name: "FirHeader",
data() {
return {
current_user: {},
appName: '',
token: '',
dialogVisible: false,
route_info: {'name': '', 'label': ''},
}
}, methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
maketoken() {
this.$confirm('该操作将导致老秘钥失效,确认重新生成新的密钥么?', '警告', {
type: 'warning'
})
// eslint-disable-next-line no-unused-vars
.then(res => {
apitoken(data => {
if (data.code === 1000) {
this.token = data.data.token;
this.$message({
type: 'success',
message: '重新生成成功!'
});
} else {
this.$message.error("失败了 " + data.msg)
}
}, {methods: 'PUT', token: this.token});
})
.catch(err => {
this.$message.error(err)
});
},
handleCommand(command) {
if (command === 'userinfo') {
this.$router.push({name: 'FirUserProfileInfo'})
} else if (command === 'storage') {
this.$router.push({name: 'FirUserStorage', params: {act: "change"}})
} else if (command === 'supersign') {
this.$store.dispatch('doucurrentapp', {});
this.$router.push({"name": 'FirSuperSignBase', params: {act: "iosdeveloper"}})
} else if (command === 'apitoken') {
apitoken(data => {
if (data.code === 1000) {
this.token = data.data.token;
this.dialogVisible = true;
} else {
this.dialogVisible = false;
this.$message.error("获取失败了 " + data.msg)
}
}, {methods: 'GET', token: this.token});
} else if (command === 'setdomian') {
this.$router.push({"name": 'FirUserDomain'})
} else if (command === 'download') {
this.$router.push({"name": 'FirUserDownload'})
} else if (command === 'setadvert') {
this.$router.push({"name": 'FirUserAdvert'})
} else if (command === 'myorder') {
this.$router.push({"name": 'FirUserOrders'})
} else if (command === 'setnotify') {
this.$router.push({"name": 'FirUserNotify'})
} else if (command === 'qrcode') {
this.$router.push({"name": 'FirUserQrcode'})
} else if (command === 'contact') {
this.$router.push({"name": 'FirContact'})
} else if (command === 'exit') {
logout(data => {
if (data.code === 1000) {
this.$message.success("退出成功");
this.$cookies.remove("token");
this.$cookies.remove("auth_token");
this.$cookies.remove("username");
this.$cookies.remove("first_name");
this.$store.dispatch('doucurrentapp', {});
this.$store.dispatch('doUserinfo', {});
this.$router.push({name: 'FirLogin'});
} else {
this.$message.error("退出失败")
}
}, {})
}
},
init_route_name() {
if (this.$route.meta) {
this.route_info.label = this.$route.meta.label;
this.route_info.name = this.$route.name;
}
},
}, created() {
this.appName = this.$route.params.id
}, watch: {
$route: function () {
this.appName = this.$route.params.id;
this.init_route_name();
}
}, mounted() {
this.init_route_name();
}
}
</script>
<style scoped>
.el-container, .el-row {
margin: 10px auto;
width: 1166px;
}
.el-container, .el-row {
margin: 10px auto;
width: 1166px;
}
.navbar-wrapper {
font-size: 0;
border-radius: 10px;
}
.navbar-wrapper {
font-size: 0;
border-radius: 10px;
}
.el-dropdown {
vertical-align: top;
}
.el-dropdown {
vertical-align: top;
}
.el-dropdown + .el-dropdown {
margin-left: 15px;
}
.el-dropdown + .el-dropdown {
margin-left: 15px;
}
.el-icon-arrow-down {
font-size: 12px;
}
.el-icon-arrow-down {
font-size: 12px;
}
</style>

@ -1,317 +1,473 @@
<template>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button round icon="el-icon-arrow-left" @click="$router.go(-1)"/>
<el-button round icon="el-icon-s-home" @click="$router.push({name:'FirIndex'})"/>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button icon="el-icon-arrow-left" round @click="$router.go(-1)"/>
<el-button icon="el-icon-s-home" round @click="$router.push({name:'FirIndex'})"/>
</div>
<el-header>
<div>
<span>登录</span>
</div>
</el-header>
<el-main>
<el-form ref="form" :model="form">
<el-tabs v-model="activeName">
<el-tab-pane v-if="allow_ways.up" label="用户名登录" name="username">
<el-form-item>
<el-input v-model="form.email" autofocus clearable placeholder="用户名"
prefix-icon="el-icon-user"/>
</el-form-item>
</el-tab-pane>
<el-tab-pane v-if="allow_ways.sms || allow_ways.email" :label="rutitle+'登录'" name="smsemail">
<el-form-item>
<el-input v-model="form.email" :placeholder="rutitle" autofocus clearable
prefix-icon="el-icon-user"/>
</el-form-item>
</el-tab-pane>
</el-tabs>
<el-form-item v-if="is_captcha">
<el-input v-model="form.password" clearable placeholder="密码"
prefix-icon="el-icon-lock"
show-password @keyup.enter.native="onSubmit"/>
</el-form-item>
<el-form-item v-else>
<el-input v-model="form.password" clearable placeholder="密码"
prefix-icon="el-icon-lock" show-password/>
</el-form-item>
<el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px">
<el-col :span="16">
<el-input v-model="form.verify_code" clearable maxlength="6"
placeholder="请输入验证码" @keyup.enter.native="onSubmit"/>
</el-col>
<el-col :span="8">
<el-image
:src="captcha.captcha_image"
fit="contain"
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item>
<el-button :disabled="login_disable" type="danger" @click="onSubmit">登录</el-button>
</el-form-item>
<el-form-item v-if="register_enable">
<el-button plain type="primary" @click="onRegister">注册</el-button>
</el-form-item>
<el-form-item>
<el-link :underline="false" plain @click="$router.push({name: 'FirResetPwd'})">忘记密码</el-link>
</el-form-item>
<div v-if="allow_ways.third && JSON.stringify(allow_ways.third).indexOf('true')!==-1" class="other-way">
<hr>
<el-popover
v-model="showuserlist"
placement="top"
title="该微信绑定了多个用户,请选择账户,并点击登录"
trigger="manual"
width="400">
<el-row style="margin-top: 20px">
<el-col v-for="userinfo in userinfo_list" :key="userinfo.uid" :span="8" style="margin-left: 40px">
<div @click="weixinlogin(userinfo)">
<el-card :body-style="{textAlign:'center',padding:'8px',width:'120px'}" shadow="hover">
<!-- <el-image :src="userinfo.head_img"></el-image>-->
<el-avatar :size="100" :src="userinfo.head_img"></el-avatar>
<div style="margin: 5px 0 5px">
<span>{{ userinfo.first_name }}</span>
</div>
</el-card>
</div>
</el-col>
</el-row>
<span slot="reference" class="info">或使用以下账户登录</span>
</el-popover>
<el-popover
v-if="allow_ways.third && allow_ways.third.wxp"
v-model="wx_visible"
placement="top"
title="微信扫码关注公众号登录"
trigger="manual">
<div>
<el-image :src="wx_login_qr_url" style="width: 176px;height: 166px"/>
</div>
<el-button slot="reference"
size="small" style="color: #1fc939;border: 1px solid rgba(31,201,57,.5); width: 110px"
@click="wxLogin">微信
</el-button>
</el-popover>
</div>
<el-header>
</el-form>
<div>
<span>登录</span>
</div>
</el-header>
<el-main>
<el-form ref="form" :model="form">
<el-tabs v-model="activeName">
<el-tab-pane label="用户名登录" name="username" v-if="allow_ways.up">
<el-form-item>
<el-input v-model="form.email" prefix-icon="el-icon-user" placeholder="用户名" autofocus
clearable/>
</el-form-item>
</el-tab-pane>
<el-tab-pane :label="rutitle+'登录'" name="smsemail" v-if="allow_ways.sms || allow_ways.email">
<el-form-item>
<el-input v-model="form.email" prefix-icon="el-icon-user" :placeholder="rutitle" autofocus
clearable/>
</el-form-item>
</el-tab-pane>
</el-tabs>
<el-form-item v-if="is_cptch">
<el-input v-model="form.password" prefix-icon="el-icon-lock" placeholder="密码"
@keyup.enter.native="onSubmit"
show-password clearable/>
</el-form-item>
<el-form-item v-else>
<el-input v-model="form.password" prefix-icon="el-icon-lock" placeholder="密码"
show-password clearable/>
</el-form-item>
<el-form-item style="height: 40px" v-if="cptch.cptch_image">
<el-row style="height: 40px">
<el-col :span="16">
<el-input placeholder="请输入验证码" v-model="form.authcode" maxlength="6"
@keyup.enter.native="onSubmit" clearable/>
</el-col>
<el-col :span="8">
<el-image
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px"
:src="cptch.cptch_image"
fit="contain" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item>
<el-button type="danger" :disabled="login_disable" @click="onSubmit">登录</el-button>
</el-form-item>
<el-form-item v-if="register_enable">
<el-button type="primary" @click="onRegister" plain>注册</el-button>
</el-form-item>
<el-form-item>
<el-link :underline="false" @click="$router.push({name: 'FirResetPwd'})" plain>忘记密码</el-link>
</el-form-item>
</el-form>
</el-main>
</el-container>
</el-main>
</el-container>
</template>
<script>
import {loginFun, set_auth_token} from "@/restful";
import {checkEmail, checkphone, geetest} from "@/utils";
export default {
name: "FirLogin",
data() {
return {
form: {
email: '',
password: '',
authcode: ''
},
cptch: {"cptch_image": '', "cptch_key": '', "length": 8},
activeName: 'username',
allow_ways: {},
rutitle: '',
rctitle: '',
register_enable: false,
login_disable: false,
}
},
methods: {
is_cptch() {
let cptch_flag = this.form.authcode.length === this.cptch.length;
if (this.cptch.cptch_key === '' || !this.cptch.cptch_key) {
cptch_flag = true
}
return cptch_flag
},
onSubmit() {
let email = this.form.email;
let password = this.form.password;
let authcode = this.form.authcode;
let login_type = 'up';
let cptch_flag = this.form.authcode.length === this.cptch.length;
if (this.cptch.cptch_key === '' || !this.cptch.cptch_key) {
cptch_flag = true
}
if (cptch_flag) {
if (this.activeName === "username") {
if (email.length < 6) {
this.$message({
message: '用户名至少6位',
type: 'error'
});
return
}
} else if (this.activeName === "smsemail") {
let checkp = checkphone(this.form.email);
let checke = checkEmail(this.form.email);
if (!checkp && !checke) {
this.$message({
message: '邮箱或者手机号输入有误',
type: 'error'
});
return
}
if (checkp) {
login_type = 'sms';
} else if (checke) {
login_type = 'email';
}
} else {
this.$message({
message: '未知登录方式',
type: 'error'
});
return
}
if (password.length > 6) {
let params = {
"username": email,
"password": password,
"authcode": authcode,
"cptch_key": this.cptch.cptch_key,
"login_type": login_type,
};
this.login_disable = true;
if (this.cptch.geetest) {
geetest(this, params, (n_params) => {
this.do_login(n_params);
})
} else {
this.do_login(params)
}
} else {
this.$message({
message: '密码长度过短',
type: 'warning'
});
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
do_login(params) {
loginFun(data => {
if (data.code === 1000) {
this.$message({
message: '登录成功',
type: 'success'
});
this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token'], 3600 * 24 * 30);
this.$cookies.set("username", data.userinfo.username, 3600 * 24 * 30);
this.$cookies.set("first_name", data.userinfo.first_name, 3600 * 24 * 30);
this.$store.dispatch("doUserinfo", data.userinfo);
set_auth_token();
this.$router.push({name: 'FirApps'})
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
this.login_disable = false;
}, {
"methods": "POST",
"data": params
});
},
onRegister() {
this.$router.push({name: 'FirRegist'})
},
set_activename() {
if (this.allow_ways.up) {
this.activeName = 'username';
} else {
this.activeName = 'smsemail';
}
},
set_rtitle() {
this.rutitle = '';
if (this.allow_ways.sms) {
this.rutitle = this.rutitle + '手机号 ';
}
if (this.allow_ways.email) {
this.rutitle = this.rutitle + '邮箱 ';
}
this.rutitle = this.rutitle.trim().replace(' ', '或');
},
get_auth_code() {
loginFun(data => {
if (data.code === 1000) {
this.cptch = data.data;
this.allow_ways = data.data.login_type;
this.register_enable = data.data.register_enable;
this.form.authcode = '';
this.set_rtitle();
this.set_activename();
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods": "GET",
"data": {}
});
},
},
mounted() {
this.get_auth_code();
}, created() {
}
import {loginFun, wxLoginFun} from "@/restful";
import {geetest} from "@/utils";
import {checkEmail, checkphone, getRandomStr} from "@/utils/base/utils";
export default {
name: "FirLogin",
data() {
return {
form: {
email: '',
password: '',
verify_code: ''
},
captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
activeName: 'username',
allow_ways: {'third': {}},
rutitle: '',
rctitle: '',
register_enable: false,
login_disable: false,
wx_login_qr_url: '',
wx_visible: false,
loop_flag: false,
unique_key: '',
userinfo_list: [],
unique_wid: '',
showuserlist: false
}
},
methods: {
weixinlogin(userinfo) {
wxLoginFun(data => {
if (data.code === 1000) {
this.set_cookie_and_token(data);
} else if (data.code === 1005) {
this.$message.error(data.msg)
} else {
this.showuserlist = false
this.$message.error(data.msg)
}
}, {
'methods': 'PUT',
'data': {uid: userinfo.uid, token: userinfo.token, 'wid': this.unique_wid}
})
},
set_cookie_and_token(data) {
this.$cookies.remove("auth_token");
this.$cookies.set("token", data['token'], 3600 * 24 * 30);
this.$cookies.set("username", data.userinfo.username, 3600 * 24 * 30);
this.$cookies.set("first_name", data.userinfo.first_name, 3600 * 24 * 30);
this.$store.dispatch("doUserinfo", data.userinfo);
this.$router.push({name: 'FirApps'})
},
loop_get_wx_info(wx_login_ticket, c_count = 1, unique_key = getRandomStr()) {
if (wx_login_ticket && wx_login_ticket.length < 3) {
this.$message.error("获取登陆码失败,请稍后再试");
return
}
if (!this.wx_visible) {
return;
}
wxLoginFun(data => {
c_count += 1;
if (c_count > 30) {
return;
}
if (data.code === 1000) {
this.set_cookie_and_token(data);
} else if (data.code === 1005) {
this.wx_visible = false;
this.loop_flag = false;
this.$message({
message: data.msg,
type: 'error',
duration: 30000
});
} else if (data.code === 1004) {
this.loop_flag = false;
} else if (data.code === 1006) {
return this.loop_get_wx_info(wx_login_ticket, c_count, unique_key)
} else if (data.code === 2000) {
this.wx_visible = false;
this.loop_flag = false;
this.userinfo_list = data.data;
this.unique_wid = data.wid;
this.showuserlist = true
}
}, {
"methods": "POST",
data: {"ticket": wx_login_ticket, "unique_key": unique_key}
})
// }, 3000)
},
wxLogin() {
this.wx_visible = !this.wx_visible;
this.wx_login_qr_url = '';
if (this.wx_visible) {
wxLoginFun(data => {
if (data.code === 1000) {
this.wx_login_qr_url = data.data.qr;
this.loop_flag = true;
this.loop_get_wx_info(data.data.ticket);
} else {
this.$message.error(data.msg);
this.wx_visible = false;
this.loop_flag = false;
}
}, {
"methods": "GET", "data": {"unique_key": this.unique_key}
})
} else {
this.loop_flag = false;
}
},
is_captcha() {
let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
return captcha_flag
},
onSubmit() {
let email = this.form.email;
let password = this.form.password;
let verify_code = this.form.verify_code;
let login_type = 'up';
let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
if (captcha_flag) {
if (this.activeName === "username") {
if (email.length < 6) {
this.$message({
message: '用户名至少6位',
type: 'error'
});
return
}
} else if (this.activeName === "smsemail") {
let checkp = checkphone(this.form.email);
let checke = checkEmail(this.form.email);
if (!checkp && !checke) {
this.$message({
message: '邮箱或者手机号输入有误',
type: 'error'
});
return
}
if (checkp) {
login_type = 'sms';
} else if (checke) {
login_type = 'email';
}
} else {
this.$message({
message: '未知登录方式',
type: 'error'
});
return
}
if (password.length >= 6) {
let params = {
"username": email,
"password": password,
"verify_code": verify_code,
"captcha_key": this.captcha.captcha_key,
"login_type": login_type,
};
this.login_disable = true;
if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => {
this.do_login(n_params);
})
} else {
this.do_login(params)
}
} else {
this.$message({
message: '密码长度过短',
type: 'warning'
});
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
do_login(params) {
loginFun(data => {
if (data.code === 1000) {
this.$message({
message: '登录成功',
type: 'success'
});
this.set_cookie_and_token(data);
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
this.login_disable = false;
}, {
"methods": "POST",
"data": params
});
},
onRegister() {
this.$router.push({name: 'FirRegist'})
},
set_activename() {
if (this.allow_ways.up) {
this.activeName = 'username';
} else {
this.activeName = 'smsemail';
}
},
set_rtitle() {
this.rutitle = '';
if (this.allow_ways.sms) {
this.rutitle = this.rutitle + '手机号 ';
}
if (this.allow_ways.email) {
this.rutitle = this.rutitle + '邮箱 ';
}
this.rutitle = this.rutitle.trim().replace(' ', '或');
},
get_auth_code() {
loginFun(data => {
if (data.code === 1000) {
this.auth_rules = data.data.auth_rules
this.captcha = this.auth_rules.captcha;
this.allow_ways = data.data.login_type;
this.register_enable = data.data.register_enable;
this.form.verify_code = '';
this.set_rtitle();
this.set_activename();
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods": "GET",
"data": {}
});
},
},
mounted() {
this.get_auth_code();
this.unique_key = getRandomStr();
}, created() {
}
}
</script>
<style scoped>
.el-container {
margin: 10px auto;
width: 1266px;
}
.el-header {
margin-top: 13%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
/*padding: 16px 20px;*/
width: 100%;
position: relative;
height: 50px;
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
.other-way {
position: relative;
text-align: center;
}
.other-way hr {
height: 1px;
margin: 30px 0;
border: 0;
background-color: #e4e7ed;
}
.other-way span.info {
font-size: 12px;
line-height: 1;
position: absolute;
top: -6px;
left: 50%;
padding: 0 10px;
transform: translate(-50%, 0);
color: #9ba3af;
}
.el-container {
margin: 10px auto;
width: 1266px;
}
.el-header {
margin-top: 13%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
/*padding: 16px 20px;*/
width: 100%;
position: relative;
height: 50px;
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
</style>

@ -1,389 +1,392 @@
<template>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button round icon="el-icon-arrow-left" @click="$router.go(-1)"/>
<el-button round icon="el-icon-s-home" @click="$router.push({name:'FirIndex'})"/>
</div>
<el-header>
<div>
<span>注册</span>
</div>
</el-header>
<el-main v-if="allow_r">
<el-form ref="form" :model="form">
<el-form-item v-if="allow_ways.code">
<el-input v-model="form.icode" prefix-icon="el-icon-postcard" placeholder="邀请码必填" clearable
/>
</el-form-item>
<el-form-item>
<el-input v-model="form.email" prefix-icon="el-icon-user" :placeholder="rutitle" autofocus
clearable/>
</el-form-item>
<el-form-item style="height: 40px" v-if="cptch.cptch_image">
<el-row style="height: 40px">
<el-col :span="16">
<el-input placeholder="请输入图片验证码" v-model="form.authcode" maxlength="6" clearable/>
</el-col>
<el-col :span="8">
<el-image
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px"
:src="cptch.cptch_image"
fit="contain" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="16">
<el-input v-model="form.seicode" prefix-icon="el-icon-mobile"
placeholder="验证码" clearable/>
</el-col>
<el-col :span="8">
<el-button type="info" @click="getphonecode" plain
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px">获取验证码
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-input v-model="form.password" prefix-icon="el-icon-lock" placeholder="密码"
show-password clearable/>
</el-form-item>
<el-form-item>
<el-input v-model="form.password2" prefix-icon="el-icon-lock" placeholder="确认密码"
show-password @keyup.enter.native="onRegist" clearable/>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item>
<el-button type="danger" @click="onRegist" :disabled="register_disable">注册</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onLogin" plain>我是老用户,要登录</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button icon="el-icon-arrow-left" round @click="$router.go(-1)"/>
<el-button icon="el-icon-s-home" round @click="$router.push({name:'FirIndex'})"/>
</div>
<el-header>
<div>
<span>注册</span>
</div>
</el-header>
<el-main v-if="allow_r">
<el-form ref="form" :model="form">
<el-form-item v-if="allow_ways.code">
<el-input v-model="form.icode" clearable placeholder="邀请码必填" prefix-icon="el-icon-postcard"
/>
</el-form-item>
<el-form-item>
<el-input v-model="form.email" :placeholder="rutitle" autofocus clearable
prefix-icon="el-icon-user"/>
</el-form-item>
<el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px">
<el-col :span="16">
<el-input v-model="form.verify_code" clearable maxlength="6" placeholder="请输入图片验证码"/>
</el-col>
<el-col :span="8">
<el-image
:src="captcha.captcha_image"
fit="contain"
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="16">
<el-input v-model="form.seicode" clearable
placeholder="验证码" prefix-icon="el-icon-mobile"/>
</el-col>
<el-col :span="8">
<el-button plain style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" type="info"
@click="getphonecode">获取验证码
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-input v-model="form.password" clearable placeholder="密码"
prefix-icon="el-icon-lock" show-password/>
</el-form-item>
<el-form-item>
<el-input v-model="form.password2" clearable placeholder="确认密码"
prefix-icon="el-icon-lock" show-password @keyup.enter.native="onRegist"/>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item>
<el-button :disabled="register_disable" type="danger" @click="onRegist">注册</el-button>
</el-form-item>
<el-form-item>
<el-button plain type="primary" @click="onLogin">我是老用户,要登录</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
</template>
<script>
import {getAuthTokenFun, registerFun} from "@/restful";
import {checkEmail, checkphone, geetest} from "@/utils";
export default {
name: "FirRegist",
data() {
return {
form: {
email: '',
password: '',
password2: '',
authcode: '',
srccode: '',
seicode: '',
authtoken: '',
icode: '',
auth_token: '',
},
cptch: {"cptch_image": '', "cptch_key": '', "length": 8},
allow_r: false,
allow_ways: {},
rutitle: '',
register_disable: false,
}
},
methods: {
set_rtitle() {
this.rutitle = '';
if (this.allow_ways.sms) {
this.rutitle = this.rutitle + '手机号 ';
}
if (this.allow_ways.email) {
this.rutitle = this.rutitle + '邮箱 ';
}
this.rutitle = this.rutitle.trim().replace(' ', '或');
},
get_auth_code() {
registerFun(data => {
if (data.code === 1000) {
let jdata = data.data;
if (jdata.enable) {
this.allow_r = true;
this.allow_ways = jdata.register_type;
this.form.authcode = '';
this.set_rtitle();
this.cptch = data.data;
} else {
this.allow_r = false;
this.$message({
message: "该服务器不允许注册",
type: 'error'
});
}
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods": "GET",
"data": {}
});
},
do_get_auth_token(params) {
getAuthTokenFun(data => {
if (data.code === 1000) {
this.$notify({
title: '验证码',
message: '您正在进行注册,验证码已经发送您',
type: 'success'
});
this.form.auth_token = data.data.auth_token;
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {"methods": 'POST', 'data': params})
},
getphonecode() {
let act = 'up';
if (!this.docheck()) {
return
}
if (checkphone(this.form.email)) {
act = 'sms'
}
if (checkEmail(this.form.email)) {
act = 'email'
}
let authcode = this.form.authcode;
let cptch_flag = authcode.length === this.cptch.length;
if (this.cptch.cptch_key === '' || !this.cptch.cptch_key) {
cptch_flag = true
}
if (cptch_flag) {
let picode = {
"authcode": authcode,
"cptch_key": this.cptch.cptch_key,
"icode": this.form.icode,
};
let params = {'act': act, 'target': this.form.email, 'ext': picode};
if (this.cptch.geetest) {
geetest(this, params, (n_params) => {
this.do_get_auth_token(n_params);
})
} else {
this.do_get_auth_token(params);
}
} else {
this.$message({
message: '图片验证码有误',
type: 'warning'
});
}
},
docheck() {
let checkp = checkphone(this.form.email);
let checke = checkEmail(this.form.email);
if (this.allow_ways.sms && this.allow_ways.email) {
if (!checke && !checkp) {
this.$message({
message: '请输入正确的邮箱地址或手机号码',
type: 'warning'
});
return 0
}
} else {
if (this.allow_ways.email) {
if (!checke) {
this.$message({
message: '请输入正确的邮箱地址',
type: 'warning'
});
return 0
}
}
if (this.allow_ways.sms) {
if (!checkp) {
this.$message({
message: '请输入正确的手机号码',
type: 'warning'
});
return 0
}
}
}
if (this.allow_ways.code && this.form.icode.length === 0) {
this.$message({
message: '请输入邀请码',
type: 'warning'
});
return 0
}
return 1
},
do_register(params) {
registerFun(data => {
if (data.code === 1000) {
this.$message({
message: '注册成功',
type: 'success'
});
this.$router.push({name: 'FirLogin'})
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
}, {
"methods": "POST",
"data": params
});
this.register_disable = false;
},
onRegist() {
let email = this.form.email;
let password = this.form.password;
let password2 = this.form.password2;
if (!this.docheck()) {
return
}
let authcode = this.form.authcode;
let cptch_flag = authcode.length === this.cptch.length;
if (this.cptch.cptch_key === '' || !this.cptch.cptch_key) {
cptch_flag = true
}
if (cptch_flag) {
if (password === password2 && password.length >= 6) {
let params = {
"username": email,
"password": password,
"password2": password2,
"auth_token": this.form.auth_token,
"auth_key": this.form.seicode
};
this.register_disable = true;
if (this.cptch.geetest) {
geetest(this, params, (n_params) => {
this.do_register(n_params);
})
} else {
this.do_register(params);
}
} else {
this.$message({
message: '密码不一致或者密码长度小于6',
type: 'warning'
});
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
onLogin() {
this.$router.push({name: 'FirLogin'})
},
import {getAuthTokenFun, registerFun} from "@/restful";
import {geetest} from "@/utils";
import {checkEmail, checkphone} from "@/utils/base/utils";
export default {
name: "FirRegist",
data() {
return {
form: {
email: '',
password: '',
password2: '',
verify_code: '',
srccode: '',
seicode: '',
authtoken: '',
icode: '',
auth_token: '',
},
captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
allow_r: false,
allow_ways: {},
rutitle: '',
register_disable: false,
}
},
methods: {
set_rtitle() {
this.rutitle = '';
if (this.allow_ways.sms) {
this.rutitle = this.rutitle + '手机号 ';
}
if (this.allow_ways.email) {
this.rutitle = this.rutitle + '邮箱 ';
}
this.rutitle = this.rutitle.trim().replace(' ', '或');
},
get_auth_code() {
registerFun(data => {
if (data.code === 1000) {
let jdata = data.data;
if (jdata.enable) {
this.allow_r = true;
this.allow_ways = jdata.register_type;
this.form.verify_code = '';
this.set_rtitle();
this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
} else {
this.allow_r = false;
this.$message({
message: "该服务器不允许注册",
type: 'error'
});
}
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
,
created() {
}, mounted() {
this.get_auth_code();
}, {
"methods": "GET",
"data": {}
});
},
do_get_auth_token(params) {
getAuthTokenFun(data => {
if (data.code === 1000) {
this.$notify({
title: '验证码',
message: '您正在进行注册,验证码已经发送您',
type: 'success'
});
this.form.auth_token = data.data.auth_token;
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}
}, {"methods": 'POST', 'data': params})
},
getphonecode() {
let act = 'up';
if (!this.docheck()) {
return
}
if (checkphone(this.form.email)) {
act = 'sms'
}
if (checkEmail(this.form.email)) {
act = 'email'
}
let verify_code = this.form.verify_code;
let captcha_flag = verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
if (captcha_flag) {
let picode = {
"verify_code": verify_code,
"captcha_key": this.captcha.captcha_key,
"icode": this.form.icode,
};
let params = {'act': act, 'target': this.form.email, 'ext': picode};
if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => {
this.do_get_auth_token(n_params);
})
} else {
this.do_get_auth_token(params);
}
} else {
this.$message({
message: '图片验证码有误',
type: 'warning'
});
}
},
docheck() {
let checkp = checkphone(this.form.email);
let checke = checkEmail(this.form.email);
if (this.allow_ways.sms && this.allow_ways.email) {
if (!checke && !checkp) {
this.$message({
message: '请输入正确的邮箱地址或手机号码',
type: 'warning'
});
return 0
}
} else {
if (this.allow_ways.email) {
if (!checke) {
this.$message({
message: '请输入正确的邮箱地址',
type: 'warning'
});
return 0
}
}
if (this.allow_ways.sms) {
if (!checkp) {
this.$message({
message: '请输入正确的手机号码',
type: 'warning'
});
return 0
}
}
}
if (this.allow_ways.code && this.form.icode.length === 0) {
this.$message({
message: '请输入邀请码',
type: 'warning'
});
return 0
}
return 1
},
do_register(params) {
registerFun(data => {
if (data.code === 1000) {
this.$message({
message: '注册成功',
type: 'success'
});
this.$router.push({name: 'FirLogin'})
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
}, {
"methods": "POST",
"data": params
});
this.register_disable = false;
},
onRegist() {
let email = this.form.email;
let password = this.form.password;
let password2 = this.form.password2;
if (!this.docheck()) {
return
}
let verify_code = this.form.verify_code;
let captcha_flag = verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
if (captcha_flag) {
if (password === password2 && password.length >= 6) {
let params = {
"username": email,
"password": password,
"password2": password2,
"auth_token": this.form.auth_token,
"auth_key": this.form.seicode
};
this.register_disable = true;
if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => {
this.do_register(n_params);
})
} else {
this.do_register(params);
}
} else {
this.$message({
message: '密码不一致或者密码长度小于6',
type: 'warning'
});
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
onLogin() {
this.$router.push({name: 'FirLogin'})
},
}
,
created() {
}, mounted() {
this.get_auth_code();
}
}
</script>
<style scoped>
.el-container {
margin: 10px auto;
width: 1266px;
}
.el-header {
margin-top: 13%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
width: 100%;
position: relative;
height: 50px;
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
.el-container {
margin: 10px auto;
width: 1266px;
}
.el-header {
margin-top: 13%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
width: 100%;
position: relative;
height: 50px;
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
</style>

@ -1,235 +1,280 @@
<template>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button round icon="el-icon-arrow-left" @click="$router.go(-1)"/>
<el-button round icon="el-icon-s-home" @click="$router.push({name:'FirIndex'})"/>
</div>
<el-header>
<div>
<span>忘记密码</span>
</div>
</el-header>
<el-main>
<el-form ref="form" :model="form">
<el-form-item>
<el-input v-model="form.email" prefix-icon="el-icon-user" placeholder="邮箱" autofocus
clearable/>
</el-form-item>
<el-form-item style="height: 40px" v-if="cptch.cptch_image">
<el-row style="height: 40px">
<el-col :span="16">
<el-input placeholder="请输入验证码" v-model="form.authcode" maxlength="6"
@keyup.enter.native="onSubmit" clearable/>
</el-col>
<el-col :span="8">
<el-image
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px"
:src="cptch.cptch_image"
fit="contain" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item style="margin-top: 30px">
<el-button type="danger" :disabled="login_disable" @click="onSubmit">发送重置密码邮件</el-button>
</el-form-item>
<el-form-item style="margin-top: 30px">
<el-button type="primary" @click="onLogin">我是老用户,要登录</el-button>
</el-form-item>
<el-form-item v-if="register_enable" style="margin-top: 30px">
<el-button type="primary" @click="onRegister" plain>注册</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
<el-container>
<div style="margin: 10px 0 10px 0 ;position:absolute;right:20px;top:auto;">
<el-button icon="el-icon-arrow-left" round @click="$router.go(-1)"/>
<el-button icon="el-icon-s-home" round @click="$router.push({name:'FirIndex'})"/>
</div>
<el-header>
<div>
<span>忘记密码</span>
</div>
</el-header>
<el-main>
<el-form ref="form" :model="form">
<el-form-item>
<el-input v-model="form.email" autofocus clearable placeholder="邮箱或手机号"
prefix-icon="el-icon-user"/>
</el-form-item>
<el-form-item v-if="captcha.captcha_image" style="height: 40px">
<el-row style="height: 40px">
<el-col :span="16">
<el-input v-model="form.verify_code" clearable maxlength="6"
placeholder="请输入验证码" @keyup.enter.native="onSubmit"/>
</el-col>
<el-col :span="8">
<el-image
:src="captcha.captcha_image"
fit="contain"
style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" @click="get_auth_code">
</el-image>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="16">
<el-input v-model="form.seicode" clearable
placeholder="验证码" prefix-icon="el-icon-mobile"/>
</el-col>
<el-col :span="8">
<el-button plain style="margin:0 4px;border-radius:4px;cursor:pointer;height: 40px" type="info"
@click="onGetCode">获取验证码
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<div id="captcha" ref="captcha"></div>
</el-form-item>
<el-form-item style="margin-top: 30px">
<el-button :disabled="login_disable" type="danger" @click="onReset">发送重置密码邮件</el-button>
</el-form-item>
<el-form-item style="margin-top: 30px">
<el-button type="primary" @click="onLogin">我是老用户,要登录</el-button>
</el-form-item>
<el-form-item v-if="register_enable" style="margin-top: 30px">
<el-button plain type="primary" @click="onRegister">注册</el-button>
</el-form-item>
</el-form>
</el-main>
</el-container>
</template>
<script>
import {loginFun} from "@/restful";
import {checkEmail, geetest} from "@/utils";
export default {
name: "FirResetPwd",
data() {
return {
form: {
email: '',
password: '',
authcode: ''
},
cptch: {"cptch_image": '', "cptch_key": '', "length": 8},
activeName: 'username',
allow_ways: {},
rutitle: '',
rctitle: '',
register_enable: false,
login_disable: false,
}
},
methods: {
is_cptch() {
let cptch_flag = this.form.authcode.length === this.cptch.length;
if (this.cptch.cptch_key === '' || !this.cptch.cptch_key) {
cptch_flag = true
}
return cptch_flag
},
onSubmit() {
let email = this.form.email;
let authcode = this.form.authcode;
let cptch_flag = this.form.authcode.length === this.cptch.length;
if (this.cptch.cptch_key === '' || !this.cptch.cptch_key) {
cptch_flag = true
}
if (cptch_flag) {
let checke = checkEmail(this.form.email);
if (!checke) {
this.$message({
message: '邮箱输入有误',
type: 'error'
});
return
}
let params = {
"username": email,
"authcode": authcode,
"cptch_key": this.cptch.cptch_key,
"login_type": 'reset',
};
this.login_disable = true;
if (this.cptch.geetest) {
geetest(this, params, (n_params) => {
this.do_login(n_params);
})
} else {
this.do_login(params)
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
do_login(params) {
loginFun(data => {
if (data.code === 1000) {
this.$message({
message: '密码重置成功,请登录邮箱查看邮件',
type: 'success'
});
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
this.login_disable = false;
}, {
"methods": "POST",
"data": params
});
},
onRegister() {
this.$router.push({name: 'FirRegist'})
},
onLogin() {
this.$router.push({name: 'FirLogin'})
},
get_auth_code() {
loginFun(data => {
if (data.code === 1000) {
this.cptch = data.data;
this.register_enable = data.data.register_enable;
this.form.authcode = '';
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods": "GET",
"data": {}
});
},
},
mounted() {
this.get_auth_code();
}, created() {
}
import {loginFun} from "@/restful";
import {geetest} from "@/utils";
import {checkEmail, checkphone} from "@/utils/base/utils";
export default {
name: "FirResetPwd",
data() {
return {
form: {
email: '',
password: '',
verify_code: '',
seicode: '',
auth_token: ''
},
captcha: {"captcha_image": '', "captcha_key": '', "length": 8},
auth_rules: {},
activeName: 'username',
allow_ways: {},
rutitle: '',
rctitle: '',
register_enable: false,
login_disable: false,
}
},
methods: {
onGetCode() {
this.form.auth_token = '';
this.form.seicode = '';
this.onSubmit();
},
onReset() {
if (this.form.auth_token && this.form.seicode && this.form.seicode.length > 3) {
this.onSubmit()
} else {
this.$message.error("输入有误,请检查")
}
},
is_captcha() {
let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
return captcha_flag
},
onSubmit() {
let email = this.form.email;
let verify_code = this.form.verify_code;
let captcha_flag = this.form.verify_code.length === this.captcha.length;
if (this.captcha.captcha_key === '' || !this.captcha.captcha_key) {
captcha_flag = true
}
if (captcha_flag) {
let checke = checkEmail(this.form.email);
let checkp = checkphone(this.form.email);
if (checke || checkp) {
let params = {
"username": email,
"verify_code": verify_code,
"captcha_key": this.captcha.captcha_key,
"login_type": 'reset',
};
let seicode = this.form.seicode;
let auth_token = this.form.auth_token;
if (seicode && seicode.length > 3) {
params['seicode'] = seicode
}
if (auth_token && auth_token.length > 3) {
params['auth_token'] = auth_token
}
this.login_disable = true;
if (this.auth_rules.geetest) {
geetest(this, this.form.email, params, (n_params) => {
this.do_login(n_params);
})
} else {
this.do_login(params)
}
} else {
this.$message({
message: '邮箱或手机号输入有误',
type: 'error'
});
}
} else {
this.$message({
message: '验证码有误',
type: 'warning'
});
}
},
do_login(params) {
loginFun(data => {
if (data.code === 1000) {
let msg = '密码重置成功,请登录邮箱或者手机短信查看';
if (data.data && data.data.auth_token) {
this.form.auth_token = data.data.auth_token;
msg = "验证码发送成功,请登录邮箱或者手机短信查看"
}
this.$message({
message: msg,
type: 'success'
});
} else {
this.$message({
message: data.msg,
type: 'error'
});
this.get_auth_code();
}
this.login_disable = false;
}, {
"methods": "POST",
"data": params
});
},
onRegister() {
this.$router.push({name: 'FirRegist'})
},
onLogin() {
this.$router.push({name: 'FirLogin'})
},
get_auth_code() {
loginFun(data => {
if (data.code === 1000) {
this.auth_rules = data.data.auth_rules;
this.captcha = this.auth_rules.captcha;
this.register_enable = data.data.register_enable;
this.form.verify_code = '';
} else {
this.$message({
message: data.msg,
type: 'error'
});
}
}, {
"methods": "GET",
"data": {}
});
},
},
mounted() {
this.get_auth_code();
}, created() {
}
}
</script>
<style scoped>
.el-container {
margin: 10px auto;
width: 1266px;
}
.el-container {
margin: 10px auto;
width: 1266px;
}
.el-header {
margin-top: 13%;
}
.el-header {
margin-top: 13%;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form {
max-width: 360px;
margin: 0 auto;
}
.el-form-item .el-button {
max-width: 360px;
/*padding: 16px 20px;*/
width: 100%;
position: relative;
height: 50px;
.el-form-item .el-button {
max-width: 360px;
/*padding: 16px 20px;*/
width: 100%;
position: relative;
height: 50px;
}
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header {
text-align: center;
overflow: hidden;
margin-bottom: 50px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div span {
font-size: 24px;
display: inline-block;
vertical-align: middle;
padding: 8px 40px
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div:before, .el-header div:after {
content: ' ';
display: inline-block;
vertical-align: middle;
width: 50%;
height: 1px;
background-color: #babfc3;
margin: 0 0 0 -50%
}
.el-header div {
text-align: center
}
.el-header div {
text-align: center
}
.el-header div:after {
margin: 0 -50% 0 0
}
.el-header div:after {
margin: 0 -50% 0 0
}
</style>

File diff suppressed because it is too large Load Diff

@ -1,395 +1,395 @@
<template>
<div class="main">
<div class="page-app app-info">
<div class="banner">
<div class="middle-wrapper">
<div @click="defaulttimeline">
<img ref="icon_url" :src="icon_url" class="appicon" style="width:100px; height:100px" alt="">
</div>
<div class="badges">
<el-tooltip content="复制到剪切板" placement="top">
<span class="bundleid short"
v-clipboard:copy="short_full_url"
<div class="main">
<div class="page-app app-info">
<div class="banner">
<div class="middle-wrapper">
<div @click="defaulttimeline">
<img ref="icon_url" :src="icon_url" alt="" class="appicon" style="width:100px; height:100px">
</div>
<div class="badges">
<el-tooltip content="复制到剪切板" placement="top">
<span v-clipboard:copy="appinfos.preview_url"
v-clipboard:success="copy_success"
>&nbsp;{{short_full_url }}</span>
</el-tooltip>
<span>{{ master_release.release_type |getapptype }}</span>
<el-tooltip content="下载量" placement="top">
<span><i class="el-icon-cloudy"/><b class="short">{{ appinfos.count_hits }}</b></span>
</el-tooltip>
<span class="bundleid ng-binding">BundleID<b class="ng-binding">&nbsp;&nbsp;{{ appinfos.bundle_id }}</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
<span class="short ng-scope" v-if="appinfos.issupersign">超级签</span>
<el-popover v-if="$store.state.userinfo.role === 3"
placement="right"
width="288">
<div style="text-align: center; margin: 0">
<vue-qr :margin="qrinfo.margin"
:logoSrc="icon_url" :logoScale="qrinfo.logoScale"
:logoCornerRadius="qrinfo.logoCornerRadius"
:correctLevel="qrinfo.correctLevel"
:text="short_url()" :size="266"
ref="qr">
</vue-qr>
<el-button type="primary" size="small" @click="save_qr()">保存本地</el-button>
</div>
<span class="short ng-scope" slot="reference">下载码</span>
</el-popover>
</div>
<div class="actions">
<el-button v-if="appinfos.status!==1" type="danger">该应用被封禁,请联系管理员</el-button>
<el-button v-else @click="appDownload" class="download" icon="el-icon-view">
预览
</el-button>
</div>
<div class="tabs-container">
<el-row :gutter="1">
<el-col :span="3">
<a class="" ref="baseinfo" @click="baseinfo"><i class="el-icon-document"/>基本信息</a>
</el-col>
<el-col :span="3">
<a class="" ref="security" @click="security"><i class="el-icon-set-up"/>应用管理</a>
</el-col>
<el-col :span="3">
<a class="" ref="combo" @click="combo"><i class="el-icon-copy-document"
style="transform:rotateX(180deg);"/>应用合并</a>
</el-col>
<el-col :span="3"
v-if="appinfos.type===1 || appinfos.issupersign ">
<a class="" ref="supersign" @click="supersign"><i
class="el-icon-ship"/>超级签名</a>
</el-col>
<el-col :span="3"
v-if="(appinfos.type===1 && master_release.release_type ===1) || appinfos.issupersign ">
<a class="" ref="devices" @click="devices"><i class="el-icon-mobile-phone"/>设备列表</a>
</el-col>
</el-row>
</div>
class="bundleid short"
>&nbsp;{{ appinfos.preview_url }}</span>
</el-tooltip>
<el-tooltip content="点击查看下载码">
<el-popover
placement="right"
width="288">
<div style="text-align: center; margin: 0">
<vue-qr ref="qr"
:correctLevel="qrinfo.correctLevel" :logoCornerRadius="qrinfo.logoCornerRadius"
:logoScale="qrinfo.logoScale"
:logoSrc="icon_url"
:margin="qrinfo.margin" :size="266"
:text="appinfos.preview_url">
</vue-qr>
<el-button size="small" type="primary" @click="save_qr()">保存本地</el-button>
</div>
</div>
<div>
<div class="block" style="margin-top: -46px;color: #d5f9f9">
<el-slider
v-model="$store.state.appInfoIndex[0]"
range
:show-tooltip="false"
:max="100">
</el-slider>
</div>
<el-container style="padding-top: 20px;max-width: 96%">
<router-view/>
</el-container>
</div>
<span slot="reference" class="short ng-scope">下载码</span>
</el-popover>
</el-tooltip>
<span>{{ master_release.release_type |getapptype }}</span>
<el-tooltip placement="top">
<div slot="content">总下载次数{{ appinfos.count_hits }}<br/>当天下载次数{{ appinfos.today_hits_count }}</div>
<span><i class="el-icon-cloudy"/><b class="short">{{
appinfos.count_hits
}} &nbsp;|&nbsp; {{ appinfos.today_hits_count }} </b></span>
</el-tooltip>
<span class="bundleid ng-binding">BundleID<b class="ng-binding">
<el-tooltip content="复制到剪切板" placement="top">
<span v-clipboard:copy="appinfos.bundle_id"
v-clipboard:success="copy_success"
class="bundleid short"
>&nbsp;{{ appinfos.bundle_id }}</span>
</el-tooltip>
</b></span>
<span class="version ng-scope">{{ master_release.minimum_os_version }}&nbsp; 或者高版本</span>
<span v-if="appinfos.issupersign" class="short ng-scope">超级签</span>
</div>
<div class="actions">
<el-button v-if="appinfos.status!==1" type="danger">该应用被封禁,请联系管理员</el-button>
<el-button v-else class="download" icon="el-icon-view" @click="appDownload(appinfos)">
预览
</el-button>
</div>
<div class="tabs-container">
<el-row :gutter="1">
<el-col :span="3">
<a ref="baseinfo" class="" @click="baseinfo"><i class="el-icon-document"/>基本信息</a>
</el-col>
<el-col :span="3">
<a ref="security" class="" @click="security"><i class="el-icon-set-up"/>应用管理</a>
</el-col>
<el-col :span="3">
<a ref="combo" class="" @click="combo"><i class="el-icon-copy-document"
style="transform:rotateX(180deg);"/>应用合并</a>
</el-col>
<el-col v-if="appinfos.type===1 || appinfos.issupersign "
:span="3">
<a ref="supersign" class="" @click="supersign"><i
class="el-icon-ship"/>超级签名</a>
</el-col>
<el-col v-if="(appinfos.type===1 && master_release.release_type ===1) || appinfos.issupersign "
:span="3">
<a ref="devices" class="" @click="devices"><i class="el-icon-mobile-phone"/>设备列表</a>
</el-col>
</el-row>
</div>
</div>
</div>
<div>
<div class="block" style="margin-top: -46px;color: #d5f9f9">
<el-slider
v-model="$store.state.appInfoIndex[0]"
:max="100"
:show-tooltip="false"
range>
</el-slider>
</div>
<el-container style="padding-top: 20px;max-width: 96%">
<router-view/>
</el-container>
</div>
</div>
</div>
</template>
<script>
import {apputils} from "@/restful";
import VueQr from 'vue-qr';
import {getUserInfoFun} from "@/utils";
export default {
name: "FirAppInfosBase",
components: {
VueQr
},
data() {
return {
qrinfo: {
logoScale: 0.3,
logoCornerRadius: 12,
correctLevel: 3,
margin: 20
},
icon_url: "",
appinfos: {status: 1},
master_release: {},
allapp: [],
activity: {
editing: false
},
short_full_url: '',
}
},
methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
save_qr() {
let dtype = "I";
if (this.master_release.release_type === 0) {
dtype = "A";
}
let a = document.createElement('a');
//
a.download = this.appinfos.name + '_' + dtype + "_下载码";
//url
a.href = this.$refs.qr.$el.src;
//
a.dispatchEvent(new MouseEvent('click'))
},
short_url() {
return location.origin + '/' + this.appinfos.short;
},
setfunactive(item, index) {
for (let key in this.$refs) {
if (key === "qr") continue;
if (key === item) {
if (this.$refs[key]) {
this.$refs[key].classList.add('active');
this.$store.dispatch('doappInfoIndex', [[index, index], [index, index]]);
}
} else {
if (this.$refs[key]) {
this.$refs[key].classList.remove('active');
}
}
}
},
appDownload() {
let routeData = this.$router.resolve({name: 'FirDownload', params: {short: this.appinfos.short}});
// window.open(routeData.href, '_blank');
let p_url = routeData.href;
// let p_url = location.origin + '/' + this.appinfos.short;
if (this.appinfos.preview_url && this.appinfos.preview_url.length > 6) {
p_url = this.appinfos.preview_url + p_url
}
window.open(p_url, '_blank', '');
},
defaulttimeline() {
this.setfunactive('timeline', 5);
this.$router.push({name: 'FirAppInfostimeline'});
},
baseinfo() {
this.setfunactive('baseinfo', 18);
this.$router.push({name: 'FirAppInfosbaseinfo'});
},
security() {
this.setfunactive('security', 31);
this.$router.push({name: 'FirAppInfossecurity'});
},
combo() {
this.setfunactive('combo', 44);
this.$router.push({name: 'FirAppInfoscombo'});
},
devices() {
this.setfunactive('devices', 70);
this.$router.push({name: 'FirAppInfosdevices'});
},
supersign() {
this.setfunactive('supersign', 57);
this.$router.push({name: 'FirAppInfossupersign'});
},
set_icon_url() {
this.icon_url = this.$store.state.currentapp.master_release.icon_url;
},
}, created() {
}, filters: {
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
},
computed: {}, mounted() {
getUserInfoFun(this);
apputils(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
this.short_full_url = this.appinfos.preview_url + "/" + this.appinfos.short;
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("Error");
}
}, {
"methods": "GET",
"app_id": this.$route.params.id
});
if (this.$store.state.currentapp.master_release) {
this.set_icon_url()
}
}, watch: {
'$store.state.currentapp.master_release.icon_url': function () {
this.set_icon_url()
},
'$store.state.currentapp': function () {
this.appinfos = this.$store.state.currentapp;
},
}, destroyed() {
this.$store.dispatch('doucurrentapp', {});
import {apputils} from "@/restful";
import VueQr from 'vue-qr';
import {getUserInfoFun} from "@/utils";
export default {
name: "FirAppInfosBase",
components: {
VueQr
},
data() {
return {
qrinfo: {
logoScale: 0.3,
logoCornerRadius: 12,
correctLevel: 3,
margin: 20
},
icon_url: "",
appinfos: {status: 1, preview_url: ''},
master_release: {},
allapp: [],
activity: {
editing: false
},
}
},
methods: {
copy_success() {
this.$message.success('复制剪切板成功');
},
save_qr() {
let dtype = "I";
if (this.master_release.release_type === 0) {
dtype = "A";
}
let a = document.createElement('a');
//
a.download = this.appinfos.name + '_' + dtype + "_下载码";
//url
a.href = this.$refs.qr.$el.src;
//
a.dispatchEvent(new MouseEvent('click'))
},
setfunactive(item, index) {
for (let key in this.$refs) {
if (key === "qr") continue;
if (key === item) {
if (this.$refs[key]) {
this.$refs[key].classList.add('active');
this.$store.dispatch('doappInfoIndex', [[index, index], [index, index]]);
}
} else {
if (this.$refs[key]) {
this.$refs[key].classList.remove('active');
}
}
}
},
appDownload(appinfo) {
window.open(appinfo.preview_url, '_blank', '');
},
defaulttimeline() {
this.setfunactive('timeline', 5);
this.$router.push({name: 'FirAppInfostimeline'});
},
baseinfo() {
this.setfunactive('baseinfo', 18);
this.$router.push({name: 'FirAppInfosbaseinfo'});
},
security() {
this.setfunactive('security', 31);
this.$router.push({name: 'FirAppInfossecurity'});
},
combo() {
this.setfunactive('combo', 44);
this.$router.push({name: 'FirAppInfoscombo'});
},
devices() {
this.setfunactive('devices', 70);
this.$router.push({name: 'FirAppInfosdevices'});
},
supersign() {
this.setfunactive('supersign', 57);
this.$router.push({name: 'FirAppInfossupersign'});
},
set_icon_url() {
this.icon_url = this.$store.state.currentapp.master_release.icon_url;
},
}, created() {
}, filters: {
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
},
computed: {}, mounted() {
getUserInfoFun(this);
apputils(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("Error");
}
}, {
"methods": "GET",
"app_id": this.$route.params.id
});
if (this.$store.state.currentapp.master_release) {
this.set_icon_url()
}
}, watch: {
'$store.state.currentapp.master_release.icon_url': function () {
this.set_icon_url()
},
'$store.state.currentapp': function () {
this.appinfos = this.$store.state.currentapp;
},
}, destroyed() {
this.$store.dispatch('doucurrentapp', {});
}
}
</script>
<style scoped>
.main {
margin: 40px auto 100px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.appicon {
overflow: hidden;
margin-left: 10px;
border-radius: 17.544%;
}
.page-app {
padding-bottom: 0;
width: 1166px;
margin: 10px auto 100px;
}
.page-app .banner {
padding-top: 60px;
padding-bottom: 40px;
border-bottom: 1px solid rgba(208, 208, 208, .5);
background-color: #ffffff;
border-radius: 10px;
}
.page-app .banner .actions {
position: absolute;
right: 0;
top: 0;
}
.page-app .banner .actions .download {
display: block;
min-width: 150px;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
margin: 0 12px 12px 12px
}
.page-app .banner .actions .download {
text-align: center;
text-decoration: none;
color: #3ab2a7;
border: 1px solid;
background-color: transparent
}
.page-app .banner .middle-wrapper {
position: relative
}
.page-app .badges {
margin-left: 160px;
font-size: 12px;
line-height: initial;
position: relative;
margin-top: -100px;
}
.page-app .badges > span {
position: relative;
display: inline-block;
margin-right: 8px;
padding: 4px 8px;
border: 1px solid;
border-radius: 5px
}
.page-app .badges .short {
color: #6f6ef8
}
.page-app .badges .short:hover {
cursor: pointer;
}
.page-app .badges b {
display: inline-block;
padding-left: 12px;
height: 100%;
font-weight: 400
}
.page-app .badges b:before {
position: absolute;
top: 0;
width: 0;
height: 100%;
border-left: 1px solid;
content: ' ';
margin-left: -6px
}
.page-app .tabs-container {
margin-top: 40px;
margin-left: 160px
}
.page-app .tabs-container .el-row {
margin: 10px auto;
}
.page-app .tabs-container .el-row .el-col {
margin-right: 30px;
border-left: 1px solid;
}
.page-app .tabs-container .el-row .el-col a {
display: block;
padding-left: 15px;
color: #599b1a;
text-decoration: none;
-webkit-transition: all .5s;
transition: all .5s
}
.page-app .tabs-container .el-row .el-col a > i {
display: block;
margin-bottom: 14px;
height: 22px;
font-size: 22px
}
.page-app .tabs-container .el-row .el-col a.active {
color: #4a4a4a
}
.page-app .tabs-container .el-row .el-col:nth-child(6) {
display: none
}
.page-app .has-devices .tabs-container .el-row .el-col:nth-child(6) {
display: inline-block
}
.main {
margin: 40px auto 100px;
position: relative;
padding-bottom: 1px;
color: #9b9b9b;
-webkit-font-smoothing: antialiased;
border-radius: 1%;
}
.appicon {
overflow: hidden;
margin-left: 10px;
border-radius: 17.544%;
}
.page-app {
padding-bottom: 0;
width: 1166px;
margin: 10px auto 100px;
}
.page-app .banner {
padding-top: 60px;
padding-bottom: 40px;
border-bottom: 1px solid rgba(208, 208, 208, .5);
background-color: #ffffff;
border-radius: 10px;
}
.page-app .banner .actions {
position: absolute;
right: 0;
top: 0;
}
.page-app .banner .actions .download {
display: block;
min-width: 150px;
padding: 10px 20px;
border-radius: 5px;
font-size: 14px;
margin: 0 12px 12px 12px
}
.page-app .banner .actions .download {
text-align: center;
text-decoration: none;
color: #3ab2a7;
border: 1px solid;
background-color: transparent
}
.page-app .banner .middle-wrapper {
position: relative
}
.page-app .badges {
margin-left: 160px;
font-size: 12px;
line-height: initial;
position: relative;
margin-top: -100px;
}
.page-app .badges > span {
position: relative;
display: inline-block;
margin-right: 8px;
padding: 4px 8px;
border: 1px solid;
border-radius: 5px
}
.page-app .badges .short {
color: #6f6ef8
}
.page-app .badges .short:hover {
cursor: pointer;
}
.page-app .badges b {
display: inline-block;
padding-left: 12px;
height: 100%;
font-weight: 400
}
.page-app .badges b:before {
position: absolute;
top: 0;
width: 0;
height: 100%;
border-left: 1px solid;
content: ' ';
margin-left: -6px
}
.page-app .tabs-container {
margin-top: 40px;
margin-left: 160px
}
.page-app .tabs-container .el-row {
margin: 10px auto;
}
.page-app .tabs-container .el-row .el-col {
margin-right: 30px;
border-left: 1px solid;
}
.page-app .tabs-container .el-row .el-col a {
display: block;
padding-left: 15px;
color: #599b1a;
text-decoration: none;
-webkit-transition: all .5s;
transition: all .5s
}
.page-app .tabs-container .el-row .el-col a > i {
display: block;
margin-bottom: 14px;
height: 22px;
font-size: 22px
}
.page-app .tabs-container .el-row .el-col a.active {
color: #4a4a4a
}
.page-app .tabs-container .el-row .el-col:nth-child(6) {
display: none
}
.page-app .has-devices .tabs-container .el-row .el-col:nth-child(6) {
display: inline-block
}
</style>

@ -1,293 +1,295 @@
<template>
<div style="margin-top: 20px;width: 100%;margin-left: 8%">
<el-form ref="form" label-width="80px">
<el-form-item label="应用ID" style="width: 56%">
<el-row>
<el-col :span="19">
<el-input v-model="currentapp.app_id" :disabled="true"/>
</el-col>
<el-col :span="4">
<el-button type="danger" plain @click="delApp"
style="margin:0 8px;border-radius:4px;cursor:pointer;height: 40px">删除应用
</el-button>
</el-col>
</el-row>
<div style="margin-top: 20px;width: 100%;margin-left: 8%">
<el-form ref="form" label-width="80px">
<el-form-item label="应用ID" style="width: 56%">
<el-row>
<el-col :span="19">
<el-input v-model="currentapp.app_id" :disabled="true"/>
</el-col>
<el-col :span="4">
<el-button plain style="margin:0 8px;border-radius:4px;cursor:pointer;height: 40px" type="danger"
@click="delApp">删除应用
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="使用空间" style="width: 56%">
<el-input v-model="currentapp.storage_used" :disabled="true" :value="currentapp.storage_used"/>
</el-form-item>
<el-form-item label="应用名称" style="width: 56%">
<el-input v-model="currentapp.name"/>
</el-form-item>
</el-form-item>
<el-form-item label="短链接" style="width: 56%">
<el-input v-model="currentapp.short" maxlength="16" show-word-limit type="text">
<template slot="prepend">{{ currentapp.preview_url }}/</template>
</el-input>
</el-form-item>
<el-form-item label="应用名称" style="width: 56%">
<el-input v-model="currentapp.name"/>
</el-form-item>
<el-form-item label="应用图标" style="width: 56%">
<el-upload
:before-upload="upload_app_icon"
:show-file-list="false"
accept=".png , .jpg , .jpeg"
action="#"
class="avatar-uploader">
<img v-if="currentapp.icon_url" :src="currentapp.icon_url"
alt="" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
<el-form-item label="应用描述" style="width: 66%">
<el-input v-model="currentapp.description" :autosize="{ minRows: 8, maxRows: 18}"
type="textarea"/>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="15">
<el-button plain size="medium" style="width: 160px;height: 50px;font-size: large;float: right"
type="primary"
@click="saveappinfo('save')">保存信息
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-divider/>
<el-form-item label="应用截图" style="width: 100%">
<el-form-item label="短链接" style="width: 56%">
<el-input v-model="currentapp.short" maxlength="16" show-word-limit type="text">
<template slot="prepend">{{ currentapp.preview_url }}/</template>
</el-input>
</el-form-item>
<div class="appdownload">
<el-tooltip v-for="(screen) in currentapp.screenshots" :key="screen.id" content="点击删除该应用截图"
effect="light" placement="top">
<el-image
:src="screen.url"
alt=""
class="screen-img"
fit="scale-down" @click="delscreen(screen.id)">
<div slot="error" class="image-slot" style="text-align: center;margin-top: 80%"
@click="delscreen(screen.id)">
<i class="el-icon-picture-outline"> 加载失败</i>
</div>
</el-image>
</el-tooltip>
<div v-if="currentapp.screenshots && currentapp.screenshots.length < 5"
class="screen-img">
<el-upload
:before-upload="upload_app_screen"
accept=".png , .jpg , .jpeg"
action="#"
drag>
<i class="el-icon-upload"/>
</el-upload>
</div>
<el-form-item label="应用图标" style="width: 56%">
<el-upload
class="avatar-uploader"
action="#"
:show-file-list="false"
accept=".png , .jpg , .jpeg"
:before-upload="upload_app_icon">
<img v-if="currentapp.icon_url" :src="currentapp.icon_url"
class="avatar" alt="">
<i v-else class="el-icon-plus avatar-uploader-icon"/>
</el-upload>
</el-form-item>
<el-form-item label="应用描述" style="width: 66%">
<el-input type="textarea" v-model="currentapp.description"
:autosize="{ minRows: 8, maxRows: 18}"/>
</el-form-item>
<el-form-item>
<el-row>
<el-col :span="15">
<el-button type="primary" plain @click="saveappinfo('save')" size="medium"
style="width: 160px;height: 50px;font-size: large;float: right">保存信息
</el-button>
</el-col>
</el-row>
</el-form-item>
<el-divider/>
<el-form-item label="应用截图" style="width: 100%">
</div>
<div class="appdownload">
<el-tooltip content="点击删除该应用截图" v-for="(screen) in currentapp.screenshots" :key="screen.id"
placement="top" effect="light">
<el-image
class="screen-img"
fit="scale-down"
@click="delscreen(screen.id)"
:src="screen.url" alt="">
<div slot="error" class="image-slot" @click="delscreen(screen.id)"
style="text-align: center;margin-top: 80%">
<i class="el-icon-picture-outline"> 加载失败</i>
</div>
</el-image>
</el-tooltip>
<div class="screen-img"
v-if="currentapp.screenshots && currentapp.screenshots.length < 5">
<el-upload
drag
action="#"
accept=".png , .jpg , .jpeg"
:before-upload="upload_app_screen">
<i class="el-icon-upload"/>
</el-upload>
</div>
</el-form-item>
</el-form>
</div>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import {apputils, getapppicurl, releaseapputils} from "@/restful"
import {AvatarUploadUtils} from "@/utils";
export default {
name: "FirAppInfosbaseinfo",
data() {
return {
currentapp: {},
imageUrl: "",
uploadconf: {},
uploadprocess: 0,
dialogImageUrl: '',
dialogVisible: false,
appinfos: {},
}
},
methods: {
// eslint-disable-next-line no-unused-vars
delscreen(screen_id) {
this.$confirm('确定要删除该应用截图?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
releaseapputils(data => {
if (data.code === 1000) {
this.$message.success('删除成功');
this.getappinfo();
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
}
}, {
"methods": "DELETE",
"app_id": this.$route.params.id,
"release_id": "screen",
"data": {'screen_id': screen_id}
})
import {apputils, getapppicurl, releaseapputils} from "@/restful"
import {AvatarUploadUtils} from "@/utils";
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
export default {
name: "FirAppInfosbaseinfo",
data() {
return {
currentapp: {},
imageUrl: "",
uploadconf: {},
uploadprocess: 0,
dialogImageUrl: '',
dialogVisible: false,
appinfos: {},
}
},
methods: {
// eslint-disable-next-line no-unused-vars
delscreen(screen_id) {
this.$confirm('确定要删除该应用截图?', '警告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
releaseapputils(data => {
if (data.code === 1000) {
this.$message.success('删除成功');
this.getappinfo();
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
}
}, {
"methods": "DELETE",
"app_id": this.$route.params.id,
"release_id": "screen",
"data": {'screen_id': screen_id}
})
delApp() {
//APP
this.$confirm('确认删除 ' + this.currentapp.name + ' ?')
// eslint-disable-next-line no-unused-vars
.then(res => {
const loadingobj = this.$loading({
lock: true,
text: '操作中,请耐心等待',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
apputils(data => {
loadingobj.close();
if (data.code === 1000) {
this.$message({
message: '删除成功',
type: 'success'
});
this.$router.push({name: 'FirApps'});
} else {
this.$message({
message: '删除失败,请联系管理员',
type: 'error'
});
}
}, {
"methods": "DELETE",
"app_id": this.currentapp.app_id
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
});
});
},
})
.catch(err => {
this.$message.error(err)
});
},
getappinfo() {
apputils(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("Error");
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id
delApp() {
//APP
this.$confirm('确认删除 ' + this.currentapp.name + ' ?')
// eslint-disable-next-line no-unused-vars
.then(res => {
const loadingobj = this.$loading({
lock: true,
text: '操作中,请耐心等待',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
apputils(data => {
loadingobj.close();
if (data.code === 1000) {
this.$message({
message: '删除成功',
type: 'success'
});
},
saveappinfo() {
apputils(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
this.$message.error('操作失败,' + data.msg);
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"description": this.currentapp.description,
"short": this.currentapp.short,
"name": this.currentapp.name,
}
this.$router.push({name: 'FirApps'});
} else {
this.$message({
message: '删除失败,请联系管理员',
type: 'error'
});
},
upload_app_icon(file) {
return this.beforeAvatarUpload(file, 'app')
},
upload_app_screen(file) {
return this.beforeAvatarUpload(file, 'screen')
},
beforeAvatarUpload(file, act) {
return AvatarUploadUtils(this, file, {
'app_id': this.currentapp.app_id,
'upload_key': file.name,
'ftype': act
// eslint-disable-next-line no-unused-vars
}, res => {
this.getappinfo();
});
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[18, 18], [18, 18]]);
if (!this.currentapp.app_id) {
this.currentapp = this.$store.state.currentapp;
}
this.uploadconf = {
"AuthHeaders": {"Authorization": this.$cookies.get("auth_token")}
};
},
watch: {
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
}
}, computed: {
getuppicurl() {
return getapppicurl(this.currentapp.app_id)
}
}
}, {
"methods": "DELETE",
"app_id": this.currentapp.app_id
});
})
.catch(err => {
this.$message.error(err)
});
},
getappinfo() {
apputils(data => {
if (data.code === 1000) {
this.appinfos = data.data;
this.master_release = data.data.master_release;
this.appinfos["icon_url"] = this.master_release.icon_url;
this.$store.dispatch('doucurrentapp', this.appinfos);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("Error");
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id
});
},
saveappinfo() {
apputils(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
this.$message.error('操作失败,' + data.msg);
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"description": this.currentapp.description,
"short": this.currentapp.short,
"name": this.currentapp.name,
}
});
},
upload_app_icon(file) {
return this.beforeAvatarUpload(file, 'app')
},
upload_app_screen(file) {
return this.beforeAvatarUpload(file, 'screen')
},
beforeAvatarUpload(file, act) {
return AvatarUploadUtils(this, file, {
'app_id': this.currentapp.app_id,
'upload_key': file.name,
'ftype': act
// eslint-disable-next-line no-unused-vars
}, res => {
this.getappinfo();
});
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[18, 18], [18, 18]]);
if (!this.currentapp.app_id) {
this.currentapp = this.$store.state.currentapp;
}
this.uploadconf = {
"AuthHeaders": {"Authorization": this.$cookies.get("auth_token")}
};
},
watch: {
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
}
}, computed: {
getuppicurl() {
return getapppicurl(this.currentapp.app_id)
}
}
}
</script>
<style scoped>
.appdownload /deep/ .el-upload-dragger {
background: #f1f1f1;
width: 155px;
height: 288px;
}
.appdownload /deep/ .el-upload-dragger {
background: #f1f1f1;
width: 155px;
height: 288px;
}
.appdownload /deep/ .el-icon-upload {
margin-top: 80%;
}
.appdownload /deep/ .el-icon-upload {
margin-top: 80%;
}
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
}
.screen-img {
width: 155px;
height: 288px;
margin-right: 20px;
background-color: #f1f1f1;
float: left
}
.screen-img {
width: 155px;
height: 288px;
margin-right: 20px;
background-color: #f1f1f1;
float: left
}
.screen-img:hover {
background-color: #c2dcf1;
}
.screen-img:hover {
background-color: #c2dcf1;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 10px;
display: block;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 10px;
display: block;
}
</style>

@ -1,370 +1,370 @@
<template>
<div class="apps-app-combo page-tabcontent ">
<div class="middle-wrapper">
<div class="request-wrapper" v-if="has_combo ">
<p class="lead text-center ng-scope">已经与 <b>{{ has_combo.name }}</b> 合并</p>
<table>
<tr>
<td><span class="type">{{ currentapp.type|getapptype}}</span></td>
<td/>
<td><span class="type">{{ has_combo.type|getapptype}}</span></td>
</tr>
<tr>
<td>
<div class="icon"><img v-if="cmaster_release" :src="cmaster_release.icon_url"
class="ng-isolate-scope" alt="">
</div>
</td>
<td><i class="icon-combo"/></td>
<td>
<div class="icon"><img v-if="hmaster_release" :src="hmaster_release.icon_url"
class="ng-isolate-scope" alt="">
</div>
</td>
</tr>
<tr>
<td class="actions" colspan="3">
<el-button @click="each_confirm" round>解除合并</el-button>
</td>
</tr>
</table>
</div>
<div v-else>
<div class="icon-container text-center">
<img v-if="cmaster_release" :src="cmaster_release.icon_url" height="128" width="128">
</div>
<div class="apps-list">
<div class="known-apps">
<p class="lead ng-binding">
<b>选择已有的应用进行合并</b>
</p>
<div class="apps">
<el-link v-if="comboapplists.length ===0 " :underline="false" type="danger">暂无可以合并的应用
</el-link>
<div :key="comboapp.app_id" @click="each_add(comboapp.app_id)" class="app ng-scope"
v-for="comboapp in comboapplists">
<div class="icon">
<img :src="comboapp.master_release|geticon_url" class="ng-isolate-scope" alt="">
</div>
<p class="ng-binding">{{ comboapp.name }}</p></div>
</div>
</div>
<span class="hr-vertical"/>
<div class="find-apps">
<p class="lead ">
<b>输入需要合并的应用的短链接或者名字</b>
</p>
<div class="form-group">
<el-input
@click="searchapps"
clearable
placeholder="输入短链接或者名字"
prefix-icon="el-icon-search"
v-model="searchKey"
/>
</div>
</div>
<div class="apps-app-combo page-tabcontent ">
<div class="middle-wrapper">
<div v-if="has_combo " class="request-wrapper">
<p class="lead text-center ng-scope">已经与 <b>{{ has_combo.name }}</b> 合并</p>
<table>
<tr>
<td><span class="type">{{ currentapp.type|getapptype }}</span></td>
<td/>
<td><span class="type">{{ has_combo.type|getapptype }}</span></td>
</tr>
<tr>
<td>
<div class="icon"><img v-if="cmaster_release" :src="cmaster_release.icon_url"
alt="" class="ng-isolate-scope">
</div>
</td>
<td><i class="icon-combo"/></td>
<td>
<div class="icon"><img v-if="hmaster_release" :src="hmaster_release.icon_url"
alt="" class="ng-isolate-scope">
</div>
</td>
</tr>
<tr>
<td class="actions" colspan="3">
<el-button round @click="each_confirm">解除合并</el-button>
</td>
</tr>
</table>
</div>
<div v-else>
<div class="icon-container text-center">
<img v-if="cmaster_release" :src="cmaster_release.icon_url" height="128" width="128">
</div>
<div class="apps-list">
<div class="known-apps">
<p class="lead ng-binding">
<b>选择已有的应用进行合并</b>
</p>
<div class="apps">
<el-link v-if="comboapplists.length ===0 " :underline="false" type="danger">暂无可以合并的应用
</el-link>
<div v-for="comboapp in comboapplists" :key="comboapp.app_id" class="app ng-scope"
@click="each_add(comboapp.app_id)">
<div class="icon">
<img :src="comboapp.master_release|geticon_url" alt="" class="ng-isolate-scope">
</div>
<p class="ng-binding">{{ comboapp.name }}</p></div>
</div>
</div>
<span class="hr-vertical"/>
<div class="find-apps">
<p class="lead ">
<b>输入需要合并的应用的短链接或者名字</b>
</p>
<div class="form-group">
<el-input
v-model="searchKey"
clearable
placeholder="输入短链接或者名字"
prefix-icon="el-icon-search"
@click="searchapps"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {apputils, getapps} from "@/restful";
export default {
name: "FirAppInfoscombo"
, data() {
return {
searchKey: '',
currentapp: {},
has_combo: {},
cmaster_release: {},
hmaster_release: {},
comboapplists: [],
orgapplists: [],
comboapp: {"master_release": ""}
}
}, methods: {
searchapps() {
let keysearch = this.searchKey.replace(/^\s+|\s+$/g, "");
let newapplists = [];
for (let i = 0; i < this.orgapplists.length; i++) {
if (this.orgapplists[i].short.search(keysearch) === 0 || this.orgapplists[i].name.search(keysearch) === 0) {
newapplists.push(this.orgapplists[i]);
}
}
if (keysearch === "") {
this.comboapplists = this.orgapplists.slice();
} else {
this.comboapplists = newapplists.slice();
}
},
each_add(hcombo_id) {
apputils(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.comboapplists = [];
apputils(data => {
let appinfos = data.data;
appinfos["icon_url"] = appinfos.master_release.icon_url;
this.$store.dispatch('doucurrentapp', appinfos);
if (data.code === 1000) {
this.$store.dispatch('doucurrentapp', data.data);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("Error");
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id
});
this.setData();
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
"hcombo_id": hcombo_id,
"action": "COMBO",
}
}
});
},
each_confirm() {
apputils(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
let currentapp = this.$store.state.currentapp;
this.has_combo = this.$store.state.currentapp.has_combo;
this.has_combo = null;
currentapp.has_combo = null;
this.hmaster_release = null;
this.$store.dispatch('doucurrentapp', currentapp);
this.getappiconFun();
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
"hcombo_id": this.has_combo.app_id,
"action": "UNCOMBO",
}
}
});
},
setData() {
this.currentapp = this.$store.state.currentapp;
this.cmaster_release = this.$store.state.currentapp.master_release;
if (this.$store.state.currentapp.has_combo) {
this.has_combo = this.$store.state.currentapp.has_combo;
this.hmaster_release = this.$store.state.currentapp.has_combo.master_release;
} else {
this.has_combo = null;
this.hmaster_release = null;
this.getappiconFun();
}
},
getappiconFun() {
let type = "android";
if (this.currentapp.type === 0) {
type = "ios"
}
getapps(data => {
if (data.code === 1000) {
this.comboapplists = data.data;
this.orgapplists = this.comboapplists.slice(); //
} else {
this.$router.push({name: 'FirLogin'});
}
}, {"type": type, "page": 1, "size": 999, "act": 'combo'});
import {apputils, getapps} from "@/restful";
export default {
name: "FirAppInfoscombo"
, data() {
return {
searchKey: '',
currentapp: {},
has_combo: {},
cmaster_release: {},
hmaster_release: {},
comboapplists: [],
orgapplists: [],
comboapp: {"master_release": ""}
}
}, methods: {
searchapps() {
let keysearch = this.searchKey.replace(/^\s+|\s+$/g, "");
let newapplists = [];
for (let i = 0; i < this.orgapplists.length; i++) {
if (this.orgapplists[i].short.search(keysearch) === 0 || this.orgapplists[i].name.search(keysearch) === 0) {
newapplists.push(this.orgapplists[i]);
}
}
if (keysearch === "") {
this.comboapplists = this.orgapplists.slice();
} else {
this.comboapplists = newapplists.slice();
}
},
each_add(hcombo_id) {
apputils(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
this.comboapplists = [];
apputils(data => {
let appinfos = data.data;
appinfos["icon_url"] = appinfos.master_release.icon_url;
this.$store.dispatch('doucurrentapp', appinfos);
if (data.code === 1000) {
this.$store.dispatch('doucurrentapp', data.data);
} else if (data.code === 1003) {
this.$router.push({name: 'FirApps'});
} else {
// eslint-disable-next-line no-console
console.log("Error");
}
}, filters: {
geticon_url(app) {
if (app) {
return app.icon_url
}
return ""
},
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[44, 44], [44, 44]]);
this.setData();
},
watch: {
'$store.state.currentapp': function () {
this.setData();
},
// eslint-disable-next-line no-unused-vars
searchKey: function (val, oldVal) {
this.searchapps()
},
}, {
"methods": "GET",
"app_id": this.currentapp.app_id
});
this.setData();
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
"hcombo_id": hcombo_id,
"action": "COMBO",
}
}
});
},
each_confirm() {
apputils(data => {
if (data.code === 1000) {
this.$message.success('操作成功');
let currentapp = this.$store.state.currentapp;
this.has_combo = this.$store.state.currentapp.has_combo;
this.has_combo = null;
currentapp.has_combo = null;
this.hmaster_release = null;
this.$store.dispatch('doucurrentapp', currentapp);
this.getappiconFun();
}
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": {
"has_combo": {
"hcombo_id": this.has_combo.app_id,
"action": "UNCOMBO",
}
}
});
},
setData() {
this.currentapp = this.$store.state.currentapp;
this.cmaster_release = this.$store.state.currentapp.master_release;
if (this.$store.state.currentapp.has_combo) {
this.has_combo = this.$store.state.currentapp.has_combo;
this.hmaster_release = this.$store.state.currentapp.has_combo.master_release;
} else {
this.has_combo = null;
this.hmaster_release = null;
this.getappiconFun();
}
},
getappiconFun() {
let type = "android";
if (this.currentapp.type === 0) {
type = "ios"
}
getapps(data => {
if (data.code === 1000) {
this.comboapplists = data.data;
this.orgapplists = this.comboapplists.slice(); //
} else {
this.$router.push({name: 'FirLogin'});
}
}, {"type": type, "page": 1, "size": 999, "act": 'combo'});
}
}, filters: {
geticon_url(app) {
if (app) {
return app.icon_url
}
return ""
},
getapptype: function (type) {
let ftype = '';
if (type === 0) {
ftype = 'Android'
} else {
ftype = 'iOS'
}
return ftype
},
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[44, 44], [44, 44]]);
this.setData();
},
watch: {
'$store.state.currentapp': function () {
this.setData();
},
// eslint-disable-next-line no-unused-vars
searchKey: function (val, oldVal) {
this.searchapps()
},
}
}
</script>
<style scoped>
.text-center {
text-align: center
}
.middle-wrapper {
margin: 0 auto;
width: 1100px
}
.apps-app-combo {
position: relative;
padding-top: 45px
}
.apps-app-combo .icon-container {
margin-bottom: 60px
}
.apps-app-combo .icon-container img {
border-radius: 17.544%
}
.apps-app-combo .apps-list {
position: relative;
overflow: hidden
}
.apps-app-combo .lead {
margin-bottom: 30px;
font-size: 16px
}
.apps-app-combo .lead b {
max-width: 300px;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top
}
.known-apps {
text-align: center;
float: left;
width: 45%;
margin: 10px 30px;
}
.apps-app-combo .known-apps .icon img {
width: 80px;
height: 80px;
border-radius: 22.7%
}
.apps-app-combo .known-apps .apps .app {
display: inline-block;
margin: 0 0 20px 28px;
text-align: center;
cursor: pointer;
vertical-align: top;
}
.apps-app-combo .known-apps .apps .app > p {
margin-top: 8px;
font-size: 12px;
text-align: center;
display: inline-block;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal
}
.apps-app-combo .find-apps {
/*padding-left: 50px;*/
text-align: center;
margin: 10px 30px;
float: right;
width: 40%;
}
.apps-app-combo .find-apps .lead {
text-align: left;
}
.apps-app-combo .find-apps .form-group {
position: relative;
margin-bottom: 36px;
width: 260px;
}
.apps-app-combo .hr-vertical {
position: absolute;
z-index: 12;
width: 1px;
height: 100%;
background-color: #d5f9f9
}
.apps-app-combo .request-wrapper .lead {
margin-bottom: 80px;
font-size: 18px
}
.apps-app-combo .request-wrapper table {
margin: 0 auto;
width: 400px
}
.apps-app-combo .request-wrapper table tr:first-child td span {
display: inline-block;
margin-bottom: 12px;
font-size: 16px
}
.apps-app-combo .request-wrapper table td {
padding: 0 15px;
text-align: center
}
.apps-app-combo .request-wrapper table td img {
width: 120px;
height: 120px;
border-radius: 17.54%
}
.apps-app-combo .request-wrapper table .actions {
padding-top: 40px
}
.apps-app-combo .request-wrapper table i.icon-combo {
font-size: 30px
}
.text-center {
text-align: center
}
.middle-wrapper {
margin: 0 auto;
width: 1100px
}
.apps-app-combo {
position: relative;
padding-top: 45px
}
.apps-app-combo .icon-container {
margin-bottom: 60px
}
.apps-app-combo .icon-container img {
border-radius: 17.544%
}
.apps-app-combo .apps-list {
position: relative;
overflow: hidden
}
.apps-app-combo .lead {
margin-bottom: 30px;
font-size: 16px
}
.apps-app-combo .lead b {
max-width: 300px;
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top
}
.known-apps {
text-align: center;
float: left;
width: 45%;
margin: 10px 30px;
}
.apps-app-combo .known-apps .icon img {
width: 80px;
height: 80px;
border-radius: 22.7%
}
.apps-app-combo .known-apps .apps .app {
display: inline-block;
margin: 0 0 20px 28px;
text-align: center;
cursor: pointer;
vertical-align: top;
}
.apps-app-combo .known-apps .apps .app > p {
margin-top: 8px;
font-size: 12px;
text-align: center;
display: inline-block;
max-width: 100px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: normal
}
.apps-app-combo .find-apps {
/*padding-left: 50px;*/
text-align: center;
margin: 10px 30px;
float: right;
width: 40%;
}
.apps-app-combo .find-apps .lead {
text-align: left;
}
.apps-app-combo .find-apps .form-group {
position: relative;
margin-bottom: 36px;
width: 260px;
}
.apps-app-combo .hr-vertical {
position: absolute;
z-index: 12;
width: 1px;
height: 100%;
background-color: #d5f9f9
}
.apps-app-combo .request-wrapper .lead {
margin-bottom: 80px;
font-size: 18px
}
.apps-app-combo .request-wrapper table {
margin: 0 auto;
width: 400px
}
.apps-app-combo .request-wrapper table tr:first-child td span {
display: inline-block;
margin-bottom: 12px;
font-size: 16px
}
.apps-app-combo .request-wrapper table td {
padding: 0 15px;
text-align: center
}
.apps-app-combo .request-wrapper table td img {
width: 120px;
height: 120px;
border-radius: 17.54%
}
.apps-app-combo .request-wrapper table .actions {
padding-top: 40px
}
.apps-app-combo .request-wrapper table i.icon-combo {
font-size: 30px
}
</style>

@ -1,59 +1,59 @@
<template>
<div style="margin-top: 20px;width: 90%;margin-left: 8%">
<el-link type="primary" v-if="this.currentapp.issupersign" style="margin-bottom: 30px;font-size: x-large"
@click="$router.push({name:'FirSuperSignBase',params:{act:'useddevices'},query:{bundleid: currentapp.bundle_id}})">
该应用已经开启超级签请点击查看最新设备列表
</el-link>
<h2>UDID列表及用户信息 - {{ udidlists.length }}个设备</h2>
<el-table
:data="udidlists"
stripe
style="width: 100%">
<el-table-column width="180"
prop="model"
label="设备型号">
</el-table-column>
<el-table-column width="180"
prop="version"
label="系统版本">
</el-table-column>
<el-table-column width="380"
prop="udid"
label="UDID">
</el-table-column>
</el-table>
<div style="margin-top: 20px;width: 90%;margin-left: 8%">
<el-link v-if="this.currentapp.issupersign" style="margin-bottom: 30px;font-size: x-large" type="primary"
@click="$router.push({name:'FirSuperSignBase',params:{act:'useddevices'},query:{bundleid: currentapp.bundle_id}})">
该应用已经开启超级签请点击查看最新设备列表
</el-link>
<h2>UDID列表及用户信息 - {{ udidlists.length }}个设备</h2>
<el-table
:data="udidlists"
stripe
style="width: 100%">
<el-table-column label="设备型号"
prop="model"
width="180">
</el-table-column>
<el-table-column label="系统版本"
prop="version"
width="180">
</el-table-column>
<el-table-column label="UDID"
prop="udid"
width="380">
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
export default {
name: "FirAppInfosdevices",
data() {
return {
currentapp: {},
udidlists: []
}
},
methods: {},
mounted() {
this.$store.dispatch('doappInfoIndex', [[70, 70], [70, 70]]);
if (!this.currentapp.app_id) {
this.currentapp = this.$store.state.currentapp;
if (this.currentapp.master_release) {
this.udidlists = this.currentapp.master_release.udid;
}
}
},
watch: {
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
this.udidlists = this.currentapp.master_release.udid;
}
}, computed: {}
export default {
name: "FirAppInfosdevices",
data() {
return {
currentapp: {},
udidlists: []
}
},
methods: {},
mounted() {
this.$store.dispatch('doappInfoIndex', [[70, 70], [70, 70]]);
if (!this.currentapp.app_id) {
this.currentapp = this.$store.state.currentapp;
if (this.currentapp.master_release) {
this.udidlists = this.currentapp.master_release.udid;
}
}
},
watch: {
'$store.state.currentapp': function () {
this.currentapp = this.$store.state.currentapp;
this.udidlists = this.currentapp.master_release.udid;
}
}, computed: {}
}
</script>
<style scoped>

File diff suppressed because it is too large Load Diff

@ -1,201 +1,308 @@
<template>
<div style="margin-top: 20px;width: 66%;margin-left: 8%">
<el-form label-width="80px" v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active">
<el-form-item label-width="200px"
label="超级签名">
<el-tooltip :content="supersign.msg" placement="top">
<el-switch
@change="supersignevent"
v-model="supersign.val"
active-color="#13ce66"
inactive-color="#ff4949"
active-value="on"
inactive-value="off">
</el-switch>
</el-tooltip>
<el-button @click="clean_app" v-if="currentapp.issupersign === 0 && currentapp.count !== 0"
style="margin-left: 20px" size="small" type="info" plain>清理开发者账户脏数据
</el-button>
<el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>
</el-form-item>
<el-form-item label-width="200px" label="签名限额">
<el-tooltip content="本应用签名使用额度,超过该额度,新设备将无法安装本应用。0代表不限额" placement="top">
<el-input-number v-model="currentapp.supersign_limit_number" :disabled="supersign_disable"
:placeholder="defualt_dtitle" :min="0"
style="width: 40%;margin-right: 10px" label="签名限额"/>
</el-tooltip>
<el-button @click="saveappinfo({supersign_limit_number:currentapp.supersign_limit_number})"
:disabled="supersign_disable"
>保存
</el-button>
<el-tooltip content="点击查看使用详情" placement="top">
<el-link :underline="false" type="primary" style="margin-left: 20px"
@click="$router.push({name:'FirSuperSignBase',params:{act:'useddevices'},query:{bundleid: currentapp.bundle_id}})">
已经使用 <a style="color: #dd6161;font-size: larger">{{ currentapp.supersign_used_number }}</a>
个设备额度
</el-link>
</el-tooltip>
</el-form-item>
<el-form-item label-width="200px" label="签名类型">
<el-select v-model="currentapp.supersign_type" placeholder="特殊签名权限"
style="width: 60%;margin-right: 10px" :disabled="supersign_disable">
<el-option v-for="st in sign_type_list" :key="st.id" :label="st.name"
:value="st.id"/>
</el-select>
<el-button @click="saveappinfo({supersign_type:currentapp.supersign_type})"
:disabled="supersign_disable"
>保存
</el-button>
</el-form-item>
<el-form-item label-width="200px" label="自定义BundleID">
<el-tooltip content="新的BundleID可能会导致推送等服务失效,请了解之后进行修改" placement="top">
<el-input v-model="currentapp.new_bundle_id" clearable :disabled="supersign_disable"
style="width: 60%;margin-right: 10px" prefix-icon="el-icon-s-data"
:placeholder="defualt_dtitle"/>
</el-tooltip>
<el-button @click="saveappinfo({new_bundle_id:currentapp.new_bundle_id})" :disabled="supersign_disable"
>保存
</el-button>
</el-form-item>
</el-form>
<el-link v-else :underline="false" type="warning"> 该用户暂未开通超级签权限,请联系管理员申请开通</el-link>
</div>
<div style="margin-top: 20px;width: 72%;margin-left: 8%">
<el-dialog
:close-on-click-modal="false"
:close-on-press-escape="false"
:title="appletoapp_title"
:visible.sync="bind_appletoapp_sure"
width="1166px">
<apple-developer-bind-app v-if="bind_appletoapp_sure" :app_id="currentapp.app_id"
transitionName="bind_appletoapp_app"/>
</el-dialog>
<el-form v-if="currentapp.type === 1 && $store.state.userinfo.supersign_active" label-width="80px">
<el-form-item label="超级签名"
label-width="200px">
<el-tooltip :content="supersign.msg" placement="top">
<el-switch
v-model="supersign.val"
active-color="#13ce66"
active-value="on"
inactive-color="#ff4949"
inactive-value="off"
@change="supersignevent">
</el-switch>
</el-tooltip>
<el-button v-if="!currentapp.issupersign && currentapp.count !== 0" plain
size="small" style="margin-left: 20px" type="info" @click="clean_app">清理该应用脏数据
</el-button>
<!-- <el-link v-else :underline="false" style="margin-left: 20px">超级签名iOS专用需要配置好苹果开发者账户方可开启</el-link>-->
<el-tooltip content="点击查看所使用开发者信息" placement="top">
<el-link :disabled="supersign_disable" :underline="false" style="margin-left: 20px;color: #3875cc;"
@click="$router.push({name:'FirSuperSignBase',params:{act:'iosdeveloper'},query:{appidseach: currentapp.bundle_id}})">
使用了 {{ currentapp.developer_used_count }} 开发者
</el-link>
</el-tooltip>
</el-form-item>
<el-form-item label="自动更新"
label-width="200px">
<el-switch
v-model="currentapp.change_auto_sign"
:active-value="true"
:disabled="supersign_disable"
:inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949"
@change="saveappinfo({change_auto_sign:currentapp.change_auto_sign})">
</el-switch>
<el-link :underline="false" style="margin-left: 20px">开启自动更新新包入库或签名相关数据更改系统将会自动更新签名包</el-link>
</el-form-item>
<el-form-item label="专属配置" label-width="200px">
<el-button :disabled="supersign_disable" @click="bindAppletoapp">配置专属苹果开发账户</el-button>
<el-link :disabled="supersign_disable" :underline="false" style="margin-left: 20px" @click="bindAppletoapp"> 拥有
{{ currentapp.private_developer_number }} 个专属苹果开发者使用了 {{ currentapp.private_developer_used_number }} 个设备数
</el-link>
</el-form-item>
<el-form-item label="签名限额" label-width="200px">
<el-tooltip content="本应用签名使用额度,超过该额度,新设备将无法安装本应用。0代表不限额" placement="top">
<el-input-number v-model="currentapp.supersign_limit_number" :disabled="supersign_disable" :min="0"
label="签名限额" style="width: 40%;margin-right: 10px"/>
</el-tooltip>
<el-button :disabled="supersign_disable"
@click="saveappinfo({supersign_limit_number:currentapp.supersign_limit_number})"
>保存
</el-button>
<el-tooltip content="点击查看使用详情" placement="top">
<el-link :disabled="supersign_disable" :underline="false" style="margin-left: 20px" type="primary"
@click="$router.push({name:'FirSuperSignBase',params:{act:'useddevices'},query:{bundleid: currentapp.bundle_id}})">
已经使用 <a style="color: #dd6161;font-size: larger">{{ currentapp.supersign_used_number }}</a>
个设备额度
</el-link>
</el-tooltip>
</el-form-item>
<el-form-item label="签名类型" label-width="200px">
<el-select v-model="currentapp.supersign_type" :disabled="supersign_disable"
placeholder="特殊签名权限" style="width: 60%;margin-right: 10px">
<el-option v-for="st in sign_type_list" :key="st.id" :label="st.name"
:value="st.id"/>
</el-select>
<el-button :disabled="supersign_disable"
@click="saveappinfo({supersign_type:currentapp.supersign_type})"
>保存
</el-button>
</el-form-item>
<el-form-item label="自定义BundleID" label-width="200px">
<el-tooltip content="新的BundleID可能会导致推送等服务失效,请了解之后进行修改" placement="top">
<el-input v-model="currentapp.new_bundle_id" :disabled="supersign_disable" :placeholder="defualt_dtitle"
clearable prefix-icon="el-icon-s-data"
style="width: 60%;margin-right: 10px"/>
</el-tooltip>
<el-button :disabled="supersign_disable" @click="saveappinfo({new_bundle_id:currentapp.new_bundle_id})"
>保存
</el-button>
</el-form-item>
<el-form-item label="自定义应用名称" label-width="200px">
<el-input v-model="currentapp.new_bundle_name" :disabled="supersign_disable" :placeholder="defualt_dtitle_name"
clearable prefix-icon="el-icon-s-data"
style="width: 60%;margin-right: 10px"/>
<el-button :disabled="supersign_disable"
@click="saveappinfo({new_bundle_name:currentapp.new_bundle_name})"
>保存
</el-button>
</el-form-item>
<el-divider/>
<el-form-item label="设备异常等待跳转"
label-width="200px">
<el-switch
v-model="currentapp.abnormal_redirect"
:active-value="true"
:disabled="supersign_disable"
:inactive-value="false"
active-color="#13ce66"
inactive-color="#ff4949"
@change="saveappinfo({abnormal_redirect:currentapp.abnormal_redirect})">
</el-switch>
<el-link :underline="false" style="margin-left: 20px">该配置仅当开启异常设备注册等待有效若启用该配置根据规则会自动跳转链接</el-link>
</el-form-item>
<el-divider/>
<el-form-item label="签名不足跳转链接" label-width="200px">
<el-input v-model="currentapp.supersign_redirect_url" :disabled="supersign_disable"
clearable prefix-icon="el-icon-s-promotion"
style="width: 60%;margin-right: 10px"/>
<el-button :disabled="supersign_disable"
@click="saveappinfo({supersign_redirect_url:currentapp.supersign_redirect_url})"
>保存
</el-button>
<el-link :underline="false">当设备余额不足无法进行签名可配置该链接跳转到第三方下载平台</el-link>
</el-form-item>
<el-divider/>
</el-form>
<el-link v-else :underline="false" type="warning"> 该用户暂未开通超级签权限,请联系管理员申请开通</el-link>
</div>
</template>
<script>
import {apputils,} from "@/restful"
import {deepCopy} from "@/utils";
export default {
name: "FirAppInfossupersign",
data() {
return {
currentapp: {},
orgcurrentapp: {},
supersign: {'msg': ''},
showsupersignflag: false,
supersign_disable: true,
sign_type_list: [],
sign_type: 0,
defualt_dtitle: ''
}
},
methods: {
set_default_flag() {
this.showsupersignflag = false;
},
clean_app() {
this.saveappinfo({"clean": true});
this.currentapp.count = 0;
},
saveappinfo(data) {
const loading = this.$loading({
lock: true,
text: '执行中,请耐心等待...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
apputils(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
this.$message.error('操作失败,' + data.msg);
this.$store.dispatch('doucurrentapp', this.orgcurrentapp);
}
loading.close();
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": data
});
},
setbuttonsignshow(currentapp) {
if (currentapp.issupersign === true) {
this.supersignevent("on");
this.supersign.val = 'on';
} else {
this.supersignevent("off");
this.supersign.val = 'off';
}
this.showsupersignflag = true;
},
setbuttondefault(currentapp) {
this.setbuttonsignshow(currentapp);
},
supersignevent(newval) {
if (newval === "on") {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 1,
});
this.currentapp.issupersign = 1;
}
this.supersign.msg = '已经开启超级签名';
this.supersign_disable = false
} else {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 0,
});
this.currentapp.issupersign = 0;
}
this.supersign.msg = '关闭';
this.supersign_disable = true
}
},
set_default_ms(currentapp) {
this.sign_type_list = currentapp.sign_type_choice;
if (this.currentapp.new_bundle_id && currentapp.new_bundle_id.length > 5) {
this.defualt_dtitle = currentapp.new_bundle_id
} else {
this.defualt_dtitle = currentapp.bundle_id
}
},
appinit() {
this.currentapp = this.$store.state.currentapp;
this.set_default_flag();
this.orgcurrentapp = deepCopy(this.currentapp);
this.set_default_ms(this.currentapp);
this.setbuttondefault(this.currentapp);
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[57, 57], [57, 57]]);
if (!this.currentapp.app_id) {
this.appinit();
}
},
watch: {
'$store.state.currentapp': function () {
this.appinit();
},
'currentapp.new_bundle_id': function () {
if (!this.currentapp.new_bundle_id || this.currentapp.new_bundle_id.length === 0) {
this.defualt_dtitle = this.currentapp.bundle_id
}
}
}, computed: {}
import {appSignInfo,} from "@/restful"
import {deepCopy} from "@/utils";
import AppleDeveloperBindApp from "@/components/base/AppleDeveloperBindApp";
export default {
name: "FirAppInfossupersign",
components: {AppleDeveloperBindApp},
data() {
return {
currentapp: {},
orgcurrentapp: {},
supersign: {'msg': ''},
showsupersignflag: false,
supersign_disable: true,
sign_type_list: [],
sign_type: 0,
defualt_dtitle: '',
defualt_dtitle_name: '',
bind_appletoapp_sure: false,
appletoapp_title: ''
}
},
methods: {
bindAppletoapp() {
this.appletoapp_title = "专属签名开发者信息 " + this.currentapp.name + "-" + this.currentapp.bundle_id;
this.bind_appletoapp_sure = true;
},
set_default_flag() {
this.showsupersignflag = false;
},
clean_app() {
this.saveappinfo({"clean": true});
this.currentapp.count = 0;
},
saveappinfo(data) {
const loading = this.$loading({
lock: true,
text: '执行中,请耐心等待...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
appSignInfo(data => {
if (data.code === 1000) {
this.$message.success('数据更新成功');
} else {
this.$message.error('操作失败,' + data.msg);
this.$store.dispatch('doucurrentapp', this.orgcurrentapp);
}
this.getappinfo();
loading.close();
}, {
"methods": "PUT",
"app_id": this.currentapp.app_id,
"data": data
});
},
getappinfo() {
appSignInfo(data => {
if (data.code === 1000) {
for (let name of Object.keys(data.data)) {
this.currentapp[name] = data.data[name]
}
} else {
this.$message.error('数据获取失败,' + data.msg);
}
}, {
"methods": "GET",
"app_id": this.currentapp.app_id,
});
},
setbuttonsignshow(currentapp) {
if (currentapp.issupersign === true) {
this.supersignevent("on");
this.supersign.val = 'on';
} else {
this.supersignevent("off");
this.supersign.val = 'off';
}
this.showsupersignflag = true;
},
setbuttondefault(currentapp) {
this.setbuttonsignshow(currentapp);
},
supersignevent(newval) {
if (newval === "on") {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 1,
});
this.currentapp.issupersign = 1;
}
this.supersign.msg = '已经开启超级签名';
this.supersign_disable = false
} else {
if (this.showsupersignflag) {
this.saveappinfo({
"issupersign": 0,
});
this.currentapp.issupersign = 0;
}
this.supersign.msg = '关闭';
this.supersign_disable = true
}
},
set_default_ms(currentapp) {
this.sign_type_list = currentapp.sign_type_choice;
if (this.currentapp.new_bundle_id && currentapp.new_bundle_id.length > 5) {
this.defualt_dtitle = currentapp.new_bundle_id
} else {
this.defualt_dtitle = currentapp.bundle_id
}
if (this.currentapp.new_bundle_name && currentapp.new_bundle_name.length > 0) {
this.defualt_dtitle_name = currentapp.new_bundle_name
} else {
this.defualt_dtitle_name = currentapp.name
}
},
appinit() {
this.currentapp = this.$store.state.currentapp;
if (this.currentapp.app_id) {
this.getappinfo();
}
this.set_default_flag();
this.orgcurrentapp = deepCopy(this.currentapp);
this.set_default_ms(this.currentapp);
this.setbuttondefault(this.currentapp);
}
},
mounted() {
this.$store.dispatch('doappInfoIndex', [[57, 57], [57, 57]]);
if (!this.currentapp.app_id) {
this.appinit();
}
},
watch: {
'$store.state.currentapp': function () {
this.appinit();
},
'currentapp.new_bundle_id': function () {
if (!this.currentapp.new_bundle_id || this.currentapp.new_bundle_id.length === 0) {
this.defualt_dtitle = this.currentapp.bundle_id
}
},
'currentapp.new_bundle_name': function () {
if (!this.currentapp.new_bundle_name || this.currentapp.new_bundle_name.length === 0) {
this.defualt_dtitle_name = this.currentapp.name
}
}
}, computed: {}
}
</script>
<style scoped>

@ -1,486 +1,472 @@
<template>
<el-timeline>
<el-timeline-item v-for="(app) in release_apps" :key="app.release_id"
:timestamp="app.created_time|formatdatatimeline"
:color="app.master_color"
placement="top">
<el-card>
<div class="directive-view-release">
<i class="el-icon-cloudy" v-if="! app.is_master"/>
<i class="el-icon-cloudy" style="background-color: rgb(139,195,248)" v-else/>
<b class="ng-binding">{{app.app_version}} (Build {{app.build_version}})</b>
<div class="release-metainfo ng-hide"
v-if="app.release_type === 0">
<small>
<i class="icon-calendar"/>
<span class="el-icon-date">&nbsp;{{app.created_time|formattimeline}}</span>
</small>
</div>
<div class="release-metainfo" v-else>
<small>
<i class="icon-calendar"/>
<span class="el-icon-date">&nbsp;{{app.created_time|formattimeline}}</span>
</small> &nbsp;&nbsp;·&nbsp;&nbsp;
<small>{{app.release_type|getiOStype}}</small>
<small v-if="app.distribution_name" style="margin-left: 5px">&nbsp;&nbsp;·&nbsp;{{app.distribution_name}}</small>
</div>
<p>{{app.changelog}}</p>
<label>
<el-timeline>
<el-timeline-item v-for="(app) in release_apps" :key="app.release_id"
:color="app.master_color"
:timestamp="app.created_time|formatdatatimeline"
placement="top">
<el-card :body-style="{border: app.is_master ? '#2abb9d 1px solid' : ''}">
<div class="directive-view-release">
<i v-if="! app.is_master" class="el-icon-cloudy"/>
<i v-else class="el-icon-cloudy" style="background-color: rgb(139,195,248)"/>
<b class="ng-binding">{{ app.app_version }} (Build {{ app.build_version }})</b>
<div v-if="app.release_type === 0"
class="release-metainfo ng-hide">
<small>
<i class="icon-calendar"/>
<span class="el-icon-date">&nbsp;{{ app.created_time|formattimeline }}</span>
</small>
</div>
<div v-else class="release-metainfo">
<small>
<i class="icon-calendar"/>
<span class="el-icon-date">&nbsp;{{ app.created_time|formattimeline }}</span>
</small> &nbsp;&nbsp;·&nbsp;&nbsp;
<small>{{ app.release_type|getiOStype }}</small>
<small v-if="app.distribution_name"
style="margin-left: 5px">&nbsp;&nbsp;·&nbsp;{{ app.distribution_name }}</small>
</div>
<p>{{ app.changelog }}</p>
<label>
<textarea v-if="app.editing.changelog"
v-model="app.changelog"
placeholder="更新日志">
</textarea>
</label>
<div class="release-actions editing " v-if="app.editing.changelog">
<button class="btn-cancel" @click="endEdit(app,'changelog')"><span
>取消</span></button>
<button class="btn-save" @click="updateChangelog(app,'changelog')"><span
>保存</span></button>
</div>
<el-input v-if="app.editing.binary_url"
v-model="app.binary_url"
placeholder="下载地址,默认本服务器,填写第三方可以 自动跳转到第三方平台" clearable>
</el-input>
<div class="release-actions editing " v-if="app.editing.binary_url" style="margin-top: 10px">
<button class="btn-cancel" @click="endEdit(app,'binary_url')"><span
>取消</span></button>
<button class="btn-save" @click="updateChangelog(app,'binary_url')"><span
>保存</span></button>
</div>
<div class="release-actions" v-show="!app.editing.changelog && !app.editing.binary_url ">
<el-tooltip class="tooltip-top" content="编辑更新日志" placement="top">
<el-button @click="startEdit(app,'changelog')"
tooltip="编辑更新日志"
class="tooltip-top ">
<i class="el-icon-edit"/>
</el-button>
</el-tooltip>
<el-tooltip class="tooltip-top" content="下载原文件" placement="top">
<el-button class="tooltip-top" tooltip="下载原文件"
@click="downloadPackage(app)">
<i class="el-icon-download"/>
<span>{{app.binary_size}}</span>
</el-button>
</el-tooltip>
<el-tooltip class="tooltip-top" :content="app.binary_url|downcontent" placement="top">
<el-button class="tooltip-top" tooltip="修改下载地址"
@click="startEdit(app,'binary_url')"><i class="el-icon-link"/> <span>下载地址</span>
</el-button>
</el-tooltip>
<el-button class="tooltip-top" @click="previewRelase(app)"><i class="el-icon-view"/> <span
class="ng-binding">预览</span>
</el-button>
<el-button v-if="! app.is_master" class="tooltip-top" @click="make_master_release(app)">
<i class="el-icon-view"/>
<span class="ng-binding ng-scope">标记上线</span>
</el-button>
<el-button v-if="(! app.is_master )|| (app.is_master && release_apps.length === 1)"
class="tooltip-top" @click="del_release_app(app)">
<i class="el-icon-delete"/>
<span class="ng-binding ng-scope">删除</span>
</el-button>
</div>
</div>
</el-card>
</el-timeline-item>
<el-button v-if="has_next" class="time-line-more" @click="getapptimelineFun('more')">显示更多版本</el-button>
</el-timeline>
</label>
<div v-if="app.editing.changelog" class="release-actions editing ">
<button class="btn-cancel" @click="endEdit(app,'changelog')"><span
>取消</span></button>
<button class="btn-save" @click="updateChangelog(app,'changelog')"><span
>保存</span></button>
</div>
<el-input v-if="app.editing.binary_url"
v-model="app.binary_url"
clearable placeholder="下载地址,默认本服务器,填写第三方可以 自动跳转到第三方平台">
</el-input>
<div v-if="app.editing.binary_url" class="release-actions editing " style="margin-top: 10px">
<button class="btn-cancel" @click="endEdit(app,'binary_url')"><span
>取消</span></button>
<button class="btn-save" @click="updateChangelog(app,'binary_url')"><span
>保存</span></button>
</div>
<div v-show="!app.editing.changelog && !app.editing.binary_url " class="release-actions">
<el-tooltip class="tooltip-top" content="编辑更新日志" placement="top">
<el-button class="tooltip-top "
tooltip="编辑更新日志"
@click="startEdit(app,'changelog')">
<i class="el-icon-edit"/>
</el-button>
</el-tooltip>
<el-tooltip class="tooltip-top" content="下载原文件" placement="top">
<el-button class="tooltip-top" tooltip="下载原文件"
@click="downloadPackage(app)">
<i class="el-icon-download"/>
<span>{{ app.binary_size }}</span>
</el-button>
</el-tooltip>
<el-tooltip :content="app.binary_url|downcontent" class="tooltip-top" placement="top">
<el-button class="tooltip-top" tooltip="修改下载地址"
@click="startEdit(app,'binary_url')"><i class="el-icon-link"/> <span>下载地址</span>
</el-button>
</el-tooltip>
<el-button v-if="!currentapp.issupersign" class="tooltip-top" @click="previewRelease(app)"><i
class="el-icon-view"/> <span
class="ng-binding">预览该版本</span>
</el-button>
<el-button v-if="! app.is_master" class="tooltip-top" @click="make_master_release(app)">
<i class="el-icon-view"/>
<span class="ng-binding ng-scope">标记上线</span>
</el-button>
<el-button v-if="(! app.is_master )|| (app.is_master && release_apps.length === 1)"
class="tooltip-top" @click="del_release_app(app)">
<i class="el-icon-delete"/>
<span class="ng-binding ng-scope">删除</span>
</el-button>
</div>
</div>
</el-card>
</el-timeline-item>
<el-button v-if="has_next" class="time-line-more" @click="getAppTimelineFun('more')">显示更多版本</el-button>
</el-timeline>
</template>
<script>
import {getdownloadurl, releaseapputils} from "@/restful"
export default {
name: "FirAppInfostimeline",
data() {
return {
release_apps: [],
currentapp: {},
activity: {
editing: {'changelog': false, 'binary_url': false}
},
updatas: {},
query: {'page': 1, size: 10},
has_next: false,
import {releaseapputils} from "@/restful"
export default {
name: "FirAppInfostimeline",
data() {
return {
release_apps: [],
currentapp: {},
activity: {
editing: {'changelog': false, 'binary_url': false}
},
updatas: {},
query: {'page': 1, size: 10},
has_next: false,
}
},
methods: {
set_query_page() {
if (this.has_next) {
this.query.page += 1;
}
},
downloadPackage(app) {
releaseapputils(res => {
if (res.code === 1000) {
window.location.href = res.data.download_url;
} else {
this.$message.error(res.msg);
}
},
methods: {
set_query_page() {
if (this.has_next) {
this.query.page += 1;
}
},
downloadPackage(app) {
getdownloadurl(res => {
if (res.code === 1000) {
window.location.href = res.data.download_url;
} else {
this.$message.error(res.msg);
}
}, {
'data': {
'token': app.download_token,
'short': this.currentapp.short,
'release_id': app.release_id,
"password": this.currentapp.password,
"isdownload": true,
},
'app_id': this.currentapp.app_id
})
},
previewRelase(app) {
let routeData = this.$router.resolve({
name: 'FirDownload',
params: {short: this.currentapp.short},
query: {release_id: app.release_id}
});
let p_url = routeData.href;
if (this.currentapp.preview_url && this.currentapp.preview_url.length > 6) {
p_url = this.currentapp.preview_url + p_url
}
window.open(p_url, 'target', '');
},
getapptimelineFun(act = '') {
const loading = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
if (act === 'more') {
this.set_query_page();
} else {
this.release_apps = [];
this.query = {'page': 1, size: 10};
}
releaseapputils(data => {
if (data.code === 1000) {
this.currentapp = data.data.currentapp;
this.has_next = data.data.has_next;
this.release_apps = this.release_apps.concat(data.data.release_apps);
} else if (data.code === 1003) {
loading.close();
this.$router.push({name: 'FirApps'});
}
loading.close();
}, {
"methods": "GET",
"app_id": this.$route.params.id,
"release_id": "timeline",
"data": {
"page": this.query.page,
"size": this.query.size
}
})
},
del_release_app(app) {
//APP
this.$confirm('确认删除 ' + this.currentapp.name + '下 当前 release 版本吗?')
// eslint-disable-next-line no-unused-vars
.then(res => {
let loadingobj = this.$loading({
lock: true,
text: '操作中,请耐心等待',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
releaseapputils(data => {
loadingobj.close();
if (data.code === 1000) {
this.$message({
message: '删除成功',
type: 'success'
});
this.getapptimelineFun();
} else {
this.$message({
message: '删除失败,请联系管理员',
type: 'error'
});
}
}, {
"methods": "DELETE",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
});
});
},
updatereleaseappFun(params) {
releaseapputils(data => {
if (data.code === 1000) {
this.$message({
message: '更新成功',
type: 'success'
});
this.release_apps = data.data.release_apps;
this.currentapp = data.data.currentapp;
// this.getapptimelineFun();
// this.currentapp["icon_url"] = this.currentapp.master_release.icon_url;
// this.$store.dispatch('doucurrentapp', this.currentapp);
} else {
this.$message({
message: '更新失败,请联系管理员',
type: 'error'
});
}
}, params
);
},
make_master_release(app) {
this.updatereleaseappFun({
"methods": "PUT",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": {
"make_master": app.release_id
}
}, {
methods: 'POST',
app_id: this.currentapp.app_id,
release_id: app.release_id,
data: {'token': app.download_token, 'short': this.currentapp.short}
}
);
},
previewRelease(app) {
window.open(this.currentapp.preview_url + '?release_id=' + app.release_id, '_blank', '');
},
getAppTimelineFun(act = '') {
const loading = this.$loading({
lock: true,
text: '加载中',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
if (act === 'more') {
this.set_query_page();
} else {
this.release_apps = [];
this.query = {'page': 1, size: 10};
}
releaseapputils(data => {
if (data.code === 1000) {
this.currentapp = data.data.currentapp;
this.has_next = data.data.has_next;
this.release_apps = this.release_apps.concat(data.data.release_apps);
} else if (data.code === 1003) {
loading.close();
this.$router.push({name: 'FirApps'});
}
loading.close();
}, {
"methods": "GET",
"app_id": this.$route.params.id,
"release_id": "timeline",
"data": {
"page": this.query.page,
"size": this.query.size
}
})
},
del_release_app(app) {
//APP
this.$confirm('确认删除 ' + this.currentapp.name + '下 当前 release 版本吗?')
// eslint-disable-next-line no-unused-vars
.then(res => {
let loadingobj = this.$loading({
lock: true,
text: '操作中,请耐心等待',
spinner: 'el-icon-loading',
// background: 'rgba(0, 0, 0, 0.7)'
});
releaseapputils(data => {
loadingobj.close();
if (data.code === 1000) {
this.$message({
message: '删除成功',
type: 'success'
});
this.getAppTimelineFun();
},
endEdit(app, type) {
if (type === 'changelog') {
app.editing.changelog = false;
} else if (type === 'binary_url') {
app.editing.binary_url = false;
}
},
updateChangelog(app, type) {
if (type === 'changelog') {
this.activity.editing.changelog = false;
this.updatas = {"changelog": app.changelog}
} else if (type === 'binary_url') {
this.activity.editing.binary_url = false;
this.updatas = {"binary_url": app.binary_url}
}
this.updatereleaseappFun({
"methods": "PUT",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": this.updatas
} else {
this.$message({
message: '删除失败,请联系管理员',
type: 'error'
});
this.updatas = {}
},
startEdit(app, type) {
if (type === 'changelog') {
app.editing.changelog = true;
} else if (type === 'binary_url') {
app.editing.binary_url = true;
}
}
}, {
"methods": "DELETE",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
});
});
},
updatereleaseappFun(params) {
releaseapputils(data => {
if (data.code === 1000) {
this.$message({
message: '更新成功',
type: 'success'
});
this.release_apps = data.data.release_apps;
this.currentapp = data.data.currentapp;
} else {
this.$message({
message: '更新失败,请联系管理员',
type: 'error'
});
}
}, created() {
}, watch: {},
computed: {}, mounted() {
this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]);
this.getapptimelineFun();
}, filters: {
downcontent(content) {
if (content) {
return content
} else {
return "修改下载地址"
}
},
formatdatatimeline: function (timestr) {
return timestr.split("T")[0];
},
formattimeline: function (timestr) {
return timestr.split(".")[0].split("T")[1];
},
getiOStype: function (type) {
let ftype = '';
if (type === 1) {
ftype = '内测版'
} else if (type === 2) {
ftype = '企业版'
}
return ftype
},
}, params
);
},
make_master_release(app) {
this.updatereleaseappFun({
"methods": "PUT",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": {
"make_master": app.release_id
}
});
},
endEdit(app, type) {
if (type === 'changelog') {
app.editing.changelog = false;
} else if (type === 'binary_url') {
app.editing.binary_url = false;
}
},
updateChangelog(app, type) {
if (type === 'changelog') {
this.activity.editing.changelog = false;
this.updatas = {"changelog": app.changelog}
} else if (type === 'binary_url') {
this.activity.editing.binary_url = false;
this.updatas = {"binary_url": app.binary_url}
}
this.updatereleaseappFun({
"methods": "PUT",
"app_id": this.currentapp.app_id,
"release_id": app.release_id,
"data": this.updatas
});
this.updatas = {}
},
startEdit(app, type) {
if (type === 'changelog') {
app.editing.changelog = true;
} else if (type === 'binary_url') {
app.editing.binary_url = true;
}
}
}, created() {
}, watch: {},
computed: {}, mounted() {
this.$store.dispatch('doappInfoIndex', [[5, 5], [5, 5]]);
this.getAppTimelineFun();
}, filters: {
downcontent(content) {
if (content) {
return content
} else {
return "修改下载地址"
}
},
formatdatatimeline: function (timestr) {
return timestr.split("T")[0];
},
formattimeline: function (timestr) {
return timestr.split(".")[0].split("T")[1];
},
getiOStype: function (type) {
let ftype = '';
if (type === 1) {
ftype = '内测版'
} else if (type === 2) {
ftype = '企业版'
}
return ftype
},
}
}
</script>
<style scoped>
.directive-view-release {
position: relative;
padding-left: 80px;
color: #9b9b9b;
}
.directive-view-release > i {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
height: 50px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
left: 0;
z-index: 2;
width: 50px;
border: 1px solid rgba(151, 151, 151, .2);
border-radius: 50%;
background-color: #f6f6f6;
text-align: center;
font-size: 22px
}
.directive-view-release > i:before {
display: inline-block;
margin-top: 2px;
margin-left: 2px
}
.directive-view-release .release-metainfo {
margin-top: 2px
}
.directive-view-release .release-metainfo small {
display: inline-block;
vertical-align: middle;
margin: 8px 0;
line-height: 14px
}
.directive-view-release .release-metainfo small i, .directive-view-release .release-metainfo small span {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-metainfo small i {
margin-right: 2px;
line-height: 14px
}
.directive-view-release > b {
display: inline-block;
vertical-align: middle;
margin-right: 30px;
color: #4a4a4a;
font-weight: 400;
font-size: 20px
}
.directive-view-release pre, .directive-view-release textarea {
margin: 14px 0;
padding: 0;
border: 0;
background-color: transparent;
color: #4a4a4a;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
}
.directive-view-release textarea {
padding: 12px 16px;
width: 500px;
height: 120px;
border-radius: 5px;
resize: none;
border: 1px solid #bdc6c7;
color: #555;
font-size: 16px
}
.directive-view-release .tooltip-top {
position: relative;
overflow: visible;
color: #9b9b9b;
}
.directive-view-release .release-actions {
margin-top: 10px;
position: relative
}
.directive-view-release .release-actions a, .directive-view-release .release-actions button {
display: inline-block;
vertical-align: middle;
margin-right: 8px;
background-color: transparent;
border: 1px solid;
overflow: hidden;
border-radius: 17px;
padding: 4px 10px
}
.directive-view-release .release-actions .tooltip-top {
overflow: visible;
position: relative
}
.directive-view-release .release-actions a i, .directive-view-release .release-actions button i {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-actions a.btn-save, .directive-view-release .release-actions button.btn-save {
border-color: #3d6df8;
background-color: #92c1f8;
color: #fff
}
.directive-view-release .release-actions a.btn-cancel, .directive-view-release .release-actions button.btn-cancel {
border: 0;
}
.directive-view-release .release-actions a.btn-cancel:hover, .directive-view-release .release-actions button.btn-cancel:hover {
color: #686868
}
.directive-view-release .release-actions a {
color: #9b9b9b
}
.directive-view-release .release-actions a:focus, .directive-view-release .release-actions a:hover {
text-decoration: none
}
.directive-view-release .release-actions .has-text i {
border-right: 1px solid
}
.directive-view-release .release-actions.editing {
margin-top: 0;
text-align: right;
width: 500px
}
.time-line-more {
background: #F6F6F6;
border: 1px solid;
position: relative;
z-index: 99;
width: 160px;
padding: 10px 0;
border-radius: 40px;
margin-top: 10px;
}
.directive-view-release {
position: relative;
padding-left: 80px;
color: #9b9b9b;
}
.directive-view-release > i {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
height: 50px;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
position: absolute;
left: 0;
z-index: 2;
width: 50px;
border: 1px solid rgba(151, 151, 151, .2);
border-radius: 50%;
background-color: #f6f6f6;
text-align: center;
font-size: 22px
}
.directive-view-release > i:before {
display: inline-block;
margin-top: 2px;
margin-left: 2px
}
.directive-view-release .release-metainfo {
margin-top: 2px
}
.directive-view-release .release-metainfo small {
display: inline-block;
vertical-align: middle;
margin: 8px 0;
line-height: 14px
}
.directive-view-release .release-metainfo small i, .directive-view-release .release-metainfo small span {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-metainfo small i {
margin-right: 2px;
line-height: 14px
}
.directive-view-release > b {
display: inline-block;
vertical-align: middle;
margin-right: 30px;
color: #4a4a4a;
font-weight: 400;
font-size: 20px
}
.directive-view-release pre, .directive-view-release textarea {
margin: 14px 0;
padding: 0;
border: 0;
background-color: transparent;
color: #4a4a4a;
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
}
.directive-view-release textarea {
padding: 12px 16px;
width: 500px;
height: 120px;
border-radius: 5px;
resize: none;
border: 1px solid #bdc6c7;
color: #555;
font-size: 16px
}
.directive-view-release .tooltip-top {
position: relative;
overflow: visible;
color: #9b9b9b;
}
.directive-view-release .release-actions {
margin-top: 10px;
position: relative
}
.directive-view-release .release-actions a, .directive-view-release .release-actions button {
display: inline-block;
vertical-align: middle;
margin-right: 8px;
background-color: transparent;
border: 1px solid;
overflow: hidden;
border-radius: 17px;
padding: 4px 10px
}
.directive-view-release .release-actions .tooltip-top {
overflow: visible;
position: relative
}
.directive-view-release .release-actions a i, .directive-view-release .release-actions button i {
display: inline-block;
vertical-align: middle
}
.directive-view-release .release-actions a.btn-save, .directive-view-release .release-actions button.btn-save {
border-color: #3d6df8;
background-color: #92c1f8;
color: #fff
}
.directive-view-release .release-actions a.btn-cancel, .directive-view-release .release-actions button.btn-cancel {
border: 0;
}
.directive-view-release .release-actions a.btn-cancel:hover, .directive-view-release .release-actions button.btn-cancel:hover {
color: #686868
}
.directive-view-release .release-actions a {
color: #9b9b9b
}
.directive-view-release .release-actions a:focus, .directive-view-release .release-actions a:hover {
text-decoration: none
}
.directive-view-release .release-actions .has-text i {
border-right: 1px solid
}
.directive-view-release .release-actions.editing {
margin-top: 0;
text-align: right;
width: 500px
}
.time-line-more {
background: #F6F6F6;
border: 1px solid;
position: relative;
z-index: 99;
width: 160px;
padding: 10px 0;
border-radius: 40px;
margin-top: 10px;
}
</style>

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

Loading…
Cancel
Save