├── .editorconfig ├── .env.development ├── .env.production ├── .env.stage ├── .eslintrc.js ├── .gitignore ├── .postcssrc.js ├── LICENSE ├── LICENSE_CN ├── README.md ├── babel.config.js ├── jest.config.js ├── package-lock.json ├── package.json ├── pic ├── 1WechatIMG15.jpeg ├── 1WechatIMG16.jpeg ├── 1WechatIMG17.jpeg ├── 1WechatIMG18.jpeg ├── 1WechatIMG19.jpeg ├── 1WechatIMG20.jpeg ├── 1WechatIMG21.jpeg ├── 1WechatIMG22.jpeg ├── 1WechatIMG24.jpeg ├── WechatIMG10.jpeg ├── WechatIMG11.jpeg ├── WechatIMG12.jpeg ├── WechatIMG13.jpeg ├── WechatIMG14.jpeg ├── WechatIMG15.jpeg ├── WechatIMG16.jpeg ├── WechatIMG17.jpeg ├── WechatIMG18.jpeg ├── WechatIMG6.jpeg ├── WechatIMG7.jpeg ├── WechatIMG8.jpeg ├── WechatIMG9.jpeg ├── group-show-how.gif ├── him.jpg └── user-show-how.gif ├── public ├── favicon.ico ├── index-dev.html └── index.html ├── src ├── App.vue ├── api │ ├── test.js │ └── util │ │ └── wxUtil.js ├── assets │ └── image │ │ ├── group-default.png │ │ ├── him-big.png │ │ ├── him-mini.png │ │ ├── logo.png │ │ ├── poster-bg.png │ │ ├── user-1-default.png │ │ └── user-2-default.png ├── components │ └── Him │ │ ├── api │ │ ├── himAxios.js │ │ ├── userFriend.js │ │ ├── userFriendAsk.js │ │ ├── userFriendMsg.js │ │ ├── userGroup.js │ │ ├── userGroupMsg.js │ │ ├── userGroupUser.js │ │ ├── userIndex.js │ │ └── userLogin.js │ │ ├── emoji │ │ ├── angry.png │ │ ├── blush.png │ │ ├── clap.png │ │ ├── cold_sweat.png │ │ ├── flushed.png │ │ ├── grin.png │ │ ├── heart_eyes.png │ │ ├── joy.png │ │ ├── kissing_closed_eyes.png │ │ ├── kissing_smiling_eyes.png │ │ ├── laughing.png │ │ ├── mask.png │ │ ├── ok_hand.png │ │ ├── scream.png │ │ ├── sleeping.png │ │ ├── smile.png │ │ ├── smirk.png │ │ ├── sob.png │ │ ├── stuck_out_tongue_winking_eye.png │ │ ├── sunglasses.png │ │ ├── sweat_smile.png │ │ ├── thumbsup.png │ │ ├── wink.png │ │ └── worried.png │ │ ├── image │ │ ├── bg-1.jpg │ │ ├── bg-2.jpg │ │ ├── bg-3.jpg │ │ ├── bg-4.jpg │ │ ├── bg-5.jpg │ │ ├── default-avatar.jpg │ │ ├── emoji.png │ │ ├── emoji@2x.png │ │ ├── im-icon.png │ │ ├── im-icon@2x.png │ │ ├── login-qq.png │ │ ├── login-tourist.png │ │ ├── out-login.png │ │ ├── qrcode.png │ │ ├── search.png │ │ ├── theme.png │ │ ├── user-1-default.png │ │ ├── user-2-default.png │ │ ├── user-64.png │ │ └── user-default.png │ │ └── index.vue ├── filtres │ └── index.js ├── main.js ├── proto │ ├── WSBaseReqProto.proto │ ├── WSBaseResProto.proto │ ├── WSMessageResProto.proto │ ├── WSUserResProto.proto │ └── proto.js ├── router │ ├── router.js │ └── routerEach.js ├── store │ └── index.js ├── styles │ ├── base.scss │ └── mixin.scss ├── utils │ ├── auth.js │ ├── axios.js │ ├── baiduTJhmt.js │ └── storage.js └── views │ ├── Home.vue │ ├── day │ └── video │ │ ├── detail.js │ │ ├── detail.scss │ │ └── detail.vue │ ├── error │ ├── err401.vue │ ├── err404.vue │ └── err500.vue │ └── test │ └── canvas │ ├── poster.js │ ├── poster.scss │ └── poster.vue ├── tests └── unit │ ├── .eslintrc.js │ └── example.spec.js └── vue.config.js /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | VUE_APP_API_SUFFIX= 2 | VUE_APP_API_BASE=http://localhost:9000 3 | VUE_APP_WEBSOCKET_URL=ws://localhost:9001/ws 4 | VUE_APP_USER_QR_CODE_URL=http://localhost:8080?userCheckCode= 5 | VUE_APP_GROUP_QR_CODE_URL=http://localhost:8080?groupCheckCode= 6 | VUE_APP_ROUTER_BASE=/ 7 | template=./public/index-dev.html 8 | outputDir=serve 9 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | VUE_APP_API_SUFFIX= 2 | VUE_APP_API_BASE=http://him-netty.await.fun 3 | VUE_APP_WEBSOCKET_URL=ws://him-netty.await.fun/ws 4 | VUE_APP_USER_QR_CODE_URL=http://him-netty.await.fun/h5?userCheckCode= 5 | VUE_APP_GROUP_QR_CODE_URL=http://him-netty.await.fun/h5?groupCheckCode= 6 | VUE_APP_ROUTER_BASE=/h5 7 | template=./public/index.html 8 | outputDir=dist 9 | baseUrl=http://him-netty.await.fun/h5 10 | -------------------------------------------------------------------------------- /.env.stage: -------------------------------------------------------------------------------- 1 | VUE_APP_API_SUFFIX= 2 | VUE_APP_API_BASE=http://localhost:9998 3 | VUE_APP_WEBSOCKET_URL=ws://localhost:7272?t=ssss 4 | VUE_APP_USER_QR_CODE_URL=http://localhost:8080?userCheckCode= 5 | VUE_APP_GROUP_QR_CODE_URL=http://localhost:8080?groupCheckCode= 6 | NODE_ENV=production 7 | VUE_APP_ROUTER_BASE=/web/ 8 | outputDir=stage 9 | template=./public/index.html 10 | baseUrl=//test-static.xxxx.cn/web 11 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/prettier' 9 | ], 10 | plugins: [ 11 | 'vue' 12 | ], 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 16 | }, 17 | parserOptions: { 18 | parser: 'babel-eslint' 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | /stage 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw* 23 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | "autoprefixer": {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2 | 3 | "Anti 996" License Version 1.0 (Draft) 4 | 5 | Permission is hereby granted to any individual or legal entity 6 | obtaining a copy of this licensed work (including the source code, 7 | documentation and/or related items, hereinafter collectively referred 8 | to as the "licensed work"), free of charge, to deal with the licensed 9 | work for any purpose, including without limitation, the rights to use, 10 | reproduce, modify, prepare derivative works of, distribute, publish 11 | and sublicense the licensed work, subject to the following conditions: 12 | 13 | 1. The individual or the legal entity must conspicuously display, 14 | without modification, this License and the notice on each redistributed 15 | or derivative copy of the Licensed Work. 16 | 17 | 2. The individual or the legal entity must strictly comply with all 18 | applicable laws, regulations, rules and standards of the jurisdiction 19 | relating to labor and employment where the individual is physically 20 | located or where the individual was born or naturalized; or where the 21 | legal entity is registered or is operating (whichever is stricter). In 22 | case that the jurisdiction has no such laws, regulations, rules and 23 | standards or its laws, regulations, rules and standards are 24 | unenforceable, the individual or the legal entity are required to 25 | comply with Core International Labor Standards. 26 | 27 | 3. The individual or the legal entity shall not induce, suggest or force 28 | its employee(s), whether full-time or part-time, or its independent 29 | contractor(s), in any methods, to agree in oral or written form, to 30 | directly or indirectly restrict, weaken or relinquish his or her 31 | rights or remedies under such laws, regulations, rules and standards 32 | relating to labor and employment as mentioned above, no matter whether 33 | such written or oral agreements are enforceable under the laws of the 34 | said jurisdiction, nor shall such individual or the legal entity 35 | limit, in any methods, the rights of its employee(s) or independent 36 | contractor(s) from reporting or complaining to the copyright holder or 37 | relevant authorities monitoring the compliance of the license about 38 | its violation(s) of the said license. 39 | 40 | THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 41 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 42 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 43 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, 44 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 45 | OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE 46 | LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. 47 | -------------------------------------------------------------------------------- /LICENSE_CN: -------------------------------------------------------------------------------- 1 | 版权所有(c)<年份><版权持有人> 2 | 3 | 反996许可证版本1.0 4 | 5 | 在符合下列条件的情况下,特此免费向任何得到本授权作品的副本(包括源代码、文件和/或相关内容,以 6 | 下统称为“授权作品”)的个人和法人实体授权:被授权个人或法人实体有权以任何目的处置授权作品,包括 7 | 但不限于使用、复制,修改,衍生利用、散布,发布和再许可: 8 | 9 | 1. 个人或法人实体必须在许可作品的每个再散布或衍生副本上包含以上版权声明和本许可证,不得自行修 10 | 改。 11 | 2. 个人或法人实体必须严格遵守与个人实际所在地或个人出生地或归化地、或法人实体注册地或经营地( 12 | 以较严格者为准)的司法管辖区所有适用的与劳动和就业相关法律、法规、规则和标准。如果该司法管辖区 13 | 没有此类法律、法规、规章和标准或其法律、法规、规章和标准不可执行,则个人或法人实体必须遵守国际 14 | 劳工标准的核心公约。 15 | 3. 个人或法人不得以任何方式诱导、暗示或强迫其全职或兼职员工或其独立承包人以口头或书面形式同意 16 | 直接或间接限制、削弱或放弃其所拥有的,受相关与劳动和就业有关的法律、法规、规则和标准保护的权利 17 | 或补救措施,无论该等书面或口头协议是否被该司法管辖区的法律所承认,该等个人或法人实体也不得以任 18 | 何方法限制其雇员或独立承包人向版权持有人或监督许可证合规情况的有关当局报告或投诉上述违反许可证 19 | 的行为的权利。 20 | 21 | 该授权作品是"按原样"提供,不做任何明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵 22 | 权性的保证。在任何情况下,无论是在合同诉讼、侵权诉讼或其他诉讼中,版权持有人均不承担因本软件或 23 | 本软件的使用或其他交易而产生、引起或与之相关的任何索赔、损害或其他责任。 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | 5 | 6 | [![him-vue](https://github.com/lmxdawn/him-vue/raw/master/pic/him.jpg)](http://him-netty.await.fun/h5) 7 | 8 | 9 |

10 |

11 | 12 | [![him-vue](https://img.shields.io/badge/him-him--vue-1.svg)](https://github.com/lmxdawn/him-vue) 13 | [![him-netty](https://img.shields.io/badge/him-him--netty-1.svg)](https://github.com/lmxdawn/him-netty) 14 | [![QQ群](https://img.shields.io/badge/QQ%E7%BE%A4-210277856-orange.svg)](https://shang.qq.com/wpa/qunwpa?idkey=d4965fc7101936dcdea5eb1d05e2eaeb3128f20796028ee937ab516652083c6c) 15 | [![](https://badge.juejin.im/entry/5cd6be3ae51d456e5b66ae3d/likes.svg?style=flat-square)](https://juejin.im/post/5cd6be3ae51d456e5b66ae3d) 16 | 17 |

18 | 19 |

20 | 21 | [![vue](https://img.shields.io/badge/vue-2.x-1.svg)](https://github.com/vuejs/vue) 22 | [![netty](https://img.shields.io/badge/netty-4.1.25.Final-1.svg)](https://github.com/netty/netty) 23 | [![spring-boot](https://img.shields.io/badge/spring--boot-2.1.2.RELEASE-1.svg)](https://github.com/spring-projects/spring-boot) 24 | 25 |

26 |
27 | 28 | # him-vue 29 | 30 | > 使用前先阅读开源协议: [中文版](https://github.com/lmxdawn/him-vue/LICENSE_CN) , [English version](https://github.com/lmxdawn/him-vue/LICENSE) , 协议出处 [Anti-996-License-1.0](https://github.com/kattgu7/Anti-996-License) 31 | 32 | > 前端:vue [前往](https://github.com/lmxdawn/him-vue) ,服务端 netty [前往](https://github.com/lmxdawn/him-netty) 33 | 34 | # 踩坑指南 35 | 36 | > * 1. iOS版本手机QQ中清空不了 Cookie 的bug (Android 版本的QQ没试), 其它浏览器均正常 37 | 38 | > * 2. 手机微信中打开后点击输入文字后, 不管点不点击发送按钮都会出现短暂的不能点击的现象(任何按钮都不能点击), 后来发现是因为在微信里面, 输入法把 输入框顶上去了, 然后输入法隐藏后输入框还在上面!!!! [点击查看详情](https://developers.weixin.qq.com/community/develop/doc/00040a43cd4290dedbc7e7f1851400) 39 | 。找到一个解决输入框的方法: @blur="chatTextBlur" 监听失去焦点的事件(vue 写法), 然后在事件里面执行 `window.scroll(0, 0);` 40 | 41 | > * 3. 因为设置了定位,`overflow: scroll` 原生滚动,iOS下会不流畅,解决办法:换成 `-webkit-overflow-scrolling: touch;` 42 | 43 | # 功能列表 44 | * [x] 单聊 45 | * [x] 群聊 46 | * [x] protobuf 编解码 47 | * [x] 客户端心跳 48 | * [x] 客户端断开重连 49 | * [x] 异地登录, 通知下线 50 | * [x] 移动端/PC端适配 51 | * [x] 离线消息 (消息通过 ack 机制, 实现可达性) 52 | * [x] 第三方QQ登录 53 | * [x] 自带 emoji 表情 54 | * [x] 文本消息 55 | * [ ] 声音提示 56 | * [ ] 图片消息 57 | * [ ] 音频消息 58 | * [ ] 视屏消息 59 | * [ ] 分布式部署 60 | * [ ] PHP 版本的 (Workerman 版本) 61 | 62 | # 环境要求 63 | 64 | ## git 65 | > 这个版本管理肯定需要安装的 66 | 67 | ## node 68 | > node 版本最新的即可 69 | 70 | ## vue 71 | 72 | > 构建工具用 vue 目前使用的 2.x 版本 73 | 74 | ## 下载 75 | 76 | > git clone https://github.com/lmxdawn/him-vue.git 77 | > cd him-vue 78 | 79 | ## 安装 80 | 81 | > npm install 82 | 83 | ## 编译 84 | > npm run serve 本地测试版 85 | 86 | > him-vue [前往](https://github.com/lmxdawn/him-vue) 和 him-netty [前往](https://github.com/lmxdawn/him-netty) 都启动后访问 http://localhost:8080 87 | 88 | > npm run build 编译命令 89 | 90 | 注意默认使用 QQ登录, 这个需要去申请QQ互联, 如果不想去申请, 则可以直接设置 Cookie, 两个值 UID 和 SID, 这两个值可以通过接口 /api/user/login/byPwd 获取, 具体请看java 代码 91 | 92 | # 加好友演示 93 | 94 | [![him-vue](https://github.com/lmxdawn/him-vue/raw/master/pic/user-show-how.gif)](http://him-netty.await.fun/h5) 95 | 96 | # 加群演示 97 | 98 | [![him-vue](https://github.com/lmxdawn/him-vue/raw/master/pic/group-show-how.gif)](http://him-netty.await.fun/h5) 99 | 100 | 101 | # QQ 互联相关配置 102 | 103 | ### java 代码 104 | > him-api/src/main/resources/ 这里的配置文件里面, `qq.auth.appid` 和 `qq.auth.appkey` 配置上即可 105 | 106 | ### vue 代码 107 | > 详细配置 根目录下的 108 | `.env.development` 109 | `.env.production` 110 | `.env.stage` 这三个文件是配置, 分别代表 本地测试,生产环境,线上测试环境 111 | 112 | |名称|描述| 113 | | --- | --- | 114 | | VUE_APP_API_BASE | API接口地址 | 115 | | VUE_APP_WEBSOCKET_URL | websocket地址 | 116 | | VUE_APP_USER_QR_CODE_URL | 生成用户的二维码地址(用来加好友的) | 117 | | VUE_APP_GROUP_QR_CODE_URL | 生成群二维码的地址(用来加群的) | 118 | | VUE_APP_ROUTER_BASE | 如果用了 NGINX 做代理, 并且有二级路径, 则需要配置此项 | 119 | 120 | # 跨域问题 121 | 122 | > NGINX 做了端口的代理后, header 头 设置了跨域, 但是还是获取不了, 不知道为啥, 欢迎大神来指导 123 | 124 | > 最后我的解决办法, 全部用一个域名, 然后 NGINX 做路径的转换,下面贴一下我的配置 125 | 126 | ``` 127 | # 前端路径, 注意这里配置了二级目录后, 需要 vue 的路由里面也需要配置 128 | # 我是写在配置文件里面的 VUE_APP_ROUTER_BASE 这个配置项来控制的 129 | location /h5 { 130 | try_files $uri $uri/ /h5/index.html; 131 | } 132 | # API 路径 133 | location /api 134 | { 135 | proxy_pass http://127.0.0.1:9000/api; 136 | proxy_http_version 1.1; 137 | proxy_set_header Upgrade $http_upgrade; 138 | proxy_set_header Connection "Upgrade"; 139 | proxy_set_header X-Real-IP $remote_addr; 140 | } 141 | # ws 路径 142 | location /ws 143 | { 144 | proxy_pass http://127.0.0.1:9001; 145 | proxy_http_version 1.1; 146 | proxy_set_header Upgrade $http_upgrade; 147 | proxy_set_header Connection "Upgrade"; 148 | proxy_set_header X-Real-IP $remote_addr; 149 | } 150 | ``` 151 | 152 | # Him 组件说明 153 | | 参数 | 说明 | 类型 | 可选值 | 默认值 | 154 | | --- | --- |--- | --- |--- | 155 | | isShow | 是否显示界面 | boolean | — | true | 156 | | width | 宽度| string | — | 100% | 157 | | height| 高度 | string | — | 100% | 158 | | top | 定位的顶部位置 | string | — | —| 159 | | left| 定位的左边位置 | string | — | — | 160 | | bottom| 定位的底部位置 | string | — | — | 161 | | right| 定位的右边位置 | string | — | — | 162 | | apiBaseUrl| api 接口的地址| string | — | — | 163 | | webSocketUrl| websocket 的连接地址 | string | — | — | 164 | | userQRCodeUrl | 用户二维码的生成地址 | string | — | — | 165 | | groupQRCodeUrl| 群二维码的生成地址 | string | — | — | 166 | | isAutoInit | 是否自动初始化(如果为 false 需要执行) | boolean | — | true | 167 | | webSocketReconnectMaxCount | 尝试重新连接的最大次数 |number| — | 5 | 168 | 169 | # 图床说明 170 | 171 | > 把图片放入 git 版本控制里, 上传到 GitHub 上, 然后 在 GitHub 里打开这个图片 把里面的 blob 改为 raw 172 | 173 | > 例如: https://github.com/lmxdawn/him-vue/blob/master/pic/WechatIMG10.jpeg 改为 https://github.com/lmxdawn/him-vue/raw/master/pic/WechatIMG10.jpeg 174 | 175 | > 我这里直接用的 七牛云的, 因为怕 GitHub 的访问太慢 176 | 177 | # protobuf 杂谈 178 | 179 | > 说明: 目前所有文件都生成好了,不需要在生成,下面简单说明下 180 | 181 | ## vue 中使用 182 | 183 | > 目前我是安装好了 protobufjs 了,proto 文件放在 /src/proto 目录。 184 | > 运行命令 pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto 即可 185 | > 由于我添加到了 package.json 中,直接运行 npm run protojs 也可以 186 | 187 | ## 页面中引入 188 | 189 | > 上面的执行完成后,会在 src/proto 目录下生成 proto.js 文件,**由于 webpack 新版本的原因直接引入该文件会报错** 190 | > [](https://github.com/protobufjs/protobuf.js/issues/1216)[Cannot assign to read only property'exports'of object' ](https://github.com/protobufjs/protobuf.js/issues/1216 "Cannot assign to read only property'exports'of object' ") 191 | > **需要修改最后一行代码为**:`export default $root;` 192 | 193 | ``` 194 | import protoRoot from "@/proto/proto" 195 | const WSBaseReqProto = protoRoot.lookup("protocol.WSBaseReqProto"); 196 | const WSBaseResProto = protoRoot.lookup("protocol.WSBaseResProto"); 197 | // 编码 198 | function (payload) { 199 | // 加入登录验证 200 | payload.uid = parseInt(this.getUid()); 201 | payload.sid = this.getSid(); 202 | console.log("发送的信息:"); 203 | let errMsg = WSBaseReqProto.verify(payload); 204 | console.log("buff 解析错误信息:", errMsg); 205 | // Create a new message 206 | const wsData = WSBaseReqProto.create(payload); // or use .fromObject if conversion is necessary 207 | // Encode a message to an Uint8Array (browser) or Buffer (node) 208 | return WSBaseReqProto.encode(wsData).finish(); 209 | } 210 | // 解码 211 | function (data, cb) { 212 | let reader = new FileReader(); 213 | reader.readAsArrayBuffer(data); 214 | reader.onload = () => { 215 | const buf = new Uint8Array(reader.result); 216 | const response = WSBaseResProto.decode(buf); 217 | // 成功回调 218 | cb(response); 219 | }; 220 | } 221 | ``` 222 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue' 7 | ], 8 | transform: { 9 | '^.+\\.vue$': 'vue-jest', 10 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 11 | '^.+\\.jsx?$': 'babel-jest' 12 | }, 13 | moduleNameMapper: { 14 | '^@/(.*)$': '/src/$1' 15 | }, 16 | snapshotSerializers: [ 17 | 'jest-serializer-vue' 18 | ], 19 | testMatch: [ 20 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 21 | ], 22 | testURL: 'http://localhost/' 23 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "stage": "vue-cli-service build --mode stage", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint", 10 | "test:unit": "vue-cli-service test:unit", 11 | "proto:json": "pbjs -t json src/proto/*.proto > src/proto/proto.json", 12 | "protojs": "pbjs -t json-module -w commonjs -o src/proto/proto.js src/proto/*.proto" 13 | }, 14 | "dependencies": { 15 | "axios": "^0.18.0", 16 | "js-cookie": "^2.2.0", 17 | "postcss-px-to-viewport": "0.0.3", 18 | "protobufjs": "^6.8.8", 19 | "qrcode": "^1.3.3", 20 | "vue": "^2.5.17", 21 | "vue-router": "^3.0.1", 22 | "vuex": "^3.0.1" 23 | }, 24 | "devDependencies": { 25 | "@vue/cli-plugin-babel": "^3.0.5", 26 | "@vue/cli-plugin-eslint": "^3.0.5", 27 | "@vue/cli-plugin-unit-jest": "^3.0.5", 28 | "@vue/cli-service": "^3.0.5", 29 | "@vue/eslint-config-prettier": "^3.0.5", 30 | "@vue/test-utils": "^1.0.0-beta.20", 31 | "babel-core": "7.0.0-bridge.0", 32 | "babel-jest": "^23.0.1", 33 | "node-sass": "^4.9.0", 34 | "sass-loader": "^7.0.1", 35 | "vue-template-compiler": "^2.5.17" 36 | }, 37 | "browserslist": [ 38 | "> 1%", 39 | "last 2 versions", 40 | "not ie <= 8" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /pic/1WechatIMG15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG15.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG16.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG16.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG17.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG17.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG18.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG18.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG19.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG19.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG20.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG20.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG21.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG21.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG22.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG22.jpeg -------------------------------------------------------------------------------- /pic/1WechatIMG24.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/1WechatIMG24.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG10.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG11.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG12.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG13.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG13.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG14.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG14.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG15.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG16.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG16.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG17.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG17.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG18.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG18.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG6.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG7.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG8.jpeg -------------------------------------------------------------------------------- /pic/WechatIMG9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/WechatIMG9.jpeg -------------------------------------------------------------------------------- /pic/group-show-how.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/group-show-how.gif -------------------------------------------------------------------------------- /pic/him.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/him.jpg -------------------------------------------------------------------------------- /pic/user-show-how.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/pic/user-show-how.gif -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/public/favicon.ico -------------------------------------------------------------------------------- /public/index-dev.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 开源的H5即时聊天系统 spring-boot + netty + protobuf + vue ~ 10 | 11 | 12 | 13 | 14 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 开源的H5即时聊天系统 spring-boot + netty + protobuf + vue ~ 10 | 11 | 12 | 13 | 14 | 17 |
18 | 19 |
开源的H5即时聊天系统 spring-boot + netty + protobuf + vue ~,支持单聊群聊 protobuf 编解码,客户端心跳,客户端断开重连,异地登录, 通知下线,移动端/PC端适配。
20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /src/api/test.js: -------------------------------------------------------------------------------- 1 | import axios from "../utils/axios"; 2 | 3 | // 用户信息 4 | export function userInfo(query) { 5 | return axios({ 6 | url: "/api/test/index/userInfo", 7 | method: "get", 8 | params: query 9 | }); 10 | } 11 | // 用户分组列表 12 | export function userGroupList(query) { 13 | return axios({ 14 | url: "/api/test/index/userGroupList", 15 | method: "get", 16 | params: query 17 | }); 18 | } 19 | // 历史消息 20 | export function chatMsgList(query) { 21 | return axios({ 22 | url: "/api/test/index/chatMsgList", 23 | method: "get", 24 | params: query 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /src/api/util/wxUtil.js: -------------------------------------------------------------------------------- 1 | import axios from "../../utils/axios"; 2 | 3 | // 4 | 5 | // 微信 js-SDK 票据 6 | export function wxUtilJsapiTicket(query) { 7 | return axios({ 8 | url: "/api/wx/js_sdk/jsapiTicket", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/image/group-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/group-default.png -------------------------------------------------------------------------------- /src/assets/image/him-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/him-big.png -------------------------------------------------------------------------------- /src/assets/image/him-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/him-mini.png -------------------------------------------------------------------------------- /src/assets/image/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/logo.png -------------------------------------------------------------------------------- /src/assets/image/poster-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/poster-bg.png -------------------------------------------------------------------------------- /src/assets/image/user-1-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/user-1-default.png -------------------------------------------------------------------------------- /src/assets/image/user-2-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/assets/image/user-2-default.png -------------------------------------------------------------------------------- /src/components/Him/api/himAxios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | // 获取用户信息 4 | export function create(apiBaseUrl) { 5 | // 创建axios实例 6 | const service = axios.create({ 7 | baseURL: apiBaseUrl, // api的base_url 8 | timeout: 5000, // 请求超时时间 9 | withCredentials: true 10 | }); 11 | // request拦截器 12 | service.interceptors.request.use( 13 | config => { 14 | // Do something before request is sent 15 | return config; 16 | }, 17 | error => { 18 | // Do something with request error 19 | Promise.reject(error); 20 | } 21 | ); 22 | 23 | // respone拦截器 24 | service.interceptors.response.use( 25 | response => { 26 | const data = response.data; 27 | if (data.code) { 28 | if (data.code === 1) { 29 | // 重新登录 30 | } 31 | } 32 | return data; 33 | }, 34 | error => { 35 | return Promise.reject(error); 36 | } 37 | ); 38 | return service; 39 | } 40 | -------------------------------------------------------------------------------- /src/components/Him/api/userFriend.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 朋友列表 6 | export function userFriendLists(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/user/friend/lists", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | 14 | // 朋友列表 15 | export function userFriendDelete(apiBaseUrl, data) { 16 | return create(apiBaseUrl)({ 17 | url: "/api/user/friend/delete", 18 | method: "post", 19 | data: data 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/components/Him/api/userFriendAsk.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 好友请求 4 | 5 | // 列表 6 | export function userFriendAskLists(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/user/friendAsk/lists", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | 14 | // 发起好友请求 15 | export function userFriendAskCreate(apiBaseUrl, checkCode, remark) { 16 | return create(apiBaseUrl)({ 17 | url: "/api/user/friendAsk/create", 18 | method: "post", 19 | params: { 20 | checkCode: checkCode, 21 | remark: remark 22 | } 23 | }); 24 | } 25 | 26 | // 确认好友请求 27 | export function userFriendAskAck(apiBaseUrl, id, status) { 28 | return create(apiBaseUrl)({ 29 | url: "/api/user/friendAsk/ack", 30 | method: "post", 31 | data: { 32 | id: id, 33 | status: status 34 | } 35 | }); 36 | } 37 | 38 | // 清空好友请求数量 39 | export function userFriendAskClearFriendAskCount(apiBaseUrl) { 40 | return create(apiBaseUrl)({ 41 | url: "/api/user/friendAsk/clearFriendAskCount", 42 | method: "post" 43 | }); 44 | } 45 | -------------------------------------------------------------------------------- /src/components/Him/api/userFriendMsg.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 朋友消息列表 6 | export function userFriendMsgLists(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/user/friendMsg/lists", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | 14 | // 发送好友消息 15 | export function userFriendMsgCreate(apiBaseUrl, data) { 16 | return create(apiBaseUrl)({ 17 | url: "/api/user/friendMsg/create", 18 | method: "post", 19 | data: data 20 | }); 21 | } 22 | 23 | // 清空未读的消息数量 24 | export function userFriendMsgClearUnMsgCount(apiBaseUrl, data) { 25 | return create(apiBaseUrl)({ 26 | url: "/api/user/friendMsg/clearUnMsgCount", 27 | method: "post", 28 | data: data 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Him/api/userGroup.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 群成员列表 6 | export function userGroupLists(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/group/lists", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | 14 | // 创建群 15 | export function userGroupCreate(apiBaseUrl, name, avatar, remark) { 16 | return create(apiBaseUrl)({ 17 | url: "/api/group/create", 18 | method: "post", 19 | data: { 20 | name: name, 21 | avatar: avatar, 22 | remark: remark 23 | } 24 | }); 25 | } 26 | 27 | // 更新群信息 28 | export function userGroupUpdate(apiBaseUrl, groupId, name, avatar, remark) { 29 | return create(apiBaseUrl)({ 30 | url: "/api/group/update", 31 | method: "post", 32 | data: { 33 | groupId: groupId, 34 | name: name, 35 | avatar: avatar, 36 | remark: remark 37 | } 38 | }); 39 | } 40 | 41 | // 删除/解散群 42 | export function userGroupDelete(apiBaseUrl, groupId) { 43 | return create(apiBaseUrl)({ 44 | url: "/api/group/delete", 45 | method: "post", 46 | params: groupId 47 | }); 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Him/api/userGroupMsg.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 群消息列表 6 | export function userGroupMsgLists(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/group/msg/lists", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | 14 | // 创建消息 15 | export function userGroupMsgCreate(apiBaseUrl, data) { 16 | return create(apiBaseUrl)({ 17 | url: "/api/group/msg/create", 18 | method: "post", 19 | data: data 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /src/components/Him/api/userGroupUser.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 加入的群列表 6 | export function userGroupUserLists(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/group/user/lists", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | 14 | // 加群 15 | export function userGroupUserCreate(apiBaseUrl, checkCode) { 16 | return create(apiBaseUrl)({ 17 | url: "/api/group/user/create", 18 | method: "post", 19 | params: { 20 | checkCode: checkCode 21 | } 22 | }); 23 | } 24 | 25 | // 更新 26 | export function userGroupUserUpdate(apiBaseUrl, groupId, remark) { 27 | return create(apiBaseUrl)({ 28 | url: "/api/group/user/update", 29 | method: "post", 30 | data: { 31 | groupId: groupId, 32 | remark: remark 33 | } 34 | }); 35 | } 36 | 37 | // 删除/退群 38 | export function userGroupUserDelete(apiBaseUrl, groupId) { 39 | return create(apiBaseUrl)({ 40 | url: "/api/group/user/delete", 41 | method: "post", 42 | params: { 43 | groupId: groupId 44 | } 45 | }); 46 | } 47 | 48 | // 获取验证码 49 | export function userGroupUserCheckCode(apiBaseUrl, groupId) { 50 | return create(apiBaseUrl)({ 51 | url: "/api/group/user/getCheckCode", 52 | method: "get", 53 | params: { 54 | groupId: groupId 55 | } 56 | }); 57 | } 58 | 59 | // 清空未读消息 60 | export function userGroupUserClearUnMsgCount(apiBaseUrl, groupId) { 61 | return create(apiBaseUrl)({ 62 | url: "/api/group/user/clearUnMsgCount", 63 | method: "post", 64 | params: { 65 | groupId: groupId 66 | } 67 | }); 68 | } 69 | -------------------------------------------------------------------------------- /src/components/Him/api/userIndex.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 登录用户信息 6 | export function userLoginInfo(apiBaseUrl, query) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/user/loginInfo", 9 | method: "get", 10 | params: query 11 | }); 12 | } 13 | // 获取用户信息 14 | export function userRead(apiBaseUrl, query) { 15 | return create(apiBaseUrl)({ 16 | url: "/api/user/read", 17 | method: "get", 18 | params: query 19 | }); 20 | } 21 | // 获取二维码验证信息 22 | export function userQRCheckCode(apiBaseUrl, query) { 23 | return create(apiBaseUrl)({ 24 | url: "/api/user/getQRCheckCode", 25 | method: "get", 26 | params: query 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Him/api/userLogin.js: -------------------------------------------------------------------------------- 1 | import { create } from "./himAxios"; 2 | 3 | // 4 | 5 | // 游客 登录 6 | export function userLoginByTourist(apiBaseUrl, sex) { 7 | return create(apiBaseUrl)({ 8 | url: "/api/user/login/byTourist", 9 | method: "post", 10 | params: { 11 | sex 12 | } 13 | }); 14 | } 15 | 16 | // QQ 登录 17 | export function userLoginByQq(apiBaseUrl, code, redirect_uri) { 18 | return create(apiBaseUrl)({ 19 | url: "/api/user/login/byQq", 20 | method: "post", 21 | params: { 22 | code, 23 | redirect_uri 24 | } 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /src/components/Him/emoji/angry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/angry.png -------------------------------------------------------------------------------- /src/components/Him/emoji/blush.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/blush.png -------------------------------------------------------------------------------- /src/components/Him/emoji/clap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/clap.png -------------------------------------------------------------------------------- /src/components/Him/emoji/cold_sweat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/cold_sweat.png -------------------------------------------------------------------------------- /src/components/Him/emoji/flushed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/flushed.png -------------------------------------------------------------------------------- /src/components/Him/emoji/grin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/grin.png -------------------------------------------------------------------------------- /src/components/Him/emoji/heart_eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/heart_eyes.png -------------------------------------------------------------------------------- /src/components/Him/emoji/joy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/joy.png -------------------------------------------------------------------------------- /src/components/Him/emoji/kissing_closed_eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/kissing_closed_eyes.png -------------------------------------------------------------------------------- /src/components/Him/emoji/kissing_smiling_eyes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/kissing_smiling_eyes.png -------------------------------------------------------------------------------- /src/components/Him/emoji/laughing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/laughing.png -------------------------------------------------------------------------------- /src/components/Him/emoji/mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/mask.png -------------------------------------------------------------------------------- /src/components/Him/emoji/ok_hand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/ok_hand.png -------------------------------------------------------------------------------- /src/components/Him/emoji/scream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/scream.png -------------------------------------------------------------------------------- /src/components/Him/emoji/sleeping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/sleeping.png -------------------------------------------------------------------------------- /src/components/Him/emoji/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/smile.png -------------------------------------------------------------------------------- /src/components/Him/emoji/smirk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/smirk.png -------------------------------------------------------------------------------- /src/components/Him/emoji/sob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/sob.png -------------------------------------------------------------------------------- /src/components/Him/emoji/stuck_out_tongue_winking_eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/stuck_out_tongue_winking_eye.png -------------------------------------------------------------------------------- /src/components/Him/emoji/sunglasses.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/sunglasses.png -------------------------------------------------------------------------------- /src/components/Him/emoji/sweat_smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/sweat_smile.png -------------------------------------------------------------------------------- /src/components/Him/emoji/thumbsup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/thumbsup.png -------------------------------------------------------------------------------- /src/components/Him/emoji/wink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/wink.png -------------------------------------------------------------------------------- /src/components/Him/emoji/worried.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/emoji/worried.png -------------------------------------------------------------------------------- /src/components/Him/image/bg-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/bg-1.jpg -------------------------------------------------------------------------------- /src/components/Him/image/bg-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/bg-2.jpg -------------------------------------------------------------------------------- /src/components/Him/image/bg-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/bg-3.jpg -------------------------------------------------------------------------------- /src/components/Him/image/bg-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/bg-4.jpg -------------------------------------------------------------------------------- /src/components/Him/image/bg-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/bg-5.jpg -------------------------------------------------------------------------------- /src/components/Him/image/default-avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/default-avatar.jpg -------------------------------------------------------------------------------- /src/components/Him/image/emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/emoji.png -------------------------------------------------------------------------------- /src/components/Him/image/emoji@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/emoji@2x.png -------------------------------------------------------------------------------- /src/components/Him/image/im-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/im-icon.png -------------------------------------------------------------------------------- /src/components/Him/image/im-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/im-icon@2x.png -------------------------------------------------------------------------------- /src/components/Him/image/login-qq.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/login-qq.png -------------------------------------------------------------------------------- /src/components/Him/image/login-tourist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/login-tourist.png -------------------------------------------------------------------------------- /src/components/Him/image/out-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/out-login.png -------------------------------------------------------------------------------- /src/components/Him/image/qrcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/qrcode.png -------------------------------------------------------------------------------- /src/components/Him/image/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/search.png -------------------------------------------------------------------------------- /src/components/Him/image/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/theme.png -------------------------------------------------------------------------------- /src/components/Him/image/user-1-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/user-1-default.png -------------------------------------------------------------------------------- /src/components/Him/image/user-2-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/user-2-default.png -------------------------------------------------------------------------------- /src/components/Him/image/user-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/user-64.png -------------------------------------------------------------------------------- /src/components/Him/image/user-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lmxdawn/him-vue/1fcdb8a0053a884ab39a50e078b05a189d507c61/src/components/Him/image/user-default.png -------------------------------------------------------------------------------- /src/filtres/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @return {string} 3 | */ 4 | export function FormatDate(date_str, fmt) { 5 | let date = new Date(date_str); 6 | if (isNaN(date.getDate())) { 7 | return ""; 8 | } 9 | const o = { 10 | "M+": date.getMonth() + 1, //月份 11 | "d+": date.getDate(), //日 12 | "h+": date.getHours(), //小时 13 | "m+": date.getMinutes(), //分 14 | "s+": date.getSeconds(), //秒 15 | "q+": Math.floor((date.getMonth() + 3) / 3), //季度 16 | S: date.getMilliseconds() //毫秒 17 | }; 18 | if (/(y+)/.test(fmt)) 19 | fmt = fmt.replace( 20 | RegExp.$1, 21 | (date.getFullYear() + "").substr(4 - RegExp.$1.length) 22 | ); 23 | for (let k in o) 24 | if (new RegExp("(" + k + ")").test(fmt)) 25 | fmt = fmt.replace( 26 | RegExp.$1, 27 | RegExp.$1.length === 1 28 | ? o[k] 29 | : ("00" + o[k]).substr(("" + o[k]).length) 30 | ); 31 | return fmt; 32 | } 33 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router/router"; 4 | import "./router/routerEach"; // 路由钩子 5 | import store from "./store/index"; 6 | import * as filters from "./filtres/index"; // 全局过滤器 7 | 8 | // 注册全局实用程序过滤器(register global utility filters). 9 | Object.keys(filters).forEach(key => { 10 | Vue.filter(key, filters[key]); 11 | }); 12 | 13 | Vue.config.productionTip = false; 14 | 15 | new Vue({ 16 | router, 17 | store, 18 | render: h => h(App) 19 | }).$mount("#app"); 20 | -------------------------------------------------------------------------------- /src/proto/WSBaseReqProto.proto: -------------------------------------------------------------------------------- 1 | syntax ="proto3"; 2 | package protocol; 3 | 4 | //optimize_for 加快解析的速度 5 | option optimize_for = SPEED; 6 | option java_package = "com.lmxdawn.him.common.protobuf"; 7 | 8 | // 请求实体 type (0: 心跳, 1: 登录) 9 | message WSBaseReqProto{ 10 | int32 type = 1; 11 | uint64 uid = 2; // 用户ID 12 | string sid = 3; // 登录验证 13 | } 14 | -------------------------------------------------------------------------------- /src/proto/WSBaseResProto.proto: -------------------------------------------------------------------------------- 1 | syntax ="proto3"; 2 | package protocol; 3 | 4 | import "WSMessageResProto.proto"; 5 | import "WSUserResProto.proto"; 6 | 7 | //optimize_for 加快解析的速度 8 | option optimize_for = SPEED; 9 | option java_package = "com.lmxdawn.him.common.protobuf"; 10 | 11 | // 返回实体 12 | message WSBaseResProto{ 13 | int32 type = 1; 14 | WSMessageResProto message = 2; 15 | WSUserResProto user = 3; 16 | string create_time = 4; 17 | } 18 | -------------------------------------------------------------------------------- /src/proto/WSMessageResProto.proto: -------------------------------------------------------------------------------- 1 | syntax ="proto3"; 2 | package protocol; 3 | 4 | //optimize_for 加快解析的速度 5 | option optimize_for = SPEED; 6 | option java_package = "com.lmxdawn.him.common.protobuf"; 7 | 8 | // 请求实体 9 | message WSMessageResProto{ 10 | uint64 receive_id = 1; 11 | int32 msg_type = 2; 12 | string msg_content = 3; 13 | } 14 | -------------------------------------------------------------------------------- /src/proto/WSUserResProto.proto: -------------------------------------------------------------------------------- 1 | syntax ="proto3"; 2 | package protocol; 3 | 4 | //optimize_for 加快解析的速度 5 | option optimize_for = SPEED; 6 | option java_package = "com.lmxdawn.him.common.protobuf"; 7 | 8 | // 请求实体 9 | message WSUserResProto{ 10 | uint64 uid = 1; 11 | string name = 2; 12 | string avatar = 3; 13 | string remark = 4; 14 | } 15 | -------------------------------------------------------------------------------- /src/proto/proto.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable*/ 2 | "use strict"; 3 | 4 | var $protobuf = require("protobufjs/light"); 5 | 6 | var $root = ($protobuf.roots["default"] || ($protobuf.roots["default"] = new $protobuf.Root())) 7 | .addJSON({ 8 | protocol: { 9 | options: { 10 | optimize_for: "SPEED", 11 | java_package: "com.lmxdawn.him.common.protobuf" 12 | }, 13 | nested: { 14 | WSBaseReqProto: { 15 | fields: { 16 | type: { 17 | type: "int32", 18 | id: 1 19 | }, 20 | uid: { 21 | type: "uint64", 22 | id: 2 23 | }, 24 | sid: { 25 | type: "string", 26 | id: 3 27 | } 28 | } 29 | }, 30 | WSBaseResProto: { 31 | fields: { 32 | type: { 33 | type: "int32", 34 | id: 1 35 | }, 36 | message: { 37 | type: "WSMessageResProto", 38 | id: 2 39 | }, 40 | user: { 41 | type: "WSUserResProto", 42 | id: 3 43 | }, 44 | createTime: { 45 | type: "string", 46 | id: 4 47 | } 48 | } 49 | }, 50 | WSMessageResProto: { 51 | fields: { 52 | receiveId: { 53 | type: "uint64", 54 | id: 1 55 | }, 56 | msgType: { 57 | type: "int32", 58 | id: 2 59 | }, 60 | msgContent: { 61 | type: "string", 62 | id: 3 63 | } 64 | } 65 | }, 66 | WSUserResProto: { 67 | fields: { 68 | uid: { 69 | type: "uint64", 70 | id: 1 71 | }, 72 | name: { 73 | type: "string", 74 | id: 2 75 | }, 76 | avatar: { 77 | type: "string", 78 | id: 3 79 | }, 80 | remark: { 81 | type: "string", 82 | id: 4 83 | } 84 | } 85 | } 86 | } 87 | } 88 | }); 89 | 90 | export default $root; 91 | -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import Home from "../views/Home.vue"; 4 | 5 | if (process.env.NODE_ENV === "development") { 6 | Vue.use(VueRouter); 7 | } 8 | 9 | const err401 = () => 10 | import(/* webpackChunkName: "error" */ "../views/error/err401.vue"); 11 | const err404 = () => 12 | import(/* webpackChunkName: "error" */ "../views/error/err404.vue"); 13 | 14 | export default new VueRouter({ 15 | mode: "history", // 后端支持可开 16 | base: process.env.VUE_APP_ROUTER_BASE, 17 | routes: [ 18 | { 19 | path: "*", 20 | component: err404 21 | }, 22 | { 23 | path: "/401", 24 | component: err401, 25 | name: "401" 26 | }, 27 | { 28 | path: "/404", 29 | component: err404, 30 | name: "404" 31 | }, 32 | { 33 | path: "/500", 34 | component: err404, 35 | name: "500" 36 | }, 37 | { 38 | path: "/index", 39 | name: "home", 40 | component: Home 41 | }, 42 | { 43 | path: "/", 44 | name: "home", 45 | component: Home 46 | }, 47 | { 48 | path: "/day/video/detail", 49 | name: "detail", 50 | meta: { 51 | title: "视频" 52 | }, 53 | component: () => 54 | import(/* webpackChunkName: "day-video" */ "../views/day/video/detail.vue") 55 | }, 56 | { 57 | path: "/test/canvas/poster", 58 | name: "poster", 59 | meta: { 60 | title: "海报图" 61 | }, 62 | component: () => 63 | import(/* webpackChunkName: "poster" */ "../views/test/canvas/poster.vue") 64 | } 65 | ] 66 | }); 67 | -------------------------------------------------------------------------------- /src/router/routerEach.js: -------------------------------------------------------------------------------- 1 | import router from "./router"; 2 | 3 | router.beforeEach((to, from, next) => { 4 | /* 路由发生变化修改页面title */ 5 | if (to.meta && to.meta.title) { 6 | document.title = to.meta.title; 7 | } else { 8 | // document.title = "我们聊天吧~"; 9 | } 10 | next(); 11 | }); 12 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | if (process.env.NODE_ENV === "development") { 5 | Vue.use(Vuex); 6 | } 7 | 8 | export default new Vuex.Store({ 9 | state: {}, 10 | mutations: {}, 11 | actions: {} 12 | }); 13 | -------------------------------------------------------------------------------- /src/styles/base.scss: -------------------------------------------------------------------------------- 1 | /* reset css */ 2 | *, ::before, ::after { 3 | /*选择所有标签*/ 4 | margin: 0; 5 | padding: 0; 6 | 7 | /*清除移动端默认的点击高亮效果*/ 8 | -webkit-tap-highlight-color: rgba(255, 255, 255, 0); 9 | 10 | /*设置所有的元素都是以边框开始计算宽度的 百分比*/ 11 | box-sizing: border-box; 12 | } 13 | body, html { 14 | height: 100%; 15 | font-family: 'Helvetica Neue',Helvetica,Arial,'Hiragino Sans GB','Microsoft YaHei',sans-serif; 16 | font-size: 14px; 17 | } 18 | #app { 19 | height: 100%; 20 | } 21 | a { 22 | text-decoration: none; 23 | &:hover { 24 | text-decoration: none; 25 | } 26 | } 27 | 28 | ul, ol { 29 | list-style: none; 30 | } 31 | 32 | input { 33 | border: none; 34 | outline: none; 35 | /*清除移动端默认的表单样式*/ 36 | -webkit-appearance: none; 37 | } 38 | -------------------------------------------------------------------------------- /src/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | 2 | // Mixin 3 | 4 | //清除浮动 5 | @mixin clearfix() { 6 | &:before, 7 | &:after { 8 | content: " "; 9 | display: table; 10 | } 11 | &:after { 12 | clear: both; 13 | } 14 | } 15 | 16 | 17 | //水平居中 18 | @mixin center-block() { 19 | display: block; 20 | margin-left: auto; 21 | margin-right: auto; 22 | } 23 | 24 | //全屏定位 25 | @mixin fxied-absolute(){ 26 | position:absolute; 27 | top:0; 28 | right:0; 29 | } 30 | @mixin fxied-fixed(){ 31 | position:fixed; 32 | top:0; 33 | right:0; 34 | } 35 | @mixin fxied-bottom(){ 36 | position:fixed; 37 | bottom: 0; 38 | right: 0; 39 | } 40 | //定位上下左右居中 41 | @mixin fxied-center() { 42 | position: absolute; 43 | top: 50%; 44 | left: 50%; 45 | transform: translate(-50%, -50%); 46 | } 47 | //定位上下居中 48 | @mixin fxied-top() { 49 | position: absolute; 50 | top: 50%; 51 | transform: translateY(-50%); 52 | } 53 | //左右居中 54 | @mixin fxied-left() { 55 | position: absolute; 56 | left: 50%; 57 | transform: translateX(-50%); 58 | } 59 | 60 | 61 | //尺寸助手 62 | @mixin size($width, $height) { 63 | width: $width; 64 | height: $height; 65 | } 66 | //正方形 67 | @mixin square($size) { 68 | @include size($size, $size); 69 | } 70 | 71 | //调整大小的文本域 72 | @mixin resizable($direction: both) { 73 | // Options: horizontal, vertical, both 74 | resize: $direction; 75 | // Safari fix 76 | overflow: auto; 77 | } 78 | 79 | //截断文本 元素必须是 block 或 inline-block 级。 80 | @mixin text-overflow() { 81 | overflow: hidden; 82 | text-overflow: ellipsis; 83 | white-space: nowrap; 84 | } 85 | 86 | //视网膜屏幕(Retina)下的图片 87 | @mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) { 88 | background-image: url($file-1x); 89 | 90 | @media 91 | only screen and (-webkit-min-device-pixel-ratio: 2), 92 | only screen and ( min--moz-device-pixel-ratio: 2), 93 | only screen and ( -o-min-device-pixel-ratio: 2/1), 94 | only screen and ( min-device-pixel-ratio: 2), 95 | only screen and ( min-resolution: 192dpi), 96 | only screen and ( min-resolution: 2dppx) { 97 | background-image: url($file-2x); 98 | background-size: $width-1x $height-1x; 99 | } 100 | } 101 | 102 | //左右浮动 103 | @mixin f_left(){ 104 | float: left; 105 | } 106 | @mixin f_right(){ 107 | float: right; 108 | } 109 | 110 | 111 | @mixin scrollBar { 112 | &::-webkit-scrollbar-track-piece { 113 | background: #d3dce6; 114 | } 115 | &::-webkit-scrollbar { 116 | width: 6px; 117 | } 118 | &::-webkit-scrollbar-thumb { 119 | background: #99a9bf; 120 | border-radius: 20px; 121 | } 122 | } 123 | 124 | @mixin relative { 125 | position: relative; 126 | width: 100%; 127 | height: 100%; 128 | } 129 | 130 | @mixin pct($pct) { 131 | width: #{$pct}; 132 | position: relative; 133 | margin: 0 auto; 134 | } 135 | 136 | @mixin triangle($width, $height, $color, $direction) { 137 | $width: $width/2; 138 | $color-border-style: $height solid $color; 139 | $transparent-border-style: $width solid transparent; 140 | height: 0; 141 | width: 0; 142 | @if $direction==up { 143 | border-bottom: $color-border-style; 144 | border-left: $transparent-border-style; 145 | border-right: $transparent-border-style; 146 | } 147 | @else if $direction==right { 148 | border-left: $color-border-style; 149 | border-top: $transparent-border-style; 150 | border-bottom: $transparent-border-style; 151 | } 152 | @else if $direction==down { 153 | border-top: $color-border-style; 154 | border-left: $transparent-border-style; 155 | border-right: $transparent-border-style; 156 | } 157 | @else if $direction==left { 158 | border-right: $color-border-style; 159 | border-top: $transparent-border-style; 160 | border-bottom: $transparent-border-style; 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/utils/auth.js: -------------------------------------------------------------------------------- 1 | import { getStorage, setStorage, removeStorage } from "./storage"; 2 | 3 | const userId = "WEB-USERID"; 4 | const userToken = "WEB-TOKEN"; 5 | 6 | // 获取token 7 | export function getToken() { 8 | return getStorage(userToken); 9 | } 10 | 11 | // 设置token 12 | export function setToken(userId) { 13 | return setStorage(userToken, userId, 365); 14 | } 15 | 16 | // 删除token 17 | export function removeToken() { 18 | return removeStorage(userToken); 19 | } 20 | 21 | // 获取 id 22 | export function getUserId() { 23 | return getStorage(userId); 24 | } 25 | 26 | // 设置 id 27 | export function setUserId(id) { 28 | return setStorage(userId, id, 365); 29 | } 30 | 31 | // 删除 id 32 | export function removeUserId() { 33 | return removeStorage(userId); 34 | } 35 | -------------------------------------------------------------------------------- /src/utils/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { getUserId, getToken } from "../utils/auth"; 3 | 4 | // 创建axios实例 5 | const service = axios.create({ 6 | baseURL: process.env.VUE_APP_API_BASE, // api的base_url 7 | timeout: 5000 // 请求超时时间 8 | }); 9 | 10 | // request拦截器 11 | service.interceptors.request.use( 12 | config => { 13 | // Do something before request is sent 14 | let userId = getUserId(); 15 | let token = getToken(); 16 | if (userId && token) { 17 | config.headers["X-Web-UserId"] = userId; 18 | config.headers["X-Web-Token"] = token; 19 | } 20 | return config; 21 | }, 22 | error => { 23 | // Do something with request error 24 | Promise.reject(error); 25 | } 26 | ); 27 | 28 | // respone拦截器 29 | service.interceptors.response.use( 30 | response => { 31 | const data = response.data; 32 | if (data.code) { 33 | if (data.code === 20006) { 34 | // 重新登录 35 | } 36 | } 37 | return data; 38 | }, 39 | error => { 40 | return Promise.reject(error); 41 | } 42 | ); 43 | 44 | export default service; 45 | -------------------------------------------------------------------------------- /src/utils/baiduTJhmt.js: -------------------------------------------------------------------------------- 1 | /** 2 | 百度统计 3 | */ 4 | 5 | // 事件统计 6 | export function bdTrackEvent(category, action, opt_label, opt_value) { 7 | if (window._hmt) { 8 | window._hmt.push([ 9 | "_trackEvent", 10 | category, 11 | action, 12 | opt_label, 13 | opt_value 14 | ]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/storage.js: -------------------------------------------------------------------------------- 1 | import Cookies from "js-cookie"; 2 | /** 3 | * 存储localStorage 4 | */ 5 | export const setStorage = (name, content, expireDay) => { 6 | if (!name) return; 7 | if (typeof content !== "string") { 8 | content = JSON.stringify(content); 9 | } 10 | Cookies.set(name, content, { expires: expireDay }); 11 | }; 12 | 13 | /** 14 | * 获取localStorage 15 | */ 16 | export const getStorage = name => { 17 | if (!name) return; 18 | let content = Cookies.get(name); 19 | try { 20 | content = JSON.parse(content); 21 | return content; 22 | } catch (e) { 23 | return content; 24 | } 25 | }; 26 | 27 | /** 28 | * 删除localStorage 29 | */ 30 | export const removeStorage = name => { 31 | if (!name) return; 32 | Cookies.remove(name); 33 | }; 34 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 234 | 235 | 408 | 409 | 510 | -------------------------------------------------------------------------------- /src/views/day/video/detail.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: "day-detail", 3 | data() { 4 | return { 5 | videoDom: null, 6 | isMute: true, 7 | isPlay: false, 8 | isPvPlay: false, // 是否点击过播放 9 | channel: "", // 来源渠道 10 | recommendList: [], 11 | recommendListQuery: { 12 | offset: "", 13 | limit: 20 14 | }, 15 | info: { 16 | type: 0, 17 | user: {} 18 | }, 19 | title: "", 20 | query: { 21 | id: "", 22 | path_url: "", 23 | isErr: "", 24 | create_date: "" 25 | } 26 | }; 27 | }, 28 | components: {}, 29 | methods: {}, 30 | created() { 31 | // 将参数拷贝进查询对象 32 | let query = this.$route.query; 33 | this.query = Object.assign(this.query, query); 34 | this.query.isErr = query.isErr ? "1" : ""; 35 | if (query.c) { 36 | this.channel = 1; // web 渠道,没有则默认是小程序渠道 37 | } 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /src/views/day/video/detail.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | width: 100%; 3 | } 4 | .view-box { 5 | width: 100%; 6 | height: auto; 7 | } 8 | -------------------------------------------------------------------------------- /src/views/day/video/detail.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 11 | -------------------------------------------------------------------------------- /src/views/error/err401.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 31 | 182 | -------------------------------------------------------------------------------- /src/views/error/err404.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 30 | 31 | 150 | -------------------------------------------------------------------------------- /src/views/error/err500.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 32 | 33 | 165 | -------------------------------------------------------------------------------- /src/views/test/canvas/poster.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: "poster", 3 | data() { 4 | return { 5 | ctx: null, 6 | posterImgUrl: null 7 | }; 8 | }, 9 | components: {}, 10 | methods: { 11 | canIUseCanvas(canvas) { 12 | return !!canvas.getContext("2d"); 13 | }, 14 | // 画背景 15 | drawBackground() { 16 | return new Promise(resolve => { 17 | console.log(this.ctx); 18 | let background = new Image(); 19 | background.src = require("../../../assets/image/poster-bg.png"); 20 | background.onload = () => { 21 | this.ctx.drawImage( 22 | background, 23 | 0, 24 | 0, 25 | this.ctx.canvas.width, 26 | this.ctx.canvas.height 27 | ); 28 | resolve(); 29 | }; 30 | }); 31 | }, 32 | // 画文字 33 | drawText() { 34 | return new Promise(resolve => { 35 | this.ctx.fillStyle = "green"; //设置填充的背景颜色 36 | this.ctx.fillRect(0, 0, 800, 300); //绘制 800*300 像素的已填充矩形: 37 | this.ctx.fillStyle = "#fff"; 38 | this.ctx.strokeStyle = "#fff"; //设置笔触的颜色 39 | this.ctx.font = "bold 40px '字体','字体','微软雅黑','宋体'"; //设置字体 40 | this.ctx.textBaseline = "hanging"; //在绘制文本时使用的当前文本基线 41 | this.ctx.fillText("长按二维码", 10, 40); //设置文本内容 42 | resolve(); 43 | }); 44 | }, 45 | // 画头像 46 | drawAvatar() { 47 | return new Promise(resolve => { 48 | resolve(); 49 | }); 50 | }, 51 | // 画二维码 52 | drawQrCode() { 53 | return new Promise(resolve => { 54 | resolve(); 55 | }); 56 | }, 57 | drawAll() { 58 | return Promise.all([ 59 | this.drawBackground(), 60 | this.drawText(), 61 | this.drawAvatar(), 62 | this.drawQrCode() 63 | ]); 64 | } 65 | }, 66 | mounted() { 67 | const canvas = this.$refs.posterCanvas; 68 | if (this.canIUseCanvas(canvas)) { 69 | // canvas.width = this.posterW; 70 | // canvas.height = this.posterH; 71 | this.ctx = canvas.getContext("2d"); 72 | this.drawAll().then(() => { 73 | this.posterImgUrl = this.ctx.canvas.toDataURL("image/png"); 74 | }); 75 | } 76 | }, 77 | created() {} 78 | }; 79 | -------------------------------------------------------------------------------- /src/views/test/canvas/poster.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | width: 100%; 3 | } 4 | .view-box { 5 | width: 100%; 6 | height: auto; 7 | } 8 | .poster-canvas { 9 | display: none; 10 | width: 245px; 11 | height: 436px; 12 | } 13 | .poster-img { 14 | display: block; 15 | margin: 0 auto; 16 | width: 245px; 17 | height: 436px; 18 | } 19 | -------------------------------------------------------------------------------- /src/views/test/canvas/poster.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 11 | 13 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } -------------------------------------------------------------------------------- /tests/unit/example.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import HelloWorld from '@/components/HelloWorld.vue' 3 | 4 | describe('HelloWorld.vue', () => { 5 | it('renders props.msg when passed', () => { 6 | const msg = 'new message' 7 | const wrapper = shallowMount(HelloWorld, { 8 | propsData: { msg } 9 | }) 10 | expect(wrapper.text()).toMatch(msg) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | baseUrl: process.env.NODE_ENV === "production" ? process.env.baseUrl : "/", 3 | outputDir: process.env.outputDir, 4 | configureWebpack: config => { 5 | if (process.env.NODE_ENV === "production") { 6 | // 为生产环境修改配置... 7 | config.externals = { 8 | vue: "Vue", 9 | vuex: "Vuex", 10 | "vue-router": "VueRouter" 11 | }; 12 | } else { 13 | // 为开发环境修改配置... 14 | } 15 | }, 16 | chainWebpack: config => { 17 | config.plugin("html").tap(args => { 18 | args[0].template = process.env.template; 19 | return args; 20 | }); 21 | }, 22 | devServer: { 23 | disableHostCheck: true 24 | } 25 | }; 26 | --------------------------------------------------------------------------------