{{
- content
- }}
+ {{ content }}
diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js new file mode 100644 index 0000000..66793e3 --- /dev/null +++ b/frontend/.eslintrc.js @@ -0,0 +1,79 @@ +module.exports = { + "root": true, + "env": { + "browser": true, + "node": true + }, + "parserOptions": { + "parser": "babel-eslint" + }, + "extends": [ + "plugin:vue/essential", + "eslint:recommended" + ], + "rules": { + "array-bracket-spacing": ["error", "never"], // 数组括号内不加空格 + "arrow-parens": ["error", "as-needed"], // 箭头函数单个参数不加括号 + "arrow-spacing": "error", // 箭头前后加空格 + "block-spacing": "error", // 块大括号内加空格 + "brace-style": "error", // 大括号不独占一行 + "comma-spacing": "error", // 逗号前面不加空格,后面加空格 + "comma-style": "error", // 逗号在语句后面而不是下一条的前面 + "computed-property-spacing": "error", // 计算属性名前后不加空格 + "curly": "error", // 禁止省略大括号 + "dot-notation": "error", // 使用点访问成员 + "eol-last": "error", // 文件末尾加换行符 + "func-call-spacing": "error", // 调用函数名和括号间不加空格 + "func-style": ["error", "declaration", { "allowArrowFunctions": true }], // 使用函数定义语法,而不是把函数表达式赋值到变量 + "indent": ["error", 2], // 缩进2空格 + "key-spacing": ["error", { "mode": "minimum" }], + "keyword-spacing": "error", // 关键词前后加空格 + "lines-between-class-members": "error", // 类成员定义间加空格 + "max-lines-per-function": ["error", 150], // 每个函数最多行数 + "max-nested-callbacks": ["error", 3], // 每个函数最多嵌套回调数 + "new-parens": "error", // new调用构造函数加空格 + "no-array-constructor": "error", // 使用数组字面量,而不是数组构造函数 + "no-floating-decimal": "error", // 禁止省略浮点数首尾的0 + "no-implicit-coercion": "error", // 禁止隐式转换 + "no-empty": ["error", { "allowEmptyCatch": true }], // 禁止空的块,除了catch + "no-extra-parens": ["error", "all", { "nestedBinaryExpressions": false }], // 禁止多余的括号 + "no-labels": "error", // 禁止使用标签 + "no-lone-blocks": "error", // 禁止没用的块 + "no-mixed-operators": "error", // 禁止混用不同优先级的操作符而不加括号 + "no-multi-spaces": ["error", { "ignoreEOLComments": true }], // 禁止多个空格,除了行尾注释前 + "no-multiple-empty-lines": "error", // 最多2个连续空行 + "no-nested-ternary": "error", // 禁止嵌套三元表达式 + "no-sequences": "error", // 禁止使用逗号操作符 + "no-tabs": "error", // 禁止使用tab + "no-trailing-spaces": ["error", { "skipBlankLines": true }], // 禁止行尾的空格,除了空行 + "no-unused-expressions": "error", // 禁止没用的表达式 + "no-useless-concat": "error", // 禁止没用的字符串连接 + "no-useless-rename": "error", // 禁止没用的模块导入重命名、解构赋值重命名 + "no-useless-return": "error", // 禁止没用的return + "no-var": "error", // 禁止使用var声明变量 + "no-void": "error", // 禁止使用void + "no-whitespace-before-property": "error", // 禁止访问属性的点前后加空格 + "object-curly-spacing": ["error", "always"], // 对象字面量括号内加空格 + "operator-assignment": "error", // 尽量使用+= + "operator-linebreak": ["error", "before"], // 操作符放行首 + "prefer-object-spread": "error", // 使用{...obj},而不是Object.assign + "prefer-rest-params": "error", // 使用...args,而不是arguments + "prefer-spread": "error", // 使用func(...args),而不是apply + "prefer-template": "error", // 使用模板字符串,而不是字符串连接 + "rest-spread-spacing": ["error", "never"], // 解包操作符不加空格 + "semi": ["error", "never"], // 禁止使用多余的分号 + "semi-spacing": "error", // 分号前面不加空格,后面加空格 + "semi-style": "error", // 分号在语句后面而不是下一条的前面 + "space-before-blocks": "error", // 块大括号前加空格 + "space-before-function-paren": ["error", "never"], // 函数定义名称和括号间不加空格 + "space-in-parens": "error", // 括号内不加空格 + "space-infix-ops": "error", // 二元操作符前后加空格 + "space-unary-ops": "error", // 关键词一元操作符后加空格,符号一元操作符不加 + "spaced-comment": ["error", "always", { "block": { "balanced": true } }], // 注释前面加空格 + "template-curly-spacing": "error", // 模板字符串中变量大括号内不加空格 + + "no-shadow": "warn", // 变量名和外部作用域重复 + + "no-console": "off", // 线上尽量不要用console输出,看不到的 + } +} diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json new file mode 100644 index 0000000..09e9361 --- /dev/null +++ b/frontend/jsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "es2015", + "module": "esnext", + "baseUrl": "./", + "paths": { + "@/*": ["src/*"] + } + }, + "include": [ + "./src/**/*.js", + "./src/**/*.vue" + ] +} diff --git a/frontend/src/api/chat/ChatClientDirect/index.js b/frontend/src/api/chat/ChatClientDirect/index.js index 85d9788..30b396e 100644 --- a/frontend/src/api/chat/ChatClientDirect/index.js +++ b/frontend/src/api/chat/ChatClientDirect/index.js @@ -1,7 +1,7 @@ import axios from 'axios' -import {BrotliDecode} from './brotli_decode' -import {getUuid4Hex} from '@/utils' +import { BrotliDecode } from './brotli_decode' +import { getUuid4Hex } from '@/utils' import * as avatar from '../avatar' const HEADER_SIZE = 16 @@ -37,18 +37,18 @@ const AUTH_REPLY_CODE_OK = 0 // const AUTH_REPLY_CODE_TOKEN_ERROR = -101 const HEARTBEAT_INTERVAL = 10 * 1000 -const RECEIVE_TIMEOUT = HEARTBEAT_INTERVAL + 5 * 1000 +const RECEIVE_TIMEOUT = HEARTBEAT_INTERVAL + (5 * 1000) let textEncoder = new TextEncoder() let textDecoder = new TextDecoder() export default class ChatClientDirect { - constructor (roomId) { + constructor(roomId) { // 调用initRoom后初始化,如果失败,使用这里的默认值 this.roomId = roomId this.roomOwnerUid = 0 this.hostServerList = [ - {host: "broadcastlv.chat.bilibili.com", port: 2243, wss_port: 443, ws_port: 2244} + { host: "broadcastlv.chat.bilibili.com", port: 2243, wss_port: 443, ws_port: 2244 } ] this.onAddText = null @@ -65,24 +65,24 @@ export default class ChatClientDirect { this.receiveTimeoutTimerId = null } - async start () { + async start() { await this.initRoom() this.wsConnect() } - stop () { + stop() { this.isDestroying = true if (this.websocket) { this.websocket.close() } } - async initRoom () { + async initRoom() { let res try { - res = (await axios.get('/api/room_info', {params: { + res = (await axios.get('/api/room_info', { params: { roomId: this.roomId - }})).data + } })).data } catch { return } @@ -93,7 +93,7 @@ export default class ChatClientDirect { } } - makePacket (data, operation) { + makePacket(data, operation) { let body = textEncoder.encode(JSON.stringify(data)) let header = new ArrayBuffer(HEADER_SIZE) let headerView = new DataView(header) @@ -105,7 +105,7 @@ export default class ChatClientDirect { return new Blob([header, body]) } - sendAuth () { + sendAuth() { let authParams = { uid: 0, roomid: this.roomId, @@ -116,7 +116,7 @@ export default class ChatClientDirect { this.websocket.send(this.makePacket(authParams, OP_AUTH)) } - wsConnect () { + wsConnect() { if (this.isDestroying) { return } @@ -129,13 +129,13 @@ export default class ChatClientDirect { this.websocket.onmessage = this.onWsMessage.bind(this) } - onWsOpen () { + onWsOpen() { this.sendAuth() this.heartbeatTimerId = window.setInterval(this.sendHeartbeat.bind(this), HEARTBEAT_INTERVAL) this.refreshReceiveTimeoutTimer() } - sendHeartbeat () { + sendHeartbeat() { this.websocket.send(this.makePacket({}, OP_HEARTBEAT)) } @@ -147,7 +147,7 @@ export default class ChatClientDirect { } onReceiveTimeout() { - window.console.warn('接收消息超时') + console.warn('接收消息超时') this.discardWebsocket() } @@ -163,7 +163,7 @@ export default class ChatClientDirect { this.onWsClose() } - onWsClose () { + onWsClose() { this.websocket = null if (this.heartbeatTimerId) { window.clearInterval(this.heartbeatTimerId) @@ -178,14 +178,14 @@ export default class ChatClientDirect { return } this.retryCount++ - window.console.warn('掉线重连中', this.retryCount) + console.warn('掉线重连中', this.retryCount) window.setTimeout(this.wsConnect.bind(this), 1000) } - onWsMessage (event) { + onWsMessage(event) { this.refreshReceiveTimeoutTimer() if (!(event.data instanceof ArrayBuffer)) { - window.console.warn('未知的websocket消息类型,data=', event.data) + console.warn('未知的websocket消息类型,data=', event.data) return } @@ -196,7 +196,7 @@ export default class ChatClientDirect { this.retryCount = 0 } - parseWsMessage (data) { + parseWsMessage(data) { let offset = 0 let dataView = new DataView(data.buffer) let packLen = dataView.getUint32(0) @@ -231,13 +231,13 @@ export default class ChatClientDirect { default: { // 未知消息 let body = new Uint8Array(data.buffer, offset + rawHeaderSize, packLen - rawHeaderSize) - window.console.warn('未知包类型,operation=', operation, dataView, body) + console.warn('未知包类型,operation=', operation, dataView, body) break } } } - parseBusinessMessage (dataView, body) { + parseBusinessMessage(dataView, body) { let ver = dataView.getUint16(6) let operation = dataView.getUint32(8) @@ -255,7 +255,7 @@ export default class ChatClientDirect { body = JSON.parse(textDecoder.decode(body)) this.handlerCommand(body) } catch (e) { - window.console.error('body=', body) + console.error('body=', body) throw e } } @@ -266,7 +266,7 @@ export default class ChatClientDirect { // 认证响应 body = JSON.parse(textDecoder.decode(body)) if (body.code !== AUTH_REPLY_CODE_OK) { - window.console.error('认证响应错误,body=', body) + console.error('认证响应错误,body=', body) // 这里应该重新获取token再重连的,但前端没有用到token,所以不重新init了 this.discardWebsocket() throw new Error('认证响应错误') @@ -276,13 +276,13 @@ export default class ChatClientDirect { } default: { // 未知消息 - window.console.warn('未知包类型,operation=', operation, dataView, body) + console.warn('未知包类型,operation=', operation, dataView, body) break } } } - handlerCommand (command) { + handlerCommand(command) { let cmd = command.cmd || '' let pos = cmd.indexOf(':') if (pos != -1) { @@ -294,7 +294,7 @@ export default class ChatClientDirect { } } - async danmuMsgCallback (command) { + async danmuMsgCallback(command) { if (!this.onAddText) { return } @@ -329,10 +329,10 @@ export default class ChatClientDirect { authorType: authorType, content: info[1], privilegeType: privilegeType, - isGiftDanmaku: !!info[0][9], + isGiftDanmaku: Boolean(info[0][9]), authorLevel: info[4][0], isNewbie: info[2][5] < 10000, - isMobileVerified: !!info[2][6], + isMobileVerified: Boolean(info[2][6]), medalLevel: roomId === this.roomId ? medalLevel : 0, id: getUuid4Hex(), translation: '', @@ -341,7 +341,7 @@ export default class ChatClientDirect { this.onAddText(data) } - sendGiftCallback (command) { + sendGiftCallback(command) { if (!this.onAddGift) { return } @@ -362,7 +362,7 @@ export default class ChatClientDirect { this.onAddGift(data) } - async guardBuyCallback (command) { + async guardBuyCallback(command) { if (!this.onAddMember) { return } @@ -378,7 +378,7 @@ export default class ChatClientDirect { this.onAddMember(data) } - superChatMessageCallback (command) { + superChatMessageCallback(command) { if (!this.onAddSuperChat) { return } @@ -396,7 +396,7 @@ export default class ChatClientDirect { this.onAddSuperChat(data) } - superChatMessageDeleteCallback (command) { + superChatMessageDeleteCallback(command) { if (!this.onDelSuperChat) { return } @@ -405,7 +405,7 @@ export default class ChatClientDirect { for (let id of command.data.ids) { ids.push(id.toString()) } - this.onDelSuperChat({ids}) + this.onDelSuperChat({ ids }) } } diff --git a/frontend/src/api/chat/ChatClientRelay.js b/frontend/src/api/chat/ChatClientRelay.js index 401a350..e44bea1 100644 --- a/frontend/src/api/chat/ChatClientRelay.js +++ b/frontend/src/api/chat/ChatClientRelay.js @@ -11,10 +11,10 @@ const COMMAND_UPDATE_TRANSLATION = 7 const CONTENT_TYPE_EMOTICON = 1 const HEARTBEAT_INTERVAL = 10 * 1000 -const RECEIVE_TIMEOUT = HEARTBEAT_INTERVAL + 5 * 1000 +const RECEIVE_TIMEOUT = HEARTBEAT_INTERVAL + (5 * 1000) export default class ChatClientRelay { - constructor (roomId, autoTranslate) { + constructor(roomId, autoTranslate) { this.roomId = roomId this.autoTranslate = autoTranslate @@ -32,18 +32,18 @@ export default class ChatClientRelay { this.receiveTimeoutTimerId = null } - start () { + start() { this.wsConnect() } - stop () { + stop() { this.isDestroying = true if (this.websocket) { this.websocket.close() } } - wsConnect () { + wsConnect() { if (this.isDestroying) { return } @@ -57,7 +57,7 @@ export default class ChatClientRelay { this.websocket.onmessage = this.onWsMessage.bind(this) } - onWsOpen () { + onWsOpen() { this.retryCount = 0 this.websocket.send(JSON.stringify({ cmd: COMMAND_JOIN_ROOM, @@ -72,7 +72,7 @@ export default class ChatClientRelay { this.refreshReceiveTimeoutTimer() } - sendHeartbeat () { + sendHeartbeat() { this.websocket.send(JSON.stringify({ cmd: COMMAND_HEARTBEAT })) @@ -86,7 +86,7 @@ export default class ChatClientRelay { } onReceiveTimeout() { - window.console.warn('接收消息超时') + console.warn('接收消息超时') this.receiveTimeoutTimerId = null // 直接丢弃阻塞的websocket,不等onclose回调了 @@ -95,7 +95,7 @@ export default class ChatClientRelay { this.onWsClose() } - onWsClose () { + onWsClose() { this.websocket = null if (this.heartbeatTimerId) { window.clearInterval(this.heartbeatTimerId) @@ -109,14 +109,14 @@ export default class ChatClientRelay { if (this.isDestroying) { return } - window.console.warn(`掉线重连中${++this.retryCount}`) + console.warn(`掉线重连中${++this.retryCount}`) window.setTimeout(this.wsConnect.bind(this), 1000) } - onWsMessage (event) { + onWsMessage(event) { this.refreshReceiveTimeoutTimer() - let {cmd, data} = JSON.parse(event.data) + let { cmd, data } = JSON.parse(event.data) switch (cmd) { case COMMAND_HEARTBEAT: { break @@ -140,10 +140,10 @@ export default class ChatClientRelay { authorType: data[3], content: data[4], privilegeType: data[5], - isGiftDanmaku: !!data[6], + isGiftDanmaku: Boolean(data[6]), authorLevel: data[7], - isNewbie: !!data[8], - isMobileVerified: !!data[9], + isNewbie: Boolean(data[8]), + isMobileVerified: Boolean(data[9]), medalLevel: data[10], id: data[11], translation: data[12], diff --git a/frontend/src/api/chat/ChatClientTest.js b/frontend/src/api/chat/ChatClientTest.js index fa73553..7ac6231 100644 --- a/frontend/src/api/chat/ChatClientTest.js +++ b/frontend/src/api/chat/ChatClientTest.js @@ -1,4 +1,4 @@ -import {getUuid4Hex} from '@/utils' +import { getUuid4Hex } from '@/utils' import * as constants from '@/components/ChatRenderer/constants' import * as avatar from './avatar' @@ -19,7 +19,7 @@ const CONTENTS = [ '有一说一,这件事大家懂的都懂,不懂的,说了你也不明白,不如不说', '让我看看', '我柜子动了,我不玩了' ] - // TODO 改成对象? +// TODO 改成对象? const EMOTICONS = [ '/static/img/emoticons/233.png', '/static/img/emoticons/miaoa.png', @@ -27,13 +27,13 @@ const EMOTICONS = [ ] const AUTHOR_TYPES = [ - {weight: 10, value: constants.AUTHRO_TYPE_NORMAL}, - {weight: 5, value: constants.AUTHRO_TYPE_MEMBER}, - {weight: 2, value: constants.AUTHRO_TYPE_ADMIN}, - {weight: 1, value: constants.AUTHRO_TYPE_OWNER} + { weight: 10, value: constants.AUTHRO_TYPE_NORMAL }, + { weight: 5, value: constants.AUTHRO_TYPE_MEMBER }, + { weight: 2, value: constants.AUTHRO_TYPE_ADMIN }, + { weight: 1, value: constants.AUTHRO_TYPE_OWNER } ] -function randGuardInfo () { +function randGuardInfo() { let authorType = randomChoose(AUTHOR_TYPES) let privilegeType if (authorType === constants.AUTHRO_TYPE_MEMBER || authorType === constants.AUTHRO_TYPE_ADMIN) { @@ -41,16 +41,16 @@ function randGuardInfo () { } else { privilegeType = 0 } - return {authorType, privilegeType} + return { authorType, privilegeType } } const GIFT_INFO_LIST = [ - {giftName: 'B坷垃', totalCoin: 9900}, - {giftName: '礼花', totalCoin: 28000}, - {giftName: '花式夸夸', totalCoin: 39000}, - {giftName: '天空之翼', totalCoin: 100000}, - {giftName: '摩天大楼', totalCoin: 450000}, - {giftName: '小电视飞船', totalCoin: 1245000} + { giftName: 'B坷垃', totalCoin: 9900 }, + { giftName: '礼花', totalCoin: 28000 }, + { giftName: '花式夸夸', totalCoin: 39000 }, + { giftName: '天空之翼', totalCoin: 100000 }, + { giftName: '摩天大楼', totalCoin: 450000 }, + { giftName: '小电视飞船', totalCoin: 1245000 } ] const SC_PRICES = [ @@ -159,7 +159,7 @@ const MESSAGE_GENERATORS = [ } ] -function randomChoose (nodes) { +function randomChoose(nodes) { if (nodes.length === 0) { return null } @@ -187,12 +187,12 @@ function randomChoose (nodes) { return null } -function randInt (min, max) { - return Math.floor(min + (max - min + 1) * Math.random()) +function randInt(min, max) { + return Math.floor(min + ((max - min + 1) * Math.random())) } export default class ChatClientTest { - constructor () { + constructor() { this.minSleepTime = 800 this.maxSleepTime = 1200 @@ -206,25 +206,25 @@ export default class ChatClientTest { this.timerId = null } - start () { + start() { this.refreshTimer() } - stop () { + stop() { if (this.timerId) { window.clearTimeout(this.timerId) this.timerId = null } } - refreshTimer () { + refreshTimer() { this.timerId = window.setTimeout(this.onTimeout.bind(this), randInt(this.minSleepTime, this.maxSleepTime)) } - onTimeout () { + onTimeout() { this.refreshTimer() - let {type, message} = randomChoose(MESSAGE_GENERATORS)() + let { type, message } = randomChoose(MESSAGE_GENERATORS)() switch (type) { case constants.MESSAGE_TYPE_TEXT: this.onAddText(message) diff --git a/frontend/src/api/chat/avatar.js b/frontend/src/api/chat/avatar.js index 2de484a..8ddbb0a 100644 --- a/frontend/src/api/chat/avatar.js +++ b/frontend/src/api/chat/avatar.js @@ -2,7 +2,7 @@ import axios from 'axios' export const DEFAULT_AVATAR_URL = '//static.hdslb.com/images/member/noface.gif' -export function processAvatarUrl (avatarUrl) { +export function processAvatarUrl(avatarUrl) { // 去掉协议,兼容HTTP、HTTPS let m = avatarUrl.match(/(?:https?:)?(.*)/) if (m) { @@ -15,12 +15,12 @@ export function processAvatarUrl (avatarUrl) { return avatarUrl } -export async function getAvatarUrl (uid) { +export async function getAvatarUrl(uid) { let res try { - res = (await axios.get('/api/avatar_url', {params: { + res = (await axios.get('/api/avatar_url', { params: { uid: uid - }})).data + } })).data } catch { return DEFAULT_AVATAR_URL } diff --git a/frontend/src/api/chatConfig.js b/frontend/src/api/chatConfig.js index c315f30..f8c44c5 100644 --- a/frontend/src/api/chatConfig.js +++ b/frontend/src/api/chatConfig.js @@ -1,4 +1,4 @@ -import {mergeConfig} from '@/utils' +import { mergeConfig } from '@/utils' export const DEFAULT_CONFIG = { minGiftPrice: 7, // $1 @@ -22,15 +22,15 @@ export const DEFAULT_CONFIG = { giftUsernamePronunciation: '' } -export function setLocalConfig (config) { +export function setLocalConfig(config) { config = mergeConfig(config, DEFAULT_CONFIG) window.localStorage.config = JSON.stringify(config) } -export function getLocalConfig () { +export function getLocalConfig() { try { return mergeConfig(JSON.parse(window.localStorage.config), DEFAULT_CONFIG) } catch { - return {...DEFAULT_CONFIG} + return { ...DEFAULT_CONFIG } } } diff --git a/frontend/src/components/ChatRenderer/MembershipItem.vue b/frontend/src/components/ChatRenderer/MembershipItem.vue index 5e3ad2e..f447636 100644 --- a/frontend/src/components/ChatRenderer/MembershipItem.vue +++ b/frontend/src/components/ChatRenderer/MembershipItem.vue @@ -9,9 +9,9 @@