diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 0000000000000000000000000000000000000000..ebada7e1ca9fe9432ab824518f12553c2dfa5a5b --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,3 @@ +> 1% +last 2 versions +ie >= 9 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..8a04ad908c252be593b9dc4eb72c57c45558689c --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +[*.{js,jsx,ts,tsx,vue,css,scss,md,html,json}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +charset = utf-8 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000000000000000000000000000000000..f9102de6c64fb63e763ffd46892d35f928ea67d0 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,39 @@ +.DS_Store + +# Logs +logs +*.log* + +# Dependency directories +**/node_modules +**/jspm_packages + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Editor directories and files +.idea +#.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Polyv +*.html +*.min.js +**/.dist/ +**/dist/ +**/dist-saas/ +**/map-file/** +**/component-icons/** \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000000000000000000000000000000000..e24cca08bdb6a13248b70429f8ed2fe459d8d7be --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,31 @@ +/* eslint-env node */ + +module.exports = { + root: true, + extends: [ + './node_modules/@polyv/eslint-config/lib/for-vue', + './node_modules/@polyv/eslint-config/lib/for-ts', + 'plugin:prettier/recommended', + ], + parserOptions: { + parser: '@typescript-eslint/parser', + extraFileExtensions: ['.vue'], + }, + globals: { + PROJECT_BUILD_TIME: 'readonly', + PROJECT_VERSION: 'readonly', + }, + rules: { + 'no-console': 'off', + 'prettier/prettier': 'warn', + 'vue/v-on-event-hyphenation': [ + 'warn', + 'always', + { + ignore: ['statusChange'], + }, + ], + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + }, +}; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..d923235e343099be8824a94db2128ceb4bbaa9fe --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +*.js eol=lf +*.vue eol=lf +*.css eol=lf +*.scss eol=lf +*.json eol=lf +*.md eol=lf +*.jsx eol=lf +*.ts eol=lf +*.svg eol=lf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f15741c9e9daa7192ddc6f0ae5409b2014578922 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +.DS_Store + +# Logs +logs +*.log* + +# Dependency directories +**/node_modules +**/jspm_packages + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Editor directories and files +.idea +#.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +dist +dist-static \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000000000000000000000000000000000000..3e1cef88434f836b21dae31042ffd74923b0176b --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" + +npm run precommit diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000000000000000000000000000000000..f9102de6c64fb63e763ffd46892d35f928ea67d0 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,39 @@ +.DS_Store + +# Logs +logs +*.log* + +# Dependency directories +**/node_modules +**/jspm_packages + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Editor directories and files +.idea +#.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Polyv +*.html +*.min.js +**/.dist/ +**/dist/ +**/dist-saas/ +**/map-file/** +**/component-icons/** \ No newline at end of file diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000000000000000000000000000000000000..bdea8ad6a2f0d07d4dd36358da34d4b9b0b2f766 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,12 @@ +/** @type {import("prettier").Config} */ +module.exports = { + printWidth: 100, + singleQuote: true, + bracketSameLine: false, + jsxBracketSameLine: true, + trailingComma: 'all', + semi: true, + arrowParens: 'avoid', + quoteProps: 'as-needed', + singleAttributePerLine: true, +}; diff --git a/.stylelintignore b/.stylelintignore new file mode 100644 index 0000000000000000000000000000000000000000..9dc8639e9cc58920f40c04221992f5e712538c48 --- /dev/null +++ b/.stylelintignore @@ -0,0 +1,40 @@ +.DS_Store + +# Logs +logs +*.log* + +# Dependency directories +**/node_modules +**/jspm_packages + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Output of 'npm pack' +*.tgz + +# Editor directories and files +.idea +#.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Polyv +*.html +*.min.js +*.svg +**/.dist/ +**/dist/ +**/dist-saas/ +**/map-file/** +**/component-icons/** \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000000000000000000000000000000000..a8acdffb9e2b53c1073119dc6b6e5a75130cbc0e --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,12 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", + "vue.volar", + "stylelint.vscode-stylelint", + "dbaeumer.vscode-eslint", + ], + "unwantedRecommendations": [ + "HookyQR.beautify", + "octref.vetur" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..48e32fedb0e1df7a271e123e8946ff094c5ee2e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,38 @@ +{ + "prettier.enable": true, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "stylelint.vscode-stylelint" + }, + "[scss]": { + "editor.defaultFormatter": "stylelint.vscode-stylelint" + }, + "css.validate": false, + "less.validate": false, + "scss.validate": false, + "stylelint.enable": true, + "stylelint.validate": [ + "css", + "sass", + "scss", + "postcss", + "vue" + ], + "eslint.format.enable": true, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll.stylelint": true + }, + "typescript.tsdk": "node_modules/typescript/lib" +} \ No newline at end of file diff --git a/README.md b/README.md index 7a4e7ea2e50eb20d9906e12750f6116996c98359..1350770a75c656c392efb03abca398566ef51283 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,123 @@ # polyv-web-live-watch-sdk -#### 介绍 -保利威直播观看 SDK +## 介绍 -#### 软件架构 -软件架构说明 +- 为了让客户能够更便捷、友好地定制观看页,保利威推出了**POLYV 直播观看 SDK**,并将该 SDK 拆分成 UI 层和逻辑层 +- 该开源项目对应 **POLYV 直播观看 SDK** 的 UI 层,需要搭配逻辑层 `@polyv/live-watch-sdk` 一起使用 +## 项目说明 -#### 安装教程 +该开源项目基于 `vue-cli@4.x + vue@2.7 + typescript@4.x + pinia` 实现的单页面应用(SPA),除 `@polyv/live-watch-sdk` 外,还集成了其他**保利威 npm 库**和部分第三方开源工具库 -1. xxxx -2. xxxx -3. xxxx +| 保利威 npm 库 | 备注 | +| :----------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------- | +| [@polyv/emotion-sdk](https://www.npmjs.com/package/@polyv/emotion-sdk) | POLYV 表情 SDK | +| [@polyv/utils](https://www.npmjs.com/package/@polyv/utils) | POLYV 工具函数库 | +| [@polyv/web-view-bridge](https://www.npmjs.com/package/@polyv/web-view-bridge) | POLYV 前端 WebView 桥接器库 | +| [@polyv/eslint-config](https://www.npmjs.com/package/@polyv/eslint-config) | POLYV 前端工程 ESLint 通用配置 | +| @polyv/polyv-ui | POLYV 前端组件库【暂不对外提供文档】 | +| [@polyv/icons-vue](https://www.npmjs.com/package/@polyv/icons-vue) | POLYV 图标库工具 ,搭配 [@polyv/icons-cli](https://www.npmjs.com/package/@polyv/icons-cli) 使用 | -#### 使用说明 +## 项目运行 -1. xxxx -2. xxxx -3. xxxx +项目运行前请**注意** -#### 参与贡献 +- 确保 `node` 版本 `^14.15.4 || >=16.0.0"` +- 了解保利威基础业务概念,比如:频道号(channelId) +- 由于项目初始化会自动设置 `husky` 钩子,请确保项目根目录存在 `.git` 文件夹,或者通过这篇[文档](https://typicode.github.io/husky/#/?id=custom-directory)指定对应的 `.git` 文件夹位置。如果完全不使用 `git`,请删除 `package.json` 中的 `prepare` 脚本命令 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +在项目根目录使用 `npm` 来执行以下命令 +```sh +npm ci #安装依赖 +npm run dev #启动项目 +``` -#### 特技 +执行完成后,就可以在浏览器打开 `http://localhost:15020/index.html?channelId={channelId}` 来访问页面 -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +## 构建部署 + +请先阅读以下两篇官方文档 + +- [vue-cli 构建目标](https://cli.vuejs.org/zh/guide/build-targets.html) +- [vue-cli 部署](https://cli.vuejs.org/zh/guide/deployment.html) + +如果需要自定义**生产环境构建文件的目录(outputDir)**,或者更改**部署应用包时的基本 URL(publicPath)**,请查看 `build/build-config.js` 和 `vue.config.js` 后自行修改 + +### 基本构建和部署 + +如果需要用 Web 应用服务器来部署,比如 `Nginx`,那可以执行以下命令 + +```sh +npm run build +``` + +- 执行完成后,源码会构建输出到 `dist` 文件夹中,将该文件夹上传到服务器后,还需要修改 `Nginx` 的配置来支持页面访问 +- 如果需要本地预览,请先执行[静态构建和部署](#静态构建和部署) + +### 静态构建和部署 + +如果只需要简单上传代码到服务器后就能完成部署,那可以执行以下命令 + +```sh +npm run build-static +``` + +- 执行完成后,源码会构建输出到 `dist-static` 文件夹中,只要将该文件夹上传到服务器即完成部署 +- 如果需要本地预览,可以启动一个 HTTP 服务器来访问 `dist-static` 文件夹,在本地预览生产环境构建最简单的方式就是使用一个 Node.js 静态文件服务器,例如 [serve](https://github.com/vercel/serve) + +## 目录说明 + +项目根目录说明 + +| 路径 | 说明 | +| :--------- | :------------------------------------------------------------------------------------------------------------------------------ | +| .husky/ | [husky](https://github.com/typicode/husky) 钩子文件夹 | +| build/ | 构建逻辑 | +| public/ | 静态资源文件夹,请参考这篇[文档](https://cli.vuejs.org/zh/guide/html-and-static-assets.html#public-%E6%96%87%E4%BB%B6%E5%A4%B9) | +| icon-svgs/ | svg 图标文件夹 | +| types/ | 全局的 typescript 类型 | +| src/ | 项目源码 | + +`src` 目录说明 + +| 路径 | 说明 | +| :------------- | :---------------------------------------------------------------------------------------------------------------------------------------- | +| src/core | 搭配 `@polyv/live-watch-sdk` 逻辑层 | +| src/app | 项目应用入口 | +| src/store | 项目数据存储层 | +| src/pages | 项目业务页面,分为`引导页(splash)`和`观看页(watch)`,页面内再通过 `桌面端(pc)`,`移动端横屏(mobile)`,`移动端竖屏(portrait)` 来区分使用场景 | +| src/components | [项目相关组件](src/components/README.md) | +| src/hooks | [项目公共 hooks](src/hooks/README.md) | +| src/assets | 项目静态资源 , `src/assets/lang` 存放多语言文本 | +| src/skins | 项目多皮肤 | +| src/plugins | 项目第三方插件 | + +关键文件指引 + +- 主入口 + - `src/main.ts` + - `src/app/watch-app.vue` +- 搭配 SDK 逻辑层 + - `src/core/watch-sdk.ts` + - `src/store/use-watch-app-store.ts` => `resetUpWatchCore` +- 主业务 + - 引导页进观看页 `src/app/layout/main-enter/_hooks/use-main-enter.ts` + - 观看条件处理 + - `src/components/page-splash-common/auth/hooks/use-auth-common.ts` + - `src/components/page-splash-common/auth/hooks/use-auth-special.ts` + - 互动 UI 加载 `src/components/page-watch-common/interactive-receive/load-iar-ui.ts` + +## 补充说明 + +### 构建图标组件 + +- 图标组件通过 `@polyv/icons-cli` 脚手架生成,执行 `npm run generate-icon` 会构建到 `src/components/component-icons` 中 +- 需要添加图标时,需要开发者根据移动端和桌面端对图标分类,添加 svg 文件到 `icon-svgs` 对应目录下 +- 在 `icon-svgs/demo/` 文件夹中可以查看到项目中的图标示例 +- `vue` 图标组件中统一使用脚手架生成的 `map.ts` 文件引入 + +### 关于项目校验 + +- 目前该项目支持在 `git commit` 时进行校验,如校验失败需自动修复,可以执行 `npm run lint-fix` 尝试修复,如不需要该校验流程,请移除 `package.json` 中的 `precommit` 命令 +- 由于项目中使用的 `fork-ts-checker-webpack-plugin` 不支持 `vue2.7` 对 `script setup` 区块的代码校验,建议在生产构建前,执行 `npm run check` 来检查一遍代码 diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 0000000000000000000000000000000000000000..49fcd1339f9cbf2fcfdca9fac2f19a5e46db30e6 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,13 @@ +const isProd = process.env.NODE_ENV === 'production'; +const ProdPlugins = isProd + ? [['transform-remove-console', { exclude: ['error', 'warn', 'info'] }]] + : []; + +module.exports = { + presets: ['@vue/cli-plugin-babel/preset'], + plugins: [ + '@babel/plugin-proposal-nullish-coalescing-operator', + '@babel/plugin-proposal-optional-chaining', + ...ProdPlugins, + ], +}; diff --git a/build/build-config.js b/build/build-config.js new file mode 100644 index 0000000000000000000000000000000000000000..b9dde23295b0f860f9ec0e74f9215ab5b8e633f3 --- /dev/null +++ b/build/build-config.js @@ -0,0 +1,106 @@ +/** + * @file 包信息 + */ + +/* eslint-env node */ +/* eslint-disable prefer-const */ +/* eslint-disable prettier/prettier */ + +const path = require('path'); +const pkgJSON = require('../package'); + +const { formatDate } = require('@polyv/utils/dist/cjs/date'); + +/** 当前是否为开发模式 */ +const devMode = (exports.devMode = process.env.NODE_ENV !== 'production'); + +let publicPath = process.env.VUE_APP_BUILD_MODE === 'STATIC' ? './' : '/'; + + + +exports.publicPath = publicPath; + +/** 项目构建时间 */ +const projectBuildTime = (exports.projectBuildTime = formatDate(new Date(), 'YYYY-MM-DD hh:mm:ss')); + +/** 项目版本号 */ +const projectVersion = (exports.projectVersion = pkgJSON.version); + +/** + * 获取观看页 SDK 环境变量 + * */ +function getWatchSdkEnv() { + if (devMode) return 'test'; + + return 'prod'; +} + +/** 注入的全局常量 */ +exports.defineConstants = { + /** 项目构建时间 */ + PROJECT_BUILD_TIME: JSON.stringify(projectBuildTime), + /** 项目版本号 */ + PROJECT_VERSION: JSON.stringify(projectVersion), + /** 观看页 SDK 加载的环境 */ + WATCH_SDK_ENV: JSON.stringify(getWatchSdkEnv()), +}; + +/** ts 配置文件 */ +exports.tsConfigFile = path.resolve( + __dirname, + devMode ? '../tsconfig.json' : '../tsconfig.build.json', +); + +/** 需要编译的包名 */ +exports.transpileDependencies = [ + 'pinia', + '@vue/devtools-api', + '@just4', + 'lodash-es', + 'js-base64', + '@polyv/utils', +]; + +/** 需要配置到 sass-resources-loader 的资源 */ +exports.sassResources = ['assets/styles/variables.scss', 'assets/styles/mixins.scss']; + +let devAlias = {}; + + +/** 别名 */ +exports.alias = { + '@': path.resolve(__dirname, '../src'), + '@utils-ts': path.resolve(__dirname, '../node_modules/@polyv/utils/dist/es'), + ...devAlias, +}; + +/** 开发服务器端口号 */ +exports.devServerPort = 15020; + +/** 开发服务器局域网ip */ +exports.devServerHost = '0.0.0.0'; + +/** 本地开发服务器代理 */ +exports.devServerProxy = { + '^/(v2|error-verify)': { + target: 'http://live.polyv.cn', // 测试环境 + changeOrigin: true, + onProxyReq(proxyReq) { + proxyReq.setHeader('host', 'live.polyv.cn'); + }, + onProxyRes(proxyRes) { + const cookies = proxyRes.headers['set-cookie']; + if (cookies) { + const newCookie = cookies.map(item => { + return item.replace(/;\s*Domain=([^;])+/, ''); + }); + proxyRes.headers['set-cookie'] = newCookie; + } + }, + }, +}; + +/** 生产环境构建文件的目录 */ +let outputDir = process.env.VUE_APP_BUILD_MODE === 'STATIC' ? 'dist-static' : 'dist'; + +exports.outputDir = outputDir; diff --git a/build/core-import.js b/build/core-import.js new file mode 100644 index 0000000000000000000000000000000000000000..1da842f99923b0c4b79bcf30dea53555b7e93b74 --- /dev/null +++ b/build/core-import.js @@ -0,0 +1,7 @@ +const path = require('path'); + +const nodeModulesPath = path.resolve(__dirname, '../node_modules'); + +exports.coreJsImports = ['weak-map'].map(item => { + return path.join(nodeModulesPath, `core-js/es/${item}`); +}); diff --git a/build/tag-config.js b/build/tag-config.js new file mode 100644 index 0000000000000000000000000000000000000000..d7245b527521cf859fd8faeccd63cdd40d112975 --- /dev/null +++ b/build/tag-config.js @@ -0,0 +1,23 @@ +/** + * @file 自动引入 js 的配置 + */ +const { devMode } = require('./build-config'); + +/** externals 列表 */ +exports.externalConfigs = devMode + ? {} + : { + vue: 'Vue', + }; + +/** 引入配置 */ +exports.tagConfigs = devMode + ? [] + : [ + /** Vue */ + { + path: 'https://s4.videocc.net/library/vue/2.x/vue-2.7.14.runtime.min.js', + usePublicPath: false, + append: false, + }, + ]; diff --git a/build/webpack.vue.config.js b/build/webpack.vue.config.js new file mode 100644 index 0000000000000000000000000000000000000000..fc2a3ec6c6a55ba4d1a6e8cd6d84233a6b49a2fa --- /dev/null +++ b/build/webpack.vue.config.js @@ -0,0 +1,132 @@ +const path = require('path'); +const webpack = require('webpack'); + +const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin'); +const ESLintPlugin = require('eslint-webpack-plugin'); +const StyleLintPlugin = require('stylelint-webpack-plugin'); +const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); +const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); +const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); + +const { sassResources, alias, tsConfigFile, defineConstants, devMode } = require('./build-config'); +const { externalConfigs, tagConfigs } = require('./tag-config'); + +const srcPath = path.resolve(__dirname, '../src'); +const include = [srcPath]; + +/** @type {import('webpack').Configuration} */ +let webpackVueConfig = { + resolve: { + extensions: ['.js', '.mjs', '.ts', '.vue', '.css', '.scss'], + alias: { + ...alias, + }, + }, + + module: { + rules: [ + { + test: /\.ts$/, + use: [ + 'babel-loader', + { + loader: 'ts-loader', + options: { + transpileOnly: true, + appendTsSuffixTo: ['\\.vue$'], + configFile: tsConfigFile, + }, + }, + ], + exclude: /node_modules/, + include: srcPath, + }, + { + test: /\.ico$/i, + use: [ + { + loader: 'url-loader', + options: { + limit: false, + }, + }, + ], + exclude: /node_modules/, + }, + { + test: /\.scss$/, + include, + use: [ + { + loader: 'sass-resources-loader', + options: { + resources: sassResources.map(function (item) { + return path.join(srcPath, item); + }), + }, + }, + ], + }, + { + test: /\.svga$/i, + use: [ + { + loader: 'url-loader', + options: { + limit: false, + }, + }, + ], + exclude: /node_modules/, + }, + ], + }, + + externals: externalConfigs, + + plugins: [ + new webpack.DefinePlugin(defineConstants), + + new HtmlWebpackTagsPlugin({ + tags: tagConfigs, + }), + + new ForkTsCheckerWebpackPlugin({ + typescript: { + configFile: tsConfigFile, + extensions: { + vue: { + enabled: true, + compiler: require.resolve('vue/compiler-sfc'), + }, + diagnosticOptions: { + semantic: true, + }, + }, + }, + }), + + new ESLintPlugin({ + lintDirtyModulesOnly: devMode, + extensions: ['js', 'ts', 'vue'], + }), + + new StyleLintPlugin({ + lintDirtyModulesOnly: devMode, + files: ['**/*.{vue,css,scss}'], + }), + ], +}; + +if (process.env.USE_ANALYZER) { + webpackVueConfig.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + }), + ); + + const smp = new SpeedMeasurePlugin(); + webpackVueConfig = smp.wrap(webpackVueConfig); +} + +module.exports = webpackVueConfig; diff --git a/icon-svgs/demo/icon-demo-mobile.html b/icon-svgs/demo/icon-demo-mobile.html new file mode 100644 index 0000000000000000000000000000000000000000..a8be220361e2da8e7e9d38adf829e5d17e374def --- /dev/null +++ b/icon-svgs/demo/icon-demo-mobile.html @@ -0,0 +1,1977 @@ + + + + + + 移动端观看页图标 + + + +
+ + + + + + + + diff --git a/icon-svgs/demo/icon-demo-pc.html b/icon-svgs/demo/icon-demo-pc.html new file mode 100644 index 0000000000000000000000000000000000000000..45256e21f8cf150cfadce64a3050e7718f40e366 --- /dev/null +++ b/icon-svgs/demo/icon-demo-pc.html @@ -0,0 +1,2074 @@ + + + + + + PC 端观看页图标 + + + +
+ + + + + + + + diff --git a/icon-svgs/mobile/arrow-down.svg b/icon-svgs/mobile/arrow-down.svg new file mode 100644 index 0000000000000000000000000000000000000000..c85297024687e26757c8ee69408674752d6bc1ab --- /dev/null +++ b/icon-svgs/mobile/arrow-down.svg @@ -0,0 +1,6 @@ + + 箭头-下 + + diff --git a/icon-svgs/mobile/arrow-left.svg b/icon-svgs/mobile/arrow-left.svg new file mode 100644 index 0000000000000000000000000000000000000000..8a357219eefab8133390d8aaa5aed7ac6c1cb70b --- /dev/null +++ b/icon-svgs/mobile/arrow-left.svg @@ -0,0 +1,4 @@ + + 箭头-左 + + diff --git a/icon-svgs/mobile/arrow-right.svg b/icon-svgs/mobile/arrow-right.svg new file mode 100644 index 0000000000000000000000000000000000000000..0e1a290873ea1de9205ee4eeb72aaa34e8fba44a --- /dev/null +++ b/icon-svgs/mobile/arrow-right.svg @@ -0,0 +1,4 @@ + + 箭头-右 + + diff --git a/icon-svgs/mobile/arrow-up.svg b/icon-svgs/mobile/arrow-up.svg new file mode 100644 index 0000000000000000000000000000000000000000..7dada658e19970ac833ac78774e36891beca3c21 --- /dev/null +++ b/icon-svgs/mobile/arrow-up.svg @@ -0,0 +1,4 @@ + + 箭头-上 + + diff --git a/icon-svgs/mobile/booking.svg b/icon-svgs/mobile/booking.svg new file mode 100644 index 0000000000000000000000000000000000000000..0eb465872293690f6c2ba33efd6b52267aebd697 --- /dev/null +++ b/icon-svgs/mobile/booking.svg @@ -0,0 +1,7 @@ + + 预约 + + diff --git a/icon-svgs/mobile/check-round-fill.svg b/icon-svgs/mobile/check-round-fill.svg new file mode 100644 index 0000000000000000000000000000000000000000..589d4d147be10447bfc7c1f625c333cca3556398 --- /dev/null +++ b/icon-svgs/mobile/check-round-fill.svg @@ -0,0 +1,6 @@ + + 选中-圆-填充 + + diff --git a/icon-svgs/mobile/check.svg b/icon-svgs/mobile/check.svg new file mode 100644 index 0000000000000000000000000000000000000000..7197a3892f5798895b6a9233a36cee7dd9fd3ade --- /dev/null +++ b/icon-svgs/mobile/check.svg @@ -0,0 +1,6 @@ + + 选中 + + diff --git a/icon-svgs/mobile/close-round.svg b/icon-svgs/mobile/close-round.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e16e50e59d2f54aae7c0e7ca37ea566da6d66b3 --- /dev/null +++ b/icon-svgs/mobile/close-round.svg @@ -0,0 +1,11 @@ + + 关闭-圆 + + + diff --git a/icon-svgs/mobile/close.svg b/icon-svgs/mobile/close.svg new file mode 100644 index 0000000000000000000000000000000000000000..fe77ea45350a5afab320645de37437b65c26dd8e --- /dev/null +++ b/icon-svgs/mobile/close.svg @@ -0,0 +1,6 @@ + + 关闭 + + diff --git a/icon-svgs/mobile/danmu-switch.svg b/icon-svgs/mobile/danmu-switch.svg new file mode 100644 index 0000000000000000000000000000000000000000..a432034ac1687f85bef1485b979c418a0d430922 --- /dev/null +++ b/icon-svgs/mobile/danmu-switch.svg @@ -0,0 +1,7 @@ + + 弹幕开关 + + + diff --git a/icon-svgs/mobile/delete.svg b/icon-svgs/mobile/delete.svg new file mode 100644 index 0000000000000000000000000000000000000000..a67c7c251e52286107484de1b59914df2576f171 --- /dev/null +++ b/icon-svgs/mobile/delete.svg @@ -0,0 +1,6 @@ + + 删除 + + diff --git a/icon-svgs/mobile/emotion.svg b/icon-svgs/mobile/emotion.svg new file mode 100644 index 0000000000000000000000000000000000000000..c89c8c6035856bac6e9777100e264fa20feb8cf9 --- /dev/null +++ b/icon-svgs/mobile/emotion.svg @@ -0,0 +1,6 @@ + + 表情 + + diff --git a/icon-svgs/mobile/forbid.svg b/icon-svgs/mobile/forbid.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a49ee222bcf270e2c3f1d8e243d87edca78190c --- /dev/null +++ b/icon-svgs/mobile/forbid.svg @@ -0,0 +1,6 @@ + + 禁止 + + diff --git a/icon-svgs/mobile/heart.svg b/icon-svgs/mobile/heart.svg new file mode 100644 index 0000000000000000000000000000000000000000..78252f97cbbeb221b3661e2ba20a00e156d392e2 --- /dev/null +++ b/icon-svgs/mobile/heart.svg @@ -0,0 +1,6 @@ + + + + diff --git a/icon-svgs/mobile/keyboard.svg b/icon-svgs/mobile/keyboard.svg new file mode 100644 index 0000000000000000000000000000000000000000..81412e246c9cdce8d4c9d1b7655cc3462312f2fe --- /dev/null +++ b/icon-svgs/mobile/keyboard.svg @@ -0,0 +1,6 @@ + + 键盘 + + diff --git a/icon-svgs/mobile/like.svg b/icon-svgs/mobile/like.svg new file mode 100644 index 0000000000000000000000000000000000000000..b9a4d75370dfd9b8a0ee038c16931c292614feaa --- /dev/null +++ b/icon-svgs/mobile/like.svg @@ -0,0 +1,6 @@ + + 点赞 + + diff --git a/icon-svgs/mobile/media.svg b/icon-svgs/mobile/media.svg new file mode 100644 index 0000000000000000000000000000000000000000..3b90cb44d4535fb769215e4754db59ef96e64857 --- /dev/null +++ b/icon-svgs/mobile/media.svg @@ -0,0 +1,6 @@ + + icon播放中 + + diff --git a/icon-svgs/mobile/message.svg b/icon-svgs/mobile/message.svg new file mode 100644 index 0000000000000000000000000000000000000000..1d1b7961010e675d6f1c42b36919a9c733c4cc5d --- /dev/null +++ b/icon-svgs/mobile/message.svg @@ -0,0 +1,6 @@ + + 消息 + + diff --git a/icon-svgs/mobile/people.svg b/icon-svgs/mobile/people.svg new file mode 100644 index 0000000000000000000000000000000000000000..56377c644d4f25817636912a9e7ce3689f206b6b --- /dev/null +++ b/icon-svgs/mobile/people.svg @@ -0,0 +1,6 @@ + + 人物 + + diff --git a/icon-svgs/mobile/play-round.svg b/icon-svgs/mobile/play-round.svg new file mode 100644 index 0000000000000000000000000000000000000000..e8b2a6921153c5e0a3c474a9290734df61369e2f --- /dev/null +++ b/icon-svgs/mobile/play-round.svg @@ -0,0 +1,6 @@ + + icon播放 + + diff --git a/icon-svgs/mobile/product.svg b/icon-svgs/mobile/product.svg new file mode 100644 index 0000000000000000000000000000000000000000..8dacd5bfe4b265b7bb212770a1b88cb2193ff677 --- /dev/null +++ b/icon-svgs/mobile/product.svg @@ -0,0 +1,6 @@ + + 商品 + + diff --git a/icon-svgs/mobile/smiling-face.svg b/icon-svgs/mobile/smiling-face.svg new file mode 100644 index 0000000000000000000000000000000000000000..6bbd07d082fd5be9fbb874f34bacc6c3ad1901db --- /dev/null +++ b/icon-svgs/mobile/smiling-face.svg @@ -0,0 +1,6 @@ + + 笑脸 + + diff --git a/icon-svgs/mobile/translate.svg b/icon-svgs/mobile/translate.svg new file mode 100644 index 0000000000000000000000000000000000000000..f7b4ee05f8e5fdbc31007a756ae5eaafb17d4307 --- /dev/null +++ b/icon-svgs/mobile/translate.svg @@ -0,0 +1,6 @@ + + 翻译 + + diff --git a/icon-svgs/mobile/video-call.svg b/icon-svgs/mobile/video-call.svg new file mode 100644 index 0000000000000000000000000000000000000000..bf658e7f75d476af43b4aafb377b758f08e19e78 --- /dev/null +++ b/icon-svgs/mobile/video-call.svg @@ -0,0 +1,6 @@ + + 视频连线 + + diff --git a/icon-svgs/mobile/voice-call.svg b/icon-svgs/mobile/voice-call.svg new file mode 100644 index 0000000000000000000000000000000000000000..3b67ee7a8a09324392f5c7e882bef0b799b51a43 --- /dev/null +++ b/icon-svgs/mobile/voice-call.svg @@ -0,0 +1,6 @@ + + 语音连线 + + diff --git a/icon-svgs/pc/apply-video-call.svg b/icon-svgs/pc/apply-video-call.svg new file mode 100644 index 0000000000000000000000000000000000000000..ba58d74751c13b435a39f568c0f35e4f52012f22 --- /dev/null +++ b/icon-svgs/pc/apply-video-call.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/icon-svgs/pc/arrow-down.svg b/icon-svgs/pc/arrow-down.svg new file mode 100644 index 0000000000000000000000000000000000000000..c85297024687e26757c8ee69408674752d6bc1ab --- /dev/null +++ b/icon-svgs/pc/arrow-down.svg @@ -0,0 +1,6 @@ + + 箭头-下 + + diff --git a/icon-svgs/pc/arrow-left.svg b/icon-svgs/pc/arrow-left.svg new file mode 100644 index 0000000000000000000000000000000000000000..8a357219eefab8133390d8aaa5aed7ac6c1cb70b --- /dev/null +++ b/icon-svgs/pc/arrow-left.svg @@ -0,0 +1,4 @@ + + 箭头-左 + + diff --git a/icon-svgs/pc/arrow-right.svg b/icon-svgs/pc/arrow-right.svg new file mode 100644 index 0000000000000000000000000000000000000000..0e1a290873ea1de9205ee4eeb72aaa34e8fba44a --- /dev/null +++ b/icon-svgs/pc/arrow-right.svg @@ -0,0 +1,4 @@ + + 箭头-右 + + diff --git a/icon-svgs/pc/arrow-up.svg b/icon-svgs/pc/arrow-up.svg new file mode 100644 index 0000000000000000000000000000000000000000..7dada658e19970ac833ac78774e36891beca3c21 --- /dev/null +++ b/icon-svgs/pc/arrow-up.svg @@ -0,0 +1,4 @@ + + 箭头-上 + + diff --git a/icon-svgs/pc/booking.svg b/icon-svgs/pc/booking.svg new file mode 100644 index 0000000000000000000000000000000000000000..0eb465872293690f6c2ba33efd6b52267aebd697 --- /dev/null +++ b/icon-svgs/pc/booking.svg @@ -0,0 +1,7 @@ + + 预约 + + diff --git a/icon-svgs/pc/bulletin.svg b/icon-svgs/pc/bulletin.svg new file mode 100644 index 0000000000000000000000000000000000000000..3f2037b4775fe3a3ecae863ed32bfffa59e9dec3 --- /dev/null +++ b/icon-svgs/pc/bulletin.svg @@ -0,0 +1,6 @@ + + 公告 + + diff --git a/icon-svgs/pc/caret-down.svg b/icon-svgs/pc/caret-down.svg new file mode 100644 index 0000000000000000000000000000000000000000..5cb35d4438dce5e1d5ec6cad4e25fb770619f8de --- /dev/null +++ b/icon-svgs/pc/caret-down.svg @@ -0,0 +1,6 @@ + + 箭头-下-三角形 + + diff --git a/icon-svgs/pc/caret-left.svg b/icon-svgs/pc/caret-left.svg new file mode 100644 index 0000000000000000000000000000000000000000..4b369acb9fdeaf76c4d3207a1cb45ed1291760db --- /dev/null +++ b/icon-svgs/pc/caret-left.svg @@ -0,0 +1,6 @@ + + 箭头-左-三角形 + + diff --git a/icon-svgs/pc/caret-right.svg b/icon-svgs/pc/caret-right.svg new file mode 100644 index 0000000000000000000000000000000000000000..91074d2848d7ea27429a3c60130c3e4e190a3f9b --- /dev/null +++ b/icon-svgs/pc/caret-right.svg @@ -0,0 +1,6 @@ + + 箭头-右-三角形 + + diff --git a/icon-svgs/pc/caret-up.svg b/icon-svgs/pc/caret-up.svg new file mode 100644 index 0000000000000000000000000000000000000000..a370cdb67de4ebf3408bbbf1c4c01b4dbec73c2a --- /dev/null +++ b/icon-svgs/pc/caret-up.svg @@ -0,0 +1,6 @@ + + 箭头-上-三角形 + + diff --git a/icon-svgs/pc/check-round-fill.svg b/icon-svgs/pc/check-round-fill.svg new file mode 100644 index 0000000000000000000000000000000000000000..d6792165fd58eaae31f70305e5c21b7e2663049f --- /dev/null +++ b/icon-svgs/pc/check-round-fill.svg @@ -0,0 +1,6 @@ + + 选择-圆-填充 + + diff --git a/icon-svgs/pc/check.svg b/icon-svgs/pc/check.svg new file mode 100644 index 0000000000000000000000000000000000000000..7197a3892f5798895b6a9233a36cee7dd9fd3ade --- /dev/null +++ b/icon-svgs/pc/check.svg @@ -0,0 +1,6 @@ + + 选中 + + diff --git a/icon-svgs/pc/close-round.svg b/icon-svgs/pc/close-round.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e16e50e59d2f54aae7c0e7ca37ea566da6d66b3 --- /dev/null +++ b/icon-svgs/pc/close-round.svg @@ -0,0 +1,11 @@ + + 关闭-圆 + + + diff --git a/icon-svgs/pc/close.svg b/icon-svgs/pc/close.svg new file mode 100644 index 0000000000000000000000000000000000000000..f8830b211dc3a7dfe1c6a1f7caed26a5d9d687cf --- /dev/null +++ b/icon-svgs/pc/close.svg @@ -0,0 +1,6 @@ + + 关闭 + + diff --git a/icon-svgs/pc/device-setting.svg b/icon-svgs/pc/device-setting.svg new file mode 100644 index 0000000000000000000000000000000000000000..237ccb5098a31ff6d038dbb1cefbbce8a5a2c91f --- /dev/null +++ b/icon-svgs/pc/device-setting.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/icon-svgs/pc/emotion.svg b/icon-svgs/pc/emotion.svg new file mode 100644 index 0000000000000000000000000000000000000000..cb76447a2fd86c5b2de11fe0965dedd37fbdeded --- /dev/null +++ b/icon-svgs/pc/emotion.svg @@ -0,0 +1,6 @@ + + 表情 + + diff --git a/icon-svgs/pc/feedback.svg b/icon-svgs/pc/feedback.svg new file mode 100644 index 0000000000000000000000000000000000000000..7bc9f1d6a5e1e6378a25b01630c02e186c12a4ed --- /dev/null +++ b/icon-svgs/pc/feedback.svg @@ -0,0 +1,6 @@ + + 投诉反馈 + + diff --git a/icon-svgs/pc/forbid.svg b/icon-svgs/pc/forbid.svg new file mode 100644 index 0000000000000000000000000000000000000000..3a49ee222bcf270e2c3f1d8e243d87edca78190c --- /dev/null +++ b/icon-svgs/pc/forbid.svg @@ -0,0 +1,6 @@ + + 禁止 + + diff --git a/icon-svgs/pc/hang-up.svg b/icon-svgs/pc/hang-up.svg new file mode 100644 index 0000000000000000000000000000000000000000..c2478c00e4e08dc0d92cf7bfd6f399bdf24d5a6c --- /dev/null +++ b/icon-svgs/pc/hang-up.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/icon-svgs/pc/lang.svg b/icon-svgs/pc/lang.svg new file mode 100644 index 0000000000000000000000000000000000000000..cc59683618ac95702200a1c53ffd20930e0255b8 --- /dev/null +++ b/icon-svgs/pc/lang.svg @@ -0,0 +1,6 @@ + + 语言 + + diff --git a/icon-svgs/pc/media.svg b/icon-svgs/pc/media.svg new file mode 100644 index 0000000000000000000000000000000000000000..2ef85a15a024efd4bc818cfff717a70f9a88f30f --- /dev/null +++ b/icon-svgs/pc/media.svg @@ -0,0 +1,6 @@ + + 多媒体 + + diff --git a/icon-svgs/pc/people.svg b/icon-svgs/pc/people.svg new file mode 100644 index 0000000000000000000000000000000000000000..529da8272ab6d17d080b92311ea7450a723fc54b --- /dev/null +++ b/icon-svgs/pc/people.svg @@ -0,0 +1,6 @@ + + 人物 + + diff --git a/icon-svgs/pc/pic.svg b/icon-svgs/pc/pic.svg new file mode 100644 index 0000000000000000000000000000000000000000..3fc7e9e3d5baa5b71353a253f01b3b05ae915920 --- /dev/null +++ b/icon-svgs/pc/pic.svg @@ -0,0 +1,6 @@ + + 图片 + + diff --git a/icon-svgs/pc/picture.svg b/icon-svgs/pc/picture.svg new file mode 100644 index 0000000000000000000000000000000000000000..3fc7e9e3d5baa5b71353a253f01b3b05ae915920 --- /dev/null +++ b/icon-svgs/pc/picture.svg @@ -0,0 +1,6 @@ + + 图片 + + diff --git a/icon-svgs/pc/play-round.svg b/icon-svgs/pc/play-round.svg new file mode 100644 index 0000000000000000000000000000000000000000..81cebea603a37dace84039e311aa33f1d333e7c8 --- /dev/null +++ b/icon-svgs/pc/play-round.svg @@ -0,0 +1,6 @@ + + 播放-圆 + + diff --git a/icon-svgs/pc/set-nick.svg b/icon-svgs/pc/set-nick.svg new file mode 100644 index 0000000000000000000000000000000000000000..b33edfc0bc83c18876a3a0d0dc0cd003de236101 --- /dev/null +++ b/icon-svgs/pc/set-nick.svg @@ -0,0 +1,6 @@ + + 设置昵称 + + diff --git a/icon-svgs/pc/share.svg b/icon-svgs/pc/share.svg new file mode 100644 index 0000000000000000000000000000000000000000..29fbcae902a71c06bf969bf5bc8c469052f9287b --- /dev/null +++ b/icon-svgs/pc/share.svg @@ -0,0 +1,6 @@ + + 分享 + + diff --git a/icon-svgs/pc/tips.svg b/icon-svgs/pc/tips.svg new file mode 100644 index 0000000000000000000000000000000000000000..bab8c4a94bbb07ed092fcabaa723456f4b7c800f --- /dev/null +++ b/icon-svgs/pc/tips.svg @@ -0,0 +1,6 @@ + + 提示 + + diff --git a/icon-svgs/pc/translate.svg b/icon-svgs/pc/translate.svg new file mode 100644 index 0000000000000000000000000000000000000000..f7b4ee05f8e5fdbc31007a756ae5eaafb17d4307 --- /dev/null +++ b/icon-svgs/pc/translate.svg @@ -0,0 +1,6 @@ + + 翻译 + + diff --git a/icon-svgs/pc/video-call.svg b/icon-svgs/pc/video-call.svg new file mode 100644 index 0000000000000000000000000000000000000000..bf658e7f75d476af43b4aafb377b758f08e19e78 --- /dev/null +++ b/icon-svgs/pc/video-call.svg @@ -0,0 +1,6 @@ + + 视频连线 + + diff --git a/icon-svgs/pc/voice-call.svg b/icon-svgs/pc/voice-call.svg new file mode 100644 index 0000000000000000000000000000000000000000..3b67ee7a8a09324392f5c7e882bef0b799b51a43 --- /dev/null +++ b/icon-svgs/pc/voice-call.svg @@ -0,0 +1,6 @@ + + 语音连线 + + diff --git a/icon-svgs/pc/warning-round-fill.svg b/icon-svgs/pc/warning-round-fill.svg new file mode 100644 index 0000000000000000000000000000000000000000..187c58e411b8c911603eab1cad22cc6edceb4e67 --- /dev/null +++ b/icon-svgs/pc/warning-round-fill.svg @@ -0,0 +1,6 @@ + + 警告-圆-填充 + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000000000000000000000000000000000000..8c2a28bd8bad3107692ceee53da81a4274d7730e --- /dev/null +++ b/package-lock.json @@ -0,0 +1,20879 @@ +{ + "name": "polyv-web-live-watch-sdk", + "version": "0.1.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@achrinza/node-ipc": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/@achrinza/node-ipc/-/node-ipc-9.2.2.tgz", + "integrity": "sha512-b90U39dx0cU6emsOvy5hxU4ApNXnE3+Tuo8XQZfiKTGelDwpMwBVgBP7QX6dGTcJgu/miyJuNJ/2naFBliNWEw==", + "dev": true, + "requires": { + "@node-ipc/js-queue": "2.0.3", + "event-pubsub": "4.3.0", + "js-message": "1.0.7" + } + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/@ampproject%2fremapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.10.1", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/download/@babel/code-frame-7.10.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fcode-frame%2Fdownload%2F%40babel%2Fcode-frame-7.10.1.tgz", + "integrity": "sha1-1UgcUJXaocV+FuVMb5GYRDr7Sf8=", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.1" + } + }, + "@babel/compat-data": { + "version": "7.17.10", + "resolved": "https://registry.npmmirror.com/@babel%2fcompat-data/-/compat-data-7.17.10.tgz", + "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==", + "dev": true + }, + "@babel/core": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fcore/-/core-7.20.5.tgz", + "integrity": "sha512-UdOWmk4pNWTm/4DlPUl/Pt4Gz4rcEMb7CY0Y3eJl5Yz1vI8ZJGmHWaVE55LoxRjdpx0z259GE9U5STA9atUinQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-module-transforms": "^7.20.2", + "@babel/helpers": "^7.20.5", + "@babel/parser": "^7.20.5", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fcode-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fcompat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/generator": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fgenerator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.5", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhighlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fparser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "dev": true + }, + "@babel/traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftraverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/eslint-parser": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2feslint-parser/-/eslint-parser-7.19.1.tgz", + "integrity": "sha512-AqNf2QWt1rtu2/1rLswy6CDP7H9Oh3mMhk177Y67Rg8d7RD9WfOLLv8CGn6tisFvS2htm86yIe1yLF6I1UDaGQ==", + "dev": true, + "requires": { + "@nicolo-ribaudo/eslint-scope-5-internals": "5.1.1-v1", + "eslint-visitor-keys": "^2.1.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fgenerator/-/generator-7.20.0.tgz", + "integrity": "sha512-GUPcXxWibClgmYJuIwC2Bc2Lg+8b9VjaJ+HlNdACEVt+Wlr1eoU1OPZjZRm7Hzl0gaTsUZNQfeihvZJhG7oc3w==", + "dev": true, + "requires": { + "@babel/types": "^7.20.0", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz", + "integrity": "sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.10", + "@babel/helper-validator-option": "^7.16.7", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-create-class-features-plugin/-/helper-create-class-features-plugin-7.20.5.tgz", + "integrity": "sha512-3RCdA/EmEaikrhayahwToF0fpweU/8o2p8vhc1c/1kftHOdTKuC65kik/TLc+qfbS8JKw4qqJbne4ovICDhmww==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.20.5.tgz", + "integrity": "sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.2.1" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-module-imports": { + "version": "7.10.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/download/@babel/helper-module-imports-7.10.1.tgz", + "integrity": "sha1-3TMb1FvMxWbOdwBOnQX+F63ROHY=", + "dev": true, + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-module-transforms": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-module-transforms/-/helper-module-transforms-7.20.2.tgz", + "integrity": "sha512-zvBKyJXRbmK07XhMuujYoJ48B5yvvmM6+wcpv6Ivj4Yg6qO7NOZOSnvZN9CRl1zz1Z4cKf8YejmCMh8clOoOeA==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.20.2", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.19.1", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.1", + "@babel/types": "^7.20.2" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fcode-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/generator": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fgenerator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.5", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhighlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fparser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "dev": true + }, + "@babel/traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftraverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/download/@babel/helper-plugin-utils-7.10.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2F%40babel%2Fhelper-plugin-utils%2Fdownload%2F%40babel%2Fhelper-plugin-utils-7.10.1.tgz", + "integrity": "sha1-7Fpc8O7JJbZsYFgDKLEiwBIwoSc=", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-replace-supers/-/helper-replace-supers-7.19.1.tgz", + "integrity": "sha512-T7ahH7wV0Hfs46SFh5Jz3s0B6+o8g3c+7TMxu7xKfmHikg7EAZ3I2Qk9LFhjxXq8sL7UkP5JflezNwoZa8WvWw==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.19.1", + "@babel/types": "^7.19.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-simple-access": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-simple-access/-/helper-simple-access-7.20.2.tgz", + "integrity": "sha512-+0woI/WPq59IrqDYbVGfshjT5Dmk/nnbdpcF8SnMhhXObpTq2KNBdLFRFrkVdbDOyUmHBCxzm5FHV1rACIkIbA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.2" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz", + "integrity": "sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==", + "dev": true, + "requires": { + "@babel/types": "^7.20.0" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + }, + "dependencies": { + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-string-parser": { + "version": "7.19.4", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-string-parser/-/helper-string-parser-7.19.4.tgz", + "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/download/@babel/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha1-V3CwwagmxPU/Xt5eFTFj4DGOlLU=", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.16.7", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.16.7.tgz", + "integrity": "sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-wrap-function/-/helper-wrap-function-7.20.5.tgz", + "integrity": "sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fcode-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/generator": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fgenerator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.5", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhighlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fparser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "dev": true + }, + "@babel/traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftraverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/helpers": { + "version": "7.20.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelpers/-/helpers-7.20.6.tgz", + "integrity": "sha512-Pf/OjgfgFRW5bApskEz5pvidpim7tEDPlFtKcNRXWmfHGn9IEI2W2flqRQXTFb7gIPTyK++N6rVHuwKut4XK6w==", + "dev": true, + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.20.5", + "@babel/types": "^7.20.5" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fcode-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/generator": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fgenerator/-/generator-7.20.5.tgz", + "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "dev": true, + "requires": { + "@babel/types": "^7.20.5", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhighlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fparser/-/parser-7.20.5.tgz", + "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "dev": true + }, + "@babel/traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftraverse/-/traverse-7.20.5.tgz", + "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.5", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.5", + "@babel/types": "^7.20.5", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fgen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/highlight": { + "version": "7.10.1", + "resolved": "https://registry.npmmirror.com/@babel/highlight/download/@babel/highlight-7.10.1.tgz", + "integrity": "sha1-hB0Ji6YTuhpCeis4PXnjVVLDiuA=", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.18.11", + "resolved": "https://registry.npmmirror.com/@babel%2fparser/-/parser-7.18.11.tgz", + "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.1", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.1.tgz", + "integrity": "sha512-Gh5rchzSwE4kC+o/6T8waD0WHEQIsDmjltY8WnWRXHUdH8axZhuH86Ov9M72YhJfDrZseQwuuWaaIT/TmePp3g==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-decorators": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.20.2.tgz", + "integrity": "sha512-nkBH96IBmgKnbHQ5gXFrcmez+Z9S2EIDKDQGp005ROqBigc88Tky4rzCnlP/lnlj245dCEQl4/YyV0V1kYh5dw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.20.2", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/plugin-syntax-decorators": "^7.19.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + }, + "@babel/types": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.20.2.tgz", + "integrity": "sha512-FnnvsNWgZCr232sqtXggapvlkk/tuwR/qhGzcmxI0GXLCjmPYQPzio2FbdlWuY6y1sHFfQKk+rRbUZ9VStQMog==", + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.2.tgz", + "integrity": "sha512-Ks6uej9WFK+fvIMesSqbAto5dD8Dz4VuuFvGJFKgIGSkJuRGcrwGECPA1fDgQK3/DbExBJpEkTeYeB8geIFCSQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.1" + }, + "dependencies": { + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fcompat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.20.5.tgz", + "integrity": "sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-decorators": { + "version": "7.19.0", + "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.19.0.tgz", + "integrity": "sha512-xaBZUEDntt4faL1yN8oIFlhfXeQAWJW7CLKYsHTUqriCUbj8xOra8bfxxKGi/UwExPFBuPdH4XfHc9rGQhrVkQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz", + "integrity": "sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz", + "integrity": "sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true + } + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "dependencies": { + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-block-scoping/-/plugin-transform-block-scoping-7.20.5.tgz", + "integrity": "sha512-WvpEIW9Cbj9ApF3yJCjIEEf1EiNJLtXagOrL5LNWEZOo3jv8pmPoYTSNJQvqej8OavVlgOoOPw6/htGZro6IkA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-classes": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-classes/-/plugin-transform-classes-7.20.2.tgz", + "integrity": "sha512-9rbPp0lCVVoagvtEyQKSo5L8oo0nQS/iif+lwlAz29MccX2642vWDlSZK+2T2buxbopotId2ld7zZAzRfz9j1g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-replace-supers": "^7.19.1", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fcompat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-destructuring/-/plugin-transform-destructuring-7.20.2.tgz", + "integrity": "sha512-mENM+ZHrvEgxLTBXUiQ621rRXZes3KWUv6NdQlrnr1TkWVw+hUjQBZuP2X32qKlrlG2BzgR95gkuCRSkJl8vIw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fcompat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.19.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-modules-amd/-/plugin-transform-modules-amd-7.19.6.tgz", + "integrity": "sha512-uG3od2mXvAtIFQIh0xrpLH6r5fpSQN04gIVovl+ODLdUMANokxQLZnPBHcjmv3GxRjnqwLuHvppjjcelqUFZvg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.19.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.19.6.tgz", + "integrity": "sha512-8PIa1ym4XRTKuSsOUXqDG0YaOlEuTVvHMe5JCfgBMOtHvJKw/4NGovEGN33viISshG/rZNVrACiBmPQLvWN8xQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-simple-access": "^7.19.4" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.19.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.6.tgz", + "integrity": "sha512-fqGLBepcc3kErfR9R3DnVpURmckXP7gj7bAlrTQyBxrigFqszZCkFkcoxzCp2v32XmwXLvbw+8Yq9/b+QqksjQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.19.1" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + } + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz", + "integrity": "sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.20.5", + "@babel/helper-plugin-utils": "^7.20.2" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-parameters/-/plugin-transform-parameters-7.20.5.tgz", + "integrity": "sha512-h7plkOmcndIUWXZFLgpbrh2+fXAi47zcUX7IrOQuZdLD0I0KvjJ6cvo3BEcAOsDOcZhVKGJqv07mkSqK0y2isQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-regenerator/-/plugin-transform-regenerator-7.20.5.tgz", + "integrity": "sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "regenerator-transform": "^0.15.1" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.19.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", + "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-typescript": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-typescript/-/plugin-transform-typescript-7.20.0.tgz", + "integrity": "sha512-xOAsAFaun3t9hCwZ13Qe7gq423UgMZ6zAgmLxeGGapFqlT/X3L5qT2btjiVLlFn7gWtMaVyceS5VxGAuKbgizw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/plugin-syntax-typescript": "^7.20.0" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + }, + "@babel/types": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fplugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + } + } + }, + "@babel/preset-env": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fpreset-env/-/preset-env-7.20.2.tgz", + "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.1", + "@babel/helper-compilation-targets": "^7.20.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.20.1", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.20.2", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.20.0", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.20.2", + "@babel/plugin-transform-classes": "^7.20.2", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.20.2", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.19.6", + "@babel/plugin-transform-modules-commonjs": "^7.19.6", + "@babel/plugin-transform-modules-systemjs": "^7.19.6", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.20.1", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.19.0", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.20.2", + "babel-plugin-polyfill-corejs2": "^0.3.3", + "babel-plugin-polyfill-corejs3": "^0.6.0", + "babel-plugin-polyfill-regenerator": "^0.4.1", + "core-js-compat": "^3.25.1", + "semver": "^6.3.0" + }, + "dependencies": { + "@babel/compat-data": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2fcompat-data/-/compat-data-7.20.5.tgz", + "integrity": "sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==", + "dev": true + }, + "@babel/helper-compilation-targets": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-compilation-targets/-/helper-compilation-targets-7.20.0.tgz", + "integrity": "sha512-0jp//vDGp9e8hZzBc6N/KwA5ZK3Wsm/pfm4CrY7vzegkVxc65SgSn6wYOnwHe9Js9HRQ1YTCKLGPzDtaS3RoLQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.21.3", + "semver": "^6.3.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.20.2", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz", + "integrity": "sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/types": { + "version": "7.20.5", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.5.tgz", + "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "core-js-compat": { + "version": "3.26.1", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", + "dev": true, + "requires": { + "browserslist": "^4.21.4" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/@babel%2fpreset-modules/-/preset-modules-0.1.5.tgz", + "integrity": "sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fpreset-typescript/-/preset-typescript-7.18.6.tgz", + "integrity": "sha512-s9ik86kXBAnD760aybBucdpnLsAt0jK1xqJn2juOn9lkOvSHV60os5hxoVJsPzMQxvnUJFAlkont2DvvaYEBtQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-transform-typescript": "^7.18.6" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + } + } + }, + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmmirror.com/@babel/runtime/download/@babel/runtime-7.10.2.tgz", + "integrity": "sha1-0QPyHyYCSX04NIoy4AhjfVBtuDk=", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmmirror.com/@babel%2ftemplate/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fcode-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhighlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/types": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/traverse": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftraverse/-/traverse-7.20.0.tgz", + "integrity": "sha512-5+cAXQNARgjRUK0JWu2UBwja4JLSO/rBMPJzpsKb+oBF5xlUuCfljQepS4XypBQoiigL0VQjTZy6WiONtUdScQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.20.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.20.0", + "@babel/types": "^7.20.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fcode-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.19.1", + "resolved": "https://registry.npmmirror.com/@babel%2fhelper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", + "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmmirror.com/@babel%2fhighlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2fparser/-/parser-7.20.0.tgz", + "integrity": "sha512-G9VgAhEaICnz8iiJeGJQyVl6J2nTjbW0xeisva0PK6XcKsga7BIaqm4ZF8Rg1Wbaqmy6znspNqhPaPkyukujzg==", + "dev": true + }, + "@babel/types": { + "version": "7.20.0", + "resolved": "https://registry.npmmirror.com/@babel%2ftypes/-/types-7.20.0.tgz", + "integrity": "sha512-Jlgt3H0TajCW164wkTOTzHkZb075tMQMULzrLUoUeKmO7eFL96GgDxf7/Axhc5CAuKE3KFyVW1p6ysKsi2oXAg==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.19.4", + "@babel/helper-validator-identifier": "^7.19.1", + "to-fast-properties": "^2.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@babel/types": { + "version": "7.10.2", + "resolved": "https://registry.npmmirror.com/@babel/types/download/@babel/types-7.10.2.tgz", + "integrity": "sha1-MCg74xytDb9vsAvUBkHKDqZ1Fy0=", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/@discoveryjs%2fjson-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@eslint%2feslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "13.15.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "@hapi/address": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/@hapi/address/-/address-2.1.4.tgz", + "integrity": "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==", + "dev": true + }, + "@hapi/bourne": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/@hapi/bourne/-/bourne-1.3.2.tgz", + "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==", + "dev": true + }, + "@hapi/hoek": { + "version": "8.5.1", + "resolved": "https://registry.npmmirror.com/@hapi/hoek/-/hoek-8.5.1.tgz", + "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==", + "dev": true + }, + "@hapi/joi": { + "version": "15.1.1", + "resolved": "https://registry.npmmirror.com/@hapi/joi/-/joi-15.1.1.tgz", + "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", + "dev": true, + "requires": { + "@hapi/address": "2.x.x", + "@hapi/bourne": "1.x.x", + "@hapi/hoek": "8.x.x", + "@hapi/topo": "3.x.x" + } + }, + "@hapi/topo": { + "version": "3.1.6", + "resolved": "https://registry.npmmirror.com/@hapi/topo/-/topo-3.1.6.tgz", + "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", + "dev": true, + "requires": { + "@hapi/hoek": "^8.3.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmmirror.com/@humanwhocodes%2fconfig-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes%2fobject-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@intervolga/optimize-cssnano-plugin": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/@intervolga/optimize-cssnano-plugin/-/optimize-cssnano-plugin-1.0.6.tgz", + "integrity": "sha512-zN69TnSr0viRSU6cEDIcuPcP67QcpQ6uHACg58FiN9PDrU6SLyGW3MR4tiISbYxy1kDWAVPwD+XwQTWE5cigAA==", + "dev": true, + "requires": { + "cssnano": "^4.0.0", + "cssnano-preset-default": "^4.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fgen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fresolve-uri/-/resolve-uri-3.0.7.tgz", + "integrity": "sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fset-array/-/set-array-1.1.1.tgz", + "integrity": "sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.13", + "resolved": "https://registry.npmmirror.com/@jridgewell%2fsourcemap-codec/-/sourcemap-codec-1.4.13.tgz", + "integrity": "sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@jridgewell%2ftrace-mapping/-/trace-mapping-0.3.13.tgz", + "integrity": "sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@just4/dom": { + "version": "0.3.0-beta.2", + "resolved": "https://registry.npmmirror.com/@just4%2fdom/-/dom-0.3.0-beta.2.tgz", + "integrity": "sha512-iBnuw/sDoNcXNm5umc+RncoQ/Oe+6bKhcpcLz8A5Msjgjb8dcUd3C5Rq0RYcdKafrjPh63ZWHMMdHhL3tkQSTw==", + "requires": { + "@just4/util": "^0.2.0-beta.1" + }, + "dependencies": { + "@just4/util": { + "version": "0.2.0-beta.1", + "resolved": "https://registry.npmmirror.com/@just4%2futil/-/util-0.2.0-beta.1.tgz", + "integrity": "sha512-tXFtVV8GwrmXYgUDYHjQHYJcrlxomtTSJMtfeCJMP2AaH/Jb4OWGwwxAn+rQhujnUfJhOHK+xun48DreJLfuVA==" + } + } + }, + "@just4/load-script": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@just4%2fload-script/-/load-script-1.0.0.tgz", + "integrity": "sha512-fJj67ycUEj8AGh7fEnCbnev+S+/kLRoOkbZ22khyj00SopXMMI9S34aidZ//VOy0dZ67BweMFiZhHR8rltMwdA==", + "requires": { + "@just4/querystring": "^1.0.0", + "@just4/util": "^1.0.0" + } + }, + "@just4/querystring": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@just4%2fquerystring/-/querystring-1.0.0.tgz", + "integrity": "sha512-lbxu1iG2ceFzkM/4dGxVfsJdyWblGzGvNk2cy2+NDzphFE0Kv3BAEsx/tD58Yzi33+4GyPE9a9pruL3GyifAKw==", + "requires": { + "@just4/util": "^1.0.0" + } + }, + "@just4/storage": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@just4%2fstorage/-/storage-1.0.0.tgz", + "integrity": "sha512-9OGJd7UaqAcyhAhZXzqjeyVhgrl6nxKiEiO/li4C2hujuH9zoL2B5jm6de5htayQI+tvDV6TYD2TESc8StUqtQ==" + }, + "@just4/ua-info": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@just4%2fua-info/-/ua-info-1.0.0.tgz", + "integrity": "sha512-ok3RAIsZkt/qPsapFSvAQabD7mLjhUdEUmRQ0eyPoDegqj9YE/oI11buIQZqo1DmoCkee85U0XlxtkibSEF+1g==" + }, + "@just4/util": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@just4%2futil/-/util-1.0.0.tgz", + "integrity": "sha512-9nyLGBRnP7NfoPQDeCvq+DJpsCQtVQ3rg5U1cf4QSWauNRupftH+yHcb4IvPrmMlvSYXNzwv3D9V00VBkQtrng==" + }, + "@just4/virtual-list": { + "version": "0.8.0-beta.2", + "resolved": "https://registry.npmmirror.com/@just4%2fvirtual-list/-/virtual-list-0.8.0-beta.2.tgz", + "integrity": "sha512-31VqqcWSxN7dYQuVoyX878Kg9NOpUIlQtYQwj7cYbURilygk2n4peuN050bf8CXTZgv5cV+n5pbC5a6MxOjgiw==", + "requires": { + "@just4/dom": "^0.3.0-beta.2", + "@just4/util": "^0.2.0-beta.1", + "eventemitter3": "^4.0.7" + }, + "dependencies": { + "@just4/util": { + "version": "0.2.0-beta.1", + "resolved": "https://registry.npmmirror.com/@just4%2futil/-/util-0.2.0-beta.1.tgz", + "integrity": "sha512-tXFtVV8GwrmXYgUDYHjQHYJcrlxomtTSJMtfeCJMP2AaH/Jb4OWGwwxAn+rQhujnUfJhOHK+xun48DreJLfuVA==" + } + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nicolo-ribaudo/eslint-scope-5-internals": { + "version": "5.1.1-v1", + "resolved": "https://registry.npmmirror.com/@nicolo-ribaudo%2feslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", + "integrity": "sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==", + "dev": true, + "requires": { + "eslint-scope": "5.1.1" + } + }, + "@node-ipc/js-queue": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/@node-ipc/js-queue/-/js-queue-2.0.3.tgz", + "integrity": "sha512-fL1wpr8hhD5gT2dA1qifeVaoDFlQR5es8tFuKqjHX+kdOtdNHnxkVZbtIrR2rxnMFvehkjaZRNV2H/gPXlb0hw==", + "dev": true, + "requires": { + "easy-stack": "1.0.1" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmmirror.com/@nodelib%2ffs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/@nodelib%2ffs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/@nodelib%2ffs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmmirror.com/@polka%2furl/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true + }, + "@polyv/emotion-sdk": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/@polyv%2femotion-sdk/-/emotion-sdk-2.3.0.tgz", + "integrity": "sha512-bPZMW8BgFCJ54bZiYudDnTdNYWJQXYDydwFMkAWUVJFveWvWnL30HxJGSXpJabzMrPq7Vh2m58b1yddmmk4pZw==" + }, + "@polyv/eslint-config": { + "version": "0.4.0-beta.1", + "resolved": "https://registry.npmmirror.com/@polyv%2feslint-config/-/eslint-config-0.4.0-beta.1.tgz", + "integrity": "sha512-fMOTyaBI5N2ZHPOICEI0LTxgEKukreEV48yTtRIT7UolzmsBNIW0Mj6FxwT8kxPTa5qIJt1e6KdgoMBLHx2wxA==", + "dev": true, + "requires": { + "@babel/eslint-parser": "^7.19.1", + "@typescript-eslint/eslint-plugin": "^5.46.0", + "@typescript-eslint/parser": "^5.46.0", + "@vue/eslint-config-standard": "^6.1.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^6.1.1", + "eslint-plugin-sonarjs": "^0.17.0", + "eslint-plugin-vue": "^9.8.0", + "vue-eslint-parser": "^9.1.0" + } + }, + "@polyv/icons-cli": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@polyv%2ficons-cli/-/icons-cli-1.2.0.tgz", + "integrity": "sha512-XTrrT1uW3m0MZWMhYJ+gATDQyzGrqBjCncXPjCxRoYu62L7m0MlNUWrcY3YJghqKusOMET9JE4qtn2Pg4JqTVQ==", + "dev": true, + "requires": { + "camelcase": "^6.3.0", + "chalk": "^4.0.0", + "commander": "^9.3.0", + "fast-glob": "^3.2.11", + "fs-extra": "^10.0.1", + "prettier": "^2.5.1", + "rimraf": "^3.0.2", + "svgo": "^2.8.0", + "tinycolor2": "^1.4.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, + "@polyv/icons-vue": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@polyv%2ficons-vue/-/icons-vue-1.2.0.tgz", + "integrity": "sha512-XmQuHXJtp9FBlcmIQD3odyRgVPITZRsnfwQpxyPdn8ShGnWzpxxLsvHx22Zy0Rg0fr2wCMmJ0Kg61qQ1EjQ/DA==" + }, + "@polyv/live-watch-sdk": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/@polyv%2flive-watch-sdk/-/live-watch-sdk-0.1.0.tgz", + "integrity": "sha512-IIdsCKirpzUxXqgW/bpF+EG8iitgZeQST3BUFSdQf7KrolKYlO6hH+5LH0bcm4SRufcIUfz0/nTYXUdGvLv7TQ==" + }, + "@polyv/polyv-ui": { + "version": "2.24.0", + "resolved": "https://registry.npmmirror.com/@polyv%2fpolyv-ui/-/polyv-ui-2.24.0.tgz", + "integrity": "sha512-kXsGtoAuQ255v/KGmJbcbQ3Gnhwdr2YjncPfzBBrCH1d0VevylAWVYq1wwK3VFF9eAlDEwbWBG6L0Bk+9mmZ+Q==" + }, + "@polyv/utils": { + "version": "2.0.0-beta.5", + "resolved": "https://registry.npmmirror.com/@polyv%2futils/-/utils-2.0.0-beta.5.tgz", + "integrity": "sha512-5rnJb4v4G7MgkHdNyrsV7gqGsz4w57SGbxQE9tzlkAkFCyKbMs/VKiu0+Jd95RkRVoQkukqE2wYPuLwXpIiROA==" + }, + "@polyv/web-view-bridge": { + "version": "0.1.0-webview-app.8", + "resolved": "https://registry.npmmirror.com/@polyv%2fweb-view-bridge/-/web-view-bridge-0.1.0-webview-app.8.tgz", + "integrity": "sha512-/RE/c4jSgViWaDMZLciGs0Vj/KspXnELhzo4wA/ITIILi0sqvp8a9ib5iVGuGu8qzjQ85nWm3oVlIrxtdTsO5A==", + "requires": { + "events": "^3.3.0", + "url-search-params-polyfill": "^8.1.0" + } + }, + "@popperjs/core": { + "version": "2.11.4", + "resolved": "https://registry.npmmirror.com/@popperjs%2fcore/-/core-2.11.4.tgz", + "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==" + }, + "@soda/friendly-errors-webpack-plugin": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.8.1.tgz", + "integrity": "sha512-h2ooWqP8XuFqTXT+NyAFbrArzfQA7R6HTezADrvD9Re8fxMLTPPniLdqVTdDaO0eIoLaAwKT+d6w+5GeTk7Vbg==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "error-stack-parser": "^2.0.6", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@soda/get-current-script": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@soda/get-current-script/-/get-current-script-1.0.2.tgz", + "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", + "dev": true + }, + "@stylelint/postcss-css-in-js": { + "version": "0.37.3", + "resolved": "https://registry.npmmirror.com/@stylelint%2fpostcss-css-in-js/-/postcss-css-in-js-0.37.3.tgz", + "integrity": "sha512-scLk3cSH1H9KggSniseb2KNAU5D9FWc3H7BxCSAIdtU9OWIyw0zkEZ9qEKHryRM+SExYXRKNb7tOOVNAsQ3iwg==", + "dev": true, + "requires": { + "@babel/core": "^7.17.9" + } + }, + "@stylelint/postcss-markdown": { + "version": "0.36.2", + "resolved": "https://registry.npmmirror.com/@stylelint%2fpostcss-markdown/-/postcss-markdown-0.36.2.tgz", + "integrity": "sha512-2kGbqUVJUGE8dM+bMzXG/PYUWKkjLIkRLWNh39OaADkiabDRdw8ATFCgbMz5xdIcvwspPAluSL7uY+ZiTWdWmQ==", + "dev": true, + "requires": { + "remark": "^13.0.0", + "unist-util-find-all-after": "^3.0.2" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@tootallnate%2fonce/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@trysound/sax": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/@trysound%2fsax/-/sax-0.2.0.tgz", + "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "dev": true + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmmirror.com/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmmirror.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "7.29.0", + "resolved": "https://registry.npmmirror.com/@types%2feslint/-/eslint-7.29.0.tgz", + "integrity": "sha512-VNcvioYDH8/FxaeTKkM4/TiTwt6pBV9E3OfGmvaw8tPl0rrHCJ4Ll15HRT+pMiFAf/MLQvAzC+6RzUMEL9Ceng==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/@types%2festree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "@types/express": { + "version": "4.17.14", + "resolved": "https://registry.npmmirror.com/@types/express/-/express-4.17.14.tgz", + "integrity": "sha512-TEbt+vaPFQ+xpxFLFssxUDXj5cWCxZJjIcB7Yg0k0GMHGtgtQgpvx/MUQUeAkNbA9AAGrwkAsoeItdTgS7FMyg==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.31", + "resolved": "https://registry.npmmirror.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.31.tgz", + "integrity": "sha512-DxMhY+NAsTwMMFHBTtJFNp5qiHKJ7TeqOo23zVEM9alT1Ml27Q3xcTH0xwxn7Q0BbMcVEJOs/7aQtUWupUQN3Q==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/@types%2fglob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmmirror.com/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmmirror.com/@types%2fjson-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmmirror.com/@types%2fjson5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/lodash": { + "version": "4.14.182", + "resolved": "https://registry.npmmirror.com/@types%2flodash/-/lodash-4.14.182.tgz", + "integrity": "sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==", + "dev": true + }, + "@types/lodash-es": { + "version": "4.17.6", + "resolved": "https://registry.npmmirror.com/@types%2flodash-es/-/lodash-es-4.17.6.tgz", + "integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==", + "dev": true, + "requires": { + "@types/lodash": "*" + } + }, + "@types/mdast": { + "version": "3.0.10", + "resolved": "https://registry.npmmirror.com/@types%2fmdast/-/mdast-3.0.10.tgz", + "integrity": "sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA==", + "dev": true, + "requires": { + "@types/unist": "*" + } + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@types%2fminimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, + "@types/minimist": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/@types%2fminimist/-/minimist-1.2.2.tgz", + "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", + "dev": true + }, + "@types/node": { + "version": "17.0.40", + "resolved": "https://registry.npmmirror.com/@types%2fnode/-/node-17.0.40.tgz", + "integrity": "sha512-UXdBxNGqTMtm7hCwh9HtncFVLrXoqA3oJW30j6XWp5BH/wu3mVeaxo7cq5benFdBw34HB3XDT2TRPI7rXZ+mDg==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/@types/normalize-package-data/download/@types/normalize-package-data-2.4.0.tgz", + "integrity": "sha1-5IbQ2XOW15vu3QpuM/RTT/a0lz4=", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/@types%2fparse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/q": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@types/q/-/q-1.5.5.tgz", + "integrity": "sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==" + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmmirror.com/@types%2fsemver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/@types%2fsource-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/tapable": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types%2ftapable/-/tapable-1.0.8.tgz", + "integrity": "sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==", + "dev": true + }, + "@types/uglify-js": { + "version": "3.13.3", + "resolved": "https://registry.npmmirror.com/@types%2fuglify-js/-/uglify-js-3.13.3.tgz", + "integrity": "sha512-9dmBYXt/rKxedUXfCvXSxyiPvpDXLkiRlv17DnqdhS+pRustL1967rI1jZVt1xysTO+xJGMoZzcy3cWC9+b6Tw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/unist": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/@types%2funist/-/unist-2.0.6.tgz", + "integrity": "sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==", + "dev": true + }, + "@types/webpack": { + "version": "4.41.32", + "resolved": "https://registry.npmmirror.com/@types%2fwebpack/-/webpack-4.41.32.tgz", + "integrity": "sha512-cb+0ioil/7oz5//7tZUSwbrSAN/NWHrQylz5cW8G0dWTcF/g+/dSdMlKVZspBYuMAN1+WnwHrkxiRrLcwd0Heg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tapable": "^1", + "@types/uglify-js": "*", + "@types/webpack-sources": "*", + "anymatch": "^3.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@types/webpack-dev-server": { + "version": "3.11.6", + "resolved": "https://registry.npmmirror.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz", + "integrity": "sha512-XCph0RiiqFGetukCTC3KVnY1jwLcZ84illFRMbyFzCcWl90B/76ew0tSqF46oBhnLC4obNDG7dMO0JfTN0MgMQ==", + "dev": true, + "requires": { + "@types/connect-history-api-fallback": "*", + "@types/express": "*", + "@types/serve-static": "*", + "@types/webpack": "^4", + "http-proxy-middleware": "^1.0.0" + }, + "dependencies": { + "http-proxy-middleware": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz", + "integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.5", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + } + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + } + } + }, + "@types/webpack-sources": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/@types%2fwebpack-sources/-/webpack-sources-3.2.0.tgz", + "integrity": "sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/source-list-map": "*", + "source-map": "^0.7.3" + }, + "dependencies": { + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + } + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2feslint-plugin/-/eslint-plugin-5.46.0.tgz", + "integrity": "sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/type-utils": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/parser": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2fparser/-/parser-5.46.0.tgz", + "integrity": "sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "debug": "^4.3.4" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2fscope-manager/-/scope-manager-5.46.0.tgz", + "integrity": "sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2ftype-utils/-/type-utils-5.46.0.tgz", + "integrity": "sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.46.0", + "@typescript-eslint/utils": "5.46.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "@typescript-eslint/types": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2ftypes/-/types-5.46.0.tgz", + "integrity": "sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2ftypescript-estree/-/typescript-estree-5.46.0.tgz", + "integrity": "sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/visitor-keys": "5.46.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/utils": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2futils/-/utils-5.46.0.tgz", + "integrity": "sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.46.0", + "@typescript-eslint/types": "5.46.0", + "@typescript-eslint/typescript-estree": "5.46.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.46.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint%2fvisitor-keys/-/visitor-keys-5.46.0.tgz", + "integrity": "sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.46.0", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, + "@volar/language-core": { + "version": "1.0.24", + "resolved": "https://registry.npmmirror.com/@volar/language-core/-/language-core-1.0.24.tgz", + "integrity": "sha512-vTN+alJiWwK0Pax6POqrmevbtFW2dXhjwWiW/MW4f48eDYPLdyURWcr8TixO7EN/nHsUBj2udT7igFKPtjyAKg==", + "dev": true, + "requires": { + "@volar/source-map": "1.0.24", + "muggle-string": "^0.1.0" + } + }, + "@volar/source-map": { + "version": "1.0.24", + "resolved": "https://registry.npmmirror.com/@volar/source-map/-/source-map-1.0.24.tgz", + "integrity": "sha512-Qsv/tkplx18pgBr8lKAbM1vcDqgkGKQzbChg6NW+v0CZc3G7FLmK+WrqEPzKlN7Cwdc6XVL559Nod8WKAfKr4A==", + "dev": true, + "requires": { + "muggle-string": "^0.1.0" + } + }, + "@volar/typescript": { + "version": "1.0.24", + "resolved": "https://registry.npmmirror.com/@volar/typescript/-/typescript-1.0.24.tgz", + "integrity": "sha512-f8hCSk+PfKR1/RQHxZ79V1NpDImHoivqoizK+mstphm25tn/YJ/JnKNjZHB+o21fuW0yKlI26NV3jkVb2Cc/7A==", + "dev": true, + "requires": { + "@volar/language-core": "1.0.24" + } + }, + "@volar/vue-language-core": { + "version": "1.0.24", + "resolved": "https://registry.npmmirror.com/@volar/vue-language-core/-/vue-language-core-1.0.24.tgz", + "integrity": "sha512-2NTJzSgrwKu6uYwPqLiTMuAzi7fAY3yFy5PJ255bGJc82If0Xr+cW8pC80vpjG0D/aVLmlwAdO4+Ya2BI8GdDg==", + "dev": true, + "requires": { + "@volar/language-core": "1.0.24", + "@volar/source-map": "1.0.24", + "@vue/compiler-dom": "^3.2.45", + "@vue/compiler-sfc": "^3.2.45", + "@vue/reactivity": "^3.2.45", + "@vue/shared": "^3.2.45", + "minimatch": "^5.1.1", + "vue-template-compiler": "^2.7.14" + }, + "dependencies": { + "@vue/compiler-sfc": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz", + "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==", + "dev": true, + "requires": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.47", + "@vue/compiler-dom": "3.2.47", + "@vue/compiler-ssr": "3.2.47", + "@vue/reactivity-transform": "3.2.47", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7", + "postcss": "^8.1.10", + "source-map": "^0.6.1" + } + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@volar/vue-typescript": { + "version": "1.0.24", + "resolved": "https://registry.npmmirror.com/@volar/vue-typescript/-/vue-typescript-1.0.24.tgz", + "integrity": "sha512-9a25oHDvGaNC0okRS47uqJI6FxY4hUQZUsxeOUFHcqVxZEv8s17LPuP/pMMXyz7jPygrZubB/qXqHY5jEu/akA==", + "dev": true, + "requires": { + "@volar/typescript": "1.0.24", + "@volar/vue-language-core": "1.0.24" + } + }, + "@vue/babel-helper-vue-jsx-merge-props": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.2.1.tgz", + "integrity": "sha512-QOi5OW45e2R20VygMSNhyQHvpdUwQZqGPc748JLGCYEy+yp8fNFNdbNIGAgZmi9e+2JHPd6i6idRuqivyicIkA==", + "dev": true + }, + "@vue/babel-helper-vue-transform-on": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.0.2.tgz", + "integrity": "sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==", + "dev": true + }, + "@vue/babel-plugin-jsx": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.1.tgz", + "integrity": "sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.0.0", + "@babel/template": "^7.0.0", + "@babel/traverse": "^7.0.0", + "@babel/types": "^7.0.0", + "@vue/babel-helper-vue-transform-on": "^1.0.2", + "camelcase": "^6.0.0", + "html-tags": "^3.1.0", + "svg-tags": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "dev": true + } + } + }, + "@vue/babel-plugin-transform-vue-jsx": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.2.1.tgz", + "integrity": "sha512-HJuqwACYehQwh1fNT8f4kyzqlNMpBuUK4rSiSES5D4QsYncv5fxFsLyrxFPG2ksO7t5WP+Vgix6tt6yKClwPzA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "html-tags": "^2.0.0", + "lodash.kebabcase": "^4.1.1", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-preset-app": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/babel-preset-app/-/babel-preset-app-4.5.19.tgz", + "integrity": "sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q==", + "dev": true, + "requires": { + "@babel/core": "^7.11.0", + "@babel/helper-compilation-targets": "^7.9.6", + "@babel/helper-module-imports": "^7.8.3", + "@babel/plugin-proposal-class-properties": "^7.8.3", + "@babel/plugin-proposal-decorators": "^7.8.3", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-jsx": "^7.8.3", + "@babel/plugin-transform-runtime": "^7.11.0", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.0", + "@vue/babel-plugin-jsx": "^1.0.3", + "@vue/babel-preset-jsx": "^1.2.4", + "babel-plugin-dynamic-import-node": "^2.3.3", + "core-js": "^3.6.5", + "core-js-compat": "^3.6.5", + "semver": "^6.1.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.20.1", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.10" + } + }, + "regenerator-runtime": { + "version": "0.13.10", + "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", + "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "@vue/babel-preset-jsx": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-preset-jsx/-/babel-preset-jsx-1.2.4.tgz", + "integrity": "sha512-oRVnmN2a77bYDJzeGSt92AuHXbkIxbf/XXSE3klINnh9AXBmVS1DGa1f0d+dDYpLfsAKElMnqKTQfKn7obcL4w==", + "dev": true, + "requires": { + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "@vue/babel-sugar-composition-api-inject-h": "^1.2.1", + "@vue/babel-sugar-composition-api-render-instance": "^1.2.4", + "@vue/babel-sugar-functional-vue": "^1.2.2", + "@vue/babel-sugar-inject-h": "^1.2.2", + "@vue/babel-sugar-v-model": "^1.2.3", + "@vue/babel-sugar-v-on": "^1.2.3" + } + }, + "@vue/babel-sugar-composition-api-inject-h": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.2.1.tgz", + "integrity": "sha512-4B3L5Z2G+7s+9Bwbf+zPIifkFNcKth7fQwekVbnOA3cr3Pq71q71goWr97sk4/yyzH8phfe5ODVzEjX7HU7ItQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-composition-api-render-instance": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.2.4.tgz", + "integrity": "sha512-joha4PZznQMsxQYXtR3MnTgCASC9u3zt9KfBxIeuI5g2gscpTsSKRDzWQt4aqNIpx6cv8On7/m6zmmovlNsG7Q==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-functional-vue": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-sugar-functional-vue/-/babel-sugar-functional-vue-1.2.2.tgz", + "integrity": "sha512-JvbgGn1bjCLByIAU1VOoepHQ1vFsroSA/QkzdiSs657V79q6OwEWLCQtQnEXD/rLTA8rRit4rMOhFpbjRFm82w==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-inject-h": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-sugar-inject-h/-/babel-sugar-inject-h-1.2.2.tgz", + "integrity": "sha512-y8vTo00oRkzQTgufeotjCLPAvlhnpSkcHFEp60+LJUwygGcd5Chrpn5480AQp/thrxVm8m2ifAk0LyFel9oCnw==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0" + } + }, + "@vue/babel-sugar-v-model": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-sugar-v-model/-/babel-sugar-v-model-1.2.3.tgz", + "integrity": "sha512-A2jxx87mySr/ulAsSSyYE8un6SIH0NWHiLaCWpodPCVOlQVODCaSpiR4+IMsmBr73haG+oeCuSvMOM+ttWUqRQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-helper-vue-jsx-merge-props": "^1.2.1", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "camelcase": "^5.0.0", + "html-tags": "^2.0.0", + "svg-tags": "^1.0.0" + } + }, + "@vue/babel-sugar-v-on": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/@vue%2fbabel-sugar-v-on/-/babel-sugar-v-on-1.2.3.tgz", + "integrity": "sha512-kt12VJdz/37D3N3eglBywV8GStKNUhNrsxChXIV+o0MwVXORYuhDTHJRKPgLJRb/EY3vM2aRFQdxJBp9CLikjw==", + "dev": true, + "requires": { + "@babel/plugin-syntax-jsx": "^7.2.0", + "@vue/babel-plugin-transform-vue-jsx": "^1.2.1", + "camelcase": "^5.0.0" + } + }, + "@vue/cli-overlay": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-overlay/-/cli-overlay-4.5.19.tgz", + "integrity": "sha512-GdxvNSmOw7NHIazCO8gTK+xZbaOmScTtxj6eHVeMbYpDYVPJ+th3VMLWNpw/b6uOjwzzcyKlA5dRQ1DAb+gF/g==", + "dev": true + }, + "@vue/cli-plugin-babel": { + "version": "4.5.18", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-babel/-/cli-plugin-babel-4.5.18.tgz", + "integrity": "sha512-Juiu3EgV228pKcfWu6vrABrrXYoMtanf4PlG6P2drPHf1LgVmaUp1g/K7IxdCc63IY9E+eCWjZ2ZEnQvxMSWOQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.0", + "@vue/babel-preset-app": "^4.5.18", + "@vue/cli-shared-utils": "^4.5.18", + "babel-loader": "^8.1.0", + "cache-loader": "^4.1.0", + "thread-loader": "^2.1.3", + "webpack": "^4.0.0" + } + }, + "@vue/cli-plugin-router": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-router/-/cli-plugin-router-4.5.19.tgz", + "integrity": "sha512-3icGzH1IbVYmMMsOwYa0lal/gtvZLebFXdE5hcQJo2mnTwngXGMTyYAzL56EgHBPjbMmRpyj6Iw9k4aVInVX6A==", + "dev": true, + "requires": { + "@vue/cli-shared-utils": "^4.5.19" + } + }, + "@vue/cli-plugin-vuex": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-plugin-vuex/-/cli-plugin-vuex-4.5.19.tgz", + "integrity": "sha512-DUmfdkG3pCdkP7Iznd87RfE9Qm42mgp2hcrNcYQYSru1W1gX2dG/JcW8bxmeGSa06lsxi9LEIc/QD1yPajSCZw==", + "dev": true + }, + "@vue/cli-service": { + "version": "4.5.18", + "resolved": "https://registry.npmmirror.com/@vue/cli-service/-/cli-service-4.5.18.tgz", + "integrity": "sha512-ACRfiyzQNT+3Ko8NdkyvKMsAJ7IziQcM+QvRzfHo11v4YllcQBbRxYsmnffvjaZfNBefQvJ1qX9FI+ggIlxYQA==", + "dev": true, + "requires": { + "@intervolga/optimize-cssnano-plugin": "^1.0.5", + "@soda/friendly-errors-webpack-plugin": "^1.7.1", + "@soda/get-current-script": "^1.0.0", + "@types/minimist": "^1.2.0", + "@types/webpack": "^4.0.0", + "@types/webpack-dev-server": "^3.11.0", + "@vue/cli-overlay": "^4.5.18", + "@vue/cli-plugin-router": "^4.5.18", + "@vue/cli-plugin-vuex": "^4.5.18", + "@vue/cli-shared-utils": "^4.5.18", + "@vue/component-compiler-utils": "^3.1.2", + "@vue/preload-webpack-plugin": "^1.1.0", + "@vue/web-component-wrapper": "^1.2.0", + "acorn": "^7.4.0", + "acorn-walk": "^7.1.1", + "address": "^1.1.2", + "autoprefixer": "^9.8.6", + "browserslist": "^4.12.0", + "cache-loader": "^4.1.0", + "case-sensitive-paths-webpack-plugin": "^2.3.0", + "cli-highlight": "^2.1.4", + "clipboardy": "^2.3.0", + "cliui": "^6.0.0", + "copy-webpack-plugin": "^5.1.1", + "css-loader": "^3.5.3", + "cssnano": "^4.1.10", + "debug": "^4.1.1", + "default-gateway": "^5.0.5", + "dotenv": "^8.2.0", + "dotenv-expand": "^5.1.0", + "file-loader": "^4.2.0", + "fs-extra": "^7.0.1", + "globby": "^9.2.0", + "hash-sum": "^2.0.0", + "html-webpack-plugin": "^3.2.0", + "launch-editor-middleware": "^2.2.1", + "lodash.defaultsdeep": "^4.6.1", + "lodash.mapvalues": "^4.6.0", + "lodash.transform": "^4.6.0", + "mini-css-extract-plugin": "^0.9.0", + "minimist": "^1.2.5", + "pnp-webpack-plugin": "^1.6.4", + "portfinder": "^1.0.26", + "postcss-loader": "^3.0.0", + "ssri": "^8.0.1", + "terser-webpack-plugin": "^1.4.4", + "thread-loader": "^2.1.3", + "url-loader": "^2.2.0", + "vue-loader": "^15.9.2", + "vue-loader-v16": "npm:vue-loader@^16.1.0", + "vue-style-loader": "^4.1.2", + "webpack": "^4.0.0", + "webpack-bundle-analyzer": "^3.8.0", + "webpack-chain": "^6.4.0", + "webpack-dev-server": "^3.11.0", + "webpack-merge": "^4.2.2" + }, + "dependencies": { + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-walk": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-7.2.0.tgz", + "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + } + }, + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==" + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==" + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + }, + "dependencies": { + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "hash-sum": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-2.0.0.tgz", + "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==", + "dev": true + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmmirror.com/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==" + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + } + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-1.0.0.tgz", + "integrity": "sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "vue-loader-v16": { + "version": "npm:vue-loader@16.8.3", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-16.8.3.tgz", + "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "optional": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "optional": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "optional": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "optional": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "3.9.0", + "resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz", + "integrity": "sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1", + "bfj": "^6.1.1", + "chalk": "^2.4.1", + "commander": "^2.18.0", + "ejs": "^2.6.1", + "express": "^4.16.3", + "filesize": "^3.6.1", + "gzip-size": "^5.0.0", + "lodash": "^4.17.19", + "mkdirp": "^0.5.1", + "opener": "^1.5.1", + "ws": "^6.0.0" + } + } + } + }, + "@vue/cli-shared-utils": { + "version": "4.5.19", + "resolved": "https://registry.npmmirror.com/@vue/cli-shared-utils/-/cli-shared-utils-4.5.19.tgz", + "integrity": "sha512-JYpdsrC/d9elerKxbEUtmSSU6QRM60rirVubOewECHkBHj+tLNznWq/EhCjswywtePyLaMUK25eTqnTSZlEE+g==", + "dev": true, + "requires": { + "@achrinza/node-ipc": "9.2.2", + "@hapi/joi": "^15.0.1", + "chalk": "^2.4.2", + "execa": "^1.0.0", + "launch-editor": "^2.2.1", + "lru-cache": "^5.1.1", + "open": "^6.3.0", + "ora": "^3.4.0", + "read-pkg": "^5.1.1", + "request": "^2.88.2", + "semver": "^6.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" + } + } + }, + "@vue/compiler-core": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz", + "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==", + "dev": true, + "requires": { + "@babel/parser": "^7.16.4", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "@vue/compiler-dom": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz", + "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==", + "dev": true, + "requires": { + "@vue/compiler-core": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "@vue/compiler-sfc": { + "version": "2.7.14", + "resolved": "https://registry.npmmirror.com/@vue%2fcompiler-sfc/-/compiler-sfc-2.7.14.tgz", + "integrity": "sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA==", + "requires": { + "@babel/parser": "^7.18.4", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@vue/compiler-ssr": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz", + "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==", + "dev": true, + "requires": { + "@vue/compiler-dom": "3.2.47", + "@vue/shared": "3.2.47" + } + }, + "@vue/component-compiler-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/@vue%2fcomponent-compiler-utils/-/component-compiler-utils-3.3.0.tgz", + "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==", + "dev": true, + "requires": { + "consolidate": "^0.15.1", + "hash-sum": "^1.0.2", + "lru-cache": "^4.1.2", + "merge-source-map": "^1.1.0", + "postcss": "^7.0.36", + "postcss-selector-parser": "^6.0.2", + "prettier": "^1.18.2 || ^2.0.0", + "source-map": "~0.6.1", + "vue-template-es2015-compiler": "^1.9.0" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "@vue/devtools-api": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/@vue%2fdevtools-api/-/devtools-api-6.5.0.tgz", + "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==" + }, + "@vue/eslint-config-standard": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/@vue%2feslint-config-standard/-/eslint-config-standard-6.1.0.tgz", + "integrity": "sha512-9+hrEyflDzsGdlBDl9jPV5DIYUx1TOU5OSQqRDKCrNumrxRj5HRWKuk+ocXWnha6uoNRtLC24mY7d/MwqvBCNw==", + "dev": true, + "requires": { + "eslint-config-standard": "^16.0.3", + "eslint-import-resolver-node": "^0.3.4", + "eslint-import-resolver-webpack": "^0.13.1" + } + }, + "@vue/preload-webpack-plugin": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/@vue/preload-webpack-plugin/-/preload-webpack-plugin-1.1.2.tgz", + "integrity": "sha512-LIZMuJk38pk9U9Ur4YzHjlIyMuxPlACdBIHH9/nGYVTsaGKOSnSuELiE8vS9wa+dJpIYspYUOqk+L1Q4pgHQHQ==", + "dev": true + }, + "@vue/reactivity": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz", + "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==", + "dev": true, + "requires": { + "@vue/shared": "3.2.47" + } + }, + "@vue/reactivity-transform": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz", + "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==", + "dev": true, + "requires": { + "@babel/parser": "^7.16.4", + "@vue/compiler-core": "3.2.47", + "@vue/shared": "3.2.47", + "estree-walker": "^2.0.2", + "magic-string": "^0.25.7" + } + }, + "@vue/shared": { + "version": "3.2.47", + "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz", + "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==", + "dev": true + }, + "@vue/web-component-wrapper": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/@vue/web-component-wrapper/-/web-component-wrapper-1.3.0.tgz", + "integrity": "sha512-Iu8Tbg3f+emIIMmI2ycSI8QcEuAUgPTgHwesDU1eKMLE4YC/c/sFbGc70QgMq31ijRftV0R7vCm9co6rldCeOA==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "dev": true, + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2ffloating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==", + "dev": true + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "dev": true, + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==", + "dev": true + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fhelper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fleb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2futf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fwasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fwasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fwasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fwasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fwast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/@webassemblyjs%2fwast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/@xtuc%2fieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/@xtuc%2flong/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true + }, + "address": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/address/-/address-1.2.1.tgz", + "integrity": "sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==", + "dev": true + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "agentkeepalive": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz", + "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==", + "dev": true, + "requires": { + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/aggregate-error/download/aggregate-error-3.0.1.tgz", + "integrity": "sha1-2y/nJG5Tb0DZtUQqOeEX191qJOA=", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + }, + "ali-oss": { + "version": "6.17.1", + "resolved": "https://registry.npmmirror.com/ali-oss/-/ali-oss-6.17.1.tgz", + "integrity": "sha512-v2oT3UhSJTH/LrsscVvi7iEGrnundydNaFzpYAKatqOl4JNcBV4UiwtlJU+ZHLys040JH2k+CutznA0GoE+P2w==", + "dev": true, + "requires": { + "address": "^1.0.0", + "agentkeepalive": "^3.4.1", + "bowser": "^1.6.0", + "copy-to": "^2.0.1", + "dateformat": "^2.0.0", + "debug": "^2.2.0", + "destroy": "^1.0.4", + "end-or-error": "^1.0.1", + "get-ready": "^1.0.0", + "humanize-ms": "^1.2.0", + "is-type-of": "^1.0.0", + "js-base64": "^2.5.2", + "jstoxml": "^2.0.0", + "merge-descriptors": "^1.0.1", + "mime": "^2.4.5", + "mz-modules": "^2.1.0", + "platform": "^1.3.1", + "pump": "^3.0.0", + "sdk-base": "^2.0.1", + "stream-http": "2.8.2", + "stream-wormhole": "^1.0.4", + "urllib": "^2.33.1", + "utility": "^1.8.0", + "xml2js": "^0.4.16" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "js-base64": { + "version": "2.6.4", + "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==", + "dev": true + }, + "stream-http": { + "version": "2.8.2", + "resolved": "https://registry.npmmirror.com/stream-http/-/stream-http-2.8.2.tgz", + "integrity": "sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + } + } + }, + "alloyfinger": { + "version": "0.1.16", + "resolved": "https://registry.npmmirror.com/alloyfinger/-/alloyfinger-0.1.16.tgz", + "integrity": "sha512-AfsLALs929WQsjSk1pbysoiVU3bgm/4k1wdZDtMQ7uI7b8XweqCCnUiBYqqdp8uPZ1fBq/+LCJhgUlhd90FssQ==" + }, + "alphanum-sort": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz", + "integrity": "sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==", + "dev": true + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + } + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/download/ansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/arr-diff/download/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/arr-union/download/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-find": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/array-find/-/array-find-1.0.0.tgz", + "integrity": "sha1-bI4obRHtdoMn+OYuzuhzU8o+eLg=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmmirror.com/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "dependencies": { + "es-abstract": { + "version": "1.20.5", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.20.5.tgz", + "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "unbox-primitive": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + } + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/array-uniq/download/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/array-unique/download/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "dependencies": { + "es-abstract": { + "version": "1.20.5", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.20.5.tgz", + "integrity": "sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "unbox-primitive": "^1.0.2" + } + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + }, + "string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + } + } + } + }, + "array.prototype.reduce": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz", + "integrity": "sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-array-method-boxes-properly": "^1.0.0", + "is-string": "^1.0.7" + } + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmmirror.com/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "dev": true, + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/inherits/download/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", + "dev": true + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmmirror.com/util/download/util-0.10.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Futil%2Fdownload%2Futil-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "dev": true, + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/assign-symbols/download/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmmirror.com/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "requires": { + "tslib": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmmirror.com/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/async-limiter/download/async-limiter-1.0.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fasync-limiter%2Fdownload%2Fasync-limiter-1.0.1.tgz", + "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=", + "dev": true + }, + "async-validator": { + "version": "4.2.5", + "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", + "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/atob/download/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "dev": true + }, + "autoprefixer": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-8.0.0.tgz", + "integrity": "sha512-XBEqAoESCyGu3daYmWcTC37Dwmjvs0y40UtUO3MMX+Pd/w7jwNFfUKNtxoMFu0u0wcotP+arDpU3JVH54UV79Q==", + "dev": true, + "requires": { + "browserslist": "^3.0.0", + "caniuse-lite": "^1.0.30000808", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.17", + "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmmirror.com/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmmirror.com/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + } + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-import": { + "version": "1.13.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-import/download/babel-plugin-import-1.13.3.tgz?cache=0&sync_timestamp=1606209944483&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fbabel-plugin-import%2Fdownload%2Fbabel-plugin-import-1.13.3.tgz", + "integrity": "sha1-nbu6fRrHK9QSkXqDDUReAJQdJtc=", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/runtime": "^7.0.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", + "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3", + "core-js-compat": "^3.25.1" + }, + "dependencies": { + "browserslist": { + "version": "4.21.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.21.4.tgz", + "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001400", + "electron-to-chromium": "^1.4.251", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.9" + } + }, + "core-js-compat": { + "version": "3.26.1", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.26.1.tgz", + "integrity": "sha512-622/KzTudvXCDLRw70iHW4KKs1aGpcRcowGWyYJr2DEBfRrd6hNJybxSWJFuZYD4ma86xhrwDDHxmDaIq4EA8A==", + "dev": true, + "requires": { + "browserslist": "^4.21.4" + } + }, + "electron-to-chromium": { + "version": "1.4.284", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", + "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + } + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + } + }, + "babel-plugin-transform-remove-console": { + "version": "6.9.4", + "resolved": "https://registry.npmmirror.com/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz", + "integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=", + "dev": true + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/balanced-match/download/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmmirror.com/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/batch/download/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "bfj": { + "version": "6.1.2", + "resolved": "https://registry.npmmirror.com/bfj/-/bfj-6.1.2.tgz", + "integrity": "sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "check-types": "^8.0.3", + "hoopy": "^0.1.4", + "tryer": "^1.0.1" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/big.js/download/big.js-5.2.2.tgz", + "integrity": "sha1-ZfCvOC9Xi83HQr2cKB6cstd2gyg=", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "dev": true, + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + }, + "dependencies": { + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "dev": true, + "optional": true + } + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/bluebird/download/bluebird-3.7.2.tgz", + "integrity": "sha1-nyKcFb4nJFT/qXOs4NvueaGww28=", + "dev": true + }, + "bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", + "dev": true + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmmirror.com/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmmirror.com/bonjour/download/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "dev": true, + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/boolbase/download/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "bowser": { + "version": "1.9.4", + "resolved": "https://registry.npmmirror.com/bowser/-/bowser-1.9.4.tgz", + "integrity": "sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmmirror.com/brace-expansion/download/brace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/brorand/download/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", + "dev": true + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dev": true, + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "dev": true, + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz", + "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", + "dev": true, + "requires": { + "bn.js": "^5.0.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "dev": true, + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "dev": true, + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.20.4", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.20.4.tgz", + "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001349", + "electron-to-chromium": "^1.4.147", + "escalade": "^3.1.1", + "node-releases": "^2.0.5", + "picocolors": "^1.0.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmmirror.com/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", + "dev": true + }, + "buffer-json": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/buffer-json/-/buffer-json-2.0.0.tgz", + "integrity": "sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==", + "dev": true + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/buffer-xor/download/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", + "dev": true + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "dev": true + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmmirror.com/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "dev": true, + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "dev": true, + "requires": { + "figgy-pudding": "^3.5.1" + } + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cache-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/cache-loader/-/cache-loader-4.1.0.tgz", + "integrity": "sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw==", + "dev": true, + "requires": { + "buffer-json": "^2.0.0", + "find-cache-dir": "^3.0.0", + "loader-utils": "^1.2.3", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "schema-utils": "^2.0.0" + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==" + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==", + "dev": true, + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-api": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/caniuse-api/-/caniuse-api-3.0.0.tgz", + "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-lite": "^1.0.0", + "lodash.memoize": "^4.1.2", + "lodash.uniq": "^4.5.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001464", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001464.tgz", + "integrity": "sha512-oww27MtUmusatpRpCGSOneQk2/l5czXANDSFvsc7VuOQ86s3ANhZetpwXNf1zY/zdfP63Xvjz325DAdAoES13g==", + "dev": true + }, + "case-sensitive-paths-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/chalk/download/chalk-2.4.2.tgz?cache=0&sync_timestamp=1591687070184&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fchalk%2Fdownload%2Fchalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", + "dev": true + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", + "dev": true + }, + "check-types": { + "version": "8.0.3", + "resolved": "https://registry.npmmirror.com/check-types/-/check-types-8.0.3.tgz", + "integrity": "sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.4", + "resolved": "https://registry.npmmirror.com/clean-css/-/clean-css-4.2.4.tgz", + "integrity": "sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clean-stack/download/clean-stack-2.2.0.tgz?cache=0&sync_timestamp=1564586594378&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fclean-stack%2Fdownload%2Fclean-stack-2.2.0.tgz", + "integrity": "sha1-7oRy27Ep5yezHooQpCfe6d/kAIs=", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-highlight": { + "version": "2.1.11", + "resolved": "https://registry.npmmirror.com/cli-highlight/-/cli-highlight-2.1.11.tgz", + "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "highlight.js": "^10.7.1", + "mz": "^2.4.0", + "parse5": "^5.1.1", + "parse5-htmlparser2-tree-adapter": "^6.0.0", + "yargs": "^16.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "dev": true, + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true + }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + } + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, + "clipboard": { + "version": "2.0.10", + "resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.10.tgz", + "integrity": "sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==", + "requires": { + "good-listener": "^1.2.2", + "select": "^1.1.2", + "tiny-emitter": "^2.0.0" + } + }, + "clipboardy": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/clipboardy/-/clipboardy-2.3.0.tgz", + "integrity": "sha512-mKhiIL2DrQIsuXMgBgnfEHOZOryC7kY7YO//TN6c63wlEm3NG5tz+YgY5rVi29KCmq/QQjKYvM7a19+MDOTHOQ==", + "dev": true, + "requires": { + "arch": "^2.1.1", + "execa": "^1.0.0", + "is-wsl": "^2.1.1" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==", + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==" + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "requires": { + "is-regexp": "^2.0.0" + } + }, + "coa": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/coa/-/coa-2.0.2.tgz", + "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==", + "requires": { + "@types/q": "^1.5.1", + "chalk": "^2.4.1", + "q": "^1.1.2" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/collection-visit/download/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmmirror.com/color-convert/download/color-convert-1.9.3.tgz?cache=0&sync_timestamp=1566248870121&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fcolor-convert%2Fdownload%2Fcolor-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/color-name/download/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colorette": { + "version": "2.0.17", + "resolved": "https://registry.npmmirror.com/colorette/-/colorette-2.0.17.tgz", + "integrity": "sha512-hJo+3Bkn0NCHybn9Tu35fIeoOKGOk5OCC32y4Hz2It+qlCO2Q3DeQ1hRn/tDDMQKRYUEzqsl7jbF6dYKjlE60g==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmmirror.com/commander/download/commander-2.20.3.tgz?cache=0&sync_timestamp=1587781810870&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz", + "integrity": "sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/commondir/download/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/component-emitter/download/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmmirror.com/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmmirror.com/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/bytes/download/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/concat-map/download/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmmirror.com/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "dev": true + }, + "consolidate": { + "version": "0.15.1", + "resolved": "https://registry.npmmirror.com/consolidate/-/consolidate-0.15.1.tgz", + "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==", + "dev": true, + "requires": { + "bluebird": "^3.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/constants-browserify/download/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmmirror.com/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/content-type/download/content-type-1.0.4.tgz", + "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "dev": true + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/cookie-signature/download/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", + "dev": true + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/copy-descriptor/download/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "copy-to": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/copy-to/-/copy-to-2.0.1.tgz", + "integrity": "sha1-JoD7uAaKSNCGVrYJgJK9r8kG9KU=", + "dev": true + }, + "copy-webpack-plugin": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz", + "integrity": "sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ==", + "dev": true, + "requires": { + "cacache": "^12.0.3", + "find-cache-dir": "^2.1.0", + "glob-parent": "^3.1.0", + "globby": "^7.1.1", + "is-glob": "^4.0.1", + "loader-utils": "^1.2.3", + "minimatch": "^3.0.4", + "normalize-path": "^3.0.0", + "p-limit": "^2.2.1", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/globby/-/globby-7.1.1.tgz", + "integrity": "sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "dev": true + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + } + } + }, + "core-js": { + "version": "3.26.1", + "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.26.1.tgz", + "integrity": "sha512-21491RRQVzUn0GGM9Z1Jrpr6PNPxPi+Za8OM9q4tksTSnlbXXGKK1nXNg/QvwFYettXvSX6zWKCtHHfjN4puyA==", + "dev": true + }, + "core-js-compat": { + "version": "3.22.8", + "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.22.8.tgz", + "integrity": "sha512-pQnwg4xtuvc2Bs/5zYQPaEYYSuTxsF7LBWF0SvnVhthZo/Qe+rJpcEekrdNK5DWwDJ0gv0oI9NNX5Mppdy0ctg==", + "dev": true, + "requires": { + "browserslist": "^4.20.3", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/core-util-is/download/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cosmiconfig": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz", + "integrity": "sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dev": true, + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmmirror.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "dev": true, + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css-color-names": { + "version": "0.0.4", + "resolved": "https://registry.npmmirror.com/css-color-names/-/css-color-names-0.0.4.tgz", + "integrity": "sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==", + "dev": true + }, + "css-declaration-sorter": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz", + "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==", + "dev": true, + "requires": { + "postcss": "^7.0.1", + "timsort": "^0.3.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-loader": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-3.6.0.tgz", + "integrity": "sha512-M5lSukoWi1If8dhQAUCvj4H8vUt3vOnwbQBH9DdTm/s4Ym2B/3dPMtYZeJmq7Q3S3Pa+I94DcZ7pc9bP14cWIQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.32", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.1.0", + "schema-utils": "^2.7.0", + "semver": "^6.3.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-select-base-adapter": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", + "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" + }, + "css-tree": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "requires": { + "mdn-data": "2.0.14", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/cssesc/download/cssesc-3.0.0.tgz", + "integrity": "sha1-N3QZGZA7hoVl4cCep0dEXNGJg+4=", + "dev": true + }, + "cssnano": { + "version": "4.1.11", + "resolved": "https://registry.npmmirror.com/cssnano/-/cssnano-4.1.11.tgz", + "integrity": "sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "cssnano-preset-default": "^4.0.8", + "is-resolvable": "^1.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cssnano-preset-default": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz", + "integrity": "sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ==", + "dev": true, + "requires": { + "css-declaration-sorter": "^4.0.1", + "cssnano-util-raw-cache": "^4.0.1", + "postcss": "^7.0.0", + "postcss-calc": "^7.0.1", + "postcss-colormin": "^4.0.3", + "postcss-convert-values": "^4.0.1", + "postcss-discard-comments": "^4.0.2", + "postcss-discard-duplicates": "^4.0.2", + "postcss-discard-empty": "^4.0.1", + "postcss-discard-overridden": "^4.0.1", + "postcss-merge-longhand": "^4.0.11", + "postcss-merge-rules": "^4.0.3", + "postcss-minify-font-values": "^4.0.2", + "postcss-minify-gradients": "^4.0.2", + "postcss-minify-params": "^4.0.2", + "postcss-minify-selectors": "^4.0.2", + "postcss-normalize-charset": "^4.0.1", + "postcss-normalize-display-values": "^4.0.2", + "postcss-normalize-positions": "^4.0.2", + "postcss-normalize-repeat-style": "^4.0.2", + "postcss-normalize-string": "^4.0.2", + "postcss-normalize-timing-functions": "^4.0.2", + "postcss-normalize-unicode": "^4.0.1", + "postcss-normalize-url": "^4.0.1", + "postcss-normalize-whitespace": "^4.0.2", + "postcss-ordered-values": "^4.1.2", + "postcss-reduce-initial": "^4.0.3", + "postcss-reduce-transforms": "^4.0.2", + "postcss-svgo": "^4.0.3", + "postcss-unique-selectors": "^4.0.1" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cssnano-util-get-arguments": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz", + "integrity": "sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw==", + "dev": true + }, + "cssnano-util-get-match": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz", + "integrity": "sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw==", + "dev": true + }, + "cssnano-util-raw-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz", + "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "cssnano-util-same-parent": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz", + "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==", + "dev": true + }, + "csso": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/csso/-/csso-4.2.0.tgz", + "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "requires": { + "css-tree": "^1.1.2" + } + }, + "csstype": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.1.tgz", + "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-uri-to-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", + "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", + "dev": true + }, + "dateformat": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/decamelize/download/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/decamelize-keys/download/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + } + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/decode-uri-component/download/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "dev": true, + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "default-gateway": { + "version": "5.0.5", + "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-5.0.5.tgz", + "integrity": "sha512-z2RnruVmj8hVMmAnEJMTIJNijhKCDiGjbLP+BHJFOT7ld3Bo5qcIBpVYDniqhbMIIf+jZDlkP2MkPXiQy/DBLA==", + "dev": true, + "requires": { + "execa": "^3.3.0" + }, + "dependencies": { + "execa": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "p-finally": "^2.0.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-finally": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/p-finally/-/p-finally-2.0.1.tgz", + "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", + "dev": true + } + } + }, + "default-user-agent": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/default-user-agent/-/default-user-agent-1.0.0.tgz", + "integrity": "sha1-FsRu/cq6PtxF8k8r1IaLAbfCrcY=", + "dev": true, + "requires": { + "os-name": "~1.0.3" + } + }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "degenerator": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/degenerator/-/degenerator-3.0.2.tgz", + "integrity": "sha512-c0mef3SNQo56t6urUU6tdQAs+ThoD0o9B9MJ8HEt7NQcGEILCRFqQb7ZbP9JAv+QF1Ky5plydhMR/IrqWDm+TQ==", + "dev": true, + "requires": { + "ast-types": "^0.13.2", + "escodegen": "^1.8.1", + "esprima": "^4.0.0", + "vm2": "^3.9.8" + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/globby/download/globby-6.1.0.tgz?cache=0&sync_timestamp=1591083783605&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fglobby%2Fdownload%2Fglobby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/pify/download/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", + "dev": true + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "delegate": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz", + "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/depd/download/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=", + "dev": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmmirror.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "digest-header": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/digest-header/-/digest-header-0.0.1.tgz", + "integrity": "sha1-Ecz23uxXZqw3l0TZAcEsuklRS+Y=", + "dev": true, + "requires": { + "utility": "0.1.11" + }, + "dependencies": { + "utility": { + "version": "0.1.11", + "resolved": "https://registry.npmmirror.com/utility/-/utility-0.1.11.tgz", + "integrity": "sha1-/eYM+bTkdRlHoM9dEEzik2ciZxU=", + "dev": true, + "requires": { + "address": ">=0.0.1" + } + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/dns-equal/download/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", + "dev": true + }, + "dns-packet": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/dns-packet/-/dns-packet-1.3.4.tgz", + "integrity": "sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA==", + "dev": true, + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/dns-txt/download/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "dev": true, + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-converter": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/dom-converter/-/dom-converter-0.2.0.tgz", + "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", + "dev": true, + "requires": { + "utila": "~0.4" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "dev": true + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "dotenv": { + "version": "8.6.0", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-8.6.0.tgz", + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmmirror.com/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "dev": true, + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "easy-stack": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/ee-first/download/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", + "dev": true + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmmirror.com/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.147", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.147.tgz", + "integrity": "sha512-czclPqxLMPqPMkahKcske4TaS5lcznsc26ByBlEFDU8grTBVK9C5W6K9I6oEEhm4Ai4jTihGnys90xY1yjXcRg==", + "dev": true + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmmirror.com/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dev": true, + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/emoji-regex/download/emoji-regex-8.0.0.tgz", + "integrity": "sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/emojis-list/download/emojis-list-3.0.0.tgz", + "integrity": "sha1-VXBmIEatKeLpFucariYKvf9Pang=", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/encodeurl/download/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmmirror.com/end-of-stream/download/end-of-stream-1.4.4.tgz?cache=0&sync_timestamp=1569416367473&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fend-of-stream%2Fdownload%2Fend-of-stream-1.4.4.tgz", + "integrity": "sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=", + "requires": { + "once": "^1.4.0" + } + }, + "end-or-error": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/end-or-error/-/end-or-error-1.0.1.tgz", + "integrity": "sha1-3HpiEP5403L+4kqLSJnb0VVBTcs=", + "dev": true + }, + "enhanced-resolve": { + "version": "0.9.1", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", + "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.2.0", + "tapable": "^0.1.8" + } + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "dev": true, + "requires": { + "stackframe": "^1.3.4" + } + }, + "es-abstract": { + "version": "1.20.1", + "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.20.1.tgz", + "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==", + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.1", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.4", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.2", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "es-array-method-boxes-properly": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz", + "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" + }, + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/escape-html/download/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "escodegen": { + "version": "1.14.3", + "resolved": "https://registry.npmmirror.com/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "eslint": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.18.0.tgz", + "integrity": "sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + }, + "dependencies": { + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + } + } + }, + "globals": { + "version": "13.15.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-13.15.0.tgz", + "integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmmirror.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true + }, + "eslint-config-standard": { + "version": "16.0.3", + "resolved": "https://registry.npmmirror.com/eslint-config-standard/-/eslint-config-standard-16.0.3.tgz", + "integrity": "sha512-x4fmJL5hGqNJKGHSjnLdgA6U6h1YW/G2dW9fA+cyVur4SK6lyue8+UgNKWlZtUDTXvgKDD/Oa3GQjmB5kjtVvg==", + "dev": true + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmmirror.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "eslint-import-resolver-webpack": { + "version": "0.13.2", + "resolved": "https://registry.npmmirror.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.13.2.tgz", + "integrity": "sha512-XodIPyg1OgE2h5BDErz3WJoK7lawxKTJNhgPNafRST6csC/MZC+L5P6kKqsZGRInpbgc02s/WZMrb4uGJzcuRg==", + "dev": true, + "requires": { + "array-find": "^1.0.0", + "debug": "^3.2.7", + "enhanced-resolve": "^0.9.1", + "find-root": "^1.1.0", + "has": "^1.0.3", + "interpret": "^1.4.0", + "is-core-module": "^2.7.0", + "is-regex": "^1.1.4", + "lodash": "^4.17.21", + "resolve": "^1.20.0", + "semver": "^5.7.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmmirror.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "dev": true, + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-plugin-prettier": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-plugin-promise": { + "version": "6.1.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", + "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "dev": true + }, + "eslint-plugin-sonarjs": { + "version": "0.17.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.17.0.tgz", + "integrity": "sha512-jtGtxI49UbJJeJj7CVRLI3+LLH+y+hkR3GOOwM7vBbci9DEFIRGCWvEd2BJScrzltZ6D6iubukTAfc9cyG7sdw==", + "dev": true + }, + "eslint-plugin-vue": { + "version": "9.8.0", + "resolved": "https://registry.npmmirror.com/eslint-plugin-vue/-/eslint-plugin-vue-9.8.0.tgz", + "integrity": "sha512-E/AXwcTzunyzM83C2QqDHxepMzvI2y6x+mmeYHbVDQlKFqmKYvRrhaVixEeeG27uI44p9oKDFiyCRw4XxgtfHA==", + "dev": true, + "requires": { + "eslint-utils": "^3.0.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^9.0.1", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.11", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.11.tgz", + "integrity": "sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "eslint-webpack-plugin": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/eslint-webpack-plugin/-/eslint-webpack-plugin-2.7.0.tgz", + "integrity": "sha512-bNaVVUvU4srexGhVcayn/F4pJAz19CWBkKoMx7aSQ4wtTbZQCnG5O9LHCE42mM+JSKOUp7n6vd5CIwzj7lOVGA==", + "dev": true, + "requires": { + "@types/eslint": "^7.29.0", + "arrify": "^2.0.1", + "jest-worker": "^27.5.1", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^3.1.1" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "espree": { + "version": "9.3.2", + "resolved": "https://registry.npmmirror.com/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "dev": true, + "requires": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/download/estraverse-4.3.0.tgz?cache=0&sync_timestamp=1586968505635&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Festraverse%2Fdownload%2Festraverse-4.3.0.tgz", + "integrity": "sha1-OYrT88WiSUi+dyXoPRGn3ijNvR0=", + "dev": true + }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/download/esutils-2.0.3.tgz", + "integrity": "sha1-dNLrTeC42hKTcRkQ1Qd1ubcQ72Q=", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmmirror.com/etag/download/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", + "dev": true + }, + "event-pubsub": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/event-pubsub/-/event-pubsub-4.3.0.tgz", + "integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" + }, + "eventsource": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/eventsource/-/eventsource-1.1.2.tgz", + "integrity": "sha512-xAH3zWhgO2/3KIniEKYPr8plNSzlGINOUqYj0m0u7AB81iRw8b/3E73W6AuU+6klLbaSFmZnaETQ2lXPfAydrA==", + "dev": true + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dev": true, + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + } + } + }, + "execall": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "requires": { + "clone-regexp": "^2.1.0" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/expand-brackets/download/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend/download/extend-3.0.2.tgz", + "integrity": "sha1-+LETa0Bx+9jrFAr/hYsQGewpFfo=", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/extend-shallow/download/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz?cache=0&sync_timestamp=1591599697571&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ffast-deep-equal%2Fdownload%2Ffast-deep-equal-3.1.3.tgz", + "integrity": "sha1-On1WtVnWy8PrUSMlJE5hmmXGxSU=" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/download/fast-json-stable-stringify-2.1.0.tgz?cache=0&sync_timestamp=1576340291001&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ffast-json-stable-stringify%2Fdownload%2Ffast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha1-h0v2nG9ATCtdmcSBNBOZ/VWJJjM=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/download/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmmirror.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmmirror.com/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmmirror.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-loader": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/file-loader/-/file-loader-4.3.0.tgz", + "integrity": "sha512-aKrYPYjF1yG3oX0kWRrqrSMfgftm7oJW5M+m4owoldH5C51C0RkIwB++JbRvEW3IU6/ZG5n8UvEcdgwOt2UOWA==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + } + }, + "file-uri-to-path": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz", + "integrity": "sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg==", + "dev": true + }, + "filesize": { + "version": "3.6.1", + "resolved": "https://registry.npmmirror.com/filesize/-/filesize-3.6.1.tgz", + "integrity": "sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "dependencies": { + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "3.2.5", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.2.5.tgz", + "integrity": "sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/for-in/download/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true + }, + "fork-ts-checker-webpack-plugin": { + "version": "6.5.2", + "resolved": "https://registry.npmmirror.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.2.tgz", + "integrity": "sha512-m5cUmF30xkZ7h4tWUgTAcEaKmUW7tfyUyTqNNOz7OxWJ0v1VWKTcOvH8FWHUwSjlW/356Ijc9vi3XfcPstpQKA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@types/json-schema": "^7.0.5", + "chalk": "^4.1.0", + "chokidar": "^3.4.2", + "cosmiconfig": "^6.0.0", + "deepmerge": "^4.2.2", + "fs-extra": "^9.0.0", + "glob": "^7.1.6", + "memfs": "^3.1.2", + "minimatch": "^3.0.4", + "schema-utils": "2.7.0", + "semver": "^7.3.2", + "tapable": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "formstream": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/formstream/-/formstream-1.1.1.tgz", + "integrity": "sha512-yHRxt3qLFnhsKAfhReM4w17jP+U1OlhUjnKPPtonwKbIJO7oBP0MvoxkRUwb8AU9n0MIkYy5X5dK6pQnbj+R2Q==", + "dev": true, + "requires": { + "destroy": "^1.0.4", + "mime": "^2.5.2", + "pause-stream": "~0.0.11" + }, + "dependencies": { + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + } + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/fragment-cache/download/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmmirror.com/fresh/download/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", + "dev": true + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-monkey": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/fs-monkey/-/fs-monkey-1.0.3.tgz", + "integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==", + "dev": true + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/fs.realpath/download/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "ftp": { + "version": "0.3.10", + "resolved": "https://registry.npmmirror.com/ftp/-/ftp-0.3.10.tgz", + "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", + "dev": true, + "requires": { + "readable-stream": "1.1.x", + "xregexp": "2.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/functional-red-black-tree/download/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==" + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz", + "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, + "get-ready": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/get-ready/-/get-ready-1.0.0.tgz", + "integrity": "sha1-+RgX8emt7P6hOlYq38jeiDqzR4I=", + "dev": true + }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "get-uri": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/get-uri/-/get-uri-3.0.2.tgz", + "integrity": "sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "data-uri-to-buffer": "3", + "debug": "4", + "file-uri-to-path": "2", + "fs-extra": "^8.1.0", + "ftp": "^0.3.10" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/get-value/download/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmmirror.com/glob/download/glob-7.1.6.tgz", + "integrity": "sha1-FB8zuBp8JJLhJVlDB0gMRmeSeKY=", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", + "dev": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig==", + "dev": true + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "requires": { + "global-prefix": "^3.0.0" + } + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmmirror.com/globals/download/globals-11.12.0.tgz?cache=0&sync_timestamp=1591426170432&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fglobals%2Fdownload%2Fglobals-11.12.0.tgz", + "integrity": "sha1-q4eVM4hooLq9hSV1gBjCp+uVxC4=", + "dev": true + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmmirror.com/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/globjoin/download/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, + "gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "good-listener": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/good-listener/download/good-listener-1.2.2.tgz", + "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=", + "requires": { + "delegate": "^3.1.2" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmmirror.com/graceful-fs/download/graceful-fs-4.2.4.tgz", + "integrity": "sha1-Ila94U02MpWMRl68ltxGfKB6Kfs=", + "dev": true + }, + "gzip-size": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-5.1.1.tgz", + "integrity": "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA==", + "dev": true, + "requires": { + "duplexer": "^0.1.1", + "pify": "^4.0.1" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmmirror.com/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/download/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-value/download/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/has-values/download/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/kind-of/download/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dev": true, + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + } + } + }, + "hash-sum": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/hash-sum/-/hash-sum-1.0.2.tgz", + "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=", + "dev": true + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/he/download/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "dev": true + }, + "hex-color-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz", + "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==", + "dev": true + }, + "highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/hmac-drbg/download/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "dev": true, + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoopy": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/hoopy/-/hoopy-0.1.4.tgz", + "integrity": "sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmmirror.com/hpack.js/download/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "hsl-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/hsl-regex/-/hsl-regex-1.0.0.tgz", + "integrity": "sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A==", + "dev": true + }, + "hsla-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/hsla-regex/-/hsla-regex-1.0.0.tgz", + "integrity": "sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA==", + "dev": true + }, + "html-entities": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/html-entities/-/html-entities-1.4.0.tgz", + "integrity": "sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==", + "dev": true + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmmirror.com/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "dev": true, + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + }, + "dependencies": { + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==", + "dev": true + } + } + }, + "html-tags": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-2.0.0.tgz", + "integrity": "sha1-ELMKOGCF9Dzt41PMj6fLDe7qZos=", + "dev": true + }, + "html-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-Br4ifmjQojUP4EmHnRBoUIYcZ9J7M4bTMcm7u6xoIAIuq2Nte4TzXX0533owvkQKQD1WeMTTTyD4Ni4QKxS0Bg==", + "dev": true, + "requires": { + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", + "util.promisify": "1.0.0" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", + "dev": true + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmmirror.com/json5/-/json5-0.5.1.tgz", + "integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + } + } + }, + "html-webpack-tags-plugin": { + "version": "2.0.17", + "resolved": "https://registry.npmmirror.com/html-webpack-tags-plugin/-/html-webpack-tags-plugin-2.0.17.tgz", + "integrity": "sha512-TKT8hnumMni6ztKfWZpP+UBeA7+aUn+qQQ4c9wT/p1IGTO/QWoIc19E+ZrxCcToDMjBO1NMYWkUbW4c4NtlGvg==", + "dev": true, + "requires": { + "glob": "^7.1.4", + "minimatch": "^3.0.4", + "slash": "^3.0.0" + } + }, + "htmlparser2": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz", + "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.0.0", + "domutils": "^2.5.2", + "entities": "^2.0.0" + } + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmmirror.com/http-deceiver/download/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.5.6", + "resolved": "https://registry.npmmirror.com/http-parser-js/-/http-parser-js-0.5.6.tgz", + "integrity": "sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmmirror.com/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmmirror.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "dev": true, + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/https-browserify/download/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", + "dev": true + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "human-signals": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/human-signals/-/human-signals-3.0.1.tgz", + "integrity": "sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "husky": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/husky/-/husky-8.0.1.tgz", + "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", + "dev": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmmirror.com/iconv-lite/download/iconv-lite-0.4.24.tgz?cache=0&sync_timestamp=1591605412872&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ficonv-lite%2Fdownload%2Ficonv-lite-0.4.24.tgz", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmmirror.com/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg==", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w==", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + } + } + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/download/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/indent-string/download/indent-string-4.0.0.tgz", + "integrity": "sha1-Yk+PRJfWGbLZdoUx1Y9BIoVNclE=", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/indexes-of/download/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/infer-owner/download/infer-owner-1.0.4.tgz", + "integrity": "sha1-xM78qo5RBRwqQLos6KPScpWvlGc=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmmirror.com/inflight/download/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/inherits/download/inherits-2.0.4.tgz", + "integrity": "sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=", + "dev": true + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmmirror.com/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "dev": true, + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "default-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + } + } + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/ip/download/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", + "dev": true + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/ip-regex/download/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", + "dev": true + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, + "is-absolute-url": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz", + "integrity": "sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "dev": true, + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/is-arrayish/download/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/is-buffer/download/is-buffer-1.1.6.tgz?cache=0&sync_timestamp=1588707106955&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fis-buffer%2Fdownload%2Fis-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "dev": true + }, + "is-callable": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/is-callable/-/is-callable-1.2.4.tgz", + "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==" + }, + "is-class-hotfix": { + "version": "0.0.6", + "resolved": "https://registry.npmmirror.com/is-class-hotfix/-/is-class-hotfix-0.0.6.tgz", + "integrity": "sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ==", + "dev": true + }, + "is-color-stop": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-color-stop/-/is-color-stop-1.1.0.tgz", + "integrity": "sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA==", + "dev": true, + "requires": { + "css-color-names": "^0.0.4", + "hex-color-regex": "^1.1.0", + "hsl-regex": "^1.0.0", + "hsla-regex": "^1.0.0", + "rgb-regex": "^1.0.1", + "rgba-regex": "^1.0.0" + } + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/download/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==" + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/is-extendable/download/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/download/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/download/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/is-glob/download/is-glob-4.0.1.tgz", + "integrity": "sha1-dWfb6fL14kZ7x3q4PEopSCQHpdw=", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "dev": true + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", + "dev": true + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "dev": true, + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "dev": true, + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true + }, + "is-resolvable": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-resolvable/-/is-resolvable-1.1.0.tgz", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", + "dev": true + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-type-of": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/is-type-of/-/is-type-of-1.2.1.tgz", + "integrity": "sha512-uK0kyX9LZYhSDS7H2sVJQJop1UnWPWmo5RvR3q2kFH6AUHYs7sOrVg0b4nyBHw29kRRNFofYN/JbHZDlHiItTA==", + "dev": true, + "requires": { + "core-util-is": "^1.0.2", + "is-class-hotfix": "~0.0.6", + "isstream": "~0.1.2" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-typedarray/download/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-wsl/download/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/isarray/download/isarray-1.0.0.tgz?cache=0&sync_timestamp=1562592096220&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fisarray%2Fdownload%2Fisarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/download/isexe-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fisexe%2Fdownload%2Fisexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/isobject/download/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", + "dev": true + }, + "jest-worker": { + "version": "28.1.1", + "resolved": "https://registry.npmmirror.com/jest-worker/-/jest-worker-28.1.1.tgz", + "integrity": "sha512-Au7slXB08C6h+xbJPp7VIb6U0XX5Kc9uel/WFc6/rcTzGiaVCBRngBExSYuXSLFPULPSYU3cJ3ybS988lNFQhQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-base64": { + "version": "3.7.2", + "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.2.tgz", + "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==" + }, + "js-message": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/download/js-tokens-4.0.0.tgz?cache=0&sync_timestamp=1586796260005&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fjs-tokens%2Fdownload%2Fjs-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmmirror.com/jsesc/download/jsesc-2.5.2.tgz", + "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/download/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/download/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==", + "dev": true + }, + "json5": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "jstoxml": { + "version": "2.2.9", + "resolved": "https://registry.npmmirror.com/jstoxml/-/jstoxml-2.2.9.tgz", + "integrity": "sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==", + "dev": true + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "known-css-properties": { + "version": "0.21.0", + "resolved": "https://registry.npmmirror.com/known-css-properties/-/known-css-properties-0.21.0.tgz", + "integrity": "sha512-sZLUnTqimCkvkgRS+kbPlYW5o8q5w1cu+uIisKpEWkj31I8mx8kNG162DwRav8Zirkva6N5uoFsm9kzK4mUXjw==", + "dev": true + }, + "ko-sleep": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/ko-sleep/-/ko-sleep-1.1.4.tgz", + "integrity": "sha512-s05WGpvvzyTuRlRE8fM7ru2Z3O+InbJuBcckTWKg2W+2c1k6SnFa3IfiSSt0/peFrlYAXgNoxuJWWVNmWh+K/A==", + "dev": true, + "requires": { + "ms": "*" + } + }, + "launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "dev": true, + "requires": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, + "launch-editor-middleware": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/launch-editor-middleware/-/launch-editor-middleware-2.6.0.tgz", + "integrity": "sha512-K2yxgljj5TdCeRN1lBtO3/J26+AIDDDw+04y6VAiZbWcTdBwsYN6RrZBnW5DN/QiSIdKNjKdATLUUluWWFYTIA==", + "dev": true, + "requires": { + "launch-editor": "^2.6.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "lilconfig": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-2.0.5.tgz", + "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "lint-staged": { + "version": "13.0.3", + "resolved": "https://registry.npmmirror.com/lint-staged/-/lint-staged-13.0.3.tgz", + "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "dev": true, + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.17", + "commander": "^9.3.0", + "debug": "^4.3.4", + "execa": "^6.1.0", + "lilconfig": "2.0.5", + "listr2": "^4.0.5", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.2", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.1.1" + }, + "dependencies": { + "commander": { + "version": "9.4.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", + "dev": true + }, + "execa": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/execa/-/execa-6.1.0.tgz", + "integrity": "sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^3.0.1", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + }, + "yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "dev": true + } + } + }, + "listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", + "dev": true + }, + "loader-utils": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.defaultsdeep": { + "version": "4.6.1", + "resolved": "https://registry.npmmirror.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz", + "integrity": "sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA==", + "dev": true + }, + "lodash.kebabcase": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", + "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", + "dev": true + }, + "lodash.mapvalues": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz", + "integrity": "sha512-JPFqXFeZQ7BfS00H58kClY7SPVeHertPE0lNuCyZ26/XlN8TvakYD7b9bGyNmXbT/D3BbtPAAmq90gPWqLkxlQ==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmmirror.com/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "loglevel": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/loglevel/-/loglevel-1.8.0.tgz", + "integrity": "sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==", + "dev": true + }, + "longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/download/lru-cache-5.1.1.tgz", + "integrity": "sha1-HaJ+ZxAnGUdpXa9oSOhH8B2EuSA=", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/map-cache/download/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/map-visit/download/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmmirror.com/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdast-util-from-markdown": { + "version": "0.8.5", + "resolved": "https://registry.npmmirror.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz", + "integrity": "sha512-2hkTXtYYnr+NubD/g6KGBS/0mFmBcifAsI0yIWRiRo0PjVs6SSOSOdtzbp6kSGnShDN6G5aWZpKQ2lWRy27mWQ==", + "dev": true, + "requires": { + "@types/mdast": "^3.0.0", + "mdast-util-to-string": "^2.0.0", + "micromark": "~2.11.0", + "parse-entities": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "mdast-util-to-markdown": { + "version": "0.6.5", + "resolved": "https://registry.npmmirror.com/mdast-util-to-markdown/-/mdast-util-to-markdown-0.6.5.tgz", + "integrity": "sha512-XeV9sDE7ZlOQvs45C9UKMtfTcctcaj/pGwH8YLbMHoMOXNNCn2LsqVQOqrF1+/NU8lKDAqozme9SCXWyo9oAcQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "longest-streak": "^2.0.0", + "mdast-util-to-string": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.0.0", + "zwitch": "^1.0.0" + } + }, + "mdast-util-to-string": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/mdast-util-to-string/-/mdast-util-to-string-2.0.0.tgz", + "integrity": "sha512-AW4DRS3QbBayY/jJmD8437V1Gombjf8RSOUCMFBuo5iHi58AGEgVCKQ+ezHkZZDpAQS75hcBMpLqjpJTjtUL7w==", + "dev": true + }, + "mdn-data": { + "version": "2.0.14", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.14.tgz", + "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/media-typer/download/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", + "dev": true + }, + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmmirror.com/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "memory-fs": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.2.0.tgz", + "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", + "dev": true + }, + "meow": { + "version": "9.0.0", + "resolved": "https://registry.npmmirror.com/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "dependencies": { + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/merge-descriptors/download/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", + "dev": true + }, + "merge-source-map": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/merge-source-map/-/merge-source-map-1.1.0.tgz", + "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/merge-stream/download/merge-stream-2.0.0.tgz", + "integrity": "sha1-UoI2KaFN0AyXcPtq1H3GMQ8sH2A=", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/merge2/download/merge2-1.4.1.tgz?cache=0&sync_timestamp=1591170027156&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fmerge2%2Fdownload%2Fmerge2-1.4.1.tgz", + "integrity": "sha1-Q2iJL4hekHRVpv19xVwMnUBJkK4=", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/methods/download/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, + "micromark": { + "version": "2.11.4", + "resolved": "https://registry.npmmirror.com/micromark/-/micromark-2.11.4.tgz", + "integrity": "sha512-+WoovN/ppKolQOFIAajxi7Lu9kInbPxFuTBVEavFcL8eAfVstoc5MocPmqBeAdBOJV00uaVjegzH4+MA0DN/uA==", + "dev": true, + "requires": { + "debug": "^4.0.0", + "parse-entities": "^2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "dev": true, + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmmirror.com/mime/download/mime-2.4.6.tgz?cache=0&sync_timestamp=1590596706367&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fmime%2Fdownload%2Fmime-2.4.6.tgz", + "integrity": "sha1-5bQHyQ20QvK+tbFiNz0Htpr/pNE=", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "0.9.0", + "resolved": "https://registry.npmmirror.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz", + "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "normalize-url": "1.9.1", + "schema-utils": "^1.0.0", + "webpack-sources": "^1.1.0" + }, + "dependencies": { + "normalize-url": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-1.9.1.tgz", + "integrity": "sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==", + "dev": true, + "requires": { + "object-assign": "^4.0.1", + "prepend-http": "^1.0.0", + "query-string": "^4.1.0", + "sort-keys": "^1.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/minimalistic-crypto-utils/download/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + } + } + }, + "minipass": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/minipass/download/minipass-3.1.3.tgz", + "integrity": "sha1-fUL/HzljVILhX5zbUxhN7r1YFf0=", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/download/yallist-4.0.0.tgz", + "integrity": "sha1-m7knkNnA7/7GO+c1GeEaNQGaOnI=", + "dev": true + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "dev": true, + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mitt": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.0.tgz", + "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==" + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmmirror.com/mkdirp/download/mkdirp-0.5.5.tgz?cache=0&sync_timestamp=1587535418745&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fmkdirp%2Fdownload%2Fmkdirp-0.5.5.tgz", + "integrity": "sha1-2Rzv1i0UNsoPQWIOJRKI1CAJne8=", + "requires": { + "minimist": "^1.2.5" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "dev": true, + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/download/ms-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "muggle-string": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/muggle-string/-/muggle-string-0.1.0.tgz", + "integrity": "sha512-Tr1knR3d2mKvvWthlk7202rywKbiOm4rVFLsfAaSIhJ6dt9o47W4S+JMtWhd/PW9Wrdew2/S2fSvhz3E2gkfEg==", + "dev": true + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmmirror.com/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "dev": true, + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/multicast-dns-service-types/download/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", + "dev": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "mz-modules": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mz-modules/-/mz-modules-2.1.0.tgz", + "integrity": "sha512-sjk8lcRW3vrVYnZ+W+67L/2rL+jbO5K/N6PFGIcLWTiYytNr22Ah9FDXFs+AQntTM1boZcoHi5qS+CV1seuPog==", + "dev": true, + "requires": { + "glob": "^7.1.2", + "ko-sleep": "^1.0.3", + "mkdirp": "^0.5.1", + "pump": "^3.0.0", + "rimraf": "^2.6.1" + } + }, + "nan": { + "version": "2.17.0", + "resolved": "https://registry.npmmirror.com/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "dev": true, + "optional": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/download/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmmirror.com/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "dev": true, + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmmirror.com/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "dev": true, + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/punycode/download/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + } + } + }, + "node-releases": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.5.tgz", + "integrity": "sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==", + "dev": true + }, + "normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/normalize-path/download/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/normalize-range/download/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", + "dev": true + }, + "normalize-url": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/normalize-url/-/normalize-url-3.3.0.tgz", + "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/num2fraction/download/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmmirror.com/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/download/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/object-copy/download/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==" + }, + "object-is": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/object-is/-/object-is-1.1.5.tgz", + "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/object-visit/download/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.4", + "resolved": "https://registry.npmmirror.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz", + "integrity": "sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ==", + "requires": { + "array.prototype.reduce": "^1.0.4", + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/object.pick/download/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmmirror.com/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/once/download/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmmirror.com/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + } + }, + "ora": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/ora/-/ora-3.4.0.tgz", + "integrity": "sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-spinners": "^2.0.0", + "log-symbols": "^2.2.0", + "strip-ansi": "^5.2.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/os-browserify/download/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", + "dev": true + }, + "os-name": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/os-name/-/os-name-1.0.3.tgz", + "integrity": "sha1-GzefZINa98Wn9JizV8uVIVwVnt8=", + "dev": true, + "requires": { + "osx-release": "^1.0.0", + "win-release": "^1.0.0" + } + }, + "osx-release": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/osx-release/-/osx-release-1.1.0.tgz", + "integrity": "sha1-8heRGigTaUmvG/kwiyQeJzfTzWw=", + "dev": true, + "requires": { + "minimist": "^1.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/p-finally/download/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "dev": true, + "requires": { + "retry": "^0.12.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pac-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz", + "integrity": "sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4", + "get-uri": "3", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "5", + "pac-resolver": "^5.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "5" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "pac-resolver": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/pac-resolver/-/pac-resolver-5.0.1.tgz", + "integrity": "sha512-cy7u00ko2KVgBAjuhevqpPeHIkCIqPe1v24cydhWjmeuzaBfmUWFCZJ1iAh5TuVzVZoUzXIW7K8sMYOZ84uZ9Q==", + "dev": true, + "requires": { + "degenerator": "^3.0.2", + "ip": "^1.1.5", + "netmask": "^2.0.2" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "dev": true, + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==", + "dev": true, + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/parent-module/download/parent-module-1.0.1.tgz", + "integrity": "sha1-aR0nCeeMefrjoVZiJFLQB2LKqqI=", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/callsites/download/callsites-3.1.0.tgz", + "integrity": "sha1-s2MKvYlDQy9Us/BRkjjjPNffL3M=", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmmirror.com/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "dev": true, + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "dev": true + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/pascalcase/download/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmmirror.com/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/path-dirname/download/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/path-is-absolute/download/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/path-is-inside/download/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmmirror.com/path-to-regexp/download/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pause-stream": { + "version": "0.0.11", + "resolved": "https://registry.npmmirror.com/pause-stream/-/pause-stream-0.0.11.tgz", + "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=", + "dev": true, + "requires": { + "through": "~2.3" + } + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dev": true, + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pinia": { + "version": "2.0.34", + "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.0.34.tgz", + "integrity": "sha512-cgOoGUiyqX0SSgX8XelK9+Ri4XA2/YyNtgjogwfzIx1g7iZTaZPxm7/bZYMCLU2qHRiHhxG7SuQO0eBacFNc2Q==", + "requires": { + "@vue/devtools-api": "^6.5.0", + "vue-demi": "*" + } + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/pinkie/download/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/pinkie-promise/download/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "platform": { + "version": "1.3.6", + "resolved": "https://registry.npmmirror.com/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true + }, + "pnp-webpack-plugin": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.7.0.tgz", + "integrity": "sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==", + "dev": true, + "requires": { + "ts-pnp": "^1.1.6" + } + }, + "portfinder": { + "version": "1.0.28", + "resolved": "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.28.tgz", + "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==", + "dev": true, + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/posix-character-classes/download/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-calc": { + "version": "7.0.5", + "resolved": "https://registry.npmmirror.com/postcss-calc/-/postcss-calc-7.0.5.tgz", + "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==", + "dev": true, + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-colormin": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz", + "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "color": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-convert-values": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz", + "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-discard-comments": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz", + "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-discard-duplicates": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz", + "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-discard-empty": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz", + "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-discard-overridden": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz", + "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-html": { + "version": "0.36.0", + "resolved": "https://registry.npmmirror.com/postcss-html/-/postcss-html-0.36.0.tgz", + "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", + "dev": true, + "requires": { + "htmlparser2": "^3.10.0" + }, + "dependencies": { + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "postcss-less": { + "version": "3.1.4", + "resolved": "https://registry.npmmirror.com/postcss-less/-/postcss-less-3.1.4.tgz", + "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-load-config": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz", + "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true + } + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "dev": true, + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmmirror.com/postcss-media-query-parser/download/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "postcss-merge-longhand": { + "version": "4.0.11", + "resolved": "https://registry.npmmirror.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz", + "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==", + "dev": true, + "requires": { + "css-color-names": "0.0.4", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "stylehacks": "^4.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-merge-rules": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz", + "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "cssnano-util-same-parent": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0", + "vendors": "^1.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-minify-font-values": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz", + "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-minify-gradients": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz", + "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "is-color-stop": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-minify-params": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz", + "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "browserslist": "^4.0.0", + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-minify-selectors": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz", + "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "dev": true, + "requires": { + "postcss": "^7.0.5" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz", + "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==", + "dev": true, + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.32", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "dev": true, + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "dev": true, + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-charset": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz", + "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==", + "dev": true, + "requires": { + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-display-values": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz", + "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-positions": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz", + "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-repeat-style": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz", + "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-string": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz", + "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==", + "dev": true, + "requires": { + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-timing-functions": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz", + "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-unicode": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz", + "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-url": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz", + "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==", + "dev": true, + "requires": { + "is-absolute-url": "^2.0.0", + "normalize-url": "^3.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-normalize-whitespace": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz", + "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-ordered-values": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz", + "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==", + "dev": true, + "requires": { + "cssnano-util-get-arguments": "^4.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-reduce-initial": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz", + "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "caniuse-api": "^3.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-reduce-transforms": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz", + "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==", + "dev": true, + "requires": { + "cssnano-util-get-match": "^4.0.0", + "has": "^1.0.0", + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/postcss-resolve-nested-selector/download/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "dev": true, + "requires": { + "postcss": "^7.0.26" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-sass": { + "version": "0.4.4", + "resolved": "https://registry.npmmirror.com/postcss-sass/-/postcss-sass-0.4.4.tgz", + "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", + "dev": true, + "requires": { + "gonzales-pe": "^4.3.0", + "postcss": "^7.0.21" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "requires": { + "postcss": "^7.0.6" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-selector-parser": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/download/postcss-selector-parser-6.0.2.tgz?cache=0&sync_timestamp=1582039646348&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fpostcss-selector-parser%2Fdownload%2Fpostcss-selector-parser-6.0.2.tgz", + "integrity": "sha1-k0z3mdAWyDQRhZ4J3Oyt4BKG7Fw=", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-sorting": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/postcss-sorting/-/postcss-sorting-7.0.1.tgz", + "integrity": "sha512-iLBFYz6VRYyLJEJsBJ8M3TCqNcckVzz4wFounSc5Oez35ogE/X+aoC5fFu103Ot7NyvjU3/xqIXn93Gp3kJk4g==", + "dev": true + }, + "postcss-svgo": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz", + "integrity": "sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw==", + "dev": true, + "requires": { + "postcss": "^7.0.0", + "postcss-value-parser": "^3.0.0", + "svgo": "^1.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "css-select": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/css-select/-/css-select-2.1.0.tgz", + "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^3.2.1", + "domutils": "^1.7.0", + "nth-check": "^1.0.2" + } + }, + "css-tree": { + "version": "1.0.0-alpha.37", + "resolved": "https://registry.npmmirror.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz", + "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==", + "dev": true, + "requires": { + "mdn-data": "2.0.4", + "source-map": "^0.6.1" + } + }, + "css-what": { + "version": "3.4.2", + "resolved": "https://registry.npmmirror.com/css-what/-/css-what-3.4.2.tgz", + "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==", + "dev": true + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + }, + "dependencies": { + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "mdn-data": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/mdn-data/-/mdn-data-2.0.4.tgz", + "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==", + "dev": true + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "dev": true, + "requires": { + "boolbase": "~1.0.0" + } + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "svgo": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-1.3.2.tgz", + "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "coa": "^2.0.2", + "css-select": "^2.0.0", + "css-select-base-adapter": "^0.1.1", + "css-tree": "1.0.0-alpha.37", + "csso": "^4.0.2", + "js-yaml": "^3.13.1", + "mkdirp": "~0.5.1", + "object.values": "^1.1.0", + "sax": "~1.2.4", + "stable": "^0.1.8", + "unquote": "~1.1.1", + "util.promisify": "~1.0.0" + } + } + } + }, + "postcss-syntax": { + "version": "0.36.2", + "resolved": "https://registry.npmmirror.com/postcss-syntax/-/postcss-syntax-0.36.2.tgz", + "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", + "dev": true + }, + "postcss-unique-selectors": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz", + "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==", + "dev": true, + "requires": { + "alphanum-sort": "^1.0.0", + "postcss": "^7.0.0", + "uniqs": "^2.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/postcss-value-parser/download/postcss-value-parser-4.1.0.tgz", + "integrity": "sha1-RD9qIM7WSBor2k+oUypuVdeJoss=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", + "dev": true + }, + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "pretty-error": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/pretty-error/-/pretty-error-2.1.2.tgz", + "integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==", + "dev": true, + "requires": { + "lodash": "^4.17.20", + "renderkid": "^2.0.4" + } + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmmirror.com/process/download/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/process-nextick-args/download/process-nextick-args-2.0.1.tgz", + "integrity": "sha1-eCDZsWEgzFXKmud5JoCufbptf+I=", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/promise-inflight/download/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, + "proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/proxy-agent/-/proxy-agent-5.0.0.tgz", + "integrity": "sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g==", + "dev": true, + "requires": { + "agent-base": "^6.0.0", + "debug": "4", + "http-proxy-agent": "^4.0.0", + "https-proxy-agent": "^5.0.0", + "lru-cache": "^5.1.1", + "pac-proxy-agent": "^5.0.0", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^5.0.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/prr/download/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmmirror.com/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "dev": true, + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmmirror.com/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "dev": true + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pump/download/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "dev": true, + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/punycode/download/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=" + }, + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "query-string": { + "version": "4.3.4", + "resolved": "https://registry.npmmirror.com/query-string/-/query-string-4.3.4.tgz", + "integrity": "sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==", + "dev": true, + "requires": { + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/querystring/download/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", + "dev": true + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/querystring-es3/download/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmmirror.com/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/randombytes/download/randombytes-2.1.0.tgz", + "integrity": "sha1-32+ENy8CcNxlzfYpE0mrekc9Tyo=", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "dev": true, + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/read-pkg/download/read-pkg-5.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fread-pkg%2Fdownload%2Fread-pkg-5.2.0.tgz", + "integrity": "sha1-e/KVQ4yloz5WzTDgU7NO5yUMk8w=", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + } + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmmirror.com/readable-stream/download/readable-stream-2.3.7.tgz?cache=0&sync_timestamp=1581624324274&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Freadable-stream%2Fdownload%2Freadable-stream-2.3.7.tgz", + "integrity": "sha1-Hsoc9xGu+BTAT2IlKjamL2yyO1c=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmmirror.com/regenerator-runtime/download/regenerator-runtime-0.13.5.tgz?cache=0&sync_timestamp=1584052481783&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fregenerator-runtime%2Fdownload%2Fregenerator-runtime-0.13.5.tgz", + "integrity": "sha1-2Hih0JS0MG0QuQlkhLM+vVXiZpc=", + "dev": true + }, + "regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmmirror.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "regexpu-core": { + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-5.2.2.tgz", + "integrity": "sha512-T0+1Zp2wjF/juXMrMxHxidqGYn8U4R+zleSJhX9tQ1PUsS8a9UtYfbsF9LdiVgNX3kiX8RNaKM42nfSgvFJjmw==", + "dev": true, + "requires": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsgen": "^0.7.1", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "regjsgen": { + "version": "0.7.1", + "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.7.1.tgz", + "integrity": "sha512-RAt+8H2ZEzHeYWxZ3H2z6tF18zyyOnlcdaafLrm21Bguj7uZy6ULibiAFdXEtKQY4Sy7wDTwDiOazasMLc4KPA==", + "dev": true + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmmirror.com/relateurl/download/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=", + "dev": true + }, + "remark": { + "version": "13.0.0", + "resolved": "https://registry.npmmirror.com/remark/-/remark-13.0.0.tgz", + "integrity": "sha512-HDz1+IKGtOyWN+QgBiAT0kn+2s6ovOxHyPAFGKVE81VSzJ+mq7RwHFledEvB5F1p4iJvOah/LOKdFuzvRnNLCA==", + "dev": true, + "requires": { + "remark-parse": "^9.0.0", + "remark-stringify": "^9.0.0", + "unified": "^9.1.0" + } + }, + "remark-parse": { + "version": "9.0.0", + "resolved": "https://registry.npmmirror.com/remark-parse/-/remark-parse-9.0.0.tgz", + "integrity": "sha512-geKatMwSzEXKHuzBNU1z676sGcDcFoChMK38TgdHJNAYfFtsfHDQG7MoJAjs6sgYMqyLduCYWDIWZIxiPeafEw==", + "dev": true, + "requires": { + "mdast-util-from-markdown": "^0.8.0" + } + }, + "remark-stringify": { + "version": "9.0.1", + "resolved": "https://registry.npmmirror.com/remark-stringify/-/remark-stringify-9.0.1.tgz", + "integrity": "sha512-mWmNg3ZtESvZS8fv5PTvaPckdL4iNlCHTt8/e/8oN08nArHRHjNZMKzA/YW3+p7/lYqIw4nx1XsjCBo/AxNChg==", + "dev": true, + "requires": { + "mdast-util-to-markdown": "^0.6.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "renderkid": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/renderkid/-/renderkid-2.0.7.tgz", + "integrity": "sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==", + "dev": true, + "requires": { + "css-select": "^4.1.3", + "dom-converter": "^0.2.0", + "htmlparser2": "^6.1.0", + "lodash": "^4.17.21", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "repeat-element": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmmirror.com/repeat-string/download/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmmirror.com/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmmirror.com/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/require-directory/download/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/requires-port/download/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmmirror.com/resolve/download/resolve-1.17.0.tgz", + "integrity": "sha1-sllBtUloIxzC0bt2p5y38sC/hEQ=", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/resolve-url/download/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "dependencies": { + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + } + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmmirror.com/retry/download/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rgb-regex": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/rgb-regex/-/rgb-regex-1.0.1.tgz", + "integrity": "sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w==", + "dev": true + }, + "rgba-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/rgba-regex/-/rgba-regex-1.0.0.tgz", + "integrity": "sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/rimraf/download/rimraf-2.7.1.tgz?cache=0&sync_timestamp=1581229865753&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Frimraf%2Fdownload%2Frimraf-2.7.1.tgz", + "integrity": "sha1-NXl/E6f9rcVmFCwp1PB8ytSD4+w=", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dev": true, + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "dev": true, + "requires": { + "aproba": "^1.1.1" + } + }, + "rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmmirror.com/safe-buffer/download/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex/download/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "dependencies": { + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + } + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/safer-buffer/download/safer-buffer-2.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fsafer-buffer%2Fdownload%2Fsafer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "dev": true + }, + "sass": { + "version": "1.52.2", + "resolved": "https://registry.npmmirror.com/sass/-/sass-1.52.2.tgz", + "integrity": "sha512-mfHB2VSeFS7sZlPv9YohB9GB7yWIgQNTGniQwfQ04EoQN0wsQEv7SwpCwy/x48Af+Z3vDeFXz+iuXM3HK/phZQ==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "10.2.1", + "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-10.2.1.tgz", + "integrity": "sha512-RRvWl+3K2LSMezIsd008ErK4rk6CulIMSwrcc2aZvjymUgKo/vjXGp1rSWmfTUX7bblEOz8tst4wBwWtCGBqKA==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "loader-utils": "^2.0.0", + "neo-async": "^2.6.2", + "schema-utils": "^3.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "sass-resources-loader": { + "version": "2.2.5", + "resolved": "https://registry.npmmirror.com/sass-resources-loader/-/sass-resources-loader-2.2.5.tgz", + "integrity": "sha512-po8rfETH9cOQACWxubT/1CCu77KjxwRtCDm6QAXZH99aUHBydwSoxdIjC40SGp/dcS/FkSNJl0j1VEojGZqlvQ==", + "dev": true, + "requires": { + "async": "^3.2.3", + "chalk": "^4.1.0", + "glob": "^7.1.6", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "async": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + } + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + } + }, + "sdk-base": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/sdk-base/-/sdk-base-2.0.1.tgz", + "integrity": "sha1-ukAonovfJy7RHdnql+r5jgNtJMY=", + "dev": true, + "requires": { + "get-ready": "~1.0.0" + } + }, + "select": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/select/download/select-1.1.2.tgz", + "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/select-hose/download/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", + "dev": true + }, + "selfsigned": { + "version": "1.10.14", + "resolved": "https://registry.npmmirror.com/selfsigned/-/selfsigned-1.10.14.tgz", + "integrity": "sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA==", + "dev": true, + "requires": { + "node-forge": "^0.10.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmmirror.com/semver/download/semver-5.7.1.tgz", + "integrity": "sha1-qVT5Ma66UI0we78Gnv8MAclhFvc=" + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmmirror.com/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ms/download/ms-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fms%2Fdownload%2Fms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/serve-index/download/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmmirror.com/http-errors/download/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmmirror.com/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/set-blocking/download/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/setimmediate/download/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmmirror.com/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shell-quote": { + "version": "1.7.4", + "resolved": "https://registry.npmmirror.com/shell-quote/-/shell-quote-1.7.4.tgz", + "integrity": "sha512-8o/QEhSSRb1a5i7TFR0iM4G16Z0vYB2OQVs4G3aAFXjn3T6yEx8AZxy1PgDF7I00LZHYA3WxaSYIf5e5sAX8Rw==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmmirror.com/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "dev": true, + "requires": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmmirror.com/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/download/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "sockjs-client": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/sockjs-client/-/sockjs-client-1.5.1.tgz", + "integrity": "sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==", + "dev": true, + "requires": { + "debug": "^3.2.6", + "eventsource": "^1.0.7", + "faye-websocket": "^0.11.3", + "inherits": "^2.0.4", + "json3": "^3.3.3", + "url-parse": "^1.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "socks": { + "version": "2.7.1", + "resolved": "https://registry.npmmirror.com/socks/-/socks-2.7.1.tgz", + "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", + "dev": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "dependencies": { + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + } + } + }, + "socks-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz", + "integrity": "sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "4", + "socks": "^2.3.3" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "sort-keys": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/sort-keys/-/sort-keys-1.1.2.tgz", + "integrity": "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + }, + "dependencies": { + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/source-list-map/download/source-list-map-2.0.1.tgz", + "integrity": "sha1-OZO9hzv8SEecyp6jpUeDXHwVSzQ=", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmmirror.com/source-map/download/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmmirror.com/source-map-resolve/download/source-map-resolve-0.5.3.tgz?cache=0&sync_timestamp=1584829515586&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fsource-map-resolve%2Fdownload%2Fsource-map-resolve-0.5.3.tgz", + "integrity": "sha1-GQhmvs51U+H48mei7oLGBrVQmho=", + "dev": true, + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/source-map-url/download/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.11", + "resolved": "https://registry.npmmirror.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz", + "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true + }, + "speed-measure-webpack-plugin": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.5.0.tgz", + "integrity": "sha512-Re0wX5CtM6gW7bZA64ONOfEPEhwbiSF/vz6e2GvadjuaPrQcHTQdRGsD8+BE7iUOysXH8tIenkPCQBEcspXsNg==", + "dev": true, + "requires": { + "chalk": "^4.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmmirror.com/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==" + }, + "stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmmirror.com/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/static-extend/download/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmmirror.com/define-property/download/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/statuses/download/statuses-1.5.0.tgz?cache=0&sync_timestamp=1587327902535&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fstatuses%2Fdownload%2Fstatuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=", + "dev": true + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "dev": true, + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmmirror.com/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "dev": true, + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", + "dev": true + }, + "stream-wormhole": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/stream-wormhole/-/stream-wormhole-1.1.0.tgz", + "integrity": "sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==", + "dev": true + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==", + "dev": true + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/string_decoder/download/string_decoder-1.1.1.tgz?cache=0&sync_timestamp=1565170823020&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fstring_decoder%2Fdownload%2Fstring_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/download/strip-ansi-6.0.0.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-6.0.0.tgz", + "integrity": "sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/strip-eof/download/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/strip-final-newline/download/strip-final-newline-2.0.0.tgz", + "integrity": "sha1-ibhS+y/L6Tb29LMYevsKEsGrWK0=", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "style-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/style-loader/-/style-loader-2.0.0.tgz", + "integrity": "sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + } + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/style-search/download/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, + "stylehacks": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/stylehacks/-/stylehacks-4.0.3.tgz", + "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==", + "dev": true, + "requires": { + "browserslist": "^4.0.0", + "postcss": "^7.0.0", + "postcss-selector-parser": "^3.0.0" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz", + "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "stylelint": { + "version": "13.13.1", + "resolved": "https://registry.npmmirror.com/stylelint/-/stylelint-13.13.1.tgz", + "integrity": "sha512-Mv+BQr5XTUrKqAXmpqm6Ddli6Ief+AiPZkRsIrAoUKFuq/ElkUh9ZMYxXD0iQNZ5ADghZKLOWz1h7hTClB7zgQ==", + "dev": true, + "requires": { + "@stylelint/postcss-css-in-js": "^0.37.2", + "@stylelint/postcss-markdown": "^0.36.2", + "autoprefixer": "^9.8.6", + "balanced-match": "^2.0.0", + "chalk": "^4.1.1", + "cosmiconfig": "^7.0.0", + "debug": "^4.3.1", + "execall": "^2.0.0", + "fast-glob": "^3.2.5", + "fastest-levenshtein": "^1.0.12", + "file-entry-cache": "^6.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.0.3", + "globjoin": "^0.1.4", + "html-tags": "^3.1.0", + "ignore": "^5.1.8", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.21.0", + "lodash": "^4.17.21", + "log-symbols": "^4.1.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.4", + "normalize-selector": "^0.2.0", + "postcss": "^7.0.35", + "postcss-html": "^0.36.0", + "postcss-less": "^3.1.4", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^4.0.2", + "postcss-sass": "^0.4.4", + "postcss-scss": "^2.1.1", + "postcss-selector-parser": "^6.0.5", + "postcss-syntax": "^0.36.2", + "postcss-value-parser": "^4.1.0", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.2", + "strip-ansi": "^6.0.0", + "style-search": "^0.1.0", + "sugarss": "^2.0.0", + "svg-tags": "^1.0.0", + "table": "^6.6.0", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "autoprefixer": { + "version": "9.8.8", + "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-9.8.8.tgz", + "integrity": "sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001109", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "picocolors": "^0.2.1", + "postcss": "^7.0.32", + "postcss-value-parser": "^4.1.0" + } + }, + "balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-tags": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==" + } + } + }, + "stylelint-config-prettier": { + "version": "9.0.3", + "resolved": "https://registry.npmmirror.com/stylelint-config-prettier/-/stylelint-config-prettier-9.0.3.tgz", + "integrity": "sha512-5n9gUDp/n5tTMCq1GLqSpA30w2sqWITSSEiAWQlpxkKGAUbjcemQ0nbkRvRUa0B1LgD3+hCvdL7B1eTxy1QHJg==", + "dev": true + }, + "stylelint-config-recess-order": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/stylelint-config-recess-order/-/stylelint-config-recess-order-3.0.0.tgz", + "integrity": "sha512-uNXrlDz570Q7HJlrq8mNjgfO/xlKIh2hKVKEFMTG1/ih/6tDLcTbuvO1Zoo2dnQay990OAkWLDpTDOorB+hmBw==", + "dev": true, + "requires": { + "stylelint-order": "5.x" + } + }, + "stylelint-config-recommended": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/stylelint-config-recommended/-/stylelint-config-recommended-5.0.0.tgz", + "integrity": "sha512-c8aubuARSu5A3vEHLBeOSJt1udOdS+1iue7BmJDTSXoCBmfEQmmWX+59vYIj3NQdJBY6a/QRv1ozVFpaB9jaqA==", + "dev": true + }, + "stylelint-config-recommended-scss": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/stylelint-config-recommended-scss/-/stylelint-config-recommended-scss-4.3.0.tgz", + "integrity": "sha512-/noGjXlO8pJTr/Z3qGMoaRFK8n1BFfOqmAbX1RjTIcl4Yalr+LUb1zb9iQ7pRx1GsEBXOAm4g2z5/jou/pfMPg==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^5.0.0" + } + }, + "stylelint-order": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/stylelint-order/-/stylelint-order-5.0.0.tgz", + "integrity": "sha512-OWQ7pmicXufDw5BlRqzdz3fkGKJPgLyDwD1rFY3AIEfIH/LQY38Vu/85v8/up0I+VPiuGRwbc2Hg3zLAsJaiyw==", + "dev": true, + "requires": { + "postcss": "^8.3.11", + "postcss-sorting": "^7.0.1" + } + }, + "stylelint-scss": { + "version": "3.21.0", + "resolved": "https://registry.npmmirror.com/stylelint-scss/-/stylelint-scss-3.21.0.tgz", + "integrity": "sha512-CMI2wSHL+XVlNExpauy/+DbUcB/oUZLARDtMIXkpV/5yd8nthzylYd1cdHeDMJVBXeYHldsnebUX6MoV5zPW4A==", + "dev": true, + "requires": { + "lodash": "^4.17.15", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "stylelint-webpack-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/stylelint-webpack-plugin/-/stylelint-webpack-plugin-2.4.0.tgz", + "integrity": "sha512-MhXDqd8HPXdY51nGeDeUEXToximoIbc0Z5TQC1M0ApR0ejrOwj9dRZKiL/00MDRrQfuAGkjcJ6sOVvc4gRzbgQ==", + "dev": true, + "requires": { + "arrify": "^2.0.1", + "globby": "^11.0.4", + "jest-worker": "^28.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^3.1.1" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + }, + "dependencies": { + "picocolors": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-0.2.1.tgz", + "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==", + "dev": true + }, + "postcss": { + "version": "7.0.39", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-7.0.39.tgz", + "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==", + "dev": true, + "requires": { + "picocolors": "^0.2.1", + "source-map": "^0.6.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmmirror.com/supports-color/download/supports-color-5.5.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fsupports-color%2Fdownload%2Fsupports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/svg-tags/download/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "svgaplayerweb": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/svgaplayerweb/download/svgaplayerweb-2.3.1.tgz", + "integrity": "sha1-Au3BavFiCvn1dKU254E/wlE55Hs=" + }, + "svgo": { + "version": "2.8.0", + "resolved": "https://registry.npmmirror.com/svgo/-/svgo-2.8.0.tgz", + "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "dev": true, + "requires": { + "@trysound/sax": "0.2.0", + "commander": "^7.2.0", + "css-select": "^4.1.3", + "css-tree": "^1.1.3", + "csso": "^4.2.0", + "picocolors": "^1.0.0", + "stable": "^0.1.8" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "table": { + "version": "6.8.0", + "resolved": "https://registry.npmmirror.com/table/-/table-6.8.0.tgz", + "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "tapable": { + "version": "0.1.10", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-0.1.10.tgz", + "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", + "dev": true + }, + "terser": { + "version": "4.8.1", + "resolved": "https://registry.npmmirror.com/terser/-/terser-4.8.1.tgz", + "integrity": "sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.5", + "resolved": "https://registry.npmmirror.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", + "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==", + "dev": true, + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^4.0.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/text-table/download/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "thread-loader": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/thread-loader/-/thread-loader-2.1.3.tgz", + "integrity": "sha512-wNrVKH2Lcf8ZrWxDF/khdlLlsTMczdcwPA9VEK4c2exlEPynYWxi9op3nPTo5lAnDIkE0rQEB3VBP+4Zncc9Hg==", + "dev": true, + "requires": { + "loader-runner": "^2.3.1", + "loader-utils": "^1.1.0", + "neo-async": "^2.6.0" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "timers-browserify": { + "version": "2.0.12", + "resolved": "https://registry.npmmirror.com/timers-browserify/-/timers-browserify-2.0.12.tgz", + "integrity": "sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==", + "dev": true, + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true + }, + "tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==" + }, + "tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/to-fast-properties/download/to-fast-properties-2.0.0.tgz?cache=0&sync_timestamp=1580550317222&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fto-fast-properties%2Fdownload%2Fto-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/to-object-path/download/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/download/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true + }, + "toposort": { + "version": "1.0.7", + "resolved": "https://registry.npmmirror.com/toposort/-/toposort-1.0.7.tgz", + "integrity": "sha512-FclLrw8b9bMWf4QlCJuHBEVhSRsqDj6u3nIjAzPeJvgl//1hBlffdlk0MALceL14+koWEdU4ofRAXofbODxQzg==", + "dev": true + }, + "totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "dev": true + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", + "dev": true + }, + "tryer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/tryer/-/tryer-1.0.1.tgz", + "integrity": "sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==", + "dev": true + }, + "ts-loader": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/ts-loader/-/ts-loader-8.4.0.tgz", + "integrity": "sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^2.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "dependencies": { + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + } + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "ts-pnp": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/ts-pnp/-/ts-pnp-1.2.0.tgz", + "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==", + "dev": true + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmmirror.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmmirror.com/tty-browserify/download/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", + "dev": true + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmmirror.com/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmmirror.com/type-fest/download/type-fest-0.6.0.tgz?cache=0&sync_timestamp=1591686750124&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Ftype-fest%2Fdownload%2Ftype-fest-0.6.0.tgz", + "integrity": "sha1-jSojcNPfiG61yQraHFv2GIrPg4s=", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmmirror.com/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dev": true, + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmmirror.com/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmmirror.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "4.7.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.7.3.tgz", + "integrity": "sha512-WOkT3XYvrpXx4vMMqlD+8R8R37fZkjyLGlxavMc4iB8lrl8L0DeTcHbYgw/v0N/z9wAFsgBhcsF0ruoySS22mA==", + "dev": true + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmmirror.com/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "dev": true, + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "unescape": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/unescape/-/unescape-1.0.1.tgz", + "integrity": "sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true + }, + "unified": { + "version": "9.2.2", + "resolved": "https://registry.npmmirror.com/unified/-/unified-9.2.2.tgz", + "integrity": "sha512-Sg7j110mtefBD+qunSLO1lqOEKdrwBFBrR6Qd8f4uwkhWNlbkaqwHse6e7QvD3AP/MNoJdEDLaf8OxYyoWgorQ==", + "dev": true, + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + } + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "uniq": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/uniq/download/uniq-1.0.1.tgz", + "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", + "dev": true + }, + "uniqs": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/uniqs/-/uniqs-2.0.0.tgz", + "integrity": "sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/unique-filename/download/unique-filename-1.1.1.tgz", + "integrity": "sha1-HWl2k2mtoFgxA6HmrodoG1ZXMjA=", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/unique-slug/download/unique-slug-2.0.2.tgz", + "integrity": "sha1-uqvOkQg/xk6UWw861hPiZPfNTmw=", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unist-util-find-all-after": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/unist-util-find-all-after/-/unist-util-find-all-after-3.0.2.tgz", + "integrity": "sha512-xaTC/AGZ0rIM2gM28YVRAFPIZpzbpDtU3dRmp7EXlNVA8ziQc4hY3H7BHXM1J49nEmiqc3svnqMReW+PGqbZKQ==", + "dev": true, + "requires": { + "unist-util-is": "^4.0.0" + } + }, + "unist-util-is": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-4.1.0.tgz", + "integrity": "sha512-ZOQSsnce92GrxSqlnEEseX0gi7GH9zTJZ0p9dtu87WRb/37mMPO2Ilx1s/t9vBHrFhbgweUwb+t7cIn5dxPhZg==", + "dev": true + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.2" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unpipe/download/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=", + "dev": true + }, + "unquote": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/unquote/-/unquote-1.1.1.tgz", + "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/unset-value/download/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmmirror.com/has-value/download/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/isobject/download/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/has-values/download/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.10", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", + "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/uri-js/download/uri-js-4.2.2.tgz", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/urix/download/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmmirror.com/url/download/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "dev": true, + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmmirror.com/punycode/download/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", + "dev": true + } + } + }, + "url-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/url-loader/-/url-loader-2.3.0.tgz", + "integrity": "sha512-goSdg8VY+7nPZKUEChZSEtW5gjbS66USIGCeSJ1OVOJ7Yfuh/36YxCwMi5HVEJh6mqUYOoy3NJ0vlOMrWsSHog==", + "dev": true, + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmmirror.com/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-search-params-polyfill": { + "version": "8.1.1", + "resolved": "https://registry.npmmirror.com/url-search-params-polyfill/-/url-search-params-polyfill-8.1.1.tgz", + "integrity": "sha512-KmkCs6SjE6t4ihrfW9JelAPQIIIFbJweaaSLTh/4AO+c58JlDcb+GbdPt8yr5lRcFg4rPswRFRRhBGpWwh0K/Q==" + }, + "urllib": { + "version": "2.39.1", + "resolved": "https://registry.npmmirror.com/urllib/-/urllib-2.39.1.tgz", + "integrity": "sha512-c3sLtY8uhc/WoyJt/nNcEwO4fFC9sFYMQmU5NKoUz9OqUYrPSbYFPflocZCA5oCTavky9weK+YA2EHjsva9AwQ==", + "dev": true, + "requires": { + "any-promise": "^1.3.0", + "content-type": "^1.0.2", + "debug": "^2.6.9", + "default-user-agent": "^1.0.0", + "digest-header": "^0.0.1", + "ee-first": "~1.1.1", + "formstream": "^1.1.0", + "humanize-ms": "^1.2.0", + "iconv-lite": "^0.4.15", + "ip": "^1.1.5", + "proxy-agent": "^5.0.0", + "pump": "^3.0.0", + "qs": "^6.4.0", + "statuses": "^1.3.1", + "utility": "^1.16.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmmirror.com/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "dev": true, + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/inherits/download/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/util-deprecate/download/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "util.promisify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/util.promisify/-/util.promisify-1.0.1.tgz", + "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.2", + "has-symbols": "^1.0.1", + "object.getownpropertydescriptors": "^2.1.0" + } + }, + "utila": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/utila/download/utila-0.4.0.tgz", + "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=", + "dev": true + }, + "utility": { + "version": "1.17.0", + "resolved": "https://registry.npmmirror.com/utility/-/utility-1.17.0.tgz", + "integrity": "sha512-KdVkF9An/0239BJ4+dqOa7NPrPIOeQE9AGfx0XS16O9DBiHNHRJMoeU5nL6pRGAkgJOqdOu8R4gBRcXnAocJKw==", + "dev": true, + "requires": { + "copy-to": "^2.0.1", + "escape-html": "^1.0.3", + "mkdirp": "^0.5.1", + "mz": "^2.7.0", + "unescape": "^1.0.1" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/utils-merge/download/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vary/download/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "dev": true + }, + "vconsole": { + "version": "3.3.4", + "resolved": "https://registry.npmmirror.com/vconsole/download/vconsole-3.3.4.tgz", + "integrity": "sha1-p9rNiIez0+kC6NGEJc2lbDTnf1E=" + }, + "vendors": { + "version": "1.0.4", + "resolved": "https://registry.npmmirror.com/vendors/-/vendors-1.0.4.tgz", + "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmmirror.com/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vfile": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/vfile/-/vfile-4.2.1.tgz", + "integrity": "sha512-O6AE4OskCG5S1emQ/4gl8zK586RqA3srz3nfK/Viy0UPToBc5Trp9BVFb1u0CjsKrAWwnpr4ifM/KBXPWwJbCA==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "dev": true + } + } + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", + "dev": true + }, + "vm2": { + "version": "3.9.11", + "resolved": "https://registry.npmmirror.com/vm2/-/vm2-3.9.11.tgz", + "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==", + "dev": true, + "requires": { + "acorn": "^8.7.0", + "acorn-walk": "^8.2.0" + } + }, + "vue": { + "version": "2.7.14", + "resolved": "https://registry.npmmirror.com/vue/-/vue-2.7.14.tgz", + "integrity": "sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ==", + "requires": { + "@vue/compiler-sfc": "2.7.14", + "csstype": "^3.1.0" + } + }, + "vue-demi": { + "version": "0.14.0", + "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.0.tgz", + "integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==" + }, + "vue-eslint-parser": { + "version": "9.1.0", + "resolved": "https://registry.npmmirror.com/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz", + "integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "vue-hot-reload-api": { + "version": "2.3.4", + "resolved": "https://registry.npmmirror.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", + "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==", + "dev": true + }, + "vue-loader": { + "version": "15.10.1", + "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-15.10.1.tgz", + "integrity": "sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA==", + "dev": true, + "requires": { + "@vue/component-compiler-utils": "^3.1.0", + "hash-sum": "^1.0.2", + "loader-utils": "^1.1.0", + "vue-hot-reload-api": "^2.3.0", + "vue-style-loader": "^4.1.0" + } + }, + "vue-style-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmmirror.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz", + "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==", + "dev": true, + "requires": { + "hash-sum": "^1.0.2", + "loader-utils": "^1.0.2" + } + }, + "vue-template-compiler": { + "version": "2.7.14", + "resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", + "integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.2.0" + } + }, + "vue-template-es2015-compiler": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz", + "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", + "dev": true + }, + "vue-tsc": { + "version": "1.0.24", + "resolved": "https://registry.npmmirror.com/vue-tsc/-/vue-tsc-1.0.24.tgz", + "integrity": "sha512-mmU1s5SAqE1nByQAiQnao9oU4vX+mSdsgI8H57SfKH6UVzq/jP9+Dbi2GaV+0b4Cn361d2ln8m6xeU60ApiEXg==", + "dev": true, + "requires": { + "@volar/vue-language-core": "1.0.24", + "@volar/vue-typescript": "1.0.24" + } + }, + "vue-types": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/vue-types/-/vue-types-4.2.1.tgz", + "integrity": "sha512-DNQZmJuOvovLUIp0BENRkdnZHbI0V4e2mNvjAZOAXKD56YGvRchtUYOXA/XqTxdv7Ng5SJLZqRKRpAhm5NLaPQ==", + "requires": { + "is-plain-object": "5.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" + } + } + }, + "watchpack": { + "version": "1.7.5", + "resolved": "https://registry.npmmirror.com/watchpack/-/watchpack-1.7.5.tgz", + "integrity": "sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==", + "dev": true, + "requires": { + "chokidar": "^3.4.1", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.1" + } + }, + "watchpack-chokidar2": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", + "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "dev": true, + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/normalize-path/download/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-binary-path/download/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmmirror.com/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webpack": { + "version": "4.46.0", + "resolved": "https://registry.npmmirror.com/webpack/-/webpack-4.46.0.tgz", + "integrity": "sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.5.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.7.4", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "acorn": { + "version": "6.4.2", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-6.4.2.tgz", + "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "enhanced-resolve": { + "version": "4.5.0", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz", + "integrity": "sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "ssri": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", + "dev": true + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "webpack-bundle-analyzer": { + "version": "4.8.0", + "resolved": "https://registry.npmmirror.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.8.0.tgz", + "integrity": "sha512-ZzoSBePshOKhr+hd8u6oCkZVwpVaXgpw23ScGLFpR6SjYI7+7iIWYarjN6OEYOfRt8o7ZyZZQk0DuMizJ+LEIg==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "0.5.7", + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "requires": { + "duplexer": "^0.1.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmmirror.com/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true + } + } + }, + "webpack-chain": { + "version": "6.5.1", + "resolved": "https://registry.npmmirror.com/webpack-chain/-/webpack-chain-6.5.1.tgz", + "integrity": "sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA==", + "dev": true, + "requires": { + "deepmerge": "^1.5.2", + "javascript-stringify": "^2.0.1" + }, + "dependencies": { + "deepmerge": { + "version": "1.5.2", + "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz", + "integrity": "sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==", + "dev": true + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.3", + "resolved": "https://registry.npmmirror.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz", + "integrity": "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ==", + "dev": true, + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "dev": true, + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "webpack-dev-server": { + "version": "3.11.3", + "resolved": "https://registry.npmmirror.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz", + "integrity": "sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA==", + "dev": true, + "requires": { + "ansi-html-community": "0.0.8", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.8", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "^0.3.21", + "sockjs-client": "^1.5.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/normalize-path/download/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmmirror.com/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "dev": true, + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "is-absolute-url": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/is-binary-path/download/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/download/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "dev": true, + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1573280518303&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "dev": true, + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmmirror.com/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "4.2.2", + "resolved": "https://registry.npmmirror.com/webpack-merge/-/webpack-merge-4.2.2.tgz", + "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmmirror.com/webpack-sources/download/webpack-sources-1.4.3.tgz", + "integrity": "sha1-7t2OwLko+/HL/plOItLYkPMwqTM=", + "dev": true, + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/source-map/download/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmmirror.com/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/which-module/download/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "win-release": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/win-release/-/win-release-1.1.1.tgz", + "integrity": "sha1-X6VeAr58qTTt/BJmVjLoSbcuUgk=", + "dev": true, + "requires": { + "semver": "^5.0.1" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmmirror.com/wrappy/download/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "6.2.2", + "resolved": "https://registry.npmmirror.com/ws/-/ws-6.2.2.tgz", + "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmmirror.com/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + } + }, + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true + }, + "xregexp": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/xregexp/-/xregexp-2.0.0.tgz", + "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/xtend/download/xtend-4.0.2.tgz", + "integrity": "sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=", + "dev": true + }, + "y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/download/yallist-3.1.1.tgz", + "integrity": "sha1-27fa+b/YusmrRev2ArjLrQ1dCP0=", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmmirror.com/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmmirror.com/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + } + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + }, + "zwitch": { + "version": "1.0.5", + "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-1.0.5.tgz", + "integrity": "sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..18f3626807e86e42543112450cfce80c6d9f6e92 --- /dev/null +++ b/package.json @@ -0,0 +1,103 @@ +{ + "name": "polyv-web-live-watch-sdk", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "vue-cli-service serve", + "build": "cross-env NODE_ENV=production vue-cli-service build", + "build-static": "cross-env VUE_APP_BUILD_MODE=STATIC npm run build", + "generate-icon": "polyv-icon generate", + "build-analyzer": "cross-env USE_ANALYZER=y npm run build", + "check": "vue-tsc --noEmit --skipLibCheck", + "lint:es": "eslint . --ext .js,.ts,.vue", + "lint:style": "stylelint **/*.{css,scss,sass,vue}", + "lint": "npm run lint:es && npm run lint:style", + "lint-fix:es": "eslint . --ext .js,.ts,.vue --fix", + "lint-fix:style": "stylelint **/*.{css,scss,sass,vue} --fix", + "lint-fix": "npm run lint-fix:es && npm run lint-fix:style", + "precommit": "lint-staged", + "prepare": "husky install .husky" + }, + "lint-staged": { + "*.{js,ts,vue}": [ + "cross-env NODE_ENV=production eslint" + ], + "*.{css,sass,scss,vue}": [ + "stylelint" + ] + }, + "dependencies": { + "@just4/dom": "^0.3.0-beta.2", + "@just4/load-script": "^1.0.0", + "@just4/querystring": "^1.0.0", + "@just4/storage": "^1.0.0", + "@just4/ua-info": "^1.0.0", + "@just4/virtual-list": "^0.8.0-beta.2", + "@polyv/emotion-sdk": "^2.3.0", + "@polyv/icons-vue": "^1.2.0", + "@polyv/live-watch-sdk": "0.1.0", + "@polyv/polyv-ui": "^2.24.0", + "@polyv/utils": "^2.0.0-beta.5", + "@polyv/web-view-bridge": "0.1.0-webview-app.8", + "@popperjs/core": "^2.11.4", + "alloyfinger": "^0.1.16", + "async-validator": "^4.2.5", + "clipboard": "^2.0.10", + "js-base64": "^3.7.2", + "lodash-es": "^4.17.21", + "mitt": "^3.0.0", + "pinia": "2.0.34", + "svgaplayerweb": "^2.3.1", + "vconsole": "^3.3.4", + "vue": "2.7.14", + "vue-types": "^4.2.1" + }, + "devDependencies": { + "@babel/core": "^7.20.5", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/preset-env": "^7.20.2", + "@babel/preset-typescript": "^7.18.6", + "@polyv/eslint-config": "^0.4.0-beta.1", + "@polyv/icons-cli": "^1.2.0", + "@types/lodash-es": "^4.17.6", + "@vue/cli-plugin-babel": "^4.5.18", + "@vue/cli-service": "^4.5.18", + "autoprefixer": "^8.0.0", + "babel-loader": "^8.2.2", + "babel-plugin-import": "^1.13.3", + "babel-plugin-transform-remove-console": "^6.9.4", + "core-js": "^3.26.1", + "cross-env": "^7.0.3", + "eslint": "^8.17.0", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-prettier": "^4.2.1", + "eslint-webpack-plugin": "^2.7.0", + "fork-ts-checker-webpack-plugin": "^6.5.2", + "html-webpack-tags-plugin": "^2.0.17", + "husky": "^8.0.0", + "lint-staged": "^13.0.3", + "prettier": "^2.7.1", + "sass": "^1.33.0", + "sass-loader": "^10.2.0", + "sass-resources-loader": "^2.2.5", + "sockjs-client": "1.5.1", + "speed-measure-webpack-plugin": "^1.5.0", + "style-loader": "^2.0.0", + "stylelint": "^13.13.1", + "stylelint-config-prettier": "^9.0.3", + "stylelint-config-recess-order": "^3.0.0", + "stylelint-config-recommended": "^5.0.0", + "stylelint-config-recommended-scss": "^4.2.0", + "stylelint-scss": "^3.19.0", + "stylelint-webpack-plugin": "^2.1.1", + "ts-loader": "^8.4.0", + "typescript": "^4.7.3", + "vue-loader": "15.10.1", + "vue-style-loader": "^4.1.3", + "vue-tsc": "1.0.24", + "webpack-bundle-analyzer": "^4.7.0" + }, + "engines": { + "node": "^14.15.4 || >=16.0.0" + } +} diff --git a/polyv-icon.config.js b/polyv-icon.config.js new file mode 100644 index 0000000000000000000000000000000000000000..f0a9d3f9a5ded7ebaaaf4830536c5f6888e6cc96 --- /dev/null +++ b/polyv-icon.config.js @@ -0,0 +1,27 @@ +/** + * 文档地址:https://npm-registry.polyv.net/-/web/detail/@polyv/icons-cli + */ + +/** @type {import('@polyv/icons-cli').IconLibraryCliConfig[]} */ +module.exports = [ + { + resourcesDir: './icon-svgs/pc', + outDir: './src/components/component-icons/pc', + libraryType: 'vue', + iconMap: './src/components/component-icons/pc/map.ts', + convertColorMode: 'all', + mapExportPrefix: 'PcIcon', + demoHtml: './icon-svgs/demo/icon-demo-pc.html', + libraryName: 'PC 端观看页图标', + }, + { + resourcesDir: './icon-svgs/mobile', + outDir: './src/components/component-icons/mobile', + libraryType: 'vue', + iconMap: './src/components/component-icons/mobile/map.ts', + convertColorMode: 'all', + mapExportPrefix: 'MobileIcon', + demoHtml: './icon-svgs/demo/icon-demo-mobile.html', + libraryName: '移动端观看页图标', + }, +]; diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000000000000000000000000000000000000..a8b929ad1f0907c7818cccc4ea5459a331662168 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,7 @@ +/* eslint-env node */ + +module.exports = { + plugins: { + autoprefixer: {}, + }, +}; diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000000000000000000000000000000000000..6be8f7d260e6b8b59bd1e71d666b7b34bcbd8ad7 --- /dev/null +++ b/public/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + +
+ + + diff --git a/src/app/_hooks/use-global-events-effect.ts b/src/app/_hooks/use-global-events-effect.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e75f3a8c3bc199268c2b08562dbd5198535a56c --- /dev/null +++ b/src/app/_hooks/use-global-events-effect.ts @@ -0,0 +1,35 @@ +import { useScreenOrientHook } from '@/hooks/core/use-screen-orient'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { usePageStore } from '@/store/use-page-store'; +import { onMounted } from 'vue'; + +/** + * @hook 监听全局事件的副作用处理 + */ +export function useGlobalEventsEffect() { + const layoutStore = useLayoutStore(); + const pageStore = usePageStore(); + + // 处理屏幕旋转 + useScreenOrientHook({ + autoListenCb: screenOrientationMode => { + layoutStore.$patch({ + screenOrientationMode, + }); + }, + }); + + onMounted(() => { + // 处理页面显隐 + document.addEventListener('visibilitychange', () => { + pageStore.$patch({ + visibilityState: document.visibilityState === 'visible', + }); + }); + + // 禁止 safari 浏览器双击放大缩小效果 + document.addEventListener('gesturestart', (event: Event) => { + event.preventDefault(); + }); + }); +} diff --git a/src/app/_hooks/use-global-style.ts b/src/app/_hooks/use-global-style.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc9e987e36ae31a7dec2b00161e79d9a986d8b82 --- /dev/null +++ b/src/app/_hooks/use-global-style.ts @@ -0,0 +1,106 @@ +/** + * @file 全局样式及页面元素处理 + */ +import { useChannelInfoStore } from '@/store/use-channel-info-store'; +import { useChannelStore } from '@/store/use-channel-store'; +import { $ } from '@just4/dom'; +import { watchEffect } from 'vue'; +import { DEFAULT_BROWSER_ICON } from '@/assets/constants/defaults'; +import { isIOS, isMobile } from '@/assets/utils/browser'; + +/** + * @hook 全局样式 hook + */ +export const useGlobalStyle = () => { + /** + * 设置全局的 css 样式逻辑 + */ + function setupGlobalStyle(): void { + // 针对 ios 设备添加文档的 css 标识 + const iosPageClass = 'g-page--ios'; + if (isIOS) { + $(document.documentElement).addClass(iosPageClass); + $(document.body).addClass(iosPageClass); + } + } + + /** + * 移除骨架屏节点 + */ + function removeSkeleton(): void { + if (isMobile) { + const skeleton = document.querySelector('#skeleton'); + skeleton?.parentNode?.removeChild(skeleton); + } + } + + return { + setupGlobalStyle, + removeSkeleton, + }; +}; + +/** + * 浏览器标签页标题 hook + */ +export const useBrowserTitle = () => { + const channelInfoStore = useChannelInfoStore(); + + /** + * 设置浏览器标签页标题 + */ + function setBrowserTitle(): void { + const channelTitle = channelInfoStore.channelTitle; + if (channelTitle) { + window.document.title = channelTitle; + } + } + + watchEffect(setBrowserTitle); + + return { + setBrowserTitle, + }; +}; + +/** + * 浏览器标签页图标 hook + */ +export const useBrowserIcon = () => { + const channelStore = useChannelStore(); + + /** + * 设置图标元素 + * @param href 链接 + */ + function setFavIconElem(href: string): void { + let favIconNode = document.querySelector('link[rel*=icon]'); + if (!favIconNode) { + favIconNode = document.createElement('link'); + window.document.head.appendChild(favIconNode); + } + favIconNode.rel = 'shortcut icon'; + favIconNode.type = 'image/x-icon'; + favIconNode.href = href; + } + + function setBrowserIcon(): void { + const channelDetail = channelStore.channelDetail; + if (!channelDetail) { + return; + } + + let icon = DEFAULT_BROWSER_ICON; + if (channelDetail.channelInfo.customIcon) { + icon = channelDetail.channelInfo.customIcon; + } + + setFavIconElem(icon); + } + + watchEffect(setBrowserIcon); + + return { + setBrowserIcon, + }; +}; diff --git a/src/app/_hooks/use-weixin-setup.ts b/src/app/_hooks/use-weixin-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..9056321faddb17eb3c1ad6d93c7ee3f3a4b56dc2 --- /dev/null +++ b/src/app/_hooks/use-weixin-setup.ts @@ -0,0 +1,72 @@ +import { + useWeixinAuthorize, + useWorkWeixinAuthorize, +} from '@/hooks/platform/use-weixin/use-weixin-authorize'; +import { useWeixinSdk } from '@/hooks/platform/use-weixin/use-weixin-sdk'; +import { useWeixinShare } from '@/hooks/platform/use-weixin/use-weixin-share'; +import { useAuthStore } from '@/store/use-auth-store'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { unref } from 'vue'; +import { AuthType } from '@polyv/live-watch-sdk'; +import { useViewerStore } from '@/store/use-viewer-store'; + +/** + * @hook 处理微信流程 + * @returns 返回 true 则表示中断页面渲染,需要进行微信授权 + */ +export const useWeixinSetup = async () => { + const weixinStore = useWeixinStore(); + const viewerStore = useViewerStore(); + const authStore = useAuthStore(); + + /** + * 是否需要微信授权 + */ + function needWeixinAuthorized() { + /** + * 如果是付费观看进来的观众且没有进行非静默授权的,都要进行微信授权 + * 如:从非微信浏览器付费进入,再从微信端打开观看页 + */ + if (viewerStore.authType === AuthType.Pay && !weixinStore.weixinWatchAuthorized) { + return true; + } + + return !weixinStore.weixinAuthorized; + } + + /** + * 是否使用静默授权 + */ + function toSnsApiBase() { + // 存在付费观看的观看条件则不使用静默授权 + if (authStore.hasAuth(AuthType.Pay)) { + return false; + } + + return true; + } + + // 处理微信授权 + const { canWeixinAuthorize, redirectWeixinAuthorize } = useWeixinAuthorize(); + if (unref(canWeixinAuthorize) && needWeixinAuthorized()) { + await redirectWeixinAuthorize({ + snsApiBase: toSnsApiBase(), + }); + return true; + } + + // 处理企业微信授权 + const { canWorkWeixinAuthorize, redirectWorkWeixinAuthorize } = useWorkWeixinAuthorize(); + if (unref(canWorkWeixinAuthorize) && !weixinStore.workWeixinAuthorized) { + await redirectWorkWeixinAuthorize(); + return true; + } + + // 处理微信 js-sdk + const { getWeixinSdk } = useWeixinSdk(); + const { configureWeixinShare } = useWeixinShare(); + if (unref(canWeixinAuthorize) || unref(canWorkWeixinAuthorize)) { + await getWeixinSdk(); + await configureWeixinShare(); + } +}; diff --git a/src/app/app-events/events/chat.ts b/src/app/app-events/events/chat.ts new file mode 100644 index 0000000000000000000000000000000000000000..d384322d2445ac588d9bbef36c5d77cddf668b00 --- /dev/null +++ b/src/app/app-events/events/chat.ts @@ -0,0 +1,40 @@ +/** + * @file 组件通信-聊天室消息控制事件 + */ + +export const chatEvents = { + /** 重建聊天列表 */ + ResetUpChatMsgList: 'ResetUpChatMsgList', + /** + * 滚动到最新的消息 + */ + ScrollToNew: 'ScrollToNew', + /** + * 发送消息到聊天室 + * @param {string} content 消息文本 + */ + SendMessageToChat: 'SendMessageToChat', + /** + * 打开设置昵称 + * @param {boolean} visible 显示状态 + */ + OpenSetNick: 'OpenSetNick', + /** + * 聊天消息更新 + * @param {object} msgResult 消息结果 + */ + MsgResultUpdate: 'MsgResultUpdate', + /** + * 将聊天室输入框聚焦 + */ + FocusToChatInput: 'FocusToChatInput', + /** + * 显示完整消息 + * @param {string} content 完整的消息内容 + */ + ShowFullMessage: 'ShowFullMessage', + /** + * 触发发送图片 + */ + ToSendImage: 'ToSendImage', +}; diff --git a/src/app/app-events/events/connectMic.ts b/src/app/app-events/events/connectMic.ts new file mode 100644 index 0000000000000000000000000000000000000000..488fba5d4d432f957b0c9f4efcc0fbfe40c5ab3f --- /dev/null +++ b/src/app/app-events/events/connectMic.ts @@ -0,0 +1,34 @@ +/** + * @file 连麦相关全局事件 + */ +export const connectMicEvents = { + /** + * 重新初始化连麦 + */ + ResetUpConnectMic: 'ResetUpConnectMic', + + /** + * 触发打开设备设置 + */ + TriggerOpenDeviceSetting: 'TriggerOpenDeviceSetting', + + /** + * 触发申请连麦 + */ + TriggerApplyConnectMic: 'TriggerApplyConnectMic', + + /** + * 触发取消申请连麦 + */ + TriggerCancelApplyConnectMic: 'TriggerCancelApplyConnectMic', + + /** + * 触发下麦 + */ + TriggerEndConnectMic: 'TriggerEndConnectMic', + + /** + * 触发下麦二次确认 + */ + TriggerEndConnectConfirm: 'TriggerEndConnectConfirm', +}; diff --git a/src/app/app-events/events/doc.ts b/src/app/app-events/events/doc.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b5b4ee996bf5690ce5bb7ac8209f17e058f31ba --- /dev/null +++ b/src/app/app-events/events/doc.ts @@ -0,0 +1,7 @@ +/** + * @file 白板相关全局事件 + */ +export const docEvents = { + /** 重新创建文档 */ + ResetUpDoc: 'ResetUpDoc', +}; diff --git a/src/app/app-events/events/donate.ts b/src/app/app-events/events/donate.ts new file mode 100644 index 0000000000000000000000000000000000000000..7a7151b2557754426a11a41c90890beb27fe4bbb --- /dev/null +++ b/src/app/app-events/events/donate.ts @@ -0,0 +1,8 @@ +/** + * @file 组件通信-打赏事件 + */ + +export const donateEvents = { + /** 显示打赏弹层(竖屏) */ + OpenDonatePopup: 'OpenDonatePopup', +}; diff --git a/src/app/app-events/events/finance.ts b/src/app/app-events/events/finance.ts new file mode 100644 index 0000000000000000000000000000000000000000..07bd19badbc5b89eefde658528359374cc859079 --- /dev/null +++ b/src/app/app-events/events/finance.ts @@ -0,0 +1,13 @@ +/** + * @file 金融相关全局事件 + */ +export const financeEvents = { + /** 风险测评-展示主要弹窗 */ + RiskEvaluationMainDialogShow: 'risk-evaluation-main-dialog-show', + /** 风险测评-关闭主要弹窗 */ + RiskEvaluationMainDialogHidden: 'risk-evaluation-main-dialog-hidden', + /** 风险测评-挂件倒计时缓存清除 */ + RiskEvaluationPendantTimeoutCacheClear: 'risk-evaluation-pendant-timeout-cache-clear', + /** 风险测评-展示跳转弹窗 */ + RiskEvaluationRedirectDialogShow: 'risk-evaluation-redirect-dialog-show', +}; diff --git a/src/app/app-events/events/global.ts b/src/app/app-events/events/global.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed44d38ef54078b898dcf297634f4790945d12dd --- /dev/null +++ b/src/app/app-events/events/global.ts @@ -0,0 +1,25 @@ +export const globalEvents = { + /** + * 打开多语言切换 + */ + OpenLanguageSwitch: 'OpenLanguageSwitch', + /** + * PC 端侧边菜单栏被切换 + * @param {string} name 菜单栏名 + */ + PcAsideMenuSwitch: 'PcAsideMenuSwitch', + /** + * 移动端底部菜单栏切换 + * @param {string} name 菜单栏名 + */ + MobileBottomMenuSwitch: 'MobileBottomMenuSwitch', + /** + * 消息输入框聚焦 + */ + MsgInputFocus: 'MsgInputFocus', + /** + * 打开关注弹层 + * @param {boolean} visible 是否显示 + */ + OpenFollowLayer: 'OpenFollowLayer', +}; diff --git a/src/app/app-events/events/index.ts b/src/app/app-events/events/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..71796f7c91800da7b2703ba16dd25aeae55280fb --- /dev/null +++ b/src/app/app-events/events/index.ts @@ -0,0 +1,48 @@ +import { docEvents } from './doc'; +import { playerEvents } from './player'; +import { globalEvents } from './global'; +import { chatEvents } from './chat'; +import { donateEvents } from './donate'; +import { interactionEvents } from './interaction'; +import { inviteEvents } from './invite'; +import { microActivityEvents } from './microActivity'; +import { financeEvents } from './finance'; +import { connectMicEvents } from './connectMic'; +import { portraitEvents } from './portrait'; +import { withdrawEvents } from './withdraw'; + +const modules = { + doc: docEvents, + player: playerEvents, + global: globalEvents, + chat: chatEvents, + donate: donateEvents, + interaction: interactionEvents, + invite: inviteEvents, + withdraw: withdrawEvents, + portrait: portraitEvents, + microActivity: microActivityEvents, + finance: financeEvents, + connectMic: connectMicEvents, +}; + +type EventModules = typeof modules; + +/** + * 给模块事件加上模块名限定,如 chat 模块 { ALTER_MSG: 'ALTER_MSG' } -> { ALTER_MSG: 'chat.ALTER_MSG' } + */ +function addModuleName(moduleName: string, events: UniversalParams) { + const moduleEvent: UniversalParams = {}; + Object.keys(events).forEach(event => { + moduleEvent[event] = `${moduleName}.${events[event]}`; + }); + return moduleEvent; +} + +const newModules: UniversalParams> = {}; +Object.keys(modules).forEach(moduleName => { + const modName = moduleName as keyof EventModules; + newModules[modName] = addModuleName(modName, modules[modName]); +}); + +export default newModules as EventModules; diff --git a/src/app/app-events/events/interaction.ts b/src/app/app-events/events/interaction.ts new file mode 100644 index 0000000000000000000000000000000000000000..026799d330e4e24a24813cf2d8464bf437c1efbb --- /dev/null +++ b/src/app/app-events/events/interaction.ts @@ -0,0 +1,99 @@ +/** + * @file 互动功能相关全局事件 + */ +export const interactionEvents = { + /** + * 打开公告列表 + */ + OpenAnnouncementList: 'OpenAnnouncementList', + /** + * 打开某个公告详情 + * @param {object} data 公告详情 + */ + OpenAnnouncementDetail: 'OpenAnnouncementDetail', + /** + * 打开签到弹窗 + * @param {boolean} visible 显示状态 + */ + OpenCheckIn: 'OpenCheckIn', + /** + * 打开问卷弹窗 + * @param {boolean} visible 显示状态 + */ + OpenQuestionnaire: 'OpenQuestionnaire', + /** + * 打开答题卡弹窗 + * @param {boolean} visible 显示状态 + */ + OpenAnswerCard: 'OpenAnswerCard', + /** + * 打开快速答题卡弹窗 + * @param {boolean} visible 显示状态 + */ + OpenQuickAnswerCard: 'OpenQuickAnswerCard', + /** + * 打开竖屏的商品库列表弹窗 + * @param {boolean} visible 显示状态 + */ + OpenPortraitProductList: 'OpenPortraitProductList', + /** + * 打开举报反馈弹窗 + * @param {boolean} visible 显示状态 + */ + OpenFeedBack: 'OpenFeedBack', + /** + * 判断发言是否满足评论抽奖 + * @param {string} comment 消息内容 + */ + CheckCommentLotteryComment: 'CheckCommentLotteryComment', + /** + * 打开红包雨 + * @param {object} data 红包消息 + */ + OpenRedpackRain: 'OpenRedpackRain', + /** + * 打开红包弹窗 + * @param {object} data 红包消息 + */ + OpenRedpack: 'OpenRedpack', + /** + * 设置红包状态 + */ + SetRedpackStatus: 'SetRedpackStatus', + /** + * 打开抽奖结果弹窗 + * @param {boolean} visible 显示状态 + * @param {object} record 红包记录 + */ + OpenLotteryResult: 'OpenLotteryResult', + /** + * 打开积分记录弹层 + * @param {boolean} visible 显示状态 + */ + OpenScoreRecord: 'OpenScoreRecord', + /** + * 打开中奖记录 + * @param {boolean} visible 显示状态 + */ + OpenLotteryRecord: 'OpenLotteryRecord', + /** + * 打开报名抽奖 + * @param {boolean} visible 显示状态 + */ + OpenEnrollLottery: 'OpenEnrollLottery', + /** + * 打开发送红包 + * @param {boolean} visible 显示状态 + */ + OpenSendRedpack: 'OpenSendRedpack', + /** + * 打开问答弹层 + * @param {boolean} visible 显示状态 + */ + OpenQuestionAnswer: 'OpenQuestionAnswer', + /** + * 打开商品库的职位详情 + * @param {object} jobDetail 职位详情 + */ + OpenProductJobDetail: 'OpenProductJobDetail', +}; diff --git a/src/app/app-events/events/invite.ts b/src/app/app-events/events/invite.ts new file mode 100644 index 0000000000000000000000000000000000000000..11701bfc00066f665e4dac0a07a1a6b1ff28ccd3 --- /dev/null +++ b/src/app/app-events/events/invite.ts @@ -0,0 +1,8 @@ +/** + * @file 组件通信-邀请事件 + */ + +export const inviteEvents = { + /** 邀请榜弹窗显示(竖屏) */ + OpenPortraitInviteRank: 'OpenPortraitInviteRank', +}; diff --git a/src/app/app-events/events/microActivity.ts b/src/app/app-events/events/microActivity.ts new file mode 100644 index 0000000000000000000000000000000000000000..8100c58f6a1a4348efeeb815fac41d314ea8951f --- /dev/null +++ b/src/app/app-events/events/microActivity.ts @@ -0,0 +1,8 @@ +/** + * @file 组件通信-微活动事件 + */ + +export const microActivityEvents = { + /** 微活动弹窗显示(竖屏) */ + OpenPortraitMicroActivity: 'OpenPortraitMicroActivity', +}; diff --git a/src/app/app-events/events/player.ts b/src/app/app-events/events/player.ts new file mode 100644 index 0000000000000000000000000000000000000000..591d49c2cd11f984c401726337d64ca992afaf27 --- /dev/null +++ b/src/app/app-events/events/player.ts @@ -0,0 +1,26 @@ +/** + * @file 播放器模块事件 + */ +export const playerEvents = { + /** + * 重新创建播放器 + */ + ResetUpPlayer: 'ResetUpPlayer', + /** + * 点击了移动端播放器容器 + */ + ClickMobilePlayerContainer: 'ClickMobilePlayerContainer', + /** + * 显示历史播放提示 + * @param {number} duration 播放进度 + */ + ShowHistoryTips: 'ShowHistoryTips', + /** + * 试看播放开始 + */ + TrialPlayEnd: 'TrialPlayEnd', + /** + * 试看播放结束 + */ + TrialPlayStart: 'TrialPlayStart', +}; diff --git a/src/app/app-events/events/portrait.ts b/src/app/app-events/events/portrait.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e383a01786959faab2d07439e28fd942f278b81 --- /dev/null +++ b/src/app/app-events/events/portrait.ts @@ -0,0 +1,44 @@ +/** + * @file 竖屏模块事件 + */ +export const portraitEvents = { + /** + * 打开菜单栏弹层 + * @param {boolean} visible 显示状态,默认:true + * @param {boolean} useTransition 是否使用动画 + */ + OpenMenuPopup: 'OpenMenuPopup', + /** + * 点击了播放器区域 + * @param {MouseEvent} event 事件对象 + */ + ClickPlayerRegion: 'ClickPlayerRegion', + /** + * 打开线路切换弹层 + * @param {boolean} visible 显示状态,默认:true + * @param {boolean} useTransition 是否使用动画,默认:true + */ + OpenLineSettingPopup: 'OpenLineSettingPopup', + /** + * 打开清晰度切换弹层 + * @param {boolean} visible 显示状态,默认:true + * @param {boolean} useTransition 是否使用动画,默认:true + */ + OpenQualitySettingPopup: 'OpenQualitySettingPopup', + /** + * 打开倍速设置弹层 + * @param {boolean} visible 显示状态,默认:true + * @param {boolean} useTransition 是否使用动画,默认:true + */ + OpenRateSettingPopup: 'OpenRateSettingPopup', + /** + * 打开回放+章节弹层 + * @param {boolean} visible 显示状态,默认:true + */ + OpenPlaybackChapter: 'OpenPlaybackChapter', + /** + * 打开延迟模式弹层 + * @param {boolean} visible 显示状态,默认:true + */ + OpenLatencySwitchPopup: 'OpenLatencySwitchPopup', +}; diff --git a/src/app/app-events/events/withdraw.ts b/src/app/app-events/events/withdraw.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2e429ef5101516aa2f67d3e9fb3fbbac0f0baf6 --- /dev/null +++ b/src/app/app-events/events/withdraw.ts @@ -0,0 +1,16 @@ +/** + * @file 提现事件 + */ + +export const withdrawEvents = { + /** + * 打开提现详情 + * @param {boolean} visible 显示状态 + */ + OpenWithdrawDetail: 'OpenWithdrawDetail', + /** + * 打开提现申请 + * @param {boolean} visible 显示状态 + */ + OpenWithdrawApply: 'OpenWithdrawApply', +}; diff --git a/src/app/app-events/index.ts b/src/app/app-events/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5a665f10d17bc97bf4c6f2e3cb270be562bd524a --- /dev/null +++ b/src/app/app-events/index.ts @@ -0,0 +1,5 @@ +/** + * @file 全局组件通讯插件 + */ + +export * from './use-app-events'; diff --git a/src/app/app-events/use-app-events.ts b/src/app/app-events/use-app-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..d0680a89783209edda94a1c65b2686a209be8459 --- /dev/null +++ b/src/app/app-events/use-app-events.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { onBeforeUnmount, onMounted, readonly } from 'vue'; +import mitt from 'mitt'; +import events from './events/'; + +// 消息模块 +export const appEvents = readonly(events); + +type AppMittEvents = { + [key: string]: any; +}; + +const emitter = mitt(); +// 应用消息总线 +export const eventBus = { + $on: emitter.on, + $emit: emitter.emit, + $off: emitter.off, +}; + +/** + * 设置 eventBus 监听 + * @param event appEvents 中的事件 + * @param callback 回调 + */ +export const useEventBusListener = (event: string, callback: (...arg: any[]) => unknown) => { + onMounted(() => { + eventBus.$on(event, callback); + }); + + onBeforeUnmount(() => { + eventBus.$off(event, callback); + }); +}; + +/** + * 触发 eventBus 事件 + * @param event appEvents 中的事件 + */ +export const useEventBusEmitter = (event: string) => { + function emitEvent(params: any) { + eventBus.$emit(event, params); + } + + return { + emitEvent, + }; +}; diff --git a/src/app/layout/main-enter/_hooks/use-main-enter.ts b/src/app/layout/main-enter/_hooks/use-main-enter.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4d4b33a18b62015504b476bdb75f4edc32a9519 --- /dev/null +++ b/src/app/layout/main-enter/_hooks/use-main-enter.ts @@ -0,0 +1,29 @@ +import { watch } from 'vue'; + +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +export const useMainEnter = ( + options: { + watchLoader?: () => Promise; + } = {}, +) => { + const { watchLoader } = options; + + const watchAppStore = useWatchAppStore(); + + watch( + () => watchAppStore.shouldShowSplash, + async newVal => { + if (newVal === false) { + if (watchLoader) { + await watchLoader(); + } + // 从引导页进入观看页 + await watchAppStore.setupLiveWatch(); + } + }, + { + immediate: true, + }, + ); +}; diff --git a/src/app/layout/main-enter/_hooks/use-page-layout-style.ts b/src/app/layout/main-enter/_hooks/use-page-layout-style.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe14f0e368db53b8284b82d8613609e52f066cbf --- /dev/null +++ b/src/app/layout/main-enter/_hooks/use-page-layout-style.ts @@ -0,0 +1,32 @@ +import { onBeforeUnmount, onBeforeMount, ref } from 'vue'; + +/** + * @hook PC 页面布局 hook + */ +export const usePageLayoutStyle = () => { + const pageWidth = ref(0); + + function updatePageWidth(): void { + const width = document.documentElement.clientWidth; + pageWidth.value = width; + + // 浏览器可用区域宽度在 1024 和 1920 之间变化时,内容区域对应宽度为 964 和 1680。 + // 设置 fontSize 为内容区域宽度后,就不再需要在 CSS 中计算宽度(实际上也很难在 CSS 里面算),仅需设为 1rem。 + document.documentElement.style.fontSize = + 964 + ((1680 - 964) * (Math.min(1920, Math.max(width, 1024)) - 1024)) / (1920 - 1024) + 'px'; + } + + onBeforeMount(() => { + updatePageWidth(); + window.addEventListener('resize', updatePageWidth, false); + }); + + onBeforeUnmount(() => { + window.removeEventListener('resize', updatePageWidth, false); + document.documentElement.style.fontSize = ''; + }); + + return { + pageWidth, + }; +}; diff --git a/src/app/layout/main-enter/mobile-main.vue b/src/app/layout/main-enter/mobile-main.vue new file mode 100644 index 0000000000000000000000000000000000000000..417994ec6bd5604043fdc7e525d660eeb23b1e72 --- /dev/null +++ b/src/app/layout/main-enter/mobile-main.vue @@ -0,0 +1,78 @@ + + + + + + + + diff --git a/src/app/layout/main-enter/pc-main.vue b/src/app/layout/main-enter/pc-main.vue new file mode 100644 index 0000000000000000000000000000000000000000..a8134545127e27bb1cfc629f557529f9f71315d6 --- /dev/null +++ b/src/app/layout/main-enter/pc-main.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/app/layout/page-error/_components/page-error-mobile-default.vue b/src/app/layout/page-error/_components/page-error-mobile-default.vue new file mode 100644 index 0000000000000000000000000000000000000000..dddd5058abb9ee8d10e24cb2e581961650aa2279 --- /dev/null +++ b/src/app/layout/page-error/_components/page-error-mobile-default.vue @@ -0,0 +1,153 @@ + + + + + + + diff --git a/src/app/layout/page-error/_components/page-error-pc-default.vue b/src/app/layout/page-error/_components/page-error-pc-default.vue new file mode 100644 index 0000000000000000000000000000000000000000..5eda922d7990b5c819789449645abd3cdb083f7d --- /dev/null +++ b/src/app/layout/page-error/_components/page-error-pc-default.vue @@ -0,0 +1,78 @@ + + + + + + + diff --git a/src/app/layout/page-error/_components/page-error-work-wechat.vue b/src/app/layout/page-error/_components/page-error-work-wechat.vue new file mode 100644 index 0000000000000000000000000000000000000000..1e7cf6eff8342e77711f324d5e06dcd79f327cc7 --- /dev/null +++ b/src/app/layout/page-error/_components/page-error-work-wechat.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/src/app/layout/page-error/_hooks/use-page-error.ts b/src/app/layout/page-error/_hooks/use-page-error.ts new file mode 100644 index 0000000000000000000000000000000000000000..2eb42c34e22c4f4cc35ce9bd01020fcc9da945d1 --- /dev/null +++ b/src/app/layout/page-error/_hooks/use-page-error.ts @@ -0,0 +1,59 @@ +import { computed, unref } from 'vue'; + +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { PageErrorType, PageErrorData } from '@/app/layout/page-error/page-error-type'; + +/** + * @hook 全局错误页-页面错误数据 + */ +export const usePageErrorHook = () => { + /** 页面错误数据,必定存在 */ + const pageError = computed(() => { + return storeDefinitionToRefs(useWatchAppStore).pageError.value as PageErrorData; + }); + + const isWorkWechatError = computed(() => { + return unref(pageError).type === PageErrorType.WorkWechatCode; + }); + + const isDisallowMobileWatch = computed(() => { + return unref(pageError).type === PageErrorType.MobileChannelClosed; + }); + + const isAuthError = computed(() => { + const authErrorTypes = [ + PageErrorType.ExternalAuthFail, + PageErrorType.DirectAuthFail, + PageErrorType.ExternalAuthFail, + PageErrorType.NotAuthorized, + ]; + return authErrorTypes.includes(unref(pageError).type); + }); + + const isNotExistError = computed(() => { + return ( + unref(pageError).type === PageErrorType.ChannelIdError || + unref(pageError).type === PageErrorType.ChannelClosed + ); + }); + + const isNotFoundError = computed(() => { + return ( + !isAuthError.value && + !isNotExistError.value && + !isWorkWechatError.value && + !isDisallowMobileWatch.value + ); + }); + + return { + pageError, + + isWorkWechatError, + isDisallowMobileWatch, + isAuthError, + isNotExistError, + isNotFoundError, + }; +}; diff --git a/src/app/layout/page-error/images/error-404.png b/src/app/layout/page-error/images/error-404.png new file mode 100644 index 0000000000000000000000000000000000000000..8dc1fc071d70e3c96a749dc2cde3a34e38fd5d22 Binary files /dev/null and b/src/app/layout/page-error/images/error-404.png differ diff --git a/src/app/layout/page-error/images/error-auth.png b/src/app/layout/page-error/images/error-auth.png new file mode 100644 index 0000000000000000000000000000000000000000..922753ab8ea0e69a6fb534cbf12c3ece3c5c4650 Binary files /dev/null and b/src/app/layout/page-error/images/error-auth.png differ diff --git a/src/app/layout/page-error/images/error-noExist.png b/src/app/layout/page-error/images/error-noExist.png new file mode 100644 index 0000000000000000000000000000000000000000..a3cf5ab4f5da46db5a8de0238026dfc96910c27b Binary files /dev/null and b/src/app/layout/page-error/images/error-noExist.png differ diff --git a/src/app/layout/page-error/images/tip-icon.png b/src/app/layout/page-error/images/tip-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b6085af2e67429793df163e00e19dd055284df Binary files /dev/null and b/src/app/layout/page-error/images/tip-icon.png differ diff --git a/src/app/layout/page-error/page-error-entry.vue b/src/app/layout/page-error/page-error-entry.vue new file mode 100644 index 0000000000000000000000000000000000000000..e3a582690b0c76592e128c6ba953c76143c6aab1 --- /dev/null +++ b/src/app/layout/page-error/page-error-entry.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/app/layout/page-error/page-error-type.ts b/src/app/layout/page-error/page-error-type.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3fc82ecaa70b3da331c98f912377e899f2bad15 --- /dev/null +++ b/src/app/layout/page-error/page-error-type.ts @@ -0,0 +1,31 @@ +/** 页面异常状态类型 */ +export enum PageErrorType { + /** 频道号异常 */ + ChannelIdError = 'ChannelIdError', + /** 频道号被关闭 */ + ChannelClosed = 'ChannelClosed', + /** 移动端观看被关闭 */ + MobileChannelClosed = 'MobileChannelClosed', + /** 外部授权失败 */ + ExternalAuthFail = 'ExternalAuthFail', + /** 独立授权失败 */ + DirectAuthFail = 'DirectAuthFail', + /** 未被授权 */ + NotAuthorized = 'NotAuthorized', + /** 企业微信异常 */ + WorkWechatCode = 'WorkWechatCode', // WORK_WECHAT_CODE + /** 未知错误 */ + UnknownError = 'UnknownError', +} + +/** + * 页面异常数据 + */ +export interface PageErrorData { + /** 异常类型 */ + type: PageErrorType; + /** 标题 */ + title: string; + /** 描述 */ + desc?: string; +} diff --git a/src/app/use-watch-app.ts b/src/app/use-watch-app.ts new file mode 100644 index 0000000000000000000000000000000000000000..61bf470b019537932768e8dc85802a2096135117 --- /dev/null +++ b/src/app/use-watch-app.ts @@ -0,0 +1,216 @@ +import Vue, { onBeforeMount } from 'vue'; + +import { getWatchCore, initWatchSdk } from '@/core/watch-sdk'; + +import { useErrorCatch } from '@/hooks/core/use-error-catch'; +import { paramGetter } from '@/hooks/core/use-query-params'; +import { usePageSkin } from '@/skins/use-page-skin'; +import { useAuthSpecial } from '@/components/page-splash-common/auth/hooks/use-auth-special'; +import { useWeixinSetup } from './_hooks/use-weixin-setup'; +import { useGlobalEventsEffect } from './_hooks/use-global-events-effect'; +import { useBrowserIcon, useBrowserTitle, useGlobalStyle } from './_hooks/use-global-style'; +import { loadInteractiveReceiveUI } from '@/components/page-watch-common/interactive-receive/load-iar-ui'; + +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useChannelInfoStore } from '@/store/use-channel-info-store'; +import { useLiveBookingStore } from '@/store/use-live-booking-store'; +import { useViewerStore } from '@/store/use-viewer-store'; + +import { translate } from '@/assets/lang'; +import { isMobile, isMockPc } from '@/assets/utils/browser'; +import { ynToBool } from '@utils-ts/boolean'; + +import { PageErrorType } from '@/app/layout/page-error/page-error-type'; +import { YN } from '@polyv/live-watch-sdk'; +import { useLangStore } from '@/store/use-lang-store'; +import { LangType } from '@/assets/lang/lang-enum'; + +/** 错误日志记录 */ +const useErrorLogRecord = () => { + const watchCore = getWatchCore(); + const { windowErrorHandler, promiseErrorHandler, vueErrorHandler } = useErrorCatch(data => { + watchCore.logger.error('watch-app', 'error-log-record', JSON.stringify(data)); + }); + + windowErrorHandler(); + promiseErrorHandler(); + Vue.config.errorHandler = (error, vm, info) => { + vueErrorHandler(error, vm, info); + }; +}; + +/** + * 拦截观看页应用初始化 + * @desc 存在需要拦截的情况下,拦截器内部会自行处理,调用方需要终止初始化函数继续执行 + * @returns 是否需要拦截 + * */ +const useInterceptWatchAppInit = (): boolean => { + const watchCore = getWatchCore(); + const watchAppStore = useWatchAppStore(); + + const channelDetail = watchCore.channel.getChannelDetail(); + + // 当前处于分会场,是否需要跳转到主会场进行观看条件授权 + if (ynToBool(channelDetail.action?.mainChannelRedirect, YN.N)) { + const mainChannelId = channelDetail.multiMeetingSetting?.mainChannelId; + if (!mainChannelId) { + console.error('缺少主会场频道号'); + return true; + } + const watchUrl = watchCore.channel.generateWatchUrl(mainChannelId.toString()); + location.replace(watchUrl); + return true; + } + + // 观看页被关闭 + if (ynToBool(channelDetail.channelInfo.isClosePreview, YN.N)) { + watchAppStore.setPageError({ + type: PageErrorType.ChannelClosed, + title: translate('pageError.channelClosed'), + }); + return true; + } + + // 移动端观看页被关闭 + if ((isMobile || isMockPc) && !ynToBool(channelDetail.channelInfo.mobileEnabled, YN.Y)) { + watchAppStore.setPageError({ + type: PageErrorType.MobileChannelClosed, + title: translate('pageError.mobileChannelClosed'), + }); + return true; + } + + return false; +}; + +/** + * @hook 观看页应用 hook + */ +export function useWatchApp() { + useBrowserTitle(); + useBrowserIcon(); + + const { setupGlobalStyle, removeSkeleton } = useGlobalStyle(); + const { initPageSkin } = usePageSkin(); + + const watchAppStore = useWatchAppStore(); + const channelStore = useChannelStore(); + const channelInfoStore = useChannelInfoStore(); + const viewerStore = useViewerStore(); + const liveBookingStore = useLiveBookingStore(); + const langStore = useLangStore(); + + function onLayoutMounted() { + removeSkeleton(); + } + + /** 获取地址上的频道号 */ + function getChannelIdByUrl(): string | undefined { + let channelId: string | undefined; + if (/\d+$/.test(window.location.pathname)) { + channelId = RegExp.lastMatch; + } else if (paramGetter.channelId()) { + channelId = paramGetter.channelId(); + } + + if (!channelId) { + watchAppStore.setPageError({ + type: PageErrorType.ChannelIdError, + title: translate('pageError.channelClosed'), + }); + return; + } + + return channelId; + } + + /** + * 设置观看页应用 + */ + async function setupWatchApp(): Promise { + const channelId = getChannelIdByUrl(); + if (!channelId) { + return; + } + + // 初始化观看页 SDK + initWatchSdk({ + env: WATCH_SDK_ENV, + channelId, + vid: paramGetter.vid(), + testModeToken: paramGetter.testModeToken(), + promoteId: paramGetter.promoteId(), + invitePosterId: paramGetter.invitePosterId(), + wxInviteId: paramGetter.wxInviteId(), + invitee: paramGetter.invitee(), + userInfo: { + userId: paramGetter.viewerid(), + nick: paramGetter.nickname() || paramGetter.name(), + pic: paramGetter.avatar(), + }, + zone: paramGetter.zone(), + language: langStore.currentLang === LangType.Chinese ? 'zh_CN' : 'en', + }); + + try { + await Promise.all([ + // 安装观看页核心 + watchAppStore.resetUpWatchCore(), + // 记载互动功能 UI 组件 + loadInteractiveReceiveUI(), + ]); + } catch (error) { + console.error(error); + watchAppStore.setPageError({ + type: PageErrorType.UnknownError, + title: '数据请求异常,页面初始化失败!', + }); + return; + } + + // 错误日志记录 + useErrorLogRecord(); + + /** 需要拦截观看页应用初始化 */ + const needInterceptWatchAppInit = useInterceptWatchAppInit(); + if (needInterceptWatchAppInit) { + return; + } + + // 同步相关 store 的数据 + channelStore.syncChannelStore(); + channelInfoStore.syncChannelInfo(); + viewerStore.syncViewerInfo(); + liveBookingStore.initLiveBookingData(); + + // 初始化多皮肤 + initPageSkin(); + + // 处理特殊授权 + const { handleSpecialAuth } = useAuthSpecial(); + await handleSpecialAuth(); + + // 处理微信流程 + const weixinResult = await useWeixinSetup(); + if (weixinResult) { + return; + } + + watchAppStore.$patch({ + watchAppInited: true, + }); + } + + onBeforeMount(() => { + setupGlobalStyle(); + setupWatchApp(); + }); + + useGlobalEventsEffect(); + + return { + onLayoutMounted, + getChannelIdByUrl, + }; +} diff --git a/src/app/watch-app.vue b/src/app/watch-app.vue new file mode 100644 index 0000000000000000000000000000000000000000..bfc78c882336e92812df69ac67c236c084dc376d --- /dev/null +++ b/src/app/watch-app.vue @@ -0,0 +1,52 @@ + + + + + + + diff --git a/src/assets/constants/date-format.ts b/src/assets/constants/date-format.ts new file mode 100644 index 0000000000000000000000000000000000000000..cdc3c0c27b4908edff0e3f814252e32d64166e04 --- /dev/null +++ b/src/assets/constants/date-format.ts @@ -0,0 +1,14 @@ +/** + * 完整的日期格式 + */ +export const DATE_FORMAT_COMPLETE = 'YYYY-MM-DD hh:mm:ss'; + +/** + * 斜杠格式 + */ +export const DATE_FORMAT_SLASH = 'YYYY/MM/DD hh:mm'; + +/** + * 仅小时分钟 + */ +export const DATE_FORMAT_HM = 'hh:mm'; diff --git a/src/assets/constants/defaults.ts b/src/assets/constants/defaults.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba3769b58d711413b33ac854614f35289ccfa623 --- /dev/null +++ b/src/assets/constants/defaults.ts @@ -0,0 +1,36 @@ +/** + * @file 一些静态资源的默认地址(包括通过 require 引入到图片) + */ + +/** 默认的频道 logo */ +export const DEFAULT_CHANNEL_LOGO = 'https://s1.videocc.net/default-img/channel/default-logo.png'; + +/** 默认观众头像 */ +export const DEFAULT_VIEWER_AVATAR = 'https://s1.videocc.net/default-img/avatar/viewer.png'; + +/** 默认引导页封面图 */ +export const DEFAULT_SPLASH_IMG = 'https://s1.videocc.net/default-img/channel/default-splash.png'; + +/** 默认引导页大图封面图 */ +export const DEFAULT_SPLASH_FULL_IMG = require('@/assets/images/splash/default-splash-full-img.jpg'); + +/** 默认 SVGA 文件目录 */ +export const DEFAULT_SVGA_DIRECTORY = 'https://s1.videocc.net/default-img/donate-svga/'; + +/** 默认的浏览器 ico */ +export { default as DEFAULT_BROWSER_ICON } from '@/assets/favicons/polyv.ico'; + +/** 默认分享图标 - QQ */ +export const DEFAULT_SHARE_QQ_ICON: string = require('@/assets/images/share/qq.png'); + +/** 默认分享图标 - QQ 空间 */ +export const DEFAULT_SHARE_QZONE_ICON: string = require('@/assets/images/share/qzone.png'); + +/** 默认分享图标 - 微博 */ +export const DEFAULT_SHARE_WEIBO_ICON: string = require('@/assets/images/share/weibo.png'); + +/** 默认选择的手机区号 */ +export const DEFAULT_PHONE_NUMBER_AREA_CODE = '+86'; + +/** 默认唯一登录校验频率 */ +export const DEFAULT_VERIFY_QUERY_FREQUENCY = 30; diff --git a/src/assets/constants/key-code.ts b/src/assets/constants/key-code.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f74956df61ee4187c4c9d99f2daa8f4aa1f59a8 --- /dev/null +++ b/src/assets/constants/key-code.ts @@ -0,0 +1,13 @@ +/** 键盘按键 */ +export enum KeyCodeMap { + /** 上箭头 */ + ArrowUp = 38, + /** 下箭头 */ + ArrowDown = 40, + /** 左箭头 */ + ArrowLeft = 37, + /** 右箭头 */ + ArrowRight = 39, + /** 空格键 */ + Space = 32, +} diff --git a/src/assets/constants/selector.ts b/src/assets/constants/selector.ts new file mode 100644 index 0000000000000000000000000000000000000000..a05a599e0dc026216804e472dffce7a54c33fcc6 --- /dev/null +++ b/src/assets/constants/selector.ts @@ -0,0 +1,12 @@ +/** + * @file 常用的 className + */ + +/** PC 端播放器主屏 className */ +export const PC_MAIN_PLAYER_SELECTOR = '.c-pc-watch-layout__main__left'; + +/** PC 端用于全屏的选择器 */ +export const PC_PLAYER_FULL_SCREEN_SELECTOR = '.c-pc-watch-layout__main__fullscreen-wrap'; + +/** PC 端右边侧边栏外层 */ +export const PC_LAYOUT_ASIDE_WRAP = '.c-pc-watch-layout__main__right'; diff --git a/src/assets/constants/special-user-types.ts b/src/assets/constants/special-user-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..cba9402ea6b60227573d23adf5324638a25613be --- /dev/null +++ b/src/assets/constants/special-user-types.ts @@ -0,0 +1,21 @@ +import { ChatUserTypes } from '@polyv/live-watch-sdk'; + +/** + * 特殊用户身份类型列表 + */ +export const specialUserTypes = [ + ChatUserTypes.Teacher, + ChatUserTypes.Manager, + ChatUserTypes.Guest, + ChatUserTypes.Assistant, + ChatUserTypes.Attendee, + ChatUserTypes.Dummy, +]; + +/** + * 判断是否为特殊的用户类型 + * @param userType 用户类型 + */ +export function isSpecialUserType(userType: ChatUserTypes): boolean { + return specialUserTypes.includes(userType); +} diff --git a/src/assets/constants/tab-name.ts b/src/assets/constants/tab-name.ts new file mode 100644 index 0000000000000000000000000000000000000000..93cbbbb7ad6ca57c5ea9b15b2f6caf46fdc37830 --- /dev/null +++ b/src/assets/constants/tab-name.ts @@ -0,0 +1,69 @@ +/** + * 移动端下文档和摄像头 tab 名 + */ +export const TAB_NAME_DOC_OR_VIDEO = 'docOrVideo'; + +/** + * 聊天室 tab 名 + */ +export const TAB_NAME_CHAT = 'chat'; + +/** + * 问答 tab 名 + */ +export const TAB_NAME_QA = 'qa'; + +/** + * 提问 tab 名 + */ +export const TAB_NAME_ASK = 'ask'; + +/** + * 连麦 tab 名 + */ +export const TAB_NAME_CONNECT_MIC = 'connectMic'; + +/** + * 投票 tab 名 + */ +export const TAB_NAME_VOTE = 'vote'; + +/** + * 云席 tab 名 + */ +export const TAB_NAME_SEAT = 'seat'; + +/** + * 邀请榜 tab 名 + */ +export const TAB_NAME_INVITE_RANK = 'inviteRank'; + +/** + * 成员列表 tab 名 + */ +export const TAB_NAME_MEMBERS = 'members'; + +/** + * 商品库 tab 名 + */ +export const TAB_NAME_PRODUCT = 'product'; + +/** + * 回放 tab 名 + */ +export const TAB_NAME_PLAYBACK = 'playback'; + +/** + * 章节 tab 名 + */ +export const TAB_NAME_CHAPTER = 'chapter'; + +/** + * 微活动 tab 名 + */ +export const TAB_NAME_MICRO_ACTIVITY = 'microActivity'; + +/** + * 图文直播 tab 名 + */ +export const TAB_NAME_TUWEN_LIVE = 'tuwenLive'; diff --git a/src/assets/favicons/polyv.ico b/src/assets/favicons/polyv.ico new file mode 100644 index 0000000000000000000000000000000000000000..4a26ad425a95082983f384a12b9485b97c08cc27 Binary files /dev/null and b/src/assets/favicons/polyv.ico differ diff --git a/src/assets/images/low-latency/low-latency-bg-mobile.svg b/src/assets/images/low-latency/low-latency-bg-mobile.svg new file mode 100644 index 0000000000000000000000000000000000000000..93c92a5e3c9c25d7cd4488e9b3640aac12323950 --- /dev/null +++ b/src/assets/images/low-latency/low-latency-bg-mobile.svg @@ -0,0 +1,119 @@ + + + + icon无延迟-移动端 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/low-latency/low-latency-bg-pc.svg b/src/assets/images/low-latency/low-latency-bg-pc.svg new file mode 100644 index 0000000000000000000000000000000000000000..d8d202f00a73bedf760ba5de17070514fd3a4553 --- /dev/null +++ b/src/assets/images/low-latency/low-latency-bg-pc.svg @@ -0,0 +1,117 @@ + + + + icon无延迟备份 2 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/player/icon-cam-closed.png b/src/assets/images/player/icon-cam-closed.png new file mode 100644 index 0000000000000000000000000000000000000000..4fddd3221419149077c9d0210377626c91687516 Binary files /dev/null and b/src/assets/images/player/icon-cam-closed.png differ diff --git a/src/assets/images/player/icon-sip.png b/src/assets/images/player/icon-sip.png new file mode 100644 index 0000000000000000000000000000000000000000..1b54d6f45389e60b5ba80f7ca803d891539bef8f Binary files /dev/null and b/src/assets/images/player/icon-sip.png differ diff --git a/src/assets/images/player/play-btn.png b/src/assets/images/player/play-btn.png new file mode 100644 index 0000000000000000000000000000000000000000..fafd0896e24c759f473de6f9cc4664cce6463571 Binary files /dev/null and b/src/assets/images/player/play-btn.png differ diff --git a/src/assets/images/player/player-audio-mode.png b/src/assets/images/player/player-audio-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..3b6b20aac0017ff0409791731b23dffe23c5f365 Binary files /dev/null and b/src/assets/images/player/player-audio-mode.png differ diff --git a/src/assets/images/player/player-no-live.png b/src/assets/images/player/player-no-live.png new file mode 100644 index 0000000000000000000000000000000000000000..0634450d7647c55ca7da54a6392dc33d0d83b00d Binary files /dev/null and b/src/assets/images/player/player-no-live.png differ diff --git a/src/assets/images/player/player-stop.png b/src/assets/images/player/player-stop.png new file mode 100644 index 0000000000000000000000000000000000000000..da641688bca80b67716894701fa8fb71b42fd2ba Binary files /dev/null and b/src/assets/images/player/player-stop.png differ diff --git a/src/assets/images/share/facebook.png b/src/assets/images/share/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..d1ed03ddf70aa2a9ff55d3d6bb2508c847af4526 Binary files /dev/null and b/src/assets/images/share/facebook.png differ diff --git a/src/assets/images/share/qq.png b/src/assets/images/share/qq.png new file mode 100644 index 0000000000000000000000000000000000000000..4b1f1533c158386fbbafa1124d63ca1083c4c13c Binary files /dev/null and b/src/assets/images/share/qq.png differ diff --git a/src/assets/images/share/qzone.png b/src/assets/images/share/qzone.png new file mode 100644 index 0000000000000000000000000000000000000000..c6ca532ad3e0c9298ff0f14794dcf9fae6e0078e Binary files /dev/null and b/src/assets/images/share/qzone.png differ diff --git a/src/assets/images/share/twitter.png b/src/assets/images/share/twitter.png new file mode 100644 index 0000000000000000000000000000000000000000..a19593635491b6a6b870aa4fb9a14d19e2f17140 Binary files /dev/null and b/src/assets/images/share/twitter.png differ diff --git a/src/assets/images/share/weibo.png b/src/assets/images/share/weibo.png new file mode 100644 index 0000000000000000000000000000000000000000..78ccb2459544799f3a43dc0962e5060ad85848b8 Binary files /dev/null and b/src/assets/images/share/weibo.png differ diff --git a/src/assets/images/splash/default-splash-full-img.jpg b/src/assets/images/splash/default-splash-full-img.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6c33a6fb21e6268e2241339d0cd44dd4aecbc05c Binary files /dev/null and b/src/assets/images/splash/default-splash-full-img.jpg differ diff --git a/src/assets/lang/i18ns/arabic.ts b/src/assets/lang/i18ns/arabic.ts new file mode 100644 index 0000000000000000000000000000000000000000..c3bbf1263a558465e4edba8de97d0d480e9953ab --- /dev/null +++ b/src/assets/lang/i18ns/arabic.ts @@ -0,0 +1,9 @@ +import { LangConfig } from '../types'; +import { englishLang } from './english'; + +/** + * 阿拉伯语配置 + */ +export const arabicLang: LangConfig = { + ...englishLang, +}; diff --git a/src/assets/lang/i18ns/china.ts b/src/assets/lang/i18ns/china.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad9820707b3403929c28c5fafbf25614f2d07cda --- /dev/null +++ b/src/assets/lang/i18ns/china.ts @@ -0,0 +1,458 @@ +/** + * 中文语言配置 + */ +export const chinaLang = { + 'watchCore.error.uploadImage': '图片上传失败!', + + 'auth.button.none': '观看直播', + 'auth.button.phone': '会员观看', + 'auth.button.pay': '付费观看', + 'auth.button.info': '登记观看', + 'auth.button.code': '验证码观看', + 'auth.button.login': '登录观看', + 'auth.button.workWx': '企微观看', + 'auth.button.playback': '立即回看', + 'auth.code.placeholder': '请输入验证信息', + 'auth.info.registered.entrance': '我已登记 直接进入>', + 'auth.info.registered.title': '我已登记', + 'auth.phone.placeholder': '请输入验证信息', + 'auth.pay.tips': '本次观看需支付', + 'auth.pay.scan': '微信扫码支付', + 'auth.pay.hasPay': '我已支付 >', + 'auth.pay.wechatPrefix': '请使用已付款的', + 'auth.pay.wechatSuffix': '微信,扫一扫二维码进入观看', + 'auth.pay.clickPay': '点击付费购买', + 'auth.pay.audience': '已付费观众?请', + 'auth.pay.clickHere': '点击这里', + 'auth.pay.wechat': '微信支付', + 'auth.workWeixin.title': '企业微信登录', + 'auth.workWeixin.qrcodeLoading': '二维码加载中', + 'auth.error.checkProtocol': '请勾选隐私声明', + 'auth.error.unknown': '未知错误', + 'auth.error.codeEmpty': '请输入验证信息', + 'auth.error.codeError': '输入的验证码错误', + 'auth.error.smsCodeError': '短信验证码错误', + 'auth.error.phoneEmpty': '请输入验证信息', + 'auth.error.payTimeout': '支付超时,请重新扫码付费', + 'auth.error.phoneNotExistent': '输入的会员码错误', + 'auth.error.phoneNotRegister': '手机号未登记', + 'auth.error.improveInfo': '请完善信息', + 'auth.error.externalFail': '该页面使用外部授权,请使用授权入口进入观看', + 'auth.error.directFail': '授权出错,该页面使用独立授权,请使用授权入口进行观看', + 'auth.error.workWeixinExpire': '观看条件已失效,无法继续观看,请联系管理员', + 'auth.error.workWeixinNotAllow': '若为企微员工请在企业微信打开该链接观看直播', + 'auth.error.workWeixinNotStaff': '登录失败,请确认员工身份', + 'auth.enter': '进入直播', + + 'ask.placeholderMsg': '您好!请问有什么问题吗', + + 'base.frequentOperation': '您操作过于频繁,请稍后再试', + 'base.region': '地区', + 'base.ok': '我知道了', + 'base.backTop': '回到顶部', + 'base.people': '人', + 'base.error.payFail': '支付失败', + 'base.confirm': '确认', + 'base.cancel': '取消', + 'base.ignore': '忽略', + 'base.warmTips': '温情提示', + 'base.enterLive': '进入直播', + 'base.tips': '提示', + 'base.me': '我', + 'base.more': '更多', + 'base.notAuthorized': '您未被授权观看本直播', + 'base.changeLang': '语言切换', + 'base.message': '消息', + 'base.packUp': '收起', + 'base.spreadOut': '展开', + 'base.copy': '复制', + 'base.fullText': '全文', + 'base.translateFinish': '翻译完成', + 'base.camera': '摄像头', + 'base.popup.supportDragUp': '弹窗支持向上拖动', + 'base.popup.understood': '知道了', + + 'channel.descEmpty': '暂无直播介绍~', + + 'call.connect': '连线', + 'connectMic.deviceCheck': '设备检测', + 'connectMic.openVideoTips': '讲师开启了视频连线', + 'connectMic.openAudioTips': '讲师开启了语音连线', + 'connectMic.joinConnect': '点击申请连线按钮参与', + 'connectMic.applying': '申请连线中', + 'connectMic.applying2': '请求中', + 'connectMic.applyConnect': '申请连线', + 'connectMic.applyConnect.video': '视频连线', + 'connectMic.applyConnect.audio': '语音连线', + 'connectMic.cancelApply': '取消申请', + 'connectMic.endConnect': '结束连线', + 'connectMic.keepConnect': '保持连线', + 'connectMic.me': '我', + 'connectMic.teacher': '讲师', + 'connectMic.confirm1': '您当前正在进行音视频连线', + 'connectMic.confirm2': '确认结束?', + 'connectMic.teacherHangUp': '连线已结束,您可以再次申请连线', + 'connectMic.agreement': '视频连线时需拍摄、收集人像信息', + 'connectMic.error.getDeviceFail': '无法获取设备权限或无设备,请检查后重试!', + 'connectMic.error.overLimit': '上麦失败,当前上麦人数已达最大人数', + + 'chat.empty': '聊天室空空如也~', + 'chat.connectError': '聊天室连接失败,无法与其他人互动,立即刷新重试?', + 'chat.norMore': '没有更多了', + 'chat.loadError': '消息加载失败', + 'chat.closeTips': '本直播间已长时间未使用,聊天室暂时关闭,将在下次开播时恢复正常', + 'chat.transformTip': '已关联其他房间,仅可观看直播内容', + 'chat.onlyHost': '只看我和讲师', + 'chat.onlyHost2': '只看主持', + 'chat.lookAll': '查看全部', + 'chat.sendImage': '发送图片', + 'chat.setNick': '修改昵称', + 'chat.setNick.title': '设置昵称', + 'chat.setNick.placeholder': '请输入昵称', + 'chat.setNick.success': '设置成功', + 'chat.setNick.tips1': '点击', + 'chat.setNick.tips2': '设置昵称', + 'chat.setNick.tips3': '参与互动', + 'chat.setNick.error.empty': '请输入昵称', + 'chat.setNick.error.special': '昵称包含非法字符', + 'chat.send': '发送', + 'chat.input.placeholder': '我也来参与一下互动', + 'chat.input.placeholder2': '跟大家聊点什么吧~', + 'chat.input.placeholder3': '点击设置昵称参与互动', + 'chat.input.maxImageTips': '最多插入不超过${maxCount}张图片', + 'chat.closeRoom': '聊天室已关闭', + 'chat.roomClosed': '聊天室暂时关闭', + 'chat.roomReopened': '聊天室重新打开', + 'chat.reply': '回复', + 'chat.illegalImage': '图片违规', + 'chat.moreMsg': '有更多消息', + 'chat.moreMsg2': '查看新消息', + + 'copy.link': '复制链接', + 'copy.success': '复制成功', + 'copy.linkSuccess': '链接复制成功,可去浏览器粘贴下载', + + 'doc.name': '文档', + + 'donate.title': '打赏', + 'donate.cash': '现金', + 'donate.cashReward': '现金打赏', + 'donate.custom': '自定义', + 'donate.custom2': '自定义金额', + 'donate.placeholder': '请输入金额', + 'donate.random': '随机', + 'donate.goodAction': '赠送了', + 'donate.cashAction': '打赏', + 'donate.yuan': '元', + 'donate.free': '免费', + 'donate.button': '打赏', + 'donate.name.six': '666', + 'donate.name.bear': '小熊', + 'donate.name.microphone': '麦克风', + 'donate.name.villa': '别墅', + 'donate.name.coffee': '咖啡', + 'donate.name.trophy': '奖杯', + 'donate.name.star': '小星星', + 'donate.name.applause': '掌声', + 'donate.name.yacht': '游艇', + 'donate.name.rocket': '火箭', + 'donate.name.like': '点赞', + 'donate.name.fireworks': '烟火', + 'donate.name.crown': '皇冠', + 'donate.name.cars': '跑车', + 'donate.name.goldenEggs': '金蛋', + 'donate.name.diamonds': '钻石', + 'donate.name.plane': '飞机', + 'donate.name.flower': '鲜花', + 'donate.confirm': '确认打赏', + 'donate.point.getFail': '获取积分失败', + 'donate.point.myPoint': '我的积分:', + 'donate.error.notChoice': '请选择打赏的礼物', + 'donate.error.fail': '打赏失败', + 'donate.error.unknown': '打赏发生未知错误', + 'donate.error.payTimeOut': '支付超时', + 'donate.error.payFail': '支付失败', + 'donate.error.notZero': '输入金额不能是0', + 'donate.error.mini': '请输入大于等于 ${price} 的金额', + 'donate.hideAnimation': '屏蔽特效', + 'donate.hideAnimation.tips': '已屏蔽其他用户的礼物特效', + 'donate.showAnimation': '展示特效', + 'donate.showAnimation.tips': '已恢复其他用户的礼物特效', + 'donate.spot': '点', + + 'enroll.text': '报名观看', + 'enroll.enrolledTips': '您已报名,新填写的信息会覆盖原有信息', + 'enroll.entrance': '我已报名 直接进入>', + 'enroll.auditing': '报名审核中', + 'enroll.loginTitle': '我已报名', + 'enroll.error.emailUnenroll': '该邮箱尚未报名,请先填写报名信息后再观看直播', + 'enroll.error.phoneUnenroll': '该手机号尚未报名,请先填写报名信息后再观看直播', + 'enroll.error.smsCodeError': '短信验证码错误', + + 'form.name.phoneNumber': '手机号码', + 'form.name.verifyCode': '验证码', + 'form.placeholder.phoneInput': '请输入手机号码', + 'form.placeholder.smsVerify': '短信验证码', + 'form.placeholder.imageVerify': '请输入验证码', + 'form.placeholder.enterCountry': '请输入国家名查询', + 'form.error.phoneNumberRequired': '请输入手机号码', + 'form.error.phoneNumberError': '请输入正确的手机号码', + 'form.error.smsVerifyRequired': '请输入短信验证码', + 'form.error.imageCaptchaError': '图片验证码错误', + 'form.error.emailRequired': '请输入邮箱地址', + 'form.error.emailError': '请输入正确的邮箱地址', + 'form.error.onlyCnAnEn': '只能包含中文或英文', + 'form.error.areaError': '请选择正确的地区', + 'form.getSmsVerify': '获取验证码', + 'form.resend': '重新发送', + 'form.radio': '单选', + 'form.checkbox': '多选', + 'form.select.multiPrefix': '已选', + 'form.select.multiSuffix': '项', + 'form.uploadImage.prefixTips': '当前可上传', + 'form.uploadImage.suffixTips': '张', + + 'global.singleSession.error.unknow': '未知原因异常,您将被退出观看', + 'global.singleSession.error.relogin': '帐号在另外的地方登录,您将被退出观看', + + 'interact.announcement.title': '公告', + 'interact.announcement.titleList': '公告列表', + 'interact.announcement.titleDetail': '公告', + 'interact.answerCard.singleChoice': '【单选】', + 'interact.answerCard.multiChoice': '【多选】', + 'interact.answerCard.title': '答题卡', + 'interact.answerCard.checkResultTitle': '查看结果', + 'interact.answerCard.checkAnswerTitle': '查看答案', + 'interact.answerCard.notSubmitted': '未作答', + 'interact.checkIn.title': '签到', + 'interact.feedBack.title': '投诉反馈', + 'interact.lottery.result': '中奖结果', + 'interact.lottery.record': '中奖记录', + 'interact.lottery.checkList': '中奖名单', + 'interact.lottery.submitInfo': '填写中奖信息', + 'interact.product.jobDetail': '职位详情', + 'interact.questionnaire.title': '问卷', + 'interact.questionnaire.result': '问卷结果', + 'interact.questionnaire.result.list': '问卷列表', + 'interact.vote.title': '投票', + 'interact.task.title': '任务', + 'interact.task.me': '我的任务', + 'interact.tuwen.tuwenMode': '图文模式', + 'interact.tuwen.picMode': '图片模式', + 'interact.tuwen.total': '共', + 'interact.tuwen.totalCount': '条图文直播', + 'interact.scoreRecord.title': '积分记录', + 'interact.enrollLottery.title': '报名抽奖', + + 'liveBooking.book': '预约', + 'liveBooking.bookLive': '预约直播', + 'liveBooking.booked': '已预约', + 'liveBooking.writeForm': '填写预约信息', + 'liveBooking.confirm': '确认预约', + 'liveBooking.bookingSuccess': '预约成功,请等待通知', + 'liveBooking.cancelSuccess': '取消预约成功', + 'liveBooking.smsWrong': '验证码错误,请重新输入', + 'liveBooking.fail': '预约失败', + 'liveBooking.subscribe1': '长按二维码关注', + 'liveBooking.subscribe2': '可以在直播开始', + 'liveBooking.subscribe3': '前15分钟', + 'liveBooking.subscribe4': '收到开播提醒', + 'liveBooking.welcome': '欢迎观看本次直播', + + 'liveCountDown.text': '直播倒计时', + + 'liveStatus.live': '直播中', + 'liveStatus.waiting': '等待中', + 'liveStatus.end': '暂无直播', + 'liveStatus.stop': '直播暂停', + 'liveStatus.playback': '回放中', + 'liveStatus.unStart': '未开始', + + 'live.audioLive': '音频模式', + 'live.goLive': '直播开始 立即前往', + + 'layout.fullscreen': '全屏', + 'layout.fullscreenCancel': '取消全屏', + + 'pageError.feedBack': '可以先检查您的频道号,再反馈给管理员,稍后就可以处理好。', + 'pageError.channelNotExist': '频道号不存在', + 'pageError.channelClosed': '频道号已被关闭', + 'pageError.mobileChannelClosed': '本直播仅支持PC端观看,请使用PC浏览以下地址', + + 'player.play.button.text': '立即播放', + 'player.play': '播放', + 'player.pause': '暂停', + 'player.refresh': '刷新', + 'player.switchScreen': '切换', + 'player.manualPlayTips1': '点击', + 'player.manualPlayTips2': '播放按钮', + 'player.manualPlayTips3': ',进入直播', + 'player.rtc.paused': '已暂停直播和连麦画面', + 'player.volumeTips1': '是否允许浏览器播放声音?', + 'player.volumeTips2': '播放声音', + 'player.barrage': '弹幕', + 'player.barrage.show': '弹幕开', + 'player.barrage.close': '弹幕关', + 'player.openBarrage': '开启弹幕', + 'player.closeBarrage': '关闭弹幕', + 'player.lineSwitch': '线路选择', + 'player.lineSetting': '切换线路', + 'player.line': '线路', + 'player.quality': '清晰度', + 'player.qualitySetting': '切换清晰度', + 'player.quality.auto': '自动', + 'player.quality.sd': '流畅', + 'player.quality.hd': '高清', + 'player.quality.fhd': '超清', + 'player.rate': '倍速', + 'player.historyTips': '已为您定位到上次观看进度', + 'player.restart': '从头播放', + 'player.latencyMode.name': '模式', + 'player.latencyMode.low': '无延迟', + 'player.latencyMode.normal': '正常延迟', + 'player.latency.error': '播放无延迟异常,已帮您切换到正常延迟模式', + 'player.mode.audio': '音频模式', + 'player.mode.video': '视频模式', + 'player.audio.ing': '音频播放中', + + 'playback.list': '回放列表', + 'playback.playing': '播放中', + 'playback.startTime': '开始时间:', + 'playback.chapter': '章节', + 'playback.no.more': '没有更多了', + + 'trial.play.finish.tip': '试看已结束', + 'trial.player.start.tip': '本次直播为付费直播,您可试看 ${time} 分钟', + 'trial.player.end.tip': '试看已结束。如果您觉得本直播不错,请点击付费观看', + + 'redpack.receive.name': '红包', + 'redpack.finish': ',红包已被领完', + 'redpack.normal.get': '从普通红包中获得', + 'redpack.password.get': '从口令红包中获得', + 'redpack.rain.get': '从红包雨中获得', + 'redpack.rain.finish': ',红包雨已被领完', + 'redpack.type.normal': '普通红包', + 'redpack.type.password': '口令红包', + 'redpack.type.rain': '红包雨', + 'redpack.type.alipayPassword': '支付宝口令红包', + 'redpack.sendMsg': '发送了一个', + 'redpack.receive': '点击领取', + + 'redpack.name': '红包', + 'redpack.send.title': '发红包', + 'redpack.send.sum': '总金额', + 'redpack.send.sum.placeholder': '请填写总金额', + 'redpack.send.sum.unit': '元', + 'redpack.send.num': '个数', + 'redpack.send.num.placeholder': '请填写个数', + 'redpack.send.num.unit': '个', + 'redpack.blessing.placeholder': '恭喜发财,大吉大利', + 'redpack.desc': '红包为随机红包,总金额需大于 1 元,另需收取 2% 手续费', + 'redpack.send.btn.text': '塞钱进红包', + 'redpack.single.amount.limit': '单个红包不可超过 ${amount} 元', + 'redpack.single.num.limit': '红包数量不可超过 ${count}', + 'redpack.equal.amount.limit': '最低平分金额大于等于 ${average} 元', + 'redpack.bad.words': '涉及敏感词', + 'redpack.send.amount.low': '金额不能小于1元', + 'redpack.greeting.long': '祝福语过长', + 'redpack.unsupported': '不支持该红包类型', + + 'risk.confirm.letter.agree': '同意并观看直播', + 'risk.confirm.letter.disagree': '不同意', + 'risk.confirm.letter.viewAgreement': '查看协议', + + 'seminar.host': '主持人', + + 'share.title': '分享', + 'share.wxScan': '微信扫一扫', + 'share.QQ': 'QQ', + 'share.QZone': 'QQ空间', + 'share.weibo': '新浪微博', + + 'unit.day': '天', + 'unit.hour': '时', + 'unit.minute': '分', + 'unit.second': '秒', + + 'watch.liveTime': '直播时间', + 'watch.pvSuffix': '次观看', + + 'welcome.text': '欢迎', + 'welcome.wait': '等', + 'welcome.peopleJoin': '人加入', + 'welcome.join': '加入', + + 'weixin.scanToPay': '请使用微信扫码支付', + 'weixin.pay.success': '支付成功', + 'weixin.error.payTimeout': '支付超时', + 'weixin.error.toWechatPay': '请在微信中打开本页进行支付', + 'weixin.error.openInClient': '请在微信端打开', + + 'withdraw.name': '提现', + 'withdraw.apply.popup.title': '提现', + 'withdraw.apply.amount': '提现金额', + 'withdraw.apply.amount.all': '全部', + 'withdraw.apply.amount.most': '最多可提现 ${amount} 元', + 'withdraw.apply.amount.most.merchant': '单次最多提现 ${amount} 元', + 'withdraw.apply.username': '提现用户名', + 'withdraw.apply.tips': + '* 红包余额需满 ¥${amount} 才可提现,仅支持微信实名认证用户提现,申请提现后在 ${day} 个工作日内,提现至你的微信钱包。', + 'withdraw.apply.btn.text': '提现', + 'withdraw.apply.success.msg': '提现申请成功', + 'withdraw.apply.rules.amount.required': '请填写提现金额', + 'withdraw.apply.rules.amount.low': '金额不能小于1元', + + 'withdraw.error.noUser': '找不到该微信用户', + 'withdraw.error.getCashExceed': '提取金额超过用户余额', + 'withdraw.error.getCashMoreThan1': '提取金额必须大于1元', + 'withdraw.error.getCashFailed': '申请记录失败', + + 'withdraw.detail.popup.title': '我的红包余额', + 'withdraw.detail.btn.text': '提现', + 'withdraw.redpack.receive.record': '历史领取', + 'withdraw.redpack.receive.record.listName': '红包领取', + 'withdraw.cash.receive.record': '历史提现', + 'withdraw.cash.receive.record.listName': '提现成功', + + 'menu.tabName.desc': '直播介绍', + 'menu.tabName.chat': '聊天', + 'menu.tabName.text': '图文', + 'menu.tabName.seat': '云席', + 'menu.tabName.quiz': '提问', + 'menu.tabName.iframe': '链接', + 'menu.tabName.previous': '往期', + 'menu.tabName.tuwen': '图文直播', + 'menu.tabName.qa': '问答', + 'menu.tabName.buy': '边看边买', + 'menu.tabName.invite': '邀请榜', + 'menu.tabName.members': '成员', + 'menu.tabName.vote': '投票', + 'menu.tabName.microActivity': '微活动', + 'menu.tabName.chapter': '章节', + + 'multiMeeting.other': '其它会场', + + 'member.self': '我', + 'member.failTips': '网络异常,成员加载失败,请', + 'member.failTips.retry': '点击重试', + + 'microActivity.wechatScan': '微信扫码参与该互动', + + 'invite.rank': '排行', + 'invite.rank.people.number': '邀请人数', + 'invite.rank.people': '人', + 'invite.rank.me': '我', + 'invite.rank.no.more': '没有更多了~', + 'invite.rank.loaded.failed': '榜单正在更新中,请稍等~', + 'invite.rank.invited': '你已邀请${number}人', + 'invite.rank.invite.btn': '去邀请', + 'invite.rank.click.btn.desc': '点击右侧按钮,邀请微信好友观看', + 'invite.rank.desc1': '继续保持!', + 'invite.rank.desc2': '继续保持,还差${number}人被第二名超越', + 'invite.rank.desc3': '距离第一名只差${number}人', + + 'invite.poster.num.text': '邀请人数', + 'invite.poster.not.on.the.list': '未上榜', + 'invite.poster.go.invite': '冲榜', + 'invite.poster.no.data': '暂无邀请,赶快抓住机会上榜吧!', +}; diff --git a/src/assets/lang/i18ns/english.ts b/src/assets/lang/i18ns/english.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab9204d58faa7bfbdaae7f8f4245edd5656fd89b --- /dev/null +++ b/src/assets/lang/i18ns/english.ts @@ -0,0 +1,474 @@ +import { LangConfig } from '../types'; + +/** + * 英文语言配置 + */ +export const englishLang: LangConfig = { + 'watchCore.error.uploadImage': 'Upload Image Fail!', + + 'auth.button.none': 'Enter', + 'auth.button.phone': 'Member entrance', + 'auth.button.pay': 'Pay to watch', + 'auth.button.info': 'Register to watch', + 'auth.button.code': 'Fill in the captcha', + 'auth.button.login': 'Login in to watch', + 'auth.button.workWx': 'Wecom entrance', + 'auth.button.playback': 'Enter', + 'auth.code.placeholder': 'Please enter verification information', + 'auth.info.registered.entrance': 'I have registered, go directly to', + 'auth.info.registered.title': 'I have registered', + 'auth.phone.placeholder': 'Please enter verification information', + 'auth.pay.tips': 'Need to pay for watching', + 'auth.pay.scan': 'Scan with WeChat and pay', + 'auth.pay.hasPay': 'Paid >', + 'auth.pay.wechatPrefix': 'Please use the paid', + 'auth.pay.wechatSuffix': 'WeChat and scan the QR code to watch', + 'auth.pay.clickPay': 'Click to buy', + 'auth.pay.audience': 'Paid viewers ? please', + 'auth.pay.clickHere': 'click here', + 'auth.pay.wechat': 'Wechat Pay', + 'auth.workWeixin.title': 'Work wechat login', + 'auth.workWeixin.qrcodeLoading': 'Qrcode loading', + 'auth.error.checkProtocol': 'Please tick the privacy statement', + 'auth.error.unknown': 'unknown', + 'auth.error.codeEmpty': 'Please enter verification information', + 'auth.error.codeError': 'The verification code entered is incorrect', + 'auth.error.smsCodeError': 'SMS verification code error', + 'auth.error.phoneEmpty': 'Please enter verification information', + 'auth.error.payTimeout': 'Payment Timeout!', + 'auth.error.phoneNotExistent': 'Wrong member code entered', + 'auth.error.phoneNotRegister': 'Phone number not registered', + 'auth.error.improveInfo': 'Please complete the information', + 'auth.error.externalFail': + 'This page uses external authorization. Please use the authorized entry to view!', + 'auth.error.directFail': + 'Error, this page uses independent authorization, please use the authorization portal to watch', + 'auth.error.workWeixinExpire': + 'The view channel condition have expired, and you cannot continue to watch, please contact the administrator', + 'auth.error.workWeixinNotAllow': 'Please open this link within Wecom to watch the live stream.', + 'auth.error.workWeixinNotStaff': 'Login failed, please confirm staff identity.', + 'auth.enter': 'Enter', + + 'ask.placeholderMsg': 'Hi, what can I do for you', + + 'base.frequentOperation': 'Your operation is too frequent, please try again later', + 'base.region': 'Region', + 'base.ok': 'OK', + 'base.backTop': 'Top', + 'base.people': 'people', + 'base.error.payFail': 'The payment is failed', + 'base.confirm': 'Confirm', + 'base.cancel': 'Cancel', + 'base.ignore': 'Ignore', + 'base.warmTips': 'Warm Tips', + 'base.enterLive': 'Enter', + 'base.tips': 'Tips', + 'base.me': 'me', + 'base.more': 'More', + 'base.notAuthorized': 'You are not allowed to access this page', + 'base.changeLang': 'Language switch', + 'base.message': 'Message', + 'base.packUp': 'Pack up', + 'base.spreadOut': 'Open', + 'base.copy': 'Copy', + 'base.fullText': 'Full text', + 'base.translateFinish': 'Completed', + 'base.camera': 'Camera', + 'base.popup.supportDragUp': 'Support drag up', + 'base.popup.understood': 'I see', + + 'channel.descEmpty': 'No live broadcast introduction~', + + 'call.connect': 'Video call', + 'connectMic.deviceCheck': 'Equipment inspection', + 'connectMic.openVideoTips': 'Lecturer started a video call', + 'connectMic.openAudioTips': 'Lecturer started a voice call', + 'connectMic.joinConnect': 'Click the button to join the call', + 'connectMic.applying': 'Requesting a call', + 'connectMic.applying2': 'applying', + 'connectMic.applyConnect': 'Apply for a call', + 'connectMic.applyConnect.video': 'Apply video call', + 'connectMic.applyConnect.audio': 'Apply audio call', + 'connectMic.cancelApply': 'Cancel Apply', + 'connectMic.endConnect': 'End call', + 'connectMic.keepConnect': 'Keep calling', + 'connectMic.me': 'Me', + 'connectMic.teacher': 'Teacher', + 'connectMic.confirm1': 'Are you sure to end the current call', + 'connectMic.confirm2': 'Sure end call?', + 'connectMic.teacherHangUp': 'The call has ended. You can request again', + 'connectMic.agreement': + 'It is necessary to shoot and collect portrait information during video connection', + 'connectMic.error.getDeviceFail': + 'Unable to get device access or no device, please check and try again!', + 'connectMic.error.overLimit': + 'Failed to register. The number of people currently registered has reached the maximum', + + 'chat.empty': 'Nothing in chat room ~', + 'chat.connectError': 'Chatroom disconnect, you cannot chat with others, refresh now?', + 'chat.norMore': 'No more updates', + 'chat.loadError': 'Message failed to load', + 'chat.closeTips': + 'As the channel has not been used for a long time, the chat room is temporarily closed till the next broadcast.', + 'chat.transformTip': 'Chatroom occupied, video only.', + 'chat.onlyHost': 'Presenter', + 'chat.onlyHost2': 'Presenter', + 'chat.lookAll': 'Look all', + 'chat.sendImage': 'Send picture', + 'chat.setNick': 'Rename', + 'chat.setNick.title': 'Rename', + 'chat.setNick.placeholder': 'Please set your nickname', + 'chat.setNick.success': 'Successfully set', + 'chat.setNick.tips1': 'Click to', + 'chat.setNick.tips2': 'set nickname', + 'chat.setNick.tips3': 'and join live chat', + 'chat.setNick.error.empty': 'Please set your nickname', + 'chat.setNick.error.special': 'Nicknames contain special characters', + 'chat.send': 'Send', + 'chat.input.placeholder': 'Join the chat', + 'chat.input.placeholder2': 'Join the chat', + 'chat.input.placeholder3': 'Click to set nickname to participate in interaction', + 'chat.input.maxImageTips': 'Insert no more than ${maxCount} pictures at most', + 'chat.closeRoom': 'Chat room is closed', + 'chat.roomClosed': 'Chat room temporarily closed', + 'chat.roomReopened': 'Chat room reopened', + 'chat.reply': 'reply', + 'chat.illegalImage': 'Image illegal', + 'chat.moreMsg': 'Click to see more', + 'chat.moreMsg2': 'See new message', + + 'copy.link': 'Copy Link', + 'copy.success': 'Copy success', + 'copy.linkSuccess': 'The link is copied successfully. You can download it in the browser', + + 'doc.name': 'Document', + + 'donate.title': 'Gift', + 'donate.cash': 'Cash', + 'donate.cashReward': 'Reward', + 'donate.custom': 'Or', + 'donate.custom2': 'Arbitrary amount', + 'donate.placeholder': 'Please enter the amount', + 'donate.yuan': '¥', + 'donate.random': 'Random', + 'donate.goodAction': 'send', + 'donate.cashAction': 'gift', + 'donate.free': 'Free', + 'donate.button': 'Confirm', + 'donate.name.six': '666', + 'donate.name.bear': 'Bear', + 'donate.name.microphone': 'Microphone', + 'donate.name.villa': 'Villa', + 'donate.name.coffee': 'Coffee', + 'donate.name.trophy': 'Trophy', + 'donate.name.star': 'Star', + 'donate.name.applause': 'Applause', + 'donate.name.yacht': 'Yacht', + 'donate.name.rocket': 'Rocket', + 'donate.name.like': 'Like', + 'donate.name.fireworks': 'Fireworks', + 'donate.name.crown': 'Crown', + 'donate.name.cars': 'Cars', + 'donate.name.goldenEggs': 'Golden Eggs', + 'donate.name.diamonds': 'Diamonds', + 'donate.name.plane': 'Plane', + 'donate.name.flower': 'Flower', + 'donate.confirm': 'Confirm donate', + 'donate.point.getFail': 'Failed to obtain integral', + 'donate.point.myPoint': 'My integral: ', + 'donate.error.notChoice': 'Please select a gift for reward', + 'donate.error.fail': 'donate failed', + 'donate.error.unknown': 'donate happen unknow error', + 'donate.error.payTimeOut': 'Payment Timeout', + 'donate.error.payFail': 'The payment is failed', + 'donate.error.notZero': 'The input amount cannot be 0', + 'donate.error.mini': 'Please enter an amount greater than or equal to ${price}', + 'donate.hideAnimation': 'Hide animation', + 'donate.hideAnimation.tips': 'Other users gift effects have been blocked', + 'donate.showAnimation': 'Show animation', + 'donate.showAnimation.tips': 'Other users gift effects have been restored', + 'donate.spot': 'Spot', + + 'enroll.text': 'Sign up', + 'enroll.enrolledTips': + 'You have already signed up, the content currently filled in will overwirte the previous', + 'enroll.entrance': 'I have signed up, go directly to', + 'enroll.auditing': 'Under review', + 'enroll.loginTitle': 'Login', + 'enroll.error.emailUnenroll': + 'The phone number has not been registered yet, please register first to watch the live', + 'enroll.error.phoneUnenroll': + 'The email has not been registered yet, please register first to watch the live', + 'enroll.error.smsCodeError': 'Incorrect SMS verification code', + + 'form.name.phoneNumber': 'Phone number', + 'form.name.verifyCode': 'Verification code', + 'form.placeholder.phoneInput': 'Please enter the phone number', + 'form.placeholder.smsVerify': 'SMS verification code', + 'form.placeholder.imageVerify': 'Please enter the verification code', + 'form.placeholder.enterCountry': 'Please enter the country name', + 'form.error.phoneNumberRequired': 'Please enter the phone number', + 'form.error.phoneNumberError': 'Please enter the correct mobile phone number', + 'form.error.smsVerifyRequired': 'Please enter SMS verification code', + 'form.error.imageCaptchaError': 'Image verification code error', + 'form.error.emailRequired': 'Please enter the email address', + 'form.error.emailError': 'Please enter the correct email address', + 'form.error.areaError': 'Please select the correct region', + 'form.error.onlyCnAnEn': 'Only supports Chinese or English', + 'form.getSmsVerify': 'Get verification code', + 'form.resend': 'Resend', + 'form.radio': 'single choice', + 'form.checkbox': 'multiple choice', + 'form.select.multiPrefix': '', + 'form.select.multiSuffix': ' items selected', + 'form.uploadImage.prefixTips': 'Currently uploading ', + 'form.uploadImage.suffixTips': ' sheets', + + 'global.singleSession.error.unknow': 'Account has logged in other place', + 'global.singleSession.error.relogin': 'Account has logged in other place', + + 'interact.announcement.title': 'Announcement', + 'interact.announcement.titleList': 'Announcement List', + 'interact.announcement.titleDetail': 'Announcement', + 'interact.answerCard.singleChoice': '【Single Choice】', + 'interact.answerCard.multiChoice': '【Multiple Choice】', + 'interact.answerCard.title': 'Answer Sheet', + 'interact.answerCard.checkResultTitle': 'Result', + 'interact.answerCard.checkAnswerTitle': 'Answers', + 'interact.answerCard.notSubmitted': 'Not Submitted', + 'interact.checkIn.title': 'CheckIn', + 'interact.feedBack.title': 'feed Back', + 'interact.lottery.result': 'results', + 'interact.lottery.record': 'Records', + 'interact.lottery.checkList': 'Check out the list', + 'interact.lottery.submitInfo': 'Fill in the information', + 'interact.product.jobDetail': 'Job Details', + 'interact.questionnaire.title': 'Questionnaire', + 'interact.questionnaire.result': 'Questionnaire Result', + 'interact.questionnaire.result.list': 'List', + 'interact.vote.title': 'Vote', + 'interact.task.title': 'Task', + 'interact.task.me': 'My task', + 'interact.tuwen.tuwenMode': 'Pictures&Text', + 'interact.tuwen.picMode': 'Only Pictures', + 'interact.tuwen.total': '', + 'interact.tuwen.totalCount': ' Records', + 'interact.scoreRecord.title': 'Score record', + 'interact.enrollLottery.title': 'Enroll lottery', + + 'liveBooking.book': 'Book', + 'liveBooking.bookLive': 'Book', + 'liveBooking.booked': 'Booked', + 'liveBooking.writeForm': 'Fill in the appointment information', + 'liveBooking.confirm': 'Confirm appointment', + 'liveBooking.bookingSuccess': 'The appointment is successful, please wait for notification', + 'liveBooking.cancelSuccess': 'Cancel the appointment successfully', + 'liveBooking.smsWrong': 'The verification code is wrong, please re-enter', + 'liveBooking.fail': 'Booking fail', + 'liveBooking.subscribe1': 'Long press the QR code to follow', + 'liveBooking.subscribe2': "and you 'll receive a reminder", + 'liveBooking.subscribe3': ' 15 minutes ', + 'liveBooking.subscribe4': 'before this webcast.', + 'liveBooking.welcome': 'Welcome to the live', + + 'liveCountDown.text': 'Countdown', + + 'liveStatus.live': 'Live', + 'liveStatus.waiting': 'Waiting', + 'liveStatus.end': 'Not Live', + 'liveStatus.stop': 'Pause', + 'liveStatus.playback': 'Replaying', + 'liveStatus.unStart': 'Not Started', + + 'live.audioLive': 'Audio Live', + 'live.goLive': 'Live broadcast has started', + + 'layout.fullscreen': 'Full screen', + 'layout.fullscreenCancel': 'Full screen off', + + 'pageError.feedBack': + 'You can check your channel number first, then feed it back to the administrator, and you can deal with it later.', + 'pageError.channelNotExist': 'Channel number does not exist', + 'pageError.channelClosed': 'Channel is closed', + 'pageError.mobileChannelClosed': + 'This live broadcast only supports PC viewing, please use PC to browse the following address', + + 'player.play.button.text': 'Click to enter', + 'player.play': 'Play', + 'player.pause': 'Pause', + 'player.refresh': 'Refresh', + 'player.switchScreen': 'Switch', + 'player.manualPlayTips1': 'Click the', + 'player.manualPlayTips2': 'play button', + 'player.manualPlayTips3': 'to enter the live broadcast', + 'player.rtc.paused': 'Live video and calling stopped', + 'player.volumeTips1': 'Do you allow the browser to play sound?', + 'player.volumeTips2': 'Yes', + 'player.barrage': 'Barrage', + 'player.barrage.show': 'Danmaku on', + 'player.barrage.close': 'Danmaku off', + 'player.openBarrage': 'Barrage on', + 'player.closeBarrage': 'Barrage off', + 'player.lineSwitch': 'Line', + 'player.lineSetting': 'Switch Line', + 'player.line': 'line', + 'player.quality': 'quality', + 'player.qualitySetting': 'Switch Quality', + 'player.quality.auto': 'Auto', + 'player.quality.sd': 'SD', + 'player.quality.hd': 'HD', + 'player.quality.fhd': 'FHD', + 'player.rate': 'Rate', + 'player.historyTips': 'Locating the Playback progress ', + 'player.restart': 'Restart', + 'player.latencyMode.name': 'Mode', + 'player.latencyMode.low': 'Undelayed', + 'player.latencyMode.normal': 'Normal', + 'player.latency.error': 'Undelayed playmode error, you are now in normal playmode', + 'player.mode.audio': 'Audio', + 'player.mode.video': 'Video', + 'player.audio.ing': 'Audio playing', + + 'playback.list': 'Playback', + 'playback.playing': 'Playing', + 'playback.startTime': 'start time:', + 'playback.chapter': 'Chapter', + 'playback.no.more': 'No more updates', + + 'trial.play.finish.tip': 'Free preview expired', + 'trial.player.start.tip': 'This live is a paid live.You can tyr it for ${time} minute.', + 'trial.player.end.tip': + 'The trial is over. If you think this live broadcast is good, please click pay to watch', + + 'redpack.receive.name': '', + 'redpack.finish': '. The red envelope has been taken out', + 'redpack.normal.get': 'got the red envelope from the normal red envelope', + 'redpack.password.get': 'got the red envelope from the password red envelope', + 'redpack.rain.get': 'got the red envelope from the red envelope rain', + 'redpack.rain.finish': ' The red envelope rain has been taken out', + 'redpack.type.normal': 'normal redpack', + 'redpack.type.password': 'password redpack', + 'redpack.type.rain': 'redpack rain', + 'redpack.type.alipayPassword': 'Alipay password red envelope', + 'redpack.sendMsg': 'send ', + 'redpack.receive': 'receive', + + 'redpack.name': 'Redpack', + 'redpack.send.title': 'Red Packet', + 'redpack.send.sum': 'Sum', + 'redpack.send.sum.placeholder': 'Fill in the total amount', + 'redpack.send.sum.unit': 'yuan', + 'redpack.send.num': 'Number', + 'redpack.send.num.placeholder': 'Fill in the number', + 'redpack.send.num.unit': 'packet', + 'redpack.blessing.placeholder': 'Congratulations on making a fortune', + 'redpack.desc': '* The red packet is a random red packet with a total amount of more than 1 yuan', + 'redpack.send.btn.text': 'Sure', + 'redpack.single.amount.limit': 'The amount should not exceed {amount} yuan', + 'redpack.single.num.limit': 'The number of red packets should not exceed ${count}', + 'redpack.equal.amount.limit': + 'The minimum split amount is greater than or equal to ${average} yuan', + 'redpack.bad.words': 'There are sensitive words ', + 'redpack.send.amount.low': 'The amount should not be less than 1 yuan', // 发红包限制 + 'redpack.greeting.long': 'Blessing words too long', + 'redpack.unsupported': 'The red packets type is not supported', + + 'risk.confirm.letter.agree': 'Agree and watch the live broadcast', + 'risk.confirm.letter.disagree': 'Disagree', + 'risk.confirm.letter.viewAgreement': 'View Agreement', + + 'seminar.host': 'Host', + + 'share.title': 'Share', + 'share.wxScan': 'Scan code and share to WeChat', + 'share.QQ': 'QQ', + 'share.QZone': 'QZone', + 'share.weibo': 'Weibo', + + 'unit.day': 'D', + 'unit.hour': 'H', + 'unit.minute': 'M', + 'unit.second': 'S', + + 'watch.liveTime': 'Live start time', + 'watch.pvSuffix': 'views', + + 'welcome.text': 'welcome', + 'welcome.wait': 'and', + 'welcome.peopleJoin': 'people entered', + 'welcome.join': 'join', + + 'weixin.scanToPay': 'Scan with Wechat and Pay', + 'weixin.pay.success': 'Payment success', + 'weixin.error.payTimeout': 'Payment timeout', + 'weixin.error.toWechatPay': 'Please pay on WeChat', + 'weixin.error.openInClient': 'Please open it at WeChat', + + 'withdraw.name': 'Withdraw', + 'withdraw.apply.popup.title': 'confirm', + 'withdraw.apply.amount': 'amount', + 'withdraw.apply.amount.all': 'All', + 'withdraw.apply.amount.most': 'Can withdraw up to ¥${amount}', + 'withdraw.apply.amount.most.merchant': 'The maximum single withdrawal amount is ¥${amount}', + 'withdraw.apply.username': 'username', + 'withdraw.apply.tips': + '* Balance should be more than ¥${amount} Yuan for withdrawing. Withdrawal will be in your Wechat wallet within ${day} working days', + 'withdraw.apply.btn.text': 'Withdrawal', + 'withdraw.apply.success.msg': 'Successful application for withdrawals', + 'withdraw.apply.rules.amount.required': 'Fill in the total amount', + 'withdraw.apply.rules.amount.low': 'The amount should not be less than 1 yuan', + + 'withdraw.error.noUser': 'The WeChat user is not found', + 'withdraw.error.getCashExceed': 'The amount exceeds the user balance', + 'withdraw.error.getCashMoreThan1': 'The amount must be more than ¥ 1', + 'withdraw.error.getCashFailed': 'Failure of application record', + + 'withdraw.detail.popup.title': 'Balance', + 'withdraw.detail.btn.text': 'Withdrawal', + 'withdraw.redpack.receive.record': 'Receive history', + 'withdraw.redpack.receive.record.listName': 'Received', + 'withdraw.cash.receive.record': 'Cash withdrawal', + 'withdraw.cash.receive.record.listName': 'Withdrawal', + + 'menu.tabName.desc': 'Intro', + 'menu.tabName.chat': 'Chat', + 'menu.tabName.text': 'Image & Text', + 'menu.tabName.seat': 'Seat', + 'menu.tabName.quiz': 'Ask', + 'menu.tabName.iframe': 'Link', + 'menu.tabName.previous': 'Previous', + 'menu.tabName.tuwen': 'Tuwen Live', + 'menu.tabName.qa': 'Q & A', + 'menu.tabName.buy': 'Buy', + 'menu.tabName.invite': 'Ranking List', + 'menu.tabName.members': 'Members', + 'menu.tabName.vote': 'Vote', + 'menu.tabName.microActivity': 'MicroActivity', + 'menu.tabName.chapter': 'Chapter', + + 'multiMeeting.other': 'Breakout rooms', + + 'member.self': 'me', + 'member.failTips': 'Network error, members load failed,', + 'member.failTips.retry': 'click here to retry', + + 'microActivity.wechatScan': 'Scan the code to participate', + + 'invite.rank': 'Ranking', + 'invite.rank.people.number': 'Number of people', + 'invite.rank.people': ' people', + 'invite.rank.me': 'me', + 'invite.rank.no.more': 'No more', + 'invite.rank.loaded.failed': 'The list is being updated, please wait a moment~', + 'invite.rank.invited': 'You have invited ${number} people', + 'invite.rank.invite.btn': 'Invite', + 'invite.rank.click.btn.desc': 'Click the button on the right to invite your WeChat friends', + 'invite.rank.desc1': 'Keep going!', + 'invite.rank.desc2': 'Keep going, or be overtaken by second place', + 'invite.rank.desc3': 'only ${number} people short of becoming first', + + 'invite.poster.num.text': 'invitees', + 'invite.poster.not.on.the.list': 'Not listed', + 'invite.poster.go.invite': 'Invite', + 'invite.poster.no.data': 'No invitation at the moment. Jump at the chance to on the list.', +}; diff --git a/src/assets/lang/i18ns/farsi.ts b/src/assets/lang/i18ns/farsi.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c79dd5b1fa791795bea0f926ef449c29736f0c9 --- /dev/null +++ b/src/assets/lang/i18ns/farsi.ts @@ -0,0 +1,9 @@ +import { LangConfig } from '../types'; +import { englishLang } from './english'; + +/** + * 波斯语配置 + */ +export const farsiLang: LangConfig = { + ...englishLang, +}; diff --git a/src/assets/lang/i18ns/index.ts b/src/assets/lang/i18ns/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d74930359a93ee128e592a0f7164c80678a1e853 --- /dev/null +++ b/src/assets/lang/i18ns/index.ts @@ -0,0 +1,21 @@ +import { LangType } from '../lang-enum'; +import { LangI18nData } from '../types'; +import { arabicLang } from './arabic'; +import { chinaLang } from './china'; +import { englishLang } from './english'; +import { farsiLang } from './farsi'; +import { portugueseLang } from './portuguese'; +import { russianLang } from './russian'; +import { spanishLang } from './spanish'; + +export const i18nData: LangI18nData = { + [LangType.Chinese]: chinaLang, + [LangType.English]: englishLang, + [LangType.Arabic]: arabicLang, + [LangType.Farsi]: farsiLang, + [LangType.Portuguese]: portugueseLang, + [LangType.Russian]: russianLang, + [LangType.Spanish]: spanishLang, +}; + +export default i18nData; diff --git a/src/assets/lang/i18ns/portuguese.ts b/src/assets/lang/i18ns/portuguese.ts new file mode 100644 index 0000000000000000000000000000000000000000..ad4c00a36b2a333e8f6c37e8bab52754fe43c893 --- /dev/null +++ b/src/assets/lang/i18ns/portuguese.ts @@ -0,0 +1,9 @@ +import { LangConfig } from '../types'; +import { englishLang } from './english'; + +/** + * 葡萄牙语配置 + */ +export const portugueseLang: LangConfig = { + ...englishLang, +}; diff --git a/src/assets/lang/i18ns/russian.ts b/src/assets/lang/i18ns/russian.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ae7c3e84ffd31654b832743bbf7ff12e32d9c99 --- /dev/null +++ b/src/assets/lang/i18ns/russian.ts @@ -0,0 +1,9 @@ +import { LangConfig } from '../types'; +import { englishLang } from './english'; + +/** + * 俄罗斯语配置 + */ +export const russianLang: LangConfig = { + ...englishLang, +}; diff --git a/src/assets/lang/i18ns/spanish.ts b/src/assets/lang/i18ns/spanish.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e492095772959f23d043729b0549234d522652c --- /dev/null +++ b/src/assets/lang/i18ns/spanish.ts @@ -0,0 +1,9 @@ +import { LangConfig } from '../types'; +import { englishLang } from './english'; + +/** + * 西班牙语配置 + */ +export const spanishLang: LangConfig = { + ...englishLang, +}; diff --git a/src/assets/lang/index.ts b/src/assets/lang/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..471067fb2249645aedea17bd069eee92f6ae1b8c --- /dev/null +++ b/src/assets/lang/index.ts @@ -0,0 +1,53 @@ +import Vue from 'vue'; +import { LangType } from './lang-enum'; +import { i18nData } from './i18ns/index'; +import { LangKey, LangLocaleFn } from './types'; + +let locale: LangType | LangLocaleFn = LangType.Chinese; // 默认中文 + +/** 替换多语言中的字符串模板 */ +function replace(key: string, value: string, str: string): string { + return str.replace(new RegExp('\\${' + key + '}', 'g'), value); +} + +/** 格式化多语言 */ +function formatLang(lang: string): LangType { + const lowerLocale = lang && lang.toLowerCase && lang.toLowerCase(); + if (lowerLocale === 'zh_cn' || lowerLocale === 'zh-cn') { + return LangType.Chinese; + } + return lang as LangType; +} + +/** 转换多语言 */ +export function translate(key: LangKey, options: UniversalParams = {}): string { + let lang: LangType = LangType.Chinese; + + if (typeof locale === 'function') { + lang = formatLang(locale()); + } else if (typeof locale === 'string') { + lang = formatLang(locale); + } + + const textList = i18nData[lang] || i18nData[LangType.Chinese]; + let text = textList[key]; + + if (options) { + const keys = Object.keys(options); + for (const key of keys) { + const value = options[key]; + text = replace(key, value, text); + } + } + + return text; +} + +export type LangInstallOptions = { + locale: LangType | LangLocaleFn; +}; + +export const i18nInstall = (vue: typeof Vue, options: LangInstallOptions) => { + Vue.prototype.$lang = translate; + locale = options.locale || locale; +}; diff --git a/src/assets/lang/lang-enum.ts b/src/assets/lang/lang-enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..e6cfce4fa10ec25e0b0c8b655dd4d7d3a5c6cfeb --- /dev/null +++ b/src/assets/lang/lang-enum.ts @@ -0,0 +1,23 @@ +/** + * @file 多语言相关枚举 + */ + +/** + * 语言类型枚举 + */ +export enum LangType { + /** 中文 */ + Chinese = 'zh_CN', + /** 英文 */ + English = 'en', + /** 俄语 */ + Russian = 'ru', + /** 葡萄牙语 */ + Portuguese = 'pt', + /** 阿拉伯语 */ + Arabic = 'ar', + /** 波斯语 */ + Farsi = 'fa', + /** 西班牙语 */ + Spanish = 'es', +} diff --git a/src/assets/lang/types.ts b/src/assets/lang/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6720b77251c82137f4326ec2d4d8a7042c6c204 --- /dev/null +++ b/src/assets/lang/types.ts @@ -0,0 +1,12 @@ +import { LangType } from './lang-enum'; +import { chinaLang } from './i18ns/china'; + +/** 某个多语言配置 */ +export type LangConfig = typeof chinaLang; + +export type LangKey = keyof LangConfig; + +/** 多语言匹配类型 */ +export type LangI18nData = Record; + +export type LangLocaleFn = () => LangType; diff --git a/src/assets/styles/animation.scss b/src/assets/styles/animation.scss new file mode 100644 index 0000000000000000000000000000000000000000..7448509c6046b08a1fa139a99117263768dcd164 --- /dev/null +++ b/src/assets/styles/animation.scss @@ -0,0 +1,88 @@ +// 渐变动画 +.fade-enter-active { + animation: fade-in .3s; +} +.fade-leave-active { + animation: fade-in .35s reverse; +} +@keyframes fade-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +// 底部滑出动画 +.slide-enter-active { + animation: slide-in .2s ease-in-out; +} +.slide-leave-active { + animation: slide-in .15s reverse ease-in-out; +} +@keyframes slide-in { + 0% { + transform: translate(0, 100%); + } + 100% { + transform: translate(0, 0); + } +} + +// 旋转动画 +@keyframes spin { + 0% { + transform: rotate(0); + } + 100% { + transform: rotate(360deg); + } +} + +// 放大 +@keyframes scale-animation { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.2); + } + 100% { + transform: scale(1); + } +} + +.g-transition-menu-right-enter-active { + animation: menu-right 0.3s cubic-bezier(0.76, 0, 0.24, 1); +} +.g-transition-menu-right-leave-active { + animation: menu-left 0.3s cubic-bezier(0.76, 0, 0.24, 1); +} +.g-transition-menu-left-leave-active { + animation: menu-right 0.3s cubic-bezier(0.76, 0, 0.24, 1) reverse; +} +.g-transition-menu-left-enter-active { + animation: menu-left 0.3s cubic-bezier(0.76, 0, 0.24, 1) reverse; +} +@keyframes menu-right { + 0% { + opacity: 0; + transform: translateX(100vw); + } + 100% { + opacity: 1; + transform: translateX(0); + } +} +@keyframes menu-left { + 0% { + opacity: 1; + transform: translateX(0); + } + 100% { + opacity: 0; + transform: translateX(-100vw); + } +} + diff --git a/src/assets/styles/fonts/ALIBABAFont.eot b/src/assets/styles/fonts/ALIBABAFont.eot new file mode 100644 index 0000000000000000000000000000000000000000..1139bf9cf51442e09dd1ad38d6a1ae47e58a632a Binary files /dev/null and b/src/assets/styles/fonts/ALIBABAFont.eot differ diff --git a/src/assets/styles/fonts/ALIBABAFont.otf b/src/assets/styles/fonts/ALIBABAFont.otf new file mode 100644 index 0000000000000000000000000000000000000000..1b5343ad14973fd1488a9d5409a0d71e37ce347a Binary files /dev/null and b/src/assets/styles/fonts/ALIBABAFont.otf differ diff --git a/src/assets/styles/fonts/ALIBABAFont.svg b/src/assets/styles/fonts/ALIBABAFont.svg new file mode 100644 index 0000000000000000000000000000000000000000..feddfbd03e4332368cd1585aa1c948bb1a6ca999 --- /dev/null +++ b/src/assets/styles/fonts/ALIBABAFont.svg @@ -0,0 +1,182 @@ + + + + +Created by FontForge 20200314 at Mon Apr 15 18:44:59 2019 + By www-data +copyright missing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/styles/fonts/ALIBABAFont.ttf b/src/assets/styles/fonts/ALIBABAFont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e0193f0cbc0a8cc363ed3b1536dc7e5c88fe8fd6 Binary files /dev/null and b/src/assets/styles/fonts/ALIBABAFont.ttf differ diff --git a/src/assets/styles/fonts/ALIBABAFont.woff b/src/assets/styles/fonts/ALIBABAFont.woff new file mode 100644 index 0000000000000000000000000000000000000000..cc64e7985a564de7d363b38d87fc8be7d06ee812 Binary files /dev/null and b/src/assets/styles/fonts/ALIBABAFont.woff differ diff --git a/src/assets/styles/function.scss b/src/assets/styles/function.scss new file mode 100644 index 0000000000000000000000000000000000000000..c34bac1ae01e13767aebf6027e1100f749d2e02c --- /dev/null +++ b/src/assets/styles/function.scss @@ -0,0 +1,7 @@ +@use 'sass:math'; +@use 'sass:map'; + +// 获取颜色在白色叠底下的透明级别颜色 +@function get-opacity-color($color, $level) { + @return mix(#FFFFFF, $color, math.percentage(math.div($level, 10))); +} diff --git a/src/assets/styles/global.scss b/src/assets/styles/global.scss new file mode 100644 index 0000000000000000000000000000000000000000..740118a75b00121278440582a05dc2fe4e58a1fb --- /dev/null +++ b/src/assets/styles/global.scss @@ -0,0 +1,122 @@ +body { + margin: 0; + font-family: 'Helvetica Neue', Helvetica, 'PingFang SC', 'Microsoft YaHei', Arial, sans-serif; + font-size: 14px; + line-height: 1; + word-break: break-word; + word-wrap: break-word; + background: #fff; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + -webkit-text-size-adjust: none; +} +body.g-body-locked { + overflow: hidden; +} +h1, h2, h3, h4, h5, h6, p, ul, ol, li, dl, dt, dd, input, textarea, button { + padding: 0; + margin: 0; +} +h1, h2, h3, h4, h5, h6 { + font-size: inherit; + font-weight: normal; +} +ul, ol { + list-style-type: none; +} +img { + vertical-align: middle; + border: none; +} +input, textarea { + border-radius: 0; +} +input, textarea, select, button { + outline: none; +} +input[disabled] { + opacity: 1; +} +input[type=button], +input[type=submit], +input[type=reset], +button { + -webkit-appearance: none; + border: none; +} + +/* 清浮动 */ +.g-clearfix:after { + display: block; + height: 0; + overflow: hidden; + clear: both; + visibility: hidden; + content: '.'; +} + +/* 单行内容,如有超出则以省略号结尾 */ +.g-singleline { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* 多行内容,只兼容webkit(默认 2 行) */ +.g-multiline { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +/** 文字不换行 */ +.g-text-nowrap { + white-space: nowrap; +} + +/* 行内垂直居中 */ +.g-vmid { + vertical-align: middle; +} + +/* 图片容器(填满不拉伸) */ +.g-img-cover { + background-repeat: no-repeat; + background-position: center center; + background-size: cover; +} +.g-img-contain { + background-repeat: no-repeat; + background-position: center center; + background-size: contain; +} + +/* 半透明遮罩层 */ +.g-mask { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 9999; + background: rgba(0, 0, 0, 0.6); +} + +/* 观看页背景图上的文字要加阴影,避免文字与背景颜色相似看不清 */ +.g-text-on-bg { + text-shadow: 0 0 2px rgba(0, 0, 0, 0.5); +} + +.plv-icon { + width: initial !important; + height: initial !important; +} + +@font-face { + font-family: ALIBABAFont; + src: url('./fonts/ALIBABAFont.eot'); + src: url('fonts/ALIBABAFont.woff') format('woff'), + url('fonts/ALIBABAFont.ttf') format('truetype'), + url('fonts/ALIBABAFont.svg') format('svg'); +} diff --git a/src/assets/styles/mixins.scss b/src/assets/styles/mixins.scss new file mode 100644 index 0000000000000000000000000000000000000000..325736869b3996218d35848362ba6f984af71961 --- /dev/null +++ b/src/assets/styles/mixins.scss @@ -0,0 +1,147 @@ +// 对于 iphone x 系列采用安全距离作为底部 padding +// $normalBottom, 若非 iphone x 系列,底部距离 +@mixin iphone-x-pb($normalBottom, $isReplace: true) { + @if $normalBottom == 0 { + $normalBottom: unquote('0px'); + } + + // 用安全距离替换原底部padding值。若原底部padding值大于底部安全距离,则不会被替换。 + @if $isReplace { + padding-bottom: $normalBottom; + .g-page--ios & { + @supports (padding-bottom: env(safe-area-inset-bottom)) or (padding-bottom: constant(safe-area-inset-bottom, $normalBottom)) { + padding-bottom: unquote('max(constant(safe-area-inset-bottom, #{$normalBottom}), #{$normalBottom})'); + padding-bottom: unquote('max(env(safe-area-inset-bottom, #{$normalBottom}), #{$normalBottom})'); + } + } + } @else { + // 安全距离与原底部padding值叠加 + // ios < 11.2 + padding-bottom: calc(constant(safe-area-inset-bottom) + #{$normalBottom}); + // ios >= 11.2 + padding-bottom: calc(env(safe-area-inset-bottom) + #{$normalBottom}); + // 兼容不支持calc(env(safe-area-inset-bottom)和calc(constant(safe-area-inset-bottom)这两种写法的浏览器,假设安全距离为0,设置padding-bottom为传出的值 + @supports not(padding-bottom: calc(env(safe-area-inset-bottom) + #{$normalBottom}) or (padding-bottom: calc(constant(safe-area-inset-bottom, $normalBottom) + #{$normalBottom}))) { + padding-bottom: $normalBottom; + } + } +} + +@mixin clear-input-style { + background: none; + border: none; + border-radius: 0; + outline: none; + &::-webkit-input-placeholder { + color: #9E9E9E; + } + &::placeholder { + color: #9E9E9E; + } + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none !important; + margin: 0; + } + &[type=number] { + -moz-appearance: textfield; + } + &[disabled] { + opacity: 1; + } + &[type=button], + &[type=submit], + &[type=reset] { + -webkit-appearance: none; + background: #fff; + } +} + +@mixin scrollbar($scrollbar-color: null) { + &::-webkit-scrollbar { + width: 6px; + } + &::-webkit-scrollbar-thumb { + @if ($scrollbar-color) { + background-color: $scrollbar-color; + } + border-radius: 3px; + } +} + +// 隐藏滚动条 +@mixin hidden-scrollbar { + &::-webkit-scrollbar { + display: none; + } +} + +@mixin scroll-style { + &::-webkit-scrollbar { + width: 12px; + height: 12px; + cursor: pointer; + } + &::-webkit-scrollbar-thumb { + background: #CFD8DC; + border: 2px solid #EDF1F7; + border-radius: 8px; + } + &::-webkit-scrollbar-track-piece { + background: #EDF1F7; + } +} + +// 单行内容超出省略 +@mixin singleline-text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// 多行内容超出省略 +@mixin multiline-text($lineCount: 2) { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: $lineCount; +} + +// 利用缩放生成0.5px 边框 +@mixin minor-border($color: #e1e1e1) { + &::after { + position: absolute; + top: 0; + left: 0; + box-sizing: border-box; + display: block; + width: 200%; + height: 200%; + pointer-events: none; + content: ''; + border: 1px solid $color; + border-radius: 50%; + transform: scale(0.5); + transform-origin: 0 0; + } +} + +$others-font: 'Helvetica Neue', helvetica, 'PingFang SC', 'Microsoft YaHei', arial, sans-serif; +$mobile-font: 'PingFangSC-Regular', 'Roboto', $others-font; +@mixin mobile-font($type: regular, $font-size: 12px, $font-weight: normal) { + @if $type == regular { + font: $font-weight $font-size $mobile-font; + } @else if $type == medium { + font: $font-weight $font-size $mobile-font; + } @else if $type == semibold { + font: $font-weight $font-size $mobile-font; + } +} + + +// 混入 alibaba 字体 +@mixin alibaba-font($font-size: 12px, $font-weight: normal) { + font: $font-weight $font-size ALIBABAFont, $mobile-font; +} + diff --git a/src/assets/styles/mobile.scss b/src/assets/styles/mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..e4cbe8ca7782692cbcf37d887bb602d73f447cc8 --- /dev/null +++ b/src/assets/styles/mobile.scss @@ -0,0 +1,5 @@ +html, body { + width: 100%; + height: 100%; + overflow: hidden; +} diff --git a/src/assets/styles/pc-response.scss b/src/assets/styles/pc-response.scss new file mode 100644 index 0000000000000000000000000000000000000000..095de12328008051f8ab8c6ac96d4531edc8f82e --- /dev/null +++ b/src/assets/styles/pc-response.scss @@ -0,0 +1,11 @@ +// PC 端响应式样式 + +.g-aside-font-size { + font-size: 16px; +} + +@media (max-width: 1920px) { + .g-aside-font-size { + font-size: 14px !important; + } +} diff --git a/src/assets/styles/pc.scss b/src/assets/styles/pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..56865f9c6e06669c0b481ffe6386c1ff61b7b522 --- /dev/null +++ b/src/assets/styles/pc.scss @@ -0,0 +1,10 @@ +@import './pc-response.scss'; + +.g-outside { + min-width: 1rem; +} +.g-boundary { + width: 1rem; + margin-right: auto; + margin-left: auto; +} diff --git a/src/assets/styles/portrait.scss b/src/assets/styles/portrait.scss new file mode 100644 index 0000000000000000000000000000000000000000..a8bcdcba2a7f4f9a32f95dc44da6873a6bd3cc15 --- /dev/null +++ b/src/assets/styles/portrait.scss @@ -0,0 +1,5 @@ + +// 竖屏主体色 +$--portrait-primary-color: #FFD16B; +// 竖屏弹窗内线条颜色 +$--portrait-popper-border: rgba(255, 255, 255, .1); diff --git a/src/assets/styles/variables.scss b/src/assets/styles/variables.scss new file mode 100644 index 0000000000000000000000000000000000000000..1bf5b8d5d505c521adbc182345cdbcce4ae64912 --- /dev/null +++ b/src/assets/styles/variables.scss @@ -0,0 +1,115 @@ +@use 'sass:map'; +@import './function.scss'; +@import './portrait.scss'; + +// 最高层级的 z-index +$--zIndex-top: 10001; +// 一般挂件的 z-index +$--zIndex-pendant: 2000; + +$--color-white: #FFFFFF !default; // 白色 +$--color-black: #000000 !default; // 黑色 +$--color-primary: #3082FE !default; // 主题色 +$--color-primary-portrait: #FFA611 !default; // 竖屏主题色 + +// #458ffe +$--color-primary-light-1: get-opacity-color($--color-primary, 1); +// #599bfe +$--color-primary-light-2: get-opacity-color($--color-primary, 2); +// #6ea8fe +$--color-primary-light-3: get-opacity-color($--color-primary, 3); +// #83b4fe +$--color-primary-light-4: get-opacity-color($--color-primary, 4); +// #98c1ff +$--color-primary-light-5: get-opacity-color($--color-primary, 5); +// #accdff +$--color-primary-light-6: get-opacity-color($--color-primary, 6); +// #c1daff +$--color-primary-light-7: get-opacity-color($--color-primary, 7); +// #d6e6ff +$--color-primary-light-8: get-opacity-color($--color-primary, 8); +// #eaf3ff +$--color-primary-light-9: get-opacity-color($--color-primary, 9); + +// 背景色 +$--bg-color-disabled: #E9ECEE !default; // 不可用 +$--bg-color-portrait-popup: #262523 !default; // 竖屏下的弹层背景色 +$--bg-color-portrait-disabled: #CDCDCD !default; // 竖屏下的不可用 + +// 字体色 +$--font-color-primary: #333333 !default; // 默认字体颜色 +$--font-color-disabled: #9E9E9E !default; // 不可用 +$--font-color-info: #999999 !default; // 信息 +$--font-color-error: #ff5722 !default; // 异常 + +// 边框色 +$--border-color-primary: #EDEDEF !default; // 常规边框色 +$--border-color-error: #ff5722 !default; // 异常状态边框色 + +// 阴影 +$--shadow-dialog: 0 0 16px 0 rgba(0, 0, 0, 0.08) !default; // 对话框阴影 +$--shadow-popper: 0 6px 12px rgb(0, 0, 0, 0.1) !default; // 弹层阴影 + +// 表单相关样式变量 +$--form-input-height: 40px !default; // 输入框高度 +$--form-input-height-small: 30px !default; // 输入框高度 - small + +$--form-input-padding: 0 16px !default; // 输入框内边距 +$--form-input-padding-small: 0 8px !default; // 输入框内边距 - small + +$--form-input-font-size: 14px !default; // 输入框字体大小 +$--form-input-font-size-small: 12px !default; // 输入框字体大小 - small + +$--form-input-border-color: #d8d8d8 !default; // 输入框边框颜色 +$--form-input-border-color-error: $--border-color-error !default; // 输入框异常边框色 + +$--form-input-bg: $--color-white !default; // 输入框背景色 +$--form-input-bg-error: #ffe9e9 !default; // 输入框异常背景色 + +// 表单相关样式变量(移动端) +$--mobile-form-input-sizes: ( + 'default': ( + 'height': 48px, + 'padding': 0 16px, + 'font-size': 16px, + 'radius': 24px, + ), + 'medium': ( + 'height': 40px, + 'padding': 0 16px, + 'font-size': 14px, + 'radius': 20px, + ), + 'small': ( + 'height': 36px, + 'padding': 0 12px, + 'font-size': 14px, + 'radius': 18px, + ), +) !default; + +$--mobile-form-input-themes: ( + 'default': ( + 'border-color': #f6f6f6, + 'background': #f6f6f6, + 'font-color': $--font-color-primary, + ), + 'error': ( + 'border-color': $--border-color-error, + 'background': #f6f6f6, + 'font-color': $--font-color-primary, + ), + 'dark': ( + 'border-color': #1a1b1f, + 'background': #1a1b1f, + 'font-color': $--color-white, + ), +) !default; + +// 播放器控制栏相关样式变量 +// 播放器面板背景 +$--player-control-panel-bg: rgba(0, 0, 0, 0.65) !default; + + +$--finance-primary-text-color: rgb(208, 172, 82); +$--finance-primary-bg-color: rgb(217, 186, 108); diff --git a/src/assets/utils/array.ts b/src/assets/utils/array.ts new file mode 100644 index 0000000000000000000000000000000000000000..441edfbd09466ec15210bdc88fc8ee8d288dc65b --- /dev/null +++ b/src/assets/utils/array.ts @@ -0,0 +1,8 @@ +/** + * 创建字符串元组 + */ +export const tupleString = (...args: T): T => args; + +export function isArray(target: unknown): target is T { + return Array.isArray(target); +} diff --git a/src/assets/utils/browser.ts b/src/assets/utils/browser.ts new file mode 100644 index 0000000000000000000000000000000000000000..c7e9227e45ebff9523258c69a553209a61bad3bf --- /dev/null +++ b/src/assets/utils/browser.ts @@ -0,0 +1,71 @@ +import { getCurrentUAInfo } from '@just4/ua-info'; + +const ua = navigator.userAgent.toLowerCase(); + +const uaInfo = getCurrentUAInfo(); + +/** 是否移动端 */ +export const isMobile = uaInfo.isPortable; + +/** 是否微信(非企业微信) */ +export const isWeixin = uaInfo.client.isWx; + +/** 是否企业微信 */ +export const isWorkWeixin = uaInfo.client.isWxWork; + +/** 是否为 QQ 浏览器 */ +export const isQQBrowser = uaInfo.client.isQQBrowser; + +/** 是否为 UC 浏览器 */ +export const isUCBrowser = uaInfo.client.isUCBrowser; + +/** 是否为百度 app */ +export const isBaiduApp = uaInfo.client.isBaiduApp; + +/** 是否安卓 */ +export const isAndroid = uaInfo.os.isAndroid; + +/** 是否 ios */ +export const isIOS = uaInfo.os.isIOS; + +/** 是否被 iframe */ +export const isIframe = window.self !== window.top; + +/** 获取 IOS 版本 */ +export function getIosVersion() { + const ver = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/); + if (ver) { + return parseInt(ver[1], 10); + } else { + return null; + } +} + +/** 针对 iOS 13 以上 iPad(ua 与桌面端 Mac OS 近似) */ +export const isDesktopIPad = /mac os x/i.test(ua) && 'ontouchend' in document; + +/** IE 浏览器版本号,不符合该浏览器特征时返回空字符串 */ +const ieVersion = /\bMSIE\s(\d+)/i.test(ua) || /\bTrident\/.*;\srv:(\d+)/.test(ua) ? RegExp.$1 : ''; + +/** 是否为低版本 IE */ +export const isLowerIE = ieVersion && Number(ieVersion) <= 10; + +/** + * 判断是否模拟 PC 的 UA + * + * 通过媒体查询判断,不通过 UA 和 navigator.platform + * - pointer:coarse 表示当前设备的指针是不精确的。手机不支持鼠标,只支持触摸 + * - hover: none 表示当前设备不支持 mouse hover 处理 + */ +function judgeIsMockPC(): boolean { + const matchMedia = window.matchMedia; + if (matchMedia) { + const isCoarsePointer = matchMedia('(pointer:coarse)').matches; + const isNotHover = matchMedia('(hover: none)').matches; + return isCoarsePointer && isNotHover && !isMobile; // 内部逻辑:支持触摸,不支持鼠标悬浮,UA 判断又不是移动端 => 模拟 PC + } + return false; +} + +/** 是否模拟 UA 为 PC */ +export const isMockPc = judgeIsMockPC(); diff --git a/src/assets/utils/copy.ts b/src/assets/utils/copy.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd57768577ff40eb1db5a8f08b887ff2b1962a03 --- /dev/null +++ b/src/assets/utils/copy.ts @@ -0,0 +1,43 @@ +import { toast } from '@/hooks/components/use-toast'; +import Clipboard from 'clipboard'; +import { translate } from '../lang'; + +/** + * 复制文本 + * @param text 复制内容 + */ +export function copyText(text: string) { + return new Promise((resolve, reject) => { + const btnElm = document.createElement('button'); + btnElm.style.width = '0'; + btnElm.style.height = '0'; + const clipboard = new Clipboard(btnElm, { + text: () => { + return text; + }, + action: () => { + return 'copy'; + }, + }); + + clipboard.on('success', function (e) { + clipboard.destroy(); + document.body.removeChild(btnElm); + resolve(e); + }); + + clipboard.on('error', function (e) { + clipboard.destroy(); + document.body.removeChild(btnElm); + reject(e); + }); + + document.body.appendChild(btnElm); + btnElm.click(); + }); +} + +/** 提示复制成功 */ +export const toastCopySuccess = () => { + toast.success(translate('copy.success')); +}; diff --git a/src/assets/utils/date-time.ts b/src/assets/utils/date-time.ts new file mode 100644 index 0000000000000000000000000000000000000000..7506b2246e063d656527e7b0c00d98660055ad4e --- /dev/null +++ b/src/assets/utils/date-time.ts @@ -0,0 +1,21 @@ +/** + * @file 日期时间相关的工具方法 + */ + +/** + * 秒数转成时长字符串 + * @param duration 秒数,单位:秒 + */ +export function formatDuration(duration: number): string { + const seconds = Math.floor(duration % 60) + .toString() + .padStart(2, '0'); + const minutes = Math.floor((duration / 60) % 60) + .toString() + .padStart(2, '0'); + const hours = Math.floor(duration / 3600) + .toString() + .padStart(2, '0'); + + return `${hours}:${minutes}:${seconds}`; +} diff --git a/src/assets/utils/dom.ts b/src/assets/utils/dom.ts new file mode 100644 index 0000000000000000000000000000000000000000..26cbd977a93c3b6d0b6ef660fb96d630a6f071c4 --- /dev/null +++ b/src/assets/utils/dom.ts @@ -0,0 +1,45 @@ +/** 当前浏览器是否支持 filter 样式 */ +export const supportsFilterStyle = 'filter' in document.body.style; + +export function getElement(selector: string | HTMLElement): HTMLElement | null { + if (selector instanceof HTMLElement) { + return selector; + } + return document.querySelector(selector); +} + +/** + * 格式化样式数值 + * @param size 数值或数值字符串 + */ +export function formatStyleSize(size?: string | number): string | undefined { + if (typeof size === 'string') { + return size; + } + + if (typeof size === 'number') { + return `${size}px`; + } + + return size; +} + +/** + * 判断是否是否在 input 等可编辑的表单元素中 + */ +export function isFocusActiveForm(): boolean { + const activeElem = document.activeElement; + if (!activeElem) { + return false; + } + const tagName = activeElem.tagName.toLowerCase(); + + // 可编辑的 div + if (tagName === 'div' && activeElem.getAttribute('contenteditable')) { + return true; + } + + // 表单元素 + const tagNames = ['input', 'textarea']; + return tagNames.includes(tagName); +} diff --git a/src/assets/utils/file.ts b/src/assets/utils/file.ts new file mode 100644 index 0000000000000000000000000000000000000000..792915f6b6128d92a5bd40cdc0e30280f9bf6f4a --- /dev/null +++ b/src/assets/utils/file.ts @@ -0,0 +1,54 @@ +/** + * @file 和文件相关的工具方法 + */ +import { changeProtocol } from '@utils-ts/net'; +import { isIframe, isMobile, isWeixin } from './browser'; + +/** + * 获取文件后缀名 + * @param fileName 文件名 + */ +export function getFileSuffix(fileName: string): string { + const result = fileName.match(/[^.]+$/); + if (!result) return ''; + return result[0].toLocaleLowerCase(); +} + +/** + * 下载文件 + * @param fileUrl 文件地址 + * @param fileName 文件名 + */ +export function downloadFile(fileUrl: string, fileName: string) { + const path = changeProtocol(fileUrl, 'https'); + const a = document.createElement('a'); + a.setAttribute('target', '_blank'); + + // 在 iframe 下上报事件到父窗口中进行下载 + if (isIframe) { + const record = { name: 'file', path }; + window.parent.postMessage(record, '*'); + return; + } + + // 移动端与微信 + if (isWeixin || isMobile) { + a.href = path; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + return; + } + + const xhr = new window.XMLHttpRequest(); + xhr.open('GET', path, true); + xhr.responseType = 'blob'; + xhr.onload = () => { + const blobUrl = window.URL.createObjectURL(xhr.response); + a.href = blobUrl; + a.download = fileName; + a.click(); + a.remove(); + }; + xhr.send(); +} diff --git a/src/assets/utils/function.ts b/src/assets/utils/function.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2628fb2ed178b32fc6a37d47dadba8442d9f7a5 --- /dev/null +++ b/src/assets/utils/function.ts @@ -0,0 +1,7 @@ +/** + * 判断目标是否为函数类型 + * @param target 目标 + */ +export function isFunction(target: unknown): target is T { + return typeof target === 'function'; +} diff --git a/src/assets/utils/image.ts b/src/assets/utils/image.ts new file mode 100644 index 0000000000000000000000000000000000000000..2aa9e5144304129acec76985b97821f51e134185 --- /dev/null +++ b/src/assets/utils/image.ts @@ -0,0 +1,89 @@ +/** + * @file 和图片相关的工具函数 + */ +import { changeProtocol, startsWithProtocol } from '@utils-ts/net'; + +/** + * 重置图片的地址 + * @param url 图片地址 + */ +export function resetImageProtocol(url: string): string { + // 如果当前页面是 http 协议则用 http + if (startsWithProtocol(location.href, ['http'])) { + return changeProtocol(url, 'http'); + } + + // 其余情况都用 https + return changeProtocol(url, 'https'); +} + +/** + * 重置 oss 的图片尺寸 + * @param url 图片地址 + * @param width 宽度 + * @param height 高度 + */ +export function resizeOSSImg(url: string, width?: number, height?: number): string { + if (!url) { + return url; + } + + const a = document.createElement('a'); + a.href = url; + + const host = a.host.toLowerCase(); + const search = a.search; + + if (host !== 'liveimages.videocc.net' || search) { + return url; + } + + let result = url + '?x-oss-process=image/resize,mfit'; + if (width) { + result += ',w_' + width; + } + if (height) { + result += ',h_' + height; + } + result += ',limit_1'; + return result; +} + +/** + * 获取图片地址的尺寸 + * @param url 图片地址 + * @returns 尺寸与图片实例 + */ +export function getImgSize( + url: string, +): Promise<{ width: number; height: number; imgTarget: HTMLImageElement }> { + return new Promise<{ width: number; height: number; imgTarget: HTMLImageElement }>( + (resolve, reject) => { + const img = new Image(); + const resolvePromise = () => { + resolve({ + width: img.width, + height: img.height, + imgTarget: img, + }); + }; + img.onload = () => { + resolvePromise(); + }; + img.onerror = ( + event: Event | string, + source?: string, + lineno?: number, + colno?: number, + error?: Error, + ) => { + const reason = error?.message || '未知原因'; + reject(new Error(`获取图片尺寸失败:${reason}`)); + }; + img.src = url; + if (img.complete) { + resolvePromise(); + } + }, + ); +} diff --git a/src/assets/utils/number.ts b/src/assets/utils/number.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd9392722a6294a4ca4055daab01482b539424c7 --- /dev/null +++ b/src/assets/utils/number.ts @@ -0,0 +1,42 @@ +/** + * @file 跟数值有关的工具方法 + */ + +export function isNumber(target: unknown): target is number { + return typeof target === 'number'; +} + +/** + * 缩短万以上的数值 + * @param num 数值 + */ +export function shortNumber(num: number): string { + if (num >= 10000) { + return (num / 10000).toFixed(1) + 'w'; + } else { + return num.toString(); + } +} + +/** + * 保留小数位 + * @param number + * @param accuracy 精度,选传,默认为 2 + */ +export function numberToFixed(number: number, accuracy = 2): number { + return Number(number.toFixed(accuracy)); +} + +/** + * 保留数值的小数位,并转为字符串 + * @param num + * @param accuracy 精度,默认:1 + */ +export function numberAccuracy(num: number, accuracy = 1): string { + const numStr = num.toString(); + const numStrList = numStr.split('.'); + const integerStr = numStrList[0]; + let decimalStr = numStrList[1] || ''; + decimalStr = decimalStr.padEnd(accuracy, '0'); + return `${integerStr}.${decimalStr}`; +} diff --git a/src/assets/utils/storage.ts b/src/assets/utils/storage.ts new file mode 100644 index 0000000000000000000000000000000000000000..54af7b72e39452c8065d23b1517e1cdf3f235e77 --- /dev/null +++ b/src/assets/utils/storage.ts @@ -0,0 +1,32 @@ +import { local } from '@just4/storage'; + +/** + * Storage 存储 key 前缀 + */ +export const STORAGE_KEY_PREFIX = 'plv-live-watch-sdk-'; + +/** + * 获取 storage 的完整 key + * @param suffix 后缀 + */ +export function getStorageKey(suffix: string): string { + return `${STORAGE_KEY_PREFIX}${suffix}`; +} + +/** + * 设置 localStorage + * @param key + * @param value + */ +export function setLocalStorage(key: string, value: string): void { + local.set(getStorageKey(key), value); +} + +/** + * 获取 localStorage + * @param key + * @returns + */ +export function getLocalStorage(key: string): string | null { + return local.get(getStorageKey(key)); +} diff --git a/src/assets/utils/string.ts b/src/assets/utils/string.ts new file mode 100644 index 0000000000000000000000000000000000000000..1ee6a3e19089c0c4fb7406f1241da67d49e8e765 --- /dev/null +++ b/src/assets/utils/string.ts @@ -0,0 +1,56 @@ +/** + * @file 跟字符串相关的工具方法 + */ + +import { resizeOSSImg } from './image'; + +/** + * 将字符串中的 转换为空格 + * 背景:新版观看在做英文自动折行时,如果后端返回的富文本字符串包含 ,依然会存在换行切割单词的问题,因此需要做这个转换 + * 影响:如果出现连续空格的情况,使用该方法转换后,只会存在一个空格;目前会在使用的地方增加white-space: pre-wrap处理 + * 目前应用范围:对后端返回的富文本字符串进行转换,包括直播介绍、报名观看的隐私政策 + * @param str 需要处理的字符串 + */ +export function nbspToSpace(str: string): string { + return String(str).replace(/ /g, ' '); +} + +/** + * 将富文本字符串中的图片地址进行大小处理 + * @param htmlContent 富文本字符串 + */ +export function resizeHtmlContentImg(htmlContent: string): string { + if (!htmlContent) { + return ''; + } + return htmlContent.replace( + /()/gi, + (match, before, quot, src, after) => { + return before + '"' + resizeOSSImg(src, 1680) + '"' + ' data-src="' + src + '"' + after; + }, + ); +} + +export function isString(target: unknown): target is string { + return typeof target === 'string'; +} + +/** + * html字符串提取纯文本 + */ +export function htmlStr2Text(htmlStr: string): string | null { + if (!htmlStr) return ''; + const dom = document.createElement('div'); + dom.innerHTML = htmlStr; + return dom.textContent; +} + +/** + * 将字符串中的链接转成 a 标签 + * @param text 目标字符串 + */ +export function parseLinkContent(text: string) { + const linkReg = /(http[s]?:\/\/)([\w-]+(\.[\w-]+)+([\w\-.,@?^=%&:/~+#]*[\w\-@?^=%&/~+#])?)/g; + + return text.replace(linkReg, '$1$2'); +} diff --git a/src/assets/utils/types.ts b/src/assets/utils/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..33485bc7259997a73ab64163f7735fd32f514aac --- /dev/null +++ b/src/assets/utils/types.ts @@ -0,0 +1,3 @@ +export function isUndefined(target: unknown): target is undefined { + return typeof target === 'undefined'; +} diff --git a/src/assets/utils/url.ts b/src/assets/utils/url.ts new file mode 100644 index 0000000000000000000000000000000000000000..edaf555716f7855a5f22c57d9f318e7800d90590 --- /dev/null +++ b/src/assets/utils/url.ts @@ -0,0 +1,15 @@ +/** + * @file 与地址有关的工具方法 + */ +import { changeProtocol } from '@utils-ts/net'; + +/** + * 将 url 修改成当前访问协议/补全协议头 + * @param url 目标 url + */ +export function fitProtocol(url: string): string { + const matchRs = /^(http(s?)):/.exec(location.href); + const protocol = (matchRs && matchRs[1]) || 'https'; + + return changeProtocol(url, protocol); +} diff --git a/src/assets/utils/utils.ts b/src/assets/utils/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..323ba411aa4764d285072ec7df2b0c20bc222888 --- /dev/null +++ b/src/assets/utils/utils.ts @@ -0,0 +1,36 @@ +/** + * 等待一段时间 + * @param time 等待时间 + */ +export function wait(time = 3000) { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, time); + }); +} + +/** + * 获取事件对象中的坐标 + * @param event 事件对象 + */ +export function getEventPosition(event: MouseEvent | TouchEvent): { + clientX: number; + clientY: number; +} { + if (event instanceof MouseEvent) { + return { + clientX: event.clientX, + clientY: event.clientY, + }; + } + + const touch = event.touches[0] || event.changedTouches[0] || event.targetTouches[0]; + const touchX = touch.pageX; + const touchY = touch.pageY; + + return { + clientX: touchX, + clientY: touchY, + }; +} diff --git a/src/assets/utils/validate.ts b/src/assets/utils/validate.ts new file mode 100644 index 0000000000000000000000000000000000000000..289f9ec76b891bec7a4c4dbcbf5b8a263ecd3d03 --- /dev/null +++ b/src/assets/utils/validate.ts @@ -0,0 +1,47 @@ +/** + * @file 验证相关的工具函数 + */ + +/** + * 根据区号验证手机号是否正确 + * @param phoneNumber 手机号 + * @param areaCode 区号,默认:+86 + * @returns 正确的手机号返回 true + */ +export function validatePhoneNumber(phoneNumber: string, areaCode = '+86'): boolean { + // 对于非国内的手机号,只需要 5~20 位 + if (areaCode !== '+86') { + return /(^\d{5,20}$)/.test(phoneNumber); + } + + return /^1[3-9]\d{9}$/.test(phoneNumber); +} + +/** + * 验证图片验证码 + * @param imageCaptcha 图片验证码 + * @param imageId 图片验证码 id + * @returns 正确的验证码返回 true + */ +export function validateImageCaptcha(imageCaptcha: string, imageId: string): boolean { + return imageCaptcha.length >= 5 && imageId.length > 0; +} + +/** + * 文本是否包含特殊字符 + * @param text 目标文本 + * @returns 如果存在特殊字符则返回 true + */ +export function validateSpecialString(text: string): boolean { + const containSpecial = /[~!@#$%^&*()+=[\]{}|\\;:",./<>?]+/; + return containSpecial.test(text); +} + +/** + * 检测文本中是否仅包含中英文和空格 + * @param text 目标文本 + * @returns 如果存在其他字符则返回 false + */ +export function validateCnAndEn(text: string): boolean { + return /^[\u4e00-\u9fa5a-zA-Z ]+$/.test(text); +} diff --git a/src/assets/utils/vue-utils/emit-utils.ts b/src/assets/utils/vue-utils/emit-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7fff0add5f5d0ec237b7e23168c014b228c3a44 --- /dev/null +++ b/src/assets/utils/vue-utils/emit-utils.ts @@ -0,0 +1,18 @@ +import { EmitFn, ObjectEmitsOptions } from 'vue/types/v3-setup-context'; + +type EmitFuncType

= (arg: P) => void; + +/** + * 创建 emit 配置的空函数 + */ +export const emitFunc =

(): EmitFuncType

=> (() => true) as EmitFuncType

; + +export type VueEmit ObjectEmitsOptions> = EmitFn>; + +type UpdateEmitReturn = Record<`update:${K}`, EmitFuncType

>; + +export function updateModelEmit(field: K) { + return { + [`update:${field}`]: () => true, + } as unknown as UpdateEmitReturn; +} diff --git a/src/assets/utils/vue-utils/index.ts b/src/assets/utils/vue-utils/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7dc83b5eb3212fe4c62382b36a0275c8b4c8501 --- /dev/null +++ b/src/assets/utils/vue-utils/index.ts @@ -0,0 +1,23 @@ +import Vue, { Ref, unref } from 'vue'; + +export type SimilarResponsive = T | Ref; + +/** + * 获取 ref 响应式对象中的 dom 节点,如果不是则返回 undefined + * @param targetRef 响应式对象 + */ +export const getRefElem = (targetRef: Ref | HTMLElement): E | undefined => { + const refValue = unref(targetRef); + + // 如果解构出来的是 dom 节点 + if (refValue instanceof HTMLElement) { + return refValue as E; + } + + // 如果解构出来的是 vue 对象,则返回 $el + if (refValue instanceof Vue) { + return refValue.$el; + } + + return undefined; +}; diff --git a/src/assets/utils/vue-utils/props-utils.ts b/src/assets/utils/vue-utils/props-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..297e5a7f1b4d0f014730c3de47845ef0d77b5466 --- /dev/null +++ b/src/assets/utils/vue-utils/props-utils.ts @@ -0,0 +1,94 @@ +/** + * @file Vue 的 props 工具 + */ +import { IconComponentOption } from '@polyv/icons-vue/icon-builder'; +import { computed, ComputedRef, ExtractPropTypes } from 'vue'; +import { + string as _string, + number as _number, + bool as _bool, + object as _object, + func as _func, + oneOf as _oneOf, + array as _array, + oneOfType as _oneOfType, + VueTypeValidableDef, +} from 'vue-types'; + +/** Vue prop 配置工具 */ +export class PropUtils { + /** String 类型,默认:'' */ + static get string() { + return _string().def(''); + } + + /** String 类型,默认:undefined */ + static get looseString() { + return _string(); + } + + /** Number 类型,默认:0 */ + static get number() { + return _number().def(0); + } + + /** Number 类型,默认:undefined */ + static get looseNumber() { + return _number(); + } + + /** Boolean 类型,默认:true */ + static get bool() { + return _bool().def(true); + } + + static readonly objectType = _object; + + /** Array 类型,默认:[] */ + static readonly array = () => { + return _array().def([]); + }; + + /** Object 类型 */ + static readonly object = _object; + + /** Function 类型 */ + static readonly func = _func; + + /** 指定为数组中的值 */ + static readonly oneOf = _oneOf; + + /** 自定义类型 */ + static readonly oneOfType = _oneOfType; + + /** 枚举类型 */ + static readonly enum = _string; + + /** 图标组件类型 */ + static readonly icon = (): VueTypeValidableDef => { + return { + type: undefined, + default: undefined, + required: false, + } as VueTypeValidableDef; + }; +} + +export type VueProps UniversalParams> = Readonly>>; + +export type FormatProps UniversalParams> = Partial< + ExtractPropTypes> +>; + +export type PropComputedRefs

= { + [K in keyof P]: ComputedRef; +}; + +/** 结构 props,将 props 转成 computed */ +export const useProps =

(props: P): PropComputedRefs

=> { + const ret: PropComputedRefs

= {} as PropComputedRefs

; + for (const key in props) { + ret[key] = computed(() => props[key]); + } + return ret; +}; diff --git a/src/components/README.md b/src/components/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b801d9a72c6dbdd2d86097a662668f2b0efeabdc --- /dev/null +++ b/src/components/README.md @@ -0,0 +1,9 @@ +# 说明 + +| 目录 | 说明 | +| :----------------- | :------------------------------------------------------------- | +| common-base | 存放一些基础组件,尽量不要使用 store | +| common-business | 存放 `splash` 和 `watch` 都需要用到的业务组件 | +| component-icons | 源文件在 `icon-svgs`,该文件夹是经过脚手架自动生成的的图标组件 | +| page-splash-common | 引导页公共组件 | +| page-watch-common | 直播观看页公共组件 | diff --git a/src/components/common-base/action-sheet/mobile-action-sheet.vue b/src/components/common-base/action-sheet/mobile-action-sheet.vue new file mode 100644 index 0000000000000000000000000000000000000000..386abdfbb91cdcbbabb7b22348ff04917ec3d558 --- /dev/null +++ b/src/components/common-base/action-sheet/mobile-action-sheet.vue @@ -0,0 +1,73 @@ + + + + + + diff --git a/src/components/common-base/action-sheet/types.ts b/src/components/common-base/action-sheet/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7b5ce977aaa77b5c616b25cb3eafde6be425004 --- /dev/null +++ b/src/components/common-base/action-sheet/types.ts @@ -0,0 +1,8 @@ +export type ActionSheetValue = string | number; + +export interface ActionSheetItem { + /** 名称 */ + name: string; + /** 选项值 */ + value: ActionSheetValue; +} diff --git a/src/components/common-base/action-sheet/use-action-sheet.ts b/src/components/common-base/action-sheet/use-action-sheet.ts new file mode 100644 index 0000000000000000000000000000000000000000..968af6c7588e826a3a2187a9406034a57a00c355 --- /dev/null +++ b/src/components/common-base/action-sheet/use-action-sheet.ts @@ -0,0 +1,76 @@ +import { emitFunc, updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ref, watch } from 'vue'; +import { PopupInstance } from '@/components/common-base/popup/types'; +import { popupEmits, popupProps } from '@/components/common-base/popup/use-popup'; +import { ActionSheetItem, ActionSheetValue } from './types'; + +export const actionSheetProps = () => ({ + ...popupProps(), + /** 操作选项列表 */ + actions: PropUtils.array(), + /** 当前选择的操作,支持.sync */ + actionValue: PropUtils.oneOfType([String, Number]).def(''), + /** 是否显示取消,默认:true */ + showCancel: PropUtils.bool.def(false), +}); + +export const actionSheetEmits = () => ({ + ...popupEmits(), + ...updateModelEmit<'actionValue', ActionSheetValue>('actionValue'), + /** 点击选项 */ + 'click-action': emitFunc(), +}); + +export const useActionSheet = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const actionVisible = ref(false); + + watch( + () => props.visible, + () => { + actionVisible.value = props.visible; + }, + ); + + watch( + () => actionVisible.value, + () => { + if (actionVisible.value === props.visible) return; + emit('update:visible', actionVisible.value); + }, + ); + + const popupRef = ref(); + + /** 处理点击选项 */ + function onClickOption(option: ActionSheetItem) { + if (props.actionValue !== option.value) { + emit('update:actionValue', option.value); + } + + if (popupRef.value) { + popupRef.value.closePopup(); + } + + emit('click-action', option.value); + } + + /** 处理点击取消 */ + function onClickCancel() { + if (popupRef.value) { + popupRef.value.closePopup(); + } + } + + return { + actionVisible, + popupRef, + onClickOption, + onClickCancel, + }; +}; diff --git a/src/components/common-base/count-down/count-down.vue b/src/components/common-base/count-down/count-down.vue new file mode 100644 index 0000000000000000000000000000000000000000..27771089ff245aab504d5e0c6ae10ed9a051555c --- /dev/null +++ b/src/components/common-base/count-down/count-down.vue @@ -0,0 +1,191 @@ + + + + + + diff --git a/src/components/common-base/count-down/types.ts b/src/components/common-base/count-down/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1b261eac3ff1319f9315ad4fef0fbff02b49e5f --- /dev/null +++ b/src/components/common-base/count-down/types.ts @@ -0,0 +1,7 @@ +import { tupleString } from '@/assets/utils/array'; + +export const countDownSizes = tupleString('default', 'small'); +export type CountDownSize = typeof countDownSizes[number]; + +export const countdownThemes = tupleString('square', 'text'); +export type CountDownTheme = typeof countdownThemes[number]; diff --git a/src/components/common-base/count-down/use-count-down.ts b/src/components/common-base/count-down/use-count-down.ts new file mode 100644 index 0000000000000000000000000000000000000000..29060e10f1fec4f94934b0c9ea1915efe4dd5a15 --- /dev/null +++ b/src/components/common-base/count-down/use-count-down.ts @@ -0,0 +1,159 @@ +import { translate } from '@/assets/lang'; +import { SimilarResponsive } from '@/assets/utils/vue-utils'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { useSecondCountDown } from '@/hooks/tools/use-count-down'; +import { IRemaining as CountDownSurplusData } from '@utils-ts/countdown'; +import { computed, Ref, unref, watch } from 'vue'; +import { countDownSizes, countdownThemes } from './types'; + +export const countDownProps = () => ({ + /** 尺寸,small-小型尺寸,常用于移动端 */ + size: PropUtils.oneOf(countDownSizes).def('default'), + /** 倒计时样式,square-方形,text-文本,默认:square */ + theme: PropUtils.oneOf(countdownThemes).def('square'), + /** 计时时间,优先级高于 endTimestamp */ + second: PropUtils.number.def(undefined), + /** 结束的时间戳 */ + endTimestamp: PropUtils.number.def(undefined), + /** 只有 0 天时是否隐藏天数,默认:true */ + hideZeroDays: PropUtils.bool.def(true), +}); + +/** + * 倒计时组件的 emit 配置 + */ +export const countDownEmits = () => ({ + /** 倒计时改变 */ + 'count-down-change': emitFunc(), + /** 倒计时结束 */ + 'count-down-finish': emitFunc(), +}); + +export type CountDownField = 'days' | 'hours' | 'minutes' | 'seconds'; + +export interface CountDownFieldItem { + /** 字段名 */ + field: CountDownField; + /** 标题 */ + title: string; + /** 时间单位,多语言 */ + unit: string; +} + +export interface CountDownItem { + /** 数值 */ + count: number; + /** 数值文案,自动补 0 */ + text: string; + /** 标题 */ + title: string; + /** 时间单位,含多语言 */ + unit: string; +} + +export interface UseCountDownOptions { + /** 总秒数,单位秒 */ + second?: SimilarResponsive; + /** 结束的时间戳 */ + endTimestamp?: SimilarResponsive; + /** 当天数为 0 时隐藏天数,默认:true */ + hideZeroDays?: boolean | Ref; + emit?: VueEmit; +} + +/** + * 倒计时 hook + * @param {Object} options 选项 + * @param {Proxy} options.second 总秒数,单位:秒 + * @param {Proxy} options.endTimestamp 结束的时间戳 + * @param {Proxy} options.hideZeroDays 当天数为 0 时隐藏天数,默认:true + * @param {Function} options.emit 回调方法 + */ +export const useCountDown = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + const { hideZeroDays, second, endTimestamp } = useProps(props); + + const { surplusTime, computedSecond, initCountDown, startCountDown, stopCountDown } = + useSecondCountDown({ + second, + endTimestamp, + onCountDownChange: data => emit('count-down-change', data), + onCountDownFinish: () => emit('count-down-finish'), + }); + + /** 显示的时间内容列表 */ + const timeContents = computed(() => { + const contents: CountDownItem[] = []; + + const surplusVal = unref(surplusTime); + + const fields: CountDownFieldItem[] = [ + { field: 'days', title: 'DAY', unit: translate('unit.day') }, + { field: 'hours', title: 'HOUR', unit: translate('unit.hour') }, + { field: 'minutes', title: 'MIN', unit: translate('unit.minute') }, + { field: 'seconds', title: 'SEC', unit: translate('unit.second') }, + ]; + + fields.forEach(({ field, title, unit }) => { + const count = surplusVal[field]; + + // 对于 0 天则不在显示列表中 + if (unref(hideZeroDays) && field === 'days' && count === 0) { + return; + } + + contents.push({ + // 数值 + count, + // 数值文案,自动补 0 + text: formatNumber(count), + // 标题 + title, + // 单位,含多语言 + unit: unref(unit), + }); + }); + + return contents; + }); + + /** + * 显示的时间文案 + * @example + * 01 天 10 时 22 分 30 秒 + */ + const timeText = computed(() => { + const texts: string[] = []; + + unref(timeContents).forEach(data => { + texts.push(`${data.text} ${data.unit}`); + }); + + return texts.join(' '); + }); + + /** 格式化数值,自动补 0 */ + function formatNumber(count: number): string { + return count < 10 ? `0${count}` : count.toString(); + } + + watch( + () => unref(computedSecond), + () => initCountDown(), + { + immediate: true, + }, + ); + + return { + surplusTime, + timeContents, + timeText, + startCountDown, + stopCountDown, + }; +}; diff --git a/src/components/common-base/custom-teleport/custom-teleport.ts b/src/components/common-base/custom-teleport/custom-teleport.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e60a5a9dfb45bcbe01bf1c0286ca7f0c1585494 --- /dev/null +++ b/src/components/common-base/custom-teleport/custom-teleport.ts @@ -0,0 +1,101 @@ +/** + * @file 自定义 vue3 的 teleport + */ +import { useVue } from '@/hooks/core/use-vue'; +import { PropUtils } from '@/assets/utils/vue-utils/props-utils'; +import { defineComponent, nextTick, onBeforeUnmount, onMounted, watch } from 'vue'; +import { $ } from '@just4/dom'; + +const customTeleportProps = () => ({ + /** 渲染到某个节点 */ + to: PropUtils.oneOfType([String, HTMLElement]), +}); + +export const CustomTeleport = defineComponent({ + props: { + ...customTeleportProps(), + }, + setup(props, { slots }) { + const { getCurrentElem } = useVue(); + + const startComment = document.createComment('teleport start'); + const $startComment = $(startComment); + + const endComment = document.createComment('teleport end'); + const $endComment = $(endComment); + + /** 设置渲染位置的占位 */ + function setTeleportPosition() { + const elem = getCurrentElem(); + if (!elem) { + return; + } + const $elem = $(elem); + $startComment.insertBefore($elem); + $endComment.insertAfter($elem); + } + + /** 重置元素到初时位置 */ + function resetToDefaultPosition() { + const elem = getCurrentElem(); + if (!elem) { + return; + } + const $elem = $(elem); + $elem.insertAfter($startComment); + } + + /** 设置节点位置 */ + async function setElemPosition() { + await nextTick(); + // 没传则重置到初时位置 + if (!props.to) { + resetToDefaultPosition(); + return; + } + + const elem = getCurrentElem(); + if (!elem) { + return; + } + + let parentElem: Element | undefined | null; + if (typeof props.to === 'string') { + parentElem = document.querySelector(props.to); + } else if (props.to instanceof Element) { + parentElem = props.to; + } + + if (!parentElem) { + resetToDefaultPosition(); + return; + } + + const $parentElem = $(parentElem); + $parentElem.append(elem); + } + + watch( + () => props.to, + () => { + setElemPosition(); + }, + ); + + onMounted(() => { + setTeleportPosition(); + if (props.to) { + setElemPosition(); + } + }); + + onBeforeUnmount(() => { + $startComment.remove(); + $endComment.remove(); + }); + + return () => slots.default && slots.default(); + }, +}); + +export default CustomTeleport; diff --git a/src/components/common-base/dialog/mobile-dialog/imgs/icon-arrow-l.png b/src/components/common-base/dialog/mobile-dialog/imgs/icon-arrow-l.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec661d8ada78cac0027c4072aec4d9c08c498a6 Binary files /dev/null and b/src/components/common-base/dialog/mobile-dialog/imgs/icon-arrow-l.png differ diff --git a/src/components/common-base/dialog/mobile-dialog/imgs/icon_close.png b/src/components/common-base/dialog/mobile-dialog/imgs/icon_close.png new file mode 100644 index 0000000000000000000000000000000000000000..8c03227f8e3fb819459d37129e783d76deab34c9 Binary files /dev/null and b/src/components/common-base/dialog/mobile-dialog/imgs/icon_close.png differ diff --git a/src/components/common-base/dialog/mobile-dialog/mobile-dialog.vue b/src/components/common-base/dialog/mobile-dialog/mobile-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..0dec16f9aec1d642f42b3d59794cc9dee0bae7a2 --- /dev/null +++ b/src/components/common-base/dialog/mobile-dialog/mobile-dialog.vue @@ -0,0 +1,270 @@ + + + + + diff --git a/src/components/common-base/dialog/pc-dialog/pc-dialog.vue b/src/components/common-base/dialog/pc-dialog/pc-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..793fcf2f4ee7ce8ade05bd7c2c3b40beddce95c5 --- /dev/null +++ b/src/components/common-base/dialog/pc-dialog/pc-dialog.vue @@ -0,0 +1,498 @@ + + + + + + diff --git a/src/components/common-base/dialog/pc-dialog/types.ts b/src/components/common-base/dialog/pc-dialog/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc56ced864d5cc6f56ef895b3078f1397dc90cd5 --- /dev/null +++ b/src/components/common-base/dialog/pc-dialog/types.ts @@ -0,0 +1,4 @@ +export interface DialogInstance { + /** 重置位置 */ + resetPosition(): void; +} diff --git a/src/components/common-base/dialog/use-dialog-tips.ts b/src/components/common-base/dialog/use-dialog-tips.ts new file mode 100644 index 0000000000000000000000000000000000000000..b3366feefe79b4853f62080b963d7de23a48f2f9 --- /dev/null +++ b/src/components/common-base/dialog/use-dialog-tips.ts @@ -0,0 +1,74 @@ +import { inject, InjectionKey, onBeforeUnmount, provide, ref } from 'vue'; + +export interface DialogTipsInstance { + /** 显示弹层的提示 */ + showDialogTips: (tips: string) => void; +} + +export const DIALOG_TIPS_PROVIDE_KEY: InjectionKey = + Symbol('DIALOG_TIPS_PROVIDE_KEY'); + +/** + * 弹层提示 hooks(用于弹层中) + */ +export const useDialogTips = () => { + let timer: number | undefined; + + const dialogTips = ref(); + + /** 显示弹层提示 */ + function showDialogTips(tips: string) { + dialogTips.value = tips; + setHiddenTimer(); + } + + /** 设置隐藏定时器 */ + function setHiddenTimer() { + clearHiddenTimer(); + + timer = window.setTimeout(() => { + dialogTips.value = undefined; + clearHiddenTimer(); + }, 2000); + } + + /** 清楚隐藏定时器 */ + function clearHiddenTimer() { + if (timer) { + clearTimeout(timer); + timer = undefined; + } + } + + onBeforeUnmount(() => { + clearHiddenTimer(); + }); + + const dialogTipsInstance: DialogTipsInstance = { + showDialogTips, + }; + + provide(DIALOG_TIPS_PROVIDE_KEY, dialogTipsInstance); + + return { + dialogTips, + showDialogTips, + dialogTipsInstance, + }; +}; + +/** + * 弹层提示 hook + */ +export const useDialogTipsUtils = () => { + const dialogTipsContext = inject(DIALOG_TIPS_PROVIDE_KEY); + + /** 显示弹层提示 */ + function showDialogTips(tips: string) { + dialogTipsContext && dialogTipsContext.showDialogTips(tips); + } + + return { + showDialogTips, + }; +}; diff --git a/src/components/common-base/form/form-area-picker/mobile-form-area-picker.vue b/src/components/common-base/form/form-area-picker/mobile-form-area-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..6302e60dc1c5d98b5646fd58358f523a4935615f --- /dev/null +++ b/src/components/common-base/form/form-area-picker/mobile-form-area-picker.vue @@ -0,0 +1,160 @@ + + + + + + diff --git a/src/components/common-base/form/form-area-picker/pc-form-area-picker.vue b/src/components/common-base/form/form-area-picker/pc-form-area-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..263a467300397efe914cd5c6772328b77b3cae66 --- /dev/null +++ b/src/components/common-base/form/form-area-picker/pc-form-area-picker.vue @@ -0,0 +1,47 @@ + + + + + + diff --git a/src/components/common-base/form/form-area-picker/use-form-area-picker.ts b/src/components/common-base/form/form-area-picker/use-form-area-picker.ts new file mode 100644 index 0000000000000000000000000000000000000000..09a14ed9cd98a9ff75efec38795be2db2c44afd2 --- /dev/null +++ b/src/components/common-base/form/form-area-picker/use-form-area-picker.ts @@ -0,0 +1,144 @@ +import { computed, ComputedRef, onBeforeMount, ref, unref, watch } from 'vue'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import type { AreaData, AreaPickerComponent } from '@/plugins/polyv-ui/types'; +import { formCommonProps, useFormCommonValidate } from '../hooks/use-form-common'; +import { isMobile } from '@/assets/utils/browser'; +import { getSelectData, isSelectFinish } from '@/plugins/polyv-ui/area-utils'; +import { useCommonStore } from '@/store/use-common-store'; + +export const formAreaPickerProps = () => ({ + ...formCommonProps(), + /** 绑定值 */ + value: PropUtils.array(), + /** 占位文本 */ + placeholder: PropUtils.string, +}); + +export const formAreaPickerEmit = () => ({ + input: emitFunc(), +}); + +export interface FormAreaPickerInstance { + /** 是否已选择完成 */ + isFinish: ComputedRef; +} + +export const useFormAreaPicker = (options: { + AreaPicker: AreaPickerComponent; + props: VueProps; + emit: VueEmit; +}) => { + const commonStore = useCommonStore(); + const { AreaPicker, props, emit } = options; + + const { formItemIsError, blurToValidateItem, focusToRemoveError } = useFormCommonValidate({ + props, + }); + + const innerValue = ref([]); + + const inputPlaceholder = computed(() => props.placeholder); + + /** 是否已选择完成 */ + const isFinish = computed(() => { + return isSelectFinish(props.value, commonStore.areaData); + }); + + const innerIsFinish = computed(() => { + return isSelectFinish(innerValue.value, commonStore.areaData); + }); + + const inputValue = computed(() => { + let data: AreaData[] = []; + + if (innerIsFinish.value) { + data = getSelectData(innerValue.value, commonStore.areaData); + } else if (isSelectFinish(props.value, commonStore.areaData)) { + data = getSelectData(props.value, commonStore.areaData); + } + + return data.map(item => item.name).join('/'); + }); + + watch( + () => unref(innerValue), + () => { + if (!isMobile) { + emit('input', unref(innerValue)); + } + }, + ); + + const instance: FormAreaPickerInstance = { + isFinish, + }; + + const pickerVisible = ref(false); + + const refreshKey = ref(1); + + function openPicker() { + if (!isFinish.value) { + refreshKey.value += 1; + } + pickerVisible.value = true; + onShowPicker(); + } + + function closePicker() { + if (!innerIsFinish.value) { + innerValue.value = props.value; + } + pickerVisible.value = false; + onHiddenPicker(); + } + + /** 处理隐藏弹层 */ + function onHiddenPicker() { + blurToValidateItem(); + } + + /** 处理显示弹层 */ + function onShowPicker() { + focusToRemoveError(); + } + + function onClickConfirm() { + if (!unref(innerIsFinish)) { + return; + } + emit('input', unref(innerValue)); + closePicker(); + } + + onBeforeMount(async () => { + if (commonStore.areaData.length === 0) { + const areaData = await AreaPicker.loadAreaDataUrl(); + commonStore.$patch({ + areaData, + }); + } + }); + + return { + innerValue, + isFinish, + innerIsFinish, + instance, + inputValue, + inputPlaceholder, + refreshKey, + + formItemIsError, + blurToValidateItem, + focusToRemoveError, + + pickerVisible, + openPicker, + closePicker, + onHiddenPicker, + onShowPicker, + onClickConfirm, + }; +}; diff --git a/src/components/common-base/form/form-checkbox/mobile-form-checkbox.vue b/src/components/common-base/form/form-checkbox/mobile-form-checkbox.vue new file mode 100644 index 0000000000000000000000000000000000000000..45265f592ffc1881fb8cba289ea870acff01938d --- /dev/null +++ b/src/components/common-base/form/form-checkbox/mobile-form-checkbox.vue @@ -0,0 +1,73 @@ + + + + + + diff --git a/src/components/common-base/form/form-checkbox/pc-form-checkbox.vue b/src/components/common-base/form/form-checkbox/pc-form-checkbox.vue new file mode 100644 index 0000000000000000000000000000000000000000..79dbb7b6e5df5445b6bab96bb09275beee0f43df --- /dev/null +++ b/src/components/common-base/form/form-checkbox/pc-form-checkbox.vue @@ -0,0 +1,78 @@ + + + + + + diff --git a/src/components/common-base/form/form-checkbox/use-form-checkbox.ts b/src/components/common-base/form/form-checkbox/use-form-checkbox.ts new file mode 100644 index 0000000000000000000000000000000000000000..f729d37f1716e563bcad64919e8214e636b23607 --- /dev/null +++ b/src/components/common-base/form/form-checkbox/use-form-checkbox.ts @@ -0,0 +1,34 @@ +import { emitFunc, updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, unref } from 'vue'; + +export const formCheckboxProps = () => ({ + /** 是否选中,支持.sync */ + checked: PropUtils.bool.def(false), +}); + +export const formCheckboxEmits = () => ({ + ...updateModelEmit<'checked', boolean>('checked'), + change: emitFunc(), +}); + +export const useFormCheckbox = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + /** 是否已选中 */ + const isChecked = computed(() => props.checked); + + /** 处理点击复选框 */ + function onClickCheckbox(): void { + emit('update:checked', !unref(isChecked)); + emit('change', !unref(isChecked)); + } + + return { + isChecked, + onClickCheckbox, + }; +}; diff --git a/src/components/common-base/form/form-image-verify-input/mobile-form-image-verify-input.vue b/src/components/common-base/form/form-image-verify-input/mobile-form-image-verify-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..be1481bb09f435434e6b0d459032e46e9aa919a1 --- /dev/null +++ b/src/components/common-base/form/form-image-verify-input/mobile-form-image-verify-input.vue @@ -0,0 +1,53 @@ + + + + + + diff --git a/src/components/common-base/form/form-image-verify-input/pc-form-image-verify-input.vue b/src/components/common-base/form/form-image-verify-input/pc-form-image-verify-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..f697c54feff1a37aa80c2ffe03e8898a86a14784 --- /dev/null +++ b/src/components/common-base/form/form-image-verify-input/pc-form-image-verify-input.vue @@ -0,0 +1,53 @@ + + + + + + diff --git a/src/components/common-base/form/form-image-verify-input/type.ts b/src/components/common-base/form/form-image-verify-input/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c64277e8b6abb2b9326d04734692a6030266022 --- /dev/null +++ b/src/components/common-base/form/form-image-verify-input/type.ts @@ -0,0 +1,4 @@ +export interface ImageVerifyInputInstance { + /** 刷新图片验证码 */ + refreshVerifyImage(resetVal?: boolean): Promise; +} diff --git a/src/components/common-base/form/form-image-verify-input/use-image-verify-input.ts b/src/components/common-base/form/form-image-verify-input/use-image-verify-input.ts new file mode 100644 index 0000000000000000000000000000000000000000..00f8edc68c40f1e5c4d0e0b19fc81d13de159e9f --- /dev/null +++ b/src/components/common-base/form/form-image-verify-input/use-image-verify-input.ts @@ -0,0 +1,75 @@ +import { translate } from '@/assets/lang'; +import { emitFunc, updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { getWatchCore } from '@/core/watch-sdk'; +import { computed, onMounted, ref, watch } from 'vue'; +import { InputValueType } from '../form-input/hooks/use-form-input'; +import { ImageVerifyInputInstance } from './type'; + +export const formImageVerifyInputProps = () => ({ + // 验证码绑定值 + value: PropUtils.string.def(''), + // 图片 id,支持 .sync + imageId: PropUtils.string.def(''), +}); + +export const formImageVerifyInputEmits = () => ({ + ...updateModelEmit<'imageId', string>('imageId'), + input: emitFunc(), +}); + +export const useImageVerifyInput = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const inputPlaceholder = computed(() => { + return translate('form.placeholder.imageVerify'); + }); + + /** 验证码图片地址 */ + const imageUrl = ref(''); + + /** 刷新验证码图片 */ + async function refreshVerifyImage(resetVal = false) { + const watchCore = getWatchCore(); + const data = await watchCore.sms.generateImageVerifyCode(); + imageUrl.value = data.url; + emit('update:imageId', data.imageId); + + if (resetVal) { + emit('input', ''); + } + } + + onMounted(() => { + refreshVerifyImage(); + }); + + watch( + () => props.imageId, + () => { + if (props.imageId === '') { + refreshVerifyImage(); + } + }, + ); + + /** 处理输入框输入 */ + function onInputChange(val: InputValueType): void { + emit('input', `${val}`); + } + + const instance: ImageVerifyInputInstance = { + refreshVerifyImage, + }; + + return { + inputPlaceholder, + imageUrl, + refreshVerifyImage, + onInputChange, + instance, + }; +}; diff --git a/src/components/common-base/form/form-input/hooks/use-form-input.ts b/src/components/common-base/form/form-input/hooks/use-form-input.ts new file mode 100644 index 0000000000000000000000000000000000000000..252b2510c82f7842e17f3e7e83738a881aad1bef --- /dev/null +++ b/src/components/common-base/form/form-input/hooks/use-form-input.ts @@ -0,0 +1,152 @@ +/** + * @file 表单组件输入框 hook + */ + +import { useVue } from '@/hooks/core/use-vue'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { + formCommonProps, + useFormCommon, + useFormCommonValidate, +} from '@/components/common-base/form/hooks/use-form-common'; +import { computed, ref, unref } from 'vue'; +import { FormInputInstance } from '../types/form-input-types'; + +export type InputValueType = string | number; + +export const formInputProps = () => ({ + ...formCommonProps(), + /** 禁用输入 */ + disabled: PropUtils.bool.def(false), + /** 绑定值 */ + value: PropUtils.oneOfType([String, Number]).def(''), + /** 输入框原生类型 */ + type: PropUtils.string.def('text'), + /** 输入框占位 */ + placeholder: PropUtils.string.def(''), + /** 最大长度 */ + maxlength: PropUtils.number.def(Infinity), + /** 显示字数限制,默认:false */ + showWordLimit: PropUtils.bool.def(false), + /** 尾部文字 */ + suffixText: PropUtils.string.def(''), +}); + +export const formInputEmits = () => ({ + input: emitFunc(), + /** 失焦 */ + blur: emitFunc(), + /** 聚焦 */ + focus: emitFunc(), + /** 回车事件 */ + enter: emitFunc(), +}); + +export const useFormInput = (options: { + props: VueProps; + emit: VueEmit; + classPrefix: string; +}) => { + const { emit, props, classPrefix } = options; + + const { blurToValidateItem, focusToRemoveError, formItemIsError } = useFormCommonValidate({ + props, + }); + + const { commonClassNames } = useFormCommon({ + props, + classPrefix, + }); + + /** 实例 */ + const inputRef = ref(); + + /** 输入框 className */ + const inputClassNames = computed(() => { + const classNames = [...unref(commonClassNames)]; + + if (unref(formItemIsError)) { + classNames.push(`${classPrefix}--error`); + } + + return classNames; + }); + + /** 字数限制文案 */ + const wordLimitText = computed(() => { + if (!props.showWordLimit) { + return ''; + } + + const length = `${props.value ?? ''}`.length; + if (props.maxlength === Infinity) { + return `${length}`; + } + + return `${length}/${props.maxlength}`; + }); + + /** 处理输入框输入事件 */ + function onInputChanged(event: Event) { + const target = event.target as HTMLInputElement; + let value = target.value; + if (props.type === 'number') { + value = value.slice(0, props.maxlength); + target.value = value; + } + emit('input', value); + } + + /** 处理输入框失焦事件 */ + function onInputBlur() { + blurToValidateItem(); + emit('blur', props.value); + } + + /** 处理输入框聚焦事件 */ + function onInputFocus() { + focusToRemoveError(); + emit('focus', props.value); + } + + /** 处理输入框回车事件 */ + function onInputEnter() { + emit('enter', props.value); + } + + const { getInstance } = useVue(); + + /** 强制更新 */ + function forceUpdate(): void { + const instance = getInstance(); + instance?.$forceUpdate(); + } + + /** 聚焦输入框 */ + function focusInput() { + inputRef.value?.focus(); + } + + /** 失焦输入框 */ + function blurInput() { + inputRef.value?.blur(); + } + + const formInstance: FormInputInstance = { + forceUpdate, + focusInput, + blurInput, + }; + + return { + inputRef, + inputClassNames, + onInputChanged, + onInputBlur, + onInputFocus, + onInputEnter, + formInstance, + wordLimitText, + }; +}; diff --git a/src/components/common-base/form/form-input/mobile-form-input.vue b/src/components/common-base/form/form-input/mobile-form-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..527f3e996400919018d1f7cc548f6301c5d34cce --- /dev/null +++ b/src/components/common-base/form/form-input/mobile-form-input.vue @@ -0,0 +1,200 @@ + + + + + + diff --git a/src/components/common-base/form/form-input/pc-form-input.vue b/src/components/common-base/form/form-input/pc-form-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..9729b1cd018375243e10912c82123d9b6611f9e2 --- /dev/null +++ b/src/components/common-base/form/form-input/pc-form-input.vue @@ -0,0 +1,127 @@ + + + + + + diff --git a/src/components/common-base/form/form-input/types/form-input-types.ts b/src/components/common-base/form/form-input/types/form-input-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6fb46a0a98850880c67d76e2ca9754fd574c8cf --- /dev/null +++ b/src/components/common-base/form/form-input/types/form-input-types.ts @@ -0,0 +1,11 @@ +/** + * 实例 + */ +export interface FormInputInstance { + /** 强制更新 */ + forceUpdate(): void; + /** 聚焦输入框 */ + focusInput(): void; + /** 失焦输入框 */ + blurInput(): void; +} diff --git a/src/components/common-base/form/form-item.vue b/src/components/common-base/form/form-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..181c61f4ba5c883de92e0b7fe3c1f75459d45ef3 --- /dev/null +++ b/src/components/common-base/form/form-item.vue @@ -0,0 +1,133 @@ + + + + + + diff --git a/src/components/common-base/form/form-number-input/mobile-form-number-input.vue b/src/components/common-base/form/form-number-input/mobile-form-number-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..c5e7662fa9399c7f76bdc5379d39d71bec638428 --- /dev/null +++ b/src/components/common-base/form/form-number-input/mobile-form-number-input.vue @@ -0,0 +1,40 @@ + + + + diff --git a/src/components/common-base/form/form-number-input/pc-form-number-input.vue b/src/components/common-base/form/form-number-input/pc-form-number-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..774209ca137eade2d8ca0e8424e947d44412b098 --- /dev/null +++ b/src/components/common-base/form/form-number-input/pc-form-number-input.vue @@ -0,0 +1,33 @@ + + + + diff --git a/src/components/common-base/form/form-number-input/use-form-number-input.ts b/src/components/common-base/form/form-number-input/use-form-number-input.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e1b8b6832b34eba4b46b8d893c3daf9ecac664a --- /dev/null +++ b/src/components/common-base/form/form-number-input/use-form-number-input.ts @@ -0,0 +1,76 @@ +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ref, unref } from 'vue'; +import { InputValueType } from '../form-input/hooks/use-form-input'; +import { FormInputInstance } from '../form-input/types/form-input-types'; +import { formCommonProps } from '../hooks/use-form-common'; + +export type NumberInputValueType = string | number; + +export const formNumberInputProps = () => ({ + ...formCommonProps(), + // 绑定值 + value: PropUtils.oneOfType([String, Number]).def(''), + // 最大值 + max: PropUtils.number.def(Infinity), + // 最小值 + min: PropUtils.number.def(-Infinity), +}); + +export const formNumberInputEmits = () => ({ + input: emitFunc(), +}); + +export const useFormNumberInput = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const { value } = useProps(props); + + const inputRef = ref(); + + /** + * 格式化值 + */ + function formatValue(val: string | number): NumberInputValueType { + if (val === '') { + return val; + } + return Number(val); + } + + /** + * 检查最大值和最小值 + */ + function checkMaxMin(val: NumberInputValueType): NumberInputValueType { + if (val === '') { + return val; + } + if (val > props.max) { + return props.max; + } + if (val < props.min) { + return props.min; + } + return val; + } + + function onInputChange(val: InputValueType): void { + let newVal = formatValue(val); + newVal = checkMaxMin(newVal); + emit('input', newVal); + + const inputInstance = unref(inputRef); + if (inputInstance) { + inputInstance.forceUpdate(); + } + } + + return { + inputRef, + value, + onInputChange, + }; +}; diff --git a/src/components/common-base/form/form-phone-input/mobile-form-phone-input.vue b/src/components/common-base/form/form-phone-input/mobile-form-phone-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..38981ab11d66776e3196b28f90e59dc9a8f7f79b --- /dev/null +++ b/src/components/common-base/form/form-phone-input/mobile-form-phone-input.vue @@ -0,0 +1,103 @@ + + + + + + diff --git a/src/components/common-base/form/form-phone-input/pc-form-phone-input.vue b/src/components/common-base/form/form-phone-input/pc-form-phone-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..ae4d530450e6ec2e28ba88628344d4f74ea763d9 --- /dev/null +++ b/src/components/common-base/form/form-phone-input/pc-form-phone-input.vue @@ -0,0 +1,101 @@ + + + + + + diff --git a/src/components/common-base/form/form-phone-input/use-form-phone-input.ts b/src/components/common-base/form/form-phone-input/use-form-phone-input.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee905255516fb64f7edd6d7d0812b5a2e2a5bd77 --- /dev/null +++ b/src/components/common-base/form/form-phone-input/use-form-phone-input.ts @@ -0,0 +1,69 @@ +/** + * @file 手机号码输入框 hook + */ + +import { translate } from '@/assets/lang'; +import { formCommonProps, useFormCommonValidate } from '../hooks/use-form-common'; +import { computed } from 'vue'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { emitFunc, updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { InputValueType } from '../form-input/hooks/use-form-input'; + +export const formPhoneInputProps = () => ({ + ...formCommonProps(), + // 绑定值 + value: PropUtils.string, + // 区号,支持.sync + areaCode: PropUtils.string.def('+86'), + // 占位符 + placeholder: PropUtils.string, +}); + +export const formPhoneInputEmits = () => ({ + input: emitFunc(), + blur: emitFunc(), + ...updateModelEmit<'areaCode', string>('areaCode'), +}); + +export const useFormPhoneInput = (options: { + props: VueProps; + emit: VueEmit; + closePhoneCode?: () => void; +}) => { + const { props, emit, closePhoneCode } = options; + const { validateCurrentFormItem, blurToValidateItem } = useFormCommonValidate({ + props, + }); + + /** 输入框占位文本 */ + const inputPlaceholder = computed(() => { + return props.placeholder || translate('form.placeholder.phoneInput'); + }); + + /** 处理表单输入框修改 */ + function onFormInputChange(val: InputValueType) { + emit('input', `${val}`); + } + + /** 处理表单输入框失焦 */ + async function onFormInputBlur() { + try { + await blurToValidateItem(); + emit('blur', props.value); + } catch (error) {} + } + + /** 处理区号选择 */ + function onPhoneCodeInput(code: string) { + emit('update:areaCode', code); + validateCurrentFormItem(); + closePhoneCode && closePhoneCode(); + } + + return { + inputPlaceholder, + onFormInputChange, + onFormInputBlur, + onPhoneCodeInput, + }; +}; diff --git a/src/components/common-base/form/form-protocol/mobile-form-protocol.vue b/src/components/common-base/form/form-protocol/mobile-form-protocol.vue new file mode 100644 index 0000000000000000000000000000000000000000..d20b0e474dbf25d924608e511584707c15c2ddde --- /dev/null +++ b/src/components/common-base/form/form-protocol/mobile-form-protocol.vue @@ -0,0 +1,44 @@ + + + + + + diff --git a/src/components/common-base/form/form-protocol/pc-form-protocol.vue b/src/components/common-base/form/form-protocol/pc-form-protocol.vue new file mode 100644 index 0000000000000000000000000000000000000000..b13bba3eb4a27bda18d65bf54cfe02bdacbebb74 --- /dev/null +++ b/src/components/common-base/form/form-protocol/pc-form-protocol.vue @@ -0,0 +1,48 @@ + + + + + + diff --git a/src/components/common-base/form/form-protocol/use-form-protocol.ts b/src/components/common-base/form/form-protocol/use-form-protocol.ts new file mode 100644 index 0000000000000000000000000000000000000000..9cfa505fbf1df3e419f9852daa2116b9cc7a9265 --- /dev/null +++ b/src/components/common-base/form/form-protocol/use-form-protocol.ts @@ -0,0 +1,35 @@ +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed } from 'vue'; + +export const formProtocolProps = () => ({ + /** 是否选中 */ + value: PropUtils.bool.def(false), + /** 协议内容 */ + content: PropUtils.string, +}); + +export const formProtocolEmits = () => ({ + input: emitFunc(), +}); + +export const useFormProtocol = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const checkboxChecked = computed(() => props.value); + + const contentHtml = computed(() => props.content); + + function onCheckboxChange(checked: boolean) { + emit('input', checked); + } + + return { + checkboxChecked, + contentHtml, + onCheckboxChange, + }; +}; diff --git a/src/components/common-base/form/form-select/mobile-form-select.vue b/src/components/common-base/form/form-select/mobile-form-select.vue new file mode 100644 index 0000000000000000000000000000000000000000..59cb73a1e0d04d7756f0d93d30f53e2c94119ee8 --- /dev/null +++ b/src/components/common-base/form/form-select/mobile-form-select.vue @@ -0,0 +1,164 @@ + + + + + + diff --git a/src/components/common-base/form/form-select/pc-form-select.vue b/src/components/common-base/form/form-select/pc-form-select.vue new file mode 100644 index 0000000000000000000000000000000000000000..0e12fe7ebbbbff5a2f4adec498f5256f48e66ed3 --- /dev/null +++ b/src/components/common-base/form/form-select/pc-form-select.vue @@ -0,0 +1,168 @@ + + + + + + diff --git a/src/components/common-base/form/form-select/types/form-select-types.ts b/src/components/common-base/form/form-select/types/form-select-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..d3eb3f764514d954c684f55beb95456779eaa3cf --- /dev/null +++ b/src/components/common-base/form/form-select/types/form-select-types.ts @@ -0,0 +1,8 @@ +export type SelectValueType = string | number; + +export interface SelectOptionItem { + /** 显示的文案 */ + label: string; + /** 绑定值 */ + value: SelectValueType; +} diff --git a/src/components/common-base/form/form-select/use-form-select.ts b/src/components/common-base/form/form-select/use-form-select.ts new file mode 100644 index 0000000000000000000000000000000000000000..741a90af308b9fbb91a17b1867527786f4133ea4 --- /dev/null +++ b/src/components/common-base/form/form-select/use-form-select.ts @@ -0,0 +1,201 @@ +import { translate } from '@/assets/lang'; +import { isArray } from '@/assets/utils/array'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, ref } from 'vue'; +import { SelectOptionItem, SelectValueType } from './types/form-select-types'; +import { formCommonProps, useFormCommonValidate } from '../hooks/use-form-common'; + +export const formSelectProps = () => ({ + ...formCommonProps(), + // 绑定值 + value: PropUtils.oneOfType([String, Number, Array]).def(''), + // 输入框标题 + title: PropUtils.string, + // 占位符 + placeholder: PropUtils.string, + // 选项列表 + options: PropUtils.array(), + // 是否多选 + multiple: PropUtils.bool.def(false), +}); + +export const formSelectEmits = () => ({ + input: emitFunc(), +}); + +export const useFormSelect = (options: { + props: VueProps; + emit: VueEmit; + closeOptions?: () => void; +}) => { + const { props, emit, closeOptions } = options; + const { formItemIsError, blurToValidateItem, focusToRemoveError } = useFormCommonValidate({ + props, + }); + + const { title, multiple } = useProps(props); + + /** 输入框的 value 值 */ + const inputValue = computed(() => { + let valText = ''; + + if (props.multiple && isArray(props.value) && props.value.length) { + return `${translate('form.select.multiPrefix')}${props.value.length}${translate( + 'form.select.multiSuffix', + )}`; + } + + if (!props.multiple) { + const index = props.options.findIndex(option => option.value === props.value); + if (index !== -1) { + valText = props.options[index].label; + } + } + + return valText; + }); + + /** 输入框占位符 */ + const inputPlaceholder = computed(() => props.placeholder); + + /** 选项列表 */ + const optionList = computed(() => { + return props.options; + }); + + function onClickOption(option: SelectOptionItem): void { + if (props.multiple && isArray(props.value)) { + if (props.value.includes(option.value)) { + emit( + 'input', + props.value.filter(item => item !== option.value), + ); + } else { + emit('input', [...props.value, option.value]); + } + } + + if (!props.multiple) { + if (option.value !== props.value) { + emit('input', option.value); + } + + if (closeOptions) { + closeOptions(); + } + } + } + + /** 是否已选中 */ + function isSelectedOption(value: SelectValueType): boolean { + if (props.multiple && isArray(props.value)) { + return props.value.includes(value); + } + if (!props.multiple) { + return value === props.value; + } + return false; + } + + return { + multiple, + title, + inputValue, + inputPlaceholder, + optionList, + onClickOption, + isSelectedOption, + + formItemIsError, + blurToValidateItem, + focusToRemoveError, + }; +}; + +/** + * 移动端下的 select + */ +export const useFormSelectMobile = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + /** 选项是否显示 */ + const optionVisible = ref(false); + + /** 打开选项 */ + function openOption() { + optionVisible.value = true; + } + + /** 关闭选项 */ + function closeOption() { + optionVisible.value = false; + } + + /** 已选的 value */ + const selectValue = ref(''); + + /** 处理点击选项 */ + function onClickOption(option: SelectOptionItem): void { + if (!props.multiple) { + selectValue.value = option.value; + return; + } + + if (!isArray(selectValue.value)) { + selectValue.value = []; + } + + if (selectValue.value.includes(option.value)) { + selectValue.value = selectValue.value.filter(item => item !== option.value); + } else { + selectValue.value = [...selectValue.value, option.value]; + } + } + + /** 处理点击确认 */ + function onClickConfirm() { + if (!props.multiple) { + if (!selectValue.value) { + return; + } + + if (selectValue.value !== props.value) { + emit('input', selectValue.value); + closeOption(); + } + } + + if (props.multiple) { + if (!isArray(selectValue.value)) { + selectValue.value = []; + } + + emit('input', selectValue.value); + closeOption(); + } + } + + /** 是否已选中 */ + function isSelectedOption(value: SelectValueType): boolean { + if (props.multiple && isArray(selectValue.value)) { + return selectValue.value.includes(value); + } + if (!props.multiple) { + return value === selectValue.value; + } + return false; + } + + return { + optionVisible, + openOption, + closeOption, + onClickOption, + onClickConfirm, + isSelectedOption, + }; +}; diff --git a/src/components/common-base/form/form-slide-verify/mobile-form-slide-verify.vue b/src/components/common-base/form/form-slide-verify/mobile-form-slide-verify.vue new file mode 100644 index 0000000000000000000000000000000000000000..955b6f256443632f03b485692c06183877b07318 --- /dev/null +++ b/src/components/common-base/form/form-slide-verify/mobile-form-slide-verify.vue @@ -0,0 +1,61 @@ + + + + + + diff --git a/src/components/common-base/form/form-slide-verify/pc-form-slide-verify.vue b/src/components/common-base/form/form-slide-verify/pc-form-slide-verify.vue new file mode 100644 index 0000000000000000000000000000000000000000..be24ba51c0fdaa60a142e2c5b221a0163e9904fa --- /dev/null +++ b/src/components/common-base/form/form-slide-verify/pc-form-slide-verify.vue @@ -0,0 +1,54 @@ + + + + + + diff --git a/src/components/common-base/form/form-slide-verify/use-slide-verify.ts b/src/components/common-base/form/form-slide-verify/use-slide-verify.ts new file mode 100644 index 0000000000000000000000000000000000000000..839089d53bd8c4b8e5993eef1c5cc42f32e62b8a --- /dev/null +++ b/src/components/common-base/form/form-slide-verify/use-slide-verify.ts @@ -0,0 +1,100 @@ +/** + * @file 滑块验证码 hook + */ + +import { loadAliAwsc } from '@/plugins/external-lib-loaders/load-ali-awsc'; +import { randomStr } from '@utils-ts/string'; +import { onMounted } from 'vue'; +import { useLangStore } from '@/store/use-lang-store'; +import { LangType } from '@/assets/lang/lang-enum'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { getWatchCore } from '@/core/watch-sdk'; + +export interface SlideVerifyData { + sessionId: string; + sig: string; + token: string; + scene: string; +} + +export const formSlideVerifyProps = () => ({ + /** 绑定的验证值 */ + value: PropUtils.objectType(), +}); + +export const formSlideVerifyEmits = () => ({ + input: emitFunc(), + 'verify-success': emitFunc(), + 'verify-fail': emitFunc(), + 'verify-error': emitFunc(), +}); + +export const useSlideVerify = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { emit } = options; + const langStore = useLangStore(); + + const id = randomStr(8); + const scene = 'nc_register_h5'; + + // 语言配置 + const langConfig = { + cn: { + SLIDE: '按住滑块,拖动到最右', + }, + }; + + /** 初始化验证码 */ + async function initSlideVerify() { + const AWSC = await loadAliAwsc(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + AWSC.use('nc', (state: unknown, module: any) => { + const watchCore = getWatchCore(); + module.init({ + // 应用类型标识。它和使用场景标识(scene字段)一起决定了滑动验证的业务场景与后端对应使用的策略模型。您可以在阿里云验证码控制台的配置管理页签找到对应的appkey字段值,请务必正确填写。 + appkey: watchCore.utils.getAliSliderAppKey(), + // 使用场景标识。它和应用类型标识(appkey字段)一起决定了滑动验证的业务场景与后端对应使用的策略模型。您可以在阿里云验证码控制台的配置管理页签找到对应的scene值,请务必正确填写。 + scene, + // 声明滑动验证需要渲染的目标ID。 + renderTo: `#${id}`, + // 配置语言 + language: langStore.currentLang === LangType.Chinese ? 'cn' : 'en', + // 更新多语言配置 + upLang: langConfig, + // 前端滑动验证通过时会触发该回调参数。您可以在该回调参数中将会话ID(sessionId)、签名串(sig)、请求唯一标识(token)字段记录下来,随业务请求一同发送至您的服务端调用验签。 + success: (data: SlideVerifyData) => { + const slideData = { + sessionId: data.sessionId, + sig: data.sig, + token: data.token, + scene, + }; + + emit('input', slideData); + emit('verify-success', slideData); + }, + // 滑动验证失败时触发该回调参数。 + fail: (failCode: string) => { + emit('verify-fail', failCode); + }, + // 验证码加载出现异常时触发该回调参数。 + error: (errorCode: string) => { + emit('verify-error', errorCode); + }, + }); + }); + } + + onMounted(() => { + initSlideVerify(); + }); + + return { + id, + initSlideVerify, + }; +}; diff --git a/src/components/common-base/form/form-sms-verify-input/mobile-form-sms-verify-input.vue b/src/components/common-base/form/form-sms-verify-input/mobile-form-sms-verify-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..2bf875fac05296486fc8a316ff557f163a54ef17 --- /dev/null +++ b/src/components/common-base/form/form-sms-verify-input/mobile-form-sms-verify-input.vue @@ -0,0 +1,83 @@ + + + + + + diff --git a/src/components/common-base/form/form-sms-verify-input/pc-form-sms-verify-input.vue b/src/components/common-base/form/form-sms-verify-input/pc-form-sms-verify-input.vue new file mode 100644 index 0000000000000000000000000000000000000000..49174b43298265dae45dba24e11b654d7e7d76ab --- /dev/null +++ b/src/components/common-base/form/form-sms-verify-input/pc-form-sms-verify-input.vue @@ -0,0 +1,91 @@ + + + + + + diff --git a/src/components/common-base/form/form-sms-verify-input/use-sms-verify-input.ts b/src/components/common-base/form/form-sms-verify-input/use-sms-verify-input.ts new file mode 100644 index 0000000000000000000000000000000000000000..c11a6f28e5da64d8f7e362f888e736b616b0cc9f --- /dev/null +++ b/src/components/common-base/form/form-sms-verify-input/use-sms-verify-input.ts @@ -0,0 +1,242 @@ +/** + * @file 短信验证码输入框 hook + */ + +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { translate } from '@/assets/lang'; +import { validateImageCaptcha, validatePhoneNumber } from '@/assets/utils/validate'; +import { computed, ref, unref, watchEffect } from 'vue'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useSecondCountDown } from '@/hooks/tools/use-count-down'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { + SmsScene, + SmsValidType, + SendSmsVerifyCodeFail, + SendSmsVerifyResult, +} from '@polyv/live-watch-sdk'; +import { InputValueType } from '../form-input/hooks/use-form-input'; +import { ImageVerifyInputInstance } from '../form-image-verify-input/type'; +import { isArray } from '@/assets/utils/array'; + +/** 验证类型,imageCode - 图片验证码,sliderCode - 滑块验证码 */ +export type VerifyType = 'imageCaptcha' | 'sliderCode'; + +/** + * props 配置 + */ +export const formSmsVerifyInputProps = () => ({ + /** 绑定值 */ + value: PropUtils.string.def(''), + /** 区号 */ + areaCode: PropUtils.string.def('+86'), + /** 手机号 */ + phoneNumber: PropUtils.string.def(''), + /** 短信场景 */ + smsScene: PropUtils.enum().isRequired, + /** 验证类型 */ + validType: PropUtils.enum().def(SmsValidType.Image), + /** 图片验证码 id */ + imageId: PropUtils.string.def(''), + /** 图片验证码 */ + imageCaptcha: PropUtils.string.def(''), + /** 图片验证码节点 */ + imageVerifyInputRef: PropUtils.oneOfType([ + Array, + Object, + ]), +}); + +/** + * emit 配置 + */ +export const formSmsVerifyInputEmits = () => ({ + input: emitFunc(), +}); + +export const useSmsVerifyInput = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + const { areaCode, phoneNumber, smsScene, imageCaptcha, imageId, validType } = useProps(props); + const { surplusTime, initCountDown } = useSecondCountDown({ + second: 60, + }); + + /** 输入框占位文本 */ + const inputPlaceholder = computed(() => { + return translate('form.placeholder.smsVerify'); + }); + + /** 发送按钮是否禁用 */ + const sendDisabled = computed(() => { + let isDisabled = false; + const areaCodeVal = unref(areaCode); + const phoneNumberVal = unref(phoneNumber); + + // 手机号不规范 + if (!validatePhoneNumber(phoneNumberVal, areaCodeVal)) { + isDisabled = true; + } + + // 存在倒计时 + if (unref(surplusTime).seconds !== 0) { + isDisabled = true; + } + + // 如果是图片验证码则验证长度 + if ( + unref(validType) === SmsValidType.Image && + !validateImageCaptcha(unref(imageCaptcha), unref(imageId)) + ) { + isDisabled = true; + } + + return isDisabled; + }); + + /** 发送按钮文本 */ + const sendText = computed(() => { + const seconds = unref(surplusTime).seconds; + if (seconds !== 0) { + return `${translate('form.resend')}${seconds}${translate('unit.second')}`; + } + + return translate('form.getSmsVerify'); + }); + + /** 处理输入框输入 */ + function onInputChange(val: InputValueType): void { + emit('input', `${val}`); + } + + /** 滑块验证码是否显示 */ + const slideVerifyVisible = ref(false); + + watchEffect(() => { + if (unref(sendDisabled)) { + slideVerifyVisible.value = false; + } + }); + + /** 处理点击发送 */ + function onClickSend() { + if (unref(sendDisabled)) { + return; + } + + if (unref(validType) === SmsValidType.Slider) { + slideVerifyVisible.value = true; + } else { + toSendImageSmsCode(); + } + } + + /** + * 发送短信验证码(滑块验证) + */ + async function toSendSliderSmsCode(slideData: AliAwscSliceData) { + const watchCore = getWatchCore(); + const validTypeVal = unref(validType); + + let result: SendSmsVerifyResult | undefined; + + if (validTypeVal === SmsValidType.Slider) { + result = await watchCore.sms.sendSmsVerifyCode({ + phoneNumber: unref(phoneNumber), + areaCode: unref(areaCode), + validType: validTypeVal, + scene: unref(smsScene), + sessionId: slideData.sessionId, + sig: slideData.sig, + token: slideData.token, + }); + } + + if (!result) { + return; + } + + if (result.success) { + onSendSuccess(); + return; + } + + toast.error(translate('base.frequentOperation')); + slideVerifyVisible.value = false; + } + + /** + * 发送短信验证码(图片验证) + */ + async function toSendImageSmsCode() { + const watchCore = getWatchCore(); + const validTypeVal = unref(validType); + + let result: SendSmsVerifyResult | undefined; + + if (validTypeVal === SmsValidType.Image) { + result = await watchCore.sms.sendSmsVerifyCode({ + phoneNumber: unref(phoneNumber), + areaCode: unref(areaCode), + validType: validTypeVal, + scene: unref(smsScene), + imageId: unref(imageId), + imageCaptcha: unref(imageCaptcha), + }); + } + + if (!result) { + return; + } + + if (result.success) { + onSendSuccess(); + return; + } + + const failReason = result.failReason; + if (failReason === SendSmsVerifyCodeFail.ImageCaptchaError) { + toast.error(translate('form.error.imageCaptchaError')); + refreshImageVerify(); + } + } + + /** 刷新图片验证码的输入框 */ + function refreshImageVerify() { + if (isArray(props.imageVerifyInputRef)) { + props.imageVerifyInputRef.forEach(instance => { + instance.refreshVerifyImage(true); + }); + } else { + props.imageVerifyInputRef?.refreshVerifyImage(true); + } + } + + /** + * 处理发送成功 + */ + function onSendSuccess() { + // 开始倒计时 + initCountDown(); + + // 2秒后隐藏滑块验证码 + setTimeout(() => { + slideVerifyVisible.value = false; + }, 2000); + } + + return { + inputPlaceholder, + sendDisabled, + sendText, + SmsValidType, + onInputChange, + slideVerifyVisible, + onClickSend, + toSendSliderSmsCode, + toSendImageSmsCode, + }; +}; diff --git a/src/components/common-base/form/form-submit-button.vue b/src/components/common-base/form/form-submit-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..c15b5961c549c9d57a81dd2991a616f25bfc8024 --- /dev/null +++ b/src/components/common-base/form/form-submit-button.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/components/common-base/form/form-upload-image/form-upload-image.vue b/src/components/common-base/form/form-upload-image/form-upload-image.vue new file mode 100644 index 0000000000000000000000000000000000000000..83b941efce20988101d0a04f8052903dd849e6ba --- /dev/null +++ b/src/components/common-base/form/form-upload-image/form-upload-image.vue @@ -0,0 +1,104 @@ + + + + + + diff --git a/src/components/common-base/form/form-upload-image/use-form-upload-image.ts b/src/components/common-base/form/form-upload-image/use-form-upload-image.ts new file mode 100644 index 0000000000000000000000000000000000000000..bea4a3bd74ae04ff6bdb8bf653e0c4054541733b --- /dev/null +++ b/src/components/common-base/form/form-upload-image/use-form-upload-image.ts @@ -0,0 +1,71 @@ +import { translate } from '@/assets/lang'; +import { isMobile } from '@/assets/utils/browser'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { getWatchCore } from '@/core/watch-sdk'; +import { toast } from '@/hooks/components/use-toast'; +import { computed, unref } from 'vue'; + +export const formUploadImageProps = () => ({ + /** 绑定值 */ + value: PropUtils.array(), + /** 最大上传数量 */ + maxCount: PropUtils.number.def(Infinity), +}); + +export const formUploadImageEmit = () => ({ + input: emitFunc(), +}); + +export const useFormUploadImage = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + const { value, maxCount } = useProps(props); + + /** 处理点击上传 */ + async function onClickUpload() { + try { + if (unref(value).length >= unref(maxCount)) { + return; + } + + const watchCore = getWatchCore(); + const { imageUrl } = await watchCore.utils.uploadImage(); + const newVal = [...unref(value), imageUrl]; + emit('input', newVal); + } catch (error) { + toast.error(translate('watchCore.error.uploadImage')); + } + } + + /** 处理点击删除 */ + function onClickDelete(index: number) { + const newVal = unref(value).filter((url, _index) => _index !== index); + emit('input', newVal); + } + + /** 数量提示 */ + const countTips = computed(() => { + if (props.maxCount === 1 || props.maxCount === Infinity) { + return ''; + } + const surplusCount = props.maxCount - props.value.length; + if (surplusCount === 0) { + return ''; + } + return `(${translate('form.uploadImage.prefixTips')}${surplusCount}${translate( + 'form.uploadImage.suffixTips', + )})`; + }); + + return { + value, + maxCount, + onClickUpload, + onClickDelete, + countTips, + isMobile, + }; +}; diff --git a/src/components/common-base/form/form-wrap.vue b/src/components/common-base/form/form-wrap.vue new file mode 100644 index 0000000000000000000000000000000000000000..c602f7797158fa3a48856f9cb8d43d7c288e0f88 --- /dev/null +++ b/src/components/common-base/form/form-wrap.vue @@ -0,0 +1,23 @@ + + + + diff --git a/src/components/common-base/form/hooks/use-form-common.ts b/src/components/common-base/form/hooks/use-form-common.ts new file mode 100644 index 0000000000000000000000000000000000000000..146b6e406ec334ea5b348951411c25415fff2b60 --- /dev/null +++ b/src/components/common-base/form/hooks/use-form-common.ts @@ -0,0 +1,90 @@ +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, unref } from 'vue'; +import { FormSize, FormTheme } from '../types/enums'; +import { useFormItemInject } from './use-form-item'; +import { useFormWrapPropsInject } from './use-form'; + +export const formCommonProps = () => ({ + /** 尺寸 */ + size: PropUtils.enum(), + /** 主题 */ + theme: PropUtils.enum(), + /** 是否进行表单验证 */ + validateForm: PropUtils.bool.def(true), +}); + +export const useFormCommon = (options: { + props: VueProps; + classPrefix: string; +}) => { + const { props, classPrefix } = options; + const formProps = useFormWrapPropsInject(); + + /** 表单组件公用 className */ + const commonClassNames = computed(() => { + const list = []; + + // 尺寸 + const sizeVal = props.size; + if (sizeVal && sizeVal !== FormSize.Default) { + list.push(`${classPrefix}--size-${sizeVal}`); + } + + // 主题 + const themeVal = props.theme || formProps?.theme; + if (unref(themeVal)) { + list.push(`${classPrefix}--theme-${themeVal}`); + } + + return list; + }); + + return { + commonClassNames, + }; +}; + +/** 表单组件下的验证状态 hook */ +export const useFormCommonValidate = (options: { props: VueProps }) => { + const { props } = options; + const formItemContext = useFormItemInject(); + + /** 验证当前的表单节点 */ + async function validateCurrentFormItem() { + await formItemContext?.validateFormItem(); + } + + /** 在失焦时进行表单节点验证 */ + async function blurToValidateItem() { + if (!props.validateForm) { + return; + } + + await validateCurrentFormItem(); + } + + /** 在聚焦时移除表单节点异常提示 */ + function focusToRemoveError() { + if (!props.validateForm) { + return; + } + + formItemContext?.removeErrorMessage(); + } + + /** 表单节点是否处于异常 */ + const formItemIsError = computed(() => { + if (!props.validateForm || !formItemContext) { + return false; + } + + return unref(formItemContext.formItemIsError); + }); + + return { + validateCurrentFormItem, + blurToValidateItem, + focusToRemoveError, + formItemIsError, + }; +}; diff --git a/src/components/common-base/form/hooks/use-form-item.ts b/src/components/common-base/form/hooks/use-form-item.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ea7b1b954d8b434f051fd5d2eddb08cf006234b --- /dev/null +++ b/src/components/common-base/form/hooks/use-form-item.ts @@ -0,0 +1,188 @@ +import { isMobile } from '@/assets/utils/browser'; +import { formatStyleSize } from '@/assets/utils/dom'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import Schema from '@/plugins/async-validator'; +import { computed, inject, InjectionKey, provide, ref, unref, watchEffect } from 'vue'; +import { + FormItemInstance, + formItemLabelModels, + FormItemLabelModel, + FormValidateOptions, +} from '../types/form-types'; +import { useFormWrapPropsInject } from './use-form'; + +export const formItemProps = () => ({ + /** 该节点属于表单数据中的哪个字段 */ + formField: PropUtils.looseString, + /** 节点标签 */ + label: PropUtils.looseString, + /** 表单标题宽度 */ + labelWidth: PropUtils.looseNumber, + /** 表单标题高度 */ + labelHeight: PropUtils.looseNumber, + /** 表单输入框内容宽度 */ + contentWidth: PropUtils.looseNumber, + /** 是否显示异常信息 */ + showErrorMessage: PropUtils.bool.def(true), + /** 节点异常信息 */ + errorMessage: PropUtils.looseString, + /** label 模式 */ + labelModel: PropUtils.oneOf(formItemLabelModels), + /** 是否显示必填星号 */ + required: PropUtils.bool.def(false), +}); + +/** 组件名 */ +export const FORM_ITEM_COMPONENT_NAME = 'form-item'; + +/** + * 判断 vue 实例是否为 + * @param instance 实例 + */ +export const isFormItemInstance = (instance: unknown): instance is FormItemInstance => { + const _instance = instance as Partial | undefined; + return _instance?.isFormItem ?? false; +}; + +export const FORM_ITEM_PROVIDE_KEY: InjectionKey = + Symbol('FORM_ITEM_PROVIDE_KEY'); + +export const useFormItemInject = (): FormItemInstance | undefined => { + return inject(FORM_ITEM_PROVIDE_KEY); +}; + +export const useFormItem = (options: { props: VueProps }) => { + const { props } = options; + + const required = computed(() => props.required); + + const formProps = useFormWrapPropsInject(); + + const labelText = computed(() => props.label); + + const labelModel = computed( + () => props.labelModel ?? formProps?.labelModel ?? 'inline', + ); + + /** 节点标签样式 */ + const labelStyle = computed(() => { + if (unref(labelModel) === 'header') { + return {}; + } + + const width = props.labelWidth ?? formProps?.labelWidth; + const height = props.labelHeight ?? formProps?.labelHeight; + + return { + width: formatStyleSize(width), + height: formatStyleSize(height), + }; + }); + + /** 节点内容宽度样式 */ + const contentWidthStyle = computed(() => { + const width = props.contentWidth ?? formProps?.contentWidth; + + return formatStyleSize(width); + }); + + /** 节点异常提示 */ + const itemErrorMessage = ref(); + /** 当前表单节点是否异常 */ + const formItemIsError = computed(() => typeof unref(itemErrorMessage) !== 'undefined'); + + watchEffect(() => { + if (props.errorMessage) { + setErrorMessage(props.errorMessage); + } else { + itemErrorMessage.value = undefined; + } + }); + + /** 设置异常提示 */ + function setErrorMessage(message: string | undefined): void { + if (message && !props.showErrorMessage) return; + + itemErrorMessage.value = message; + } + + /** 移除异常提示 */ + function removeErrorMessage() { + itemErrorMessage.value = undefined; + } + + /** 验证当前表单节点 */ + function validateFormItem(validateOptions: FormValidateOptions = {}): Promise { + const showErrorMessage = validateOptions.showErrorMessage ?? true; + + // 当前节点验证的字段名 + const validateField = props.formField; + const formDataVal = formProps?.formData; + const formRulesVal = formProps?.formRules; + + /** + * 没有传验证字段 + * 中没有 formData + * 以上条件满足其一则不进行验证,默认通过验证 + */ + if (!validateField || !formDataVal || !formRulesVal || !formRulesVal[validateField]) { + return Promise.resolve(); + } + + // 创建验证对象 + const descriptor = { + [validateField]: formRulesVal[validateField], + }; + const validateVal = { + [validateField]: formDataVal[validateField], + }; + const validator = new Schema(descriptor); + + // 执行验证 + let message: string | undefined; + validator.validate(validateVal, errors => { + if (errors && errors.length) { + message = errors[0].message; + } + }); + + // 验证失败则提示 + if (showErrorMessage && props.showErrorMessage) { + setErrorMessage(message); + } + + return new Promise((resolve, reject) => { + if (typeof message === 'undefined') { + resolve(); + } else { + reject(new Error(message)); + } + }); + } + + // expose 导出对象 + const formItemInstance: FormItemInstance = { + isFormItem: true, + formItemIsError, + setErrorMessage, + removeErrorMessage, + validateFormItem, + }; + + // 向下注入 + provide(FORM_ITEM_PROVIDE_KEY, formItemInstance); + + return { + required, + labelText, + labelModel, + labelStyle, + contentWidthStyle, + itemErrorMessage, + formItemIsError, + removeErrorMessage, + validateFormItem, + formItemInstance, + isMobile, + }; +}; diff --git a/src/components/common-base/form/hooks/use-form-submit-button.ts b/src/components/common-base/form/hooks/use-form-submit-button.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae1e9a4ab7d0cfd9808aafb4298582014b2a4ef3 --- /dev/null +++ b/src/components/common-base/form/hooks/use-form-submit-button.ts @@ -0,0 +1,64 @@ +import { ref, unref, watch } from 'vue'; +import { useFormWrapInject, useFormWrapPropsInject } from './use-form'; + +export const useFormSubmitButton = () => { + const formProps = useFormWrapPropsInject(); + const formContext = useFormWrapInject(); + + /** 提交按钮是否禁用 */ + const buttonIsDisabled = ref(false); + + /** 检查按钮是否需要禁用 */ + async function checkDisabled(): Promise { + let isDisabled = false; + + if (!formContext) { + return; + } + + // 表单正在提交中 + if (unref(formContext.isSubmiting)) { + isDisabled = true; + buttonIsDisabled.value = isDisabled; + return; + } + + // 进行表单验证 + try { + if (formContext.validateCurrentForm) { + await formContext.validateCurrentForm({ + showErrorMessage: false, + }); + } + } catch (e) { + if (unref(formContext.debug)) { + console.error('form-submit-button-error', e); + } + isDisabled = true; + } + + buttonIsDisabled.value = isDisabled; + } + + watch( + () => { + return [formProps && formProps.formData, formContext && unref(formContext.isSubmiting)]; + }, + () => checkDisabled(), + { + immediate: true, + deep: true, + }, + ); + + /** 处理按钮点击 */ + function onButtonClick() { + formContext && formContext.submitForm(); + } + + return { + buttonIsDisabled, + checkDisabled, + onButtonClick, + }; +}; diff --git a/src/components/common-base/form/hooks/use-form.ts b/src/components/common-base/form/hooks/use-form.ts new file mode 100644 index 0000000000000000000000000000000000000000..40d133781868d2e417764a66cc5b5a120a7beed6 --- /dev/null +++ b/src/components/common-base/form/hooks/use-form.ts @@ -0,0 +1,130 @@ +import { useVue } from '@/hooks/core/use-vue'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { ComputedRef, inject, InjectionKey, provide, Ref, ref, unref } from 'vue'; +import { FormTheme } from '../types/enums'; +import { formItemLabelModels, FormValidateOptions } from '../types/form-types'; +import { isFormItemInstance } from './use-form-item'; + +export const formWrapProps = () => ({ + /** 绑定的表单对象 */ + formData: PropUtils.object().def({}), + /** 验证规则 */ + formRules: PropUtils.object().def({}), + /** 表单标题宽度 */ + labelWidth: PropUtils.number.def(170), + /** 表单标题高度 */ + labelHeight: PropUtils.number.def(40), + /** 表单输入框内容宽度 */ + contentWidth: PropUtils.looseNumber, + /** 表单提交方法 */ + submitAction: PropUtils.func(), + /** 表单主题,dark-黑暗 */ + theme: PropUtils.enum(), + /** label 模式,默认:inline */ + labelModel: PropUtils.oneOf(formItemLabelModels).def('inline'), + /** 是否开启 debug 打印,默认:false */ + debug: PropUtils.bool.def(false), +}); + +export const formWrapEmits = () => ({ + // 表单提交事件 + 'submit-form': emitFunc(), +}); + +export type FormWrapProps = VueProps; + +/** 注入类型 */ +export interface FormWrapInjectData { + /** 是否正在提交 */ + isSubmiting: Ref; + /** debug */ + debug: ComputedRef; + /** 验证当前表单 */ + validateCurrentForm(validateOptions?: FormValidateOptions): Promise; + /** 提交当前表单 */ + submitForm(validateOptions?: FormValidateOptions): Promise; +} + +export const FORM_WRAP_PROPS_PROVIDE_KEY: InjectionKey = Symbol( + 'FORM_WRAP_PROPS_PROVIDE_KEY', +); + +export const useFormWrapPropsInject = (): FormWrapProps | undefined => { + return inject(FORM_WRAP_PROPS_PROVIDE_KEY); +}; + +export const FORM_WRAP_PROVIDE_KEY: InjectionKey = + Symbol('FORM_WRAP_PROVIDE_KEY'); + +export const useFormWrapInject = (): FormWrapInjectData | undefined => { + return inject(FORM_WRAP_PROVIDE_KEY); +}; + +export const useFormWrap = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const { formData, submitAction, debug } = useProps(props); + const { getCurrentChildrens } = useVue(); + + /** 表单是否正在提交 */ + const isSubmiting = ref(false); + + /** 验证当前表单 */ + async function validateCurrentForm(validateOptions: FormValidateOptions = {}): Promise { + const childrens = getCurrentChildrens(); + if (!childrens || childrens.length === 0) { + return; + } + + for (let i = 0; i < childrens.length; i++) { + const instance = childrens[i]; + if (isFormItemInstance(instance)) { + try { + await instance.validateFormItem(validateOptions); + } catch (e) { + return Promise.reject(e); + } + } + } + } + + /** 验证并触发表单提交 */ + async function submitForm(validateOptions: FormValidateOptions): Promise { + await validateCurrentForm(validateOptions); + + emit('submit-form', unref(formData)); + + const submitActionFn = unref(submitAction); + if (typeof submitActionFn === 'function') { + isSubmiting.value = true; + try { + await submitActionFn(unref(formData)); + } catch (e) { + isSubmiting.value = false; + } finally { + isSubmiting.value = false; + } + } + } + + // 向下注入 props + provide(FORM_WRAP_PROPS_PROVIDE_KEY, props); + + provide(FORM_WRAP_PROVIDE_KEY, { + debug, + isSubmiting, + validateCurrentForm, + submitForm, + }); + + return { + isSubmiting, + validateCurrentForm, + submitForm, + }; +}; diff --git a/src/components/common-base/form/imgs/form-upload-add.png b/src/components/common-base/form/imgs/form-upload-add.png new file mode 100644 index 0000000000000000000000000000000000000000..a3b1e6c2efa0347aeea44cd2074bd04a32df45d9 Binary files /dev/null and b/src/components/common-base/form/imgs/form-upload-add.png differ diff --git a/src/components/common-base/form/imgs/form-upload-del.png b/src/components/common-base/form/imgs/form-upload-del.png new file mode 100644 index 0000000000000000000000000000000000000000..03322421f786c2801d0237843d81d40266037bb2 Binary files /dev/null and b/src/components/common-base/form/imgs/form-upload-del.png differ diff --git a/src/components/common-base/form/imgs/form-upload-failed.png b/src/components/common-base/form/imgs/form-upload-failed.png new file mode 100644 index 0000000000000000000000000000000000000000..5c0565654b5d841433a8de81f8fc641d50759f9d Binary files /dev/null and b/src/components/common-base/form/imgs/form-upload-failed.png differ diff --git a/src/components/common-base/form/imgs/form-upload-loading.png b/src/components/common-base/form/imgs/form-upload-loading.png new file mode 100644 index 0000000000000000000000000000000000000000..600b64c8778149f02223c5220324cbd9619e56f1 Binary files /dev/null and b/src/components/common-base/form/imgs/form-upload-loading.png differ diff --git a/src/components/common-base/form/types/enums.ts b/src/components/common-base/form/types/enums.ts new file mode 100644 index 0000000000000000000000000000000000000000..4822b06c575942661340ddebec13be3479ffbb4e --- /dev/null +++ b/src/components/common-base/form/types/enums.ts @@ -0,0 +1,19 @@ +/** 表单尺寸 */ +export enum FormSize { + /** 默认 - 48px */ + Default = 'default', + /** 中 - 40px */ + Medium = 'medium', + /** 小 - 40px */ + Small = 'small', +} + +/** 表单主题 */ +export enum FormTheme { + /** 默认 */ + Default = 'default', + /** 黑暗 */ + Dark = 'dark', + /** 简约 */ + Simplicity = 'simplicity', +} diff --git a/src/components/common-base/form/types/form-types.ts b/src/components/common-base/form/types/form-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..825a04622e016f562a2026f76c3b603594f15695 --- /dev/null +++ b/src/components/common-base/form/types/form-types.ts @@ -0,0 +1,29 @@ +import { tupleString } from '@/assets/utils/array'; +import { ComputedRef } from 'vue'; + +/** + * 表单验证选项 + */ +export interface FormValidateOptions { + /** 是否显示异常消息 */ + showErrorMessage?: boolean; +} + +/** + * 表单节点对象 + */ +export interface FormItemInstance { + isFormItem: true; + /** 表单节点是否验证异常 */ + formItemIsError: ComputedRef; + /** 设置异常提示 */ + setErrorMessage(message: string | undefined): void; + /** 移除异常提示 */ + removeErrorMessage(): void; + /** 验证表单节点 */ + validateFormItem(validateOptions?: FormValidateOptions): Promise; +} + +export const formItemLabelModels = tupleString('inline', 'header'); +/** 表单节点 label 样式,inline-行内,header-头部 */ +export type FormItemLabelModel = typeof formItemLabelModels[number]; diff --git a/src/components/common-base/form/utils/utils.ts b/src/components/common-base/form/utils/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..715978353134582fa65bb2a1a4cf4ac80a3e23d8 --- /dev/null +++ b/src/components/common-base/form/utils/utils.ts @@ -0,0 +1,14 @@ +import { SelectOptionItem } from '../form-select/types/form-select-types'; + +/** + * 格式化字符串数组下拉选项 + */ +export function formatSimpleSelectOptions(options: string[]): SelectOptionItem[] { + const strList = options.filter(str => !!str); + return strList.map(str => { + return { + label: str, + value: str, + }; + }); +} diff --git a/src/components/common-base/iframe-render/mobile-iframe-render.vue b/src/components/common-base/iframe-render/mobile-iframe-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..935a7127cae732b4f3cf0b13c1a82a3da136783e --- /dev/null +++ b/src/components/common-base/iframe-render/mobile-iframe-render.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/src/components/common-base/iframe-render/pc-iframe-render.vue b/src/components/common-base/iframe-render/pc-iframe-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..10cc00a1e52fdff31da6e21a68dc5b820eb1e4dd --- /dev/null +++ b/src/components/common-base/iframe-render/pc-iframe-render.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/src/components/common-base/iframe-render/use-iframe-render.ts b/src/components/common-base/iframe-render/use-iframe-render.ts new file mode 100644 index 0000000000000000000000000000000000000000..4eae674699f34756ed0348cce76a00f070ea4133 --- /dev/null +++ b/src/components/common-base/iframe-render/use-iframe-render.ts @@ -0,0 +1,32 @@ +import { formatStyleSize } from '@/assets/utils/dom'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed } from 'vue'; +import { CSSProperties } from 'vue/types/jsx'; + +export const iframeRenderProps = () => ({ + /** 地址 */ + src: PropUtils.looseString, + /** 高度,默认:600 */ + height: PropUtils.oneOfType([String, Number]).def(600), +}); + +export const useIframeRender = (options: { props: VueProps }) => { + const { props } = options; + + const iframeSrc = computed(() => props.src); + + const wrapStyle = computed(() => { + const styles: CSSProperties = {}; + + if (props.height) { + styles.height = formatStyleSize(props.height); + } + + return styles; + }); + + return { + iframeSrc, + wrapStyle, + }; +}; diff --git a/src/components/common-base/list-loading/imgs/loading.png b/src/components/common-base/list-loading/imgs/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..8a2d5b705698d63ce54f18b469b30eab095949b8 Binary files /dev/null and b/src/components/common-base/list-loading/imgs/loading.png differ diff --git a/src/components/common-base/list-loading/list-loading.vue b/src/components/common-base/list-loading/list-loading.vue new file mode 100644 index 0000000000000000000000000000000000000000..08f686097b72b0da1bee91f631495905f6637f37 --- /dev/null +++ b/src/components/common-base/list-loading/list-loading.vue @@ -0,0 +1,15 @@ + + + diff --git a/src/components/common-base/normal-button/normal-button.vue b/src/components/common-base/normal-button/normal-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..33616e3b8d7e936bb43a6dda7d1ec590fa15a08e --- /dev/null +++ b/src/components/common-base/normal-button/normal-button.vue @@ -0,0 +1,176 @@ + + + + + + diff --git a/src/components/common-base/normal-button/types.ts b/src/components/common-base/normal-button/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e81bbbf713bf4cb746dee14eac19e96f3a93898 --- /dev/null +++ b/src/components/common-base/normal-button/types.ts @@ -0,0 +1,44 @@ +/** + * 按钮类型 + */ +export enum ButtonType { + /** 主题按钮 */ + Primary = 'primary', + /** 信息按钮 */ + Info = 'info', + /** 取消按钮 */ + Cancel = 'cancel', + /** 竖屏按钮 */ + Portrait = 'portrait', + /** 加重按钮 */ + Aggravate = 'aggravate', + /** 侧边菜单栏 */ + AsideMenu = 'aside-menu', +} + +/** + * 按钮尺寸 + */ +export enum ButtonSize { + /** 大 - 56px */ + Large = 'large', + /** 中 - 48px */ + Medium = 'medium', + /** 默认 - 40px */ + Default = 'default', + /** 小 - 32px */ + Small = 'small', + /** 超小 - 28px */ + Mini = 'mini', + /** 超级小 - 24px */ + XMini = 'x-mini', +} + +/** + * 按钮原生类型 + */ +export enum ButtonNativeType { + Button = 'button', + Submit = 'submit', + Reset = 'reset', +} diff --git a/src/components/common-base/normal-button/use-normal-button.ts b/src/components/common-base/normal-button/use-normal-button.ts new file mode 100644 index 0000000000000000000000000000000000000000..565202e8023997b1c9f08fdc431039d6e6e78aa4 --- /dev/null +++ b/src/components/common-base/normal-button/use-normal-button.ts @@ -0,0 +1,27 @@ +import { emitFunc } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils } from '@/assets/utils/vue-utils/props-utils'; +import { ButtonNativeType, ButtonSize, ButtonType } from './types'; + +export const normalButtonProps = () => ({ + /** 按钮文本,默认:按钮 */ + text: PropUtils.string.def('按钮'), + /** 原生类型,默认:button */ + nativeType: PropUtils.enum().def(ButtonNativeType.Button), + /** 按钮类型,默认:ButtonType.Primary */ + type: PropUtils.enum().def(ButtonType.Primary), + /** 按钮尺寸,默认:ButtonSize.Default */ + size: PropUtils.enum().def(ButtonSize.Default), + /** 按钮是否禁用,默认:false */ + disabled: PropUtils.bool.def(false), + /** 是否块状级按钮,默认:false */ + block: PropUtils.bool.def(false), + /** 按钮图标 */ + icon: PropUtils.icon(), + /** 按钮图标 class */ + iconClass: PropUtils.string, +}); + +export const normalButtonEmits = () => ({ + /** 点击事件 */ + click: emitFunc(), +}); diff --git a/src/components/common-base/phone-code/mobile-phone-code.vue b/src/components/common-base/phone-code/mobile-phone-code.vue new file mode 100644 index 0000000000000000000000000000000000000000..00a1bf02915d89b05535648ea404816e0368c11f --- /dev/null +++ b/src/components/common-base/phone-code/mobile-phone-code.vue @@ -0,0 +1,135 @@ + + + + + + diff --git a/src/components/common-base/phone-code/pc-phone-code.vue b/src/components/common-base/phone-code/pc-phone-code.vue new file mode 100644 index 0000000000000000000000000000000000000000..6b6a8309cbeeeb857117a5a09b7cea7a55a5d001 --- /dev/null +++ b/src/components/common-base/phone-code/pc-phone-code.vue @@ -0,0 +1,122 @@ + + + + + + diff --git a/src/components/common-base/phone-code/phone-code-data.ts b/src/components/common-base/phone-code/phone-code-data.ts new file mode 100644 index 0000000000000000000000000000000000000000..52b57d1ed968d0bdc7fc912acd9a908d680ff531 --- /dev/null +++ b/src/components/common-base/phone-code/phone-code-data.ts @@ -0,0 +1,931 @@ +const phoneCodeData = [ + { + letter: 'A', + }, + { + country: '阿富汗', + code: '+93', + }, + { + country: '阿尔巴尼亚', + code: '+355', + }, + { + country: '阿尔及利亚', + code: '+213', + }, + { + country: '安道尔', + code: '+376', + }, + { + country: '安哥拉', + code: '+244', + }, + { + country: '安圭拉', + code: '+1264', + }, + { + country: '安提瓜和巴布达', + code: '+1268', + }, + { + country: '阿根廷', + code: '+54', + }, + { + country: '阿鲁巴', + code: '+297', + }, + { + country: '澳大利亚', + code: '+61', + }, + { + country: '奥地利', + code: '+43', + }, + { + country: '阿塞拜疆', + code: '+994', + }, + { + country: '埃及', + code: '+20', + }, + { + country: '爱沙尼亚', + code: '+372', + }, + { + country: '埃塞俄比亚', + code: '+251', + }, + { + country: '爱尔兰', + code: '+353', + }, + { + country: '阿曼', + code: '+968', + }, + { + country: '阿拉伯联合酋长国', + code: '+971', + }, + { + letter: 'B', + }, + { + country: '巴哈马', + code: '+1242', + }, + { + country: '巴林', + code: '+973', + }, + { + country: '巴巴多斯', + code: '+1246', + }, + { + country: '白俄罗斯', + code: '+375', + }, + { + country: '比利时', + code: '+32', + }, + { + country: '伯利兹', + code: '+501', + }, + { + country: '贝宁', + code: '+229', + }, + { + country: '百慕大群岛', + code: '+1441', + }, + { + country: '不丹', + code: '+975', + }, + { + country: '玻利维亚', + code: '+591', + }, + { + country: '波斯尼亚和黑塞哥维那', + code: '+387', + }, + { + country: '博茨瓦纳', + code: '+267', + }, + { + country: '巴西', + code: '+55', + }, + { + country: '保加利亚', + code: '+359', + }, + { + country: '布基纳法索', + code: '+226', + }, + { + country: '布隆迪', + code: '+257', + }, + { + country: '冰岛', + code: '+354', + }, + { + country: '巴基斯坦', + code: '+92', + }, + { + country: '巴勒斯坦', + code: '+970', + }, + { + country: '巴拿马', + code: '+507', + }, + { + country: '巴布亚新几内亚', + code: '+675', + }, + { + country: '巴拉圭', + code: '+595', + }, + { + country: '波兰', + code: '+48', + }, + { + country: '波多黎各', + code: '+1787', + }, + { + letter: 'C', + }, + { + country: '赤道几内亚', + code: '+240', + }, + { + letter: 'D', + }, + { + country: '丹麦', + code: '+45', + }, + { + country: '多米尼加', + code: '+1767', + }, + { + country: '多米尼加共和国', + code: '+1809', + }, + { + country: '东帝汶', + code: '+670', + }, + { + country: '德国', + code: '+49', + }, + { + country: '多哥', + code: '+228', + }, + { + letter: 'E', + }, + { + country: '厄瓜多尔', + code: '+593', + }, + { + country: '厄立特里亚', + code: '+291', + }, + { + country: '俄罗斯', + code: '+7', + }, + { + letter: 'F', + }, + { + country: '法罗群岛', + code: '+298', + }, + { + country: '斐济', + code: '+679', + }, + { + country: '芬兰', + code: '+358', + }, + { + country: '法国', + code: '+33', + }, + { + country: '法属圭亚那', + code: '+594', + }, + { + country: '法属波利尼西亚', + code: '+689', + }, + { + country: '菲律宾', + code: '+63', + }, + { + letter: 'G', + }, + { + country: '哥伦比亚', + code: '+57', + }, + { + country: '哥斯达黎加', + code: '+506', + }, + { + country: '古巴', + code: '+53', + }, + { + country: '刚果民主共和国', + code: '+243', + }, + { + country: '冈比亚', + code: '+220', + }, + { + country: '格鲁吉亚', + code: '+995', + }, + { + country: '格陵兰岛', + code: '+299', + }, + { + country: '格林纳达', + code: '+1473', + }, + { + country: '瓜德罗普岛', + code: '+590', + }, + { + country: '关岛', + code: '+1671', + }, + { + country: '瓜地马拉', + code: '+502', + }, + { + country: '圭亚那', + code: '+592', + }, + { + country: '刚果共和国', + code: '+242', + }, + { + letter: 'H', + }, + { + country: '海地', + code: '+509', + }, + { + country: '洪都拉斯', + code: '+504', + }, + { + country: '哈萨克斯坦', + code: '+7', + }, + { + country: '黑山', + code: '+382', + }, + { + country: '荷兰', + code: '+31', + }, + { + country: '韩国', + code: '+82', + }, + { + letter: 'J', + }, + { + country: '柬埔寨', + code: '+855', + }, + { + country: '加拿大', + code: '+1', + }, + { + country: '捷克', + code: '+420', + }, + { + country: '吉布提', + code: '+253', + }, + { + country: '加蓬', + code: '+241', + }, + { + country: '加纳', + code: '+233', + }, + { + country: '几内亚', + code: '+224', + }, + { + country: '几内亚比绍共和国', + code: '+245', + }, + { + country: '基里巴斯', + code: '+686', + }, + { + country: '吉尔吉斯斯坦', + code: '+996', + }, + { + country: '津巴布韦', + code: '+263', + }, + { + letter: 'K', + }, + { + country: '喀麦隆', + code: '+237', + }, + { + country: '开普', + code: '+238', + }, + { + country: '开曼群岛', + code: '+1345', + }, + { + country: '科摩罗', + code: '+269', + }, + { + country: '库克群岛', + code: '+682', + }, + { + country: '克罗地亚', + code: '+385', + }, + { + country: '库拉索', + code: '+599', + }, + { + country: '肯尼亚', + code: '+254', + }, + { + country: '科威特', + code: '+965', + }, + { + country: '卡塔尔', + code: '+974', + }, + { + letter: 'L', + }, + { + country: '老挝', + code: '+856', + }, + { + country: '拉脱维亚', + code: '+371', + }, + { + country: '黎巴嫩', + code: '+961', + }, + { + country: '莱索托', + code: '+266', + }, + { + country: '利比里亚', + code: '+231', + }, + { + country: '利比亚', + code: '+218', + }, + { + country: '列支敦士登', + code: '+423', + }, + { + country: '立陶宛', + code: '+370', + }, + { + country: '卢森堡', + code: '+352', + }, + { + country: '留尼汪', + code: '+262', + }, + { + country: '罗马尼亚', + code: '+40', + }, + { + country: '卢旺达', + code: '+250', + }, + { + letter: 'M', + }, + { + country: '美属萨摩亚', + code: '+1684', + }, + { + country: '孟加拉国', + code: '+880', + }, + { + country: '马其顿', + code: '+389', + }, + { + country: '马达加斯加', + code: '+261', + }, + { + country: '马拉维', + code: '+265', + }, + { + country: '马来西亚', + code: '+60', + }, + { + country: '马尔代夫', + code: '+960', + }, + { + country: '马里', + code: '+223', + }, + { + country: '马耳他', + code: '+356', + }, + { + country: '马提尼克', + code: '+596', + }, + { + country: '毛里塔尼亚', + code: '+222', + }, + { + country: '毛里求斯', + code: '+230', + }, + { + country: '马约特', + code: '+262', + }, + { + country: '墨西哥', + code: '+52', + }, + { + country: '摩尔多瓦', + code: '+373', + }, + { + country: '摩纳哥', + code: '+377', + }, + { + country: '蒙古', + code: '+976', + }, + { + country: '蒙特塞拉特岛', + code: '+1664', + }, + { + country: '摩洛哥', + code: '+212', + }, + { + country: '莫桑比克', + code: '+258', + }, + { + country: '缅甸', + code: '+95', + }, + { + country: '秘鲁', + code: '+51', + }, + { + country: '美国', + code: '+1', + }, + { + country: '美属维尔京群岛', + code: '+1284', + }, + { + letter: 'N', + }, + { + country: '纳米比亚', + code: '+264', + }, + { + country: '尼泊尔', + code: '+977', + }, + { + country: '尼加拉瓜', + code: '+505', + }, + { + country: '尼日尔', + code: '+227', + }, + { + country: '尼日利亚', + code: '+234', + }, + { + country: '挪威', + code: '+47', + }, + { + country: '南非', + code: '+27', + }, + { + letter: 'P', + }, + { + country: '帕劳', + code: '+680', + }, + { + country: '葡萄牙', + code: '+351', + }, + { + letter: 'R', + }, + { + country: '日本', + code: '+81', + }, + { + country: '瑞典', + code: '+46', + }, + { + country: '瑞士', + code: '+41', + }, + { + letter: 'S', + }, + { + country: '塞浦路斯', + code: '+357', + }, + { + country: '萨尔瓦多', + code: '+503', + }, + { + country: '圣基茨和尼维斯', + code: '+1869', + }, + { + country: '圣露西亚', + code: '+1758', + }, + { + country: '圣彼埃尔和密克隆岛', + code: '+508', + }, + { + country: '圣文森特和格林纳丁斯', + code: '+1784', + }, + { + country: '萨摩亚', + code: '+685', + }, + { + country: '圣马力诺', + code: '+378', + }, + { + country: '圣多美和普林西比', + code: '+239', + }, + { + country: '沙特阿拉伯', + code: '+966', + }, + { + country: '塞内加尔', + code: '+221', + }, + { + country: '塞尔维亚', + code: '+381', + }, + { + country: '塞舌尔', + code: '+248', + }, + { + country: '塞拉利昂', + code: '+232', + }, + { + country: '圣马丁岛(荷兰部分)', + code: '+1721', + }, + { + country: '斯洛伐克', + code: '+421', + }, + { + country: '斯洛文尼亚', + code: '+386', + }, + { + country: '所罗门群岛', + code: '+677', + }, + { + country: '索马里', + code: '+252', + }, + { + country: '斯里兰卡', + code: '+94', + }, + { + country: '苏丹', + code: '+249', + }, + { + country: '苏里南', + code: '+597', + }, + { + country: '斯威士兰', + code: '+268', + }, + { + letter: 'T', + }, + { + country: '塔吉克斯坦', + code: '+992', + }, + { + country: '坦桑尼亚', + code: '+255', + }, + { + country: '泰国', + code: '+66', + }, + { + country: '汤加', + code: '+676', + }, + { + country: '特立尼达和多巴哥', + code: '+1868', + }, + { + country: '突尼斯', + code: '+216', + }, + { + country: '土耳其', + code: '+90', + }, + { + country: '土库曼斯坦', + code: '+993', + }, + { + country: '特克斯和凯科斯群岛', + code: '+1649', + }, + { + letter: 'W', + }, + { + country: '文莱', + code: '+673', + }, + { + country: '乌干达', + code: '+256', + }, + { + country: '乌克兰', + code: '+380', + }, + { + country: '乌拉圭', + code: '+598', + }, + { + country: '乌兹别克斯坦', + code: '+998', + }, + { + country: '瓦努阿图', + code: '+678', + }, + { + country: '委内瑞拉', + code: '+58', + }, + { + letter: 'X', + }, + { + country: '希腊', + code: '+30', + }, + { + country: '匈牙利', + code: '+36', + }, + { + country: '象牙海岸', + code: '+225', + }, + { + country: '新喀里多尼亚', + code: '+687', + }, + { + country: '新西兰', + code: '+64', + }, + { + country: '新加坡', + code: '+65', + }, + { + country: '西班牙', + code: '+34', + }, + { + country: '叙利亚', + code: '+963', + }, + { + letter: 'Y', + }, + { + country: '亚美尼亚', + code: '+374', + }, + { + country: '印度', + code: '+91', + }, + { + country: '印度尼西亚', + code: '+62', + }, + { + country: '伊朗', + code: '+98', + }, + { + country: '伊拉克', + code: '+964', + }, + { + country: '以色列', + code: '+972', + }, + { + country: '意大利', + code: '+39', + }, + { + country: '牙买加', + code: '+1876', + }, + { + country: '约旦', + code: '+962', + }, + { + country: '英国', + code: '+44', + }, + { + country: '越南', + code: '+84', + }, + { + country: '英属处女群岛', + code: '+1340', + }, + { + country: '也门', + code: '+967', + }, + { + letter: 'Z', + }, + { + country: '中非共和国', + code: '+236', + }, + { + country: '乍得', + code: '+235', + }, + { + country: '智利', + code: '+56', + }, + { + country: '直布罗陀', + code: '+350', + }, + { + country: '中国大陆', + code: '+86', + }, + { + country: '中国香港', + code: '+852', + }, + { + country: '中国澳门', + code: '+853', + }, + { + country: '中国台湾', + code: '+886', + }, + { + country: '赞比亚', + code: '+260', + }, +]; + +export default phoneCodeData; diff --git a/src/components/common-base/phone-code/use-phone-code.ts b/src/components/common-base/phone-code/use-phone-code.ts new file mode 100644 index 0000000000000000000000000000000000000000..205b37c679670fd78e76d9590e03f1e12b055644 --- /dev/null +++ b/src/components/common-base/phone-code/use-phone-code.ts @@ -0,0 +1,90 @@ +/** + * @file 手机区号选择 hook + */ + +import { isUndefined } from '@/assets/utils/types'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, ref, unref } from 'vue'; +import codeList from './phone-code-data'; + +export const PhoneCodeEmits = ['input']; + +export type PhoneCodeDataItem = typeof codeList[number]; + +export const phoneCodeProps = () => ({ + value: PropUtils.string.def(''), +}); + +export const phoneCodeEmits = () => ({ + input: emitFunc(), +}); + +export const usePhoneCode = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + /** 搜索的关键词 */ + const searchKey = ref(''); + + /** 所有首字母列表 */ + const letterList = computed(() => { + return codeList.filter(item => item.letter); + }); + + const phoneCodeList = computed(() => { + if (!unref(searchKey)) { + return codeList; + } + + return codeList.filter(item => { + if (item.letter) { + return false; + } + + if (item.code && item.code.indexOf(unref(searchKey)) !== -1) { + return true; + } + + if (item.country && item.country.indexOf(unref(searchKey)) !== -1) { + return true; + } + + return false; + }); + }); + + /** 区号选择列表 ref */ + const codeListRef = ref(); + + /** 滚动到某个字母中 */ + function scrollToLetter(letter: string | undefined) { + const codeListElem = unref(codeListRef); + if (!codeListElem || !letter) { + return; + } + + const targetLetterEl = codeListElem.querySelector(`[date-letter=${letter}]`); + + targetLetterEl && targetLetterEl.scrollIntoView(); + } + + /** 处理点击区号 */ + function onClickCountry(item: PhoneCodeDataItem) { + if (isUndefined(item.code)) return; + if (props.value === item.code) return; + + emit('input', item.code); + } + + return { + searchKey, + phoneCodeList, + letterList, + codeListRef, + scrollToLetter, + onClickCountry, + }; +}; diff --git a/src/components/common-base/popup/mobile-popup.vue b/src/components/common-base/popup/mobile-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..354e14bb7e6b072658b346dfb907ca740f5d0bcb --- /dev/null +++ b/src/components/common-base/popup/mobile-popup.vue @@ -0,0 +1,423 @@ + + + + + + diff --git a/src/components/common-base/popup/types.ts b/src/components/common-base/popup/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..4d6d1d9265a20b54c26c93807ea747c6afd19ec6 --- /dev/null +++ b/src/components/common-base/popup/types.ts @@ -0,0 +1,25 @@ +/** + * Popup 弹层主题 + */ +export enum PopupTheme { + /** 默认主题 */ + Default = 'default', + /** 竖屏主题-黑色主题 */ + Portrait = 'portrait', + /** 竖屏主题-白色主题 */ + PortraitLight = 'portrait-light', +} + +export interface PopupInstance { + /** 打开 */ + openPopup(): void; + /** 关闭 */ + closePopup(): void; +} + +/** 屏幕横屏下的弹窗位置 */ +export enum PopupHorizontalScreenPosition { + Left = 'left', + Center = 'center', + Right = 'right', +} diff --git a/src/components/common-base/popup/use-popup.ts b/src/components/common-base/popup/use-popup.ts new file mode 100644 index 0000000000000000000000000000000000000000..d795f8146b701f81d6b37e05f94bb8f610287971 --- /dev/null +++ b/src/components/common-base/popup/use-popup.ts @@ -0,0 +1,416 @@ +/* eslint-disable sonarjs/cognitive-complexity */ +import { computed, nextTick, onBeforeUnmount, onMounted, ref, unref, watch } from 'vue'; +import AlloyFinger from 'alloyfinger'; + +import { PlvPopperManager } from '@/plugins/polyv-ui/admin-import'; +import { Transform } from '@/plugins/alloy-finger/transform'; + +import { useAppendTo } from '@/hooks/behaviors/use-append-to'; + +import { emitFunc, updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { isUndefined } from '@/assets/utils/types'; + +import { CSSProperties } from 'vue/types/jsx'; +import { PopupInstance, PopupHorizontalScreenPosition, PopupTheme } from './types'; +import { useScreenOrientHook } from '@/hooks/core/use-screen-orient'; + +export const popupProps = () => ({ + /** 显示状态,默认:false,支持 .sync */ + visible: PropUtils.bool.def(false), + /** 弹层标题 */ + title: PropUtils.string.def(''), + /** 是否占满全屏,默认:false */ + fullScreen: PropUtils.bool.def(false), + /** 占满除播放器以外的主体区域,默认:false */ + fillBodySection: PropUtils.bool.def(false), + /** 是否显示头部标题区,默认:true */ + showHeader: PropUtils.bool.def(true), + /** 是否插入到 body 中,默认:true */ + appendToBody: PropUtils.bool.def(true), + /** 是否显示关闭,默认:true */ + closable: PropUtils.bool.def(true), + /** 是否显示返回,默认:false */ + backable: PropUtils.bool.def(false), + /** 在点击返回时自动关闭,默认:false */ + backAutoClose: PropUtils.bool.def(false), + /** 点击蒙层关闭弹窗,默认:true */ + closeOnClickMask: PropUtils.bool.def(true), + /** 蒙层透明度,默认:0.6 */ + maskOpacity: PropUtils.number.def(0.6), + /** 弹层主体样式 */ + contentStyle: PropUtils.objectType().def({}), + /** 是否使用弹出层管理器,集中控制弹窗层级,默认:true */ + usePopperManager: PropUtils.bool.def(true), + /** 窗口尺寸变化时是否重设样式,默认:false */ + updateOnResize: PropUtils.bool.def(false), + /** 弹层主题,默认:default */ + theme: PropUtils.enum().def(PopupTheme.Default), + /** 弹层主体高度 */ + contentHeight: PropUtils.number, + /** 弹层主体使用页面高度百分比,0~1,默认:false */ + contentPercentHeight: PropUtils.oneOfType([Boolean, Number]).def(false), + /** 隐藏 header 的底部边框,默认:false */ + hideHeaderBorder: PropUtils.bool.def(false), + /** 弹层主体背景色 */ + contentBackground: PropUtils.string, + /** 是否使用动画显隐,默认:true */ + useTransition: PropUtils.bool.def(true), + /** + * 是否使用纵向拖拽,默认:false + * 注意,使用拖拽功能,弹窗主体高度为 100%,具体内容高度需要外部组件搭配 drag-vertical 钩子自行计算 + * */ + draggable: PropUtils.bool.def(false), + /** 弹窗主体距离顶部的百分比距离,0~1,默认 0 */ + contentPercentTop: PropUtils.number.def(0), + /** 强制横屏展示 */ + forceHorizontalScreen: PropUtils.bool.def(false), + /** 屏幕横屏下的弹窗位置,默认为 right */ + horizontalScreenPosition: PropUtils.enum().def( + PopupHorizontalScreenPosition.Right, + ), +}); + +export const popupEmits = () => ({ + ...updateModelEmit<'visible', boolean>('visible'), + ...updateModelEmit<'useTransition', boolean>('useTransition'), + /** 进入之后 */ + 'after-enter': emitFunc(), + /** 离开之后 */ + 'after-leave': emitFunc(), + /** 点击返回 */ + 'click-back': emitFunc(), + /** 上下推拽 */ + 'drag-vertical': emitFunc<{ translateY: number }>(), +}); + +export const usePopup = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const { + isVerticalScreenOrientation, + isHorizontalScreenOrientation, + updateScreenOrientationModeManually, + } = useScreenOrientHook({ + autoListen: props.updateOnResize, + }); + + /** 弹层 ref */ + const popupRef = ref(); + /** 弹层主体 ref */ + const popupContentRef = ref(); + /** 弹层主体头部 Ref */ + const popupContentHeaderRef = ref(); + + /** 弹层外层显示状态 */ + const wrapVisible = ref(false); + + /** 主体显示状态 */ + const contentVisible = ref(false); + + /** 主体计算的高度 */ + const contentComputedHeight = ref(); + + /** 主体最大高度 */ + const contentMaxHeight = ref(); + + /** 窗口原高度 */ + const pageOriginHeight = ref(document.documentElement.clientHeight || document.body.clientHeight); + + /** 标识位-初次渲染 */ + const firstPaint = ref(true); + + /** 是否需要"可拖拽"提示框 */ + const needDraggableTip = ref(true); + + /** + * 是否展示"可拖拽"提示框 + * @desc 业务逻辑,配置可拖拽的情况下,第一次打开弹窗时必定展示 + * */ + const draggableTipVisible = computed(() => { + return props.draggable && firstPaint.value && needDraggableTip.value; + }); + + /** 蒙层样式 */ + const popupMaskStyle = computed(() => { + return { + background: `rgba(0, 0, 0, ${props.maskOpacity})`, + }; + }); + + /** 弹层主体渲染高度 */ + const contentRenderHeight = computed(() => { + if ( + props.draggable || + props.fullScreen || + props.forceHorizontalScreen || + isHorizontalScreenOrientation.value + ) { + return '100%'; + } + + if (props.contentHeight) { + return props.contentHeight; + } + + let contentPercentHeight = 0; + if (props.contentPercentHeight === true) { + contentPercentHeight = 0.6; + } else if (typeof props.contentPercentHeight === 'number') { + contentPercentHeight = props.contentPercentHeight; + } + + if (contentPercentHeight) { + return document.documentElement.clientHeight * contentPercentHeight; + } + + return contentComputedHeight.value; + }); + + /** 弹层主体样式 */ + const popupContentStyle = computed(() => { + let style: CSSProperties = {}; + + if (props.contentPercentTop) { + style.top = `${props.contentPercentTop * 100}%`; + } + + if (!isUndefined(contentRenderHeight.value)) { + style.height = + typeof contentRenderHeight.value === 'string' + ? contentRenderHeight.value + : `${contentRenderHeight.value}px`; + } + + if (contentMaxHeight.value) { + style.maxHeight = `${contentMaxHeight.value}px`; + } + + // 如果存在自定义header,则不设置圆角 + if (!props.showHeader) { + style.borderRadius = 'unset'; + } + + if (props.contentBackground) { + style.background = props.contentBackground; + } + + style = Object.assign({}, style, props.contentStyle); + + return style; + }); + + /** 关闭"可拖拽"提示框 */ + function closeDraggableTip() { + needDraggableTip.value = false; + } + + /** 点击蒙层 */ + function onClickMask() { + if (props.closeOnClickMask) { + closePopup(); + } + } + + /** 打开弹层 */ + function openPopup() { + wrapVisible.value = true; + contentVisible.value = true; + updateHeight(); + nextTick(() => { + if (props.usePopperManager && popupRef.value) { + PlvPopperManager.openPopper(popupRef.value); + } + }); + } + + /** 关闭弹层 */ + function closePopup() { + contentVisible.value = false; + emit('update:visible', false); + } + + /** 处理进入动画结束之后 */ + function onAfterEnter() { + emit('update:useTransition', true); + emit('after-enter'); + } + + /** 处理离开动画结束之后 */ + function onAfterLeave() { + wrapVisible.value = false; + emit('update:useTransition', true); + emit('after-leave'); + if (props.draggable) { + const $popupContent = popupRef.value as AlloyFingerTransformHTMLElement; + $popupContent.translateY = 0; + } + } + + /** 处理点击返回 */ + function onClickBack() { + if (props.backAutoClose) { + closePopup(); + } + emit('click-back'); + } + + /** 获取弹窗主体头部的高度 */ + function getPopupContentHeaderHeight() { + return (popupContentHeaderRef.value && popupContentHeaderRef.value.clientHeight) || 50; + } + + /** 绑定 touch 行为 */ + function bindTouchBehavior() { + const $popup = popupRef.value; + const $popupContentHeader = popupContentHeaderRef.value; + + if (!$popup || !$popupContentHeader) return; + + const contentPercentTop = props.contentPercentTop; + const documentHeight = document.documentElement.clientHeight; + const minY = documentHeight * contentPercentTop; + const maxY = documentHeight - minY - getPopupContentHeaderHeight(); + + // 在 $popup 上挂载 css-transform 钩子 + const $transformPanel = Transform($popup); + + new AlloyFinger($popupContentHeader, { + pressMove: evt => { + const translateY = $transformPanel.translateY + evt.deltaY; + if (translateY <= -minY || $transformPanel.translateY > maxY) { + evt.preventDefault(); + return; + } + $transformPanel.translateY = translateY > maxY ? maxY : translateY; + emit('drag-vertical', { translateY }); + evt.preventDefault(); + }, + }); + } + + watch( + () => props.visible, + () => { + if (props.visible) { + openPopup(); + if (props.draggable && firstPaint.value) { + nextTick(() => { + bindTouchBehavior(); + }); + } + } else { + closePopup(); + closeDraggableTip(); + firstPaint.value = false; + } + }, + ); + + // 更新高度信息 + async function updateHeight() { + contentComputedHeight.value = undefined; + contentMaxHeight.value = undefined; + // 手动更新屏幕旋转模式,减少性能开销 + updateScreenOrientationModeManually(); + await nextTick(); + setContentHeight(); + } + + /** + * 设置主题高度 + * 限高,最高不遮盖播放器区域 + * 在安卓手机上,软键盘弹起会导致页面高度变化,如果当前页面高度小于初始高度,popper高度按初始高度计算 + */ + function setContentHeight() { + const popupEl = unref(popupRef); + if (!popupEl) return; + + if ( + props.draggable || + props.fullScreen || + props.forceHorizontalScreen || + isHorizontalScreenOrientation.value + ) { + return; + } + + const ratio = 16 / 9; + const playerHeight = window.innerWidth / ratio; + const currentPageHeight = document.documentElement.clientHeight || document.body.clientHeight; + const isCompressed = currentPageHeight < pageOriginHeight.value; + const bodyClientHeight = isCompressed ? pageOriginHeight.value : popupEl.offsetHeight; + // 高度遮盖播放器,或始终需要占满除播放器以外高度(横屏状态除外,横屏状态没有最大高度,可以遮挡播放器) + const maxHeight = bodyClientHeight - playerHeight; + + contentMaxHeight.value = maxHeight; + if (isVerticalScreenOrientation.value && props.fillBodySection) { + contentComputedHeight.value = maxHeight; + } + } + + /** 若浏览器窗口尺寸变化(多窗口、webview 小窗恢复、软键盘弹出),在弹窗可见情况下,重新调整窗口尺寸 */ + function onPageResize() { + if (!props.visible) { + return; + } + const isFocusInput = + document.activeElement instanceof HTMLTextAreaElement || + document.activeElement instanceof HTMLInputElement; + // 软键盘弹出导致窗口变化 + if (isFocusInput && window.innerHeight < screen.availHeight) { + return; + } + setTimeout(() => { + updateHeight(); + }, 200); + } + + if (props.appendToBody) { + useAppendTo(popupRef); + } + + onMounted(() => { + if (props.updateOnResize) { + window.addEventListener('resize', onPageResize); + } + }); + + onBeforeUnmount(() => { + if (props.updateOnResize) { + window.removeEventListener('resize', onPageResize); + } + }); + + const popupInstance: PopupInstance = { + openPopup, + closePopup, + }; + + return { + wrapVisible, + contentVisible, + popupRef, + popupContentHeaderRef, + popupContentRef, + popupMaskStyle, + popupContentStyle, + popupInstance, + onAfterEnter, + onAfterLeave, + onClickMask, + openPopup, + closePopup, + onClickBack, + + firstPaint, + draggableTipVisible, + closeDraggableTip, + + isVerticalScreen: isVerticalScreenOrientation, + isHorizontalScreen: isHorizontalScreenOrientation, + }; +}; diff --git a/src/components/common-base/rich-text-render/mobile-rich-text-render.vue b/src/components/common-base/rich-text-render/mobile-rich-text-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..1ee6e238a16211dcf98c528c8afed7eee8920a04 --- /dev/null +++ b/src/components/common-base/rich-text-render/mobile-rich-text-render.vue @@ -0,0 +1,105 @@ + + + + + + diff --git a/src/components/common-base/rich-text-render/pc-rich-text-render.vue b/src/components/common-base/rich-text-render/pc-rich-text-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..3c6a13c05ccf2c090af158a96b486c83df24ef67 --- /dev/null +++ b/src/components/common-base/rich-text-render/pc-rich-text-render.vue @@ -0,0 +1,105 @@ + + + + + + diff --git a/src/components/common-base/rich-text-render/use-rich-text-render.ts b/src/components/common-base/rich-text-render/use-rich-text-render.ts new file mode 100644 index 0000000000000000000000000000000000000000..3dd0679198af0a4813479d7231889cd66b994c47 --- /dev/null +++ b/src/components/common-base/rich-text-render/use-rich-text-render.ts @@ -0,0 +1,24 @@ +import { resizeHtmlContentImg } from '@/assets/utils/string'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed } from 'vue'; + +/** + * props 配置 + */ +export const richTextRenderProps = () => ({ + /** html 富文本内容 */ + htmlContent: PropUtils.string.def(''), +}); + +export const useRichTextRender = (options: { props: VueProps }) => { + const { props } = options; + + /** 用于渲染的富文本内容 */ + const renderHtmlContent = computed(() => { + return resizeHtmlContentImg(props.htmlContent); + }); + + return { + renderHtmlContent, + }; +}; diff --git a/src/components/common-base/slider-bar/slider-bar.vue b/src/components/common-base/slider-bar/slider-bar.vue new file mode 100644 index 0000000000000000000000000000000000000000..5f3d0dee12ac1f8a8fef99e18ed6886517a7795f --- /dev/null +++ b/src/components/common-base/slider-bar/slider-bar.vue @@ -0,0 +1,185 @@ + + + + + + diff --git a/src/components/common-base/slider-bar/use-slider-bar.ts b/src/components/common-base/slider-bar/use-slider-bar.ts new file mode 100644 index 0000000000000000000000000000000000000000..5f82aa4ec7ff73456a13cb29b2309a50c033227f --- /dev/null +++ b/src/components/common-base/slider-bar/use-slider-bar.ts @@ -0,0 +1,280 @@ +import { tupleString } from '@/assets/utils/array'; +import { isMobile } from '@/assets/utils/browser'; +import { numberToFixed } from '@/assets/utils/number'; +import { getEventPosition } from '@/assets/utils/utils'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, onBeforeUnmount, ref, unref, watch } from 'vue'; +import { CSSProperties } from 'vue/types/jsx'; + +export const sliderBarDirections = tupleString('horizontal', 'vertical'); +export type SliderBarDirection = typeof sliderBarDirections[number]; + +export const sliderBarProps = () => ({ + /** 绑定值 */ + value: PropUtils.number.def(0), + /** hover 时放大滑块 */ + hoverToZoom: PropUtils.bool.def(false), + /** 热区范围 */ + hotAreaSize: PropUtils.number.def(28), + /** 滑块轨道粗细 */ + sliderTrackSize: PropUtils.number.def(6), + /** 隐藏轨道圆角 */ + sliderTrackRadiusHide: PropUtils.bool.def(false), + /** 外层轨道颜色 */ + wrapSliderTrackColor: PropUtils.string.def('rgba(255, 255, 255, .8)'), + /** 内层轨道颜色 */ + innerSliderTrackColor: PropUtils.string.def('#3082FE'), + /** 滑块原点大小 */ + sliderDotSize: PropUtils.number.def(14), + /** 滑块方向 */ + direction: PropUtils.oneOf(sliderBarDirections).def('horizontal'), + /** 最小值 */ + min: PropUtils.number.def(0), + /** 最大值 */ + max: PropUtils.number.def(100), + /** 显示提示 */ + tooltips: PropUtils.bool.def(false), +}); + +export const sliderBarEmits = () => ({ + input: emitFunc(), + change: emitFunc(), + 'drag-change': emitFunc(), +}); + +export const useSliderBar = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + /** 当前的进度值 */ + const currentValue = ref(0); + /** 当前是否正在拖拽中 */ + const isDragging = ref(false); + const { hoverToZoom, tooltips } = useProps(props); + + watch( + () => props.value, + () => (currentValue.value = props.value), + { + immediate: true, + }, + ); + watch( + () => unref(currentValue), + () => { + if (unref(currentValue) !== props.value) { + emit('input', unref(currentValue)); + emit('change', unref(currentValue)); + } + }, + ); + + /** 热区样式 */ + const containerStyle = computed(() => { + const styles: CSSProperties = {}; + + switch (props.direction) { + case 'horizontal': + styles.height = `${props.hotAreaSize}px`; + break; + case 'vertical': + styles.width = `${props.hotAreaSize}px`; + break; + } + + return styles; + }); + + /** 外层轨道样式 */ + const wrapTrackStyle = computed(() => { + const styles: CSSProperties = { + background: props.wrapSliderTrackColor, + borderRadius: `${props.sliderTrackSize}px`, + }; + + if (props.sliderTrackRadiusHide) { + styles.borderRadius = 0; + } + + switch (props.direction) { + case 'horizontal': + styles.height = `${props.sliderTrackSize}px`; + break; + case 'vertical': + styles.width = `${props.sliderTrackSize}px`; + break; + } + + return styles; + }); + + /** 内部轨道的长度百分比 */ + const innerTrackLengthPercent = computed(() => { + const percentNum = (100 * (unref(currentValue) - props.min)) / (props.max - props.min); + return `${percentNum > 100 ? 100 : percentNum}%`; + }); + + /** 内部轨道样式 */ + const innerTrackStyle = computed(() => { + const styles: CSSProperties = { + background: props.innerSliderTrackColor, + }; + + switch (props.direction) { + case 'horizontal': + styles.width = unref(innerTrackLengthPercent); + break; + case 'vertical': + styles.height = unref(innerTrackLengthPercent); + break; + } + + return styles; + }); + + /** 点样式 */ + const sliderDotStyle = computed(() => { + const styles: CSSProperties = { + width: `${props.sliderDotSize}px`, + height: `${props.sliderDotSize}px`, + }; + + switch (props.direction) { + case 'horizontal': + styles.left = unref(innerTrackLengthPercent); + break; + case 'vertical': + styles.bottom = unref(innerTrackLengthPercent); + break; + } + + return styles; + }); + + const containerRef = ref(); + + function listenWindowEvent(): void { + removeListenWindowEvent(); + if (isMobile) { + window.addEventListener('touchmove', onMouseMove); + window.addEventListener('touchend', onMouseUp); + window.addEventListener('touchcancel', onMouseUp); + } else { + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('mouseup', onMouseUp); + } + } + + function removeListenWindowEvent(): void { + if (isMobile) { + window.removeEventListener('touchmove', onMouseMove); + window.removeEventListener('touchend', onMouseUp); + window.removeEventListener('touchcancel', onMouseUp); + } else { + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + } + } + + function onMouseDown(event: MouseEvent | TouchEvent): void { + const eventPosition = getEventPosition(event); + let clientNumber = eventPosition.clientX; + if (props.direction === 'vertical') { + clientNumber = eventPosition.clientY; + } + computedValue(clientNumber); + isDragging.value = true; + listenWindowEvent(); + } + + function onMouseMove(event: MouseEvent | TouchEvent): void { + const eventPosition = getEventPosition(event); + let clientNumber = eventPosition.clientX; + if (props.direction === 'vertical') { + clientNumber = eventPosition.clientY; + } + computedValue(clientNumber); + + onTrackMouseMove(event); + } + + function onMouseUp(): void { + isDragging.value = false; + removeListenWindowEvent(); + } + + const trackHoverX = ref(0); + const trackHoverValue = ref(0); + + function onTrackMouseMove(event: MouseEvent | TouchEvent): void { + const containerElem = unref(containerRef); + if (!containerElem) { + return; + } + const bound = containerElem.getBoundingClientRect(); + const eventPosition = getEventPosition(event); + const hoverX = eventPosition.clientX - bound.left; + trackHoverX.value = hoverX >= 0 ? hoverX : 0; + const hoverValue = computedValue(eventPosition.clientX, false); + if (typeof hoverValue !== 'undefined' && hoverValue >= 0) { + trackHoverValue.value = hoverValue; + } + } + + function computedValue(clientNumber: number, setValue = true): number | undefined { + const containerElem = unref(containerRef); + if (!containerElem) { + return; + } + const bound = containerElem.getBoundingClientRect(); + const totalLength = props.direction === 'vertical' ? bound.height : bound.width; + const startPoint = props.direction === 'vertical' ? bound.bottom : bound.left; + let pointLength = clientNumber - startPoint; + if (props.direction === 'vertical' && pointLength > 0) { + return; + } + if (props.direction === 'horizontal' && pointLength < 0) { + return; + } + + pointLength = Math.abs(pointLength); + + let percent = pointLength / totalLength; + if (percent > 1) { + percent = 1; + } else if (percent < 0) { + percent = 0; + } + const newValue = numberToFixed(props.min + (props.max - props.min) * percent); + if (setValue && newValue !== unref(currentValue)) { + currentValue.value = newValue; + emit('drag-change', newValue); + } + + return newValue; + } + + onBeforeUnmount(() => { + removeListenWindowEvent(); + }); + + return { + currentValue, + hoverToZoom, + tooltips, + isDragging, + containerStyle, + wrapTrackStyle, + innerTrackStyle, + sliderDotStyle, + containerRef, + onMouseDown, + onMouseUp, + trackHoverX, + trackHoverValue, + onTrackMouseMove, + }; +}; diff --git a/src/components/common-base/tabs/hooks/types.ts b/src/components/common-base/tabs/hooks/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4c0523dcc3adb1ba37efd339f409e5e77f8c562 --- /dev/null +++ b/src/components/common-base/tabs/hooks/types.ts @@ -0,0 +1,50 @@ +import { Ref } from 'vue'; + +export type TabNameType = string | number; + +export interface TabPaneDataItem { + /** tab 的标签 */ + label: string; + /** tab 的名称 */ + name: TabNameType; + /** tab 的附加标签 */ + subLabel: string; + /** 是否显示红点 */ + showReminder: boolean; +} + +/** tab 附加数据 */ +export type TabPaneAdditionalData = Pick; + +export interface TabsInstance { + currentTabName: Ref; + /** 设置当前激活的面板 */ + setCurrentTab(name: TabNameType): void; +} + +export interface TabsInjectContext { + /** 上一次的 tab 激活 name */ + prevTabName: Ref; + /** 当前激活的 tab name */ + currentTabName: Ref; + /** 上一次的 tab 激活 index */ + prevTabIndex: Ref; + /** 当前激活的 tab index */ + currentTabIndex: Ref; + /** tab 面板数据列表 */ + tabPaneData: Ref; + /** 设置当前激活面板 */ + setCurrentTab: (name: TabNameType) => void; + /** 切到下一个 tab */ + toNextTab: () => void; + /** 切到上一个 tab */ + toPrevTab: () => void; +} + +/** + * tab 面板对象 + */ +export interface TabPaneInstance { + /** 获取面板数据 */ + getTabPaneData: () => TabPaneDataItem; +} diff --git a/src/components/common-base/tabs/hooks/use-simple-tabs.ts b/src/components/common-base/tabs/hooks/use-simple-tabs.ts new file mode 100644 index 0000000000000000000000000000000000000000..0640222ce964a4dfb842851bbac7eda9828ca20f --- /dev/null +++ b/src/components/common-base/tabs/hooks/use-simple-tabs.ts @@ -0,0 +1,41 @@ +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; + +export interface SimpleTabItem { + /** 文本 */ + label: string; + /** 绑定值 */ + value: string; +} + +export const simpleTabsProps = () => ({ + /** 是否显示边框 */ + border: PropUtils.bool.def(true), + /** 绑定值,支持 v-model */ + value: PropUtils.string.def(''), + /** tab 列表 */ + tabs: PropUtils.array().def([]), +}); + +export const simpleTabsEmits = () => ({ + input: emitFunc(), + 'tab-click': emitFunc(), +}); + +export const useSimpleTabs = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + function onClickTab(value: string) { + if (value !== props.value) { + emit('input', value); + } + emit('tab-click', value); + } + + return { + onClickTab, + }; +}; diff --git a/src/components/common-base/tabs/hooks/use-tab-header.ts b/src/components/common-base/tabs/hooks/use-tab-header.ts new file mode 100644 index 0000000000000000000000000000000000000000..3ba3d53f1b9eadde56a851808b40fed16c119a6f --- /dev/null +++ b/src/components/common-base/tabs/hooks/use-tab-header.ts @@ -0,0 +1,183 @@ +import { nextTick, onBeforeUnmount, onMounted, Ref, ref, unref, watch } from 'vue'; +import { useTabsInject } from './use-tabs'; +import { useLangStore } from '@/store/use-lang-store'; +import { TabPaneDataItem } from './types'; +import { useWindowResizeListener } from '@/hooks/core/use-window-resize-listener'; + +export const useTabHeader = ( + options: { + /** 点击后滚动到当前 tab,默认:false */ + scrollCurrent?: boolean; + } = {}, +) => { + const { scrollCurrent = false } = options; + const langStore = useLangStore(); + + const { tabPaneData, currentTabName, currentTabIndex, setCurrentTab, toNextTab, toPrevTab } = + useTabsInject(); + + const scrollRef = ref(); + const itemRef = ref([]); + + /** 处理点击节点 */ + function onClickHeaderItem(paneData: TabPaneDataItem) { + if (paneData.name === unref(currentTabName)) { + return; + } + setCurrentTab(paneData.name); + if (scrollCurrent) { + scrollToCurrent(); + } + } + + /** 处理点击 next 箭头 */ + async function onClickNext() { + toNextTab(); + await nextTick(); + if (scrollCurrent) { + scrollToCurrent(); + } + } + + /** 处理点击 prev 箭头 */ + async function onClickPrev() { + toPrevTab(); + await nextTick(); + if (scrollCurrent) { + scrollToCurrent(); + } + } + + /** 当前节点的下横线 left 定位 */ + const currentLineLeft = ref(); + const currentLineRef = ref(); + + /** 滚动到当前 tab */ + function scrollToCurrent() { + const currentItemElem = itemRef.value[currentTabIndex.value]; + const scrollElem = scrollRef.value; + if (currentItemElem && scrollElem) { + const scrollLeft = Math.max( + 0, + currentItemElem.offsetLeft - (scrollElem.offsetWidth - currentItemElem.offsetWidth) / 2, + ); + scrollElem.scrollTo({ + top: 0, + left: scrollLeft, + behavior: 'smooth', + }); + } + } + + /** 计算下横线 left 定位 */ + async function computeCurrentLineLeft() { + await nextTick(); + const currentLineElem = unref(currentLineRef); + const itemElements = unref(itemRef); + const currentIndex = unref(currentTabIndex); + + if (!Array.isArray(itemElements) || !currentLineElem || currentIndex === -1) { + return; + } + + const currentItemElem = itemElements[currentIndex]; + if (!currentItemElem) { + return; + } + + const left = + currentItemElem.offsetLeft + (currentItemElem.offsetWidth - currentLineElem.offsetWidth) / 2; + + if (left !== unref(currentLineLeft)) { + currentLineLeft.value = left; + } + } + + useWindowResizeListener(() => { + setTimeout(() => { + computeCurrentLineLeft(); + }, 5); + }, true); + + watch( + () => [unref(currentTabIndex), unref(tabPaneData), langStore.currentLang], + () => computeCurrentLineLeft(), + { + immediate: true, + }, + ); + + return { + scrollRef, + itemRef, + tabPaneData, + currentTabName, + scrollToCurrent, + onClickHeaderItem, + onClickNext, + onClickPrev, + currentLineLeft, + currentLineRef, + }; +}; + +/** + * Tab 左右箭头 hook + */ +export const useTabHeaderArrow = (options: { + scrollRef: Ref; + tabPaneData: Ref; +}) => { + const { scrollRef, tabPaneData } = options; + + const leftArrowVisible = ref(false); + const rightArrowVisible = ref(false); + + /** 计算箭头显示 */ + function computedArrowVisible() { + const scrollElem = unref(scrollRef); + + if (!scrollElem) return; + + const scrollLeft = Math.ceil(scrollElem.scrollLeft); + if (scrollLeft > 0) { + leftArrowVisible.value = true; + } else { + leftArrowVisible.value = false; + } + + if (scrollLeft < Math.floor(scrollElem.scrollWidth - scrollElem.clientWidth)) { + rightArrowVisible.value = true; + } else { + rightArrowVisible.value = false; + } + } + + watch( + () => unref(tabPaneData), + async () => { + await nextTick(); + computedArrowVisible(); + }, + ); + + onMounted(() => { + computedArrowVisible(); + if (scrollRef.value) { + scrollRef.value.addEventListener('scroll', computedArrowVisible); + } + }); + + onBeforeUnmount(() => { + if (scrollRef.value) { + scrollRef.value.removeEventListener('scroll', computedArrowVisible); + } + }); + + return { + scrollRef, + leftArrowVisible, + rightArrowVisible, + computedArrowVisible, + }; +}; diff --git a/src/components/common-base/tabs/hooks/use-tab-pane.ts b/src/components/common-base/tabs/hooks/use-tab-pane.ts new file mode 100644 index 0000000000000000000000000000000000000000..6cde49c21d6383f4eb2bedfb6ec7f03eca06f72e --- /dev/null +++ b/src/components/common-base/tabs/hooks/use-tab-pane.ts @@ -0,0 +1,102 @@ +/** + * @file 标签页 TabPane hook + */ + +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, onBeforeMount, ref, unref, watch } from 'vue'; +import { TabNameType, TabPaneDataItem, TabPaneInstance } from './types'; +import { useTabsInject } from './use-tabs'; + +export const tabPaneProps = () => ({ + /** 面板标签名 */ + label: PropUtils.string.def(''), + /** 面板附加标签名 */ + subLabel: PropUtils.string.def(''), + /** 面板名称 */ + name: PropUtils.oneOfType([String, Number]).isRequired, + /** 是否懒加载,默认:false */ + lazyLoad: PropUtils.bool.def(false), + /** 切换到当前面板时重新渲染,优先级高于 lazyLoad,默认:false */ + switchToRefresh: PropUtils.bool.def(false), + /** 显示红点 */ + showReminder: PropUtils.bool.def(false), +}); + +/** + * 判断 vue 实例是否为 + * @param instance 实例 + */ +export const isTabPaneInstance = (instance: unknown): instance is TabPaneInstance => { + const _instance = instance as Partial | undefined; + return typeof _instance?.getTabPaneData === 'function'; +}; + +export const useTabPane = (options: { props: VueProps }) => { + const { props } = options; + const { label, subLabel, name, lazyLoad, switchToRefresh, showReminder } = useProps(props); + const { currentTabName, currentTabIndex, prevTabIndex } = useTabsInject(); + + /** 当前面板是否显示 */ + const paneVisible = computed(() => unref(currentTabName) === unref(name)); + + /** 面板进出动画 */ + const transitionName = computed(() => { + const prevIndexVal = prevTabIndex.value; + const currentIndexVal = currentTabIndex.value; + let name = ''; + + if (prevIndexVal !== -1 && currentIndexVal !== -1) { + name = currentIndexVal > prevIndexVal ? 'g-transition-menu-right' : 'g-transition-menu-left'; + } + + return name; + }); + + /** 获取当前标签页面板的数据 */ + function getTabPaneData(): TabPaneDataItem { + return { + label: unref(label), + subLabel: unref(subLabel), + name: unref(name), + showReminder: unref(showReminder), + }; + } + + /** 是否渲染 slot */ + const renderSlot = ref(false); + + /** 检查渲染状态 */ + function checkRenderStatus() { + if (unref(switchToRefresh)) { + renderSlot.value = unref(currentTabName) === unref(name); + return; + } + + if (!unref(lazyLoad) || unref(currentTabName) === unref(name)) { + renderSlot.value = true; + } + } + + onBeforeMount(() => { + checkRenderStatus(); + }); + + watch( + () => unref(currentTabName), + () => checkRenderStatus(), + ); + + const tabPaneInstance: TabPaneInstance = { + getTabPaneData, + }; + + return { + label, + name, + paneVisible, + transitionName, + getTabPaneData, + renderSlot, + tabPaneInstance, + }; +}; diff --git a/src/components/common-base/tabs/hooks/use-tabs.ts b/src/components/common-base/tabs/hooks/use-tabs.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce15a913bc7fa9a69868f9dc03fccfec0a678abc --- /dev/null +++ b/src/components/common-base/tabs/hooks/use-tabs.ts @@ -0,0 +1,227 @@ +/** + * @file 标签页 Tabs hook + */ + +import { eventBus } from '@/app/app-events'; +import { useVue } from '@/hooks/core/use-vue'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { + inject, + InjectionKey, + nextTick, + onMounted, + onUpdated, + provide, + ref, + unref, + watch, +} from 'vue'; +import { + TabNameType, + TabPaneDataItem, + TabPaneInstance, + TabsInjectContext, + TabsInstance, +} from './types'; +import { isTabPaneInstance } from './use-tab-pane'; + +export const tabsProps = () => ({ + /** 激活的面板 name */ + value: PropUtils.oneOfType([String, Number]).def(''), +}); + +export const tabsEmits = () => ({ + input: emitFunc(), +}); + +export const TABS_PROVIDE_KEY: InjectionKey = Symbol('tabsProvideKey'); + +export const useTabsProvide = (context: TabsInjectContext) => { + provide(TABS_PROVIDE_KEY, context); +}; + +export const useTabsInject = (): TabsInjectContext => { + return inject(TABS_PROVIDE_KEY) as TabsInjectContext; +}; + +/** 标签页 tabs hook */ +export const useTabs = (options: { + props: VueProps; + emit: VueEmit; + switchEvent?: string; +}) => { + const { props, emit } = options; + const { value } = useProps(props); + const { getSlot } = useVue(); + + /** 上一次激活的面板 name */ + const prevTabName = ref(''); + /** 当前激活的面板 name */ + const currentTabName = ref(''); + /** 上一刻激活的面板索引 */ + const prevTabIndex = ref(-1); + /** 当前激活的面板索引 */ + const currentTabIndex = ref(-1); + /** 面板数据列表 */ + const tabPaneData = ref([]); + + /** 设置当前激活的面板 */ + function setCurrentTab(name: TabNameType) { + if (name === unref(currentTabName)) { + return; + } + const newIndex = unref(tabPaneData).findIndex(paneData => paneData.name === name); + + if (newIndex === -1) { + return; + } + + prevTabName.value = unref(currentTabName); + prevTabIndex.value = unref(currentTabIndex); + + currentTabName.value = name; + currentTabIndex.value = newIndex; + + emit('input', name); + + if (options.switchEvent) { + eventBus.$emit(options.switchEvent, name); + } + } + + /** 切到下一个 tab */ + function toNextTab() { + // 已是最后一个 + if (currentTabIndex.value >= tabPaneData.value.length - 1) { + return; + } + const nextTabData = tabPaneData.value[currentTabIndex.value + 1]; + nextTabData && setCurrentTab(nextTabData.name); + } + + /** 切到上一个 tab */ + function toPrevTab() { + // 已是最后一个 + if (currentTabIndex.value <= 0) { + return; + } + const prevTabData = tabPaneData.value[currentTabIndex.value - 1]; + prevTabData && setCurrentTab(prevTabData.name); + } + + watch( + () => unref(value), + () => setCurrentTab(unref(value)), + ); + + /** 获取面板实例对象 */ + function getTabPaneInstances() { + const defaultSlot = getSlot(); + + const instances: TabPaneInstance[] = []; + + defaultSlot.forEach(vNode => { + const instance = vNode.componentInstance; + if (isTabPaneInstance(instance)) { + instances.push(instance); + } + }); + + return instances; + } + + /** 获取标签面板数据 */ + function getTabPaneData() { + const panes: TabPaneDataItem[] = []; + const instances = getTabPaneInstances(); + + instances.forEach(instance => { + const panelData = instance.getTabPaneData(); + panes.push(panelData); + }); + + return panes; + } + + /** 更新标签面板数据 */ + async function updateTabPaneData() { + await nextTick(); + const panes = getTabPaneData(); + const paneDataVal = unref(tabPaneData); + + // 是否需要更新 tab 面板数据 + let shouldUpdateTabPaneData = false; + // 是否需要重置到第一个 tab + let shouldSetFirstTab = false; + + if (panes.length !== paneDataVal.length) { + shouldUpdateTabPaneData = true; + } else { + panes.forEach((paneData, index) => { + const oldItem = paneDataVal[index]; + for (const key in paneData) { + const field = key as keyof TabPaneDataItem; + if (paneData[field] !== oldItem[field]) { + shouldUpdateTabPaneData = true; + } + } + }); + } + + if (shouldUpdateTabPaneData) { + if (panes.length !== tabPaneData.value.length) { + shouldSetFirstTab = true; + } + tabPaneData.value = panes; + } + + const currentIndex = panes.findIndex(paneData => paneData.name === unref(currentTabName)); + if (currentIndex === -1 && panes.length) { + shouldSetFirstTab = true; + } + + if (shouldSetFirstTab) { + setCurrentTab(panes[0].name); + } + } + + onMounted(() => { + updateTabPaneData(); + }); + + onUpdated(() => { + updateTabPaneData(); + }); + + useTabsProvide({ + prevTabName, + currentTabName, + prevTabIndex, + currentTabIndex, + tabPaneData, + setCurrentTab, + toNextTab, + toPrevTab, + }); + + const tabsInstance: TabsInstance = { + currentTabName, + setCurrentTab, + }; + + return { + prevTabName, + currentTabName, + prevTabIndex, + currentTabIndex, + setCurrentTab, + toNextTab, + toPrevTab, + + tabPaneData, + getTabPaneData, + updateTabPaneData, + tabsInstance, + }; +}; diff --git a/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tab-header.vue b/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tab-header.vue new file mode 100644 index 0000000000000000000000000000000000000000..cc6f36ae20f05ef92bb35ca4db927094a8c16072 --- /dev/null +++ b/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tab-header.vue @@ -0,0 +1,167 @@ + + + + + + diff --git a/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tab-pane.vue b/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tab-pane.vue new file mode 100644 index 0000000000000000000000000000000000000000..5ade0a8d1b32d469ea27dc6f21f02214b9687930 --- /dev/null +++ b/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tab-pane.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tabs.vue b/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tabs.vue new file mode 100644 index 0000000000000000000000000000000000000000..54d787bf7bd8612566845e197325962e24ff8d0b --- /dev/null +++ b/src/components/common-base/tabs/mobile-menu-tabs/mobile-menu-tabs.vue @@ -0,0 +1,48 @@ + + + + + + diff --git a/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tab-header.vue b/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tab-header.vue new file mode 100644 index 0000000000000000000000000000000000000000..15276785b4a25462bac603eab6f5a1e818f6a5ce --- /dev/null +++ b/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tab-header.vue @@ -0,0 +1,171 @@ + + + + + + diff --git a/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tab-pane.vue b/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tab-pane.vue new file mode 100644 index 0000000000000000000000000000000000000000..5c4dbeb627d965e2748798bb49ad1eec6026b5ff --- /dev/null +++ b/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tab-pane.vue @@ -0,0 +1,31 @@ + + + + + + diff --git a/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tabs.vue b/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tabs.vue new file mode 100644 index 0000000000000000000000000000000000000000..bc8f173cb8736291ae99d43fe600fc5847a131d3 --- /dev/null +++ b/src/components/common-base/tabs/pc-aside-tabs/pc-aside-tabs.vue @@ -0,0 +1,45 @@ + + + + + + diff --git a/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tab-header.vue b/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tab-header.vue new file mode 100644 index 0000000000000000000000000000000000000000..95513ae23c3eeebac2637c1f38c645189145bd59 --- /dev/null +++ b/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tab-header.vue @@ -0,0 +1,78 @@ + + + + + + diff --git a/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tab-pane.vue b/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tab-pane.vue new file mode 100644 index 0000000000000000000000000000000000000000..119b12892f1081b6fc24ac14b666307289263b05 --- /dev/null +++ b/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tab-pane.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tabs.vue b/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tabs.vue new file mode 100644 index 0000000000000000000000000000000000000000..dab4e2db2a9bdb2cc8b3651a328af6323c0da8f5 --- /dev/null +++ b/src/components/common-base/tabs/pc-normal-tabs/pc-normal-tabs.vue @@ -0,0 +1,23 @@ + + + + diff --git a/src/components/common-base/tabs/portrait-normal-tabs/portrait-normal-tabs.vue b/src/components/common-base/tabs/portrait-normal-tabs/portrait-normal-tabs.vue new file mode 100644 index 0000000000000000000000000000000000000000..549fa9120ea0a17f89cd518d27b817324c47f558 --- /dev/null +++ b/src/components/common-base/tabs/portrait-normal-tabs/portrait-normal-tabs.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/src/components/common-business/basic-info/imgs/low-latency-bg-mob.svg b/src/components/common-business/basic-info/imgs/low-latency-bg-mob.svg new file mode 100644 index 0000000000000000000000000000000000000000..93c92a5e3c9c25d7cd4488e9b3640aac12323950 --- /dev/null +++ b/src/components/common-business/basic-info/imgs/low-latency-bg-mob.svg @@ -0,0 +1,119 @@ + + + + icon无延迟-移动端 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/common-business/basic-info/imgs/low-latency-bg.svg b/src/components/common-business/basic-info/imgs/low-latency-bg.svg new file mode 100644 index 0000000000000000000000000000000000000000..d8d202f00a73bedf760ba5de17070514fd3a4553 --- /dev/null +++ b/src/components/common-business/basic-info/imgs/low-latency-bg.svg @@ -0,0 +1,117 @@ + + + + icon无延迟备份 2 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/common-business/basic-info/mobile-basic-info.vue b/src/components/common-business/basic-info/mobile-basic-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..c56b6589342c7d0aa80f0bd490629d41ce4cecc8 --- /dev/null +++ b/src/components/common-business/basic-info/mobile-basic-info.vue @@ -0,0 +1,243 @@ + + + + + + diff --git a/src/components/common-business/basic-info/pc-basic-info.vue b/src/components/common-business/basic-info/pc-basic-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..7d686e81453b90f19f3d2d129f31331562d688e0 --- /dev/null +++ b/src/components/common-business/basic-info/pc-basic-info.vue @@ -0,0 +1,250 @@ + + + + + + diff --git a/src/components/common-business/enroll/hooks/use-enroll-dialog.ts b/src/components/common-business/enroll/hooks/use-enroll-dialog.ts new file mode 100644 index 0000000000000000000000000000000000000000..c1619311704fa03853f4b7254211467573abe1d3 --- /dev/null +++ b/src/components/common-business/enroll/hooks/use-enroll-dialog.ts @@ -0,0 +1,70 @@ +import { translate } from '@/assets/lang'; +import { useEnrollStore } from '@/store/use-enroll-store'; +import { computed, ref, unref } from 'vue'; + +/** + * 报名表单实例 + */ +export interface EnrollDialogInstance { + /** 显示报名观看表单弹层 */ + openEnrollForm(): void; +} + +export type EnrollDialogModel = 'form' | 'login'; + +/** + * 报名观看弹窗 hook + */ +export const useEnrollDialog = () => { + const enrollStore = useEnrollStore(); + + /** 弹层是否显示 */ + const dialogVisible = ref(false); + + /** 弹层模式 */ + const dialogModel = ref('form'); + + /** 弹层标题 */ + const dialogTitle = computed(() => { + if (unref(dialogModel) === 'login') { + return translate('enroll.loginTitle'); + } + + return enrollStore.enrollTitle; + }); + + /** 显示报名观看表单弹层 */ + function openEnrollForm() { + dialogModel.value = 'form'; + dialogVisible.value = true; + } + + /** 关闭报名观看表单弹层 */ + function closeEnrollDialog() { + dialogVisible.value = false; + } + + /** 处理点击已报名入口 */ + function onClickEnrolled() { + dialogModel.value = 'login'; + } + + function onClickBack() { + dialogModel.value = 'form'; + } + + const enrollDialogInstance: EnrollDialogInstance = { + openEnrollForm, + }; + + return { + dialogVisible, + dialogTitle, + enrollDialogInstance, + dialogModel, + openEnrollForm, + closeEnrollDialog, + onClickEnrolled, + onClickBack, + }; +}; diff --git a/src/components/common-business/enroll/hooks/use-enroll-enter.ts b/src/components/common-business/enroll/hooks/use-enroll-enter.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b80cd1276cf42bd9112e23e2645dfacd3f4a171 --- /dev/null +++ b/src/components/common-business/enroll/hooks/use-enroll-enter.ts @@ -0,0 +1,22 @@ +import { ref, unref } from 'vue'; +import { EnrollDialogInstance } from './use-enroll-dialog'; + +/** + * 报名观看入口 hook + */ +export const useEnrollEnter = () => { + const dialogRef = ref(); + + /** 处理点击入口 */ + function onClickEnter() { + const dialogInstrance = unref(dialogRef); + if (dialogInstrance) { + dialogInstrance.openEnrollForm(); + } + } + + return { + dialogRef, + onClickEnter, + }; +}; diff --git a/src/components/common-business/enroll/hooks/use-enroll-form.ts b/src/components/common-business/enroll/hooks/use-enroll-form.ts new file mode 100644 index 0000000000000000000000000000000000000000..85cfaf571b06912f2f7c59131575d2e5c07a14c3 --- /dev/null +++ b/src/components/common-business/enroll/hooks/use-enroll-form.ts @@ -0,0 +1,347 @@ +/* eslint-disable sonarjs/cognitive-complexity */ +import { translate } from '@/assets/lang'; +import { validatePhoneNumber } from '@/assets/utils/validate'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { ImageVerifyInputInstance } from '@/components/common-base/form/form-image-verify-input/type'; +import { useDialogTipsUtils } from '@/components/common-base/dialog/use-dialog-tips'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { getAreaSelectNames, isSelectFinish } from '@/plugins/polyv-ui/area-utils'; +import { useEnrollStore } from '@/store/use-enroll-store'; +import { + EnrollAuthType, + EnrollFieldItem, + EnrollFieldType, + EnrollFormContent, + SubmitEnrollError, +} from '@polyv/live-watch-sdk'; +import { computed, reactive, ref } from 'vue'; +import { useCommonStore } from '@/store/use-common-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +interface EnrollBasicFormData { + /** 验证的手机号 */ + phoneNumber: string; + /** 手机区号 */ + areaCode: string; + /** 图片验证码 */ + imageId: string; + imageCaptcha: string; + /** 短信验证码 */ + smsCode: string; + /** 验证的邮箱地址 */ + email: string; +} + +type EnrollValFormData = Record; + +export const enrollFormEmits = () => ({ + /** 关闭弹层 */ + 'close-dialog': emitFunc(), + /** 点击取消 */ + 'click-cancel': emitFunc(), + /** 点击我已报名 */ + 'click-enrolled': emitFunc(), +}); + +/** + * 报名观看表单填写 hook + */ +export const useEnrollForm = (options: { emit: VueEmit }) => { + const { emit } = options; + + const watchAppStore = useWatchAppStore(); + const enrollStore = useEnrollStore(); + const commonStore = useCommonStore(); + + /** + * 获取字段的表单初始值 + */ + function getInitFormData() { + const data: EnrollValFormData = {}; + + enrollStore.enrollFields.forEach(item => { + if ( + [EnrollFieldType.Checkbox, EnrollFieldType.Upload, EnrollFieldType.Area].includes(item.type) + ) { + data[item.fieldId] = []; + } else if (item.type === EnrollFieldType.Privacy) { + data[item.fieldId] = false; + } else { + data[item.fieldId] = ''; + } + }); + + return data; + } + + const imageVerifyInputRef = ref(); + + /** 填写表单对象 */ + const formData = reactive({ + ...getInitFormData(), + // 手机号 + phoneNumber: '', + // 手机区号 + areaCode: '+86', + // 图片验证码 id + imageId: '', + // 图片验证码 + imageCaptcha: '', + // 短信验证码 + smsCode: '', + // 验证的邮箱地址 + email: '', + }); + + /** 处理手机号码输入框改变 */ + function onPhoneNumberChange(phoneNumber: string) { + formData.phoneNumber = phoneNumber; + } + + /** 处理邮箱输入框改变 */ + function onEmailChange(email: string) { + formData.email = email; + } + + /** 表单验证规则 */ + const formRules = computed(() => { + const rules: ValidatorRules = {}; + + enrollStore.enrollFields.forEach(fieldItem => { + const fieldId = fieldItem.fieldId; + const isRequired = fieldItem.isRequired; + + switch (fieldItem.type) { + case EnrollFieldType.Name: + case EnrollFieldType.Text: + case EnrollFieldType.Radio: + rules[fieldId] = [ + { type: 'string', message: translate('auth.error.improveInfo'), required: isRequired }, + ]; + break; + case EnrollFieldType.Email: + rules[fieldId] = [ + { type: 'string', message: translate('auth.error.improveInfo'), required: isRequired }, + { type: 'email', message: translate('form.error.emailError') }, + ]; + break; + case EnrollFieldType.Checkbox: + case EnrollFieldType.Upload: + rules[fieldId] = [ + { type: 'array', message: translate('auth.error.improveInfo'), required: isRequired }, + ]; + break; + case EnrollFieldType.Area: + rules[fieldId] = [ + { type: 'array', message: translate('auth.error.improveInfo'), required: isRequired }, + { + validator: (rule, val: string[]) => { + if (val.length && !isSelectFinish(val, commonStore.areaData)) { + return [translate('form.error.areaError')]; + } + return []; + }, + }, + ]; + break; + case EnrollFieldType.Privacy: + rules[fieldId] = { + validator: (rule, val: boolean) => { + if (!val) { + return [translate('auth.error.checkProtocol')]; + } + return []; + }, + }; + break; + case EnrollFieldType.Mobile: + // 使用手机号验证 + if (fieldItem.authType === EnrollAuthType.Mobile) { + rules[fieldId] = [ + { + type: 'string', + message: translate('form.error.phoneNumberRequired'), + required: true, + }, + { + validator: () => { + const phoneNumber = formData.phoneNumber; + const areaCode = formData.areaCode; + + if (validatePhoneNumber(phoneNumber, areaCode)) { + return []; + } + + return [translate('form.error.phoneNumberError')]; + }, + }, + ]; + if (enrollStore.smsVerifyEnabled) { + rules.smsCode = [ + { + type: 'string', + message: translate('form.error.smsVerifyRequired'), + required: true, + }, + ]; + } + } + // 使用邮箱验证 + if (fieldItem.authType === EnrollAuthType.Email) { + rules[fieldId] = [ + { + type: 'string', + message: translate('form.error.emailRequired'), + required: isRequired, + }, + { type: 'email', message: translate('form.error.emailError') }, + ]; + } + break; + } + }); + + return rules; + }); + + /** 格式化特殊的字段名称 */ + function formatSpecialName(fieldItem: EnrollFieldItem): string { + let name = fieldItem.name; + if (fieldItem.type === EnrollFieldType.Radio) { + name = `[${translate('form.radio')}] ${name}`; + } + if (fieldItem.type === EnrollFieldType.Checkbox) { + name = `[${translate('form.checkbox')}] ${name}`; + } + return name; + } + + /** + * 处理表单提交 + */ + async function submitForm() { + const watchCore = getWatchCore(); + const content: EnrollFormContent = {}; + const formDataVal = formData as EnrollValFormData; + + const phoneNumber = formData.phoneNumber; + const areaCode = formData.areaCode; + const smsCode = formData.smsCode; + const email = formData.email; + + enrollStore.enrollFields.forEach(fieldItem => { + const fieldId = fieldItem.fieldId; + + switch (fieldItem.type) { + case EnrollFieldType.Name: + case EnrollFieldType.Text: + case EnrollFieldType.Radio: + case EnrollFieldType.Mobile: + case EnrollFieldType.Email: + content[fieldId] = formDataVal[fieldId] as string; + break; + case EnrollFieldType.Checkbox: + case EnrollFieldType.Upload: + content[fieldId] = formDataVal[fieldId] as string[]; + break; + case EnrollFieldType.Area: { + const areaValue = formDataVal[fieldId] as string[]; + const areaPickData = getAreaSelectNames(areaValue, commonStore.areaData).join('/'); + content[fieldId] = areaPickData; + break; + } + case EnrollFieldType.Privacy: + content[fieldId] = '√'; + break; + } + }); + + const result = await watchCore.enroll.submitEnrollForm({ + phoneNumber, + areaCode, + smsCode, + email, + content, + }); + + if (!result.success) { + switch (result.failReason) { + case SubmitEnrollError.SmsCodeVerifyError: + toast.error(translate('enroll.error.smsCodeError')); + break; + case SubmitEnrollError.LackRequired: + default: + toast.error(result.failMessage || '未知错误!'); + break; + } + return; + } + + // 优先判断是否需要审核和审核是否通过 + if (result.auditEnabled && !result.hasAudited) { + toast.success(translate('enroll.auditing')); + emit('close-dialog'); + return; + } + + // 入口报名且报名成功的情况下需要重新安装 watchCore + if (enrollStore.needEnrollByEnter && result.hasEnrolled) { + await watchAppStore.resetUpWatchCore(); + } + + emit('close-dialog'); + } + + function onClickCancel() { + emit('click-cancel'); + } + + function onClickEnrolled() { + emit('click-enrolled'); + } + + return { + EnrollFieldType, + EnrollAuthType, + imageVerifyInputRef, + formData, + formRules, + onPhoneNumberChange, + onEmailChange, + formatSpecialName, + submitForm, + onClickCancel, + onClickEnrolled, + }; +}; + +export const useEnrollCheckMobile = (options: { + /** 获取手机区号 */ + getAreaCode: () => string; +}) => { + const { showDialogTips } = useDialogTipsUtils(); + + /** 失焦检查报名状态 */ + async function blurToCheckStatus(mobileOrEmail: string) { + if (!mobileOrEmail) { + return; + } + + const watchCore = getWatchCore(); + const result = await watchCore.enroll.getEnrollStatus({ + phoneNumber: mobileOrEmail, + email: mobileOrEmail, + areaCode: options.getAreaCode(), + }); + + if (result.hasEnrolled) { + showDialogTips(translate('enroll.enrolledTips')); + } + } + + return { + blurToCheckStatus, + }; +}; diff --git a/src/components/common-business/enroll/hooks/use-enroll-login.ts b/src/components/common-business/enroll/hooks/use-enroll-login.ts new file mode 100644 index 0000000000000000000000000000000000000000..45c58f9fa6754acaea583583e94f54103ce36b78 --- /dev/null +++ b/src/components/common-business/enroll/hooks/use-enroll-login.ts @@ -0,0 +1,141 @@ +import { DEFAULT_PHONE_NUMBER_AREA_CODE } from '@/assets/constants/defaults'; +import { translate } from '@/assets/lang'; +import { validatePhoneNumber } from '@/assets/utils/validate'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { ImageVerifyInputInstance } from '@/components/common-base/form/form-image-verify-input/type'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { useEnrollStore } from '@/store/use-enroll-store'; +import { EnrollAuthType } from '@polyv/live-watch-sdk'; +import { computed, reactive, ref } from 'vue'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +export const enrollLoginEmits = () => ({ + /** 点击取消 */ + 'click-cancel': emitFunc(), + /** 关闭弹层 */ + 'close-dialog': emitFunc(), +}); + +/** + * 报名观看登录 hook + */ +export const useEnrollLogin = (options: { emit: VueEmit }) => { + const { emit } = options; + + const watchAppStore = useWatchAppStore(); + const enrollStore = useEnrollStore(); + + const imageVerifyInputRef = ref(); + + /** 表单对象 */ + const formData = reactive({ + // 手机号 + phoneNumber: '', + // 区号 + areaCode: DEFAULT_PHONE_NUMBER_AREA_CODE, + // 图片验证码 + imageId: '', + imageCaptcha: '', + // 短信验证码 + smsCode: '', + // 邮箱 + email: '', + }); + + /** 表单验证规则 */ + const formRules = computed(() => { + const rules: ValidatorRules = {}; + + // 手机号验证 + if (enrollStore.enrollAuthType === EnrollAuthType.Mobile) { + rules.phoneNumber = [ + { type: 'string', message: translate('form.error.phoneNumberRequired'), required: true }, + { + validator: () => { + const phoneNumber = formData.phoneNumber; + const areaCode = formData.areaCode; + + if (validatePhoneNumber(phoneNumber, areaCode)) { + return []; + } + + return [translate('form.error.phoneNumberError')]; + }, + }, + ]; + + if (enrollStore.smsVerifyEnabled) { + rules.smsCode = [ + { type: 'string', message: translate('form.error.smsVerifyRequired'), required: true }, + ]; + } + } + + // 邮箱验证 + if (enrollStore.enrollAuthType === EnrollAuthType.Email) { + rules.email = [ + { type: 'string', message: translate('form.error.emailRequired'), required: true }, + { type: 'email', message: translate('form.error.emailError') }, + ]; + } + + return rules; + }); + + /** + * 提交登录表单 + */ + async function submitLoginForm() { + const watchCore = getWatchCore(); + + const result = await watchCore.enroll.loginEnroll({ + phoneNumber: formData.phoneNumber, + areaCode: formData.areaCode, + smsCode: formData.smsCode, + email: formData.email, + }); + + if (!result.success) { + toast.error(result.failMessage || '未知错误!'); + return; + } + + // 未填写报名信息 + if (!result.hasEnrolled) { + let toastMsg = translate('enroll.error.phoneUnenroll'); + if (enrollStore.enrollAuthType === EnrollAuthType.Email) { + toastMsg = translate('enroll.error.emailUnenroll'); + } + toast.error(toastMsg); + return; + } + + // 已填写但报名审核中 + if (result.auditEnabled && !result.hasAudited) { + toast.success(translate('enroll.auditing')); + emit('close-dialog'); + return; + } + + // 入口报名且报名成功的情况下需要重新安装 watchCore + if (enrollStore.needEnrollByEnter && result.hasEnrolled) { + await watchAppStore.resetUpWatchCore(); + } + + emit('close-dialog'); + } + + function onClickCancel() { + emit('click-cancel'); + } + + return { + imageVerifyInputRef, + formData, + formRules, + submitLoginForm, + onClickCancel, + }; +}; diff --git a/src/components/common-business/enroll/mobile-enroll-form.vue b/src/components/common-business/enroll/mobile-enroll-form.vue new file mode 100644 index 0000000000000000000000000000000000000000..8edb3e5d0356cab53fb4c68180c32202b7e5dea3 --- /dev/null +++ b/src/components/common-business/enroll/mobile-enroll-form.vue @@ -0,0 +1,328 @@ + + + + + + diff --git a/src/components/common-business/enroll/mobile-enroll-login.vue b/src/components/common-business/enroll/mobile-enroll-login.vue new file mode 100644 index 0000000000000000000000000000000000000000..1e3b8996340bdf1d3eb7e43848359ff8c0532610 --- /dev/null +++ b/src/components/common-business/enroll/mobile-enroll-login.vue @@ -0,0 +1,111 @@ + + + + + + diff --git a/src/components/common-business/enroll/mobile-enroll-popup.vue b/src/components/common-business/enroll/mobile-enroll-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..3ca6a24708be0cee3c29d97e527eb603bee4bab5 --- /dev/null +++ b/src/components/common-business/enroll/mobile-enroll-popup.vue @@ -0,0 +1,44 @@ + + + + diff --git a/src/components/common-business/enroll/mobile-enroll.vue b/src/components/common-business/enroll/mobile-enroll.vue new file mode 100644 index 0000000000000000000000000000000000000000..8429af7fdc21aded893642a7a61881e20eedc4d0 --- /dev/null +++ b/src/components/common-business/enroll/mobile-enroll.vue @@ -0,0 +1,63 @@ + + + + + + diff --git a/src/components/common-business/enroll/pc-enroll-dialog.vue b/src/components/common-business/enroll/pc-enroll-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..7dafffa672826b6a95457f68d41437e3d3a0fd42 --- /dev/null +++ b/src/components/common-business/enroll/pc-enroll-dialog.vue @@ -0,0 +1,41 @@ + + + + diff --git a/src/components/common-business/enroll/pc-enroll-form.vue b/src/components/common-business/enroll/pc-enroll-form.vue new file mode 100644 index 0000000000000000000000000000000000000000..af87d09b5a7ace9e150fe1132868e2ba5145d9a1 --- /dev/null +++ b/src/components/common-business/enroll/pc-enroll-form.vue @@ -0,0 +1,348 @@ + + + + + + diff --git a/src/components/common-business/enroll/pc-enroll-login.vue b/src/components/common-business/enroll/pc-enroll-login.vue new file mode 100644 index 0000000000000000000000000000000000000000..8187494a445f723afa67c147f2ff34b9b75276be --- /dev/null +++ b/src/components/common-business/enroll/pc-enroll-login.vue @@ -0,0 +1,128 @@ + + + + + + diff --git a/src/components/common-business/enroll/pc-enroll.vue b/src/components/common-business/enroll/pc-enroll.vue new file mode 100644 index 0000000000000000000000000000000000000000..3ee8d7d398b4ebf4adc4a8df436519472d4c01de --- /dev/null +++ b/src/components/common-business/enroll/pc-enroll.vue @@ -0,0 +1,63 @@ + + + + + + diff --git a/src/components/common-business/lang-switch/mobile-lang-switch.vue b/src/components/common-business/lang-switch/mobile-lang-switch.vue new file mode 100644 index 0000000000000000000000000000000000000000..367e9dcad18b57556ce0d814d62a423c59e248ea --- /dev/null +++ b/src/components/common-business/lang-switch/mobile-lang-switch.vue @@ -0,0 +1,48 @@ + + + + diff --git a/src/components/common-business/lang-switch/pc-lang-switch.vue b/src/components/common-business/lang-switch/pc-lang-switch.vue new file mode 100644 index 0000000000000000000000000000000000000000..23c34b0939368bf5c4ae2ac14a11bfa82a2159e9 --- /dev/null +++ b/src/components/common-business/lang-switch/pc-lang-switch.vue @@ -0,0 +1,183 @@ + + + + + + diff --git a/src/components/common-business/live-booking/hooks/use-live-booking-fail.ts b/src/components/common-business/live-booking/hooks/use-live-booking-fail.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b83e5bfd00ed6017a7b9138e6841bc004484be4 --- /dev/null +++ b/src/components/common-business/live-booking/hooks/use-live-booking-fail.ts @@ -0,0 +1,18 @@ +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { LiveBookingFailReason } from '@polyv/live-watch-sdk'; + +/** + * 提示预约错误原因 + * @param failReason 错误原因 + */ +export function toastBookingFail(failReason: LiveBookingFailReason) { + switch (failReason) { + case LiveBookingFailReason.Unknown: + toast.error(translate('liveBooking.fail')); + break; + case LiveBookingFailReason.VerifyCodeError: + toast.error(translate('liveBooking.smsWrong')); + break; + } +} diff --git a/src/components/common-business/live-booking/hooks/use-live-booking-form.ts b/src/components/common-business/live-booking/hooks/use-live-booking-form.ts new file mode 100644 index 0000000000000000000000000000000000000000..170f805baac62276124f652ef5183409f322a46f --- /dev/null +++ b/src/components/common-business/live-booking/hooks/use-live-booking-form.ts @@ -0,0 +1,122 @@ +/** + * @file (短信)直播预约表单 hook + */ +import { translate } from '@/assets/lang'; +import { getStorageKey } from '@/assets/utils/storage'; +import { validatePhoneNumber } from '@/assets/utils/validate'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { useLiveBookingStore } from '@/store/use-live-booking-store'; +import { local } from '@just4/storage'; +import { computed, reactive, ref } from 'vue'; +import { LiveBookingFormInstance } from '../types'; +import { toastBookingFail } from './use-live-booking-fail'; + +// local 中的手机号码缓存 key +const LOCAL_PHONE_KEY = getStorageKey('live-booking-phone'); +// local 中的手机区号缓存 key +const LOCAL_AREA_CODE_KEY = getStorageKey('live-booking-area-code'); + +export const useLiveBookingForm = () => { + const liveBookingStore = useLiveBookingStore(); + + /** 直播预约表单数据 */ + const bookingFormData = reactive({ + // 手机号 + phoneNumber: local.get(LOCAL_PHONE_KEY) || '', + // 手机区号 + areaCode: local.get(LOCAL_AREA_CODE_KEY) || '+86', + // 短信验证码 + verifyCode: '', + }); + + /** 直播预约表单验证规则 */ + const bookingFormRules = computed(() => { + return { + phoneNumber: [ + { type: 'string', message: translate('form.error.phoneNumberRequired'), required: true }, + { + validator: () => { + const { phoneNumber, areaCode } = bookingFormData; + + if (validatePhoneNumber(phoneNumber, areaCode)) { + return []; + } + + return [translate('form.error.phoneNumberError')]; + }, + }, + ], + verifyCode: [ + { type: 'string', message: translate('form.error.smsVerifyRequired'), required: true }, + ], + }; + }); + + /** 预约表单是否显示 */ + const bookingFormVisible = ref(false); + + /** 打开弹层 */ + function openBookingForm() { + bookingFormVisible.value = true; + bookingFormData.verifyCode = ''; + } + + /** 提交直播预约表单 */ + async function submitLiveBookingForm() { + const watchCore = getWatchCore(); + const result = await watchCore.liveBooking.submitSmsLiveBooking({ + phoneNumber: bookingFormData.phoneNumber, + areaCode: bookingFormData.areaCode, + verifyCode: bookingFormData.verifyCode, + }); + + if (result.success) { + local.set(LOCAL_PHONE_KEY, bookingFormData.phoneNumber); + local.set(LOCAL_AREA_CODE_KEY, bookingFormData.areaCode); + + toast.success(translate('liveBooking.bookingSuccess')); + liveBookingStore.isSmsLiveBooking = true; + + bookingFormVisible.value = false; + } else { + toastBookingFail(result.failReason); + } + } + + /** 取消预约 */ + async function cancelLiveBooking() { + const watchCore = getWatchCore(); + const result = await watchCore.liveBooking.cancelSmsLiveBooking({ + phoneNumber: bookingFormData.phoneNumber, + areaCode: bookingFormData.areaCode, + }); + + if (result.success) { + local.remove(LOCAL_PHONE_KEY); + local.remove(LOCAL_AREA_CODE_KEY); + + toast.success(translate('liveBooking.cancelSuccess')); + liveBookingStore.isSmsLiveBooking = false; + } else { + toastBookingFail(result.failReason); + } + } + + const liveBookingFormInstance: LiveBookingFormInstance = { + openBookingForm, + submitLiveBookingForm, + cancelLiveBooking, + }; + + return { + bookingFormData, + bookingFormRules, + bookingFormVisible, + openBookingForm, + submitLiveBookingForm, + cancelLiveBooking, + liveBookingFormInstance, + }; +}; diff --git a/src/components/common-business/live-booking/hooks/use-live-booking-wx.ts b/src/components/common-business/live-booking/hooks/use-live-booking-wx.ts new file mode 100644 index 0000000000000000000000000000000000000000..baeba4af483d6c6a9c5f5b4a86b38985b0af5451 --- /dev/null +++ b/src/components/common-business/live-booking/hooks/use-live-booking-wx.ts @@ -0,0 +1,48 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useLiveBookingStore } from '@/store/use-live-booking-store'; +import { ref } from 'vue'; +import { SubscribeInstance } from '../types'; +import { toastBookingFail } from './use-live-booking-fail'; + +export const useLiveBookingWx = () => { + const liveBookingStore = useLiveBookingStore(); + + const subscribeRef = ref(); + + /** 提交微信预约 */ + async function wxSubmitLiveBooking() { + const watchCore = getWatchCore(); + const result = await watchCore.liveBooking.submitWxLiveBooking(); + + if (result.success) { + liveBookingStore.wxBookingCount += 1; + liveBookingStore.isWxLiveBooking = true; + + // 未关注,弹出二维码弹层 + if (!liveBookingStore.isWxSubscribed) { + subscribeRef.value?.openPopup(); + } + } else { + toastBookingFail(result.failReason); + } + } + + /** 取消微信预约 */ + async function wxCancelLiveBooking() { + const watchCore = getWatchCore(); + const result = await watchCore.liveBooking.cancelWxLiveBooking(); + + if (result.success) { + liveBookingStore.wxBookingCount -= 1; + liveBookingStore.isWxLiveBooking = false; + } else { + toastBookingFail(result.failReason); + } + } + + return { + subscribeRef, + wxSubmitLiveBooking, + wxCancelLiveBooking, + }; +}; diff --git a/src/components/common-business/live-booking/imgs/apollo-live-qrcode.jpg b/src/components/common-business/live-booking/imgs/apollo-live-qrcode.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b8985815a28ff08f27de214b50016cb23165da5d Binary files /dev/null and b/src/components/common-business/live-booking/imgs/apollo-live-qrcode.jpg differ diff --git a/src/components/common-business/live-booking/mobile-live-booking-button.vue b/src/components/common-business/live-booking/mobile-live-booking-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..a4a23965ff685a078ec9fffe5d885edfd27bb6e5 --- /dev/null +++ b/src/components/common-business/live-booking/mobile-live-booking-button.vue @@ -0,0 +1,183 @@ + + + + + + diff --git a/src/components/common-business/live-booking/mobile-live-booking-form-popup.vue b/src/components/common-business/live-booking/mobile-live-booking-form-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..ecb5b0c2a8a975422f84eccbf46058f4f8d9f0f7 --- /dev/null +++ b/src/components/common-business/live-booking/mobile-live-booking-form-popup.vue @@ -0,0 +1,75 @@ + + + + + + diff --git a/src/components/common-business/live-booking/mobile-live-booking-subscribe-popup.vue b/src/components/common-business/live-booking/mobile-live-booking-subscribe-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..d565ec4416efcf05984360edc10c841e48f8ebea --- /dev/null +++ b/src/components/common-business/live-booking/mobile-live-booking-subscribe-popup.vue @@ -0,0 +1,96 @@ + + + + + + diff --git a/src/components/common-business/live-booking/pc-live-booking-button.vue b/src/components/common-business/live-booking/pc-live-booking-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..5216600500f4865b26086792e3408d3ba2f20707 --- /dev/null +++ b/src/components/common-business/live-booking/pc-live-booking-button.vue @@ -0,0 +1,130 @@ + + + + + + diff --git a/src/components/common-business/live-booking/pc-live-booking-form-dialog.vue b/src/components/common-business/live-booking/pc-live-booking-form-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..954da7a6061777524b98c01dcc655e856c2b6233 --- /dev/null +++ b/src/components/common-business/live-booking/pc-live-booking-form-dialog.vue @@ -0,0 +1,83 @@ + + + + + + diff --git a/src/components/common-business/live-booking/types/index.ts b/src/components/common-business/live-booking/types/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1331ad52682bc96040cfeae3507503f5e605b396 --- /dev/null +++ b/src/components/common-business/live-booking/types/index.ts @@ -0,0 +1,21 @@ +/** + * 短信预约表单实例 + */ +export interface LiveBookingFormInstance { + /** 打开表单弹层 */ + openBookingForm: () => void; + /** 提交预约 */ + submitLiveBookingForm: () => Promise; + /** 取消预约 */ + cancelLiveBooking: () => Promise; +} + +/** + * 公众号二维码实例 + */ +export interface SubscribeInstance { + /** 打开公众号二维码弹层 */ + openPopup: (qrcodeUrl?: string) => void; + /** 关闭弹层 */ + closePopup: () => void; +} diff --git a/src/components/common-business/live-count-down/mobile-live-count-down.vue b/src/components/common-business/live-count-down/mobile-live-count-down.vue new file mode 100644 index 0000000000000000000000000000000000000000..15859bee6cf87629f51a11d3143a0be6dddc6e67 --- /dev/null +++ b/src/components/common-business/live-count-down/mobile-live-count-down.vue @@ -0,0 +1,30 @@ + + + + diff --git a/src/components/common-business/live-count-down/pc-live-count-down.vue b/src/components/common-business/live-count-down/pc-live-count-down.vue new file mode 100644 index 0000000000000000000000000000000000000000..2a1d5666a50d1116bb3b6904f2cb1a78b864fe46 --- /dev/null +++ b/src/components/common-business/live-count-down/pc-live-count-down.vue @@ -0,0 +1,28 @@ + + + + diff --git a/src/components/common-business/live-count-down/use-live-count-down.ts b/src/components/common-business/live-count-down/use-live-count-down.ts new file mode 100644 index 0000000000000000000000000000000000000000..100fd4f0b6314aee87db61308edb3459d13e94c4 --- /dev/null +++ b/src/components/common-business/live-count-down/use-live-count-down.ts @@ -0,0 +1,13 @@ +import { useChannelInfoStore } from '@/store/use-channel-info-store'; + +export const useLiveCountDown = () => { + const channelInfoStore = useChannelInfoStore(); + + function onCountDownFinish() { + channelInfoStore.isLiveStartCountDownEnd = true; + } + + return { + onCountDownFinish, + }; +}; diff --git a/src/components/common-business/page-fixed-widgets/fixed-back-top.vue b/src/components/common-business/page-fixed-widgets/fixed-back-top.vue new file mode 100644 index 0000000000000000000000000000000000000000..ed39d1eea8b4bc13e59b2d23724f42f7638abcdb --- /dev/null +++ b/src/components/common-business/page-fixed-widgets/fixed-back-top.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/common-business/page-fixed-widgets/fixed-web-share.vue b/src/components/common-business/page-fixed-widgets/fixed-web-share.vue new file mode 100644 index 0000000000000000000000000000000000000000..a6b0cc96b26a3b09f4f041620181547dd824b7f3 --- /dev/null +++ b/src/components/common-business/page-fixed-widgets/fixed-web-share.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/components/common-business/page-fixed-widgets/page-fixed-widgets.vue b/src/components/common-business/page-fixed-widgets/page-fixed-widgets.vue new file mode 100644 index 0000000000000000000000000000000000000000..65d8aed2e79527d4ab280e667e2a1bd94e75ebcd --- /dev/null +++ b/src/components/common-business/page-fixed-widgets/page-fixed-widgets.vue @@ -0,0 +1,63 @@ + + + + + + diff --git a/src/components/common-business/page-footer/mobile-page-footer.vue b/src/components/common-business/page-footer/mobile-page-footer.vue new file mode 100644 index 0000000000000000000000000000000000000000..28c23e326580269f9c03aac42d09107908a6a59a --- /dev/null +++ b/src/components/common-business/page-footer/mobile-page-footer.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/common-business/page-footer/pc-page-footer.vue b/src/components/common-business/page-footer/pc-page-footer.vue new file mode 100644 index 0000000000000000000000000000000000000000..7263b05c5e455c7f6c7a1d70344affcff0a2e4e1 --- /dev/null +++ b/src/components/common-business/page-footer/pc-page-footer.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/common-business/page-footer/use-page-footer.ts b/src/components/common-business/page-footer/use-page-footer.ts new file mode 100644 index 0000000000000000000000000000000000000000..83284e202a2ffc1cbe4b3778a269aac5b940b610 --- /dev/null +++ b/src/components/common-business/page-footer/use-page-footer.ts @@ -0,0 +1,41 @@ +/** + * @file 页脚 hook + */ +import { useChannelStore } from '@/store/use-channel-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { ynToBool } from '@utils-ts/boolean'; +import { changeProtocol } from '@utils-ts/net'; +import { computed, unref } from 'vue'; + +/** 页脚 hook */ +export const usePageFooter = () => { + const { channelDetail } = storeDefinitionToRefs(useChannelStore); + + /** 页脚设置信息 */ + const footerSetting = computed(() => unref(channelDetail)?.footerSetting); + + /** 页脚是否显示 */ + const footerVisible = computed(() => ynToBool(unref(footerSetting)?.showFooterEnabled || 'N')); + + /** 页脚跳转链接 */ + const footerLink = computed(() => { + const setting = unref(footerSetting); + if (!setting) { + return ''; + } + const { footTextLinkProtocol, footTextLinkUrl } = setting; + return changeProtocol(footTextLinkUrl, footTextLinkProtocol); + }); + + /** 页脚文案 */ + const footerText = computed(() => { + return unref(footerSetting)?.footerText || ''; + }); + + return { + footerSetting, + footerVisible, + footerLink, + footerText, + }; +}; diff --git a/src/components/common-business/player/player-audio-live-placeholder.vue b/src/components/common-business/player/player-audio-live-placeholder.vue new file mode 100644 index 0000000000000000000000000000000000000000..b8896ea08ea6758e4608e07b635a31f05066534f --- /dev/null +++ b/src/components/common-business/player/player-audio-live-placeholder.vue @@ -0,0 +1,114 @@ + + + + + + diff --git a/src/components/common-business/player/player-video-placeholder.vue b/src/components/common-business/player/player-video-placeholder.vue new file mode 100644 index 0000000000000000000000000000000000000000..55cb7b410fb796dc550f9d2fcb1e31374cd1f730 --- /dev/null +++ b/src/components/common-business/player/player-video-placeholder.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/common-business/risk-confirm-letter/img/no-agree.png b/src/components/common-business/risk-confirm-letter/img/no-agree.png new file mode 100644 index 0000000000000000000000000000000000000000..740c4f696e0535d3fcde068923781f83d5a7d4d6 Binary files /dev/null and b/src/components/common-business/risk-confirm-letter/img/no-agree.png differ diff --git a/src/components/common-business/risk-confirm-letter/mobile-risk-confirm-letter.vue b/src/components/common-business/risk-confirm-letter/mobile-risk-confirm-letter.vue new file mode 100644 index 0000000000000000000000000000000000000000..489d8a5509fd65735c40a199d46ed58a1593e8a9 --- /dev/null +++ b/src/components/common-business/risk-confirm-letter/mobile-risk-confirm-letter.vue @@ -0,0 +1,276 @@ + + + + + diff --git a/src/components/common-business/risk-confirm-letter/pc-risk-confirm-letter.vue b/src/components/common-business/risk-confirm-letter/pc-risk-confirm-letter.vue new file mode 100644 index 0000000000000000000000000000000000000000..9f9d7a49665349a0edf5ae0474bbdb3318578652 --- /dev/null +++ b/src/components/common-business/risk-confirm-letter/pc-risk-confirm-letter.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/src/components/common-business/risk-confirm-letter/use-risk-confirm-letter.ts b/src/components/common-business/risk-confirm-letter/use-risk-confirm-letter.ts new file mode 100644 index 0000000000000000000000000000000000000000..92624ef3cd6209d6a355ea1e0172d559483d9389 --- /dev/null +++ b/src/components/common-business/risk-confirm-letter/use-risk-confirm-letter.ts @@ -0,0 +1,170 @@ +import { computed, ref, reactive, watchEffect } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; +import { FinanceRiskConfirmAgreement, FinanceRiskConfirmInfo } from '@polyv/live-watch-sdk'; +import { isMobile } from '@/assets/utils/browser'; +import { toast } from '@/hooks/components/use-toast'; +import { useFinanceStore } from '@/store/use-finance-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +export enum RiskConfirmLetterRenderMode { + /** 默认展示 */ + DEFAULT = 'default', + /** 预览协议 */ + PREVIEW_AGREEMENT = 'previewAgreement', + /** "不同意"的提示 */ + DISAGREE_TIPS = 'disagreeTips', +} + +enum RiskConfirmAgreeStatus { + DEFAULT = 0, + AGREE = 1, + DISAGREE = 2, +} + +export const useRiskConfirmLetterHook = () => { + const watchAppStore = useWatchAppStore(); + const financeStore = useFinanceStore(); + + const dialogVisible = ref(false); + + /** 风险确认同意状态 */ + const riskConfirmAgreeStatus = ref(RiskConfirmAgreeStatus.DEFAULT); + + /** 观众风险确认信息 */ + const viewerRiskConfirmInfo = ref(null); + + /** 当前预览的协议 */ + const curPreviewAgreement = reactive({ + title: '', + src: '', + }); + + /** 风险确认函-渲染模式 */ + const riskConfirmLetterRenderMode = computed(() => { + if (curPreviewAgreement.src) { + return RiskConfirmLetterRenderMode.PREVIEW_AGREEMENT; + } + + if (riskConfirmAgreeStatus.value === RiskConfirmAgreeStatus.DISAGREE) { + return RiskConfirmLetterRenderMode.DISAGREE_TIPS; + } + + return RiskConfirmLetterRenderMode.DEFAULT; + }); + + const contentWrapperStyle = computed(() => { + if (riskConfirmLetterRenderMode.value === RiskConfirmLetterRenderMode.PREVIEW_AGREEMENT) { + return { + minHeight: '400px', + }; + } + return { + minHeight: '212px', + maxHeight: '212px', + }; + }); + + /** 预览协议 */ + function handlePreviewAgreement(agreement: FinanceRiskConfirmAgreement) { + if (!viewerRiskConfirmInfo.value) return; + + const watchCore = getWatchCore(); + const previewAgreementUrl = watchCore.financeRiskConfirm.getRiskConfirmAgreementContentUrl({ + agreement, + isMobile: isMobile, + }); + + curPreviewAgreement.title = agreement.agreementTitle; + curPreviewAgreement.src = previewAgreementUrl; + } + + /** 预览默认协议 */ + function handlePreviewDefaultAgreement(title: '服务协议' | '隐私协议') { + if (!viewerRiskConfirmInfo.value) return; + + const defaultAgreement = viewerRiskConfirmInfo.value.defaultAgreements.find( + agreement => agreement.agreementTitle === title, + ); + + if (!defaultAgreement) { + console.error(`找不到《${title}》对应的协议内容`); + return; + } + + handlePreviewAgreement(defaultAgreement); + } + + /** 不同意 */ + function handleDisagreeRiskConfirm() { + riskConfirmAgreeStatus.value = RiskConfirmAgreeStatus.DISAGREE; + } + + /** 同意确认 */ + async function handleAgreeRiskConfirm() { + const watchCore = getWatchCore(); + const result = await watchCore.financeRiskConfirm.agreeRiskConfirm(); + + if (result.success) { + dialogVisible.value = false; + riskConfirmAgreeStatus.value = RiskConfirmAgreeStatus.AGREE; + + financeStore.syncRiskConfirm({ + riskConfirmStatus: true, + }); + watchAppStore.recoverConnectLiveWatch(); + } else { + console.error(result.failMessage); + toast.error('同意失败'); + } + } + + /** 重置风险确认函渲染模式 */ + function resetRiskConfirmLetterRenderMode() { + curPreviewAgreement.title = ''; + curPreviewAgreement.src = ''; + + riskConfirmAgreeStatus.value = RiskConfirmAgreeStatus.DEFAULT; + } + + /** + * 初始化观众风险确认信息 + * @desc 需要在获取到观众信息后才能正常使用 + * */ + async function initViewerRiskConfirmInfo() { + const watchCore = getWatchCore(); + const result = await watchCore.financeRiskConfirm.getViewerRiskConfirmInfo(); + if (!result.success) { + console.error(result.failMessage); + toast.error('获取观众风险确认函信息失败'); + return; + } + + viewerRiskConfirmInfo.value = result; + } + + // 副作用监听 + const stopWatchEffect = watchEffect(async () => { + if (!financeStore.riskConfirmEnabled) return; + if (watchAppStore.shouldShowSplash) return; + if (!watchAppStore.liveWatchInited) return; + + await initViewerRiskConfirmInfo(); + dialogVisible.value = true; + stopWatchEffect(); + }); + + return { + dialogVisible, + viewerRiskConfirmInfo, + curPreviewAgreement, + + riskConfirmLetterRenderMode, + contentWrapperStyle, + + resetRiskConfirmLetterRenderMode, + handlePreviewAgreement, + handlePreviewDefaultAgreement, + handleAgreeRiskConfirm, + handleDisagreeRiskConfirm, + }; +}; diff --git a/src/components/common-business/status-tag/mini-status-tag.vue b/src/components/common-business/status-tag/mini-status-tag.vue new file mode 100644 index 0000000000000000000000000000000000000000..b4721a3db8723d16867ba83af2e055edec4e3340 --- /dev/null +++ b/src/components/common-business/status-tag/mini-status-tag.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/common-business/status-tag/mobile-status-tag.vue b/src/components/common-business/status-tag/mobile-status-tag.vue new file mode 100644 index 0000000000000000000000000000000000000000..43d0ee26fc81bbc06e9a114f97033ad5d261d9a6 --- /dev/null +++ b/src/components/common-business/status-tag/mobile-status-tag.vue @@ -0,0 +1,31 @@ + + + + + diff --git a/src/components/common-business/status-tag/pc-status-tag.vue b/src/components/common-business/status-tag/pc-status-tag.vue new file mode 100644 index 0000000000000000000000000000000000000000..8f1605198b8924bc1e93be00ea7e305dd57562e3 --- /dev/null +++ b/src/components/common-business/status-tag/pc-status-tag.vue @@ -0,0 +1,32 @@ + + + + + diff --git a/src/components/common-business/status-tag/use-status-tag.ts b/src/components/common-business/status-tag/use-status-tag.ts new file mode 100644 index 0000000000000000000000000000000000000000..92bc6c6a01abf3c6baa7f7d445f2ecf6fad9f800 --- /dev/null +++ b/src/components/common-business/status-tag/use-status-tag.ts @@ -0,0 +1,85 @@ +import { translate } from '@/assets/lang'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { useChannelStore } from '@/store/use-channel-store'; +import { LiveStatus } from '@polyv/live-watch-sdk'; +import { computed, unref } from 'vue'; + +export const statusTagProps = () => ({ + liveStatus: PropUtils.enum(), +}); + +/** + * 直播状态标签 hook + */ +export const useStatusTag = (options: { props: VueProps }) => { + const { props } = options; + + const channelStore = useChannelStore(); + + const liveStatus = computed(() => { + return props.liveStatus ?? channelStore.liveStatus; + }); + + /** + * 获取直播状态文本 + * @param status 直播状态 + */ + function getLiveStatusText(status: LiveStatus): string { + const tagTexts: Record = { + [LiveStatus.Live]: translate('liveStatus.live'), + [LiveStatus.Waiting]: translate('liveStatus.waiting'), + [LiveStatus.End]: translate('liveStatus.end'), + [LiveStatus.Stop]: translate('liveStatus.stop'), + [LiveStatus.Playback]: translate('liveStatus.playback'), + [LiveStatus.UnStart]: translate('liveStatus.unStart'), + }; + + return tagTexts[status]; + } + + /** + * 直播状态标签文本 + */ + const statusTagText = computed(() => { + return getLiveStatusText(unref(liveStatus)); + }); + + return { + liveStatus, + statusTagText, + }; +}; + +export const useMiniStatusTagStyle = (options: { props: VueProps }) => { + const { props } = options; + + const channelStore = useChannelStore(); + + const liveStatus = computed(() => { + return props.liveStatus ?? channelStore.liveStatus; + }); + + const tagColors: Record = { + [LiveStatus.Live]: 'linear-gradient(150deg, #F06E6E 0%, #E63A3A 100%)', + [LiveStatus.Playback]: 'linear-gradient(152deg, #5ba3ff, #3082fe)', + [LiveStatus.Waiting]: 'linear-gradient(152deg, #abafc0, #73778c)', + [LiveStatus.End]: 'linear-gradient(152deg, #abafc0, #73778c)', + [LiveStatus.Stop]: 'linear-gradient(152deg, #abafc0, #73778c)', + [LiveStatus.UnStart]: 'linear-gradient(152deg, #abafc0, #73778c)', + }; + + /** + * 直播状态标签样式 + */ + const minStatusTagStyle = computed(() => { + const color = tagColors[unref(liveStatus)]; + + return { + background: color, + }; + }); + + return { + minStatusTagStyle, + }; +}; diff --git a/src/components/common-business/web-share-panel/imgs/icon-share-wechat.png b/src/components/common-business/web-share-panel/imgs/icon-share-wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..2e6a3c53b7e77c03a72910cbec5ba502e4eea565 Binary files /dev/null and b/src/components/common-business/web-share-panel/imgs/icon-share-wechat.png differ diff --git a/src/components/common-business/web-share-panel/web-share-panel.vue b/src/components/common-business/web-share-panel/web-share-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..c2c25b098137c0e3903fefe893d8bf28e177ffc6 --- /dev/null +++ b/src/components/common-business/web-share-panel/web-share-panel.vue @@ -0,0 +1,182 @@ + + + + + + diff --git a/src/components/component-icons/mobile/icons/arrow-down/index.ts b/src/components/component-icons/mobile/icons/arrow-down/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe00365bba2add6700cef843d0ac17afcee5ce2c --- /dev/null +++ b/src/components/component-icons/mobile/icons/arrow-down/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowDown Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-down', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/arrow-left/index.ts b/src/components/component-icons/mobile/icons/arrow-left/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f90e0b768ba5dd534c238ff33c3dd64fced619d4 --- /dev/null +++ b/src/components/component-icons/mobile/icons/arrow-left/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowLeft Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-left', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/arrow-right/index.ts b/src/components/component-icons/mobile/icons/arrow-right/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9528ebce06bc2357cc5331a1847950ea6baab8a7 --- /dev/null +++ b/src/components/component-icons/mobile/icons/arrow-right/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowRight Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-right', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/arrow-up/index.ts b/src/components/component-icons/mobile/icons/arrow-up/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2217e91af9ba28676161be04ffd36677d2690d3 --- /dev/null +++ b/src/components/component-icons/mobile/icons/arrow-up/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowUp Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-up', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/booking/index.ts b/src/components/component-icons/mobile/icons/booking/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c7b154e2f3639d0e1f6bfe3a7c8f24f696671efd --- /dev/null +++ b/src/components/component-icons/mobile/icons/booking/index.ts @@ -0,0 +1,23 @@ +/** + * @file Booking Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'booking', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/check-round-fill/index.ts b/src/components/component-icons/mobile/icons/check-round-fill/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e87b601700a4c7754a212a9662310ea5682fee55 --- /dev/null +++ b/src/components/component-icons/mobile/icons/check-round-fill/index.ts @@ -0,0 +1,22 @@ +/** + * @file CheckRoundFill Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'check-round-fill', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/check/index.ts b/src/components/component-icons/mobile/icons/check/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3220467be73f85d008953f59a5c7cf1f996f390 --- /dev/null +++ b/src/components/component-icons/mobile/icons/check/index.ts @@ -0,0 +1,22 @@ +/** + * @file Check Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'check', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/close-round/index.ts b/src/components/component-icons/mobile/icons/close-round/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..77aecca95e47b4069f31739cf0a1cf996f1338d2 --- /dev/null +++ b/src/components/component-icons/mobile/icons/close-round/index.ts @@ -0,0 +1,27 @@ +/** + * @file CloseRound Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'close-round', + (data) => ` + + + + +` +); diff --git a/src/components/component-icons/mobile/icons/close/index.ts b/src/components/component-icons/mobile/icons/close/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5816d4c087c17e5cdc62d4b99b06cd33a86d60a3 --- /dev/null +++ b/src/components/component-icons/mobile/icons/close/index.ts @@ -0,0 +1,22 @@ +/** + * @file Close Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'close', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/danmu-switch/index.ts b/src/components/component-icons/mobile/icons/danmu-switch/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a228e295a7d058124749d67bb4cc524ab725d05 --- /dev/null +++ b/src/components/component-icons/mobile/icons/danmu-switch/index.ts @@ -0,0 +1,23 @@ +/** + * @file DanmuSwitch Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'danmu-switch', + (data) => ` + + + + +` +); diff --git a/src/components/component-icons/mobile/icons/delete/index.ts b/src/components/component-icons/mobile/icons/delete/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..087ea0dcbf37779e393bb7a64285443283bab044 --- /dev/null +++ b/src/components/component-icons/mobile/icons/delete/index.ts @@ -0,0 +1,22 @@ +/** + * @file Delete Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'delete', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/emotion/index.ts b/src/components/component-icons/mobile/icons/emotion/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9a9169b925d4899db377e147d3f6b10abe8e119b --- /dev/null +++ b/src/components/component-icons/mobile/icons/emotion/index.ts @@ -0,0 +1,22 @@ +/** + * @file Emotion Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'emotion', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/forbid/index.ts b/src/components/component-icons/mobile/icons/forbid/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ae523b984b2aecd8b7da5926cfea3adc7f59252 --- /dev/null +++ b/src/components/component-icons/mobile/icons/forbid/index.ts @@ -0,0 +1,22 @@ +/** + * @file Forbid Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'forbid', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/heart/index.ts b/src/components/component-icons/mobile/icons/heart/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..bea63f087670b4461408caeebe85dfd0cb7981ac --- /dev/null +++ b/src/components/component-icons/mobile/icons/heart/index.ts @@ -0,0 +1,22 @@ +/** + * @file Heart Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'heart', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/keyboard/index.ts b/src/components/component-icons/mobile/icons/keyboard/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..cd2f86dfaba8ff01ea772c8101e898d20396c6bc --- /dev/null +++ b/src/components/component-icons/mobile/icons/keyboard/index.ts @@ -0,0 +1,22 @@ +/** + * @file Keyboard Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'keyboard', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/like/index.ts b/src/components/component-icons/mobile/icons/like/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe50be7b594dcc11333170af9404e111367c8958 --- /dev/null +++ b/src/components/component-icons/mobile/icons/like/index.ts @@ -0,0 +1,22 @@ +/** + * @file Like Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'like', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/media/index.ts b/src/components/component-icons/mobile/icons/media/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..977c0eba8fce744cb304362f369acb0a3f61fe85 --- /dev/null +++ b/src/components/component-icons/mobile/icons/media/index.ts @@ -0,0 +1,22 @@ +/** + * @file Media Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'media', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/message/index.ts b/src/components/component-icons/mobile/icons/message/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d47217395cb514de64061e48d3b725c2a0c0b7b9 --- /dev/null +++ b/src/components/component-icons/mobile/icons/message/index.ts @@ -0,0 +1,22 @@ +/** + * @file Message Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'message', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/people/index.ts b/src/components/component-icons/mobile/icons/people/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..88fe585e7f43eb7652173f36529a2e4adce4b2cc --- /dev/null +++ b/src/components/component-icons/mobile/icons/people/index.ts @@ -0,0 +1,22 @@ +/** + * @file People Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'people', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/play-round/index.ts b/src/components/component-icons/mobile/icons/play-round/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c66f1b993b236d81f56485aa94d4393432caa5f0 --- /dev/null +++ b/src/components/component-icons/mobile/icons/play-round/index.ts @@ -0,0 +1,22 @@ +/** + * @file PlayRound Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'play-round', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/product/index.ts b/src/components/component-icons/mobile/icons/product/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f45c9e61af53d807b7207209fb6868767de1478 --- /dev/null +++ b/src/components/component-icons/mobile/icons/product/index.ts @@ -0,0 +1,22 @@ +/** + * @file Product Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'product', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/smiling-face/index.ts b/src/components/component-icons/mobile/icons/smiling-face/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..550542558ba26ccc5a07273055a84e546786b7b0 --- /dev/null +++ b/src/components/component-icons/mobile/icons/smiling-face/index.ts @@ -0,0 +1,22 @@ +/** + * @file SmilingFace Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'smiling-face', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/translate/index.ts b/src/components/component-icons/mobile/icons/translate/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..17767596edc19e6231de45661c2cdc3ae2362a50 --- /dev/null +++ b/src/components/component-icons/mobile/icons/translate/index.ts @@ -0,0 +1,22 @@ +/** + * @file Translate Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'translate', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/video-call/index.ts b/src/components/component-icons/mobile/icons/video-call/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..066397adc618e7d1a27c4dc9677cffdfb40de283 --- /dev/null +++ b/src/components/component-icons/mobile/icons/video-call/index.ts @@ -0,0 +1,22 @@ +/** + * @file VideoCall Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'video-call', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/icons/voice-call/index.ts b/src/components/component-icons/mobile/icons/voice-call/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b40bd63e3c946e45a38d188b429c92d0e550c380 --- /dev/null +++ b/src/components/component-icons/mobile/icons/voice-call/index.ts @@ -0,0 +1,22 @@ +/** + * @file VoiceCall Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'voice-call', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/mobile/map.ts b/src/components/component-icons/mobile/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..5686c42403f09a14f6a7ceb66f0335057422bd58 --- /dev/null +++ b/src/components/component-icons/mobile/map.ts @@ -0,0 +1,30 @@ +/** + * @file All Icon Exporter + * @author Auto Generated by @polyv/icons-cli + */ + +export { default as MobileIconArrowDown } from './icons/arrow-down'; +export { default as MobileIconArrowLeft } from './icons/arrow-left'; +export { default as MobileIconArrowRight } from './icons/arrow-right'; +export { default as MobileIconArrowUp } from './icons/arrow-up'; +export { default as MobileIconBooking } from './icons/booking'; +export { default as MobileIconCheck } from './icons/check'; +export { default as MobileIconCheckRoundFill } from './icons/check-round-fill'; +export { default as MobileIconClose } from './icons/close'; +export { default as MobileIconCloseRound } from './icons/close-round'; +export { default as MobileIconDanmuSwitch } from './icons/danmu-switch'; +export { default as MobileIconDelete } from './icons/delete'; +export { default as MobileIconEmotion } from './icons/emotion'; +export { default as MobileIconForbid } from './icons/forbid'; +export { default as MobileIconHeart } from './icons/heart'; +export { default as MobileIconKeyboard } from './icons/keyboard'; +export { default as MobileIconLike } from './icons/like'; +export { default as MobileIconMedia } from './icons/media'; +export { default as MobileIconMessage } from './icons/message'; +export { default as MobileIconPeople } from './icons/people'; +export { default as MobileIconPlayRound } from './icons/play-round'; +export { default as MobileIconProduct } from './icons/product'; +export { default as MobileIconSmilingFace } from './icons/smiling-face'; +export { default as MobileIconTranslate } from './icons/translate'; +export { default as MobileIconVideoCall } from './icons/video-call'; +export { default as MobileIconVoiceCall } from './icons/voice-call'; diff --git a/src/components/component-icons/pc/icons/apply-video-call/index.ts b/src/components/component-icons/pc/icons/apply-video-call/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b8cf5928e34fef61d3ddf787c6e0b90ee9c1c85d --- /dev/null +++ b/src/components/component-icons/pc/icons/apply-video-call/index.ts @@ -0,0 +1,27 @@ +/** + * @file ApplyVideoCall Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'apply-video-call', + (data) => ` + + + + + + + +` +); diff --git a/src/components/component-icons/pc/icons/arrow-down/index.ts b/src/components/component-icons/pc/icons/arrow-down/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fe00365bba2add6700cef843d0ac17afcee5ce2c --- /dev/null +++ b/src/components/component-icons/pc/icons/arrow-down/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowDown Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-down', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/arrow-left/index.ts b/src/components/component-icons/pc/icons/arrow-left/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..f90e0b768ba5dd534c238ff33c3dd64fced619d4 --- /dev/null +++ b/src/components/component-icons/pc/icons/arrow-left/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowLeft Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-left', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/arrow-right/index.ts b/src/components/component-icons/pc/icons/arrow-right/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9528ebce06bc2357cc5331a1847950ea6baab8a7 --- /dev/null +++ b/src/components/component-icons/pc/icons/arrow-right/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowRight Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-right', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/arrow-up/index.ts b/src/components/component-icons/pc/icons/arrow-up/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2217e91af9ba28676161be04ffd36677d2690d3 --- /dev/null +++ b/src/components/component-icons/pc/icons/arrow-up/index.ts @@ -0,0 +1,22 @@ +/** + * @file ArrowUp Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'arrow-up', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/booking/index.ts b/src/components/component-icons/pc/icons/booking/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c7b154e2f3639d0e1f6bfe3a7c8f24f696671efd --- /dev/null +++ b/src/components/component-icons/pc/icons/booking/index.ts @@ -0,0 +1,23 @@ +/** + * @file Booking Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'booking', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/bulletin/index.ts b/src/components/component-icons/pc/icons/bulletin/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..80aba63c9495b010a8a0cf9ff8545201d5381262 --- /dev/null +++ b/src/components/component-icons/pc/icons/bulletin/index.ts @@ -0,0 +1,22 @@ +/** + * @file Bulletin Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'bulletin', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/caret-down/index.ts b/src/components/component-icons/pc/icons/caret-down/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..58962db405272b23c85f61aec4c2ec44d5e8cfe6 --- /dev/null +++ b/src/components/component-icons/pc/icons/caret-down/index.ts @@ -0,0 +1,22 @@ +/** + * @file CaretDown Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'caret-down', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/caret-left/index.ts b/src/components/component-icons/pc/icons/caret-left/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b50c8f6947c3cbc35beaab03a5b75553506f5ad --- /dev/null +++ b/src/components/component-icons/pc/icons/caret-left/index.ts @@ -0,0 +1,22 @@ +/** + * @file CaretLeft Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'caret-left', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/caret-right/index.ts b/src/components/component-icons/pc/icons/caret-right/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ae81b889bf1e48c9f027f4fb31c89545685e66d3 --- /dev/null +++ b/src/components/component-icons/pc/icons/caret-right/index.ts @@ -0,0 +1,22 @@ +/** + * @file CaretRight Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'caret-right', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/caret-up/index.ts b/src/components/component-icons/pc/icons/caret-up/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..7c135d63bcfda34731abb1cfc6d1891e40ed8c68 --- /dev/null +++ b/src/components/component-icons/pc/icons/caret-up/index.ts @@ -0,0 +1,22 @@ +/** + * @file CaretUp Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'caret-up', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/check-round-fill/index.ts b/src/components/component-icons/pc/icons/check-round-fill/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..e87b601700a4c7754a212a9662310ea5682fee55 --- /dev/null +++ b/src/components/component-icons/pc/icons/check-round-fill/index.ts @@ -0,0 +1,22 @@ +/** + * @file CheckRoundFill Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'check-round-fill', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/check/index.ts b/src/components/component-icons/pc/icons/check/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3220467be73f85d008953f59a5c7cf1f996f390 --- /dev/null +++ b/src/components/component-icons/pc/icons/check/index.ts @@ -0,0 +1,22 @@ +/** + * @file Check Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'check', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/close-round/index.ts b/src/components/component-icons/pc/icons/close-round/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..77aecca95e47b4069f31739cf0a1cf996f1338d2 --- /dev/null +++ b/src/components/component-icons/pc/icons/close-round/index.ts @@ -0,0 +1,27 @@ +/** + * @file CloseRound Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'close-round', + (data) => ` + + + + +` +); diff --git a/src/components/component-icons/pc/icons/close/index.ts b/src/components/component-icons/pc/icons/close/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..be2561ad1caea96323b05c7e3d8d0a5b88faa150 --- /dev/null +++ b/src/components/component-icons/pc/icons/close/index.ts @@ -0,0 +1,22 @@ +/** + * @file Close Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'close', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/device-setting/index.ts b/src/components/component-icons/pc/icons/device-setting/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1605e914e4f4cc56174c28d823ac0b636f15f9a6 --- /dev/null +++ b/src/components/component-icons/pc/icons/device-setting/index.ts @@ -0,0 +1,30 @@ +/** + * @file DeviceSetting Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'device-setting', + (data) => ` + + + + + + + + + + + +` +); diff --git a/src/components/component-icons/pc/icons/emotion/index.ts b/src/components/component-icons/pc/icons/emotion/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a2156e28cbf31f70f59aed6b1e23cebcc2cf515 --- /dev/null +++ b/src/components/component-icons/pc/icons/emotion/index.ts @@ -0,0 +1,22 @@ +/** + * @file Emotion Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'emotion', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/feedback/index.ts b/src/components/component-icons/pc/icons/feedback/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e5f1a4ded9a4cd40a5187dc3baf826efd6414ff --- /dev/null +++ b/src/components/component-icons/pc/icons/feedback/index.ts @@ -0,0 +1,22 @@ +/** + * @file Feedback Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'feedback', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/forbid/index.ts b/src/components/component-icons/pc/icons/forbid/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ae523b984b2aecd8b7da5926cfea3adc7f59252 --- /dev/null +++ b/src/components/component-icons/pc/icons/forbid/index.ts @@ -0,0 +1,22 @@ +/** + * @file Forbid Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'forbid', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/hang-up/index.ts b/src/components/component-icons/pc/icons/hang-up/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1c913291d8e8b943ba3157ea384ddcb48a13247d --- /dev/null +++ b/src/components/component-icons/pc/icons/hang-up/index.ts @@ -0,0 +1,27 @@ +/** + * @file HangUp Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'hang-up', + (data) => ` + + + + + + + +` +); diff --git a/src/components/component-icons/pc/icons/lang/index.ts b/src/components/component-icons/pc/icons/lang/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..152c8a5492f9f55ba0f718397dc34273aa2823a1 --- /dev/null +++ b/src/components/component-icons/pc/icons/lang/index.ts @@ -0,0 +1,22 @@ +/** + * @file Lang Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'lang', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/media/index.ts b/src/components/component-icons/pc/icons/media/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c19cb38c80c3a9baf9695733323200c39ac14fd6 --- /dev/null +++ b/src/components/component-icons/pc/icons/media/index.ts @@ -0,0 +1,22 @@ +/** + * @file Media Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'media', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/people/index.ts b/src/components/component-icons/pc/icons/people/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..38353b5638f816d53e3c5a1c65ad6bc77a8145fe --- /dev/null +++ b/src/components/component-icons/pc/icons/people/index.ts @@ -0,0 +1,22 @@ +/** + * @file People Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'people', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/pic/index.ts b/src/components/component-icons/pc/icons/pic/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..1a90839cd3e62061ddb203a3e98258adbec31d2b --- /dev/null +++ b/src/components/component-icons/pc/icons/pic/index.ts @@ -0,0 +1,22 @@ +/** + * @file Pic Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'pic', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/picture/index.ts b/src/components/component-icons/pc/icons/picture/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9790c0f436346af51d60b7164e66fe8f2e8bfee0 --- /dev/null +++ b/src/components/component-icons/pc/icons/picture/index.ts @@ -0,0 +1,22 @@ +/** + * @file Picture Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'picture', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/play-round/index.ts b/src/components/component-icons/pc/icons/play-round/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ea18d460b5435c695e86e96ec1123ea98c15094 --- /dev/null +++ b/src/components/component-icons/pc/icons/play-round/index.ts @@ -0,0 +1,22 @@ +/** + * @file PlayRound Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'play-round', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/set-nick/index.ts b/src/components/component-icons/pc/icons/set-nick/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..6cd5e52435f55071e1b048833935aa935ea3ea12 --- /dev/null +++ b/src/components/component-icons/pc/icons/set-nick/index.ts @@ -0,0 +1,22 @@ +/** + * @file SetNick Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'set-nick', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/share/index.ts b/src/components/component-icons/pc/icons/share/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fcf9415277eb1214df505b1f3dc2973cd1785e4e --- /dev/null +++ b/src/components/component-icons/pc/icons/share/index.ts @@ -0,0 +1,22 @@ +/** + * @file Share Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'share', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/tips/index.ts b/src/components/component-icons/pc/icons/tips/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c9b2f082c92f5bb5a7f95c8af311d14cbfc0b61 --- /dev/null +++ b/src/components/component-icons/pc/icons/tips/index.ts @@ -0,0 +1,22 @@ +/** + * @file Tips Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'tips', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/translate/index.ts b/src/components/component-icons/pc/icons/translate/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..17767596edc19e6231de45661c2cdc3ae2362a50 --- /dev/null +++ b/src/components/component-icons/pc/icons/translate/index.ts @@ -0,0 +1,22 @@ +/** + * @file Translate Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'translate', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/video-call/index.ts b/src/components/component-icons/pc/icons/video-call/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..066397adc618e7d1a27c4dc9677cffdfb40de283 --- /dev/null +++ b/src/components/component-icons/pc/icons/video-call/index.ts @@ -0,0 +1,22 @@ +/** + * @file VideoCall Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'video-call', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/voice-call/index.ts b/src/components/component-icons/pc/icons/voice-call/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..b40bd63e3c946e45a38d188b429c92d0e550c380 --- /dev/null +++ b/src/components/component-icons/pc/icons/voice-call/index.ts @@ -0,0 +1,22 @@ +/** + * @file VoiceCall Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'voice-call', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/icons/warning-round-fill/index.ts b/src/components/component-icons/pc/icons/warning-round-fill/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..105675bb9f931a33e86c8773a3db9dd95be7a48b --- /dev/null +++ b/src/components/component-icons/pc/icons/warning-round-fill/index.ts @@ -0,0 +1,22 @@ +/** + * @file WarningRoundFill Icon + * @author Auto Generated by @polyv/icons-cli + */ + +import { IconBuilder } from '@polyv/icons-vue/icon-builder'; + +export default IconBuilder( + 'warning-round-fill', + (data) => ` + + + +` +); diff --git a/src/components/component-icons/pc/map.ts b/src/components/component-icons/pc/map.ts new file mode 100644 index 0000000000000000000000000000000000000000..b96272336565998921b778a576249207038d119d --- /dev/null +++ b/src/components/component-icons/pc/map.ts @@ -0,0 +1,38 @@ +/** + * @file All Icon Exporter + * @author Auto Generated by @polyv/icons-cli + */ + +export { default as PcIconApplyVideoCall } from './icons/apply-video-call'; +export { default as PcIconArrowDown } from './icons/arrow-down'; +export { default as PcIconArrowLeft } from './icons/arrow-left'; +export { default as PcIconArrowRight } from './icons/arrow-right'; +export { default as PcIconArrowUp } from './icons/arrow-up'; +export { default as PcIconBooking } from './icons/booking'; +export { default as PcIconBulletin } from './icons/bulletin'; +export { default as PcIconCaretDown } from './icons/caret-down'; +export { default as PcIconCaretLeft } from './icons/caret-left'; +export { default as PcIconCaretRight } from './icons/caret-right'; +export { default as PcIconCaretUp } from './icons/caret-up'; +export { default as PcIconCheck } from './icons/check'; +export { default as PcIconCheckRoundFill } from './icons/check-round-fill'; +export { default as PcIconClose } from './icons/close'; +export { default as PcIconCloseRound } from './icons/close-round'; +export { default as PcIconDeviceSetting } from './icons/device-setting'; +export { default as PcIconEmotion } from './icons/emotion'; +export { default as PcIconFeedback } from './icons/feedback'; +export { default as PcIconForbid } from './icons/forbid'; +export { default as PcIconHangUp } from './icons/hang-up'; +export { default as PcIconLang } from './icons/lang'; +export { default as PcIconMedia } from './icons/media'; +export { default as PcIconPeople } from './icons/people'; +export { default as PcIconPic } from './icons/pic'; +export { default as PcIconPicture } from './icons/picture'; +export { default as PcIconPlayRound } from './icons/play-round'; +export { default as PcIconSetNick } from './icons/set-nick'; +export { default as PcIconShare } from './icons/share'; +export { default as PcIconTips } from './icons/tips'; +export { default as PcIconTranslate } from './icons/translate'; +export { default as PcIconVideoCall } from './icons/video-call'; +export { default as PcIconVoiceCall } from './icons/voice-call'; +export { default as PcIconWarningRoundFill } from './icons/warning-round-fill'; diff --git a/src/components/page-splash-common/auth/auth-code/mobile-auth-code.vue b/src/components/page-splash-common/auth/auth-code/mobile-auth-code.vue new file mode 100644 index 0000000000000000000000000000000000000000..c2327fd038ae598ffec4736635bc8f605b02427a --- /dev/null +++ b/src/components/page-splash-common/auth/auth-code/mobile-auth-code.vue @@ -0,0 +1,110 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-code/pc-auth-code.vue b/src/components/page-splash-common/auth/auth-code/pc-auth-code.vue new file mode 100644 index 0000000000000000000000000000000000000000..b597d2990caebebd310e6b67a11c0dfd61d66156 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-code/pc-auth-code.vue @@ -0,0 +1,132 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-code/use-auth-code.ts b/src/components/page-splash-common/auth/auth-code/use-auth-code.ts new file mode 100644 index 0000000000000000000000000000000000000000..02961e6cf2d8a1ff8c453f5850840c4fb4628e15 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-code/use-auth-code.ts @@ -0,0 +1,93 @@ +import { translate } from '@/assets/lang'; +import { isFormItemInstance } from '@/components/common-base/form/hooks/use-form-item'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { AuthSettingItemCode } from '@polyv/live-watch-sdk'; +import { computed, reactive, ref, unref } from 'vue'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { useAuthCommon, useAuthProtocol } from '../hooks/use-auth-common'; + +/** 观看条件:验证码 hook */ +export const useAuthCode = () => { + const { authItem } = useAuthButtonInject(onClickAuthButton); + + /** 弹层是否显示 */ + const dialogVisible = ref(false); + + /** 弹层标题 */ + const dialogTitle = computed(() => authItem.codeAuthTips); + + /** 表单对象 */ + const formData = reactive({ + code: '', + checkProtocol: false, + }); + + const { protocolContent, protocolFormRules } = useAuthProtocol({ + authItem, + formData, + }); + + /** 处理点击授权入口按钮 */ + function onClickAuthButton(): void { + dialogVisible.value = true; + formData.code = ''; + formData.checkProtocol = false; + } + + /** 处理点击取消 */ + function onClickCancel(): void { + dialogVisible.value = false; + } + + /** 二维码图片地址 */ + const qrcodeImg = computed(() => authItem.qcodeImg); + + /** 二维码提示文案 */ + const qrcodeTips = computed(() => authItem.qcodeTips); + + const formRules = computed(() => { + return { + code: { type: 'string', message: translate('auth.error.codeEmpty'), required: true }, + ...unref(protocolFormRules), + }; + }); + + const { failMessage, handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + const formItemRef = ref(); + + /** 提交授权表单 */ + async function submitAuth() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyCodeAuth({ + code: formData.code, + }); + + if (result.success) { + dialogVisible.value = false; + handleAuthVerifySuccess(result); + } else { + handleAuthVerifyFail(result); + + // 设置表单的异常提示 + const formItemInstance = unref(formItemRef); + if (failMessage.value && isFormItemInstance(formItemInstance)) { + formItemInstance.setErrorMessage(failMessage.value); + } + } + } + + return { + dialogVisible, + dialogTitle, + qrcodeImg, + qrcodeTips, + protocolContent, + formData, + formRules, + onClickCancel, + submitAuth, + failMessage, + formItemRef, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-custom/auth-custom.vue b/src/components/page-splash-common/auth/auth-custom/auth-custom.vue new file mode 100644 index 0000000000000000000000000000000000000000..94d0e9447a3fd5a2c923018d7d338e998fe3c0d4 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-custom/auth-custom.vue @@ -0,0 +1,10 @@ + + + + diff --git a/src/components/page-splash-common/auth/auth-custom/use-auth-custom.ts b/src/components/page-splash-common/auth/auth-custom/use-auth-custom.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ff0f723f74b5f2f58d3ce5513695191d28eedeb --- /dev/null +++ b/src/components/page-splash-common/auth/auth-custom/use-auth-custom.ts @@ -0,0 +1,51 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { AuthSettingItemCustom } from '@polyv/live-watch-sdk'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { useAuthCommon } from '../hooks/use-auth-common'; + +/** + * 自定义授权操作 hook + */ +export const useAuthCustomAction = () => { + const { handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + + /** 跳转到自定义授权地址 */ + async function redirectCustomAuthUrl() { + const watchCore = getWatchCore(); + await watchCore.auth.redirectCustomAuthUrl(); + } + + /** 允许验证自定义授权 */ + async function allowToVerifyCustomAuth(): Promise { + const watchCore = getWatchCore(); + return watchCore.auth.allowToVerifyCustomAuth(); + } + + /** 验证自定义授权 */ + async function verifyAuthCustom() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyCustomAuth(); + if (result.success) { + await handleAuthVerifySuccess(result); + } else { + await handleAuthVerifyFail(result); + } + } + + return { + redirectCustomAuthUrl, + allowToVerifyCustomAuth, + verifyAuthCustom, + }; +}; + +export const useAuthCustom = () => { + useAuthButtonInject(onClickAuthButton); + + const { redirectCustomAuthUrl } = useAuthCustomAction(); + + /** 处理点击授权入口按钮 */ + function onClickAuthButton() { + redirectCustomAuthUrl(); + } +}; diff --git a/src/components/page-splash-common/auth/auth-direct/use-auth-direct.ts b/src/components/page-splash-common/auth/auth-direct/use-auth-direct.ts new file mode 100644 index 0000000000000000000000000000000000000000..035cdcd35b0eebfe0c9d422bb1b23490dd359c50 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-direct/use-auth-direct.ts @@ -0,0 +1,62 @@ +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { PageErrorType } from '@/app/layout/page-error/page-error-type'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { useAuthCommon } from '../hooks/use-auth-common'; +import { DirectAuthQueryParams } from '@polyv/live-watch-sdk'; +import { paramGetter } from '@/hooks/core/use-query-params'; + +export const useAuthDirectAction = () => { + const watchAppStore = useWatchAppStore(); + + const { handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + + /** + * 获取独立授权需要校验的参数 + */ + function getDirectAuthQueryParams(): DirectAuthQueryParams { + return { + userid: paramGetter.userid() || '', + ts: paramGetter.ts() || '', + sign: paramGetter.sign() || '', + nickname: paramGetter.nickname() || '', + }; + } + + /** + * 允许验证独立授权 + */ + async function allowToVerifyDirectAuth(): Promise { + const watchCore = getWatchCore(); + return watchCore.auth.allowToVerifyDirectAuth(getDirectAuthQueryParams()); + } + + /** + * 重定向到独立授权失败页 + */ + async function redirectDirectFailUrl(): Promise { + watchAppStore.setPageError({ + type: PageErrorType.DirectAuthFail, + title: translate('auth.error.directFail'), + }); + } + + /** + * 验证独立授权 + */ + async function verifyAuthDirect() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyDirectAuth(getDirectAuthQueryParams()); + if (result.success) { + await handleAuthVerifySuccess(result); + } else { + await handleAuthVerifyFail(result); + } + } + + return { + allowToVerifyDirectAuth, + redirectDirectFailUrl, + verifyAuthDirect, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-external/auth-external.vue b/src/components/page-splash-common/auth/auth-external/auth-external.vue new file mode 100644 index 0000000000000000000000000000000000000000..45922ac4d65db1da7db57068979cc12050508045 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-external/auth-external.vue @@ -0,0 +1,10 @@ + + + + diff --git a/src/components/page-splash-common/auth/auth-external/use-auth-external.ts b/src/components/page-splash-common/auth/auth-external/use-auth-external.ts new file mode 100644 index 0000000000000000000000000000000000000000..504231899c1762d92ec1de3a4e09b3af48e30f96 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-external/use-auth-external.ts @@ -0,0 +1,81 @@ +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { PageErrorType } from '@/app/layout/page-error/page-error-type'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { AuthSettingItemExternal, ExternalAuthQueryParams } from '@polyv/live-watch-sdk'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { useAuthCommon } from '../hooks/use-auth-common'; +import { paramGetter } from '@/hooks/core/use-query-params'; + +/** + * 外部授权操作 hook + */ +export const useAuthExternalAction = () => { + const watchAppStore = useWatchAppStore(); + const { handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + + /** + * 获取外部授权需要校验的参数 + */ + function getExternalAuthQueryParams(): ExternalAuthQueryParams { + return { + userid: paramGetter.userid() || '', + ts: paramGetter.ts() || '', + sign: paramGetter.sign() || '', + }; + } + + /** + * 允许验证外部授权 + */ + async function allowToVerifyExternalAuth(): Promise { + const watchCore = getWatchCore(); + return watchCore.auth.allowToVerifyExternalAuth(getExternalAuthQueryParams()); + } + + /** + * 重定向到外部授权地址 + */ + async function redirectExternalFailUrl() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.redirectExternalFailUrl(); + + // 跳转不成功,没有配置错误地址,显示本地的错误页 + if (!result) { + watchAppStore.setPageError({ + type: PageErrorType.ExternalAuthFail, + title: translate('auth.error.externalFail'), + }); + } + } + + /** + * 验证外部授权 + */ + async function verifyAuthExternal() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyExternalAuth(getExternalAuthQueryParams()); + if (result.success) { + await handleAuthVerifySuccess(result); + } else { + await handleAuthVerifyFail(result); + } + } + + return { + allowToVerifyExternalAuth, + redirectExternalFailUrl, + verifyAuthExternal, + }; +}; + +export const useAuthExternal = () => { + useAuthButtonInject(onClickAuthButton); + + const { redirectExternalFailUrl } = useAuthExternalAction(); + + /** 处理点击授权入口按钮 */ + function onClickAuthButton() { + redirectExternalFailUrl(); + } +}; diff --git a/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-form.ts b/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-form.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7a5c2a279cd40591bf2b68fcd4980d450103f60 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-form.ts @@ -0,0 +1,250 @@ +import { translate } from '@/assets/lang'; +import { validateCnAndEn, validatePhoneNumber } from '@/assets/utils/validate'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { ImageVerifyInputInstance } from '@/components/common-base/form/form-image-verify-input/type'; +import { SelectOptionItem } from '@/components/common-base/form/form-select/types/form-select-types'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { useAuthStore } from '@/store/use-auth-store'; +import { + AuthInfoPropValKey, + AuthInfoPropValType, + AuthInfoType, + AuthSettingItemInfo, +} from '@polyv/live-watch-sdk'; +import { storeToRefs } from 'pinia'; +import { computed, reactive, ref, unref } from 'vue'; +import { useAuthButtonInject } from '../../hooks/use-auth-button'; +import { useAuthCommon, useAuthProtocol } from '../../hooks/use-auth-common'; +import { useAuthInfoSetting } from './use-auth-info-setting'; + +export const authInfoFormEmits = () => ({ + /** 关闭窗口 */ + 'close-dialog': emitFunc(), + /** 点击了已登录 */ + 'click-logined': emitFunc(), +}); + +interface BasicFormData { + /** 手机号 */ + phoneNumber: string; + /** 手机区号 */ + areaCode: string; + /** 图片验证码 */ + imageId: string; + imageCaptcha: string; + /** 短信验证码 */ + smsCode: string; + /** 隐私协议勾选 */ + checkProtocol: boolean; +} + +export interface AuthInfoFormInstance { + /** 重置表单 */ + resetFormData(): void; +} + +export const useAuthInfoForm = (options: { emit: VueEmit }) => { + const { emit } = options; + + const authStore = useAuthStore(); + const { authItem } = useAuthButtonInject(); + const { smsVerifyEnabled, hasMobileField } = useAuthInfoSetting(); + + const { authInfoFields } = storeToRefs(authStore); + + /** 提示信息 */ + const infoDesc = computed(() => authItem.infoDesc); + + const imageVerifyInputRef = ref(); + + /** 格式化下拉选项 */ + function formatSelectOptions(options?: string): SelectOptionItem[] { + if (!options) { + return []; + } + + const strs = options.split(',').filter(str => !!str); + return strs.map(str => { + return { + label: str, + value: str, + }; + }); + } + + /** 表单字段前缀 */ + const fieldPrefix = 'propValue' as const; + + function generateFromData(): AuthInfoPropValType & BasicFormData { + function getInitFormData() { + const data: AuthInfoPropValType = {}; + + unref(authInfoFields).forEach((option, index) => { + data[`${fieldPrefix}${index + 1}`] = ''; + }); + + return data; + } + + return { + ...getInitFormData(), + // 手机号 + phoneNumber: '', + // 手机区号 + areaCode: '+86', + // 图片验证码 id + imageId: '', + // 图片验证码 + imageCaptcha: '', + // 短信验证码 + smsCode: '', + // 隐私协议勾选 + checkProtocol: false, + }; + } + + const formData = reactive(generateFromData()); + + function resetFormData() { + const generatedData = generateFromData(); + for (const i in formData) { + const key = i as keyof AuthInfoPropValType; + formData[key] = generatedData[key]; + } + } + + function onPhoneNumberChange(phoneNumber: string) { + formData.phoneNumber = phoneNumber; + } + + const { protocolContent, protocolFormRules } = useAuthProtocol({ + authItem, + formData, + }); + + const formRules = computed(() => { + const rules: ValidatorRules = { + ...unref(protocolFormRules), + }; + + unref(authInfoFields).forEach((option, index) => { + const type = option.type; + const field = `${fieldPrefix}${index + 1}` as AuthInfoPropValKey; + + switch (type) { + case AuthInfoType.Name: + rules[field] = [ + { type: 'string', message: translate('auth.error.improveInfo'), required: true }, + { + validator: () => { + const name = formData[field]; + + if (validateCnAndEn(name)) { + return []; + } + + return [translate('form.error.onlyCnAnEn')]; + }, + }, + ]; + break; + case AuthInfoType.Text: + case AuthInfoType.Option: + rules[field] = [ + { type: 'string', message: translate('auth.error.improveInfo'), required: true }, + ]; + break; + case AuthInfoType.Number: + rules[field] = [ + { type: 'number', message: translate('auth.error.improveInfo'), required: true }, + ]; + break; + case AuthInfoType.Mobile: + rules[field] = [ + { + type: 'string', + message: translate('form.error.phoneNumberRequired'), + required: true, + }, + { + validator: () => { + const phoneNumber = formData[field]; + const areaCode = formData.areaCode; + + if (validatePhoneNumber(phoneNumber, areaCode)) { + return []; + } + + return [translate('form.error.phoneNumberError')]; + }, + }, + ]; + if (unref(smsVerifyEnabled)) { + rules.smsCode = [ + { + type: 'string', + message: translate('form.error.smsVerifyRequired'), + required: true, + }, + ]; + } + break; + } + }); + + return rules; + }); + + const { failMessage, handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + + async function submitAuth() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyInfoAuth(formData); + + if (result.success) { + handleAuthVerifySuccess(result); + } else { + handleAuthVerifyFail(result); + + if (failMessage.value) { + toast.error(failMessage.value); + } + } + } + + /** 处理点击取消 */ + function onClickCancel(): void { + emit('close-dialog'); + } + + function onClickLogined() { + emit('click-logined'); + } + + const authFormInstance: AuthInfoFormInstance = { + resetFormData, + }; + + return { + infoDesc, + authInfoFields, + imageVerifyInputRef, + AuthInfoType, + fieldPrefix, + formData, + formRules, + + protocolContent, + smsVerifyEnabled, + authFormInstance, + hasMobileField, + + onPhoneNumberChange, + formatSelectOptions, + onClickCancel, + submitAuth, + onClickLogined, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-login.ts b/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-login.ts new file mode 100644 index 0000000000000000000000000000000000000000..026b93ffe95d9f773640edbbe82357db6959447f --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-login.ts @@ -0,0 +1,97 @@ +import { translate } from '@/assets/lang'; +import { validatePhoneNumber } from '@/assets/utils/validate'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { AuthInfoType } from '@polyv/live-watch-sdk'; +import { computed, reactive } from 'vue'; +import { useAuthCommon } from '../../hooks/use-auth-common'; + +export const authInfoLoginEmits = () => ({ + /** 关闭窗口 */ + 'close-dialog': emitFunc(), +}); + +interface AuthInfoLoginFormData { + /** 手机号 */ + phoneNumber: string; + /** 手机区号 */ + areaCode: string; +} + +export const useAuthInfoLogin = (options: { emit: VueEmit }) => { + const { emit } = options; + + function generateFromData(): AuthInfoLoginFormData { + return { + // 手机号 + phoneNumber: '', + // 手机区号 + areaCode: '+86', + }; + } + + const formData = reactive(generateFromData()); + + const formRules = computed(() => { + const rules: ValidatorRules = { + phoneNumber: [ + { + type: 'string', + message: translate('form.error.phoneNumberRequired'), + required: true, + }, + { + validator: () => { + const phoneNumber = formData.phoneNumber; + const areaCode = formData.areaCode; + + if (validatePhoneNumber(phoneNumber, areaCode)) { + return []; + } + + return [translate('form.error.phoneNumberError')]; + }, + }, + ], + }; + + return rules; + }); + + const { failMessage, handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + + async function submitAuthLogin() { + const watchCore = getWatchCore(); + + const result = await watchCore.auth.loginInfoAuth({ + phoneNumber: formData.phoneNumber, + areaCode: formData.areaCode, + }); + + if (result.success) { + handleAuthVerifySuccess(result); + } else { + handleAuthVerifyFail(result); + + if (failMessage.value) { + toast.error(failMessage.value); + } + } + } + + /** 处理点击取消 */ + function onClickCancel(): void { + emit('close-dialog'); + } + + return { + formData, + formRules, + AuthInfoType, + + onClickCancel, + submitAuthLogin, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-setting.ts b/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-setting.ts new file mode 100644 index 0000000000000000000000000000000000000000..d8817f6198ebfe92e13ad8eb2e0fb4376396faea --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/hooks/use-auth-info-setting.ts @@ -0,0 +1,33 @@ +import { ynToBool } from '@polyv/utils/dist/es/boolean'; +import { computed } from 'vue'; +import { AuthInfoType } from '@polyv/live-watch-sdk'; +import { useAuthStore } from '@/store/use-auth-store'; + +export const useAuthInfoSetting = () => { + const authStore = useAuthStore(); + + /** + * 是否需要短信验证 + */ + const smsVerifyEnabled = computed(() => { + let enabled = false; + authStore.authInfoFields.forEach(option => { + if (option.type === AuthInfoType.Mobile) { + enabled = ynToBool(option.sms); + } + }); + return enabled; + }); + + /** + * 是否具有手机选项 + */ + const hasMobileField = computed(() => { + return authStore.authInfoFields.findIndex(option => option.type === AuthInfoType.Mobile) !== -1; + }); + + return { + smsVerifyEnabled, + hasMobileField, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-info/mobile-auth-info-form.vue b/src/components/page-splash-common/auth/auth-info/mobile-auth-info-form.vue new file mode 100644 index 0000000000000000000000000000000000000000..bb98784b002ebe4f52880f7c1137df4a49cbdc52 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/mobile-auth-info-form.vue @@ -0,0 +1,222 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-info/mobile-auth-info-login.vue b/src/components/page-splash-common/auth/auth-info/mobile-auth-info-login.vue new file mode 100644 index 0000000000000000000000000000000000000000..93d2f6cc02ffad506debb209b276c2aed0a84997 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/mobile-auth-info-login.vue @@ -0,0 +1,72 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-info/mobile-auth-info.vue b/src/components/page-splash-common/auth/auth-info/mobile-auth-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..2e66c0f375ce0861f6252a33d60b63f441e08769 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/mobile-auth-info.vue @@ -0,0 +1,39 @@ + + + + diff --git a/src/components/page-splash-common/auth/auth-info/pc-auth-info-form.vue b/src/components/page-splash-common/auth/auth-info/pc-auth-info-form.vue new file mode 100644 index 0000000000000000000000000000000000000000..66d5ddfd99803d1df9bad6b413653ab58f0c819b --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/pc-auth-info-form.vue @@ -0,0 +1,236 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-info/pc-auth-info-login.vue b/src/components/page-splash-common/auth/auth-info/pc-auth-info-login.vue new file mode 100644 index 0000000000000000000000000000000000000000..4ccbacb557727bb8f440e956887cf5843a45a1a2 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/pc-auth-info-login.vue @@ -0,0 +1,83 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-info/pc-auth-info.vue b/src/components/page-splash-common/auth/auth-info/pc-auth-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..8a7fbe533d6a9ca1e8f56f9d2a2c7637ef51bd4f --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/pc-auth-info.vue @@ -0,0 +1,33 @@ + + + + diff --git a/src/components/page-splash-common/auth/auth-info/use-auth-info.ts b/src/components/page-splash-common/auth/auth-info/use-auth-info.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb341c11b8c091ca37c202b580b72bc68d62f070 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-info/use-auth-info.ts @@ -0,0 +1,65 @@ +import { translate } from '@/assets/lang'; +import { useSimpleVisible } from '@/hooks/behaviors/use-simple-visible'; +import { DialogInstance } from '@/components/common-base/dialog/pc-dialog/types'; +import { useAuthStore } from '@/store/use-auth-store'; +import { AuthSettingItemInfo } from '@polyv/live-watch-sdk'; +import { storeToRefs } from 'pinia'; +import { computed, nextTick, ref } from 'vue'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { AuthInfoFormInstance } from './hooks/use-auth-info-form'; + +export type AuthInfoDialogModel = 'form' | 'login'; + +export const useAuthInfo = () => { + const authStore = useAuthStore(); + const { authItem } = useAuthButtonInject(onClickAuthButton); + + const { authInfoFields } = storeToRefs(authStore); + + const dialogRef = ref(); + + const dialogModel = ref('form'); + + const { visible: dialogVisible, close: closeDialog } = useSimpleVisible(false); + + const formRef = ref(); + + function onClickAuthButton(): void { + dialogVisible.value = true; + dialogModel.value = 'form'; + formRef.value?.resetFormData(); + } + + /** 弹层标题 */ + const dialogTitle = computed(() => { + if (dialogModel.value === 'form') { + return authItem.infoAuthTips; + } + return translate('auth.info.registered.title'); + }); + + async function openLogined() { + dialogModel.value = 'login'; + await nextTick(); + dialogRef.value?.resetPosition(); + } + + async function openAuthInfoForm() { + dialogModel.value = 'form'; + await nextTick(); + dialogRef.value?.resetPosition(); + } + + return { + dialogVisible, + dialogModel, + dialogTitle, + dialogRef, + formRef, + + authInfoFields, + closeDialog, + openLogined, + openAuthInfoForm, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-none/auth-none.vue b/src/components/page-splash-common/auth/auth-none/auth-none.vue new file mode 100644 index 0000000000000000000000000000000000000000..cc9d90440bd711b81c99f877e163adb2d5498260 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-none/auth-none.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/page-splash-common/auth/auth-none/use-auth-none.ts b/src/components/page-splash-common/auth/auth-none/use-auth-none.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e389482eaae2c60e12b585faa938a5009c6aba5 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-none/use-auth-none.ts @@ -0,0 +1,36 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useEnrollStore } from '@/store/use-enroll-store'; +import { AuthSettingItemCode } from '@polyv/live-watch-sdk'; +import { onMounted } from 'vue'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { useAuthCommon } from '../hooks/use-auth-common'; + +export const useAuthNone = () => { + const enrollStore = useEnrollStore(); + useAuthButtonInject(onClickAuthButton); + const { handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + + /** 执行无条件授权 */ + async function toDoAuthNone() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyNoneAuth(); + + if (result.success) { + await handleAuthVerifySuccess(result); + } else { + await handleAuthVerifyFail(result); + } + } + + /** 处理点击授权按钮 */ + async function onClickAuthButton(): Promise { + await toDoAuthNone(); + } + + onMounted(() => { + // 如果显示了无条件授权但又需要报名观看,则渲染授权按钮时自动出发授权 + if (enrollStore.needEnrollByEnter) { + toDoAuthNone(); + } + }); +}; diff --git a/src/components/page-splash-common/auth/auth-pay/mobile-auth-pay.vue b/src/components/page-splash-common/auth/auth-pay/mobile-auth-pay.vue new file mode 100644 index 0000000000000000000000000000000000000000..ca629272c3c5e9849283d2eeecc85fb0818bf49f --- /dev/null +++ b/src/components/page-splash-common/auth/auth-pay/mobile-auth-pay.vue @@ -0,0 +1,73 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-pay/pc-auth-pay.vue b/src/components/page-splash-common/auth/auth-pay/pc-auth-pay.vue new file mode 100644 index 0000000000000000000000000000000000000000..7dcae4bacf56d428115bd583c057880d3b29260c --- /dev/null +++ b/src/components/page-splash-common/auth/auth-pay/pc-auth-pay.vue @@ -0,0 +1,185 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-pay/use-auth-pay.ts b/src/components/page-splash-common/auth/auth-pay/use-auth-pay.ts new file mode 100644 index 0000000000000000000000000000000000000000..b655cd68ecbc31c3c8ac4ed2d4b1b1fcccbddace --- /dev/null +++ b/src/components/page-splash-common/auth/auth-pay/use-auth-pay.ts @@ -0,0 +1,242 @@ +import { useWeixinAuthorize } from '@/hooks/platform/use-weixin/use-weixin-authorize'; +import { useWeixinPay } from '@/hooks/platform/use-weixin/use-weixin-pay'; +import { translate } from '@/assets/lang'; +import { isWeixin } from '@/assets/utils/browser'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { AuthSettingItemPay, AuthType } from '@polyv/live-watch-sdk'; +import { computed, onBeforeUnmount, ref, unref, watch } from 'vue'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { useAuthCommon } from '../hooks/use-auth-common'; + +/** + * PC 端付费观看 hook + */ +export const useAuthPayPc = () => { + const weixinStore = useWeixinStore(); + const { redirectWeixinAuthorize } = useWeixinAuthorize(); + const { authItem } = useAuthButtonInject(onClickAuthButton); + const { handleAuthVerifySuccess } = useAuthCommon(); + + /** 弹层是否显示 */ + const dialogVisible = ref(false); + + /** 弹层标题 */ + const dialogTitle = computed(() => authItem.payAuthTips); + + /** 弹层类型 */ + const dialogType = ref<'pay' | 'payCheck'>('pay'); + + /** 支付金额 */ + const payPrice = computed(() => authItem.price); + + /** 打开支付弹层 */ + function openPayDialog() { + const watchCore = getWatchCore(); + watchCore.auth.closeCheckWechatPayStatusPolling(); + dialogType.value = 'pay'; + dialogVisible.value = true; + refreshPayData(); + } + + /** 打开支付检查弹层 */ + function openPayCheckDialog() { + const watchCore = getWatchCore(); + watchCore.auth.closeCheckPayStatusPolling(); + dialogType.value = 'payCheck'; + dialogVisible.value = true; + refreshWechatPayData(); + } + + /** 处理点击授权入口按钮 */ + function onClickAuthButton(): void { + openPayDialog(); + } + + /** 处理点击我已支付 */ + function onClickWechatHasPay() { + openPayCheckDialog(); + } + + /** 支付二维码地址 */ + const payQrcodeUrl = ref(); + + /** 微信扫描二维码地址 */ + const wechatQrcodeUrl = ref(); + + /** + * 处理支付成功 + */ + function onPaySuccess() { + /** + * 针对于 PC 端微信客户端,在付费完后做微信非静默授权 + */ + if (isWeixin && !weixinStore.weixinWatchAuthorized) { + redirectWeixinAuthorize({ + snsApiBase: false, + }); + return; + } + + handleAuthVerifySuccess({ + success: true, + authType: AuthType.Pay, + }); + dialogVisible.value = false; + } + + /** + * 处理支付超时 + */ + function onPayTimeout() { + dialogVisible.value = false; + toast.error(translate('auth.error.payTimeout')); + } + + /** + * 刷新支付数据 + */ + async function refreshPayData() { + const watchCore = getWatchCore(); + const payData = await watchCore.auth.getAuthPayData(); + + payQrcodeUrl.value = payData.qrcodeUrl; + + watchCore.auth.startCheckPayStatusPolling({ + successCallback: () => onPaySuccess(), + timeoutCallback: () => onPayTimeout(), + }); + } + + /** + * 刷新微信扫码数据 + */ + async function refreshWechatPayData() { + const watchCore = getWatchCore(); + const wechatPayData = await watchCore.auth.getWechatPayCheckData(); + wechatQrcodeUrl.value = wechatPayData.qrcodeUrl; + + watchCore.auth.startCheckWechatPayStatusPolling({ + successCallback: () => onPaySuccess(), + timeoutCallback: () => onPayTimeout(), + }); + } + + watch( + () => unref(dialogVisible), + () => { + const watchCore = getWatchCore(); + + if (!unref(dialogVisible)) { + watchCore.auth.closeCheckPayStatusPolling(); + watchCore.auth.closeCheckWechatPayStatusPolling(); + } + }, + ); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.auth.closeCheckPayStatusPolling(); + watchCore.auth.closeCheckWechatPayStatusPolling(); + }); + + return { + dialogVisible, + dialogTitle, + dialogType, + payPrice, + payQrcodeUrl, + wechatQrcodeUrl, + openPayDialog, + openPayCheckDialog, + onClickWechatHasPay, + }; +}; + +/** + * 移动端付费观看 hook + */ +export const useAuthPayMobile = () => { + const { handleAuthVerifySuccess } = useAuthCommon(); + const { chooseWXPay } = useWeixinPay(); + const { redirectWeixinAuthorize } = useWeixinAuthorize(); + const { authItem } = useAuthButtonInject(onClickAuthButton); + const weixinStore = useWeixinStore(); + + /** 弹层是否显示 */ + const dialogVisible = ref(false); + + /** 弹层标题 */ + const dialogTitle = computed(() => authItem.payAuthTips); + + /** 支付金额 */ + const payPrice = computed(() => authItem.price); + + function onClickAuthButton() { + dialogVisible.value = true; + } + + /** + * 处理支付成功 + */ + async function onPaySuccess() { + // 如果未进行非静默授权,则先进行一次非静默授权 + if (!weixinStore.weixinWatchAuthorized) { + redirectWeixinAuthorize({ + snsApiBase: false, + }); + return; + } + + const watchCore = getWatchCore(); + const payed = await watchCore.auth.checkPayStatus(); + if (payed) { + handleAuthVerifySuccess({ + success: true, + authType: AuthType.Pay, + }); + dialogVisible.value = false; + } + } + + const isLoading = ref(false); + + /** 处理点击支付 */ + async function onClickPay() { + // 非微信中打开 + if (!isWeixin) { + toast.error(translate('weixin.error.toWechatPay')); + return; + } + + if (isLoading.value) { + return; + } + + isLoading.value = true; + try { + const watchCore = getWatchCore(); + const payInfo = await watchCore.auth.getAuthPayData(); + isLoading.value = false; + + if (payInfo.wxPaySignData) { + dialogVisible.value = false; + chooseWXPay(payInfo.wxPaySignData, { + successCb: async () => { + onPaySuccess(); + }, + }); + } + } catch (e) { + isLoading.value = false; + } + } + + return { + dialogVisible, + dialogTitle, + payPrice, + onClickPay, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-phone/mobile-auth-phone.vue b/src/components/page-splash-common/auth/auth-phone/mobile-auth-phone.vue new file mode 100644 index 0000000000000000000000000000000000000000..7f4e1d7cd05a1e8c4e13e519acd4fdc5f3cb8e38 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-phone/mobile-auth-phone.vue @@ -0,0 +1,74 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-phone/pc-auth-phone.vue b/src/components/page-splash-common/auth/auth-phone/pc-auth-phone.vue new file mode 100644 index 0000000000000000000000000000000000000000..8bde73a76ef368ed7e4ff18b2c0d17116cd8c139 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-phone/pc-auth-phone.vue @@ -0,0 +1,93 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-phone/use-auth-phone.ts b/src/components/page-splash-common/auth/auth-phone/use-auth-phone.ts new file mode 100644 index 0000000000000000000000000000000000000000..37c9fc9b140838ccb8ead67835c12126b8c43339 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-phone/use-auth-phone.ts @@ -0,0 +1,91 @@ +import { translate } from '@/assets/lang'; +import { isFormItemInstance } from '@/components/common-base/form/hooks/use-form-item'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { AuthSettingItemPhone } from '@polyv/live-watch-sdk'; +import { computed, reactive, ref, unref } from 'vue'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { useAuthCommon, useAuthProtocol } from '../hooks/use-auth-common'; + +/** 观看条件:白名单 hook */ +export const useAuthPhone = () => { + const { authItem } = useAuthButtonInject(onClickAuthButton); + + /** 弹层是否显示 */ + const dialogVisible = ref(false); + + /** 弹层标题 */ + const dialogTitle = computed(() => authItem.authTips); + + /** 表单对象 */ + const formData = reactive({ + phone: '', + checkProtocol: false, + }); + + /** 输入框占位文本 */ + const inputPlaceholder = computed(() => { + return authItem.whiteListInputTips || translate('auth.phone.placeholder'); + }); + + const { protocolContent, protocolFormRules } = useAuthProtocol({ + authItem, + formData, + }); + + /** 处理点击授权入口按钮 */ + function onClickAuthButton(): void { + dialogVisible.value = true; + formData.phone = ''; + formData.checkProtocol = false; + } + + /** 处理点击取消 */ + function onClickCancel(): void { + dialogVisible.value = false; + } + + const formRules = computed(() => { + return { + phone: { type: 'string', message: translate('auth.error.phoneEmpty'), required: true }, + ...unref(protocolFormRules), + }; + }); + + const { failMessage, handleAuthVerifySuccess, handleAuthVerifyFail } = useAuthCommon(); + const formItemRef = ref(); + + /** 提交授权表单 */ + async function submitAuth() { + const watchCore = getWatchCore(); + const result = await watchCore.auth.verifyPhoneAuth({ + phone: formData.phone, + }); + + if (result.success) { + dialogVisible.value = false; + handleAuthVerifySuccess(result); + } else { + handleAuthVerifyFail(result); + + // 设置表单的异常提示 + const formItemInstance = unref(formItemRef); + if (failMessage.value && isFormItemInstance(formItemInstance)) { + formItemInstance.setErrorMessage(failMessage.value); + } + } + } + + return { + dialogVisible, + dialogTitle, + protocolContent, + formData, + inputPlaceholder, + formRules, + onClickCancel, + submitAuth, + failMessage, + formItemRef, + }; +}; diff --git a/src/components/page-splash-common/auth/auth-work-weixin/mobile-auth-work-weixin.vue b/src/components/page-splash-common/auth/auth-work-weixin/mobile-auth-work-weixin.vue new file mode 100644 index 0000000000000000000000000000000000000000..4e5fc56dc3079eb2854b8dd0ea4710e6d4b50218 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-work-weixin/mobile-auth-work-weixin.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/components/page-splash-common/auth/auth-work-weixin/pc-auth-work-weixin.vue b/src/components/page-splash-common/auth/auth-work-weixin/pc-auth-work-weixin.vue new file mode 100644 index 0000000000000000000000000000000000000000..1c1ca62f46e8032fd3d6ba9539e08bbee3eb231c --- /dev/null +++ b/src/components/page-splash-common/auth/auth-work-weixin/pc-auth-work-weixin.vue @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/components/page-splash-common/auth/auth-work-weixin/use-auth-work-weixin.ts b/src/components/page-splash-common/auth/auth-work-weixin/use-auth-work-weixin.ts new file mode 100644 index 0000000000000000000000000000000000000000..0b0a03cc64d1fbf3e3cd57200612c62b1eb8c180 --- /dev/null +++ b/src/components/page-splash-common/auth/auth-work-weixin/use-auth-work-weixin.ts @@ -0,0 +1,112 @@ +import { useWorkWeixinAuthorize } from '@/hooks/platform/use-weixin/use-weixin-authorize'; +import { + loadWorkWeixinLoginSdk, + WwLoginInstance, +} from '@/plugins/external-lib-loaders/load-work-weixin-login'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { AuthSettingItemEnterpriseWeChat, YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { onBeforeMount, onBeforeUnmount, onMounted, ref, unref } from 'vue'; +import { useAuthButtonInject } from '../hooks/use-auth-button'; +import { getWwLoginStyle } from './utils'; + +export const useAuthWorkWeixinPc = () => { + const channelStore = useChannelStore(); + useAuthButtonInject(onClickAuthButton); + + const dialogVisible = ref(false); + + async function onClickAuthButton() { + // 企微已过期 + if (ynToBool(channelStore.channelDetail?.channelConfig.enterpriseWechatExpire, YN.N)) { + toast.error(translate('auth.error.workWeixinExpire')); + return; + } + + dialogVisible.value = true; + initWorkWeixinCode(); + } + + let wwloginInstance: WwLoginInstance | undefined; + + /** + * 初始化企微登录二维码 + */ + async function initWorkWeixinCode() { + destroyWwLogin(); + + const watchCore = getWatchCore(); + const data = await watchCore.weixin.getWorkWeixinAuthorizeCode(); + const WwLogin = await loadWorkWeixinLoginSdk(); + + wwloginInstance = new WwLogin({ + id: 'wx_work_code', + appid: data.corpId, + agentid: data.agentId, + redirect_uri: encodeURIComponent(data.redirectUrl), + state: data.state, + lang: 'zh', + href: getWwLoginStyle(), + }); + } + + /** + * 销毁企微登录实例 + */ + function destroyWwLogin() { + if (wwloginInstance) { + wwloginInstance.destroyed(); + wwloginInstance = undefined; + } + } + + onBeforeMount(() => { + loadWorkWeixinLoginSdk(); + }); + + onBeforeUnmount(() => { + destroyWwLogin(); + }); + + return { + dialogVisible, + }; +}; + +export const useAuthWorkWeixinMobile = () => { + const channelStore = useChannelStore(); + useAuthButtonInject(onClickAuthButton); + const { canWorkWeixinAuthorize, redirectWorkWeixinAuthorize } = useWorkWeixinAuthorize(); + + async function onClickAuthButton() { + // 企微已过期 + if (ynToBool(channelStore.channelDetail?.channelConfig.enterpriseWechatExpire, YN.N)) { + toast.error(translate('auth.error.workWeixinExpire')); + return; + } + + // 当前环境不支持企微授权 + if (!unref(canWorkWeixinAuthorize)) { + toast.error(translate('auth.error.workWeixinNotAllow')); + return; + } + + // 如果存在 cpOpenId,则表示非企业成员 + if (channelStore.channelDetail?.wxInfo?.cpOpenId) { + toast.error(translate('auth.error.workWeixinNotStaff')); + return; + } + + redirectWorkWeixinAuthorize({ + snsApiBase: false, + }); + } + + onMounted(() => { + // 企微环境下直接自动执行授权 + onClickAuthButton(); + }); +}; diff --git a/src/components/page-splash-common/auth/auth-work-weixin/utils.ts b/src/components/page-splash-common/auth/auth-work-weixin/utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..273cf5fffab8025bb102ecfaf1544a8b901a384e --- /dev/null +++ b/src/components/page-splash-common/auth/auth-work-weixin/utils.ts @@ -0,0 +1,15 @@ +/** + * 企微二维码样式转换base64 + */ +import { Base64 } from 'js-base64/base64'; + +export const getWwLoginStyle = () => { + const style = ` + .impowerBox .qrcode {width: 200px;} + .impowerBox .title {display: none;} + .impowerBox .info {width: 200px;} + .status_icon {display: none !important} + .impowerBox .status {text-align: center;} + `; + return `data:text/css;base64,${Base64.encode(style)}`; +}; diff --git a/src/components/page-splash-common/auth/hooks/use-auth-button.ts b/src/components/page-splash-common/auth/hooks/use-auth-button.ts new file mode 100644 index 0000000000000000000000000000000000000000..aff5d2926f6a774189c8fd84899fbf5d8b14f5c2 --- /dev/null +++ b/src/components/page-splash-common/auth/hooks/use-auth-button.ts @@ -0,0 +1,182 @@ +import { translate } from '@/assets/lang'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useEnrollStore } from '@/store/use-enroll-store'; +import { AuthSettingItem, AuthType, LiveStatus } from '@polyv/live-watch-sdk'; +import { + computed, + getCurrentInstance, + inject, + InjectionKey, + onBeforeUnmount, + provide, + unref, +} from 'vue'; +import { AuthButtonInject } from '../types/auth-type'; + +/** + * 观看页条件按钮 props 配置 + */ +export const authButtonProps = () => ({ + authItem: PropUtils.objectType().isRequired, +}); + +const AUTH_BUTTON_INJECT_KEY: InjectionKey = Symbol('authButtonInjectKey'); + +/** + * 观看条件按钮注入 + * @param data 注入数据 + */ +export const useAuthButtonProvide = (data: AuthButtonInject) => { + provide(AUTH_BUTTON_INJECT_KEY, data); +}; + +/** + * 获取观看条件注入信息 + */ +export const useAuthButtonInject = ( + clickButtonCallback?: AnyFunc, +): AuthButtonInject => { + const injectData = inject(AUTH_BUTTON_INJECT_KEY) as AuthButtonInject; + + if (clickButtonCallback) { + injectData.listenClickAuthButton(clickButtonCallback); + } + + onBeforeUnmount(() => { + if (clickButtonCallback) { + injectData.unlistenClickAuthButton(clickButtonCallback); + } + }); + + return injectData; +}; + +/** + * 观看条件按钮 hook + */ +export const useAuthButton = (options: { props: VueProps }) => { + const { props } = options; + const channelStore = useChannelStore(); + const enrollStore = useEnrollStore(); + + const thisInstance = getCurrentInstance()?.proxy; + + useAuthButtonProvide({ + authItem: props.authItem, + listenClickAuthButton, + unlistenClickAuthButton, + }); + + /** 当前的观看条件类型 */ + const currentAuthType = computed(() => { + return props.authItem.authType; + }); + + /** 默认的按钮文本 */ + const defaultButtonText = computed(() => { + const defaultTexts: Record = { + [AuthType.None]: translate('auth.button.none'), + [AuthType.Phone]: translate('auth.button.phone'), + [AuthType.Pay]: translate('auth.button.pay'), + [AuthType.Info]: translate('auth.button.info'), + [AuthType.Code]: translate('auth.button.code'), + [AuthType.Custom]: translate('auth.button.login'), + [AuthType.External]: translate('auth.button.login'), + [AuthType.Direct]: translate('auth.button.login'), + [AuthType.EnterpriseWeChat]: translate('auth.button.workWx'), + }; + + return defaultTexts[unref(currentAuthType)]; + }); + + /** 按钮文本 */ + const buttonText = computed(() => { + const authItem = props.authItem; + + let text = unref(defaultButtonText); + + // 白名单观看入口文本 + if ( + authItem.authType === AuthType.Phone && + authItem.whiteListEntryText && + !['会员观看', '会员入口'].includes(authItem.whiteListEntryText) + ) { + text = authItem.whiteListEntryText; + } + + // 付费观看入口文本 + if ( + authItem.authType === AuthType.Pay && + authItem.payEntryText && + !['付费观看'].includes(authItem.payEntryText) + ) { + text = authItem.payEntryText; + } + + // 验证码观看入口文本 + if ( + authItem.authType === AuthType.Code && + authItem.codeEntryText && + !['验证码观看'].includes(authItem.codeEntryText) + ) { + text = authItem.codeEntryText; + } + + // 登记观看入口文本 + if ( + authItem.authType === AuthType.Info && + authItem.infoEntryText && + !['登记观看'].includes(authItem.infoEntryText) + ) { + text = authItem.infoEntryText; + } + + // 外部授权入口文本 + if (authItem.authType === AuthType.External && authItem.externalEntryText) { + text = authItem.externalEntryText; + } + + // 无条件入口文本 + if (authItem.authType === AuthType.None && channelStore.liveStatus === LiveStatus.Playback) { + text = translate('auth.button.playback'); + } + + return text; + }); + + /** 按钮是否显示 */ + const buttonVisible = computed(() => { + const authItem = props.authItem; + + // 如果仅无条件且需要在入口处进行报名观看,则不显示按钮 + if (authItem.authType === AuthType.None && enrollStore.needEnrollByEnter) { + return false; + } + + return true; + }); + + /** 点击时回调的事件 */ + const clickCallbackEvent = 'click-auth-button'; + /** 处理点击观看条件按钮 */ + function onClickAuthButton(): void { + thisInstance?.$emit(clickCallbackEvent); + } + /** 监听点击按钮事件 */ + function listenClickAuthButton(callback: AnyFunc): void { + thisInstance?.$on(clickCallbackEvent, callback); + } + /** 取消监听事件 */ + function unlistenClickAuthButton(callback: AnyFunc): void { + thisInstance?.$off(clickCallbackEvent, callback); + } + + return { + currentAuthType, + defaultButtonText, + buttonText, + buttonVisible, + onClickAuthButton, + }; +}; diff --git a/src/components/page-splash-common/auth/hooks/use-auth-common.ts b/src/components/page-splash-common/auth/hooks/use-auth-common.ts new file mode 100644 index 0000000000000000000000000000000000000000..58cc563121ac02ed5fb223b8b49aedd2d61b3a30 --- /dev/null +++ b/src/components/page-splash-common/auth/hooks/use-auth-common.ts @@ -0,0 +1,126 @@ +import { translate } from '@/assets/lang'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { + AuthSettingItem, + AuthVerifyError, + AuthVerifyResultFail, + AuthVerifyResultSuccess, + YN, +} from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { computed, ref, unref } from 'vue'; + +export interface AuthProtocolFormData { + /** 是否勾选了隐私声明 */ + checkProtocol: boolean; +} + +export interface UseAuthProtocolOptions { + authItem: AuthSettingItem; + formData: AuthProtocolFormData; +} + +/** + * 观看条件隐私协议 hook + */ +export const useAuthProtocol = (options: UseAuthProtocolOptions) => { + const { authItem, formData } = options; + + /** 隐私协议内容 */ + const protocolContent = computed(() => { + let privacyStatus = YN.N; + let privacyContent = ''; + + if ('privacyStatus' in authItem && authItem.privacyStatus) { + privacyStatus = authItem.privacyStatus; + } + if ('privacyContent' in authItem && authItem.privacyContent) { + privacyContent = authItem.privacyContent; + } + + if (!ynToBool(privacyStatus)) { + return ''; + } + + return privacyContent; + }); + + /** 隐私协议表单验证规则 */ + const protocolFormRules = computed(() => { + return { + checkProtocol: { + validator: () => { + if (unref(protocolContent) && !formData.checkProtocol) { + return [translate('auth.error.checkProtocol')]; + } + return []; + }, + }, + }; + }); + + return { + protocolContent, + protocolFormRules, + }; +}; + +/** + * 观看条件公用 hook + */ +export const useAuthCommon = () => { + const watchAppStore = useWatchAppStore(); + + /** 失败信息 */ + const failMessage = ref(); + + /** + * 处理观看条件验证成功 + * @param successResult 成功结果 + */ + async function handleAuthVerifySuccess(successResult: AuthVerifyResultSuccess): Promise { + if (!successResult.success) return; + + failMessage.value = undefined; + await watchAppStore.resetUpWatchCore(); + } + + /** + * 处理观看条件验证失败 + * @param failResult 失败结果 + */ + async function handleAuthVerifyFail(failResult: AuthVerifyResultFail): Promise { + failMessage.value = undefined; + + switch (failResult.errorReason) { + case AuthVerifyError.Unknown: + failMessage.value = translate('auth.error.unknown'); + break; + case AuthVerifyError.PhoneNotExist: + failMessage.value = translate('auth.error.phoneNotExistent'); + break; + case AuthVerifyError.CodeError: + failMessage.value = translate('auth.error.codeError'); + break; + case AuthVerifyError.SmsCodeError: + failMessage.value = translate('auth.error.smsCodeError'); + break; + case AuthVerifyError.PhoneNotRegister: + failMessage.value = translate('auth.error.phoneNotRegister'); + break; + case AuthVerifyError.RedirectUrl: + if (failResult.getRedirectUrl) { + const redirectUrl = failResult.getRedirectUrl(); + window.location.href = redirectUrl; + } + break; + } + } + + return { + failMessage, + handleAuthVerifySuccess, + handleAuthVerifyFail, + }; +}; diff --git a/src/components/page-splash-common/auth/hooks/use-auth-special.ts b/src/components/page-splash-common/auth/hooks/use-auth-special.ts new file mode 100644 index 0000000000000000000000000000000000000000..a422c2546440ba43dac52ca8ed50121494be2c22 --- /dev/null +++ b/src/components/page-splash-common/auth/hooks/use-auth-special.ts @@ -0,0 +1,101 @@ +import { useAuthStore } from '@/store/use-auth-store'; +import { AuthType, YN } from '@polyv/live-watch-sdk'; +import { useAuthCustomAction } from '../auth-custom/use-auth-custom'; +import { useAuthDirectAction } from '../auth-direct/use-auth-direct'; +import { useAuthExternalAction } from '../auth-external/use-auth-external'; +import { ynToBool } from '@utils-ts/boolean'; +import { useChannelStore } from '@/store/use-channel-store'; + +/** + * 处理在页面进入后的特殊授权 + */ +export const useAuthSpecial = () => { + const authStore = useAuthStore(); + const channelStore = useChannelStore(); + + /** 处理特殊授权 */ + async function handleSpecialAuth() { + // 存在自定义授权 + if (authStore.hasAuth(AuthType.Custom)) { + await doCustomAuth(); + } + + // 存在独立授权 + if (authStore.hasAuth(AuthType.Direct)) { + await doDirectAuth(); + } + + // 存在外部授权 + if (authStore.hasAuth(AuthType.External)) { + await doExternalAuth(); + } + } + + /** 处理自定义授权逻辑 */ + async function doCustomAuth() { + const { allowToVerifyCustomAuth, redirectCustomAuthUrl, verifyAuthCustom } = + useAuthCustomAction(); + + const allowCustomAuth = await allowToVerifyCustomAuth(); + if (allowCustomAuth) { + await verifyAuthCustom(); + } else if ( + !authStore.isAuthorized && + authStore.onlyOneAuth && + !ynToBool(channelStore.channelDetail?.channelConfig.splashEnabled, YN.N) + ) { + /** + * 满足以下条件则自动跳转到自定义授权链接 + * 1. 未授权(没有用户信息) + * 2. 只有自定义授权 + * 3. 关闭了引导页开关 + */ + await redirectCustomAuthUrl(); + } + } + + /** 处理外部授权逻辑 */ + async function doExternalAuth() { + const { allowToVerifyExternalAuth, redirectExternalFailUrl, verifyAuthExternal } = + useAuthExternalAction(); + + const allowExternalAuth = await allowToVerifyExternalAuth(); + if (allowExternalAuth) { + await verifyAuthExternal(); + } else if ( + !authStore.isAuthorized && + authStore.authSettingLength && + (authStore.authSettingLength === 1 || authStore.hasAuth(AuthType.Direct)) + ) { + /** + * 满足以下条件则跳到外部授权失败页 + * 1. 未授权(没有用户信息) + * 2. [没有其他观看条件] 或 [另一个观看条件是独立授权] + */ + await redirectExternalFailUrl(); + } + } + + /** 处理独立授权逻辑 */ + async function doDirectAuth() { + const { allowToVerifyDirectAuth, redirectDirectFailUrl, verifyAuthDirect } = + useAuthDirectAction(); + + const allowDirectAuth = await allowToVerifyDirectAuth(); + if (allowDirectAuth) { + await verifyAuthDirect(); + } else if (!authStore.isAuthorized && authStore.onlyOneAuth) { + /** + * 满足以下条件则显示独立授权失败页 + * 1. 未授权(没有用户信息) + * 2. 只有独立授权 + */ + await redirectDirectFailUrl(); + } + } + + return { + handleSpecialAuth, + doCustomAuth, + }; +}; diff --git a/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat-green.png b/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat-green.png new file mode 100644 index 0000000000000000000000000000000000000000..c2f8c8ed3d40d875b2075d5c92fe73570c247040 Binary files /dev/null and b/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat-green.png differ diff --git a/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat-logo.png b/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..444a457f5ad43686fb4d2ac05848678b2189effe Binary files /dev/null and b/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat-logo.png differ diff --git a/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat.png b/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..7b23453cc3a8378b6209d4b0ad146e3cc6aabfb4 Binary files /dev/null and b/src/components/page-splash-common/auth/imgs/icon-auth-pay-wechat.png differ diff --git a/src/components/page-splash-common/auth/mobile-auth-button.vue b/src/components/page-splash-common/auth/mobile-auth-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..913ed59a790ed62159ca1f2d493172ccf439bc7f --- /dev/null +++ b/src/components/page-splash-common/auth/mobile-auth-button.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/components/page-splash-common/auth/mobile-auth.vue b/src/components/page-splash-common/auth/mobile-auth.vue new file mode 100644 index 0000000000000000000000000000000000000000..10f1838062248c2e8639960565116e355ec164d7 --- /dev/null +++ b/src/components/page-splash-common/auth/mobile-auth.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/components/page-splash-common/auth/pc-auth-button.vue b/src/components/page-splash-common/auth/pc-auth-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..a6299283cfb896baf72dcff65bb3e30b253f94b6 --- /dev/null +++ b/src/components/page-splash-common/auth/pc-auth-button.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/src/components/page-splash-common/auth/pc-auth.vue b/src/components/page-splash-common/auth/pc-auth.vue new file mode 100644 index 0000000000000000000000000000000000000000..6a33ff9fa341a1470b00d689e9e16e4b7f62c8f6 --- /dev/null +++ b/src/components/page-splash-common/auth/pc-auth.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/src/components/page-splash-common/auth/types/auth-type.ts b/src/components/page-splash-common/auth/types/auth-type.ts new file mode 100644 index 0000000000000000000000000000000000000000..14ad675b404d7978f0a1c4b43d0069ca24dde698 --- /dev/null +++ b/src/components/page-splash-common/auth/types/auth-type.ts @@ -0,0 +1,10 @@ +import { AuthSettingItem } from '@polyv/live-watch-sdk'; + +export interface AuthButtonInject { + /** 观看条件数据 */ + authItem: ItemType; + /** 监听点击事件 */ + listenClickAuthButton: (callback: AnyFunc) => void; + /** 取消监听点击事件 */ + unlistenClickAuthButton: (callback: AnyFunc) => void; +} diff --git a/src/components/page-splash-common/player-trial/_hooks/use-player-trial-event.ts b/src/components/page-splash-common/player-trial/_hooks/use-player-trial-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..78f7298c534b4bfa8133497e4d6e44fa8d41cdb0 --- /dev/null +++ b/src/components/page-splash-common/player-trial/_hooks/use-player-trial-event.ts @@ -0,0 +1,117 @@ +/** + * @file 试看播放器事件 hook + */ +import { ref } from 'vue'; +import { ChannelTrialInfo, PlayerEvents } from '@polyv/live-watch-sdk'; +import { local } from '@just4/storage'; + +import { getWatchCore } from '@/core/watch-sdk'; +import { usePlayerStore } from '@/store/use-player-store'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getStorageKey } from '@/assets/utils/storage'; + +/** 获取本地存储的"试看剩余时长" key 值 */ +const mergeStorageKey = (sessionId: string) => { + return getStorageKey(`live-trial-remaining-time-${sessionId}`); +}; + +/** @hook 试看播放器事件 hook */ +export const usePlayerTrialEvent = ({ trialInfo }: { trialInfo: ChannelTrialInfo | null }) => { + const playerStore = usePlayerStore(); + + /** 试看剩余时长,秒级别 */ + const trialRemainingTime = ref(0); + + /** 试看时长定时器 */ + const trialWatchTimer = ref(null); + + /** 设置剩余试看时长到本地缓存中 */ + function setLocalTrialRemainingTime(time: number) { + if (!trialInfo) return; + if (time < 0) return; + + local.set(mergeStorageKey(trialInfo.channelSessionId), String(time)); + } + + /** 清除试看时长轮询定时器 */ + function clearTrialWatchTimer() { + if (trialWatchTimer.value !== null) { + clearInterval(trialWatchTimer.value); + } + trialWatchTimer.value = null; + } + + /** 通过定时器轮询,更新本地缓存的试看时长 */ + function setTrialWatchTimer() { + trialWatchTimer.value = window.setInterval(() => { + if (trialRemainingTime.value <= 0) { + clearTrialWatchTimer(); + eventBus.$emit(appEvents.player.TrialPlayEnd); + return; + } + trialRemainingTime.value -= 1; + setLocalTrialRemainingTime(trialRemainingTime.value); + }, 1000); + } + + /** 初始化剩余试看时长 */ + function initTrialRemainingTime() { + if (!trialInfo) { + console.warn('需要频道试看数据'); + return; + } + + /** 后端计算的试看剩余时长 */ + const serverTrialRemainingTime = trialInfo.availableTrial; + /** 频道配置的试看总时长,以秒为单位 */ + const serverTrialConfigTime = trialInfo.trialWatchTime * 60; + + const cachedTime = local.get(mergeStorageKey(trialInfo.channelSessionId)); + /** 本地缓存剩余时长 */ + const localTrialRemainingTime = + cachedTime !== null ? parseInt(cachedTime) : serverTrialConfigTime; + + if (localTrialRemainingTime === 0) { + // 无本地缓存 || 剩余时长确实为 0 的情况 + clearTrialWatchTimer(); + eventBus.$emit(appEvents.player.TrialPlayEnd); + return; + } else { + eventBus.$emit(appEvents.player.TrialPlayStart, { + trialRemainingTime: localTrialRemainingTime, + }); + } + + // 设置剩余时长 + trialRemainingTime.value = + localTrialRemainingTime === serverTrialConfigTime + ? serverTrialConfigTime + : Math.min(localTrialRemainingTime, serverTrialRemainingTime); // 本地缓存剩余时长和服务器记录剩余时长取最小 + setLocalTrialRemainingTime(trialRemainingTime.value); + + setTrialWatchTimer(); + } + + /** 播放器初始化钩子 */ + function onPlayerInited() { + playerStore.$patch({ playerInited: true }); + initTrialRemainingTime(); + } + + function bindPlayerEvents() { + const watchCore = getWatchCore(); + watchCore.player.eventEmitter.on(PlayerEvents.PlayerInited, onPlayerInited); + } + + function unbindPlayerEvents() { + const watchCore = getWatchCore(); + watchCore.player.eventEmitter.off(PlayerEvents.PlayerInited, onPlayerInited); + + clearTrialWatchTimer(); + } + + return { + bindPlayerEvents, + unbindPlayerEvents, + }; +}; diff --git a/src/components/page-splash-common/player-trial/_hooks/use-player-trial-main.ts b/src/components/page-splash-common/player-trial/_hooks/use-player-trial-main.ts new file mode 100644 index 0000000000000000000000000000000000000000..f2f59d473d12f29b44cf2da5d6bd226b571c3140 --- /dev/null +++ b/src/components/page-splash-common/player-trial/_hooks/use-player-trial-main.ts @@ -0,0 +1,78 @@ +/** + * @file 试看播放器 hook + */ +import { onBeforeUnmount, onMounted, ref, unref } from 'vue'; +import { ChannelTrialInfo, SetupPlayerOptions } from '@polyv/live-watch-sdk'; + +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { usePlayerStore } from '@/store/use-player-store'; + +import { usePlayerTrialEvent } from './use-player-trial-event'; + +export type InitPlayerOptions = Omit; + +/** @hook 试看播放器 Hook */ +export const usePlayerTrialMain = ( + hookOptions: { + initPlayerOptions?: InitPlayerOptions; + trialInfo?: ChannelTrialInfo | null; + } = {}, +) => { + const { initPlayerOptions = {}, trialInfo = null } = hookOptions; + + const { bindPlayerEvents, unbindPlayerEvents } = usePlayerTrialEvent({ trialInfo }); + + const channelStore = useChannelStore(); + const playerStore = usePlayerStore(); + + /** 播放器渲染容器 */ + const playerContainer = ref(); + + /** 初始化播放器 */ + async function initPlayer(): Promise { + const container = unref(playerContainer); + if (!container || !trialInfo) { + return; + } + + playerStore.$patch({ + playerInited: false, + isPlayStarted: false, + }); + + const playerOptions: SetupPlayerOptions = { + container, + barrageEnabled: true, + marqueeName: trialInfo.viewerInfo.marqueeName || undefined, + vrHead: channelStore.channelDetail?.domain.assetsUrlPrefix, + x5FullPage: true, + sessionId: trialInfo.channelSessionId, + ...initPlayerOptions, + }; + const watchCore = getWatchCore(); + await watchCore.player.setupPlayer(playerOptions); + + bindPlayerEvents(); + } + + /** 销毁播放器 */ + function destroyPlayer(): void { + const watchCore = getWatchCore(); + watchCore.player.destroyPlayer(); + + unbindPlayerEvents(); + } + + onMounted(() => { + initPlayer(); + }); + + onBeforeUnmount(() => { + destroyPlayer(); + }); + + return { + playerContainer, + }; +}; diff --git a/src/components/page-splash-common/player-trial/mobile-player-trial/mobile-player-trial.vue b/src/components/page-splash-common/player-trial/mobile-player-trial/mobile-player-trial.vue new file mode 100644 index 0000000000000000000000000000000000000000..c1f6cbec35918268ad1d73d630bdc81da5d45b3f --- /dev/null +++ b/src/components/page-splash-common/player-trial/mobile-player-trial/mobile-player-trial.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/src/components/page-splash-common/player-trial/use-player-trial.ts b/src/components/page-splash-common/player-trial/use-player-trial.ts new file mode 100644 index 0000000000000000000000000000000000000000..33c54e3fe3aff33e94e67ffb10e8b0dc0eb8f9c6 --- /dev/null +++ b/src/components/page-splash-common/player-trial/use-player-trial.ts @@ -0,0 +1,93 @@ +import { computed, onMounted, ref } from 'vue'; +import { AuthType, LiveStatus, YN, ChannelTrialInfo } from '@polyv/live-watch-sdk'; +import { isWeixin } from '@/assets/utils/browser'; + +import { useAuthStore } from '@/store/use-auth-store'; +import { useChannelStore } from '@/store/use-channel-store'; +import { ynToBool } from '@utils-ts/boolean'; + +import { getWatchCore } from '@/core/watch-sdk'; +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { toast } from '@/hooks/components/use-toast'; + +/** + * @hook 试看-引导页业务逻辑 + * */ +export const usePlayerTrialHook = () => { + const channelStore = useChannelStore(); + const authStore = useAuthStore(); + + /** 频道试看配置数据 */ + const trialInfo = ref(null); + + /** + * 是否启用"试看"功能 + * 微信环境、直播状态、付费观看条件试看开关 + * */ + const trialEnabled = computed(() => { + if (!isWeixin) return false; + if (channelStore.liveStatus !== LiveStatus.Live) return false; + + const payAuthSettingItem = authStore.getAuthSettingItem(AuthType.Pay); + if ( + !payAuthSettingItem || + !ynToBool(payAuthSettingItem.trialWatchEnabled || YN.N) || + !payAuthSettingItem.trialWatchTime + ) { + return false; + } + + return true; + }); + + /** 是否展示试看播放器 */ + const trialPlayerVisible = computed(() => { + return trialEnabled.value && trialInfo.value && trialInfo.value.availableTrial > 0; + }); + + /** 试看结束提示是否显示 */ + const trialPlayFinishTipVisible = computed(() => { + return trialEnabled.value && trialInfo.value && trialInfo.value.availableTrial === 0; + }); + + /** 初始化试看数据 */ + async function initTrialInfo() { + if (!trialEnabled.value) return; + + const watchCore = getWatchCore(); + const result = await watchCore.trial.getTrialInfo(); + if (result.success) { + trialInfo.value = result; + } else { + toast.error(result.failMessage || '未知错误'); + } + } + + /** 更新试看信息 */ + function updateTrialInfo(data: Partial) { + const originData = trialInfo.value; + if (!originData) return; + + trialInfo.value = { + ...originData, + ...data, + }; + } + + onMounted(async () => { + await initTrialInfo(); + }); + + useEventBusListener(appEvents.player.TrialPlayEnd, () => { + updateTrialInfo({ + availableTrial: 0, + }); + }); + + return { + trialEnabled, + trialInfo, + trialPlayerVisible, + trialPlayFinishTipVisible, + }; +}; diff --git a/src/components/page-watch-common/ask/ask-msg-list/mobile-ask-msg-list.vue b/src/components/page-watch-common/ask/ask-msg-list/mobile-ask-msg-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..3f0e66b2dfc342037c70c52de4ffe85c8657b843 --- /dev/null +++ b/src/components/page-watch-common/ask/ask-msg-list/mobile-ask-msg-list.vue @@ -0,0 +1,37 @@ + + + + + + diff --git a/src/components/page-watch-common/ask/ask-msg-list/pc-ask-msg-list.vue b/src/components/page-watch-common/ask/ask-msg-list/pc-ask-msg-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..a8d2eb07c6bb8bf2a909fd84ab338a55835b08e5 --- /dev/null +++ b/src/components/page-watch-common/ask/ask-msg-list/pc-ask-msg-list.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/components/page-watch-common/ask/ask-msg-list/use-ask-msg-list.ts b/src/components/page-watch-common/ask/ask-msg-list/use-ask-msg-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..89dbe3082ac6d21e6ed8379376d5c514621b5805 --- /dev/null +++ b/src/components/page-watch-common/ask/ask-msg-list/use-ask-msg-list.ts @@ -0,0 +1,52 @@ +import { TAB_NAME_ASK } from '@/assets/constants/tab-name'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils } from '@/assets/utils/vue-utils/props-utils'; +import { ScrollListInstance, useScrollList } from '@/hooks/behaviors/use-scroll-list'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { AskMsgType } from '@polyv/live-watch-sdk'; +import { watch } from 'vue'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AskMsgListInstance extends ScrollListInstance {} + +export const askMsgListProps = () => ({ + /** 提问消息列表 */ + askMsgList: PropUtils.array().isRequired, +}); + +export const askMsgListEmits = () => ({ + 'near-top': emitFunc(), +}); + +export const useAskMsgList = (options: { emit: VueEmit }) => { + const { emit } = options; + const layoutStore = useLayoutStore(); + + const { scrollRef, scrollListInstance, onScrollEvent, scrollToBottom } = useScrollList({ + firstElemIndex: 1, + nearTopCallback: () => { + emit('near-top'); + }, + }); + + // 切到提问 Tab 时自动滚到最底部 + watch( + () => [layoutStore.mobileMenuCurrentName, layoutStore.pcAsideTabCurrentName], + () => { + if ( + layoutStore.mobileMenuCurrentName === TAB_NAME_ASK || + layoutStore.pcAsideTabCurrentName === TAB_NAME_ASK + ) { + scrollToBottom(); + } + }, + ); + + const askMsgListInstance: AskMsgListInstance = scrollListInstance; + + return { + scrollRef, + askMsgListInstance, + onScrollEvent, + }; +}; diff --git a/src/components/page-watch-common/ask/hooks/use-ask-send.ts b/src/components/page-watch-common/ask/hooks/use-ask-send.ts new file mode 100644 index 0000000000000000000000000000000000000000..78b9b1c6f9f4f9c0adac03ecf02c3d40bef5b58a --- /dev/null +++ b/src/components/page-watch-common/ask/hooks/use-ask-send.ts @@ -0,0 +1,94 @@ +import { ref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { getImgSize } from '@/assets/utils/image'; +import { changeProtocol } from '@utils-ts/net'; +import { wait } from '@/assets/utils/utils'; + +import { MsgInputWrapInstance } from '@/components/page-watch-common/msg-input-wrap/use-msg-input-wrap'; +import { PlvInputContent } from '@/plugins/polyv-ui/types'; +import { toast } from '@/hooks/components/use-toast'; +import { translate } from '@/assets/lang'; + +export const useAskSend = () => { + /** 消息输入框 */ + const msgInputWrapRef = ref(); + + /** 监听输入框消息发送 */ + async function onSubmitMessage({ contentList }: { contentList: PlvInputContent[] }) { + if (!msgInputWrapRef.value) return; + + msgInputWrapRef.value.clearInput(); + msgInputWrapRef.value.resetStatus(); + + await submitMessageQueue(contentList); + } + + /** 提交消息队列 */ + async function submitMessageQueue(contentList: PlvInputContent[]) { + for await (const message of contentList) { + if (message.type === 'text') { + submitAskMsg(message.content); + } else if (message.type === 'img') { + await sendAskImage(message.file); + } + await wait(200); + } + } + + /** 发送提问的图片消息 */ + async function sendAskImage(file: File | null) { + try { + const watchCore = getWatchCore(); + const { imageId, imageUrl } = await watchCore.utils.directUploadImage(file); + const { width, height } = await getImgSize(imageUrl); + + watchCore.ask.sendAskImage({ + imageId, + imageUrl: changeProtocol(imageUrl, 'https'), + size: { + width, + height, + }, + }); + } catch (error) { + toast.error(translate('watchCore.error.uploadImage')); + } + } + + /** 发送提问的发言消息 - 纯文本 */ + function submitAskMsg(content: string) { + const watchCore = getWatchCore(); + watchCore.ask.sendAskSpeak({ + content, + }); + } + + /** + * 处理点击发送图片 + */ + async function onClickSendImage() { + try { + const watchCore = getWatchCore(); + const { imageId, imageUrl } = await watchCore.utils.uploadImage(); + const { width, height } = await getImgSize(imageUrl); + + watchCore.ask.sendAskImage({ + imageId, + imageUrl: changeProtocol(imageUrl, 'https'), + size: { + width, + height, + }, + }); + } catch (error) { + toast.error(translate('watchCore.error.uploadImage')); + } + } + + return { + msgInputWrapRef, + onSubmitMessage, + onClickSendImage, + }; +}; diff --git a/src/components/page-watch-common/ask/hooks/use-ask.ts b/src/components/page-watch-common/ask/hooks/use-ask.ts new file mode 100644 index 0000000000000000000000000000000000000000..06ca47041b3d67ab18bcb5057430f2fc1e4ca5e4 --- /dev/null +++ b/src/components/page-watch-common/ask/hooks/use-ask.ts @@ -0,0 +1,143 @@ +import { computed, nextTick, onBeforeUnmount, onMounted, Ref, ref, unref } from 'vue'; +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelMenuStore } from '@/store/use-channel-menu-store'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useChatStore } from '@/store/use-chat-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; + +import { + AskEvents, + AskMsgType, + ChannelMenuType, + ChatMsgSource, + ChatMsgSpeakType, + YN, +} from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { uuidV4 } from '@utils-ts/string'; + +import { AskMsgListInstance } from '../ask-msg-list/use-ask-msg-list'; + +export interface UseAskOptions { + askMsgListRef: Ref; +} + +export const useAsk = (options: UseAskOptions) => { + const { askMsgListRef } = options; + + const channelStore = useChannelStore(); + const channelMenuStore = useChannelMenuStore(); + + const { chatInputPlaceholder, chatRoomIsClosed } = storeDefinitionToRefs(useChatStore); + + const historyStart = ref(0); + const historyLength = 20; + const isNoMore = ref(false); + + /** 提问消息列表 */ + const askMsgList = ref([]); + + /** 默认消息的 id */ + const placeholderMsgId = uuidV4(); + + /** 输入框默认值 */ + const askInputPlaceholder = computed(() => { + if (unref(chatRoomIsClosed)) { + return translate('chat.input.placeholder'); + } else { + return unref(chatInputPlaceholder); + } + }); + + /** 提问是否可以发送图片 */ + const questionImageEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.questionImageEnabled, YN.N); + }); + + /** 渲染的消息列表 */ + const askMsgRenderList = computed(() => { + // 提问的菜单数据 + const askMenuData = channelMenuStore.getMenuData(ChannelMenuType.Quiz); + const content = askMenuData?.content || translate('ask.placeholderMsg'); + + // 插入默认消息 + const placeholderMsg: ChatMsgSpeakType = { + id: placeholderMsgId, + time: Date.now(), + msgSource: ChatMsgSource.Speak, + user: channelStore.teacherInfo, + content, + }; + + return [placeholderMsg, ...unref(askMsgList)]; + }); + + /** 获取提问消息列表 */ + async function getAskMsgList() { + if (isNoMore.value) { + return; + } + const watchCore = getWatchCore(); + const start = unref(historyStart); + const end = unref(historyStart) + historyLength - 1; + const result = await watchCore.ask.getAskHistory({ + start, + end, + }); + + historyStart.value = end + 1; + isNoMore.value = result.isEnd; + const list = result.askMsgList; + askMsgList.value = [...list, ...askMsgList.value]; + + const askMsgListInstance = unref(askMsgListRef); + if (start === 0) { + askMsgListInstance?.scrollToBottom(); + } else { + await askMsgListInstance?.scrollToFirstRecord(); + } + + await nextTick(); + askMsgListInstance?.recordFirstElem(); + } + + onMounted(() => { + getAskMsgList(); + }); + + /** 处理提问相关事件 */ + function onAskEvent(evt: { askMsg: AskMsgType }) { + askMsgList.value.push(evt.askMsg); + const askMsgListInstance = unref(askMsgListRef); + askMsgListInstance?.checkScrollToBottom(); + } + + function bindAskEvent() { + const watchCore = getWatchCore(); + watchCore.ask.eventEmitter.on(AskEvents.StudentQuestion, onAskEvent); + watchCore.ask.eventEmitter.on(AskEvents.TeacherAnswer, onAskEvent); + } + + function unbindAskEvent() { + const watchCore = getWatchCore(); + watchCore.ask.eventEmitter.off(AskEvents.StudentQuestion, onAskEvent); + watchCore.ask.eventEmitter.off(AskEvents.TeacherAnswer, onAskEvent); + } + + onMounted(() => { + bindAskEvent(); + }); + onBeforeUnmount(() => { + unbindAskEvent(); + }); + + return { + askInputPlaceholder, + askMsgListRef, + askMsgList, + askMsgRenderList, + getAskMsgList, + questionImageEnabled, + }; +}; diff --git a/src/components/page-watch-common/ask/mobile-ask/mobile-ask.vue b/src/components/page-watch-common/ask/mobile-ask/mobile-ask.vue new file mode 100644 index 0000000000000000000000000000000000000000..eb0d963d68decc83e1cf8f8b27e3633a76ef50f6 --- /dev/null +++ b/src/components/page-watch-common/ask/mobile-ask/mobile-ask.vue @@ -0,0 +1,53 @@ + + + + + + diff --git a/src/components/page-watch-common/ask/pc-ask/pc-ask.vue b/src/components/page-watch-common/ask/pc-ask/pc-ask.vue new file mode 100644 index 0000000000000000000000000000000000000000..d264beb6a5117855de4320cee24f99a206967436 --- /dev/null +++ b/src/components/page-watch-common/ask/pc-ask/pc-ask.vue @@ -0,0 +1,66 @@ + + + + + + diff --git a/src/components/page-watch-common/barrage/pc-barrage.vue b/src/components/page-watch-common/barrage/pc-barrage.vue new file mode 100644 index 0000000000000000000000000000000000000000..3c86dc8e20dc066aa978229db4b83f1680d36ea9 --- /dev/null +++ b/src/components/page-watch-common/barrage/pc-barrage.vue @@ -0,0 +1,31 @@ + + + + + + diff --git a/src/components/page-watch-common/barrage/use-barrage.ts b/src/components/page-watch-common/barrage/use-barrage.ts new file mode 100644 index 0000000000000000000000000000000000000000..4eb392ee15babf42418258774ba9d768c51573d3 --- /dev/null +++ b/src/components/page-watch-common/barrage/use-barrage.ts @@ -0,0 +1,62 @@ +/** + * @file 弹幕 hook + */ +import { getWatchCore } from '@/core/watch-sdk'; +import { usePlayerStore } from '@/store/use-player-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { BarrageTarget } from '@polyv/live-watch-sdk'; +import { isRef, onBeforeUnmount, ref, Ref, unref, watchEffect } from 'vue'; + +export const useBarrage = ( + options: { + containerRef?: HTMLDivElement | Ref; + } = {}, +) => { + const { barrageBackstageEnabled, barrageShow } = storeDefinitionToRefs(usePlayerStore); + const containerRef = isRef(options.containerRef) + ? options.containerRef + : ref(options.containerRef); + let barrageTarget: BarrageTarget | undefined; + + /** 设置弹幕 */ + function setupBarrage(): void { + destroyBarrage(); + + const container = unref(containerRef); + if (!container) { + return; + } + + const watchCore = getWatchCore(); + barrageTarget = watchCore.barrage.setupBarrage({ + container, + }); + } + + /** 销毁弹幕 */ + function destroyBarrage(): void { + if (barrageTarget) { + barrageTarget.destroyTarget(); + barrageTarget = undefined; + } + } + + watchEffect(() => { + if (!unref(barrageBackstageEnabled) || !unref(barrageShow)) { + destroyBarrage(); + return; + } + + setupBarrage(); + }); + + onBeforeUnmount(() => { + destroyBarrage(); + }); + + return { + containerRef, + setupBarrage, + destroyBarrage, + }; +}; diff --git a/src/components/page-watch-common/chapter/hooks/use-chapter.ts b/src/components/page-watch-common/chapter/hooks/use-chapter.ts new file mode 100644 index 0000000000000000000000000000000000000000..06b99ece46c37ed395187be90dcc6c791c04b477 --- /dev/null +++ b/src/components/page-watch-common/chapter/hooks/use-chapter.ts @@ -0,0 +1,67 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { ChapterItem } from '@polyv/live-watch-sdk'; +import { ref, watch, watchEffect } from 'vue'; +import { usePlayerAction } from '../../player/hooks/use-player-action'; + +export const useChapter = () => { + const { toPlay, toSeekVideo } = usePlayerAction(); + const playbackStore = usePlaybackStore(); + const playerStore = usePlayerStore(); + + /** 章节列表 */ + const chapterList = ref([]); + + /** + * 获取章节列表 + */ + async function getChapterList() { + chapterList.value = []; + const playbackTarget = playbackStore.currentPlaybackTarget; + if (!playbackTarget) { + return; + } + + const watchCore = getWatchCore(); + const list = await watchCore.playback.getChapterList(playbackTarget.playbackOptions); + chapterList.value = list; + } + + /** + * 处理点击章节 + */ + function onClickChapter(item: ChapterItem) { + toSeekVideo(item.duration); + toPlay(); + } + + watch( + () => playbackStore.currentPlaybackTarget, + () => { + getChapterList(); + }, + { + immediate: true, + }, + ); + + const currentIndex = ref(0); + + watchEffect(() => { + const currentTime = playerStore.currentTime; + // 时间倒叙寻找能当前能匹配到的最后一个章节 + for (let i = chapterList.value.length - 1; i >= 0; i--) { + if (currentTime >= chapterList.value[i].duration) { + currentIndex.value = i; + break; + } + } + }); + + return { + chapterList, + currentIndex, + onClickChapter, + }; +}; diff --git a/src/components/page-watch-common/chapter/mobile-chapter.vue b/src/components/page-watch-common/chapter/mobile-chapter.vue new file mode 100644 index 0000000000000000000000000000000000000000..97eb16c0198b4c862f49bfaca3486da493fb1e84 --- /dev/null +++ b/src/components/page-watch-common/chapter/mobile-chapter.vue @@ -0,0 +1,73 @@ + + + + + + diff --git a/src/components/page-watch-common/chapter/pc-chapter.vue b/src/components/page-watch-common/chapter/pc-chapter.vue new file mode 100644 index 0000000000000000000000000000000000000000..6d681bf1cb61fb8422062bccc891993017d952cd --- /dev/null +++ b/src/components/page-watch-common/chapter/pc-chapter.vue @@ -0,0 +1,76 @@ + + + + + + diff --git a/src/components/page-watch-common/chapter/portrait-chapter.vue b/src/components/page-watch-common/chapter/portrait-chapter.vue new file mode 100644 index 0000000000000000000000000000000000000000..026fcaa65df47803d779bcf19533fd7be91ba054 --- /dev/null +++ b/src/components/page-watch-common/chapter/portrait-chapter.vue @@ -0,0 +1,73 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-close-tips/chat-close-tips.vue b/src/components/page-watch-common/chat/chat-close-tips/chat-close-tips.vue new file mode 100644 index 0000000000000000000000000000000000000000..9884ed30c764423b99809ec6414b26a8b6025355 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-close-tips/chat-close-tips.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-close-tips/imgs/closed_chat_black.png b/src/components/page-watch-common/chat/chat-close-tips/imgs/closed_chat_black.png new file mode 100644 index 0000000000000000000000000000000000000000..e4f68d49955f47e2278e653b1c0d4688d01145a4 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-close-tips/imgs/closed_chat_black.png differ diff --git a/src/components/page-watch-common/chat/chat-close-tips/imgs/closed_chat_icon.png b/src/components/page-watch-common/chat/chat-close-tips/imgs/closed_chat_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d75fa295341857c368452c52750a0054861524bd Binary files /dev/null and b/src/components/page-watch-common/chat/chat-close-tips/imgs/closed_chat_icon.png differ diff --git a/src/components/page-watch-common/chat/chat-connect-error/mobile-chat-connect-error-dialog.vue b/src/components/page-watch-common/chat/chat-connect-error/mobile-chat-connect-error-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..be9b2aa73b1040258a4029b81af5338cf46331fc --- /dev/null +++ b/src/components/page-watch-common/chat/chat-connect-error/mobile-chat-connect-error-dialog.vue @@ -0,0 +1,86 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-connect-error/pc-chat-connect-error-dialog.vue b/src/components/page-watch-common/chat/chat-connect-error/pc-chat-connect-error-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..d698b9a65fddb7181fbea2c2ce8dcb67319e870b --- /dev/null +++ b/src/components/page-watch-common/chat/chat-connect-error/pc-chat-connect-error-dialog.vue @@ -0,0 +1,55 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-connect-error/use-chat-connect-error.ts b/src/components/page-watch-common/chat/chat-connect-error/use-chat-connect-error.ts new file mode 100644 index 0000000000000000000000000000000000000000..11e2d313e24c5e26549588ddbc939cd8d2b329d9 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-connect-error/use-chat-connect-error.ts @@ -0,0 +1,46 @@ +import { translate } from '@/assets/lang'; +import { useChatStore } from '@/store/use-chat-store'; +import { ref, watchEffect } from 'vue'; + +/** + * 聊天室连接失败 hook + */ +export const useChatConnectError = () => { + const chatStore = useChatStore(); + + /** 连接失败提示是否显示 */ + const connectErrorVisible = ref(false); + + watchEffect(() => { + if (chatStore.connectError) { + connectErrorVisible.value = true; + } else { + connectErrorVisible.value = false; + } + }); + + /** 提示文案 */ + const errorTipsText = translate('chat.connectError'); + const confirmText = translate('base.confirm'); + const cancelText = translate('base.ignore'); + + /** 处理确认点击 */ + function onConfirm(): void { + connectErrorVisible.value = false; + window.location.reload(); + } + + /** 处理取消点击 */ + function onCancel(): void { + connectErrorVisible.value = false; + } + + return { + connectErrorVisible, + errorTipsText, + confirmText, + cancelText, + onConfirm, + onCancel, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-emotional-feedback/imgs/icon-no.png b/src/components/page-watch-common/chat/chat-emotional-feedback/imgs/icon-no.png new file mode 100644 index 0000000000000000000000000000000000000000..821f4f32d10a015059a5382d78355a155b9aa058 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-emotional-feedback/imgs/icon-no.png differ diff --git a/src/components/page-watch-common/chat/chat-emotional-feedback/imgs/icon-yes.png b/src/components/page-watch-common/chat/chat-emotional-feedback/imgs/icon-yes.png new file mode 100644 index 0000000000000000000000000000000000000000..1758975a033778581e64ec1b70f20746652fc9a2 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-emotional-feedback/imgs/icon-yes.png differ diff --git a/src/components/page-watch-common/chat/chat-emotional-feedback/mobile-emotional-feedback.vue b/src/components/page-watch-common/chat/chat-emotional-feedback/mobile-emotional-feedback.vue new file mode 100644 index 0000000000000000000000000000000000000000..f8b6b6fdc2c6d5f6dcfc9bd911baf1ad93eb445c --- /dev/null +++ b/src/components/page-watch-common/chat/chat-emotional-feedback/mobile-emotional-feedback.vue @@ -0,0 +1,76 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-emotional-feedback/pc-emotional-feedback.vue b/src/components/page-watch-common/chat/chat-emotional-feedback/pc-emotional-feedback.vue new file mode 100644 index 0000000000000000000000000000000000000000..ca5c70c7b7d6dc4bd463fdd7545cf3af90cb7e9f --- /dev/null +++ b/src/components/page-watch-common/chat/chat-emotional-feedback/pc-emotional-feedback.vue @@ -0,0 +1,76 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-emotional-feedback/use-emotional-feedback.ts b/src/components/page-watch-common/chat/chat-emotional-feedback/use-emotional-feedback.ts new file mode 100644 index 0000000000000000000000000000000000000000..29bfa93d68aee4db59428caf5d54334ca52d51f0 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-emotional-feedback/use-emotional-feedback.ts @@ -0,0 +1,190 @@ +import { PlvEmotionalFeedbackInstance } from '@/plugins/polyv-ui/types'; +import { onBeforeUnmount, onMounted, ref, unref } from 'vue'; +import { ChatEvents, EmotionalFeedbackMsgType, EmotionalFeedbackType } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; + +export const useEmotionalFeedback = () => { + /** 明白了实例 */ + const yesRef = ref(); + /** 没听懂实例 */ + const noRef = ref(); + + /** 点击次数 */ + let clickCount = 0; + /** 点击定时器 */ + let clickTimer: number | undefined; + + /** 本地记录待发送的数量 */ + let waitYesCount = 0; + /** 本地记录待发送的数量 */ + let waitNoCount = 0; + + /** 发送定时器 */ + let sendTimer: number | undefined; + + /** + * 开始一个动画 + * @param type 类型 + */ + function startAnimation(type: EmotionalFeedbackType) { + let instance: PlvEmotionalFeedbackInstance | undefined; + if (type === EmotionalFeedbackType.Yes) { + instance = unref(yesRef); + } else { + instance = unref(noRef); + } + + if (!instance) return; + + instance.start(type, true); + } + + /** + * 处理点击图标 + * @param type 类型 + */ + function onClickIcon(type: EmotionalFeedbackType) { + startAnimation(type); + + /** 已经超过 5 次,不再处理 */ + if (clickCount >= 5) return; + + /** 30s 内只能点击 5 次 */ + clickCount += 1; + if (clickCount >= 5) { + clickTimer = window.setTimeout(() => { + clickCount = 0; + clearTimeout(clickTimer); + clickTimer = undefined; + }, 30 * 1000); + } + + if (type === EmotionalFeedbackType.Yes) { + waitYesCount += 1; + } else { + waitNoCount += 1; + } + setSendTimer(); + } + + /** + * 发送反馈数量 + */ + function sendEmotionalCount() { + clearSendTimer(); + const watchCore = getWatchCore(); + + if (waitYesCount) { + watchCore.chat.sendEmotionalFeedback(EmotionalFeedbackType.Yes, waitYesCount); + waitYesCount = 0; + } + if (waitNoCount) { + watchCore.chat.sendEmotionalFeedback(EmotionalFeedbackType.No, waitNoCount); + waitNoCount = 0; + } + } + + /** + * 设置发送定时器 + */ + function setSendTimer() { + clearSendTimer(); + sendTimer = window.setTimeout(() => { + sendEmotionalCount(); + }, 3000); + } + + /** + * 清空发送定时器 + */ + function clearSendTimer() { + if (sendTimer) { + clearTimeout(sendTimer); + sendTimer = undefined; + } + } + + /** 动画定时器 */ + let animationTimer: number | undefined; + + let animationYesCount = 0; + let animationNoCount = 0; + + /** 处理情绪事件 */ + function onEmotionFeedbackMsg(evt: { feedbackMsg: EmotionalFeedbackMsgType }) { + const feedbackMsg = evt.feedbackMsg; + if (feedbackMsg.type === EmotionalFeedbackType.Yes) { + animationYesCount += feedbackMsg.count; + } else { + animationNoCount += feedbackMsg.count; + } + checkAnimationTimer(); + } + + onMounted(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatEmotionalFeedback, onEmotionFeedbackMsg); + }); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatEmotionalFeedback, onEmotionFeedbackMsg); + }); + + /** 设置动画定时器 */ + function setAnimationTimer() { + animationTimer = window.setInterval(() => { + if (animationYesCount !== 0) { + animationYesCount -= 1; + startAnimation(EmotionalFeedbackType.Yes); + } + + if (animationNoCount !== 0) { + animationNoCount -= 1; + startAnimation(EmotionalFeedbackType.No); + } + + checkAnimationTimer(); + }, 200); + } + + /** 清空动画定时器 */ + function clearAnimationTimer() { + if (animationTimer) { + clearInterval(animationTimer); + animationTimer = undefined; + } + } + + /** 检查动画定时器 */ + function checkAnimationTimer() { + // 动画数为空,清空定时器 + if (animationYesCount === 0 && animationNoCount === 0) { + clearAnimationTimer(); + return; + } + + if (animationTimer) { + return; + } + + setAnimationTimer(); + } + + onBeforeUnmount(() => { + if (clickTimer) { + clearTimeout(clickTimer); + clickTimer = undefined; + } + clearSendTimer(); + clearAnimationTimer(); + }); + + return { + yesRef, + noRef, + startAnimation, + onClickIcon, + sendEmotionalCount, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-error-tips/pc-chat-error-tips.vue b/src/components/page-watch-common/chat/chat-error-tips/pc-chat-error-tips.vue new file mode 100644 index 0000000000000000000000000000000000000000..e78a5a32469f7ed7db6c93b422e24b8b7c3eeef3 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-error-tips/pc-chat-error-tips.vue @@ -0,0 +1,61 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-error-tips/use-chat-error-tips.ts b/src/components/page-watch-common/chat/chat-error-tips/use-chat-error-tips.ts new file mode 100644 index 0000000000000000000000000000000000000000..c13de38709dccece54e7c339d43900bca79ed68f --- /dev/null +++ b/src/components/page-watch-common/chat/chat-error-tips/use-chat-error-tips.ts @@ -0,0 +1,24 @@ +import { PropUtils } from '@/assets/utils/vue-utils/props-utils'; +import { ref } from 'vue'; + +export const chatErrorTipsProps = () => ({ + /** 提示内容 */ + content: PropUtils.string.def(''), + /** 是否显示关闭按钮 */ + closable: PropUtils.bool.def(true), +}); + +export const useChatErrorTips = () => { + /** 提示显示状态 */ + const showTips = ref(true); + + /** 处理点击关闭 */ + function onClickClose() { + showTips.value = false; + } + + return { + showTips, + onClickClose, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-full-message/mobile-chat-full-message.vue b/src/components/page-watch-common/chat/chat-full-message/mobile-chat-full-message.vue new file mode 100644 index 0000000000000000000000000000000000000000..d24b7055d83b0f4e0acd104b31d9819048209832 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-full-message/mobile-chat-full-message.vue @@ -0,0 +1,116 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-full-message/pc-chat-full-message.vue b/src/components/page-watch-common/chat/chat-full-message/pc-chat-full-message.vue new file mode 100644 index 0000000000000000000000000000000000000000..49864ed481010f0740391783e919c4f6ce0208d6 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-full-message/pc-chat-full-message.vue @@ -0,0 +1,99 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-full-message/portrait-chat-full-message.vue b/src/components/page-watch-common/chat/chat-full-message/portrait-chat-full-message.vue new file mode 100644 index 0000000000000000000000000000000000000000..91d5786573de2532f563ab9b6062d3bb2988befc --- /dev/null +++ b/src/components/page-watch-common/chat/chat-full-message/portrait-chat-full-message.vue @@ -0,0 +1,87 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-full-message/type.ts b/src/components/page-watch-common/chat/chat-full-message/type.ts new file mode 100644 index 0000000000000000000000000000000000000000..a20c4c45c37b1e46b57ee998722d671af10f95ab --- /dev/null +++ b/src/components/page-watch-common/chat/chat-full-message/type.ts @@ -0,0 +1,11 @@ +import { ChatMsgSpeakType } from '@polyv/live-watch-sdk'; + +/** + * 完整消息数据类型 + */ +export interface FullMsgData { + /** 完整的内容 */ + fullContent: string; + /** 消息对象 */ + chatMsg: ChatMsgSpeakType; +} diff --git a/src/components/page-watch-common/chat/chat-full-message/use-chat-full-message.ts b/src/components/page-watch-common/chat/chat-full-message/use-chat-full-message.ts new file mode 100644 index 0000000000000000000000000000000000000000..b5471de938b1292358b60f1684acff7ff81278bf --- /dev/null +++ b/src/components/page-watch-common/chat/chat-full-message/use-chat-full-message.ts @@ -0,0 +1,52 @@ +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { translate } from '@/assets/lang'; +import { copyText } from '@/assets/utils/copy'; +import { useSimpleVisible } from '@/hooks/behaviors/use-simple-visible'; +import { toast } from '@/hooks/components/use-toast'; +import { parseEmotions } from '@polyv/emotion-sdk'; +import { ChatMsgSpeakType } from '@polyv/live-watch-sdk'; +import { ref } from 'vue'; +import { useChatMsgUser } from '../chat-msg-list/msg-items/_hooks/chat-msg-user-hook'; +import { FullMsgData } from './type'; +import { nl2br } from '@utils-ts/string'; + +export const useChatFullMessage = () => { + const { visible, setVisible } = useSimpleVisible(); + + const fullContent = ref(''); + + const chatMsg = ref(); + + const { msgUserAvatar, msgUserActor, msgUserNick, msgUserType } = useChatMsgUser({ + chatMsg, + }); + + useEventBusListener(appEvents.chat.ShowFullMessage, (data: FullMsgData) => { + // 将换行符转成
+ const content = nl2br(data.fullContent); + // 表情标签转换 + fullContent.value = parseEmotions(content); + + chatMsg.value = data.chatMsg; + setVisible(true); + }); + + /** + * 复制内容 + */ + async function copyContent() { + await copyText(fullContent.value); + toast.success(translate('copy.success')); + } + + return { + visible, + fullContent, + chatMsg, + msgUserAvatar, + msgUserNick, + msgUserType, + msgUserActor, + copyContent, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-input-controller/hooks/use-chat-input-controller.ts b/src/components/page-watch-common/chat/chat-input-controller/hooks/use-chat-input-controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..85480fae6c9e168261a0e920857419bae336d201 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/hooks/use-chat-input-controller.ts @@ -0,0 +1,111 @@ +import { ref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; +import { wait } from '@/assets/utils/utils'; + +import { useEventBusListener, appEvents, eventBus } from '@/app/app-events'; +import { useChatStore } from '@/store/use-chat-store'; +import { useSendMsg } from '@/components/page-watch-common/chat/chat-msg-list/hooks/send-msg-hook'; + +import { MsgInputWrapInstance } from '@/components/page-watch-common/msg-input-wrap/use-msg-input-wrap'; +import { PlvInputContent } from '@/plugins/polyv-ui/types'; +import { toast } from '@/hooks/components/use-toast'; +import { translate } from '@/assets/lang'; + +/** + * @hook 聊天输入控制器 + */ +export const useChatInputController = () => { + const chatStore = useChatStore(); + const { + // eslint-disable-next-line prettier/prettier + sendSpeakMsg: sendChatSpeakMsg, + sendImageMsg: sendChatImageMsg, + } = useSendMsg(); + + /** 消息输入框 */ + const msgInputWrapRef = ref(); + + /** 聚焦输入框 */ + function focusMsgInput() { + if (!msgInputWrapRef.value) return; + + msgInputWrapRef.value.focusInput(); + } + + /** 监听输入框消息发送 */ + async function onSubmitMessage({ contentList }: { contentList: PlvInputContent[] }) { + if (!msgInputWrapRef.value) return; + + msgInputWrapRef.value.clearInput(); + msgInputWrapRef.value.resetStatus(); + + await submitMessageQueue(contentList); + } + + /** 提交消息队列 */ + async function submitMessageQueue(contentList: PlvInputContent[]) { + for await (const message of contentList) { + if (message.type === 'text') { + submitTextInput(message.content); + } else if (message.type === 'img') { + await submitImageInput(message.file); + } + await wait(200); + } + } + + /** 提交图片文件 */ + async function submitImageInput(file: File | null) { + try { + const watchCore = getWatchCore(); + + const { imageId, imageUrl } = await watchCore.utils.directUploadImage(file); + sendChatImageMsg({ + imageId, + imageUrl, + }); + } catch (error) { + toast.error(translate('watchCore.error.uploadImage')); + } + } + + /** 提交纯文本 */ + function submitTextInput(content: string) { + sendChatSpeakMsg({ + content, + quoteMsg: chatStore.currentQuoteMsg, + }); + + // 回调事件到报名抽奖,判断是否满足评论抽奖的条件 + eventBus.$emit(appEvents.interaction.CheckCommentLotteryComment, content); + + chatStore.$patch({ + currentQuoteMsg: undefined, + }); + } + + /** 点击发送图片 */ + async function onClickSendImage() { + try { + const watchCore = getWatchCore(); + + const { imageId, imageUrl } = await watchCore.utils.uploadImage(); + sendChatImageMsg({ + imageId, + imageUrl, + }); + } catch (error) { + toast.error(translate('watchCore.error.uploadImage')); + } + } + + useEventBusListener(appEvents.chat.ToSendImage, onClickSendImage); + + return { + msgInputWrapRef, + focusMsgInput, + + onSubmitMessage, + onClickSendImage, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-gift.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-gift.png new file mode 100644 index 0000000000000000000000000000000000000000..522c8fd86f6b2aa82480a7cadf1afb7c44e00624 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-gift.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-animation-hide-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-animation-hide-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..9f0168928df96e57934bcd416fbfdceb040e0379 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-animation-hide-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-animation-show-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-animation-show-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..3fc74f1a64d78a40c4104c6cfb103f881e70820a Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-animation-show-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-announcement-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-announcement-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..4d03b7397df92af6c53200f487c533e2cc025550 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-announcement-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-feed-back.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-feed-back.png new file mode 100644 index 0000000000000000000000000000000000000000..89ceb0bbadeae63bfb1a1c1d739ea28260811d95 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-feed-back.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-img-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-img-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..ee67c4ffb6a8c74679e783a505f2cdc5c2e72651 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-img-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-lang-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-lang-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..0d3bbdd3f54cfe65de4cd16bdaf6186ff145d387 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-lang-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-msg.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-msg.png new file mode 100644 index 0000000000000000000000000000000000000000..921c2f24d5798317a5def7b14bcb9de424f926e9 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-msg.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-only-sp-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-only-sp-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..ee1b699ffb9e487418fd04dcb0e194bf463707de Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-only-sp-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-redenvelope-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-redenvelope-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..021a2c4431c8513eac33c7a5ccd576e9b962d769 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-redenvelope-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-score-redpack.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-score-redpack.png new file mode 100644 index 0000000000000000000000000000000000000000..857389996236cb9a19546419d2f906cd819dc12c Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-score-redpack.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-set-nick.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-set-nick.png new file mode 100644 index 0000000000000000000000000000000000000000..e43a948220b6ef9c6cf34ed80e5390c65c4ec6df Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-set-nick.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-show-all-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-show-all-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..2b849813712338778c51460c9bdfe2632a2fdf1e Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-show-all-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-withdraw-mob.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-withdraw-mob.png new file mode 100644 index 0000000000000000000000000000000000000000..3bf71a81cc6f73e1e0c6408976d12f62a5a595ea Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-menu-withdraw-mob.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-reward.png b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-reward.png new file mode 100644 index 0000000000000000000000000000000000000000..4b56dadb2ee1aea4dca680ad9b04e90c3025c316 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-input-controller/imgs/icon-reward.png differ diff --git a/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-chat-input-controller.vue b/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-chat-input-controller.vue new file mode 100644 index 0000000000000000000000000000000000000000..454894f739230b9f0a7548e8dcaed04d5f853d7b --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-chat-input-controller.vue @@ -0,0 +1,108 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-more-item.vue b/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-more-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..21ddaf52cd6b514cd1f355667a8f85b8474b3658 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-more-item.vue @@ -0,0 +1,116 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-more-panel.vue b/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-more-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..b67aff8145a18a89ad4239f4c30a514625e107d9 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/mobile-chat-input-controller/mobile-more-panel.vue @@ -0,0 +1,216 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-input-controller/pc-chat-input-controller/input-more-panel.vue b/src/components/page-watch-common/chat/chat-input-controller/pc-chat-input-controller/input-more-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..0b9678f67c20b5eebc4269205724564131df24d1 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/pc-chat-input-controller/input-more-panel.vue @@ -0,0 +1,119 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-input-controller/pc-chat-input-controller/pc-chat-input-controller.vue b/src/components/page-watch-common/chat/chat-input-controller/pc-chat-input-controller/pc-chat-input-controller.vue new file mode 100644 index 0000000000000000000000000000000000000000..1299d6cf18d1e01e60b5ef0fb0db867234e72e23 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/pc-chat-input-controller/pc-chat-input-controller.vue @@ -0,0 +1,223 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-input-controller/portrait-chat-input-controller/portrait-chat-input-controller.vue b/src/components/page-watch-common/chat/chat-input-controller/portrait-chat-input-controller/portrait-chat-input-controller.vue new file mode 100644 index 0000000000000000000000000000000000000000..edda9f6524bda6a496a8a68e55271c9742b8947e --- /dev/null +++ b/src/components/page-watch-common/chat/chat-input-controller/portrait-chat-input-controller/portrait-chat-input-controller.vue @@ -0,0 +1,74 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-like/_hooks/use-chat-like.ts b/src/components/page-watch-common/chat/chat-like/_hooks/use-chat-like.ts new file mode 100644 index 0000000000000000000000000000000000000000..64158adc2d34af70971931e9839506828d0883b7 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-like/_hooks/use-chat-like.ts @@ -0,0 +1,109 @@ +/** + * @file 聊天室点赞 hook + */ + +import { PropUtils } from '@/assets/utils/vue-utils/props-utils'; +import { useLikeAnimation } from './use-like-animation'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChatStore } from '@/store/use-chat-store'; +import { onBeforeMount, onBeforeUnmount, onMounted } from 'vue'; +import { ChatEvents, LikeMsgType } from '@polyv/live-watch-sdk'; + +/** + * 点赞气泡图标列表 + */ +export const likeIcons = [ + require('../imgs/icon-like-crown.png'), + require('../imgs/icon-like-diamond.png'), + require('../imgs/icon-like-donuts.png'), + require('../imgs/icon-like-gift.png'), + require('../imgs/icon-like-heart.png'), + require('../imgs/icon-like-rocket.png'), + require('../imgs/icon-like-star.png'), + require('../imgs/icon-like-thumb.png'), + require('../imgs/icon-like-trophy.png'), + require('../imgs/icon-like-watermelon.png'), +]; + +export const chatLikeProps = () => ({ + /** 点赞按钮上方是否有其他按钮 */ + upperHasIcon: PropUtils.bool.def(false), +}); + +export const useChatLike = () => { + const chatStore = useChatStore(); + + /** 上一刻的点击时间 */ + let lastClickTime = Date.now(); + /** 待发送的点赞数 */ + let waitSendLikes = 0; + + /** 处理点击事件 */ + function onClickLike() { + const now = Date.now(); + + if (now - lastClickTime > 200) { + waitSendLikes += 1; + chatStore.realtimeLikes += 1; + lastClickTime = now; + } + } + + /** 将待发送的点赞数发送到服务端 */ + async function sendLikes() { + if (!waitSendLikes) { + return; + } + + const watchCore = getWatchCore(); + try { + watchCore.chat.sendLike(waitSendLikes); + } finally { + waitSendLikes = 0; + } + } + + /** 发送点赞数计时器 */ + let sendLikeTimer: number | undefined; + onMounted(() => { + sendLikeTimer = window.setInterval(() => { + sendLikes(); + }, 5000); + }); + onBeforeUnmount(() => { + window.clearInterval(sendLikeTimer); + sendLikeTimer = undefined; + }); + + const { likeRef, joinLikeAnimationCount } = useLikeAnimation(); + + /** 处理点赞事件 */ + function onChatLikeEvent(evt: { likeMsg: LikeMsgType }) { + const likeMsg = evt.likeMsg; + if (likeMsg.isSelf) { + return; + } + const count = likeMsg.count; + chatStore.$patch({ + realtimeLikes: chatStore.realtimeLikes + count, + }); + + joinLikeAnimationCount(count); + } + + onMounted(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatLike, onChatLikeEvent); + }); + + onBeforeMount(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatLike, onChatLikeEvent); + }); + + return { + likeRef, + likeIcons, + onClickLike, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-like/_hooks/use-like-animation.ts b/src/components/page-watch-common/chat/chat-like/_hooks/use-like-animation.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3ac1e931f31bd71d75241840ab338d3b476720c --- /dev/null +++ b/src/components/page-watch-common/chat/chat-like/_hooks/use-like-animation.ts @@ -0,0 +1,84 @@ +import { LikeAnimationOptions, PlvLikeInstance } from '@/plugins/polyv-ui/types'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { usePageStore } from '@/store/use-page-store'; +import { onBeforeUnmount, ref, unref } from 'vue'; + +/** + * 点赞动画 hook + */ +export const useLikeAnimation = () => { + const pageStore = usePageStore(); + const interactReceiveStore = useInteractReceiveStore(); + + /** 点赞组件实例 */ + const likeRef = ref(); + + /** 点赞动画队列 */ + const likeAnimationQueue: LikeAnimationOptions[] = []; + + /** + * 插入点赞动画 + * @param count 数量 + */ + function joinLikeAnimationCount(count = 1) { + for (let i = 0; i < count; i++) { + if (likeAnimationQueue.length <= 500) { + likeAnimationQueue.push({}); + checkLikeAnimationTimer(); + } else { + break; + } + } + } + + /** 点赞动画定时器 */ + let likeAnimationTimer: number | undefined; + + /** 设置点赞气泡动画 */ + function setLikeAnimationTimer() { + likeAnimationTimer = window.setInterval(() => { + // 页面正在显示、没进行红包雨 + if (pageStore.visibilityState && !interactReceiveStore.isGoOnRedpackRain) { + const popedLike = likeAnimationQueue.pop(); + const instance = unref(likeRef); + if (popedLike && instance && instance.pushAnimation) { + instance.pushAnimation(popedLike); + } + checkLikeAnimationTimer(); + } + }, 200); + } + + /** + * 清楚动画定时器 + */ + function clearLikeAnimationTimer() { + if (likeAnimationTimer) { + clearInterval(likeAnimationTimer); + likeAnimationTimer = undefined; + } + } + + /** 检查点赞动画 */ + function checkLikeAnimationTimer() { + // 动画队列为空,清空定时器 + if (likeAnimationQueue.length === 0) { + clearLikeAnimationTimer(); + return; + } + + // 动画队列不为空且没有定时器,则设置点赞气泡动画 + if (!likeAnimationTimer) { + setLikeAnimationTimer(); + } + } + + onBeforeUnmount(() => { + clearLikeAnimationTimer(); + }); + + return { + likeRef, + joinLikeAnimationCount, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-chat-like-portrait.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-chat-like-portrait.png new file mode 100644 index 0000000000000000000000000000000000000000..326b11436f065dce92c993ead4a34eeace692ae7 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-chat-like-portrait.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-crown.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-crown.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a6ed8c7f642f085415537635b36d598c1271f5 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-crown.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-diamond.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..1c6f18b67ef92ae439e6f46e431f5b11f2bb0898 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-diamond.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-donuts.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-donuts.png new file mode 100644 index 0000000000000000000000000000000000000000..352338549ac5f02fd0f152ea844c6f68a64ed892 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-donuts.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-gift.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-gift.png new file mode 100644 index 0000000000000000000000000000000000000000..56be74a783e1905769c5a31a58003bd74c37af57 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-gift.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-heart.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-heart.png new file mode 100644 index 0000000000000000000000000000000000000000..1b43efe2464a8743ece6098dcb0edc29b83cfd46 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-heart.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-rocket.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-rocket.png new file mode 100644 index 0000000000000000000000000000000000000000..9f683c0b9e4ad78e2edb566f0f2e4f3b26753c31 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-rocket.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-star.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-star.png new file mode 100644 index 0000000000000000000000000000000000000000..df5068448927e31528bf7fdd9bcb97c0cb06b578 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-star.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-thumb.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..ed516d24be8f08a0d35a6212985738135ab36a0c Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-thumb.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-trophy.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-trophy.png new file mode 100644 index 0000000000000000000000000000000000000000..5c89372048769d96c32144aaf04735bddb86d803 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-trophy.png differ diff --git a/src/components/page-watch-common/chat/chat-like/imgs/icon-like-watermelon.png b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-watermelon.png new file mode 100644 index 0000000000000000000000000000000000000000..e9a981d69b15bb7035ebeb7fbe7c00a12b6cf1d3 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-like/imgs/icon-like-watermelon.png differ diff --git a/src/components/page-watch-common/chat/chat-like/mobile-chat-like.vue b/src/components/page-watch-common/chat/chat-like/mobile-chat-like.vue new file mode 100644 index 0000000000000000000000000000000000000000..b175df2b322cc8ddf9c5919f4ec4e20a5a3cb2ad --- /dev/null +++ b/src/components/page-watch-common/chat/chat-like/mobile-chat-like.vue @@ -0,0 +1,88 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-like/pc-chat-like.vue b/src/components/page-watch-common/chat/chat-like/pc-chat-like.vue new file mode 100644 index 0000000000000000000000000000000000000000..b767a0595b815ad34ab55f9a9eefcc2dc1860491 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-like/pc-chat-like.vue @@ -0,0 +1,86 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-like/portrait-chat-like.vue b/src/components/page-watch-common/chat/chat-like/portrait-chat-like.vue new file mode 100644 index 0000000000000000000000000000000000000000..0301e581f24fcb854f974300b1a33db2c04a8c27 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-like/portrait-chat-like.vue @@ -0,0 +1,85 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/chat-msg-handle-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/chat-msg-handle-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..702aad99d7d77006c1910bd991a6206ec20416df --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/chat-msg-handle-hook.ts @@ -0,0 +1,123 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { ChatEvents, ChatMsgImageType, ChatMsgType, SendMsgResult } from '@polyv/live-watch-sdk'; +import { isUndefined } from 'lodash-es'; +import { onBeforeUnmount, onMounted } from 'vue'; +import { useSendMsg } from './send-msg-hook'; +import { ChatMsgListHookOptions } from './types'; + +/** + * 聊天室消息处理 hook + */ +export const useChatMsgHandle = (hookOptions: ChatMsgListHookOptions) => { + const watchCore = getWatchCore(); + const chatMsgStore = useChatMsgStore(); + const { sendSystemMsg } = useSendMsg(); + + /** -------------- 处理聊天室消息更新 -------------- */ + /** + * 处理聊天室消息更新 + * @param msgResult 消息结果 + */ + function onChatMsgResultUpdate(msgResult: SendMsgResult) { + const localId = msgResult.localId; + const chatMsg = msgResult.chatMsg; + + // 替换消息列表 + const historyIndex = chatMsgStore.findHistoryIndex(localId); + if (historyIndex !== -1) { + chatMsgStore.setHistoryData(historyIndex, chatMsg); + } + + // 替换 realIndex + const targetRealIndex = chatMsgStore.getRealIndexById(localId); + if (!isUndefined(targetRealIndex)) { + chatMsgStore.setRealIndexById(chatMsg.id, targetRealIndex); + chatMsgStore.deleteRealIndexById(localId); + } + + // 通知虚拟列表替换消息并重新渲染消息节点,确保消息的 id 为服务端消息 + const virtualList = hookOptions.getVirtualList(); + virtualList.updateItem(chatMsg, localId); + } + + onMounted(() => { + eventBus.$on(appEvents.chat.MsgResultUpdate, onChatMsgResultUpdate); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.chat.MsgResultUpdate, onChatMsgResultUpdate); + }); + + /** -------------- 处理图片违规 -------------- */ + function onImageIllegal(evt: { id: string; chatMsg: ChatMsgImageType }) { + // 替换消息列表 + const historyIndex = chatMsgStore.findHistoryIndex(evt.id); + if (historyIndex !== -1) { + chatMsgStore.setHistoryData(historyIndex, evt.chatMsg); + } + + // 通知虚拟列表替换消息并重新渲染消息节点 + const virtualList = hookOptions.getVirtualList(); + virtualList.updateItem(evt.chatMsg, evt.id); + } + + onMounted(() => { + watchCore.chat.eventEmitter.on(ChatEvents.ImageIllegal, onImageIllegal); + }); + + onBeforeUnmount(() => { + watchCore.chat.eventEmitter.off(ChatEvents.ImageIllegal, onImageIllegal); + }); + + /** -------------- 处理清空聊天室 -------------- */ + function onClearMsgHistory() { + chatMsgStore.resetChatMsgStore(); + const virtualList = hookOptions.getVirtualList(); + virtualList.refresh(); + } + + onMounted(() => { + watchCore.chat.eventEmitter.on(ChatEvents.ClearMsgHistory, onClearMsgHistory); + }); + + onBeforeUnmount(() => { + watchCore.chat.eventEmitter.off(ChatEvents.ClearMsgHistory, onClearMsgHistory); + }); + + /** -------------- 处理删除消息 -------------- */ + function onRemoveChatMsg(evt: { id: string }) { + chatMsgStore.removeHistoryData(evt.id); + const virtualList = hookOptions.getVirtualList(); + virtualList.removeItem(evt.id); + } + + onMounted(() => { + watchCore.chat.eventEmitter.on(ChatEvents.RemoveChatMsg, onRemoveChatMsg); + }); + + onBeforeUnmount(() => { + watchCore.chat.eventEmitter.off(ChatEvents.RemoveChatMsg, onRemoveChatMsg); + }); + + /** -------------- 聊天室开关 -------------- */ + function onOpenChatRoom() { + sendSystemMsg(translate('chat.roomReopened')); + } + + function onCloseChatRoom() { + sendSystemMsg(translate('chat.roomClosed')); + } + + onMounted(() => { + watchCore.chat.eventEmitter.on(ChatEvents.OpenChatRoom, onOpenChatRoom); + watchCore.chat.eventEmitter.on(ChatEvents.CloseChatRoom, onCloseChatRoom); + }); + + onBeforeUnmount(() => { + watchCore.chat.eventEmitter.off(ChatEvents.OpenChatRoom, onOpenChatRoom); + watchCore.chat.eventEmitter.off(ChatEvents.CloseChatRoom, onCloseChatRoom); + }); +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/contract-history-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/contract-history-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..0526d5b59e2a5de4f75dbf658771e392fd9c06a2 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/contract-history-hook.ts @@ -0,0 +1,117 @@ +import { isUndefined } from '@/assets/utils/types'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { ChatMsgListHookOptions } from './types'; + +/** + * 压缩历史消息列表 hook + */ +export const useContractHistory = (hookOptions: ChatMsgListHookOptions) => { + const chatMsgStore = useChatMsgStore(); + + /** 历史消息列表最大数量 */ + const maxHistoryCount = 250; + /** 保留数量 */ + const saveHistoryCount = 200; + + /** + * 压缩历史消息 + */ + function contractHistoryData() { + const length = chatMsgStore.getHistoryData().length; + if (length <= maxHistoryCount) { + return; + } + const virtualList = hookOptions.getVirtualList(); + + let sliceStart = length - saveHistoryCount; + let sliceEnd = length; + + // 从渲染列表的区间前后修改 slice 位置 + const firstRenderItem = virtualList.items.first(); + const lastRenderItem = virtualList.items.last(); + if (firstRenderItem && lastRenderItem) { + const firstRenderItemIndex = chatMsgStore.findHistoryIndex(firstRenderItem.id); + const lastRenderItemIndex = chatMsgStore.findHistoryIndex(lastRenderItem.id); + const renderCount = lastRenderItemIndex - firstRenderItemIndex + 1; + // 剩余数量 + let surplusCount = saveHistoryCount - renderCount; + surplusCount = surplusCount < 0 ? 0 : surplusCount; + + sliceStart = firstRenderItemIndex - Math.round(surplusCount / 2); + sliceEnd = lastRenderItemIndex + Math.round(surplusCount / 2) + 1; + } + + sliceStart = sliceStart < 0 ? 0 : sliceStart; + sliceEnd = sliceEnd > length ? length : sliceEnd; + + const historyData = chatMsgStore.getHistoryData(); + const newHistoryData = historyData.slice(sliceStart, sliceEnd); + const newHistoryDataLength = newHistoryData.length; + const newFirstItem = newHistoryData[0]; + const newLastItem = newHistoryData[newHistoryDataLength - 1]; + + // 重新设置 realStartIndex 为最新的值 + const newRealStartIndex = chatMsgStore.getRealIndexById(newLastItem.id); + if (!isUndefined(newRealStartIndex)) { + chatMsgStore.setRealStartIndex(newRealStartIndex); + } + + // 重新设置 realEndIndex 为最新的值 + const newRealEndIndex = chatMsgStore.getRealIndexById(newFirstItem.id); + if (!isUndefined(newRealEndIndex)) { + chatMsgStore.setRealEndIndex(newRealEndIndex); + } + + // 遍历被移除的节点,删除 msgRealIndex 中的属性 + for (let i = 0; i < historyData.length; i++) { + if (i < sliceStart || i >= sliceEnd) { + chatMsgStore.deleteRealIndexById(historyData[i].id); + } + } + + // 重新赋值 historyData + chatMsgStore.resetHistoryData(newHistoryData); + } + + return { + contractHistoryData, + }; +}; + +/** + * 压缩等待队列 hook + */ +export const useContractQueue = () => { + const chatMsgStore = useChatMsgStore(); + + /** 队列最大数量 */ + const maxQueueCount = 300; + /** 保留数量 */ + const saveQueueCount = 200; + + /** + * 压缩队列消息 + */ + function contractMsgQueue() { + const length = chatMsgStore.getMsgQueue().length; + if (length <= maxQueueCount) { + return; + } + + const sliceStart = length - saveQueueCount; + const sliceEnd = length; + + const msgQueue = chatMsgStore.getMsgQueue(); + const newMsgQueue = msgQueue.slice(sliceStart, sliceEnd); + for (let i = 0; i < sliceStart; i++) { + chatMsgStore.deleteRealIndexById(msgQueue[i].id); + } + + // 重新复制 msgQueue + chatMsgStore.resetMsgQueue(newMsgQueue); + } + + return { + contractMsgQueue, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/hide-chat-msg-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/hide-chat-msg-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c2df24b579ac839abf7cbeb095c8c29705e07a6 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/hide-chat-msg-hook.ts @@ -0,0 +1,32 @@ +import { useChannelStore } from '@/store/use-channel-store'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { ChatMsgSource, YN } from '@polyv/live-watch-sdk'; + +/** + * 消息隐藏 hook + */ +export const useHideChatMsgHook = () => { + const channelStore = useChannelStore(); + const chatMsgStore = useChatMsgStore(); + + const hideChatMsgSources: ChatMsgSource[] = []; + + /** ---------- 自定义消息是否显示 ---------- */ + const showCustomMessageEnabled = ynToBool( + channelStore.channelDetail?.channelConfig.showCustomMessageEnabled, + YN.N, + ); + if (!showCustomMessageEnabled) { + hideChatMsgSources.push(ChatMsgSource.CustomerMessage); + } + + /** ---------- 竖屏不显示打赏消息 ---------- */ + if (channelStore.isPortraitWatchLayout) { + hideChatMsgSources.push(ChatMsgSource.Reward); + } + + chatMsgStore.$patch({ + hideChatMsgSources, + }); +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/msg-queue-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/msg-queue-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c946821cb6ce4c0a4f3f9a9b4544f2d7877adab --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/msg-queue-hook.ts @@ -0,0 +1,160 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { useChatStore } from '@/store/use-chat-store'; +import { RenderPosition } from '@just4/virtual-list/types'; +import { ChatEvents, ChatMsgSource, ChatMsgType } from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, onMounted } from 'vue'; +import { useContractHistory, useContractQueue } from './contract-history-hook'; +import { ChatMsgListHookOptions } from './types'; + +export const useChatMsgQueue = (hookOptions: ChatMsgListHookOptions) => { + const chatStore = useChatStore(); + const chatMsgStore = useChatMsgStore(); + + const { contractHistoryData } = useContractHistory(hookOptions); + const { contractMsgQueue } = useContractQueue(); + + let joinTimer: number | undefined; + + /** + * 插入一条新消息到队列中 + * @param chatMsg 消息 + */ + function pushMsgQueue(chatMsg: ChatMsgType): void { + const virtualList = hookOptions.getVirtualList(); + + if (chatMsgStore.getRealStartIndex() > 0) { + if (chatMsgStore.isChatServiceMsg(chatMsg)) { + chatMsgStore.increaseRealIndex(); + } + chatMsgStore.showMoreMsg(); + virtualList.resetBoundaryState(RenderPosition.Foot); + return; + } + + chatMsgStore.pushMsgQueue(chatMsg); + contractMsgQueue(); + + // 服务端的消息才更新 realIndex + if (chatMsgStore.isChatServiceMsg(chatMsg)) { + chatMsgStore.increaseRealIndex(); + chatMsgStore.setRealIndexById(chatMsg.id, 0); + chatMsgStore.setRealStartIndex(0); + } + + // 插入到是本地消息 + if (chatMsgStore.isLocalMsg(chatMsg)) { + // 直接将队列消息插入,不需要延迟 + const addList = chatMsgStore.concatQueueToHistoryData(true); + contractHistoryData(); + + virtualList.addBoundaryItems(addList, RenderPosition.Foot, true); + } + + checkJoinTimer(); + } + + /** + * 检查定时器 + */ + function checkJoinTimer() { + // 消息队列为空,则关闭定时器 + if (chatMsgStore.getMsgQueue().length === 0) { + removeJoinTimer(); + return; + } + + // 已有定时器则不再创建 + if (joinTimer) { + return; + } + + // 启动定时器用于消息出队进行渲染 + startJoinTimer(); + } + + /** + * 开启插入定时器 + */ + function startJoinTimer() { + removeJoinTimer(); + + joinTimer = window.setInterval(() => { + const virtualList = hookOptions.getVirtualList(); + const container = hookOptions.getContainerElem(); + // 是否在底部(50为误差值) + const isBottom = container.scrollTop + container.clientHeight + 50 >= container.scrollHeight; + + if (chatMsgStore.getRealStartIndex() === 0) { + const renderData = chatMsgStore.concatQueueToHistoryData(true); + if (renderData.length) { + if (isBottom && renderData.length) { + virtualList.addBoundaryItems(renderData, RenderPosition.Foot, true); + } else { + /** + * 重置滚动列表的状态 + * 如果当前在最底部往上滚一点点,此时收到消息时如果不重置,重新滚到下方不会加载新消息 + */ + virtualList.resetBoundaryState(RenderPosition.Foot); + chatMsgStore.showMoreMsg(); + } + } + } else { + chatMsgStore.showMoreMsg(); + } + + contractHistoryData(); + checkJoinTimer(); + }, 300); + } + + /** + * 销毁插入定时器 + */ + function removeJoinTimer() { + if (joinTimer) { + clearInterval(joinTimer); + joinTimer = undefined; + } + } + + function onChatMsg(evt: { chatMsg: ChatMsgType }) { + const chatMsg = evt.chatMsg; + const msgSource = chatMsg.msgSource; + + // 对于仅看主持人,非特殊身份,非自己发言的全部过滤掉 + if ( + chatStore.onlySpecialMsg && + [ChatMsgSource.Speak, ChatMsgSource.Image, ChatMsgSource.Emotion].includes(msgSource) && + !chatMsgStore.isSpecialUserMsg(chatMsg) && + !chatMsgStore.isSelfMsg(chatMsg) + ) { + return; + } + + pushMsgQueue(chatMsg); + } + + /** + * 监听聊天室消息 + */ + function listenChatMsg() { + unlistenChatMsg(); + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatMessage, onChatMsg); + } + + function unlistenChatMsg() { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatMessage, onChatMsg); + } + + onMounted(() => { + listenChatMsg(); + }); + + onBeforeUnmount(() => { + removeJoinTimer(); + unlistenChatMsg(); + }); +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/msg-render-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/msg-render-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..50376c37cd9d562e6f094e8d3b89a2d4bd746be7 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/msg-render-hook.ts @@ -0,0 +1,163 @@ +/** + * @file 聊天室消息渲染 hook + */ +import Vue, { ComponentOptions } from 'vue'; +import { ChatMsgItemPropsType } from '../msg-items/use-chat-msg-item'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; +import { RenderPosition } from '@just4/virtual-list/types'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { translate } from '@/assets/lang'; +import { ChatMsgListHookOptions } from './types'; +import { isUndefined } from '@/assets/utils/types'; +import { pinia } from '@/plugins/pinia'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +export type ChatMsgItemComponentOptions = ComponentOptions; + +export const useMsgRender = ( + ChatMsgItemComponent: ChatMsgItemComponentOptions, + hookOptions: ChatMsgListHookOptions, +) => { + const watchAppStore = useWatchAppStore(); + const chatMsgStore = useChatMsgStore(); + const channelStore = useChannelStore(); + + /** + * 渲染消息列表 + * @param data 消息列表 + */ + function renderItems(data: ChatMsgType[]): HTMLElement[] { + const domList: HTMLElement[] = []; + + data.forEach((chatMsg, index) => { + let prevChatMsg: ChatMsgType | undefined = data[index - 1]; + let nextChatMsg: ChatMsgType | undefined = data[index + 1]; + + const historyData = chatMsgStore.getHistoryData(); + let historyIndex = -1; + if (isUndefined(prevChatMsg) || isUndefined(nextChatMsg)) { + historyIndex = chatMsgStore.findHistoryIndex(chatMsg.id); + } + if (historyIndex !== -1 && isUndefined(prevChatMsg)) { + prevChatMsg = historyData[historyIndex - 1]; + } + if (historyIndex !== -1 && isUndefined(nextChatMsg)) { + nextChatMsg = historyData[historyIndex + 1]; + } + + const propsData: ChatMsgItemPropsType = { + containerElem: hookOptions.getContainerElem(), + getVirtualList: hookOptions.getVirtualList, + chatMsg, + prevChatMsg, + nextChatMsg, + }; + + const instance = new Vue({ + render(h) { + return h(ChatMsgItemComponent, { + props: propsData, + }); + }, + pinia, + }); + + instance.$mount(); + + const domElem = instance.$el as HTMLElement; + domElem.__vueVm = instance; + domList.push(domElem); + }); + + return domList; + } + + /** + * 渲染边界节点 + * @param position 位置 + */ + function renderBoundary(position: RenderPosition) { + console.log('>>>renderBoundary:', position); + const virtualList = hookOptions.getVirtualList(); + + // 渲染没有更多 + if ( + !channelStore.isPortraitWatchLayout && + position === RenderPosition.Head && + chatMsgStore.historyEnd && + virtualList.items.length >= chatMsgStore.batchCount + ) { + const instance = new Vue({ + render(h) { + return h( + 'div', + { + class: 'c-chat-msg-list__tips pws-chat-msg-list-tips', + }, + `${translate('chat.norMore')}`, + ); + }, + }); + instance.$mount(); + return instance.$el as HTMLDivElement; + } + } + + /** + * 渲染空数据提示。 + */ + function renderEmpty() { + if (!channelStore.isPortraitWatchLayout && watchAppStore.isWatchBackUrl) { + const instance = new Vue({ + render(h) { + return h( + 'div', + { + class: 'c-chat-msg-list__tips pws-chat-msg-list-tips', + }, + `${translate('chat.empty')}`, + ); + }, + }); + instance.$mount(); + return instance.$el as HTMLDivElement; + } + } + + /** + * 渲染数据加载中 + */ + function renderLoading() { + const div = document.createElement('div'); + div.className = 'c-chat-msg-list__loading'; + return div; + } + + /** + * 渲染数据加载失败 + */ + function renderError() { + const instance = new Vue({ + render(h) { + return h( + 'div', + { + class: 'c-chat-msg-list__tips pws-chat-msg-list-tips', + }, + `${translate('chat.loadError')}`, + ); + }, + }); + instance.$mount(); + return instance.$el as HTMLDivElement; + } + + return { + renderItems, + renderBoundary, + renderLoading, + renderError, + renderEmpty, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/request-history-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/request-history-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..bb5ce1f9568ea41a2e6f49d8d194d43f9564f000 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/request-history-hook.ts @@ -0,0 +1,179 @@ +/** + * @file 用于请求历史消息 hook + */ + +import { isString } from '@/assets/utils/string'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { useChatStore } from '@/store/use-chat-store'; +import { InitialResponse } from '@just4/virtual-list/types'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; +import { useContractHistory } from './contract-history-hook'; +import { ChatMsgListHookOptions, RequestHistoryCommonResult } from './types'; + +export const useRequestHistory = ( + hookOptions: ChatMsgListHookOptions, +): RequestHistoryCommonResult => { + const chatStore = useChatStore(); + const chatMsgStore = useChatMsgStore(); + const { contractHistoryData } = useContractHistory(hookOptions); + + /** + * 请求聊天室历史消息 + */ + async function requestChatHistory(start = 0, end?: number) { + const reqStart = start; + const reqEnd = end ?? start + chatMsgStore.batchCount - 1; + const watchCore = getWatchCore(); + + let data = await watchCore.chat.getChatHistory({ + start: reqStart, + end: reqEnd, + getSpecialMessage: chatStore.onlySpecialMsg ? 1 : 0, + }); + + chatMsgStore.historyEnd = data.length < chatMsgStore.batchCount; + + // 存储数据的下标 + data.forEach((chatMsg, index) => { + const realIndex = start + index; + chatMsgStore.setRealIndexById(chatMsg.id, realIndex); + }); + + // 反转列表 + data = data.reverse(); + + return { + data, + realStartIndex: start, + realEndIndex: start + data.length - 1, + }; + } + + /** + * 加载初始数据 + */ + async function loadInitialData(): Promise> { + chatMsgStore.resetChatMsgStore(); + const result = await requestChatHistory(); + const data = result.data; + chatMsgStore.resetHistoryData(data); + + if (data.length) { + chatMsgStore.setRealStartIndex(result.realStartIndex); + chatMsgStore.setRealEndIndex(result.realEndIndex); + } + + return { + data: data, + reachedFootBoundary: true, + reachedHeadBoundary: false, + }; + } + + /** + * 加载最新的数据 + */ + async function loadNextData(ref: unknown): Promise { + if (!isString(ref)) { + throw new Error('loadNextData fail, ref is not string'); + } + console.log('loadNextData', ref); + + const historyLength = chatMsgStore.getHistoryData().length; + if (historyLength === 0) { + return []; + } + + const id = ref; + let data: ChatMsgType[] = []; + + // ref 对应的 historyData 下标 + let refIndex = chatMsgStore.findHistoryIndex(id); + if (refIndex === -1) { + return []; + } + + // 从 historyData 中截取数据返回 + let sliceStart = refIndex + 1; + if (sliceStart > historyLength - 1) { + sliceStart = historyLength - 1; + } + + let sliceEnd = refIndex + chatMsgStore.batchCount + 1; + if (sliceEnd > historyLength - 1) { + sliceEnd = historyLength - 1; + } + + let needRequest = false; + // 此时如果是数组尾部的一批,且 realStartIndex 非 0,则请求接口获取 + if (sliceEnd - sliceStart < chatMsgStore.batchCount && chatMsgStore.getRealStartIndex() !== 0) { + needRequest = true; + } + + if (needRequest && chatMsgStore.getRealStartIndex() !== 0) { + const reqEnd = chatMsgStore.getRealStartIndex() - 1; + let reqStart = reqEnd - chatMsgStore.batchCount; + reqStart = reqStart < 0 ? 0 : reqStart; + const result = await requestChatHistory(reqStart, reqEnd); + chatMsgStore.concatHistoryData(result.data, 'tail'); + chatMsgStore.setRealStartIndex(result.realStartIndex); + // 数据新增,refIndex 需要重新获取 + refIndex = chatMsgStore.findHistoryIndex(id); + } + + sliceStart = refIndex + 1; + sliceEnd = refIndex + chatMsgStore.batchCount + 1; + + data = chatMsgStore.getHistoryData().slice(sliceStart, sliceEnd); + + setTimeout(() => { + contractHistoryData(); + }, 50); + + return data; + } + + /** + * 加载更旧的数据 + */ + async function loadPreviousData(ref: unknown): Promise { + if (!isString(ref)) { + throw new Error('loadPreviousData fail, ref is not string'); + } + console.log('loadPreviousData', ref); + + const batchCount = chatMsgStore.batchCount; + const id = ref; + let data: ChatMsgType[] = []; + + // ref 对应的 historyData 下标 + let refIndex = chatMsgStore.findHistoryIndex(id); + + // ref 下标小于页数,需要请求一批历史消息 + if (refIndex < batchCount) { + const result = await requestChatHistory(chatMsgStore.getRealEndIndex() + 1); + chatMsgStore.concatHistoryData(result.data, 'header'); + chatMsgStore.setRealEndIndex(result.realEndIndex); + // 数据新增,refIndex 需要重新获取 + refIndex = chatMsgStore.findHistoryIndex(id); + } + + // 从 historyData 中截取数据返回 + const sliceStart = refIndex - batchCount < 0 ? 0 : refIndex - batchCount; + const sliceEnd = refIndex; + data = chatMsgStore.getHistoryData().slice(sliceStart, sliceEnd); + + setTimeout(() => { + contractHistoryData(); + }, 50); + + return data; + } + + return { + loadInitialData, + loadNextData, + loadPreviousData, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/request-history-session-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/request-history-session-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..7283eb3ccbf2c04b58f30e10e5b70789b9751aca --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/request-history-session-hook.ts @@ -0,0 +1,156 @@ +import { ref } from 'vue'; +import { isString } from '@/assets/utils/string'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { InitialResponse } from '@just4/virtual-list/types'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; +import { RequestHistoryCommonResult } from './types'; + +/** + * @hook 请求场次历史消息 + */ +export const useRequestHistorySession = (): RequestHistoryCommonResult => { + const playbackStore = usePlaybackStore(); + const chatMsgStore = useChatMsgStore(); + + /** 当前页码,默认为 1 */ + const curPageNumber = ref(1); + + /** + * 请求聊天室历史消息 + */ + async function requestChatHistory(pageNumber: number, start = 0) { + const watchCore = getWatchCore(); + + try { + let { curPage, totalPage, data } = await watchCore.chat.getChatHistoryBySessionId({ + sessionId: playbackStore.currentPlaybackTarget?.playbackOptions.sessionId || '', + pageNumber: pageNumber, + pageSize: chatMsgStore.batchCount, + }); + + curPageNumber.value = curPage; + chatMsgStore.historyEnd = + totalPage === 0 || curPage === totalPage || data.length < chatMsgStore.batchCount; + + // 存储数据的下标 + data.forEach((chatMsg, index) => { + const realIndex = start + index; + chatMsgStore.setRealIndexById(chatMsg.id, realIndex); + }); + + // 反转列表 + data = data.reverse(); + + return { + data, + realStartIndex: start, + realEndIndex: start + data.length - 1, + }; + } catch (error) { + console.error('error', error); + return { + data: [], + realStartIndex: start, + realEndIndex: start + 0 - 1, + }; + } + } + + /** + * 加载初始数据 + */ + async function loadInitialData(): Promise> { + chatMsgStore.resetChatMsgStore(); + curPageNumber.value = 1; + + const result = await requestChatHistory(curPageNumber.value, 0); + const data = result.data; + chatMsgStore.resetHistoryData(data); + + if (data.length) { + chatMsgStore.setRealStartIndex(result.realStartIndex); + chatMsgStore.setRealEndIndex(result.realEndIndex); + } + + return { + data: data, + reachedFootBoundary: true, + reachedHeadBoundary: false, + }; + } + + /** + * 加载最新的数据 + */ + async function loadNextData(ref: unknown): Promise { + if (!isString(ref)) { + throw new Error('loadNextData fail, ref is not string'); + } + console.log('loadNextData', ref); + + const historyLength = chatMsgStore.getHistoryData().length; + if (historyLength === 0) { + return []; + } + + const id = ref; + let data: ChatMsgType[] = []; + + // ref 对应的 historyData 下标 + const refIndex = chatMsgStore.findHistoryIndex(id); + if (refIndex === -1) { + return []; + } + + // 从 historyData 中截取数据返回 + const sliceStart = refIndex + 1; + const sliceEnd = refIndex + chatMsgStore.batchCount + 1; + data = chatMsgStore.getHistoryData().slice(sliceStart, sliceEnd); + + return data; + } + + /** + * 加载更旧的数据 + */ + async function loadPreviousData(ref: unknown): Promise { + if (!isString(ref)) { + throw new Error('loadPreviousData fail, ref is not string'); + } + console.log('loadPreviousData', ref); + + const batchCount = chatMsgStore.batchCount; + const id = ref; + let data: ChatMsgType[] = []; + + // ref 对应的 historyData 下标 + let refIndex = chatMsgStore.findHistoryIndex(id); + + // ref 下标小于页数,需要请求一批历史消息 + if (refIndex < batchCount) { + const result = await requestChatHistory( + curPageNumber.value + 1, + chatMsgStore.getRealEndIndex() + 1, + ); + chatMsgStore.concatHistoryData(result.data, 'header'); + chatMsgStore.setRealEndIndex(result.realEndIndex); + // 数据新增,refIndex 需要重新获取 + refIndex = chatMsgStore.findHistoryIndex(id); + } + + // 从 historyData 中截取数据返回 + const sliceStart = refIndex - batchCount < 0 ? 0 : refIndex - batchCount; + const sliceEnd = refIndex; + data = chatMsgStore.getHistoryData().slice(sliceStart, sliceEnd); + + return data; + } + + return { + loadInitialData, + loadNextData, + loadPreviousData, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/send-msg-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/send-msg-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..57e16d1e1e84e17c612e7d7aef56d367cfda56d9 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/send-msg-hook.ts @@ -0,0 +1,58 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; +import { + SendEmotionImageMsgOptions, + SendSpeakMsgOptions, + SendImageMsgOptions, +} from '@polyv/live-watch-sdk'; + +export const useSendMsg = () => { + /** + * 发送 speak 消息 + */ + function sendSpeakMsg(options: SendSpeakMsgOptions) { + const watchCore = getWatchCore(); + watchCore.chat.sendSpeakMsg(options, msgResult => { + eventBus.$emit(appEvents.chat.MsgResultUpdate, msgResult); + }); + eventBus.$emit(appEvents.chat.ScrollToNew); + } + + /** + * 发送 emotion 图片消息 + */ + async function sendEmotionImageMsg(options: SendEmotionImageMsgOptions) { + const watchCore = getWatchCore(); + watchCore.chat.sendEmotionImageMsg(options, msgResult => { + eventBus.$emit(appEvents.chat.MsgResultUpdate, msgResult); + }); + eventBus.$emit(appEvents.chat.ScrollToNew); + } + + /** + * 发送 system 系统消息 + */ + function sendSystemMsg(content: string) { + const watchCore = getWatchCore(); + watchCore.chat.sendSystemMsg(content); + eventBus.$emit(appEvents.chat.ScrollToNew); + } + + /** + * 发送图片 + */ + function sendImageMsg(options: SendImageMsgOptions) { + const watchCore = getWatchCore(); + watchCore.chat.sendImageMsg(options, msgResult => { + eventBus.$emit(appEvents.chat.MsgResultUpdate, msgResult); + }); + eventBus.$emit(appEvents.chat.ScrollToNew); + } + + return { + sendSpeakMsg, + sendEmotionImageMsg, + sendSystemMsg, + sendImageMsg, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/hooks/types.ts b/src/components/page-watch-common/chat/chat-msg-list/hooks/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..d1f6fb7cc1899f96510909fa15d41218e79d4360 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/hooks/types.ts @@ -0,0 +1,18 @@ +import { VirtualList } from '@just4/virtual-list'; +import { InitialResponse } from '@just4/virtual-list/types'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; + +export type GetVirtualListFn = () => VirtualList; + +export interface ChatMsgListHookOptions { + /** 获取列表容器节点 */ + getContainerElem: () => HTMLDivElement; + /** 获取虚拟列表实例 */ + getVirtualList: GetVirtualListFn; +} + +export interface RequestHistoryCommonResult { + loadInitialData(): Promise>; + loadNextData(ref: unknown): Promise; + loadPreviousData(ref: unknown): Promise; +} diff --git a/src/components/page-watch-common/chat/chat-msg-list/imgs/loading.png b/src/components/page-watch-common/chat/chat-msg-list/imgs/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..8a2d5b705698d63ce54f18b469b30eab095949b8 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-msg-list/imgs/loading.png differ diff --git a/src/components/page-watch-common/chat/chat-msg-list/mobile-chat-msg-list.vue b/src/components/page-watch-common/chat/chat-msg-list/mobile-chat-msg-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..eb24683c0c49cc611f2dd06181bbc3a7eb7bb7a8 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/mobile-chat-msg-list.vue @@ -0,0 +1,80 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-computed-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-computed-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1b721a49578f483df9cbf9f42f0b6824383e186 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-computed-hook.ts @@ -0,0 +1,24 @@ +import { VirtualList } from '@just4/virtual-list'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; +import { ComputedRef, inject, InjectionKey, provide } from 'vue'; + +export interface ChatMsgComputedContext { + /** 滚动容器 */ + containerRef: ComputedRef; + /** 是否为同一个人连续发送的消息 */ + isContinuityMsg: ComputedRef; + /** 是否为连续相同的消息来源 */ + isContinuitySourceMsg: ComputedRef; + /** 获取虚拟列表 */ + getVirtualList(): VirtualList | undefined; +} + +const MSG_COMPUTED_INJECT_KEY: InjectionKey = Symbol('MsgComputed'); + +export const useChatMsgComputedProvide = (context: ChatMsgComputedContext) => { + provide(MSG_COMPUTED_INJECT_KEY, context); +}; + +export const useChatMsgComputedInject = () => { + return inject(MSG_COMPUTED_INJECT_KEY) as ChatMsgComputedContext; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-data-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-data-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..05554155f59c0113c022a1868b37a8ae71dd88d4 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-data-hook.ts @@ -0,0 +1,76 @@ +import { VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ChatMsgType, ChatMsgQuoteType } from '@polyv/live-watch-sdk'; +import { computed, ComputedRef, inject, InjectionKey, provide, unref } from 'vue'; +import { chatMsgItemProps } from '../use-chat-msg-item'; + +/** + * 聊天室消息数据上下文 + */ +export interface ChatMsgDataContext { + /** 聊天消息 */ + chatMsg: ComputedRef; + /** 上一条消息 */ + prevChatMsg: ComputedRef; + /** 下一条消息 */ + nextChatMsg: ComputedRef; + /** 消息 id */ + msgId: ComputedRef; + /** 消息引用内容 */ + msgQuote: ComputedRef; +} + +const MSG_DATA_INJECT_KEY: InjectionKey = Symbol('MsgData'); + +/** + * 注入聊天室消息数据 + * @param context + */ +export const useChatMsgDataProvide = (context: ChatMsgDataContext) => { + provide(MSG_DATA_INJECT_KEY, context); +}; + +/** + * 获取聊天室消息数据的注入 + */ +export const useChatMsgDataInject = < + MsgType extends ChatMsgType = ChatMsgType, +>(): ChatMsgDataContext => { + return inject(MSG_DATA_INJECT_KEY) as ChatMsgDataContext; +}; + +/** + * 聊天室消息数据 hook + * @param props + * @param autoProvide + */ +export const useChatMsgData = (props: VueProps, autoProvide = true) => { + const chatMsg = computed(() => props.chatMsg); + const prevChatMsg = computed(() => props.prevChatMsg); + const nextChatMsg = computed(() => props.nextChatMsg); + + /** 消息 id */ + const msgId = computed(() => unref(chatMsg).id); + + /** 引用内容 */ + const msgQuote = computed(() => { + const chatMsgVal = unref(chatMsg); + if ('quote' in chatMsgVal) { + return chatMsgVal.quote; + } + return undefined; + }); + + const chatMsgDataContext: ChatMsgDataContext = { + chatMsg, + prevChatMsg, + nextChatMsg, + msgId, + msgQuote, + }; + + if (autoProvide) { + useChatMsgDataProvide(chatMsgDataContext); + } + + return chatMsgDataContext; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-quote-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-quote-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..4f3d65636ee5abe80f32d43ca7983ee89ec0c44e --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-quote-hook.ts @@ -0,0 +1,67 @@ +import { useChannelStore } from '@/store/use-channel-store'; +import { useChatStore } from '@/store/use-chat-store'; +import { YN, ChatMsgQuoteOriginType } from '@polyv/live-watch-sdk'; +import { computed, unref } from 'vue'; +import { ynToBool } from '@utils-ts/boolean'; +import { useViewerStore } from '@/store/use-viewer-store'; +import { appEvents, eventBus } from '@/app/app-events'; +import { TAB_NAME_ASK } from '@/assets/constants/tab-name'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +/** + * 聊天消息回复 hook + * @param chatMsgRef 聊天消息 + */ +export const useChatMsgQuote = (chatMsgRef: ResponsiveRef) => { + const chatStore = useChatStore(); + const watchAppStore = useWatchAppStore(); + const channelStore = useChannelStore(); + const viewerStore = useViewerStore(); + const layoutStore = useLayoutStore(); + + /** 能否进行引用回复 */ + const canQuoteReply = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + // 当前处于提问不需要回复 + if ( + layoutStore.mobileMenuCurrentName === TAB_NAME_ASK || + layoutStore.pcAsideTabCurrentName === TAB_NAME_ASK + ) { + return false; + } + + // 引用回复开关 + const quoteReplyEnabled = ynToBool( + channelStore.channelDetail?.channelConfig.quoteReplyEnabled, + YN.N, + ); + + // 聊天室是否被关闭 + const chatRoomIsClosed = chatStore.chatRoomIsClosed; + + return quoteReplyEnabled && !chatRoomIsClosed; + }); + + /** 点击回复 */ + function onClickQuote() { + // 未设置昵称 + if (!viewerStore.hasNickname) { + eventBus.$emit(appEvents.chat.OpenSetNick, true); + return; + } + + // 设置引用的消息 + chatStore.$patch({ + currentQuoteMsg: unref(chatMsgRef), + }); + // 聚焦输入框 + eventBus.$emit(appEvents.chat.FocusToChatInput); + } + + return { + canQuoteReply, + onClickQuote, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-time-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-time-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..02a1c41c1b367f755952056ac9cc4d98807d54b7 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-time-hook.ts @@ -0,0 +1,91 @@ +/** + * @file 聊天室消息时间相关 hook + */ +import { DATE_FORMAT_HM, DATE_FORMAT_SLASH } from '@/assets/constants/date-format'; +import { formatDate } from '@utils-ts/date'; +import { computed, ComputedRef, inject, InjectionKey, provide, unref } from 'vue'; +import { ChatMsgDataContext } from './chat-msg-data-hook'; + +/** 消息间隔时间 */ +const MSG_TIME_GAP = 2 * 60 * 1000; + +export interface ChatMsgTimeContext { + /** 是否显示消息的时间 */ + showMsgTime: ComputedRef; + /** 消息时间文案 */ + msgTimeText: ComputedRef; +} + +export const MSG_TIME_INJECT_KEY: InjectionKey = Symbol('MsgTime'); + +/** + * 注入聊天室消息时间 + * @param context 聊天室消息用户上下文 + */ +export const useChatMsgTimeProvide = (context: ChatMsgTimeContext) => { + provide(MSG_TIME_INJECT_KEY, context); +}; + +/** + * 获取聊天室消息时间的注入 + */ +export const useChatMsgTimeInject = () => { + return inject(MSG_TIME_INJECT_KEY) as ChatMsgTimeContext; +}; + +/** + * 聊天室消息时间 hook + * @param chatMsgData + * @param autoProvide + */ +export const useChatMsgTime = (chatMsgData: ChatMsgDataContext, autoProvide = true) => { + const { chatMsg, prevChatMsg } = chatMsgData; + + /** + * 是否显示消息的时间 + */ + const showMsgTime = computed(() => { + const prevMsg = unref(prevChatMsg); + const msg = unref(chatMsg); + + if (!prevMsg) { + return true; + } + + if (msg.time - prevMsg.time > MSG_TIME_GAP) { + return true; + } + + return false; + }); + + /** + * 消息时间文案 + */ + const msgTimeText = computed(() => { + const time = unref(chatMsg).time; + const tdtime = new Date().toLocaleDateString(); + const showTime = new Date(time); + const ottime = showTime.toLocaleDateString(); + let timeContent = ''; + + if (tdtime !== ottime) { + timeContent = formatDate(time, DATE_FORMAT_SLASH); + } else { + timeContent = formatDate(time, DATE_FORMAT_HM); + } + + return timeContent; + }); + + const chatMsgTimeContext: ChatMsgTimeContext = { + showMsgTime, + msgTimeText, + }; + + if (autoProvide) { + useChatMsgTimeProvide(chatMsgTimeContext); + } + + return chatMsgTimeContext; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-user-hook.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-user-hook.ts new file mode 100644 index 0000000000000000000000000000000000000000..2c48125ddb4fda36170673c09438f1bdf80a7f5c --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_hooks/chat-msg-user-hook.ts @@ -0,0 +1,148 @@ +/** + * @file 聊天消息用户相关 hook + */ +import { DEFAULT_VIEWER_AVATAR } from '@/assets/constants/defaults'; +import { resetImageProtocol } from '@/assets/utils/image'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { useViewerStore } from '@/store/use-viewer-store'; +import { ChatMessageUser, ChatMsgType, ChatUserTypes } from '@polyv/live-watch-sdk'; +import { computed, ComputedRef, inject, InjectionKey, provide, unref } from 'vue'; + +/** + * 聊天室消息用户上下文 + */ +export interface ChatMsgUserContext { + /** 消息的用户信息 */ + msgUser: ComputedRef; + /** 消息的用户昵称 */ + msgUserNick: ComputedRef; + /** 消息的用户头像 */ + msgUserAvatar: ComputedRef; + /** 消息的用户类型 */ + msgUserType: ComputedRef; + /** 消息的用户头衔 */ + msgUserActor: ComputedRef; + /** 是否为自己发言 */ + isSelfMsg: ComputedRef; + /** 是否为特殊用户 */ + isSpecialUser: ComputedRef; +} + +/** + * 聊天消息用户相关 hook + * @param chatMsgRef 聊天消息响应式对象 + */ +export const useChatMsgUserBasic = (chatMsgRef: ResponsiveRef) => { + const viewerStore = useViewerStore(); + const chatMsgStore = useChatMsgStore(); + + /** + * 消息的用户信息 + */ + const msgUser = computed(() => { + if (!chatMsgRef.value) { + return; + } + return chatMsgStore.getChatMsgUser(chatMsgRef.value); + }); + + /** + * 消息的用户昵称 + */ + const msgUserNick = computed(() => { + const user = unref(msgUser); + return user?.nick ?? ''; + }); + + /** + * 消息的用户头像 + */ + const msgUserAvatar = computed(() => { + const user = unref(msgUser); + return resetImageProtocol(user?.pic ?? DEFAULT_VIEWER_AVATAR); + }); + + /** + * 消息的用户类型 + */ + const msgUserType = computed(() => { + const user = unref(msgUser); + return user?.userType; + }); + + /** + * 消息的用户头衔 + */ + const msgUserActor = computed(() => { + const user = unref(msgUser); + return user?.actor; + }); + + /** + * 是否为自己发言 + */ + const isSelfMsg = computed(() => { + return unref(msgUser)?.userId === viewerStore.viewerId; + }); + + /** + * 是否为特殊用户 + */ + const isSpecialUser = computed(() => { + if (!chatMsgRef.value) { + return false; + } + return chatMsgStore.isSpecialUserMsg(chatMsgRef.value); + }); + + const chatMsgUserContext: ChatMsgUserContext = { + msgUser, + msgUserNick, + msgUserAvatar, + msgUserType, + msgUserActor, + isSelfMsg, + isSpecialUser, + }; + + return chatMsgUserContext; +}; + +export const MSG_USER_INJECT_KEY: InjectionKey = Symbol('MsgUser'); + +/** + * 注入聊天室消息用户 + * @param context + */ +export const useChatMsgUserProvide = (context: ChatMsgUserContext) => { + provide(MSG_USER_INJECT_KEY, context); +}; + +/** + * 获取聊天室消息用户的注入 + */ +export const useChatMsgUserInject = () => { + return inject(MSG_USER_INJECT_KEY) as ChatMsgUserContext; +}; + +type UseChatMsgUserOptions = { + /** 聊天消息响应式对象 */ + chatMsg: ResponsiveRef; +}; + +/** + * 聊天室消息用户 hook + * @param options + * @param autoProvide + */ +export const useChatMsgUser = (options: UseChatMsgUserOptions, autoProvide = true) => { + const { chatMsg } = options; + + const chatMsgUserContext = useChatMsgUserBasic(chatMsg); + + if (autoProvide) { + useChatMsgUserProvide(chatMsgUserContext); + } + + return chatMsgUserContext; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_imgs/img-msg-placeholder.png b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_imgs/img-msg-placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..791f4749c99b94bc47906f352b73782cd8892bfe Binary files /dev/null and b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_imgs/img-msg-placeholder.png differ diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/mobile-msg-container.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/mobile-msg-container.vue new file mode 100644 index 0000000000000000000000000000000000000000..02f22b2a4034697ab130a1041d5b398f7c9eed86 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/mobile-msg-container.vue @@ -0,0 +1,156 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/pc-msg-container.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/pc-msg-container.vue new file mode 100644 index 0000000000000000000000000000000000000000..e69ffaafbada238a63ee8e1695b3861c556bb4c4 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/pc-msg-container.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/portrait-msg-container.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/portrait-msg-container.vue new file mode 100644 index 0000000000000000000000000000000000000000..13b2c19ec06fcd81f00753d23f6a15f23b308510 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/portrait-msg-container.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/use-msg-container.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/use-msg-container.ts new file mode 100644 index 0000000000000000000000000000000000000000..d319949731b8c7658c2b7b6b984bc9571b3ed9d8 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/use-msg-container.ts @@ -0,0 +1,36 @@ +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed } from 'vue'; +import { CSSProperties } from 'vue/types/jsx'; + +export const msgContainerProps = () => ({ + /** 气泡模式 */ + bubbleModel: PropUtils.bool.def(true), + /** 气泡最小宽度 */ + minWidth: PropUtils.number, + /** 气泡内边距 */ + bubblePadding: PropUtils.string, + /** 是否渲染另一个内容,默认:false */ + renderOtherContent: PropUtils.bool.def(false), +}); + +export const useMsgContainer = (options: { props: VueProps }) => { + const { props } = options; + + const bubbleStyle = computed(() => { + const style: CSSProperties = {}; + + if (props.minWidth) { + style.minWidth = `${props.minWidth}px`; + } + + if (props.bubblePadding) { + style.padding = props.bubblePadding; + } + + return style; + }); + + return { + bubbleStyle, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/use-msg-tips-control.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/use-msg-tips-control.ts new file mode 100644 index 0000000000000000000000000000000000000000..b38ffc1770a232aafb5e086e08724f860916ebca --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-container/use-msg-tips-control.ts @@ -0,0 +1,66 @@ +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { useClickOutside } from '@/hooks/behaviors/use-click-outside'; +import { useSimpleVisible } from '@/hooks/behaviors/use-simple-visible'; +import { ref, unref, watch } from 'vue'; + +export const msgTipsControlProps = () => ({ + /** 是否禁用提示,默认:true */ + disabledTips: PropUtils.bool.def(true), +}); + +export const useMsgTipsControl = (options: { props: VueProps }) => { + const { props } = options; + + /** 外层节点 */ + const referenceRef = ref(); + + const { listenClickOutSide, removeListenClickOutSide } = useClickOutside( + referenceRef, + onClickOutside, + false, + ); + + /** 提示是否显示 */ + const { + visible: tipsVisible, + toggle: toggleTipsVisible, + close: closeTips, + } = useSimpleVisible(false); + + /** 点击内容 */ + function onClickControl() { + if (props.disabledTips) { + return; + } + + listenClickOutSide(); + toggleTipsVisible(); + } + + /** 点击提示 */ + function onClickTips() { + closeTips(); + } + + /** 处理点击外部 */ + function onClickOutside() { + removeListenClickOutSide(); + closeTips(); + } + + watch( + () => unref(tipsVisible), + () => { + if (!unref(tipsVisible)) { + removeListenClickOutSide(); + } + }, + ); + + return { + referenceRef, + tipsVisible, + onClickControl, + onClickTips, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/mobile-msg-quote-content.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/mobile-msg-quote-content.vue new file mode 100644 index 0000000000000000000000000000000000000000..b8e9914d01ad1648a47a40df4f8a1245491164e5 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/mobile-msg-quote-content.vue @@ -0,0 +1,77 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/pc-msg-quote-content.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/pc-msg-quote-content.vue new file mode 100644 index 0000000000000000000000000000000000000000..14b68f9672915b098cd55ffa10d9e8838e7da6b2 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/pc-msg-quote-content.vue @@ -0,0 +1,78 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/portrait-msg-quote-content.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/portrait-msg-quote-content.vue new file mode 100644 index 0000000000000000000000000000000000000000..9f48f116564b2dc370eace2ff4e50b2ad73f90cb --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/portrait-msg-quote-content.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/use-msg-quote-content.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/use-msg-quote-content.ts new file mode 100644 index 0000000000000000000000000000000000000000..249ae3eb3a1c038823c01d3dc75c59b600d951c9 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-quote-content/use-msg-quote-content.ts @@ -0,0 +1,25 @@ +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ChatMsgQuoteType, ChatMsgSource } from '@polyv/live-watch-sdk'; +import { computed } from 'vue'; + +export const msgQuoteContentProps = () => ({ + /** 引用内容 */ + msgQuote: PropUtils.object().isRequired, +}); + +export const useMsgQuoteContent = (options: { props: VueProps }) => { + const { props } = options; + + /** 引用消息的昵称 */ + const quoteNick = computed(() => { + return props.msgQuote.nick; + }); + + /** 引用的消息来源 */ + const quoteMsgSource = computed(() => props.msgQuote.msgSource); + + return { + quoteNick, + quoteMsgSource, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-actor/msg-user-actor.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-actor/msg-user-actor.vue new file mode 100644 index 0000000000000000000000000000000000000000..7d8ba3dca56ec161151f2cf66aaa63e33f7f53bb --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-actor/msg-user-actor.vue @@ -0,0 +1,38 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-actor/use-msg-user-actor.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-actor/use-msg-user-actor.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2086b50b1c120101a8e09a80fe6ba79f39bd31f --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-actor/use-msg-user-actor.ts @@ -0,0 +1,41 @@ +import { ChatUserTypes } from '@polyv/live-watch-sdk'; +import { computed, unref } from 'vue'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; + +export const msgUserActorProps = () => ({ + actor: PropUtils.string.def(''), + userType: PropUtils.enum().def(ChatUserTypes.Viewer), +}); + +export const useMsgUserActor = (options: { props: VueProps }) => { + const { props } = options; + + /** 头衔的颜色关系 */ + const actorColors = { + [ChatUserTypes.Manager]: '#33BBC5', + [ChatUserTypes.Assistant]: '#598FE5', + [ChatUserTypes.Teacher]: '#F09343', + [ChatUserTypes.Guest]: '#EB6165', + [ChatUserTypes.Dummy]: '#F09343', + }; + + const msgUserType = computed(() => props.userType); + + const msgUserActor = computed(() => props.actor); + + /** 头衔标签的颜色 */ + const actorLabelColor = computed(() => { + const userType = unref(msgUserType); + if (!userType) { + return undefined; + } + + return actorColors[userType as keyof typeof actorColors]; + }); + + return { + msgUserActor, + msgUserType, + actorLabelColor, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/mobile-msg-user-info.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/mobile-msg-user-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..4fd449aa8f5ea8c5b50b17d4ef4a65e53b5a633a --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/mobile-msg-user-info.vue @@ -0,0 +1,95 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/pc-msg-user-info.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/pc-msg-user-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..65d2b2cf3bb1d7a2fac099878a9a7cdce07fd845 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/pc-msg-user-info.vue @@ -0,0 +1,116 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/portrait-msg-user-info.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/portrait-msg-user-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..ee327f00f2763ffa030994bff08351b0fa4ae388 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/portrait-msg-user-info.vue @@ -0,0 +1,38 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/use-msg-user-info.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/use-msg-user-info.ts new file mode 100644 index 0000000000000000000000000000000000000000..ebc7bc61cfcbc50c06cb342414b5a959e62b521d --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_msg-user-info/use-msg-user-info.ts @@ -0,0 +1,9 @@ +import { useChatMsgUserInject } from '../_hooks/chat-msg-user-hook'; + +export const useMsgUserInfo = () => { + const chatMsgUserContext = useChatMsgUserInject(); + + return { + ...chatMsgUserContext, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/_styles/vars.scss b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_styles/vars.scss new file mode 100644 index 0000000000000000000000000000000000000000..3a42177e0ccd1af29b84034a4c4b369a51cf7dfb --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/_styles/vars.scss @@ -0,0 +1,14 @@ +// 消息右侧边距(为挂件按钮让路) +$--msg-container-padding-size: 40px; +// 消息水平外边距 +$--margin-horizontal-margin: 16px; +// 消息垂直外边距 +$--margin-vertical-margin: 16px; +// 头像的大小 +$--msg-user-avatar-size: 28px; +// 头像的大小(移动端) +$--msg-user-avatar-size-mobile: 32px; +// 头像的大小距离 +$--msg-user-avatar-padding: $--msg-user-avatar-size + 8px; +// 头像的大小距离(移动端) +$--msg-user-avatar-padding-mobile: $--msg-user-avatar-size-mobile + 8px; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/mobile-customer-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/mobile-customer-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..ec02b8045863d20700b1867b9eb74c15b62db6ad --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/mobile-customer-msg.vue @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/pc-customer-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/pc-customer-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..62c3aacbc99858f75c3588701629bcdf9858c4f4 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/pc-customer-msg.vue @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/portrait-customer-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/portrait-customer-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..8d794d3b0cf4cfcc451e7d0ab2e5adca5ad3ef5f --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/portrait-customer-msg.vue @@ -0,0 +1,16 @@ + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/use-customer-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/use-customer-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..93cacbfeda39c0591257c0541a37f42e720e46eb --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/customer-msg/use-customer-msg.ts @@ -0,0 +1,10 @@ +import { ChatMsgCustomerMessageType } from '@polyv/live-watch-sdk'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +export const useCustomerMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + + return { + chatMsg, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/mobile-emotion-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/mobile-emotion-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..d36bdcbd5c6443ccfc32907542a862432cd72632 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/mobile-emotion-msg.vue @@ -0,0 +1,43 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/pc-emotion-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/pc-emotion-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..bb09927f113e81e8078b3031be17f3f7705e52df --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/pc-emotion-msg.vue @@ -0,0 +1,43 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/portrait-emotion-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/portrait-emotion-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..c79154e42d5f0b19fdf84fe365b7d9d030157b42 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/portrait-emotion-msg.vue @@ -0,0 +1,43 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/use-emotion-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/use-emotion-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..a7a0a963cc7e090eb8a2e194daba1e1ed43c836b --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/emotion-msg/use-emotion-msg.ts @@ -0,0 +1,31 @@ +import { previewImage } from '@/hooks/components/use-image-preview'; +import { ChatMsgEmotionType } from '@polyv/live-watch-sdk'; +import { computed, ref, unref } from 'vue'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +export const useEmotionMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + + /** 表情图片地址 */ + const emotionUrl = computed(() => { + return unref(chatMsg).emotionUrl; + }); + + /** 是否加载完成 */ + const isLoaded = ref(false); + + function onImgLoad() { + isLoaded.value = true; + } + + function onClickEmotion() { + previewImage([unref(emotionUrl)]); + } + + return { + emotionUrl, + isLoaded, + onImgLoad, + onClickEmotion, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_doc.svg b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_doc.svg new file mode 100644 index 0000000000000000000000000000000000000000..99c4165c5e2c92b9cc4037a6b13cc4045f57b75a --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_doc.svg @@ -0,0 +1,14 @@ + + + 编组备份 + + + + + + w + + + + + \ No newline at end of file diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_pdf.svg b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_pdf.svg new file mode 100644 index 0000000000000000000000000000000000000000..a1ae37a224a157a952e91c15acefbb7768bfc33f --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_pdf.svg @@ -0,0 +1,14 @@ + + + 编组 19 + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_ppt.svg b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_ppt.svg new file mode 100644 index 0000000000000000000000000000000000000000..3185d16db1c6eb641b141b491f6bd56d4e3fa549 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_ppt.svg @@ -0,0 +1,15 @@ + + + 编组 2 + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_xls.svg b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_xls.svg new file mode 100644 index 0000000000000000000000000000000000000000..9273a9b0a2df2b194e3b843036012828e0695f48 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/imgs/icon_xls.svg @@ -0,0 +1,16 @@ + + + icon xls + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/mobile-file-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/mobile-file-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..969235e23509c3cb204bda6e8009cc224663fb93 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/mobile-file-msg.vue @@ -0,0 +1,122 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/pc-file-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/pc-file-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..c53fa31180076c5a1dbb8c366344271d7e9a7c6c --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/pc-file-msg.vue @@ -0,0 +1,125 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/portrait-file-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/portrait-file-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..6340823a0636d4ebb91d6c58ceb3f06b53da605a --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/portrait-file-msg.vue @@ -0,0 +1,100 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/use-file-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/use-file-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..47ad42127b71e23c90d1dad29a99bbddb38d26b0 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/file-msg/use-file-msg.ts @@ -0,0 +1,102 @@ +import { translate } from '@/assets/lang'; +import { isIframe } from '@/assets/utils/browser'; +import { copyText } from '@/assets/utils/copy'; +import { downloadFile, getFileSuffix } from '@/assets/utils/file'; +import { toast } from '@/hooks/components/use-toast'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { ChatMsgFileType } from '@polyv/live-watch-sdk'; +import { computed, nextTick, onMounted, ref, unref } from 'vue'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +export const useFileMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + const watchAppStore = useWatchAppStore(); + + /** 文件地址 */ + const fileUrl = computed(() => { + return unref(chatMsg).fileUrl; + }); + + /** 文件名 */ + const fileName = computed(() => { + return unref(chatMsg).fileName; + }); + + /** 文件扩展名 */ + const fileSuffix = computed(() => { + const name = unref(fileName); + return getFileSuffix(name); + }); + + /** 下载文件 */ + function toDownloadFile() { + // 微信小程序 webview 下改为复制下载地址 + if (watchAppStore.isWxMiniProgram) { + copyFileUrl(); + return; + } + + if (isIframe) { + const msg = { + name: 'file', + path: unref(fileUrl), + }; + + window.parent.postMessage(msg, '*'); + return; + } + + const url = unref(fileUrl); + const name = unref(fileName); + + downloadFile(url, name); + } + + /** 复制下载地址 */ + async function copyFileUrl() { + await copyText(unref(fileUrl)); + toast.success(translate('copy.linkSuccess')); + } + + return { + chatMsg, + fileName, + fileSuffix, + toDownloadFile, + copyFileUrl, + }; +}; + +/** + * 文件名截断 hook + */ +export const useFileNameBlock = () => { + const textWrapRef = ref(); + const textContentRef = ref(); + + /** 是否需要截断 */ + const needBlock = ref(false); + + async function checkNeedBlock() { + await nextTick(); + const wrapElem = unref(textWrapRef); + const contentElem = unref(textContentRef); + + if (!wrapElem || !contentElem) { + return; + } + + needBlock.value = contentElem.clientHeight > wrapElem.clientHeight; + } + + onMounted(() => { + checkNeedBlock(); + }); + + return { + textWrapRef, + textContentRef, + needBlock, + checkNeedBlock, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/mobile-image-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/mobile-image-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..0c0180e73ee1e693d0a3237c7d9f96ac675ab135 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/mobile-image-msg.vue @@ -0,0 +1,78 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/pc-image-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/pc-image-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..e83ffd4c1026dde6998d4318256a9371b76a7804 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/pc-image-msg.vue @@ -0,0 +1,81 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/portrait-image-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/portrait-image-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..9a6c49463996faddd397ca7d421b53f4934af26a --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/portrait-image-msg.vue @@ -0,0 +1,74 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/use-image-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/use-image-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..b4405348932e6cd175de84d00266b996635a1486 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/image-msg/use-image-msg.ts @@ -0,0 +1,170 @@ +import { isMobile } from '@/assets/utils/browser'; +import { resetImageProtocol } from '@/assets/utils/image'; +import { previewImage } from '@/hooks/components/use-image-preview'; +import { ChatMsgImageType } from '@polyv/live-watch-sdk'; +import { computed, nextTick, ref, unref } from 'vue'; +import { CSSProperties } from 'vue/types/jsx'; +import { useChatMsgComputedInject } from '../_hooks/chat-msg-computed-hook'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +export interface ImageSizeType { + width: number; + height: number; +} + +const recordImageSize: UniversalParams = {}; + +// 图片最小显示宽度 +const minPicWidth = isMobile ? 48 : 44; +// 图片最小高度 +const minPicHeight = isMobile ? 48 : 25; +// 图片最大显示宽度 +const maxPicWidth = isMobile ? 250 : 258; +// 图片最大显示高度 +const maxPicHeight = isMobile ? 140 : 144; +// 纵向超长图比例(高/宽) +const portraitLongPicRatio = 2.25; +// 横向超长图比例(高/宽) +const landscapeLongPicRatio = 0.14; + +export const useImageMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + const { containerRef, getVirtualList } = useChatMsgComputedInject(); + + const imageRef = ref(); + + /** 图片地址 */ + const imageUrl = computed(() => { + return resetImageProtocol(unref(chatMsg).imageUrl); + }); + + /** 图片宽高 */ + const imageSize = ref( + recordImageSize[imageUrl.value] ?? chatMsg.value.size, + ); + + /** 图片高宽比 */ + const imageRatio = computed(() => { + if (!imageSize.value) { + return 72 / 128; + } + return imageSize.value.height / imageSize.value.width; + }); + + /** 图片外层样式 */ + const wrapStyle = ref>({}); + /** 图片样式 */ + const imageStyle = ref>({}); + + /** 是否加载完成 */ + const isLoaded = ref(false); + + /** 计算样式 */ + // eslint-disable-next-line sonarjs/cognitive-complexity + function computedStyle() { + if (!imageSize.value) { + return; + } + const { width: imageWidth, height: imageHeight } = imageSize.value; + const ratio = imageHeight / imageWidth; + + const _wrapStyle: CSSProperties = {}; + const _imageStyle: CSSProperties = {}; + + if (ratio > 1) { + // 纵向图片 + if (ratio > portraitLongPicRatio) { + // 图片纵向超长 + if (imageHeight >= minPicHeight && imageHeight <= maxPicHeight) { + _imageStyle.height = `${imageHeight}px`; + } + + if (imageWidth >= minPicWidth && imageWidth <= maxPicWidth) { + _imageStyle.width = `${imageWidth}px`; + } + } + _imageStyle.maxWidth = `${maxPicWidth}px`; + _imageStyle.maxHeight = `${maxPicHeight}px`; + } else { + // 横向图片 + if (ratio < landscapeLongPicRatio) { + // 图片横向超长 + if (imageHeight >= minPicHeight && imageHeight <= maxPicHeight) { + _imageStyle.height = `${imageHeight}px`; + } + + if (imageWidth >= minPicWidth && imageWidth <= maxPicWidth) { + _imageStyle.width = `${imageWidth}px`; + } + + _imageStyle.maxWidth = `${maxPicWidth}px`; + _imageStyle.maxHeight = `${maxPicHeight}px`; + } else { + _imageStyle.maxWidth = '100%'; + _imageStyle.maxHeight = `${maxPicHeight}px`; + } + } + + _wrapStyle.maxWidth = `${maxPicWidth}px`; + _wrapStyle.maxHeight = `${maxPicHeight}px`; + _imageStyle.minWidth = `${minPicWidth}px`; + _imageStyle.minHeight = `${minPicHeight}px`; + + wrapStyle.value = _wrapStyle as UniversalParams; + imageStyle.value = _imageStyle as UniversalParams; + } + computedStyle(); + + /** 处理图片加载完成 */ + async function onImageLoad() { + const imageElem = unref(imageRef); + if (!imageElem) return; + + imageSize.value = { + width: imageElem.width, + height: imageElem.height, + }; + + recordImageSize[unref(imageUrl)] = imageSize.value; + + computedStyle(); + + isLoaded.value = true; + + /** 容器节点 */ + const container = unref(containerRef); + if (container) { + // 是否在底部 + const isBottom = container.scrollTop + container.clientHeight >= container.scrollHeight; + + await nextTick(); + + // 如果在设置图片显示之前滚动容器在最底部,则显示之后手动置底 + const virtualList = getVirtualList(); + if (isBottom && virtualList) { + virtualList.scrollToFoot(); + } + } + } + + function onClickImage() { + previewImage([unref(imageUrl)]); + } + + return { + chatMsg, + minPicWidth, + minPicHeight, + maxPicWidth, + maxPicHeight, + wrapStyle, + imageStyle, + imageRef, + imageSize, + imageUrl, + isLoaded, + imageRatio, + onImageLoad, + onClickImage, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/mobile-chat-msg-item.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/mobile-chat-msg-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..9341b255bd2bb275a1be31d0e7fb13a46ffc7ea0 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/mobile-chat-msg-item.vue @@ -0,0 +1,67 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/pc-chat-msg-item.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/pc-chat-msg-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..b9fcdfb42b8b9fbcde03ff346d858a53ea877b8f --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/pc-chat-msg-item.vue @@ -0,0 +1,65 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/portrait-chat-msg-item.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/portrait-chat-msg-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..3052523650a7c0b0f541693d169ae07ceca3aadb --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/portrait-chat-msg-item.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/imgs/icon_redpack.png b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/imgs/icon_redpack.png new file mode 100644 index 0000000000000000000000000000000000000000..98329d10802597ce5bdc9db1729f988f2233a972 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/imgs/icon_redpack.png differ diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/mobile-redpaper-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/mobile-redpaper-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..1b1def110d2e06a0ae8246b95888742641243dd5 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/mobile-redpaper-msg.vue @@ -0,0 +1,29 @@ + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/pc-redpaper-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/pc-redpaper-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..8cc266cdfeabd73839590b5e0c71559314e21106 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/pc-redpaper-msg.vue @@ -0,0 +1,53 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/portrait-redpaper-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/portrait-redpaper-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..54ae9465eaabc7e3c634372493f294046dec8c5e --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/portrait-redpaper-msg.vue @@ -0,0 +1,47 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/use-redpaper-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/use-redpaper-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..e009252e1d4c1e3a8a0cb9369ce4e8d10da08d19 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-msg/use-redpaper-msg.ts @@ -0,0 +1,87 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +import { computed, onBeforeUnmount, onMounted, ref, unref } from 'vue'; +import { eventBus, appEvents } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; +import { ChatMsgRedpaperType, RedpackType } from '@polyv/live-watch-sdk'; +import { RedpackStatusData } from '@/components/page-watch-common/interactive-receive/redpack-rain/types/redpack-rain-types'; + +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +interface RedpaperEntranceInstance { + /** 更新红包状态 */ + updateStatus(status: string): void; +} + +/** + * @hook 互动-红包-聊天室红包消息 + */ +export const useRedpaperMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + + const watchCore = getWatchCore(); + + /** 红包 SDK 实例 */ + const redpackSdk = watchCore.interactReceive.getRedpack(); + + /** 红包入口实例 */ + const entranceRef = ref(); + + /** 发送者的昵称 */ + const sendNick = computed(() => { + return chatMsg.value.user?.nick; + }); + + /** 红包文案 */ + const redpackText = computed(() => { + const type = chatMsg.value.type || RedpackType.Normal; + const redpackTexts: Record = { + [RedpackType.Normal]: translate('redpack.type.normal'), + [RedpackType.OfficialNormal]: translate('redpack.type.normal'), + [RedpackType.Password]: translate('redpack.type.password'), + [RedpackType.Rain]: translate('redpack.type.rain'), + [RedpackType.ScoreOfficialNormal]: translate('redpack.type.normal'), + [RedpackType.ScorePassword]: translate('redpack.type.password'), + [RedpackType.ScoreRain]: translate('redpack.type.rain'), + [RedpackType.AlipayPasswordOfficialNormal]: translate('redpack.type.alipayPassword'), + }; + + return redpackTexts[type] || translate('redpack.type.normal'); + }); + + /** 更新入口状态 */ + function updateEntranceStatus(data: RedpackStatusData) { + const { redpackId, state } = data; + if (redpackId !== unref(chatMsg).redpackId) { + return; + } + + const instance = unref(entranceRef); + if (instance && instance.updateStatus) { + instance.updateStatus(state); + } + } + + /** 打开红包弹层 */ + function openRedpackDialog() { + eventBus.$emit(appEvents.interaction.OpenRedpack, unref(chatMsg)); + } + + onMounted(() => { + eventBus.$on(appEvents.interaction.SetRedpackStatus, updateEntranceStatus); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.SetRedpackStatus, updateEntranceStatus); + }); + + return { + chatMsg, + entranceRef, + redpackSdk, + sendNick, + redpackText, + openRedpackDialog, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/imgs/redpaper-msg-ico.png b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/imgs/redpaper-msg-ico.png new file mode 100644 index 0000000000000000000000000000000000000000..623989bf9f6f430bd25bfe5011d3e1b52f84e641 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/imgs/redpaper-msg-ico.png differ diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/mobile-redpaper-receive-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/mobile-redpaper-receive-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..99f0429a187341270b82dd7a845543ca03fb4348 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/mobile-redpaper-receive-msg.vue @@ -0,0 +1,63 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/pc-redpaper-receive-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/pc-redpaper-receive-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..9e04f8a83caede4af3454d25249fe9ca9d2e0e53 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/pc-redpaper-receive-msg.vue @@ -0,0 +1,60 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/portrait-redpaper-receive-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/portrait-redpaper-receive-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..caa289f9b834a4ce192b12ce95f1a45fd04be37c --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/portrait-redpaper-receive-msg.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/use-redpaper-receive-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/use-redpaper-receive-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..e73c39c6df176e327ce7a083efc5356f0e342e4e --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/redpaper-receive-msg/use-redpaper-receive-msg.ts @@ -0,0 +1,34 @@ +import { ChatMsgRedpaperReceiveType } from '@polyv/live-watch-sdk'; +import { computed, unref } from 'vue'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +export const useRedpaperReceiveMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + + /** 领取人昵称 */ + const receiveNick = computed(() => { + return unref(chatMsg).nick; + }); + + /** 是否红包雨 */ + const isRedpaperRain = computed(() => { + return unref(chatMsg).type.includes('rain'); + }); + + /** 是否口令红包 */ + const isRedpaperPassword = computed(() => { + return unref(chatMsg).type.includes('password'); + }); + + /** 是否领取完 */ + const isOver = computed(() => { + return unref(chatMsg).isOver; + }); + + return { + receiveNick, + isRedpaperRain, + isRedpaperPassword, + isOver, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/imgs/icon-redpaper.png b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/imgs/icon-redpaper.png new file mode 100644 index 0000000000000000000000000000000000000000..08a05c1be55f9333f3028b8d2949f20c64fa6708 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/imgs/icon-redpaper.png differ diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/mobile-reward-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/mobile-reward-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..586fb476813f29950acbbcec5653a7ba0862b6ea --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/mobile-reward-msg.vue @@ -0,0 +1,71 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/pc-reward-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/pc-reward-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..393d8aa57414cee9af67e76220d0134e03c7f9c0 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/pc-reward-msg.vue @@ -0,0 +1,72 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/portrait-reward-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/portrait-reward-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..0c50eb48c6ab15e5178f2b9864d21f7177f709f9 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/reward-msg/portrait-reward-msg.vue @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/mobile-speak-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/mobile-speak-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..75f05a398a028fdc0dc630c2d1659f4ac8842835 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/mobile-speak-msg.vue @@ -0,0 +1,199 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/pc-speak-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/pc-speak-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..8f9356dfb9bf2278da1037fc759fa7b9af29327e --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/pc-speak-msg.vue @@ -0,0 +1,182 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/portrait-speak-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/portrait-speak-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..bd899cca92eb1635fe48bd635ed65992ca1e66ff --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/portrait-speak-msg.vue @@ -0,0 +1,132 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/use-speak-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/use-speak-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..c5df62b7377f3e59381c3db267dd02bc71af801d --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/speak-msg/use-speak-msg.ts @@ -0,0 +1,234 @@ +import { ChatMsgSpeakType, YN } from '@polyv/live-watch-sdk'; +import { computed, ref, unref } from 'vue'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; +import { parseEmotions, removeEmotions } from '@polyv/emotion-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; +import { copyText } from '@/assets/utils/copy'; +import { toast } from '@/hooks/components/use-toast'; +import { translate } from '@/assets/lang'; +import { appEvents, eventBus } from '@/app/app-events'; +import { FullMsgData } from '../../../chat-full-message/type'; +import { useChatMsgUserBasic } from '../_hooks/chat-msg-user-hook'; +import { isString, parseLinkContent } from '@/assets/utils/string'; +import { useChannelStore } from '@/store/use-channel-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { TAB_NAME_ASK } from '@/assets/constants/tab-name'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +/** + * 转换发言消息的内容 + * @param options + */ +export function parseSpeakContent(options: { + /** 消息内容 */ + content: string; + /** 是否转换链接,默认:true */ + parseLink?: boolean; + /** 是否转换表情,默认:true */ + parseEmotion?: boolean; +}) { + const { content, parseLink = true, parseEmotion } = options; + let newContent = content; + + // 转换链接 + if (parseLink) { + newContent = parseLinkContent(newContent); + } + + // 转换表情 + if (parseEmotion) { + newContent = parseEmotions(newContent); + } + + return newContent; +} + +/** 转换引用的 speak 内容 */ +export function parseQuoteSpeakContent(content: string) { + return parseSpeakContent({ + content, + parseLink: false, + parseEmotion: true, + }); +} + +/** + * 发言消息内容 hook + * @param chatMsgRef speak 聊天消息响应式 + */ +export const useSpeakMsgContent = (chatMsgRef: ResponsiveRef) => { + const { isSpecialUser } = useChatMsgUserBasic(chatMsgRef); + + /** 原始的发言内容 */ + const speakContent = computed(() => { + return unref(chatMsgRef).content; + }); + + /** 用于渲染的发言 html 内容 */ + const speakHtmlContent = computed(() => { + const content = unref(chatMsgRef).content; + + return parseSpeakContent({ + content, + parseLink: unref(isSpecialUser), + parseEmotion: true, + }); + }); + + /** 去除掉表情的发言内容 */ + const cleanSpeakContent = computed(() => { + const contentVal = unref(speakContent); + if (contentVal.indexOf('<') < 0) { + return contentVal; + } + return contentVal.replace(/]*>/gi, ''); + }); + + return { + speakContent, + speakHtmlContent, + cleanSpeakContent, + }; +}; + +/** + * 发言消息翻译 hook + * @param chatMsgRef + */ +export const useSpeakMsgTranslate = (chatMsgRef: ResponsiveRef) => { + const channelStore = useChannelStore(); + const layoutStore = useLayoutStore(); + const { isSelfMsg } = useChatMsgUserBasic(chatMsgRef); + + /** 用于翻译的内容 */ + const translateOriginContent = computed(() => { + let content = unref(chatMsgRef).content; + + // 移除 emotion 表情 + content = removeEmotions(content); + + return content; + }); + + /** 能否进行翻译 */ + const canTranslate = computed(() => { + // 翻译开关 + if (!ynToBool(channelStore.channelDetail?.channelConfig.chatTranslateEnabled, YN.N)) { + return false; + } + + // 自己发言不需要翻译 + if (unref(isSelfMsg)) { + return false; + } + + // 长文不需要翻译 + if (unref(chatMsgRef).isOverLength) { + return false; + } + + // 当前处于提问不需要翻译 + if ( + layoutStore.mobileMenuCurrentName === TAB_NAME_ASK || + layoutStore.pcAsideTabCurrentName === TAB_NAME_ASK + ) { + return false; + } + + const content = unref(translateOriginContent); + return /[\u4E00-\u9FA5]|[\uFE30-\uFFA0]/gi.test(content) || content.search(/[a-zA-Z]+/) >= 0; + }); + + /** 翻译后的内容 */ + const translatedContent = ref(); + + /** 是否翻译中 */ + const isTranslating = ref(false); + + /** 翻译当前消息 */ + async function translateCurrentMsg() { + // 防止重复请求 + if (isTranslating.value) { + return; + } + + // 已翻译过,不再翻译 + if (isString(translatedContent.value)) { + return; + } + + const watchCore = getWatchCore(); + + const content = unref(translateOriginContent); + + isTranslating.value = true; + try { + const result = await watchCore.utils.translateText(content); + translatedContent.value = result; + isTranslating.value = false; + } catch (e) { + isTranslating.value = false; + } + } + + return { + translatedContent, + canTranslate, + isTranslating, + translateCurrentMsg, + }; +}; + +export const useSpeakMsg = () => { + const watchAppStore = useWatchAppStore(); + const { chatMsg, msgQuote } = useChatMsgDataInject(); + + const { speakContent, speakHtmlContent } = useSpeakMsgContent(chatMsg); + + /** + * 获取完整的消息内容 + */ + async function getFullSpeakContent() { + if (!unref(chatMsg).isOverLength || watchAppStore.isWatchBackUrl) { + return unref(chatMsg).content; + } + const id = unref(chatMsg).id; + const watchCore = getWatchCore(); + + return watchCore.chat.getFullMessage(id); + } + + /** 复制当前消息 */ + async function copyMsg() { + let copyContent = unref(chatMsg).content; + + // 长文消息 + if (unref(chatMsg).isOverLength) { + copyContent = await getFullSpeakContent(); + } + + await copyText(copyContent); + toast.success(translate('copy.success')); + } + + /** 显示完整的消息 */ + async function showCompleteMsg() { + const fullContent = await getFullSpeakContent(); + const data: FullMsgData = { + fullContent, + chatMsg: chatMsg.value, + }; + eventBus.$emit(appEvents.chat.ShowFullMessage, data); + } + + return { + chatMsg, + msgQuote, + speakContent, + speakHtmlContent, + getFullSpeakContent, + copyMsg, + showCompleteMsg, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/mobile-system-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/mobile-system-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..b938224914ad458be8327faf6185bf9f0422c85a --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/mobile-system-msg.vue @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/pc-system-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/pc-system-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..90d6296cd6c7543fd108a238aea1dbc25f477fde --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/pc-system-msg.vue @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/portrait-system-msg.vue b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/portrait-system-msg.vue new file mode 100644 index 0000000000000000000000000000000000000000..52c0a40dc7084e54ed97e7f784399fe983568409 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/portrait-system-msg.vue @@ -0,0 +1,16 @@ + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/use-system-msg.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/use-system-msg.ts new file mode 100644 index 0000000000000000000000000000000000000000..8bf70bd2361736bc14d1cd791ba8e7e9733b25d3 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/system-msg/use-system-msg.ts @@ -0,0 +1,10 @@ +import { ChatMsgSystemType } from '@polyv/live-watch-sdk'; +import { useChatMsgDataInject } from '../_hooks/chat-msg-data-hook'; + +export const useSystemMsg = () => { + const { chatMsg } = useChatMsgDataInject(); + + return { + chatMsg, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/msg-items/use-chat-msg-item.ts b/src/components/page-watch-common/chat/chat-msg-list/msg-items/use-chat-msg-item.ts new file mode 100644 index 0000000000000000000000000000000000000000..a62d045c63f272f243448de68a2950b464d88bb5 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/msg-items/use-chat-msg-item.ts @@ -0,0 +1,105 @@ +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; +import { computed, ExtractPropTypes, unref } from 'vue'; +import { GetVirtualListFn } from '../hooks/types'; +import { useChatMsgComputedProvide } from './_hooks/chat-msg-computed-hook'; +import { useChatMsgData } from './_hooks/chat-msg-data-hook'; +import { useChatMsgTime } from './_hooks/chat-msg-time-hook'; +import { useChatMsgUser } from './_hooks/chat-msg-user-hook'; + +export const chatMsgItemProps = () => ({ + /** 消息滚动容器 */ + containerElem: PropUtils.oneOfType([HTMLDivElement]), + /** 获取虚拟列表方法 */ + getVirtualList: PropUtils.func(), + /** 聊天消息 */ + chatMsg: PropUtils.object().isRequired, + /** 上一条消息 */ + prevChatMsg: PropUtils.object(), + /** 下一条消息 */ + nextChatMsg: PropUtils.object(), +}); + +export type ChatMsgItemPropsType = ExtractPropTypes>; + +export const useChatMsgItem = (options: { props: VueProps }) => { + const { props } = options; + const chatMsgStore = useChatMsgStore(); + + // 消息数据 + const chatMsgDataContext = useChatMsgData(props); + // 消息用户 + const { msgUser } = useChatMsgUser(chatMsgDataContext); + // 消息时间 + const { showMsgTime, msgTimeText } = useChatMsgTime(chatMsgDataContext); + + const { chatMsg, prevChatMsg, nextChatMsg, msgId } = chatMsgDataContext; + + /** 滚动容器 */ + const containerRef = computed(() => props.containerElem); + + /** + * 是否为同一个人连续发送的消息 + */ + const isContinuityMsg = computed(() => { + const prevMsg = unref(prevChatMsg); + const msg = unref(chatMsg); + + // 没有上一条消息 + if (!prevMsg) return false; + // 显示时间 + if (unref(showMsgTime)) return false; + // 与上一条消息类型不同 + if (prevMsg.msgSource !== msg.msgSource) return false; + + // 与上一条消息属于同一个人 + return ( + chatMsgStore.getChatMsgUser(msg)?.userId === chatMsgStore.getChatMsgUser(prevMsg)?.userId + ); + }); + + /** + * 是否为连续相同的消息来源 + */ + const isContinuitySourceMsg = computed(() => { + // 显示时间 + if (unref(showMsgTime)) return false; + + const prevMsg = unref(prevChatMsg); + const msg = unref(chatMsg); + return prevMsg?.msgSource === msg.msgSource; + }); + + /** + * 是否显示当前消息 + */ + const msgVisible = computed(() => { + return !chatMsgStore.hideChatMsgSources.includes(unref(chatMsg).msgSource); + }); + + function getVirtualList() { + if (props.getVirtualList) { + return props.getVirtualList(); + } + } + + useChatMsgComputedProvide({ + containerRef, + isContinuityMsg, + isContinuitySourceMsg, + getVirtualList, + }); + + return { + chatMsg, + prevChatMsg, + nextChatMsg, + msgId, + msgUser, + showMsgTime, + msgTimeText, + isContinuityMsg, + msgVisible, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-list/pc-chat-msg-list.vue b/src/components/page-watch-common/chat/chat-msg-list/pc-chat-msg-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..981e9c97e0da559a9d6147d89e54ee1bc6f01006 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/pc-chat-msg-list.vue @@ -0,0 +1,86 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/portrait-chat-msg-list.vue b/src/components/page-watch-common/chat/chat-msg-list/portrait-chat-msg-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..ebb912c67a71982cee53bb3342cd5a461f9f6b42 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/portrait-chat-msg-list.vue @@ -0,0 +1,73 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-list/styles/index.scss b/src/components/page-watch-common/chat/chat-msg-list/styles/index.scss new file mode 100644 index 0000000000000000000000000000000000000000..5ec7d2db5e1eddf711ffb54204525e5cd03177ed --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/styles/index.scss @@ -0,0 +1,25 @@ +@import '../msg-items/_styles/vars.scss'; + +// 聊天室列表提示样式 +.c-chat-msg-list__tips { + margin: $--margin-vertical-margin 0; + text-align: center; +} + +// 聊天室列表加载中样式 +.c-chat-msg-list__loading { + width: 100%; + height: 32px; + overflow: hidden; + &::after { + display: block; + width: 32px; + height: 32px; + margin: 0 auto; + content: ''; + background-image: url(../imgs/loading.png); + background-position: center; + background-size: cover; + animation: spin 1s ease infinite; + } +} diff --git a/src/components/page-watch-common/chat/chat-msg-list/use-chat-msg-list.ts b/src/components/page-watch-common/chat/chat-msg-list/use-chat-msg-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..ba68729c2c6604a80a4de1d4794942b9ebc227da --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-list/use-chat-msg-list.ts @@ -0,0 +1,227 @@ +import { appEvents, eventBus, useEventBusListener } from '@/app/app-events'; +import { TAB_NAME_CHAT } from '@/assets/constants/tab-name'; +import { useChatMsgStore } from '@/store/use-chat-msg-store'; +import { useChatStore } from '@/store/use-chat-store'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { VirtualList } from '@just4/virtual-list'; +import { ItemsRemoveEvent, VirtualListEvent } from '@just4/virtual-list/events'; +import { ChatMsgType } from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, onMounted, ref, unref, watch } from 'vue'; +import { useChatMsgHandle } from './hooks/chat-msg-handle-hook'; +import { useHideChatMsgHook } from './hooks/hide-chat-msg-hook'; +import { useChatMsgQueue } from './hooks/msg-queue-hook'; +import { ChatMsgItemComponentOptions, useMsgRender } from './hooks/msg-render-hook'; +import { useRequestHistory } from './hooks/request-history-hook'; +import { useRequestHistorySession } from './hooks/request-history-session-hook'; +import { ChatMsgListHookOptions } from './hooks/types'; +import { RenderPosition } from '@just4/virtual-list/types'; +import { isMobile } from '@/assets/utils/browser'; + +/** + * 聊天室消息列表 hook + */ +// eslint-disable-next-line sonarjs/cognitive-complexity +export const useChatMsgList = (options: { ChatMsgItemComponent: ChatMsgItemComponentOptions }) => { + const watchAppStore = useWatchAppStore(); + const chatStore = useChatStore(); + const chatMsgStore = useChatMsgStore(); + const layoutStore = useLayoutStore(); + + const { ChatMsgItemComponent } = options; + + const hookOptions: ChatMsgListHookOptions = { + getContainerElem, + getVirtualList, + }; + + // 聊天消息渲染 + const { renderItems, renderBoundary, renderLoading, renderError, renderEmpty } = useMsgRender( + ChatMsgItemComponent, + hookOptions, + ); + + const requestHistoryHookFunc = watchAppStore.isWatchBackUrl + ? useRequestHistorySession + : useRequestHistory; + + // 聊天消息加载 + const { loadInitialData, loadNextData, loadPreviousData } = requestHistoryHookFunc(hookOptions); + // 聊天消息队列 + useChatMsgQueue(hookOptions); + // 聊天消息处理 + useChatMsgHandle(hookOptions); + // 隐藏聊天消息 + useHideChatMsgHook(); + + /** 聊天消息容器节点 */ + const containerRef = ref(); + + /** + * 获取消息容器节点 + */ + function getContainerElem() { + return unref(containerRef) as HTMLDivElement; + } + + /** ---------- 虚拟列表 start ---------- */ + let virtualList: VirtualList | undefined; + + /** + * 获取虚拟列表实例 + */ + function getVirtualList(): VirtualList { + if (!virtualList) { + throw new Error('getVirtualList error'); + } + return virtualList; + } + + /** + * 初始化虚拟列表 + */ + function initVirtualList() { + destroyVirtualList(); + + const containerElem = getContainerElem(); + if (!containerElem) return; + + if (!chatStore.chatMsgListEnabled) return; + + virtualList = new VirtualList({ + container: containerElem, + itemKey: 'id', + prefetchDistance: 1, + defaultView: 'foot', + dataSource: { + loadInitialData, + loadNextData, + loadPreviousData, + }, + renderer: { + renderItems, + renderBoundary, + renderLoading, + renderError, + renderEmpty, + }, + }); + + /** + * dom 节点通过实例化 Vue 来创建 + * 移除时,需要手动销毁实例,否则不会被回收掉,高并发下会占用很高内存 + */ + virtualList.on(VirtualListEvent.ITEM_REMOVE, (data: unknown) => { + const result = data as ItemsRemoveEvent; + const itemNodes = result.itemNodes; + itemNodes.forEach(elem => { + const domElem = elem as unknown as HTMLElement; + const vueVm = domElem.__vueVm; + if (vueVm) { + vueVm.$destroy(); + } + }); + }); + } + + /** + * 销毁虚拟列表 + */ + function destroyVirtualList() { + if (virtualList) { + virtualList.destroy(); + virtualList = undefined; + } + } + + /** + * 重新请求最新消息并渲染 + */ + async function initChatMsgRender() { + if (!virtualList) { + return; + } + + const result = await loadInitialData(); + if (!result.data) { + return; + } + + await virtualList.clear(); + virtualList.addBoundaryItems(result.data, RenderPosition.Foot, true); + virtualList.scrollToFoot(); + + /** clear 之后会变为边界状态,这里要手动重置头部边界,不然不会加载旧消息 */ + virtualList.resetBoundaryState(RenderPosition.Head); + } + + /** 滚动到最新位置 */ + async function scrollToNew() { + console.log('scrollToNew'); + if (!virtualList) { + return; + } + chatMsgStore.hideMoreMsg(); + + const lastItem = virtualList.items.last(); + if (lastItem && chatMsgStore.getRealIndexById(lastItem.id) === 0) { + virtualList.scrollToFoot(); + } else { + /** + * 这里不用 virtualList.refresh() 的原因: + * 该 api 会先清空节点后请求数据,请求期间会有明显的空屏现象,体验不佳 + */ + initChatMsgRender(); + } + } + + useEventBusListener(appEvents.chat.ResetUpChatMsgList, () => { + initVirtualList(); + }); + + onMounted(() => { + initVirtualList(); + eventBus.$on(appEvents.chat.ScrollToNew, scrollToNew); + }); + + onBeforeUnmount(() => { + destroyVirtualList(); + eventBus.$on(appEvents.chat.ScrollToNew, scrollToNew); + }); + /** ---------- 虚拟列表 end ---------- */ + + watch( + () => chatStore.onlySpecialMsg, + () => { + virtualList?.refresh(); + }, + ); + + // 切到聊天室 Tab 时自动滚到最底部 + // 首次打开聊天室时 oldVal 为空,此时不触发,因为会跟虚拟列表初始化冲突,会导致首屏消息重复 + if (isMobile) { + watch( + () => layoutStore.mobileMenuCurrentName, + (newVal, oldVal) => { + if (oldVal && layoutStore.mobileMenuCurrentName === TAB_NAME_CHAT) { + scrollToNew(); + } + }, + ); + } else { + watch( + () => layoutStore.pcAsideTabCurrentName, + (newVal, oldVal) => { + if (oldVal && layoutStore.pcAsideTabCurrentName === TAB_NAME_CHAT) { + scrollToNew(); + } + }, + ); + } + + return { + containerRef, + getVirtualList, + scrollToNew, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-msg-quote/mobile-chat-msg-quote.vue b/src/components/page-watch-common/chat/chat-msg-quote/mobile-chat-msg-quote.vue new file mode 100644 index 0000000000000000000000000000000000000000..9fa2744a3803c0ab577070584b297543f1dd83df --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-quote/mobile-chat-msg-quote.vue @@ -0,0 +1,59 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-quote/pc-chat-msg-quote.vue b/src/components/page-watch-common/chat/chat-msg-quote/pc-chat-msg-quote.vue new file mode 100644 index 0000000000000000000000000000000000000000..67855adf2fd57b5bebf213f1a1b15b562aa6e3e7 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-quote/pc-chat-msg-quote.vue @@ -0,0 +1,57 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-msg-quote/use-chat-msg-quote.ts b/src/components/page-watch-common/chat/chat-msg-quote/use-chat-msg-quote.ts new file mode 100644 index 0000000000000000000000000000000000000000..46e6c832073fadbd53d1bbd83d630a9872e3e207 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-msg-quote/use-chat-msg-quote.ts @@ -0,0 +1,23 @@ +import { useChatStore } from '@/store/use-chat-store'; +import { storeToRefs } from 'pinia'; +import { useChatMsgUserBasic } from '../chat-msg-list/msg-items/_hooks/chat-msg-user-hook'; + +export const useChatMsgQuoteControl = () => { + const chatStore = useChatStore(); + const { currentQuoteMsg } = storeToRefs(chatStore); + + const { msgUserNick } = useChatMsgUserBasic(currentQuoteMsg); + + /** 点击关闭 */ + function onClickClose() { + chatStore.$patch({ + currentQuoteMsg: undefined, + }); + } + + return { + currentQuoteMsg, + msgUserNick, + onClickClose, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-room-pendant/mobile-chat-room-pendant.vue b/src/components/page-watch-common/chat/chat-room-pendant/mobile-chat-room-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..1ff6cd9f99a6aaf0683251f617dc70aee982053b --- /dev/null +++ b/src/components/page-watch-common/chat/chat-room-pendant/mobile-chat-room-pendant.vue @@ -0,0 +1,151 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-room-pendant/pc-chat-room-pendant.vue b/src/components/page-watch-common/chat/chat-room-pendant/pc-chat-room-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..93be47e11ccb84b8c12a48996b05256ed6b702cf --- /dev/null +++ b/src/components/page-watch-common/chat/chat-room-pendant/pc-chat-room-pendant.vue @@ -0,0 +1,97 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-room-pendant/portrait-chat-room-pendant.vue b/src/components/page-watch-common/chat/chat-room-pendant/portrait-chat-room-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..6d06d1613161705ea38afb0c991a5ecf73905bee --- /dev/null +++ b/src/components/page-watch-common/chat/chat-room-pendant/portrait-chat-room-pendant.vue @@ -0,0 +1,108 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-room/mobile-chat-room.vue b/src/components/page-watch-common/chat/chat-room/mobile-chat-room.vue new file mode 100644 index 0000000000000000000000000000000000000000..3a78ad15d07250b33df81728f39b1a1478be04e8 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-room/mobile-chat-room.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-room/pc-chat-room.vue b/src/components/page-watch-common/chat/chat-room/pc-chat-room.vue new file mode 100644 index 0000000000000000000000000000000000000000..02332f592be083f203406a41b0b0d7134aae2b4e --- /dev/null +++ b/src/components/page-watch-common/chat/chat-room/pc-chat-room.vue @@ -0,0 +1,115 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-set-nick/mobile-chat-set-nick.vue b/src/components/page-watch-common/chat/chat-set-nick/mobile-chat-set-nick.vue new file mode 100644 index 0000000000000000000000000000000000000000..0d92209081a91697dc35ea0536da567c610f9eab --- /dev/null +++ b/src/components/page-watch-common/chat/chat-set-nick/mobile-chat-set-nick.vue @@ -0,0 +1,49 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-set-nick/pc-chat-set-nick.vue b/src/components/page-watch-common/chat/chat-set-nick/pc-chat-set-nick.vue new file mode 100644 index 0000000000000000000000000000000000000000..517388169b91a6f737ba528aee65ffe2ae891793 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-set-nick/pc-chat-set-nick.vue @@ -0,0 +1,72 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-set-nick/portrait-chat-set-nick.vue b/src/components/page-watch-common/chat/chat-set-nick/portrait-chat-set-nick.vue new file mode 100644 index 0000000000000000000000000000000000000000..a344239570df4d329a6f820d9995f811891224f3 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-set-nick/portrait-chat-set-nick.vue @@ -0,0 +1,61 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-set-nick/use-chat-set-nick.ts b/src/components/page-watch-common/chat/chat-set-nick/use-chat-set-nick.ts new file mode 100644 index 0000000000000000000000000000000000000000..49d89ffce1f58aac4647a15fa8497d52fd3f36d8 --- /dev/null +++ b/src/components/page-watch-common/chat/chat-set-nick/use-chat-set-nick.ts @@ -0,0 +1,94 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { translate } from '@/assets/lang'; +import { validateSpecialString } from '@/assets/utils/validate'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { UserEvents } from '@polyv/live-watch-sdk'; +import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'; + +/** + * 设置昵称 hook + */ +export const useChatSetNick = () => { + /** 显示状态 */ + const dialogVisible = ref(false); + + /** 表单 */ + const formData = reactive({ + nickname: '', + }); + + const formRules = computed(() => { + return { + nickname: [ + { type: 'string', message: translate('chat.setNick.error.empty'), required: true }, + { + validator: () => { + if (validateSpecialString(formData.nickname)) { + return [translate('chat.setNick.error.special')]; + } + return []; + }, + }, + ], + }; + }); + + /** 设置弹层状态 */ + function setDialogVisible(visible = true) { + dialogVisible.value = visible; + } + + /** 提交表单 */ + function submitSetNick() { + const watchCore = getWatchCore(); + watchCore.user.setCurrentUserNick(formData.nickname); + listenSetNickEvent(); + } + + /** 处理设置昵称成功 */ + function onSetNickSuccess() { + toast.success(translate('chat.setNick.success')); + setDialogVisible(false); + unlistenSetNickEvent(); + } + + /** 处理设置昵称失败 */ + function onSetNickError(evt: { message: string }) { + toast.error(evt.message); + unlistenSetNickEvent(); + } + + /** 监听设置昵称事件 */ + function listenSetNickEvent() { + unlistenSetNickEvent(); + const watchCore = getWatchCore(); + watchCore.user.eventEmitter.on(UserEvents.CurrentUserSetNick, onSetNickSuccess); + watchCore.user.eventEmitter.on(UserEvents.SetNickError, onSetNickError); + } + + /** 移除事件监听 */ + function unlistenSetNickEvent() { + const watchCore = getWatchCore(); + watchCore.user.eventEmitter.off(UserEvents.CurrentUserSetNick, onSetNickSuccess); + watchCore.user.eventEmitter.off(UserEvents.SetNickError, onSetNickError); + } + + onMounted(() => { + eventBus.$on(appEvents.chat.OpenSetNick, setDialogVisible); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.chat.OpenSetNick, setDialogVisible); + unlistenSetNickEvent(); + }); + + return { + dialogVisible, + setDialogVisible, + formData, + formRules, + submitSetNick, + }; +}; diff --git a/src/components/page-watch-common/chat/chat-welcome/chat-welcome-tips.vue b/src/components/page-watch-common/chat/chat-welcome/chat-welcome-tips.vue new file mode 100644 index 0000000000000000000000000000000000000000..5affaae852209bfb4a630cd1262fbba66647c20a --- /dev/null +++ b/src/components/page-watch-common/chat/chat-welcome/chat-welcome-tips.vue @@ -0,0 +1,99 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-welcome/imgs/bg-portrait.png b/src/components/page-watch-common/chat/chat-welcome/imgs/bg-portrait.png new file mode 100644 index 0000000000000000000000000000000000000000..ce0ac202cae02f7ffb00336d2a7e3865af5d3404 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-welcome/imgs/bg-portrait.png differ diff --git a/src/components/page-watch-common/chat/chat-welcome/imgs/bg.png b/src/components/page-watch-common/chat/chat-welcome/imgs/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3a6df9826a9fc1886d7379986330cb1924fcf6 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-welcome/imgs/bg.png differ diff --git a/src/components/page-watch-common/chat/chat-welcome/imgs/icon-start-left.png b/src/components/page-watch-common/chat/chat-welcome/imgs/icon-start-left.png new file mode 100644 index 0000000000000000000000000000000000000000..c56612dfb77b486dc24466fe905b534e470337c7 Binary files /dev/null and b/src/components/page-watch-common/chat/chat-welcome/imgs/icon-start-left.png differ diff --git a/src/components/page-watch-common/chat/chat-welcome/imgs/icon-start-right.png b/src/components/page-watch-common/chat/chat-welcome/imgs/icon-start-right.png new file mode 100644 index 0000000000000000000000000000000000000000..ffdc67788ec2638a1f39cafca5ff46fb29f88f3a Binary files /dev/null and b/src/components/page-watch-common/chat/chat-welcome/imgs/icon-start-right.png differ diff --git a/src/components/page-watch-common/chat/chat-welcome/portrait-chat-welcome-tips.vue b/src/components/page-watch-common/chat/chat-welcome/portrait-chat-welcome-tips.vue new file mode 100644 index 0000000000000000000000000000000000000000..361c1368bc10d44797719422d45ab6ee92c515da --- /dev/null +++ b/src/components/page-watch-common/chat/chat-welcome/portrait-chat-welcome-tips.vue @@ -0,0 +1,51 @@ + + + + + + diff --git a/src/components/page-watch-common/chat/chat-welcome/use-chat-welcome-tips.ts b/src/components/page-watch-common/chat/chat-welcome/use-chat-welcome-tips.ts new file mode 100644 index 0000000000000000000000000000000000000000..b179996584e1c9b760cfc347b3f998f4e427eb3c --- /dev/null +++ b/src/components/page-watch-common/chat/chat-welcome/use-chat-welcome-tips.ts @@ -0,0 +1,132 @@ +/** + * @file 欢迎语相关 hook + */ +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChatStore } from '@/store/use-chat-store'; +import { useLangStore } from '@/store/use-lang-store'; +import { ChatEvents, ChatMessageUser, UserLoginMsgType } from '@polyv/live-watch-sdk'; +import { computed, onBeforeUnmount, onMounted, ref, unref } from 'vue'; + +/** 最大显示数量 */ +const maxCount = 10; +/** 文案上最大显示数量 */ +const maxShowCount = 3; + +/** 欢迎语提示 hook */ +export const useChatWelcomeTips = () => { + const chatStore = useChatStore(); + const langStore = useLangStore(); + + /** 当前正在显示的观众 */ + const showingUsers = ref([]); + /** 等待显示的观众 */ + const waitingUsers = ref([]); + + /** 显示的欢迎文案 */ + const welcomeText = ref(''); + + function setWelcomeText() { + let text = ''; + + const len = unref(showingUsers).length; + const nicks: string[] = []; + + unref(showingUsers) + .reverse() + .forEach((user, index) => { + if (index < maxShowCount) { + nicks.push(langStore.translateNick(user.nick)); + } + }); + + text = nicks.join('、'); + + if (len > maxShowCount) { + text = `${text} ${translate('welcome.wait')}${len}${translate('welcome.peopleJoin')}`; + } else { + text = `${text} ${translate('welcome.join')}`; + } + + welcomeText.value = text; + } + + /** 欢迎语是否显示 */ + const welcomeVisible = computed(() => showingUsers.value.length !== 0); + + /** 隐藏定时器 */ + let hideTimer: number | undefined; + + /** 设置隐藏定时器 */ + function setHideTimer() { + hideTimer = window.setTimeout(() => { + showingUsers.value = []; + clearHideTimer(); + resetShowUser(); + }, 3000); + } + /** 清空隐藏定时器 */ + function clearHideTimer() { + clearTimeout(hideTimer); + hideTimer = undefined; + } + + /** 重置显示的用户 */ + function resetShowUser() { + clearHideTimer(); + + const surplusCount = maxCount - showingUsers.value.length; + const joinUsers = waitingUsers.value.splice(0, surplusCount); + + if (joinUsers.length) { + showingUsers.value.push(...joinUsers); + setWelcomeText(); + } + + if (showingUsers.value.length) { + setHideTimer(); + } + } + + /** 处理聊天室登录事件 */ + function onUserLoginEvent(event: { userLoginMsg: UserLoginMsgType }) { + const user = event.userLoginMsg.user; + if (!user) return; + + // 正在两个列表中的用户不再插入 + const showIndex = showingUsers.value.findIndex(_user => _user.userId === user.userId); + const waitIndex = waitingUsers.value.findIndex(_user => _user.userId === user.userId); + if (showIndex !== -1 || waitIndex !== -1) return; + + // 显示中的数量小于 10,直接显示否则进入等待队列 + if (showingUsers.value.length < maxCount) { + showingUsers.value.push(user); + } else { + waitingUsers.value.push(user); + } + + resetShowUser(); + setWelcomeText(); + } + + onMounted(() => { + if (chatStore.welcomeEnabled) { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatUserLogin, onUserLoginEvent); + } + }); + + onBeforeUnmount(() => { + if (chatStore.welcomeEnabled) { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatUserLogin, onUserLoginEvent); + } + clearHideTimer(); + }); + + return { + showingUsers, + welcomeText, + welcomeVisible, + }; +}; diff --git a/src/components/page-watch-common/connect-mic/common/connect-mic-info.vue b/src/components/page-watch-common/connect-mic/common/connect-mic-info.vue new file mode 100644 index 0000000000000000000000000000000000000000..7571dbff05ab69baea364dcb12a49c756e5bfa29 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/common/connect-mic-info.vue @@ -0,0 +1,62 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/common/connect-mic-network.vue b/src/components/page-watch-common/connect-mic/common/connect-mic-network.vue new file mode 100644 index 0000000000000000000000000000000000000000..4054783b24ddd535606ed4daf2cc0683a3a43af2 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/common/connect-mic-network.vue @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/common/connect-microphone.vue b/src/components/page-watch-common/connect-mic/common/connect-microphone.vue new file mode 100644 index 0000000000000000000000000000000000000000..169b691768ddf44f4987f5e1ae535243abf8d8c1 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/common/connect-microphone.vue @@ -0,0 +1,58 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/hooks/use-connect-mic-item.ts b/src/components/page-watch-common/connect-mic/hooks/use-connect-mic-item.ts new file mode 100644 index 0000000000000000000000000000000000000000..f981a777ecea0c621859f1a4b63420fd55d38839 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/hooks/use-connect-mic-item.ts @@ -0,0 +1,112 @@ +import { translate } from '@/assets/lang'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ConnectMicItem } from '@polyv/live-watch-sdk'; +import { computed, nextTick, onBeforeUnmount, onMounted, ref, unref } from 'vue'; + +export const connectMicItemProps = () => ({ + micItem: PropUtils.object().isRequired, + /** 连麦信息是否显示 */ + micInfoVisible: PropUtils.bool.def(true), +}); + +/** 连麦节点 hook */ +export const useConnectMicItem = (options: { props: VueProps }) => { + const { props } = options; + const { micInfoVisible } = useProps(props); + + const wrapRef = ref(); + + /** 连麦用户昵称 */ + const micNickname = computed(() => { + const micItem = props.micItem; + if (micItem.isTeacher) { + return translate('connectMic.teacher'); + } + if (micItem.isSelf) { + return translate('connectMic.me'); + } + return micItem.nickname; + }); + + /** 是否关闭麦克风 */ + const isAudioMuted = computed(() => props.micItem.isAudioMuted); + + /** 当前音量,0 ~ 1 */ + const currentVolume = computed(() => props.micItem.currentVolume); + + /** 是否关闭摄像头 */ + const isVideoMuted = computed(() => props.micItem.isVideoMuted); + + /** 是否 sip 电话入座 */ + const isSipUser = computed(() => props.micItem.isSipUser); + + /** 是否本地推流 */ + const isLocal = computed(() => props.micItem.isLocal); + + /** 是否主讲 */ + const isMaster = computed(() => props.micItem.isMaster); + + /** 当前用户的连麦方式 */ + const currentConnectMicType = computed(() => props.micItem.currentConnectMicType); + + /** + * 初始化连麦节点 + */ + async function initMicItem() { + await nextTick(); + const micItem = props.micItem; + const wrapElement = unref(wrapRef); + if (!wrapElement) { + return; + } + wrapElement.innerHTML = ''; + + // 本地流则调用推送方法 + if (micItem.isLocal) { + micItem.publishSteam({ + element: wrapElement, + control: false, + playConfig: { + fit: 'contain', + }, + }); + } else { + micItem.subscribeStream({ + element: wrapElement, + control: false, + playConfig: { + fit: 'contain', + }, + }); + } + } + + /** + * 销毁连麦节点 + */ + function destroyMicItem(): void { + const micItem = props.micItem; + micItem.removeStream(); + } + + onMounted(() => { + initMicItem(); + }); + + onBeforeUnmount(() => { + destroyMicItem(); + }); + + return { + wrapRef, + micInfoVisible, + micNickname, + isAudioMuted, + currentVolume, + isVideoMuted, + isSipUser, + isLocal, + isMaster, + currentConnectMicType, + }; +}; diff --git a/src/components/page-watch-common/connect-mic/hooks/use-connect-mic.ts b/src/components/page-watch-common/connect-mic/hooks/use-connect-mic.ts new file mode 100644 index 0000000000000000000000000000000000000000..d626afec61bef96885eec680e9fc5a5fcdd735d2 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/hooks/use-connect-mic.ts @@ -0,0 +1,156 @@ +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useConnectMicStore } from '@/store/use-connect-mic-store'; +import { ConnectMicError, ConnectMicEvents } from '@polyv/live-watch-sdk'; +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { onBeforeUnmount, onMounted } from 'vue'; + +/** + * 连麦设备设置 hook + */ +export const useConnectMicDevice = () => { + /** 开启设备设置 */ + function openDeviceSetting() { + const watchCore = getWatchCore(); + watchCore.connectMic.openDeviceSetting(); + } + + /** 关闭设备设置 */ + function closeDeviceSetting() { + const watchCore = getWatchCore(); + watchCore.connectMic.closeDeviceSetting(); + } + + return { + openDeviceSetting, + closeDeviceSetting, + }; +}; + +/** + * 连麦申请 hook + */ +export const useConnectMicApply = () => { + const watchCore = getWatchCore(); + + /** 申请连麦 */ + async function applyConnectMic() { + const result = await watchCore.connectMic.applyConnectMic(); + + if (!result.success && result.failReason === ConnectMicError.GetDevicePermissionFail) { + toast.error(translate('connectMic.error.getDeviceFail')); + } + } + + /** 取消连麦申请 */ + function cancelApplyConnectMic() { + watchCore.connectMic.cancelApplyConnectMic(); + } + + return { + applyConnectMic, + cancelApplyConnectMic, + }; +}; + +/** + * 连麦控制 hook + */ +export const useConnectMicControl = () => { + const watchCore = getWatchCore(); + const connectMicStore = useConnectMicStore(); + + /** 开启摄像头 */ + function enabledVideo() { + watchCore.connectMic.enabledVideo(); + } + + /** 关闭摄像头 */ + function disabledVideo() { + watchCore.connectMic.disabledVideo(); + } + + /** 切换摄像头 */ + function toggleVideo() { + if (connectMicStore.localIsVideoMuted) { + enabledVideo(); + } else { + disabledVideo(); + } + } + + /** 开启麦克风 */ + function enabledAudio() { + watchCore.connectMic.enabledAudio(); + } + + /** 关闭麦克风 */ + function disabledAudio() { + watchCore.connectMic.disabledAudio(); + } + + /** 切换麦克风 */ + function toggleAudio() { + if (connectMicStore.localIsAudioMuted) { + enabledAudio(); + } else { + disabledAudio(); + } + } + + /** 结束连麦 */ + function endConnectMic() { + watchCore.connectMic.endConnectMic(); + } + + return { + enabledVideo, + disabledVideo, + toggleVideo, + enabledAudio, + disabledAudio, + toggleAudio, + endConnectMic, + }; +}; + +/** + * 连麦总 hook + * @description 各端仅限注册一次 + */ +export const useConnectMic = () => { + const { openDeviceSetting } = useConnectMicDevice(); + useEventBusListener(appEvents.connectMic.TriggerOpenDeviceSetting, openDeviceSetting); + + const { applyConnectMic, cancelApplyConnectMic } = useConnectMicApply(); + useEventBusListener(appEvents.connectMic.TriggerApplyConnectMic, applyConnectMic); + useEventBusListener(appEvents.connectMic.TriggerCancelApplyConnectMic, cancelApplyConnectMic); + + const { endConnectMic } = useConnectMicControl(); + useEventBusListener(appEvents.connectMic.TriggerEndConnectMic, endConnectMic); + + const watchCore = getWatchCore(); + + // 邀请上麦 + function onInviteConnectMic() { + watchCore.connectMic.openInviting(); + } + onMounted(() => { + watchCore.connectMic.eventEmitter.on(ConnectMicEvents.InviteConnectMic, onInviteConnectMic); + }); + onBeforeUnmount(() => { + watchCore.connectMic.eventEmitter.off(ConnectMicEvents.InviteConnectMic, onInviteConnectMic); + }); + + // 连麦人数达到限制 + function onOverLimit() { + toast.error(translate('connectMic.error.overLimit')); + } + onMounted(() => { + watchCore.connectMic.eventEmitter.on(ConnectMicEvents.ConnectMicOverLimit, onOverLimit); + }); + onBeforeUnmount(() => { + watchCore.connectMic.eventEmitter.off(ConnectMicEvents.ConnectMicOverLimit, onOverLimit); + }); +}; diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-audio-close.png b/src/components/page-watch-common/connect-mic/imgs/btn-audio-close.png new file mode 100644 index 0000000000000000000000000000000000000000..978746694054504809055c0ce093490a7395f410 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-audio-close.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-audio-open.png b/src/components/page-watch-common/connect-mic/imgs/btn-audio-open.png new file mode 100644 index 0000000000000000000000000000000000000000..274fabd94921e008415707a3c8145beeb1c9c4b9 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-audio-open.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-close.png b/src/components/page-watch-common/connect-mic/imgs/btn-close.png new file mode 100644 index 0000000000000000000000000000000000000000..f04bb98442b0925520258acfe8ed234e79c684df Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-close.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-icon-default.png b/src/components/page-watch-common/connect-mic/imgs/btn-icon-default.png new file mode 100644 index 0000000000000000000000000000000000000000..ebbb563e316edbc6e22b1f9336689e71c5d88130 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-icon-default.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-icon-disabled.png b/src/components/page-watch-common/connect-mic/imgs/btn-icon-disabled.png new file mode 100644 index 0000000000000000000000000000000000000000..c1c4527f6deb5918867bf642db9edb5f07ea7596 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-icon-disabled.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-icon-hangup.png b/src/components/page-watch-common/connect-mic/imgs/btn-icon-hangup.png new file mode 100644 index 0000000000000000000000000000000000000000..e536d46e6de12a8161f4aec0b1af4b9aeab7a23e Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-icon-hangup.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-set.png b/src/components/page-watch-common/connect-mic/imgs/btn-set.png new file mode 100644 index 0000000000000000000000000000000000000000..26c428d1f2c718f54e617898bc795029e737fe64 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-set.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-video-close.png b/src/components/page-watch-common/connect-mic/imgs/btn-video-close.png new file mode 100644 index 0000000000000000000000000000000000000000..4ebb76f8816fd33379f050f175eeb521b9ffffa8 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-video-close.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/btn-video-open.png b/src/components/page-watch-common/connect-mic/imgs/btn-video-open.png new file mode 100644 index 0000000000000000000000000000000000000000..6dfa26e3009e0fd50014479104449b8904e73d4d Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/btn-video-open.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/call-connecting.gif b/src/components/page-watch-common/connect-mic/imgs/call-connecting.gif new file mode 100644 index 0000000000000000000000000000000000000000..5de709033a2c75ddd91af7ad22c9838a3d2ecb40 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/call-connecting.gif differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-audio-close-hover.png b/src/components/page-watch-common/connect-mic/imgs/item-audio-close-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..95ae96f91232afd49ae57e48fa716711c22f0cbd Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-audio-close-hover.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-audio-close.png b/src/components/page-watch-common/connect-mic/imgs/item-audio-close.png new file mode 100644 index 0000000000000000000000000000000000000000..a00f61e798b967fbfaa9206b4b6baf6f50f9ae4f Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-audio-close.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-audio-open-hover.png b/src/components/page-watch-common/connect-mic/imgs/item-audio-open-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..348d2fc7f23972550f272387b7a0e534ad9471b6 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-audio-open-hover.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-audio-open.png b/src/components/page-watch-common/connect-mic/imgs/item-audio-open.png new file mode 100644 index 0000000000000000000000000000000000000000..9914ba56cc3cae76db1ee7c27ab7c7011ca3b3f7 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-audio-open.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-bottom-bar.png b/src/components/page-watch-common/connect-mic/imgs/item-bottom-bar.png new file mode 100644 index 0000000000000000000000000000000000000000..446c2955757e6ddc81ac760ce7f91dfd12a2d04d Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-bottom-bar.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-status-audio-closed.png b/src/components/page-watch-common/connect-mic/imgs/item-status-audio-closed.png new file mode 100644 index 0000000000000000000000000000000000000000..6bfeeaefc3e2dc2a4b00aedbad63f080165f11b6 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-status-audio-closed.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-status-audio-full.png b/src/components/page-watch-common/connect-mic/imgs/item-status-audio-full.png new file mode 100644 index 0000000000000000000000000000000000000000..2b968bed45218e6bfa0c80294698430c3b5847b7 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-status-audio-full.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-status-audio-zero.png b/src/components/page-watch-common/connect-mic/imgs/item-status-audio-zero.png new file mode 100644 index 0000000000000000000000000000000000000000..118aa1148dc4b45c581afd4f9c966e3d187fa8bb Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-status-audio-zero.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-video-close-hover.png b/src/components/page-watch-common/connect-mic/imgs/item-video-close-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..945352fe7124ca94146998fb268c543c81b899b7 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-video-close-hover.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-video-close.png b/src/components/page-watch-common/connect-mic/imgs/item-video-close.png new file mode 100644 index 0000000000000000000000000000000000000000..9a21e5bd0a55eb54b864db2436a9fee0cee88873 Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-video-close.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-video-open-hover.png b/src/components/page-watch-common/connect-mic/imgs/item-video-open-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..af43634247de77f5cf36b4b2b8a30eb2d17a3b8a Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-video-open-hover.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/item-video-open.png b/src/components/page-watch-common/connect-mic/imgs/item-video-open.png new file mode 100644 index 0000000000000000000000000000000000000000..ab18261b4644ae8533b57f5063bc8a6c435a787c Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/item-video-open.png differ diff --git a/src/components/page-watch-common/connect-mic/imgs/phone.png b/src/components/page-watch-common/connect-mic/imgs/phone.png new file mode 100644 index 0000000000000000000000000000000000000000..013aab8bd65bc1f04db10e4c7ee563f4554e081d Binary files /dev/null and b/src/components/page-watch-common/connect-mic/imgs/phone.png differ diff --git a/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-pendant.vue b/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..134c54b73969dae7a5c6d52aadccb853a3cebcb2 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-pendant.vue @@ -0,0 +1,346 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-user-item.vue b/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-user-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..dfeb10a5fcd8a94b6ece2da060616a06ff90cdaf --- /dev/null +++ b/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-user-item.vue @@ -0,0 +1,91 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-user-list.vue b/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-user-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..74a24d50e3ab6fc8a752fea57d05dcbab2e87a41 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/mobile-connect-mic/mobile-connect-mic-user-list.vue @@ -0,0 +1,63 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-bottom.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-bottom.vue new file mode 100644 index 0000000000000000000000000000000000000000..f99eca105b345658cc9611f53a8640be77312d4a --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-bottom.vue @@ -0,0 +1,54 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-button.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..df86b17c9c1994bd42ea0ab22e2cf801fca03280 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-button.vue @@ -0,0 +1,84 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-confirm.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-confirm.vue new file mode 100644 index 0000000000000000000000000000000000000000..b3413195f144624892be1920a99bf9a226419193 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-confirm.vue @@ -0,0 +1,93 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-placeholder.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-placeholder.vue new file mode 100644 index 0000000000000000000000000000000000000000..b8282d7ce4916e9dbacef59c9c37bd4cd24bd3ee --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-placeholder.vue @@ -0,0 +1,71 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-item-control.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-item-control.vue new file mode 100644 index 0000000000000000000000000000000000000000..257711724457c6d3eb22a720eca5763d647afa75 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-item-control.vue @@ -0,0 +1,164 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-item.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..70989d937d9e32e3b6db97a5334bce3d6cd52132 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-item.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-list.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..6ac0a6d39a3b3dff4537c2f6985f9efa95f4162f --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic-user-list.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic.vue b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic.vue new file mode 100644 index 0000000000000000000000000000000000000000..ee13b64185261df734a3762de6ef8ad88ed0bad8 --- /dev/null +++ b/src/components/page-watch-common/connect-mic/pc-connect-mic/pc-connect-mic.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/page-watch-common/count-down-layer/mobile-count-down-layer.vue b/src/components/page-watch-common/count-down-layer/mobile-count-down-layer.vue new file mode 100644 index 0000000000000000000000000000000000000000..8a26877e3f1799c302839c5431ecf731a453f74e --- /dev/null +++ b/src/components/page-watch-common/count-down-layer/mobile-count-down-layer.vue @@ -0,0 +1,115 @@ + + + + + + diff --git a/src/components/page-watch-common/count-down-layer/pc-count-down-layer.vue b/src/components/page-watch-common/count-down-layer/pc-count-down-layer.vue new file mode 100644 index 0000000000000000000000000000000000000000..f80229091ca6eb01b1f7133f9be2e83c6cbe8d72 --- /dev/null +++ b/src/components/page-watch-common/count-down-layer/pc-count-down-layer.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/src/components/page-watch-common/count-down-layer/portrait-count-down-layer.vue b/src/components/page-watch-common/count-down-layer/portrait-count-down-layer.vue new file mode 100644 index 0000000000000000000000000000000000000000..edbea0bd8ddeeae135aa06517afdf6786f812389 --- /dev/null +++ b/src/components/page-watch-common/count-down-layer/portrait-count-down-layer.vue @@ -0,0 +1,59 @@ + + + + + + diff --git a/src/components/page-watch-common/count-down-layer/use-count-down-layer.ts b/src/components/page-watch-common/count-down-layer/use-count-down-layer.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3ad6ed95a88bdee58c09aa0747075a7ae77594b --- /dev/null +++ b/src/components/page-watch-common/count-down-layer/use-count-down-layer.ts @@ -0,0 +1,17 @@ +import { useChannelStore } from '@/store/use-channel-store'; +import { computed } from 'vue'; +import { ChannelPlayMode } from '@polyv/live-watch-sdk'; + +export const useCountDownLayer = () => { + const channelStore = useChannelStore(); + + /** 是否显示右上角模式 */ + const isTopRightStyle = computed(() => { + const playMode = channelStore.channelDetail?.playbackInfo?.playMode; + return typeof playMode !== 'undefined' && playMode !== ChannelPlayMode.End; + }); + + return { + isTopRightStyle, + }; +}; diff --git a/src/components/page-watch-common/custom-tuwen-text-menu/mobile-custom-tuwen-text-menu.vue b/src/components/page-watch-common/custom-tuwen-text-menu/mobile-custom-tuwen-text-menu.vue new file mode 100644 index 0000000000000000000000000000000000000000..45e9833819b4ac6005bc5803662eaf128b065bb9 --- /dev/null +++ b/src/components/page-watch-common/custom-tuwen-text-menu/mobile-custom-tuwen-text-menu.vue @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/components/page-watch-common/doc/_hooks/use-keydown-control.ts b/src/components/page-watch-common/doc/_hooks/use-keydown-control.ts new file mode 100644 index 0000000000000000000000000000000000000000..3aa5012dfea7e4d239159b32bb0cdefb16e9668c --- /dev/null +++ b/src/components/page-watch-common/doc/_hooks/use-keydown-control.ts @@ -0,0 +1,53 @@ +import { KeyCodeMap } from '@/assets/constants/key-code'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { MainScreenContent } from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, watchEffect } from 'vue'; + +/** + * 文档键盘控制 hook + */ +export const useDocKeydownControl = () => { + const layoutStore = useLayoutStore(); + const playbackStore = usePlaybackStore(); + + function onKeydownEvent(event: KeyboardEvent) { + const keyCode = event.keyCode; + if (layoutStore.mainScreen !== MainScreenContent.Doc) { + return; + } + + const watchCore = getWatchCore(); + + if (keyCode === KeyCodeMap.ArrowLeft) { + watchCore.doc.gotoPrev(); + return; + } + + if (keyCode === KeyCodeMap.ArrowRight) { + watchCore.doc.gotoNext(); + } + } + + function listenEvent() { + removeListenEvent(); + window.addEventListener('keydown', onKeydownEvent); + } + + function removeListenEvent() { + window.addEventListener('keydown', onKeydownEvent); + } + + watchEffect(() => { + if (playbackStore.isPlaybacking) { + listenEvent(); + } else { + removeListenEvent(); + } + }); + + onBeforeUnmount(() => { + removeListenEvent(); + }); +}; diff --git a/src/components/page-watch-common/doc/doc-side-switch/mobile-doc-side-switch.vue b/src/components/page-watch-common/doc/doc-side-switch/mobile-doc-side-switch.vue new file mode 100644 index 0000000000000000000000000000000000000000..09254be88cc6e6da1d4f51f1961c66f082020b09 --- /dev/null +++ b/src/components/page-watch-common/doc/doc-side-switch/mobile-doc-side-switch.vue @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/components/page-watch-common/doc/doc-side-switch/side.png b/src/components/page-watch-common/doc/doc-side-switch/side.png new file mode 100644 index 0000000000000000000000000000000000000000..f0574cac4d60ec62689ccb58f8ab3c9a2c4244d8 Binary files /dev/null and b/src/components/page-watch-common/doc/doc-side-switch/side.png differ diff --git a/src/components/page-watch-common/doc/doc-switch/portrait-doc-switch.vue b/src/components/page-watch-common/doc/doc-switch/portrait-doc-switch.vue new file mode 100644 index 0000000000000000000000000000000000000000..2150eb81b70d0f57a518099dfd87545faef322f6 --- /dev/null +++ b/src/components/page-watch-common/doc/doc-switch/portrait-doc-switch.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/page-watch-common/doc/imgs/icon-doc-hide.png b/src/components/page-watch-common/doc/imgs/icon-doc-hide.png new file mode 100644 index 0000000000000000000000000000000000000000..8c16e3b82ad7c601cda3491adab229e90e9461e7 Binary files /dev/null and b/src/components/page-watch-common/doc/imgs/icon-doc-hide.png differ diff --git a/src/components/page-watch-common/doc/imgs/icon-doc-show.png b/src/components/page-watch-common/doc/imgs/icon-doc-show.png new file mode 100644 index 0000000000000000000000000000000000000000..435b55578c5a76205186f42611a3419b8e72f639 Binary files /dev/null and b/src/components/page-watch-common/doc/imgs/icon-doc-show.png differ diff --git a/src/components/page-watch-common/doc/imgs/player-ppt.png b/src/components/page-watch-common/doc/imgs/player-ppt.png new file mode 100644 index 0000000000000000000000000000000000000000..b64ff9a01eb3b742f531033f9247a9412cf7a0dd Binary files /dev/null and b/src/components/page-watch-common/doc/imgs/player-ppt.png differ diff --git a/src/components/page-watch-common/doc/mobile-doc.vue b/src/components/page-watch-common/doc/mobile-doc.vue new file mode 100644 index 0000000000000000000000000000000000000000..48d243f46de673e9816701b554799bd5015032ab --- /dev/null +++ b/src/components/page-watch-common/doc/mobile-doc.vue @@ -0,0 +1,31 @@ + + + + + + diff --git a/src/components/page-watch-common/doc/pc-doc-empty-placeholder.vue b/src/components/page-watch-common/doc/pc-doc-empty-placeholder.vue new file mode 100644 index 0000000000000000000000000000000000000000..9e7f0ae1fb996086172607b73fd0a9e1b84d9994 --- /dev/null +++ b/src/components/page-watch-common/doc/pc-doc-empty-placeholder.vue @@ -0,0 +1,25 @@ + + + + diff --git a/src/components/page-watch-common/doc/pc-doc.vue b/src/components/page-watch-common/doc/pc-doc.vue new file mode 100644 index 0000000000000000000000000000000000000000..645ffd7c1d1056706e712eec7905f2ee82c8581e --- /dev/null +++ b/src/components/page-watch-common/doc/pc-doc.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/components/page-watch-common/doc/portrait-doc.vue b/src/components/page-watch-common/doc/portrait-doc.vue new file mode 100644 index 0000000000000000000000000000000000000000..4d33b572e25b1da88ea9b227002237727c2cb9d9 --- /dev/null +++ b/src/components/page-watch-common/doc/portrait-doc.vue @@ -0,0 +1,31 @@ + + + + + + diff --git a/src/components/page-watch-common/doc/use-doc.ts b/src/components/page-watch-common/doc/use-doc.ts new file mode 100644 index 0000000000000000000000000000000000000000..73a95266fd48e8deb977590161799587c4c5db4f --- /dev/null +++ b/src/components/page-watch-common/doc/use-doc.ts @@ -0,0 +1,99 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { computed, nextTick, onBeforeUnmount, onMounted, ref, unref, watch } from 'vue'; +import { DocEvents, LiveStatus, SetupDocOptions } from '@polyv/live-watch-sdk'; +import { useLayoutStore } from '@/store/use-layout-store'; +// import { useMarquee } from '@/components/common-business/marquee/use-marquee'; +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { useDocStore } from '@/store/use-doc-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; + +export const useDoc = ( + useOptions: { + setupOptions?: Partial; + } = {}, +) => { + const docStore = useDocStore(); + const channelStore = useChannelStore(); + const layoutStore = useLayoutStore(); + const playbackStore = usePlaybackStore(); + + /** 创建文档的外层节点 */ + const wrapRef = ref(); + + // useMarquee({ + // containerRef: wrapRef, + // }); + + /** 初始化文档 */ + function initDoc() { + const { setupOptions = {} } = useOptions; + const wrapElem = unref(wrapRef); + if (!wrapElem) return; + + if (!docStore.docEnabled) return; + + const watchCore = getWatchCore(); + watchCore.doc.setupDoc({ + container: wrapElem, + keyboardEnabled: !playbackStore.isPlaybacking, + ...setupOptions, + }); + + listenDocEvent(); + } + + function listenDocEvent() { + unlistenDocEvent(); + const watchCore = getWatchCore(); + watchCore.doc.eventEmitter.on(DocEvents.FullScreenStateChange, onFullScreenStateChange); + } + + function unlistenDocEvent() { + const watchCore = getWatchCore(); + watchCore.doc.eventEmitter.off(DocEvents.FullScreenStateChange, onFullScreenStateChange); + } + + function onFullScreenStateChange(evt: { isFullScreen: boolean }) { + layoutStore.$patch({ + mobileDocIsPageFullscreen: evt.isFullScreen, + }); + } + + useEventBusListener(appEvents.doc.ResetUpDoc, () => { + initDoc(); + }); + + onMounted(() => { + initDoc(); + }); + + /** 暂无直播状态是否显示 */ + const emptyPlaceholderVisible = computed(() => { + if ( + [LiveStatus.UnStart, LiveStatus.End, LiveStatus.Waiting].includes(channelStore.liveStatus) + ) { + return true; + } + + return false; + }); + + watch( + () => [layoutStore.mainScreen, layoutStore.mobileDocIsPageFullscreen], + async () => { + await nextTick(); + const watchCore = getWatchCore(); + watchCore.doc.resize(); + }, + ); + + onBeforeUnmount(() => { + unlistenDocEvent(); + }); + + return { + wrapRef, + emptyPlaceholderVisible, + }; +}; diff --git a/src/components/page-watch-common/donate/donate-animation/donate-animation-item.vue b/src/components/page-watch-common/donate/donate-animation/donate-animation-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..53e22a16f59780f41608ae66af2ca3c6235fd78b --- /dev/null +++ b/src/components/page-watch-common/donate/donate-animation/donate-animation-item.vue @@ -0,0 +1,249 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-animation/donate-animation.vue b/src/components/page-watch-common/donate/donate-animation/donate-animation.vue new file mode 100644 index 0000000000000000000000000000000000000000..266f6a8dfd459059f10a389acf9cfe93f91efe0a --- /dev/null +++ b/src/components/page-watch-common/donate/donate-animation/donate-animation.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-animation/imgs/donate-float-tips-bg.png b/src/components/page-watch-common/donate/donate-animation/imgs/donate-float-tips-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..5560b0e33affe9a8f4dc6c2b55c782486ef24468 Binary files /dev/null and b/src/components/page-watch-common/donate/donate-animation/imgs/donate-float-tips-bg.png differ diff --git a/src/components/page-watch-common/donate/donate-animation/imgs/donate-float-tips-redpaper.png b/src/components/page-watch-common/donate/donate-animation/imgs/donate-float-tips-redpaper.png new file mode 100644 index 0000000000000000000000000000000000000000..08a05c1be55f9333f3028b8d2949f20c64fa6708 Binary files /dev/null and b/src/components/page-watch-common/donate/donate-animation/imgs/donate-float-tips-redpaper.png differ diff --git a/src/components/page-watch-common/donate/donate-animation/styles/var.scss b/src/components/page-watch-common/donate/donate-animation/styles/var.scss new file mode 100644 index 0000000000000000000000000000000000000000..350c53f5461b5b719041a0263f73ca02f7ba2f02 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-animation/styles/var.scss @@ -0,0 +1,6 @@ +// 打赏动画宽度 +$--donate-animation-item-width: 270px; +// 打赏动画高度 +$--donate-animation-item-height: 40px; +// 打赏动画距离 +$--donate-animation-item-margin: 32px; diff --git a/src/components/page-watch-common/donate/donate-animation/use-donate-animation.ts b/src/components/page-watch-common/donate/donate-animation/use-donate-animation.ts new file mode 100644 index 0000000000000000000000000000000000000000..12f3ec8a6731716223133a80cd0e02bd052eb148 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-animation/use-donate-animation.ts @@ -0,0 +1,147 @@ +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useDonateStore } from '@/store/use-donate-store'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { + ChatEvents, + ChatMsgRewardType, + ChatMsgSource, + ChatMsgType, + DonateType, +} from '@polyv/live-watch-sdk'; +import { uuidV4 } from '@utils-ts/string'; +import { onBeforeUnmount, onMounted, ref, unref } from 'vue'; + +/** 打赏动画轨道节点 */ +export interface AnimationTrackItem { + /** 动画 id */ + animationId: string; + /** 昵称 */ + nick: string; + /** 礼物道具图片 */ + gimg: string; + /** 礼物数量 */ + count: number; + /** 打赏内容 */ + rewardContent: string; + /** 打赏类型 */ + donateType: DonateType; +} + +export const donateAnimationProps = () => ({ + /** 动画轨道数 */ + trackCount: PropUtils.number.def(3), +}); + +export const useDonateAnimation = (options: { props: VueProps }) => { + const interactReceiveStore = useInteractReceiveStore(); + const donateStore = useDonateStore(); + + const { props } = options; + + /** 节点高度(高度+距离) */ + const itemHeight = 40 + 32; + + /** 动画轨道列表 */ + const trackList = ref([]); + + /** 等待队列 */ + const waitQueue: ChatMsgRewardType[] = []; + + /** 检查动画轨道 */ + function checkAnimationTrack() { + if (!waitQueue.length || interactReceiveStore.isGoOnRedpackRain) { + return; + } + + if (unref(trackList).length < props.trackCount) { + // 有空位,则补充空位 + const surplusCount = props.trackCount - unref(trackList).length; + const pushList = waitQueue.splice(0, surplusCount); + + pushList.forEach(chatMsg => { + trackList.value.push({ + animationId: uuidV4(), + nick: chatMsg.user.nick, + gimg: chatMsg.gimg, + count: chatMsg.goodNum ?? 1, + rewardContent: chatMsg.rewardContent, + donateType: chatMsg.donateType, + }); + }); + } + } + + /** + * 处理节点离开完毕 + * @param animationId 动画 id + */ + function onItemLeaved(animationId: string) { + const index = trackList.value.findIndex(item => item.animationId === animationId); + if (index !== -1) { + trackList.value.splice(index, 1); + checkAnimationTrack(); + } + } + + /** + * 打赏道具是否可以插入到队列中 + */ + function canPushInQueue(chatMsg: ChatMsgRewardType) { + const isSelfGood = chatMsg.isSelf; + const isPayGood = !chatMsg.isFree; + + // 自己打赏的必须插入 + if (isSelfGood) { + return true; + } + + // 屏蔽了其他人的打赏动画 + if (!donateStore.donateAnimationVisible) { + return false; + } + + return isPayGood || waitQueue.length < 100; + } + + /** 处理聊天室消息 */ + function onChatMsgEvent(evt: { chatMsg: ChatMsgType }) { + const chatMsg = evt.chatMsg; + // 非打赏消息 + if (chatMsg.msgSource !== ChatMsgSource.Reward) { + return; + } + + /** 是否需要插入到队列中 */ + const pushInQueue = canPushInQueue(chatMsg); + const isSelfGood = chatMsg.isSelf; + + if (!pushInQueue) { + return; + } + + if (isSelfGood) { + // 如果是自己的打赏则优先显示 + waitQueue.unshift(chatMsg); + } else { + waitQueue.push(chatMsg); + } + checkAnimationTrack(); + } + + onMounted(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatMessage, onChatMsgEvent); + }); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatMessage, onChatMsgEvent); + }); + + return { + trackList, + itemHeight, + onItemLeaved, + }; +}; diff --git a/src/components/page-watch-common/donate/donate-panel/hooks/types.ts b/src/components/page-watch-common/donate/donate-panel/hooks/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..0391f37649bbcaa91be5d1b881aa420644043291 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/hooks/types.ts @@ -0,0 +1,7 @@ +/** + * 打赏选项 + */ +export type DonateOptions = { + /** 打赏成功回调 */ + donateSuccess?: () => void; +}; diff --git a/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-cash.ts b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-cash.ts new file mode 100644 index 0000000000000000000000000000000000000000..852d530bb6e3f981e051222bc77299482067271f --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-cash.ts @@ -0,0 +1,68 @@ +/** + * @file 现金打赏 hook + */ + +import { useFormatPrice } from '@/hooks/tools/use-format'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { ref, unref } from 'vue'; +import { useDonatePay } from './use-donate-pay'; +import { DonateType } from '@polyv/live-watch-sdk'; +import { useDonateStore } from '@/store/use-donate-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { DonateOptions } from './types'; + +/** 现金打赏 hook */ +export const useDonateCash = (donateOptions: DonateOptions = {}) => { + // 打赏成功回调 + const donateSuccess = donateOptions.donateSuccess; + + const { donateCashMin } = storeDefinitionToRefs(useDonateStore); + const { payDonate } = useDonatePay(); + + /** 现金金额 */ + const cashAmount = ref('0'); + const { randomPrice } = useFormatPrice(cashAmount); + + /** 处理点击随机 */ + function onClickRandom() { + randomPrice(); + } + + /** 处理点击快捷选项 */ + function onClickQuickOptions(amount: number) { + cashAmount.value = `${amount}`; + } + + /** 打赏现金 */ + async function donateCash() { + const cash = Number(unref(cashAmount)); + if (cash === 0) { + toast.error(translate('donate.error.notZero')); + return; + } + + if (cash < unref(donateCashMin)) { + toast.error( + translate('donate.error.mini', { + price: unref(donateCashMin).toString(), + }), + ); + return; + } + + await payDonate({ + donateType: DonateType.Cash, + amount: `${cash}`, + }); + + donateSuccess && donateSuccess(); + } + + return { + cashAmount, + onClickRandom, + onClickQuickOptions, + donateCash, + }; +}; diff --git a/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-good-point.ts b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-good-point.ts new file mode 100644 index 0000000000000000000000000000000000000000..5af62f0b43ccc256be83a2369d442ffca1095acc --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-good-point.ts @@ -0,0 +1,55 @@ +/** + * @file 礼物打赏(积分)hook + */ +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { DonateGoodInfo, useDonateStore } from '@/store/use-donate-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { onMounted, ref, unref } from 'vue'; + +/** 礼物打赏(积分)hook */ +export const useDonateGoodPoint = () => { + const { donatePointEnabled } = storeDefinitionToRefs(useDonateStore); + + /** 当前用户剩余的积分总数 */ + const surplusPoint = ref(0); + /** 获取当前用户剩余的积分 */ + async function getSurplusPoint() { + const watchCore = getWatchCore(); + try { + const points = await watchCore.donate.getDonateSurplusPoint(); + surplusPoint.value = points; + } catch (e) { + toast.error(translate('donate.point.getFail')); + } + } + + /** 处理打赏积分礼物 */ + async function donatePointGood(good: DonateGoodInfo, count: number): Promise { + const watchCore = getWatchCore(); + try { + const points = await watchCore.donate.payDonatePoint({ + goodId: good.goodId, + goodCount: count, + }); + surplusPoint.value = points; + } catch (e) { + const err = e as Error; + toast.error(err.message || translate('donate.error.unknown')); + } + } + + onMounted(() => { + if (unref(donatePointEnabled)) { + getSurplusPoint(); + } + }); + + return { + /** 当前剩余积分 */ + surplusPoint, + /** 进行积分礼物打赏 */ + donatePointGood, + }; +}; diff --git a/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-good.ts b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-good.ts new file mode 100644 index 0000000000000000000000000000000000000000..96519b9640914c94c30762c8191c806086d1eac5 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-good.ts @@ -0,0 +1,71 @@ +/** + * @file 道具打赏 hook + */ + +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { DonateGoodInfo, useDonateStore } from '@/store/use-donate-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { DonateType } from '@polyv/live-watch-sdk'; +import { ref, unref } from 'vue'; +import { DonateOptions } from './types'; +import { useDonateGoodPoint } from './use-donate-good-point'; +import { useDonatePay } from './use-donate-pay'; + +/** + * 道具打赏 hook + */ +export const useDonateGood = (donateOptions: DonateOptions = {}) => { + // 打赏成功回调 + const donateSuccess = donateOptions.donateSuccess; + + const { donatePointEnabled, donateGoodEnabled } = storeDefinitionToRefs(useDonateStore); + + /** 打赏数量 */ + const donateCount = ref(1); + /** 打赏数量快捷选项 */ + const quickCounts = [1, 5, 10, 66, 88, 666]; + + /** 当前选择的礼物 */ + const selectedGood = ref(); + /** 设置选中的礼物 */ + function setSelectedGood(good: DonateGoodInfo): void { + selectedGood.value = good; + donateCount.value = 1; + } + + const { surplusPoint, donatePointGood } = useDonateGoodPoint(); + const { payDonate } = useDonatePay(); + + /** 打赏礼物 */ + async function donateGood() { + const good = unref(selectedGood); + const count = unref(donateCount) || 1; + + if (!good) { + toast.error(translate('donate.error.notChoice')); + return; + } + + if (unref(donatePointEnabled)) { + await donatePointGood(good, count); + } else if (unref(donateGoodEnabled)) { + await payDonate({ + donateType: DonateType.Good, + goodId: good.goodId, + }); + } + + donateSuccess && donateSuccess(); + } + + return { + donateCount, + quickCounts, + surplusPoint, + selectedGood, + setSelectedGood, + donateGood, + donatePointGood, + }; +}; diff --git a/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-pay.ts b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-pay.ts new file mode 100644 index 0000000000000000000000000000000000000000..eec4fd00fc45a9b97eca5bc5420489baf7dabec5 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/hooks/use-donate-pay.ts @@ -0,0 +1,244 @@ +/** + * @file 礼物、现金打赏支付 hook + */ + +import { usePolyvWatchDomain } from '@/hooks/platform/use-polyv-watch-domain'; +import { useWeixinAuthorize } from '@/hooks/platform/use-weixin/use-weixin-authorize'; +import { useWeixinPay } from '@/hooks/platform/use-weixin/use-weixin-pay'; +import { translate } from '@/assets/lang'; +import { isMobile, isWeixin } from '@/assets/utils/browser'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useDonateStore } from '@/store/use-donate-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { + DonateType, + PayDonateData, + PayDonateParams, + WatchDomainPayType, +} from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, unref, watch } from 'vue'; + +/** + * 打赏支付状态检查 hook + */ +const useDonatePayCheck = () => { + const { waitPayDonateData } = storeDefinitionToRefs(useDonateStore); + + let checkTimer: number | undefined; + let checkCount = 100; + /** 清空检查支付状态定时器 */ + function clearCheckTimer() { + clearInterval(checkTimer); + checkTimer = undefined; + checkCount = 100; + } + + /** 设置检查支付状态定时器 */ + function setCheckTimer() { + clearCheckTimer(); + checkTimer = window.setInterval(() => { + checkPayStatus(); + }, 1000); + } + + /** 检查支付状态 */ + async function checkPayStatus() { + const watchCore = getWatchCore(); + + const data = unref(waitPayDonateData); + if (!data || !data.logId) { + clearCheckTimer(); + return; + } + // 100 秒未支付则超时 + if (checkCount === 0) { + clearCheckTimer(); + waitPayDonateData.value = undefined; + toast.error(translate('donate.error.payTimeOut')); + return; + } + checkCount -= 1; + + try { + const status = await watchCore.donate.checkDonatePayStatus({ + logId: data.logId, + donateType: data.donateType, + }); + + if (status.paySuccess) { + waitPayDonateData.value = undefined; + clearCheckTimer(); + } + } catch (e) { + toast.error(translate('donate.error.payFail')); + clearCheckTimer(); + } + } + + onBeforeUnmount(() => { + clearCheckTimer(); + }); + + return { + setCheckTimer, + clearCheckTimer, + checkPayStatus, + }; +}; + +export interface DonatePayOptions { + /** 打赏类型 */ + donateType: DonateType; + /** 打赏道具 id */ + goodId?: string; + /** 现金打赏下的打赏金额 */ + amount?: string; +} + +/** PC 端支付逻辑 */ +const useDonatePayPc = () => { + const { waitPayDonateData } = storeDefinitionToRefs(useDonateStore); + + const { setCheckTimer, clearCheckTimer } = useDonatePayCheck(); + + watch( + () => unref(waitPayDonateData), + () => { + if (!unref(waitPayDonateData)) { + clearCheckTimer(); + } + }, + ); + + /** + * 支付打赏(PC 逻辑) + */ + async function payDonate(payOptions: DonatePayOptions): Promise { + const watchCore = getWatchCore(); + + clearCheckTimer(); + const donateType = payOptions.donateType; + const amount = payOptions.amount; + const goodId = payOptions.goodId; + + try { + const params: PayDonateParams = { + donateType, + }; + // 道具打赏 + if (typeof goodId !== 'undefined') { + params.goodId = goodId; + } + // 现金打赏 + if (amount) { + params.amount = amount; + } + const payData = await watchCore.donate.payDonate(params); + + // 免费礼物 + if (payData.isFree) { + return; + } + + waitPayDonateData.value = payData; + setCheckTimer(); + } catch (e) { + clearCheckTimer(); + toast.error(translate('donate.error.payFail')); + throw e; + } + } + + return { + payDonate, + }; +}; + +/** 移动端支付逻辑 */ +const useDonatePayMobile = () => { + const { redirectWeixinAuthorize } = useWeixinAuthorize(); + const { chooseWXPay } = useWeixinPay(); + const { polyvWatchDomainEnabled, skipWatchDomainPay } = usePolyvWatchDomain(); + + /** + * 支付打赏(移动端逻辑) + */ + // eslint-disable-next-line sonarjs/cognitive-complexity + function payDonate(payOptions: DonatePayOptions) { + return new Promise((resolve, reject) => { + (async () => { + const watchCore = getWatchCore(); + + const donateType = payOptions.donateType; + const amount = payOptions.amount; + const goodId = payOptions.goodId; + + let payInfo: PayDonateData; + const params: PayDonateParams = { + donateType, + }; + // 道具打赏 + if (typeof goodId !== 'undefined') { + params.goodId = goodId; + } + // 现金打赏 + if (amount) { + params.amount = amount; + } + + // 泛域名支付 + if (unref(polyvWatchDomainEnabled)) { + await skipWatchDomainPay({ + payType: WatchDomainPayType.Donate, + params, + }); + return; + } + + try { + payInfo = await watchCore.donate.payDonate(params); + } catch (e) { + const err = e as Error; + // 需要跳转授权 + if (err.message === 'authorization') { + redirectWeixinAuthorize(); + } else { + toast.error(translate('donate.error.payFail')); + } + reject(e); + return; + } + + // 免费礼物 + if (payInfo.isFree) { + resolve(); + return; + } + + // 非微信下直接提示 + if (!isWeixin) { + toast.error(translate('weixin.error.toWechatPay')); + reject(new Error('pay fail')); + return; + } + + if (payInfo.wxPaySignData) { + chooseWXPay(payInfo.wxPaySignData, { + successCb: () => resolve(), + cancelCb: () => reject(new Error('pay fail')), + }); + } + })(); + }); + } + + return { + payDonate, + }; +}; + +/** 礼物、现金打赏支付 hook */ +export const useDonatePay = () => { + return isMobile ? useDonatePayMobile() : useDonatePayPc(); +}; diff --git a/src/components/page-watch-common/donate/donate-panel/imgs/redpack-cash.png b/src/components/page-watch-common/donate/donate-panel/imgs/redpack-cash.png new file mode 100644 index 0000000000000000000000000000000000000000..87ae70fc13dc8daf6f93dc5eb0ed33f3b4eee968 Binary files /dev/null and b/src/components/page-watch-common/donate/donate-panel/imgs/redpack-cash.png differ diff --git a/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel-cash.vue b/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel-cash.vue new file mode 100644 index 0000000000000000000000000000000000000000..497ccbeeea44e9d6faa8eca3e79aa8e841da24af --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel-cash.vue @@ -0,0 +1,187 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel-good.vue b/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel-good.vue new file mode 100644 index 0000000000000000000000000000000000000000..4a843e406d033abcd26b2ef1f4bb5dae87dcd75f --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel-good.vue @@ -0,0 +1,252 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel.vue b/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..3a66fb7dfe729d01b68d272e0ac4c86f48821644 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/mobile-donate-panel/mobile-donate-panel.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-cash.vue b/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-cash.vue new file mode 100644 index 0000000000000000000000000000000000000000..f4ec8beebb2ccde0e4d74bc0573400a89f621100 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-cash.vue @@ -0,0 +1,158 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-panel.vue b/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..72b88746022d62b26b812d9a6c504a825de314d0 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-panel.vue @@ -0,0 +1,305 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-pay-qrcode.vue b/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-pay-qrcode.vue new file mode 100644 index 0000000000000000000000000000000000000000..678da5d258909b76b8c4da4a78e7b75b8392362f --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/pc-donate-panel/pc-donate-pay-qrcode.vue @@ -0,0 +1,104 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-panel-cash.vue b/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-panel-cash.vue new file mode 100644 index 0000000000000000000000000000000000000000..7e745df98cc480dde0e4ff7851cd3478ca33868a --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-panel-cash.vue @@ -0,0 +1,161 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-panel-good.vue b/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-panel-good.vue new file mode 100644 index 0000000000000000000000000000000000000000..47affef901707ad35524f198145c355a6a6f910a --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-panel-good.vue @@ -0,0 +1,270 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-popup.vue b/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..71e292bc4ffc9c157b3105bfe6fe9203c8b2eec7 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-panel/portrait-donate-popup/portrait-donate-popup.vue @@ -0,0 +1,63 @@ + + + + diff --git a/src/components/page-watch-common/donate/donate-svga-animation/donate-svga-animation.vue b/src/components/page-watch-common/donate/donate-svga-animation/donate-svga-animation.vue new file mode 100644 index 0000000000000000000000000000000000000000..f260760e536cb09bb26fc274d945c407f2ff5d88 --- /dev/null +++ b/src/components/page-watch-common/donate/donate-svga-animation/donate-svga-animation.vue @@ -0,0 +1,36 @@ + + + + + + diff --git a/src/components/page-watch-common/donate/donate-svga-animation/use-donate-svga-animation.ts b/src/components/page-watch-common/donate/donate-svga-animation/use-donate-svga-animation.ts new file mode 100644 index 0000000000000000000000000000000000000000..fffb0d8c0783ba716a48a6e0068dee92ef2fe09c --- /dev/null +++ b/src/components/page-watch-common/donate/donate-svga-animation/use-donate-svga-animation.ts @@ -0,0 +1,132 @@ +/** + * @file 打赏 svga 特效 hook + */ +import { DEFAULT_SVGA_DIRECTORY } from '@/assets/constants/defaults'; +import { useSvgaPlayer } from '@/hooks/animation/use-svga-player'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useDonateStore } from '@/store/use-donate-store'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { + ChatEvents, + ChatMsgRewardType, + ChatMsgSource, + ChatMsgType, + DonateType, +} from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, onMounted, ref, unref, watch } from 'vue'; + +const svgaMap: UniversalParams = { + coffee: 'coffee.svga', + like: 'like.svga', + handclap: 'handclap.svga', + 666: '666.svga', + star: 'star.svga', + diamond: 'diamond.svga', + car: 'car.svga', + rocket: 'rocket.svga', + bear: 'bear.svga', + crown: 'crown.svga', + cup: 'cup.svga', + microphone: 'microphone.svga', + villa: 'villa.svga', + house: 'villa.svga', // 旧版的house图匹配新版的别墅动画 +}; + +export const useDonateSvgaAnimation = () => { + const donateStore = useDonateStore(); + const { isGoOnRedpackRain } = storeDefinitionToRefs(useInteractReceiveStore); + + /** 用于渲染动画的 ref 节点 */ + const donateRef = ref(); + + const { animationQueue, pushAnimationQueue, pauseAnimationQueue, startAnimationQueue } = + useSvgaPlayer(donateRef); + + /** 根据打赏图片地址获取 svga 地址 */ + function getSvgaUrl(gimg: string): string | undefined { + // 如果后台有上传 svga 特效文件,则优先使用后台配置 + const goodInfo = donateStore.findDonateGoodByImg(gimg); + if (goodInfo && goodInfo.dynamicFile) { + return goodInfo.dynamicFile; + } + + // 如果没有则截取图片文件名来获取默认的 svga 文件 + const a = document.createElement('a'); + a.href = gimg; + const paths = a.pathname.split('/'); + const goodName = paths[paths.length - 1].split('.')[0]; + if (!svgaMap[goodName]) { + return undefined; + } + + return DEFAULT_SVGA_DIRECTORY + svgaMap[goodName]; + } + + // 如果正在显示红包雨不显示打赏特效 + watch( + () => unref(isGoOnRedpackRain), + () => { + if (unref(isGoOnRedpackRain)) { + pauseAnimationQueue(); + } else { + startAnimationQueue(); + } + }, + ); + + function canPushInQueue(chatMsg: ChatMsgRewardType) { + const isSelfGood = chatMsg.isSelf; + const isPayGood = !chatMsg.isFree; + + // 自己打赏的必须插入 + if (isSelfGood) { + return true; + } + + // 屏蔽了其他人的打赏动画 + if (!donateStore.donateAnimationVisible) { + return false; + } + + return isPayGood || unref(animationQueue).length < 100; + } + + /** 处理打赏消息 */ + function onChatMsgEvent(evt: { chatMsg: ChatMsgType }) { + const chatMsg = evt.chatMsg; + // 非打赏消息 + if (chatMsg.msgSource !== ChatMsgSource.Reward) { + return; + } + // 非道具打赏 + if (chatMsg.donateType !== DonateType.Good) { + return; + } + + const isSelfGood = chatMsg.isSelf; + const pushInQueue = canPushInQueue(chatMsg); + + if (!pushInQueue) { + return; + } + + // 如果是自己的打赏则优先显示 + const svgaUrl = getSvgaUrl(chatMsg.gimg); + pushAnimationQueue(svgaUrl || chatMsg.gimg, isSelfGood); + } + + onMounted(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatMessage, onChatMsgEvent); + }); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatMessage, onChatMsgEvent); + }); + + return { + donateRef, + }; +}; diff --git a/src/components/page-watch-common/emotion-picker/hooks/use-emotion-face-picker.ts b/src/components/page-watch-common/emotion-picker/hooks/use-emotion-face-picker.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b83ddef00a37d5d0e98df520af4a915315f6202 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/hooks/use-emotion-face-picker.ts @@ -0,0 +1,45 @@ +import { onMounted, ref, unref } from 'vue'; +import { genDOMList } from '@polyv/emotion-sdk'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; + +export const emotionFacePickerEmits = () => ({ + /** 点击表情 */ + 'click-face': emitFunc(), +}); + +/** + * face 表情选取 hook + */ +export const useEmotionFacePicker = (options: { emit: VueEmit }) => { + const { emit } = options; + + /** 容器节点 */ + const containerRef = ref(); + + /** 处理点击容器 */ + function onClickContainer(event: Event) { + const target = event.target as HTMLElement; + if (target.tagName !== 'I') { + return; + } + const title = target.getAttribute('data-title'); + if (!title) { + return; + } + + emit('click-face', `[${title}]`); + } + + /** 加载表情节点 */ + onMounted(() => { + const containerElem = unref(containerRef); + if (containerElem) { + containerElem.appendChild(genDOMList()); + } + }); + + return { + containerRef, + onClickContainer, + }; +}; diff --git a/src/components/page-watch-common/emotion-picker/hooks/use-emotion-image-picker.ts b/src/components/page-watch-common/emotion-picker/hooks/use-emotion-image-picker.ts new file mode 100644 index 0000000000000000000000000000000000000000..bf378f87209f6da159cc5a77db49bb8080ec27b7 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/hooks/use-emotion-image-picker.ts @@ -0,0 +1,42 @@ +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { getWatchCore } from '@/core/watch-sdk'; +import { EmotionImageData } from '@polyv/live-watch-sdk'; +import { onMounted, ref } from 'vue'; +import { useSendMsg } from '../../chat/chat-msg-list/hooks/send-msg-hook'; + +export const emotionImagePickerEmits = () => ({ + /** 点击表情 */ + 'click-image': emitFunc(), +}); + +export const useEmotionImagePicker = (options: { + emit: VueEmit; +}) => { + const { sendEmotionImageMsg } = useSendMsg(); + + const { emit } = options; + + /** 图片表情列表 */ + const emotionList = ref([]); + + /** 处理点击表情 */ + async function onClickItem(data: EmotionImageData) { + emit('click-image', data); + + sendEmotionImageMsg({ + emotionId: data.id, + emotionUrl: data.url, + }); + } + + onMounted(async () => { + const watchCore = getWatchCore(); + const data = await watchCore.chat.getEmotionImages(); + emotionList.value = data; + }); + + return { + emotionList, + onClickItem, + }; +}; diff --git a/src/components/page-watch-common/emotion-picker/hooks/use-emotion-picker.ts b/src/components/page-watch-common/emotion-picker/hooks/use-emotion-picker.ts new file mode 100644 index 0000000000000000000000000000000000000000..a72a30ece12cdb342a4d5b8ce09f568de9249125 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/hooks/use-emotion-picker.ts @@ -0,0 +1,77 @@ +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { ref } from 'vue'; +import { EmotionImageData } from '@polyv/live-watch-sdk'; + +/** + * 表情选择类型,face-普通表情,image-图片表情 + */ +export type EmotionPickerType = 'face' | 'image'; + +export const emotionPickerProps = () => ({ + /** 图片表情开关 */ + imageEmotionEnabled: PropUtils.bool.def(true), + /** 点击 face 表情后关闭,默认:true */ + clickFaceToClose: PropUtils.bool.def(true), +}); + +export const emotionPickerEmits = () => ({ + /** 点击表情 */ + 'click-face': emitFunc(), + /** 点击表情 */ + 'click-image': emitFunc(), + /** 点击删除(仅移动端) */ + 'click-delete': emitFunc(), + /** 点击发送(仅移动端) */ + 'click-send': emitFunc(), + /** 关闭 */ + close: emitFunc(), +}); + +export const useEmotionPicker = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + /** 当前面板类型 */ + const currentType = ref('face'); + + /** 切换类型 */ + function switchEmotionTab(type: EmotionPickerType) { + currentType.value = type; + } + + /** 点击 face 表情 */ + function onClickFace(title: string) { + emit('click-face', title); + if (props.clickFaceToClose) { + emit('close'); + } + } + + /** 点击图片表情 */ + function onClickImage(data: EmotionImageData) { + emit('click-image', data); + emit('close'); + } + + /** 点击删除按钮 */ + function onClickDelete() { + emit('click-delete'); + } + + /** 点击发送按钮 */ + function onClickSend() { + emit('click-send'); + } + + return { + currentType, + switchEmotionTab, + onClickFace, + onClickImage, + onClickDelete, + onClickSend, + }; +}; diff --git a/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-face-picker.vue b/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-face-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..02234b09842e5b1047b4a7fea1597e3f1672b5ef --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-face-picker.vue @@ -0,0 +1,44 @@ + + + + + + diff --git a/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-image-picker.vue b/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-image-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..b4ec2506556e9c5dde49eb76dddc795c702c00d0 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-image-picker.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-picker.vue b/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..e41cc14bb3ac137d5cb3725bcdfe937ecd5120dd --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/mobile-emotion-picker/mobile-emotion-picker.vue @@ -0,0 +1,192 @@ + + + + + + diff --git a/src/components/page-watch-common/emotion-picker/pc-emotion-picker/imgs/icon-face-emotion.png b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/imgs/icon-face-emotion.png new file mode 100644 index 0000000000000000000000000000000000000000..6ead6f7ca73d06e919802aa4c5cbbb3ddff5dee8 Binary files /dev/null and b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/imgs/icon-face-emotion.png differ diff --git a/src/components/page-watch-common/emotion-picker/pc-emotion-picker/imgs/icon-image-emotion.png b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/imgs/icon-image-emotion.png new file mode 100644 index 0000000000000000000000000000000000000000..2d109b2fb1f1793e7f6f4999c82f7572a40cd128 Binary files /dev/null and b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/imgs/icon-image-emotion.png differ diff --git a/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-face-picker.vue b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-face-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..323ef1d7cf3aee8dc807b62657e02f2a16aea0c7 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-face-picker.vue @@ -0,0 +1,49 @@ + + + + + + diff --git a/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-image-picker.vue b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-image-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..731a4aa91f7a1fa29df9f5592dd27a657b0b8f36 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-image-picker.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-picker.vue b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..dd8f0032013b6dfb007f8785ff54d2d984a02978 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/pc-emotion-picker/pc-emotion-picker.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/src/components/page-watch-common/emotion-picker/portrait-emotion-picker/portrait-emotion-picker.vue b/src/components/page-watch-common/emotion-picker/portrait-emotion-picker/portrait-emotion-picker.vue new file mode 100644 index 0000000000000000000000000000000000000000..db7b641556c35b281966540b8f012c8c9b4c0018 --- /dev/null +++ b/src/components/page-watch-common/emotion-picker/portrait-emotion-picker/portrait-emotion-picker.vue @@ -0,0 +1,201 @@ + + + + + + diff --git a/src/components/page-watch-common/follow/follow-btn/mobile-follow-btn.vue b/src/components/page-watch-common/follow/follow-btn/mobile-follow-btn.vue new file mode 100644 index 0000000000000000000000000000000000000000..38e3e6cb5817ebd602a88db0b60fc8526d1ef8db --- /dev/null +++ b/src/components/page-watch-common/follow/follow-btn/mobile-follow-btn.vue @@ -0,0 +1,31 @@ + + + + + + diff --git a/src/components/page-watch-common/follow/follow-btn/portrait-follow-btn.vue b/src/components/page-watch-common/follow/follow-btn/portrait-follow-btn.vue new file mode 100644 index 0000000000000000000000000000000000000000..e5c4570442da63edd6ac5833620bc1fdfe490e7f --- /dev/null +++ b/src/components/page-watch-common/follow/follow-btn/portrait-follow-btn.vue @@ -0,0 +1,34 @@ + + + + + + diff --git a/src/components/page-watch-common/follow/follow-layer/imgs/mobile-promotion-close-1.png b/src/components/page-watch-common/follow/follow-layer/imgs/mobile-promotion-close-1.png new file mode 100644 index 0000000000000000000000000000000000000000..fab094019acd245d99e88f338a3b92b2c958acf8 Binary files /dev/null and b/src/components/page-watch-common/follow/follow-layer/imgs/mobile-promotion-close-1.png differ diff --git a/src/components/page-watch-common/follow/follow-layer/imgs/mobile-promotion-close.png b/src/components/page-watch-common/follow/follow-layer/imgs/mobile-promotion-close.png new file mode 100644 index 0000000000000000000000000000000000000000..1dce0e32b14fc40757a11689a0639671284a5dc8 Binary files /dev/null and b/src/components/page-watch-common/follow/follow-layer/imgs/mobile-promotion-close.png differ diff --git a/src/components/page-watch-common/follow/follow-layer/imgs/promotion-background.png b/src/components/page-watch-common/follow/follow-layer/imgs/promotion-background.png new file mode 100644 index 0000000000000000000000000000000000000000..f7c5f5ff3bd99c24d3b6e9bc7927e15c82e588c3 Binary files /dev/null and b/src/components/page-watch-common/follow/follow-layer/imgs/promotion-background.png differ diff --git a/src/components/page-watch-common/follow/follow-layer/mobile-follow-layer.vue b/src/components/page-watch-common/follow/follow-layer/mobile-follow-layer.vue new file mode 100644 index 0000000000000000000000000000000000000000..fa0733df6edac3e3f09cd179fa0d25ceb195b6e1 --- /dev/null +++ b/src/components/page-watch-common/follow/follow-layer/mobile-follow-layer.vue @@ -0,0 +1,86 @@ + + + + + + diff --git a/src/components/page-watch-common/follow/follow-layer/portrait-follow-layer.vue b/src/components/page-watch-common/follow/follow-layer/portrait-follow-layer.vue new file mode 100644 index 0000000000000000000000000000000000000000..9f64dfed6be25c67e3dc437dbbca1eeafec2f318 --- /dev/null +++ b/src/components/page-watch-common/follow/follow-layer/portrait-follow-layer.vue @@ -0,0 +1,91 @@ + + + + + + diff --git a/src/components/page-watch-common/follow/hooks/use-follow-btn.ts b/src/components/page-watch-common/follow/hooks/use-follow-btn.ts new file mode 100644 index 0000000000000000000000000000000000000000..ff8007c2a5299f4e2f0d0ae63bfd9fb6b0f7aa8b --- /dev/null +++ b/src/components/page-watch-common/follow/hooks/use-follow-btn.ts @@ -0,0 +1,11 @@ +import { appEvents, eventBus } from '@/app/app-events'; + +export const useFollowBtn = () => { + function onClickFollowBtn() { + eventBus.$emit(appEvents.global.OpenFollowLayer, true); + } + + return { + onClickFollowBtn, + }; +}; diff --git a/src/components/page-watch-common/follow/hooks/use-follow-layer.ts b/src/components/page-watch-common/follow/hooks/use-follow-layer.ts new file mode 100644 index 0000000000000000000000000000000000000000..eabf42227e564bbe38273c96bf77b6061ac620eb --- /dev/null +++ b/src/components/page-watch-common/follow/hooks/use-follow-layer.ts @@ -0,0 +1,40 @@ +import { ref, watchEffect } from 'vue'; +import { PlvPopperManager } from '@/plugins/polyv-ui/admin-import'; + +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { useSimpleVisible } from '@/hooks/behaviors/use-simple-visible'; +import { useFollowSetting } from './use-follow-setting'; + +/** + * 关注弹层 hook + */ +export const useFollowLayer = () => { + const { followAutoShow } = useFollowSetting(); + + const layerRef = ref(); + + const { + visible: layerVisible, + setVisible: setLayerVisible, + show: showLayer, + close: closeLayer, + } = useSimpleVisible(followAutoShow.value); + + watchEffect(() => { + if (layerVisible.value && layerRef.value) { + PlvPopperManager.openPopper(layerRef.value); + } + }); + + useEventBusListener(appEvents.global.OpenFollowLayer, (visible: boolean) => { + setLayerVisible(visible); + }); + + return { + layerRef, + layerVisible, + setLayerVisible, + showLayer, + closeLayer, + }; +}; diff --git a/src/components/page-watch-common/follow/hooks/use-follow-setting.ts b/src/components/page-watch-common/follow/hooks/use-follow-setting.ts new file mode 100644 index 0000000000000000000000000000000000000000..7dbe7d8dca5da734c10d008b6d433e14be0f8686 --- /dev/null +++ b/src/components/page-watch-common/follow/hooks/use-follow-setting.ts @@ -0,0 +1,50 @@ +import { computed } from 'vue'; +import { ynToBool } from '@utils-ts/boolean'; +import { YN } from '@polyv/live-watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; + +/** + * 关注设置 hook + */ +export const useFollowSetting = () => { + const watchAppStore = useWatchAppStore(); + const channelStore = useChannelStore(); + + /** 是否自动打开 */ + const followAutoShow = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + return ynToBool(channelStore.channelDetail?.channelPromotion?.followAutoShow, YN.N); + }); + + /** 关注开关 */ + const followEnabled = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + return ynToBool(channelStore.channelDetail?.channelPromotion?.followEnabled, YN.N); + }); + + /** 关注入口文案 */ + const followEntrance = computed(() => { + return channelStore.channelDetail?.channelPromotion?.followEntrance || ''; + }); + + /** 关注图片 */ + const followImage = computed(() => { + return channelStore.channelDetail?.channelPromotion?.followImage; + }); + + /** 关注提示文案 */ + const followTips = computed(() => { + return channelStore.channelDetail?.channelPromotion?.followTips; + }); + + return { + followAutoShow, + followEnabled, + followEntrance, + followImage, + followTips, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/_style/cover-mobile-interact-receive.scss b/src/components/page-watch-common/interactive-receive/_style/cover-mobile-interact-receive.scss new file mode 100644 index 0000000000000000000000000000000000000000..5633fef4340613606958e7062027123f8cc4c707 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/_style/cover-mobile-interact-receive.scss @@ -0,0 +1,54 @@ +// @file 移动端的互动功能接收端样式覆盖 + +// 加载 +.plv-ia-loading-container { + color: #adadc0; +} + +// 卡片推送 +.plv-iar-push-card, +.plv-iar-push-card__modal { + z-index: 400; +} + +// 图文直播 +.plv-ia-tuwen-message-item { + .plv-ia-tuwen-message-item-content { + .pws-plv-ia-tuwen-message-item-content-name { + font-size: 12px; + color: #78A7ED; + } + + .pws-plv-ia-tuwen-message-item-content-time { + margin-top: 4px; + color: #595A66; + } + + .pws-plv-ia-tuwen-message-item-content-text { + color: #adadc0; + } + } +} + +// 条件抽奖 +.plv-iar-welfare-lottery__modal--normal, +.plv-iar-welfare-lottery__modal--gift-box, +.plv-iar-welfare-lottery__on-lottery-modal { + z-index: 11; +} + +// 商品推送 +.plv-iar-product-bubble { + pointer-events: none; +} +.plv-iar-product-bubble__wrap { + pointer-events: initial; +} + +// 问答 +.plv-ia-qa-ask-emotion { + height: 100%; + margin-top: 0 !important; + margin-bottom: 0 !important; + background-size: 32px; +} diff --git a/src/components/page-watch-common/interactive-receive/_style/cover-pc-interact-receive.scss b/src/components/page-watch-common/interactive-receive/_style/cover-pc-interact-receive.scss new file mode 100644 index 0000000000000000000000000000000000000000..4e09eeb90e2571438d2f689c92cc2ccf6712ebe2 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/_style/cover-pc-interact-receive.scss @@ -0,0 +1,70 @@ +// @file PC 端的互动功能接收端样式覆盖 + +// 投票 +.plv-iar-vote__main { + @include scrollbar; +} +@media (max-width: 1280px) { + .plv-iar-vote-item__title { + max-width: 204px !important; + font-size: 12px !important; + } + .plv-iar-vote-item__count { + font-size: 12px !important; + > span { + font-size: 18px !important; + } + } +} + +// 卡片推送 +.plv-iar-push-card, +.plv-iar-push-card__modal { + z-index: 400; +} + +// 问答 +.plv-ia-qa-emotions { + z-index: 500; +} + +// 条件抽奖 +.plv-iar-welfare-lottery__modal--normal, +.plv-iar-welfare-lottery__modal--gift-box, +.plv-iar-welfare-lottery__on-lottery-modal { + z-index: 400; +} + +// 报名抽奖 +.plv-ia-enroll-lottery__pc { + width: 100%; + overflow: hidden; +} + +// 抽奖 +.plv-on-lottery-default { + position: fixed; + top: 50%; + left: 50%; + z-index: 999; + transform: translate(-50%, -50%); +} + +// 商品推送 +.plv-iar-product-bubble { + pointer-events: none; +} +.plv-iar-product-bubble__wrap { + pointer-events: initial; +} + +// 公告 +.plv-iar-bulletin__list__item__content { + cursor: pointer; +} + +// 问答 +.plv-ia-qa-ask-emotion { + color: #adadc0; + background-size: 18px; +} diff --git a/src/components/page-watch-common/interactive-receive/_style/cover-portrait-interact-receive.scss b/src/components/page-watch-common/interactive-receive/_style/cover-portrait-interact-receive.scss new file mode 100644 index 0000000000000000000000000000000000000000..ff3cf60f27eae34b92c93451492902624a81a1ce --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/_style/cover-portrait-interact-receive.scss @@ -0,0 +1,57 @@ +// @file 竖屏端的互动功能接收端样式覆盖 + +// 按钮 +.plv-iar-default-btn { + background: $--color-primary-portrait !important; + + &:disabled { + opacity: 0.6; + } +} + +// 快速问答 +.plv-iar-quick-answer-default__btn { + background: $--color-primary-portrait !important; +} + +// 商品库 +.pws-iar-product-list-wrap { + padding: 0; +} +.pws-iar-product-list-wrap { + background: none; +} +.plv-iar-product__content { + background-color: initial; + + &::-webkit-scrollbar { + width: 4px; + height: 4px; + } +} +.pws-iar-product-list-number { + border-radius: 6px; +} +.pws-iar-product-list-cover { + border-radius: 10px; +} +.pws-iar-product-list-cover-number { + border-radius: 0 0 6px 0; +} +.pws-iar-product-list-button { + min-width: 72px; + height: 28px; + font-size: 12px; + line-height: 28px; + background: $--color-primary-portrait; +} +.plv-iar-product__buy-button.plv-iar-product__buy-button--disabled { + .pws-iar-product-list-button { + background: $--bg-color-portrait-disabled; + } +} + +// 问答 +.plv-ia-qa-ask-emotion { + margin-bottom: 0 !important; +} diff --git a/src/components/page-watch-common/interactive-receive/_types/index.ts b/src/components/page-watch-common/interactive-receive/_types/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca6187f1217432cd8a6f5274c6603d7e27b3e8db --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/_types/index.ts @@ -0,0 +1,83 @@ +import Vue, { ComponentOptions } from 'vue'; + +export type PolyvIRSceneComponentOption = ComponentOptions; + +/** + * PC 端入口 + */ +export interface PolyvIRSceneEntrance { + // 共有端 + BulletinTop: PolyvIRSceneComponentOption; + BulletinBubble: PolyvIRSceneComponentOption; + PortraitCheckIn: PolyvIRSceneComponentOption; + EnrollLotteryPendant: PolyvIRSceneComponentOption; + ProductBubble: PolyvIRSceneComponentOption; + ProductJob: PolyvIRSceneComponentOption; + ProductButton: PolyvIRSceneComponentOption; + PushCard: PolyvIRSceneComponentOption; + RedEnvelopePendant: PolyvIRSceneComponentOption; + RedEnvelopeEntrance: PolyvIRSceneComponentOption; + EvaluationPendant: PolyvIRSceneComponentOption; + + // PC 端下才有的 + PcBulletin: PolyvIRSceneComponentOption; + PcAnswerCard: PolyvIRSceneComponentOption; + PcQuickAnswer: PolyvIRSceneComponentOption; + PcCheckIn: PolyvIRSceneComponentOption; + PcEnrollLottery: PolyvIRSceneComponentOption; + PcFeedBack: PolyvIRSceneComponentOption; + PcOnLottery: PolyvIRSceneComponentOption; + PcLotteryRecord: PolyvIRSceneComponentOption; + PcLotteryEnd: PolyvIRSceneComponentOption; + PcProduct: PolyvIRSceneComponentOption; + PcQuestionAnswer: PolyvIRSceneComponentOption; + PcQuestionnarie: PolyvIRSceneComponentOption; + PcRedEnvelope: PolyvIRSceneComponentOption; + PcRedpackRain: PolyvIRSceneComponentOption; + PcEvaluationQuestionnaire: PolyvIRSceneComponentOption; + PcTopic: PolyvIRSceneComponentOption; + PcTuwen: PolyvIRSceneComponentOption; + PcVote: PolyvIRSceneComponentOption; + PcWelfareLottery: PolyvIRSceneComponentOption; + + // 移动端下才有的 + MobileBulletin: PolyvIRSceneComponentOption; + MobileAnnouncementBar: PolyvIRSceneComponentOption; + MobileAnswerCard: PolyvIRSceneComponentOption; + MobileQuickAnswer: PolyvIRSceneComponentOption; + MobileCheckIn: PolyvIRSceneComponentOption; + MobileEnrollLottery: PolyvIRSceneComponentOption; + MobileFeedBack: PolyvIRSceneComponentOption; + MobileOnLottery: PolyvIRSceneComponentOption; + MobileLotteryRecord: PolyvIRSceneComponentOption; + MobileLotteryEnd: PolyvIRSceneComponentOption; + MobileProduct: PolyvIRSceneComponentOption; + MobileQuestionAnswer: PolyvIRSceneComponentOption; + MobileQuestionnarie: PolyvIRSceneComponentOption; + MobileRedEnvelope: PolyvIRSceneComponentOption; + MobileRedpackRain: PolyvIRSceneComponentOption; + MobileEvaluationQuestionnaire: PolyvIRSceneComponentOption; + MobilePointRERecord: PolyvIRSceneComponentOption; + MobileTopic: PolyvIRSceneComponentOption; + MobileTuwen: PolyvIRSceneComponentOption; + MobileVote: PolyvIRSceneComponentOption; + MobileWelfareLottery: PolyvIRSceneComponentOption; +} + +/** + * 互动功能组件标识 + */ +export type PolyvIRSceneKey = keyof PolyvIRSceneEntrance; + +export interface PolyvIRSceneType { + /** PC 端入口 */ + entrancePc?: PolyvIRSceneEntrance; + /** 移动端入口 */ + entranceMobile?: PolyvIRSceneEntrance; +} + +declare global { + interface Window { + PolyvIRScene?: PolyvIRSceneType; + } +} diff --git a/src/components/page-watch-common/interactive-receive/announcement/announcement-top.vue b/src/components/page-watch-common/interactive-receive/announcement/announcement-top.vue new file mode 100644 index 0000000000000000000000000000000000000000..5ad6040f0da820f8c5eaab81eadc476aafa9670d --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/announcement-top.vue @@ -0,0 +1,24 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement-tips.ts b/src/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement-tips.ts new file mode 100644 index 0000000000000000000000000000000000000000..62322b21dbc24bdf17a0eee35d34021b2af73c2a --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement-tips.ts @@ -0,0 +1,82 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { local } from '@just4/storage'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { getStorageKey } from '@/assets/utils/storage'; + +/** 公告的提示状态 */ +export enum AnnouncementTipsStatus { + /** 显示中、未关闭提示 */ + showing = 'showing', + /** 已显示过、已点击关闭 */ + closed = 'closed', +} + +/** localStorage 记录的 key */ +export const tipsStorageKey = getStorageKey('announcement-tips-status'); + +/** 获取 storage 中的状态值 */ +export const getStorageTipsStatus = (): AnnouncementTipsStatus | undefined => { + return local.get(tipsStorageKey) as AnnouncementTipsStatus; +}; + +/** 设置 storage 中的状态值 */ +export const setStorageTipsStatus = (status: AnnouncementTipsStatus) => { + local.set(tipsStorageKey, status); +}; + +/** + * @hook 互动-公告-公告提示 + */ +export const useAnnouncementTips = () => { + /** 公告提示是否显示 */ + const { announcementTipsVisible } = storeDefinitionToRefs(useInteractReceiveStore); + + /** 设置公告提示显示状态 */ + const setAnnouncementTipsVisible = (visible = true) => { + announcementTipsVisible.value = visible; + }; + + /** 显示公告提示,如果 storage 中记录的是 closed 则不响应 */ + const openAnnouncementTips = () => { + const tipsStatus = getStorageTipsStatus(); + if (tipsStatus !== AnnouncementTipsStatus.closed) { + setAnnouncementTipsVisible(true); + setStorageTipsStatus(AnnouncementTipsStatus.showing); + } + }; + + /** 关闭提示 */ + const closeAnnouncementTips = () => { + // 设为提示关闭 + setStorageTipsStatus(AnnouncementTipsStatus.closed); + setAnnouncementTipsVisible(false); + }; + + /** 处理点击公告提示 */ + const onClickAnnouncementTips = () => { + // 打开公告弹层 + eventBus.$emit(appEvents.interaction.OpenAnnouncementList, true); + closeAnnouncementTips(); + }; + + /** 处理点击公告提示关闭 */ + const onClickAnnouncementTipsClose = () => { + closeAnnouncementTips(); + }; + + return { + AnnouncementTipsStatus, + announcementTipsVisible, + getStorageTipsStatus, + setStorageTipsStatus, + + setAnnouncementTipsVisible, + + openAnnouncementTips, + closeAnnouncementTips, + + onClickAnnouncementTips, + onClickAnnouncementTipsClose, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement.ts b/src/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c3132634aa1366f1f3fda6e1da002e4ed25ebd2 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement.ts @@ -0,0 +1,118 @@ +import { computed, onBeforeMount, onBeforeUnmount, ref, unref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; +import { appEvents, eventBus } from '@/app/app-events'; + +import { translate } from '@/assets/lang'; +import { IarAnnouncementInstance } from '../types/announcement-types'; +import { useAnnouncementTips } from './use-announcement-tips'; + +/** + * @hook 互动-公告 + */ +export const useAnnouncement = () => { + const { openAnnouncementTips } = useAnnouncementTips(); + + const watchCore = getWatchCore(); + + /** 公告 SDK 实例 */ + const announcementSdk = watchCore.interactReceive.getAnnouncement(); + /** 公告是否显示 */ + const announcementVisible = ref(false); + /** 公告列表按钮是否显示 */ + const listBtnVisible = ref(false); + /** 是否正在显示详情 */ + const isShowDetailing = ref(false); + + const announcementRef = ref(); + + /** 公告标题类型 */ + const announcementTitleType = ref('detail'); + /** 公告标题 */ + const announcementTitle = computed(() => { + if (announcementTitleType.value === 'list') { + return translate('interact.announcement.titleList'); + } + return translate('interact.announcement.titleDetail'); + }); + /** 处理标题更改 */ + function onTitleChange(type: string) { + announcementTitleType.value = type; + } + + /** 设置公告显示状态 */ + function setAnnouncementVisible(visible = true) { + announcementVisible.value = visible; + } + + /** 显示公告列表 */ + function openAnnouncementList() { + listBtnVisible.value = false; + isShowDetailing.value = false; + setAnnouncementVisible(true); + const instance = unref(announcementRef); + instance?.showBulletinList(); + } + + /** 显示某个公告详情 */ + function openAnnouncementDetail(data: unknown) { + setAnnouncementVisible(true); + const instance = unref(announcementRef); + instance?.showBulletinDetail(data); + listBtnVisible.value = true; + isShowDetailing.value = true; + } + + /** 处理显示公告详情(socket 事件) */ + function onShowDetailFromSocket() { + setAnnouncementVisible(true); + listBtnVisible.value = true; + } + + function onLookover() { + isShowDetailing.value = true; + } + + /** 点击返回 */ + function onClickBack() { + openAnnouncementList(); + } + + /** 处理关闭弹层 */ + const onCloseDialog = () => { + // 设为已读 + const instance = unref(announcementRef); + instance?.markAnnounceReaded(); + + listBtnVisible.value = false; + + if (announcementSdk.content) { + // 打开公告提示 + openAnnouncementTips(); + } + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenAnnouncementList, openAnnouncementList); + eventBus.$on(appEvents.interaction.OpenAnnouncementDetail, openAnnouncementDetail); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenAnnouncementList, openAnnouncementList); + eventBus.$off(appEvents.interaction.OpenAnnouncementDetail, openAnnouncementDetail); + }); + + return { + listBtnVisible, + isShowDetailing, + announcementSdk, + announcementRef, + announcementVisible, + announcementTitle, + onTitleChange, + setAnnouncementVisible, + onCloseDialog, + onShowDetailFromSocket, + onLookover, + onClickBack, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/announcement/imgs/icon-bulletin.png b/src/components/page-watch-common/interactive-receive/announcement/imgs/icon-bulletin.png new file mode 100644 index 0000000000000000000000000000000000000000..65ae053b811fdba87f5cad5a4d849cdf4aa2faef Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/announcement/imgs/icon-bulletin.png differ diff --git a/src/components/page-watch-common/interactive-receive/announcement/mobile-announcement-popup.vue b/src/components/page-watch-common/interactive-receive/announcement/mobile-announcement-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..b7649260aadb4fde6ee8f78bf48429f4053bccfc --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/mobile-announcement-popup.vue @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/announcement/pc-announcement-dialog.vue b/src/components/page-watch-common/interactive-receive/announcement/pc-announcement-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..0e467889f2de0e27233647226bf38cd505f12725 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/pc-announcement-dialog.vue @@ -0,0 +1,40 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/announcement/portrait-annoucement-bar.vue b/src/components/page-watch-common/interactive-receive/announcement/portrait-annoucement-bar.vue new file mode 100644 index 0000000000000000000000000000000000000000..dcba6a5ce6c4de97568d8fdb8a7d2bde087ccfa2 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/portrait-annoucement-bar.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/announcement/portrait-announcement-entrance.vue b/src/components/page-watch-common/interactive-receive/announcement/portrait-announcement-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..124d868296b388d4e8ddeee7c0ae70acb24f9fe5 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/portrait-announcement-entrance.vue @@ -0,0 +1,76 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/announcement/types/announcement-types.ts b/src/components/page-watch-common/interactive-receive/announcement/types/announcement-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..74d6a800afd427daedd9c1fb13957c58196beb01 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/announcement/types/announcement-types.ts @@ -0,0 +1,11 @@ +/** + * 互动接收端商品列表 vue 实例类型 + */ +export interface IarAnnouncementInstance { + /** 标记公告已读 */ + markAnnounceReaded: () => void; + /** 显示公告列表 */ + showBulletinList: () => void; + /** 显示公告详情 */ + showBulletinDetail: (detail: unknown) => void; +} diff --git a/src/components/page-watch-common/interactive-receive/answer-card/hooks/imgs/icon-answer-card.png b/src/components/page-watch-common/interactive-receive/answer-card/hooks/imgs/icon-answer-card.png new file mode 100644 index 0000000000000000000000000000000000000000..603391fae6af3b226ccd0ae66776f1f3a0a45571 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/answer-card/hooks/imgs/icon-answer-card.png differ diff --git a/src/components/page-watch-common/interactive-receive/answer-card/hooks/imgs/icon-answer-card_p.png b/src/components/page-watch-common/interactive-receive/answer-card/hooks/imgs/icon-answer-card_p.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5e1fcdbd00ffb0053c3806c8e757a2d06998bb Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/answer-card/hooks/imgs/icon-answer-card_p.png differ diff --git a/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-answer-card-entrance.ts b/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-answer-card-entrance.ts new file mode 100644 index 0000000000000000000000000000000000000000..bcb7c527922494a5f7d85c6f1729770413c6f525 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-answer-card-entrance.ts @@ -0,0 +1,69 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { + useInteractReceiveStore, + InteractiveEntranceData, +} from '@/store/use-interact-receive-store'; + +/** + * @hook 互动-答题卡入口 + */ +export const useAnswerCardEntrance = () => { + const interactReceiveStore = useInteractReceiveStore(); + + /** 答题卡入口数据 */ + const answerCardEntranceData: InteractiveEntranceData = { + type: 'answer-card', + name: 'interact.answerCard.title', + icon: require('./imgs/icon-answer-card.png'), + icon2: require('./imgs/icon-answer-card_p.png'), + onClick: () => eventBus.$emit(appEvents.interaction.OpenAnswerCard, true), + }; + + /** 设置答题卡二次入口 */ + const setAnswerCardEntrance = () => { + interactReceiveStore.setInteractiveEntrance(answerCardEntranceData); + }; + + /** 移除答题卡二次入口 */ + const removeAnswerCardEntrance = () => { + interactReceiveStore.removeInteractiveEntrance(answerCardEntranceData.type); + }; + + return { + answerCardEntranceData, + setAnswerCardEntrance, + removeAnswerCardEntrance, + }; +}; + +/** + * 快速答题卡入口 hooks + */ +export const useQuickAnswerCardEntrance = () => { + const interactReceiveStore = useInteractReceiveStore(); + + /** 快速答题卡入口数据 */ + const quickAnswerCardEntranceData: InteractiveEntranceData = { + type: 'quick-answer-card', + name: 'interact.answerCard.title', + icon: require('./imgs/icon-answer-card.png'), + icon2: require('./imgs/icon-answer-card_p.png'), + onClick: () => eventBus.$emit(appEvents.interaction.OpenQuickAnswerCard, true), + }; + + /** 设置快速答题卡二次入口 */ + const setQuickAnswerCardEntrance = () => { + interactReceiveStore.setInteractiveEntrance(quickAnswerCardEntranceData); + }; + + /** 移除快速答题卡二次入口 */ + const removeQuickAnswerCardEntrance = () => { + interactReceiveStore.removeInteractiveEntrance(quickAnswerCardEntranceData.type); + }; + + return { + quickAnswerCardEntranceData, + setQuickAnswerCardEntrance, + removeQuickAnswerCardEntrance, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-answer-card.ts b/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-answer-card.ts new file mode 100644 index 0000000000000000000000000000000000000000..610b2c3447a7511dbb0fe43554352d832cb68d3a --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-answer-card.ts @@ -0,0 +1,93 @@ +import { onBeforeMount, onBeforeUnmount, ref, computed, unref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; + +import { AllAnswerCardStatus, IarAnswerCardInstance } from '../types/answer-card-types'; +import { useAnswerCardEntrance } from './use-answer-card-entrance'; + +/** + * @hook 互动-答题卡 + */ +export const useAnswerCard = () => { + const { setAnswerCardEntrance, removeAnswerCardEntrance } = useAnswerCardEntrance(); + + const watchCore = getWatchCore(); + /** 答题卡组件节点 */ + const answerCardRef = ref(); + /** 答题卡 SDK 实例 */ + const answerCardSdk = watchCore.interactReceive.getAnswerCard(); + /** 答题卡是否显示 */ + const answerCardVisible = ref(false); + /** 答题卡状态 */ + const answerCardStatus = ref(AllAnswerCardStatus.isShowQuestion); + + /** 答题卡标题 */ + const answerCardTitle = computed(() => { + let title = ''; + switch (unref(answerCardStatus)) { + case AllAnswerCardStatus.isShowQuestion: + title = translate('interact.answerCard.title'); + break; + case AllAnswerCardStatus.isShowResult: + title = answerCardSdk.curSubmittedAnswer + ? translate('interact.answerCard.checkResultTitle') + : translate('interact.answerCard.notSubmitted'); + break; + case AllAnswerCardStatus.isShowAnswer: + title = answerCardSdk.curSubmittedAnswer + ? translate('interact.answerCard.checkAnswerTitle') + : translate('interact.answerCard.notSubmitted'); + break; + } + return title; + }); + + /** 设置答题卡显示状态 */ + const setAnswerCardVisible = (visible = true) => { + answerCardVisible.value = visible; + + // 显示答题中的状态,设置二次入口 + const answerCardInstance = unref(answerCardRef); + if ( + visible && + answerCardInstance && + answerCardInstance.status === AllAnswerCardStatus.isShowQuestion + ) { + setAnswerCardEntrance(); + } + }; + + /** 处理答题卡状态修改 */ + const onStatusChanged = (status: AllAnswerCardStatus) => { + answerCardStatus.value = status; + }; + + /** 处理提交答题卡 */ + const onSubmitSuccess = () => { + removeAnswerCardEntrance(); + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenAnswerCard, setAnswerCardVisible); + answerCardSdk.on(answerCardSdk.events.STOP_TEST_QUESTION, removeAnswerCardEntrance); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenAnswerCard, setAnswerCardVisible); + answerCardSdk.off(answerCardSdk.events.STOP_TEST_QUESTION, removeAnswerCardEntrance); + }); + + return { + AllAnswerCardStatus, + answerCardRef, + answerCardSdk, + answerCardVisible, + answerCardTitle, + answerCardStatus, + setAnswerCardVisible, + onStatusChanged, + onSubmitSuccess, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-quick-answer-card.ts b/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-quick-answer-card.ts new file mode 100644 index 0000000000000000000000000000000000000000..f7a17379ddea62a010dd7c6a4bb543932b69ddc7 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/hooks/use-quick-answer-card.ts @@ -0,0 +1,68 @@ +import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; +import { appEvents, eventBus } from '@/app/app-events'; + +import { translate } from '@/assets/lang'; + +import { IarAnswerQuestionTypes } from '@polyv/live-watch-sdk'; +import { QuestionData } from '../types/answer-card-types'; +import { useQuickAnswerCardEntrance } from './use-answer-card-entrance'; + +/** + * @hook 互动-答题卡-快速问答 + */ +export const useQuickAnswerCard = () => { + const { setQuickAnswerCardEntrance, removeQuickAnswerCardEntrance } = + useQuickAnswerCardEntrance(); + + const watchCore = getWatchCore(); + /** 答题卡 SDK 实例 */ + const answerCardSdk = watchCore.interactReceive.getAnswerCard(); + /** 快速问答是否显示 */ + const quickAnswerCardVisible = ref(false); + /** 快速问答类型 */ + const quickAnswerCardType = ref(IarAnswerQuestionTypes.Radio); + + /** 快速问答标题 */ + const quickAnswerCardTitle = computed(() => { + return quickAnswerCardType.value === IarAnswerQuestionTypes.CheckBox + ? translate('interact.answerCard.multiChoice') + : translate('interact.answerCard.singleChoice'); + }); + + /** 设置快速问答显示状态 */ + const setQuickAnswerCardVisible = (visible = true, question?: QuestionData) => { + quickAnswerCardVisible.value = visible; + + // 显示答题中的状态,设置二次入口 + if (visible) { + setQuickAnswerCardEntrance(); + } + + if (question && question.type) { + quickAnswerCardType.value = question.type; + } + }; + + /** 处理提交答题卡 */ + const onSubmitSuccess = () => { + removeQuickAnswerCardEntrance(); + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenQuickAnswerCard, setQuickAnswerCardVisible); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenQuickAnswerCard, setQuickAnswerCardVisible); + }); + + return { + answerCardSdk, + quickAnswerCardVisible, + quickAnswerCardTitle, + quickAnswerCardType, + setQuickAnswerCardVisible, + onSubmitSuccess, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/answer-card/mobile-answer-card-popup.vue b/src/components/page-watch-common/interactive-receive/answer-card/mobile-answer-card-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..d2c39fc1afa76d2adb646c5130ae1b6b1d8a965f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/mobile-answer-card-popup.vue @@ -0,0 +1,55 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/answer-card/mobile-quick-answer-card.vue b/src/components/page-watch-common/interactive-receive/answer-card/mobile-quick-answer-card.vue new file mode 100644 index 0000000000000000000000000000000000000000..112f22d141b9afc74120d78c71630e7192508a64 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/mobile-quick-answer-card.vue @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/answer-card/pc-answer-card-dialog.vue b/src/components/page-watch-common/interactive-receive/answer-card/pc-answer-card-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..ab7b84ae9e7ade61649c9e7647dd7f955b30a912 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/pc-answer-card-dialog.vue @@ -0,0 +1,43 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/answer-card/pc-quick-answer-card-dialog.vue b/src/components/page-watch-common/interactive-receive/answer-card/pc-quick-answer-card-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..41b9d5e98e11fa41658a03aeaa578aebeccbbfd0 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/pc-quick-answer-card-dialog.vue @@ -0,0 +1,41 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/answer-card/types/answer-card-types.ts b/src/components/page-watch-common/interactive-receive/answer-card/types/answer-card-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..85a3fb321b32444a33d1985d8c7c264a075be7ad --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/answer-card/types/answer-card-types.ts @@ -0,0 +1,22 @@ +import { IarAnswerQuestionTypes } from '@polyv/live-watch-sdk'; + +/** 所有答题卡状态 */ +export enum AllAnswerCardStatus { + /** 正在显示答题中的问题 */ + isShowQuestion = 'isShowQuestion', + /** 正在显示问卷结果 */ + isShowResult = 'isShowResult', + /** 正在显示问卷答案 */ + isShowAnswer = 'isShowAnswer', +} + +export interface IarAnswerCardInstance { + status?: AllAnswerCardStatus; + /** 返回到结果 */ + backToResult?: () => void; +} + +export interface QuestionData { + /** 问题类型 */ + type?: IarAnswerQuestionTypes; +} diff --git a/src/components/page-watch-common/interactive-receive/check-in/hooks/imgs/icon-check-in.png b/src/components/page-watch-common/interactive-receive/check-in/hooks/imgs/icon-check-in.png new file mode 100644 index 0000000000000000000000000000000000000000..857b78fc30d955bad45222ffaee3a2718657a264 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/check-in/hooks/imgs/icon-check-in.png differ diff --git a/src/components/page-watch-common/interactive-receive/check-in/hooks/imgs/icon-check-in_p.png b/src/components/page-watch-common/interactive-receive/check-in/hooks/imgs/icon-check-in_p.png new file mode 100644 index 0000000000000000000000000000000000000000..01a651075173db049a7507db8b25b9bb75528825 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/check-in/hooks/imgs/icon-check-in_p.png differ diff --git a/src/components/page-watch-common/interactive-receive/check-in/hooks/use-check-in-entrance.ts b/src/components/page-watch-common/interactive-receive/check-in/hooks/use-check-in-entrance.ts new file mode 100644 index 0000000000000000000000000000000000000000..8884caf3bb12c14036d9e39b6f1fe30d010ac07b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/check-in/hooks/use-check-in-entrance.ts @@ -0,0 +1,37 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { + InteractiveEntranceData, + useInteractReceiveStore, +} from '@/store/use-interact-receive-store'; + +/** + * @hook 互动-签到入口 + */ +export const useCheckInEntrance = () => { + const interactReceiveStore = useInteractReceiveStore(); + + /** 问卷入口数据 */ + const checkInEntranceData: InteractiveEntranceData = { + type: 'check-in', + name: 'interact.checkIn.title', + icon: require('./imgs/icon-check-in.png'), + icon2: require('./imgs/icon-check-in_p.png'), + onClick: () => eventBus.$emit(appEvents.interaction.OpenCheckIn, true), + }; + + /** 设置签到二次入口 */ + const setCheckInEntrance = () => { + interactReceiveStore.setInteractiveEntrance(checkInEntranceData); + }; + + /** 移除签到二次入口 */ + const removeCheckInEntrance = () => { + interactReceiveStore.removeInteractiveEntrance(checkInEntranceData.type); + }; + + return { + checkInEntranceData, + setCheckInEntrance, + removeCheckInEntrance, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/check-in/hooks/use-check-in.ts b/src/components/page-watch-common/interactive-receive/check-in/hooks/use-check-in.ts new file mode 100644 index 0000000000000000000000000000000000000000..e053221b13a7631aebd13288f1e5255ac58adff3 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/check-in/hooks/use-check-in.ts @@ -0,0 +1,52 @@ +import { onBeforeMount, onBeforeUnmount, ref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; +import { useCheckInEntrance } from './use-check-in-entrance'; + +/** + * @hook 互动-签到 + */ +export const useCheckIn = () => { + const { setCheckInEntrance, removeCheckInEntrance } = useCheckInEntrance(); + + const watchCore = getWatchCore(); + /** 签到 SDK 实例 */ + const checkInSdk = watchCore.interactReceive.getCheckIn(); + /** 签到是否显示 */ + const checkInVisible = ref(false); + /** 签到标题 */ + const checkInTitle = translate('interact.checkIn.title'); + + /** 设置签到是否显示 */ + const setCheckInVisible = (visible = true) => { + checkInVisible.value = visible; + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenCheckIn, setCheckInVisible); + checkInSdk.on(checkInSdk.events.SIGN_IN, setCheckInEntrance); + checkInSdk.on(checkInSdk.events.SIGN_IN_SUCCESS, removeCheckInEntrance); + checkInSdk.on(checkInSdk.events.SIGN_IN_FINISH, removeCheckInEntrance); + checkInSdk.on(checkInSdk.events.STOP_SIGN_IN, removeCheckInEntrance); + checkInSdk.on(checkInSdk.events.SIGN_IN_FAIL, removeCheckInEntrance); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenCheckIn, setCheckInVisible); + checkInSdk.off(checkInSdk.events.SIGN_IN, setCheckInEntrance); + checkInSdk.off(checkInSdk.events.SIGN_IN_SUCCESS, removeCheckInEntrance); + checkInSdk.off(checkInSdk.events.SIGN_IN_FINISH, removeCheckInEntrance); + checkInSdk.off(checkInSdk.events.STOP_SIGN_IN, removeCheckInEntrance); + checkInSdk.off(checkInSdk.events.SIGN_IN_FAIL, removeCheckInEntrance); + }); + + return { + checkInSdk, + checkInVisible, + checkInTitle, + setCheckInVisible, + removeCheckInEntrance, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/check-in/mobile-check-in-popup.vue b/src/components/page-watch-common/interactive-receive/check-in/mobile-check-in-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..7b3a6f4737bc7d35b54aba061bb3a1fb00bcadab --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/check-in/mobile-check-in-popup.vue @@ -0,0 +1,34 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/check-in/pc-check-in-dialog.vue b/src/components/page-watch-common/interactive-receive/check-in/pc-check-in-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..a0403ef4385673a77638273cb3e20f3f797de44b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/check-in/pc-check-in-dialog.vue @@ -0,0 +1,39 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/check-in/portrait-check-in-popup.vue b/src/components/page-watch-common/interactive-receive/check-in/portrait-check-in-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..f4a027dc13fb629b38a3de7fca7fe6b62e05c56f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/check-in/portrait-check-in-popup.vue @@ -0,0 +1,19 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/enroll-lottery/enroll-lottery-pendant.vue b/src/components/page-watch-common/interactive-receive/enroll-lottery/enroll-lottery-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..9a0420eb3fc1e141871f85dd4fc7333f58b110a7 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/enroll-lottery/enroll-lottery-pendant.vue @@ -0,0 +1,46 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/enroll-lottery/hooks/use-enroll-lottery.ts b/src/components/page-watch-common/interactive-receive/enroll-lottery/hooks/use-enroll-lottery.ts new file mode 100644 index 0000000000000000000000000000000000000000..3713532d73c713b5cfa42cca4f10326433cd2745 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/enroll-lottery/hooks/use-enroll-lottery.ts @@ -0,0 +1,33 @@ +import { onBeforeUnmount, onMounted, ref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +/** + * @hook 互动-抽奖-报名抽奖 + */ +export const useEnrollLottery = () => { + const watchCore = getWatchCore(); + /** 报名抽奖实例 */ + const enrollLotterySdk = watchCore.interactReceive.getEnrollLottery(); + + const dialogVisible = ref(false); + + /** 更新弹层显示状态 */ + function updateDialogVisible(visible = true) { + dialogVisible.value = !!visible; + } + + onMounted(() => { + eventBus.$on(appEvents.interaction.OpenEnrollLottery, updateDialogVisible); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenEnrollLottery, updateDialogVisible); + }); + + return { + enrollLotterySdk, + dialogVisible, + updateDialogVisible, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/enroll-lottery/mobile-enroll-lottery-popup.vue b/src/components/page-watch-common/interactive-receive/enroll-lottery/mobile-enroll-lottery-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..aa36ebf88e2288c6bde4ac476bc6e418795f8026 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/enroll-lottery/mobile-enroll-lottery-popup.vue @@ -0,0 +1,34 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/enroll-lottery/pc-enroll-lottery-dialog.vue b/src/components/page-watch-common/interactive-receive/enroll-lottery/pc-enroll-lottery-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..4078f02286e1dc5d4aa6daaa29874a758f0b780e --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/enroll-lottery/pc-enroll-lottery-dialog.vue @@ -0,0 +1,26 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/feed-back/hooks/use-feed-back.ts b/src/components/page-watch-common/interactive-receive/feed-back/hooks/use-feed-back.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc5640e6fa9da90e15d09c0d91e40da19281bead --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/feed-back/hooks/use-feed-back.ts @@ -0,0 +1,40 @@ +import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; + +/** + * @hook 互动-反馈 + * */ +export const useFeedBack = () => { + const watchCore = getWatchCore(); + /** 反馈 SDK 实例 */ + const feedBackSdk = watchCore.interactReceive.getFeedBack(); + + /** 反馈标题 */ + const feedBackTitle = computed(() => translate('interact.feedBack.title')); + + /** 反馈是否显示 */ + const feedBackVisible = ref(false); + + /** 设置反馈是否显示 */ + const setFeedBackVisible = (visible = true) => { + feedBackVisible.value = visible; + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenFeedBack, setFeedBackVisible); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenFeedBack, setFeedBackVisible); + }); + + return { + feedBackSdk, + feedBackTitle, + feedBackVisible, + setFeedBackVisible, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/feed-back/mobile-feed-back-popup.vue b/src/components/page-watch-common/interactive-receive/feed-back/mobile-feed-back-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..f672f1b29fc493eeb9ed8ceebd4cbc9ef3f401a5 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/feed-back/mobile-feed-back-popup.vue @@ -0,0 +1,26 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/feed-back/pc-feed-back-dialog.vue b/src/components/page-watch-common/interactive-receive/feed-back/pc-feed-back-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..b9731f3fd1472d6250a54479653e670344048831 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/feed-back/pc-feed-back-dialog.vue @@ -0,0 +1,27 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/interactive-entrance/imgs/icon-task.png b/src/components/page-watch-common/interactive-receive/interactive-entrance/imgs/icon-task.png new file mode 100644 index 0000000000000000000000000000000000000000..799e430163730e264bbc811d67091a5ce9edcc5f Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/interactive-entrance/imgs/icon-task.png differ diff --git a/src/components/page-watch-common/interactive-receive/interactive-entrance/mobile-interactive-entrance.vue b/src/components/page-watch-common/interactive-receive/interactive-entrance/mobile-interactive-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..da756263f7cdac930260d9f25544443820aed158 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/interactive-entrance/mobile-interactive-entrance.vue @@ -0,0 +1,81 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/interactive-entrance/pc-interactive-entrance.vue b/src/components/page-watch-common/interactive-receive/interactive-entrance/pc-interactive-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..55219452f12de3264acfff0f8c0187f36dc17b41 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/interactive-entrance/pc-interactive-entrance.vue @@ -0,0 +1,137 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/interactive-entrance/portrait-interactive-entrance.vue b/src/components/page-watch-common/interactive-receive/interactive-entrance/portrait-interactive-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..376bd492b6931bb9e6fc5e01ba5939344cbea91b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/interactive-entrance/portrait-interactive-entrance.vue @@ -0,0 +1,153 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/interactive-entrance/use-interactive-entrance.ts b/src/components/page-watch-common/interactive-receive/interactive-entrance/use-interactive-entrance.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c067ca31b63139f8cb0ac639eb50996571f92c1 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/interactive-entrance/use-interactive-entrance.ts @@ -0,0 +1,37 @@ +/** + * @file 互动功能二次入口 hook + */ +import { computed } from 'vue'; +import { + InteractiveEntranceData, + useInteractReceiveStore, +} from '@/store/use-interact-receive-store'; +import { isFunction } from '@/assets/utils/function'; + +/** + * @hook 互动二次入口 + */ +export const useInteractiveEntrance = () => { + const interactReceiveStore = useInteractReceiveStore(); + + /** 入口列表是否显示 */ + const entrancesVisible = computed(() => { + return interactReceiveStore.interactiveEntrance.length > 0; + }); + + /** + * 打开互动功能 + * @param entranceData 入口数据 + */ + function openInteractive(entranceData: InteractiveEntranceData) { + // 响应对应的 onClick 回调 + if (isFunction(entranceData.onClick)) { + entranceData.onClick(); + } + } + + return { + entrancesVisible, + openInteractive, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/load-iar-ui.ts b/src/components/page-watch-common/interactive-receive/load-iar-ui.ts new file mode 100644 index 0000000000000000000000000000000000000000..c4270a1aded13d8f0dc37e044779e901aa9766a5 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/load-iar-ui.ts @@ -0,0 +1,89 @@ +import { isMobile } from '@/assets/utils/browser'; +import { getWatchCore } from '@/core/watch-sdk'; +import { loadScript } from '@just4/load-script'; +import { SdkLoaderEnv } from '@polyv/live-watch-sdk'; +import { PolyvIRSceneComponentOption, PolyvIRSceneKey, PolyvIRSceneType } from './_types'; + +// 判断当前加载移动端和PC端互动 +const interactAppEntrance = isMobile ? 'entranceMobile' : 'entrancePc'; + +/** 根据观看页 SDK 环境获取互动 UI 层对应环境的配置 */ +function getIarUIConfigByWatchSdkEnv() { + const watchCore = getWatchCore(); + const env = watchCore.getSdkLoaderEnv(); + const domainInfo = watchCore.domain.getDomainInfo(); + + const results: Record = { + [SdkLoaderEnv.Prod]: { + domain: domainInfo.webSdkDomain, + version: 'rc-20230413', + }, + [SdkLoaderEnv.Rc]: { + domain: domainInfo.webSdkDomain, + version: 'rc-20230413', + }, + [SdkLoaderEnv.Test]: { + domain: domainInfo.webSdkTest1Domain, + version: 'beta-20230413', + }, + }; + + return results[env]; +} + +/** + * 获取互动功能 UI 的加载地址 + */ +function getIarUILoaderUrl() { + const { domain, version } = getIarUIConfigByWatchSdkEnv(); + return `${domain}/interactions-receive-sdk-ui-default/${version}/lib/${interactAppEntrance}/${interactAppEntrance}.umd.min.js`; +} + +let iarUILoader: Promise | undefined; + +/** + * 加载互动功能 UI + */ +export function loadInteractiveReceiveUI(): Promise { + if (window.PolyvIRScene) { + return Promise.resolve(window.PolyvIRScene); + } + + if (!iarUILoader) { + const url = getIarUILoaderUrl(); + iarUILoader = new Promise((resolve, reject) => { + (async () => { + await loadScript(url); + if (window.PolyvIRScene) { + resolve(window.PolyvIRScene); + } else { + reject(new Error('loadInteractiveReceiveUI fail, window.PolyvIRScene is undefined')); + } + })(); + }); + } + + return iarUILoader; +} + +/** + * 获取互动功能 UI 组件配置 + * @param iarKey 互动组件 + */ +export function getIarComponent(iarKey: PolyvIRSceneKey): PolyvIRSceneComponentOption { + if (!window.PolyvIRScene) { + throw new Error('getIarComponent fail, window.PolyvIRScene is undefined'); + } + + const PolyvIRScene = window.PolyvIRScene; + const entranceApp = PolyvIRScene[interactAppEntrance]; + if (!entranceApp) { + throw new Error(`getIarComponent fail, ${interactAppEntrance} is undefined`); + } + + if (!entranceApp[iarKey]) { + throw new Error(`IarEntranceApp not exist ${iarKey}`); + } + + return entranceApp[iarKey]; +} diff --git a/src/components/page-watch-common/interactive-receive/lottery/hooks/imgs/icon-lottery-record.png b/src/components/page-watch-common/interactive-receive/lottery/hooks/imgs/icon-lottery-record.png new file mode 100644 index 0000000000000000000000000000000000000000..42c33a831bc5c87decc3891d2858a826d6fd4507 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/lottery/hooks/imgs/icon-lottery-record.png differ diff --git a/src/components/page-watch-common/interactive-receive/lottery/hooks/imgs/icon-lottery-record_p.png b/src/components/page-watch-common/interactive-receive/lottery/hooks/imgs/icon-lottery-record_p.png new file mode 100644 index 0000000000000000000000000000000000000000..7d81ce1962b0dd5966064a4fd18bffa587d70793 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/lottery/hooks/imgs/icon-lottery-record_p.png differ diff --git a/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-animation.ts b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-animation.ts new file mode 100644 index 0000000000000000000000000000000000000000..90dc280165b9c6aec26fd7248e900fc0e3b6d063 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-animation.ts @@ -0,0 +1,46 @@ +import { ref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { IarLotteryStatus } from '@polyv/live-watch-sdk'; + +/** + * @hook 互动-抽奖-抽奖动画 + * */ +export const useLotteryAnimation = () => { + const watchCore = getWatchCore(); + /** 抽奖 SDK 实例 */ + const lotterySdk = watchCore.interactReceive.getLottery(); + /** 抽奖动画是否显示 */ + const lotteryAnimationVisible = ref(false); + /** 当前抽奖状态 */ + const currentLotteryStatus = ref(); + + /** 处理抽奖状态改变 */ + function onLotteryStatusChange(status: IarLotteryStatus) { + currentLotteryStatus.value = status; + + switch (status) { + case IarLotteryStatus.running: + lotteryAnimationVisible.value = true; + eventBus.$emit(appEvents.interaction.OpenLotteryResult, { visible: false }); + break; + case IarLotteryStatus.over: + case IarLotteryStatus.end: + lotteryAnimationVisible.value = false; + break; + } + } + + /** 处理抽奖动画显示状态改变 */ + function onVisibleChange(visible = false) { + lotteryAnimationVisible.value = visible; + } + + return { + lotterySdk, + lotteryAnimationVisible, + onLotteryStatusChange, + onVisibleChange, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-record-entrance.ts b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-record-entrance.ts new file mode 100644 index 0000000000000000000000000000000000000000..bace70a789d9733fb19bfe81dfaf1ae92b7f312f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-record-entrance.ts @@ -0,0 +1,37 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { + InteractiveEntranceData, + useInteractReceiveStore, +} from '@/store/use-interact-receive-store'; + +/** + * @hook 互动-抽奖-中奖记录入口 + */ +export const useLotteryRecordEntrance = () => { + const interactReceiveStore = useInteractReceiveStore(); + + /** 中奖记录入口数据 */ + const lotteryRecordEntranceData: InteractiveEntranceData = { + type: 'lottery-record', + name: 'interact.lottery.record', + icon: require('./imgs/icon-lottery-record.png'), + icon2: require('./imgs/icon-lottery-record_p.png'), + onClick: () => eventBus.$emit(appEvents.interaction.OpenLotteryRecord, true), + }; + + /** 设置中奖记录二次入口 */ + const setLotteryRecordEntrance = () => { + interactReceiveStore.setInteractiveEntrance(lotteryRecordEntranceData); + }; + + /** 移除中奖记录二次入口 */ + const removeLotteryRecordEntrance = () => { + interactReceiveStore.removeInteractiveEntrance(lotteryRecordEntranceData.type); + }; + + return { + lotteryRecordEntranceData, + setLotteryRecordEntrance, + removeLotteryRecordEntrance, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-record.ts b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-record.ts new file mode 100644 index 0000000000000000000000000000000000000000..9009bf948d660abb854093bc3c5322075ce6ccbe --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-record.ts @@ -0,0 +1,101 @@ +import { computed, onBeforeMount, onBeforeUnmount, ref, watch } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { translate } from '@/assets/lang'; +import { updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; + +import { LotteryRecordData } from '../types/lottery-types'; +import { useLotteryRecordEntrance } from './use-lottery-record-entrance'; + +export const lotteryRecordProps = () => ({ + /** 抽奖记录是否显示,支持 .sync */ + visible: PropUtils.bool.def(false), +}); + +export const lotteryRecordEmits = () => ({ + ...updateModelEmit<'visible', boolean>('visible'), +}); + +/** + * @hook 互动-抽奖-中奖记录 + * */ +export const useLotteryRecord = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + + const interactReceiveStore = useInteractReceiveStore(); + const { setLotteryRecordEntrance, removeLotteryRecordEntrance } = useLotteryRecordEntrance(); + + const watchCore = getWatchCore(); + /** 抽奖 SDK 实例 */ + const lotterySdk = watchCore.interactReceive.getLottery(); + + /** 中奖记录是否显示 */ + const lotteryRecordVisible = ref(false); + + watch( + () => props.visible, + () => { + lotteryRecordVisible.value = props.visible; + }, + ); + + watch( + () => lotteryRecordVisible.value, + () => { + emit('update:visible', lotteryRecordVisible.value); + }, + ); + + /** 设置中奖记录显示状态 */ + function setLotteryRecordVisible(visibleState = true) { + lotteryRecordVisible.value = visibleState; + } + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenLotteryRecord, setLotteryRecordVisible); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenLotteryRecord, setLotteryRecordVisible); + }); + + /** 处理中奖列表改变事件 */ + function onLotteryListChanged(lotteryList: LotteryRecordData[] = []) { + interactReceiveStore.lotteryWinRecords = lotteryList; + + if (lotteryList.length) { + setLotteryRecordEntrance(); + } else { + removeLotteryRecordEntrance(); + } + } + + /** + * 处理显示中奖结果 + * @param record 中奖记录 + */ + function onShowLotteryResult(record: LotteryRecordData) { + eventBus.$emit(appEvents.interaction.OpenLotteryResult, { + visible: true, + record, + }); + setLotteryRecordVisible(false); + } + + const lotteryRecordTitle = computed(() => translate('interact.lottery.record')); + + return { + lotterySdk, + lotteryRecordVisible, + lotteryRecordTitle, + setLotteryRecordVisible, + onLotteryListChanged, + onShowLotteryResult, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-result.ts b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-result.ts new file mode 100644 index 0000000000000000000000000000000000000000..b485e16e5ab38dc3a451744df254dce6c297f714 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/hooks/use-lottery-result.ts @@ -0,0 +1,102 @@ +import { computed, onBeforeMount, onBeforeUnmount, ref, unref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; + +import { + AllLotteryResultStatus, + LotteryRecordData, + LotteryResultInstance, +} from '../types/lottery-types'; + +/** + * @hook 互动-抽奖-抽奖结果 + * */ +export const useLotteryResult = () => { + const watchCore = getWatchCore(); + /** 抽奖 SDK 实例 */ + const lotterySdk = watchCore.interactReceive.getLottery(); + + /** 抽奖结果组件实例 */ + const lotteryResultInstance = ref(); + + /** 抽奖结果是否显示 */ + const lotteryResultVisible = ref(false); + /** 设置抽奖显示状态 */ + function setLotteryResultVisible(visible = true) { + lotteryResultVisible.value = visible; + } + + /** 将结果状态返回到上一级 */ + function backLotteryResult() { + const instance = unref(lotteryResultInstance); + if (instance?.toBack) { + instance.toBack(); + } + } + + /** 当前抽奖结果显示状态 */ + const lotteryResultStatus = ref(); + /** 处理结果显示状态改变 */ + function onStatusChanged(status: AllLotteryResultStatus) { + lotteryResultStatus.value = status; + } + + /** + * 处理打开抽奖记录事件 + * @param record 需要显示的抽奖记录 + */ + function onOpenLotteryResult(params: { visible: boolean; record?: LotteryRecordData }) { + const { visible = true, record } = params; + const instance = unref(lotteryResultInstance); + if (record && instance?.setLottery) { + instance.setLottery({ + ...record, + isWinner: true, + }); + } + + lotteryResultStatus.value = AllLotteryResultStatus.isShowLotteryEnd; + backLotteryResult(); + + setLotteryResultVisible(visible); + } + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenLotteryResult, onOpenLotteryResult); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenLotteryResult, onOpenLotteryResult); + }); + + /** 抽奖标题 */ + const lotteryResultTitle = computed(() => { + const titles: Record = { + isShowLotteryEnd: translate('interact.lottery.result'), + isShowWinnerList: translate('interact.lottery.checkList'), + isShowSubmitInfo: translate('interact.lottery.submitInfo'), + }; + const status = unref(lotteryResultStatus) ?? AllLotteryResultStatus.isShowLotteryEnd; + return titles[status]; + }); + + const showBackable = computed(() => { + return ( + lotteryResultStatus.value && + lotteryResultStatus.value !== AllLotteryResultStatus.isShowLotteryEnd + ); + }); + + return { + lotterySdk, + lotteryResultTitle, + lotteryResultInstance, + lotteryResultVisible, + showBackable, + setLotteryResultVisible, + backLotteryResult, + onStatusChanged, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-animation.vue b/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-animation.vue new file mode 100644 index 0000000000000000000000000000000000000000..374b1ac3847fa7c9dcffb9e5ae3154dbb0589035 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-animation.vue @@ -0,0 +1,77 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-record-popup.vue b/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-record-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..6648952af05e5c81910e44aeec6ae3de9d14bf1a --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-record-popup.vue @@ -0,0 +1,50 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-result-popup.vue b/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-result-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..c60fd6b4fafac31c23422d029d3c0e924ad236f2 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/mobile-lottery-result-popup.vue @@ -0,0 +1,45 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-animation.vue b/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-animation.vue new file mode 100644 index 0000000000000000000000000000000000000000..168fd9409c60736bc1a4ae149b7236d8a32fa108 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-animation.vue @@ -0,0 +1,28 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-record.vue b/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-record.vue new file mode 100644 index 0000000000000000000000000000000000000000..7de7834dd3074e65616b314c9d6e93d4914de6e5 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-record.vue @@ -0,0 +1,71 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-result-dialog.vue b/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-result-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..b4c1f5c748b251021197cd9577144d0234826d87 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/pc-lottery-result-dialog.vue @@ -0,0 +1,35 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/lottery/types/lottery-types.ts b/src/components/page-watch-common/interactive-receive/lottery/types/lottery-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..941f6efcd4bdcbf4fd02682ed8533550210ab529 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/lottery/types/lottery-types.ts @@ -0,0 +1,28 @@ +/** + * 抽奖结果状态 + */ +export enum AllLotteryResultStatus { + /** 显示中奖结果 */ + isShowLotteryEnd = 'isShowLotteryEnd', + /** 显示中奖名单 */ + isShowWinnerList = 'isShowWinnerList', + /** 填写中奖信息 */ + isShowSubmitInfo = 'isShowSubmitInfo', +} + +/** + * 中奖记录数据类型 + */ +export interface LotteryRecordData { + /** 是否已提交 */ + received: boolean; + /** 是否中奖 */ + isWinner: boolean; +} + +export interface LotteryResultInstance { + /** 返回到结果 */ + toBack?: () => void; + /** 设置中奖记录 */ + setLottery?: (record: LotteryRecordData) => void; +} diff --git a/src/components/page-watch-common/interactive-receive/mobile-iar-render.vue b/src/components/page-watch-common/interactive-receive/mobile-iar-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..ff96fc7fa372f0fcfd351758ae867e95987ec0cf --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/mobile-iar-render.vue @@ -0,0 +1,81 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/pc-iar-render.vue b/src/components/page-watch-common/interactive-receive/pc-iar-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..18cb8a07bdfe95053ab5a1d340ec30c6a388b1b1 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/pc-iar-render.vue @@ -0,0 +1,53 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/portrait-iar-render.vue b/src/components/page-watch-common/interactive-receive/portrait-iar-render.vue new file mode 100644 index 0000000000000000000000000000000000000000..12232097d357ef388334dfa60cfa0137b887f8db --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/portrait-iar-render.vue @@ -0,0 +1,87 @@ + + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/hooks/use-product-detail.ts b/src/components/page-watch-common/interactive-receive/product/hooks/use-product-detail.ts new file mode 100644 index 0000000000000000000000000000000000000000..5ed4f72636d0e5bcee963f2975d450f948dcac1b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/hooks/use-product-detail.ts @@ -0,0 +1,30 @@ +import { ref } from 'vue'; +import { ProductGoodData } from '../types/product-types'; + +/** + * @hook 商品详情 + * */ +export const useProductDetail = () => { + /** 正在显示的商品详情 */ + const goodDetail = ref(); + /** 商品详情弹层是否可见 */ + const goodDetailVisible = ref(false); + + /** 打开商品详情 */ + const openGoodDetail = (good: ProductGoodData) => { + goodDetail.value = good; + goodDetailVisible.value = true; + }; + + /** 关闭商品详情 */ + const closeGoodDetail = () => { + goodDetailVisible.value = false; + }; + + return { + goodDetail, + goodDetailVisible, + openGoodDetail, + closeGoodDetail, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/product/hooks/use-product-job-detail.ts b/src/components/page-watch-common/interactive-receive/product/hooks/use-product-job-detail.ts new file mode 100644 index 0000000000000000000000000000000000000000..ac932b9aa05279e8171ddca2088977f5b6ce0849 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/hooks/use-product-job-detail.ts @@ -0,0 +1,31 @@ +import { appEvents, eventBus, useEventBusListener } from '@/app/app-events'; +import { ref } from 'vue'; +import { ProductGoodData } from '../types/product-types'; + +/** 处理显示职位详情事件 */ +export function onJobDetailEvent(jobDetail: ProductGoodData) { + eventBus.$emit(appEvents.interaction.OpenProductJobDetail, jobDetail); +} + +/** + * @hook 商品库职位详情 + */ +export const useProductJobDetail = () => { + /** 弹层显示状态 */ + const visible = ref(false); + + /** 职位数据 */ + const jobDetail = ref(); + + function onOpenJobDetail(detail: ProductGoodData) { + jobDetail.value = detail; + visible.value = true; + } + + useEventBusListener(appEvents.interaction.OpenProductJobDetail, onOpenJobDetail); + + return { + visible, + jobDetail, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/product/hooks/use-product-list.ts b/src/components/page-watch-common/interactive-receive/product/hooks/use-product-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..1eb34adee1fabc2f90d6d263bb266d124bb299e7 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/hooks/use-product-list.ts @@ -0,0 +1,57 @@ +import { nextTick, onMounted, ref, unref } from 'vue'; +import { IarProductListInstance } from '../types/product-types'; +import { useProductTrack } from './use-product'; + +/** + * @hook 商品列表 + * */ +export const useProductList = () => { + const { trackOpenProductList } = useProductTrack(); + + /** 互动功能商品列表组件实例 */ + const iarProductListRef = ref(); + /** 是否渲染商品列表,仅在打开 tab 或弹层时请求数据 */ + const renderProductList = ref(false); + /** 商品总数 */ + const goodTotal = ref(0); + + /** 初始化商品列表,并上报打开数据 */ + const initProductList = async () => { + trackOpenProductList(); + + if (!renderProductList.value) { + renderProductList.value = true; + return; + } + + await nextTick(); + + const listInstance = unref(iarProductListRef); + if (!listInstance) { + console.warn('initProductList error, iarProductListRef is undefined'); + return; + } + + // 调用组件的初始化方法,重置到首页 + if (listInstance.init) { + listInstance.init(); + } + }; + + /** 处理商品总数更改事件 */ + const onGoodTotalChange = (total = 0) => { + goodTotal.value = total; + }; + + onMounted(() => { + initProductList(); + }); + + return { + iarProductListRef, + renderProductList, + goodTotal, + onGoodTotalChange, + initProductList, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/product/hooks/use-product.ts b/src/components/page-watch-common/interactive-receive/product/hooks/use-product.ts new file mode 100644 index 0000000000000000000000000000000000000000..c9b44e295990aa07c95428bb20569a4c4deb9243 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/hooks/use-product.ts @@ -0,0 +1,175 @@ +import { computed, unref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useChannelStore } from '@/store/use-channel-store'; +import { useViewerStore } from '@/store/use-viewer-store'; + +import { translate } from '@/assets/lang'; +import { isMobile } from '@/assets/utils/browser'; +import { ynToBool } from '@utils-ts/boolean'; + +import { YN, LiveStatus, RtasTrackEventId, RtasTrackEventType } from '@polyv/live-watch-sdk'; +import { ProductGoodData } from '../types/product-types'; +import { useInviteStore } from '@/store/use-invite-store'; + +/** + * @hook 互动-商品库 + * */ +export const useProduct = () => { + const channelStore = useChannelStore(); + + const watchCore = getWatchCore(); + /** 商品库 SDK 实例 */ + const productSdk = watchCore.interactReceive.getProduct(); + + /** 招聘岗位开关 */ + const recruitmentEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig.recruitmentEnabled, YN.N), + ); + + /** 招聘标签文本 */ + const jobDescLabel = computed(() => { + if (!unref(recruitmentEnabled)) { + return ''; + } + return translate('interact.product.jobDetail'); + }); + + return { + productSdk, + recruitmentEnabled, + jobDescLabel, + }; +}; + +/** 商品库跳转参数 hook */ +export const useProductLinkParams = () => { + const channelStore = useChannelStore(); + const viewerStore = useViewerStore(); + const inviteStore = useInviteStore(); + + /** 获取链接参数 */ + const getLinkParams = () => { + return { + openId: viewerStore.openId, + unionId: viewerStore.unionId, + userId: viewerStore.viewerId, + // 邀请人 openId + inviteOpenId: inviteStore.inviteUserOpenId, + // 邀请人 unionId + inviteUnionId: inviteStore.inviteUserUnionId, + // 渠道 id + promoteId: channelStore.promoteId, + // 频道号 + channelId: channelStore.channelId, + // 当前场次号 + sessionId: channelStore.currentSessionId, + }; + }; + + return { + getLinkParams, + }; +}; + +/** 商品库数据上报 hook */ +export const useProductTrack = () => { + const channelStore = useChannelStore(); + + /** 商品库数据上报超管开关 */ + const productTrackEnabled = ynToBool( + channelStore.channelDetail?.channelConfig?.productTrackEnabled, + YN.N, + ); + + /** + * 上报商品信息 + * @param good 商品信息 + * @param eventType + * @param eventId + */ + const trackProductItemData = ( + good: ProductGoodData, + eventType: RtasTrackEventType, + eventId: RtasTrackEventId, + ) => { + if (channelStore.liveStatus !== LiveStatus.Live || !productTrackEnabled) { + return; + } + const watchCore = getWatchCore(); + + const { name, productId, realPrice, price, productType } = good; + const data = { + name, // 商品名字 + productId, // 商品Id + price: `${price ?? 0}`, // 价格 + realPrice: `${realPrice}`, // 原价 + productType, // 商品分类 + linkType: isMobile ? 'mobile' : 'pc', + }; + + watchCore.rtas.trackBehaviorEvent({ + eventType, + eventId, + data, + }); + }; + + /** + * 上报商品列表中浏览商品数据 + * @param good 商品信息 + */ + const trackProductListItemView = (good: ProductGoodData) => { + trackProductItemData(good, 'show', 'product_list_item_view'); + }; + + /** + * 上报商品列表中点击商品购买数据 + * @param good 商品信息 + */ + const trackProductListItemClick = (good: ProductGoodData) => { + trackProductItemData(good, 'click', 'product_push_item_click'); + }; + + /** + * 上报商品推送中浏览商品数据 + * @param good 商品信息 + */ + const trackProductPushItemView = (good: ProductGoodData) => { + trackProductItemData(good, 'show', 'product_push_item_view'); + }; + + /** + * 上报商品推送中点击商品购买数据 + * @param good 商品信息 + */ + const trackProductPushItemClick = (good: ProductGoodData) => { + trackProductItemData(good, 'click', 'product_push_item_click'); + }; + + /** 上报打开商品列表数据 */ + const trackOpenProductList = () => { + if (channelStore.liveStatus !== LiveStatus.Live || !productTrackEnabled) { + return; + } + + const watchCore = getWatchCore(); + watchCore.rtas.trackBehaviorEvent({ + eventType: 'click', + eventId: 'product_cart_click', + data: { + shoppingBagId: 'shopping_bag_universal_id', + linkType: isMobile ? 'mobile' : 'pc', + }, + }); + }; + + return { + trackProductItemData, + trackProductListItemView, + trackProductListItemClick, + trackProductPushItemView, + trackProductPushItemClick, + trackOpenProductList, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/product/imgs/icon-job.png b/src/components/page-watch-common/interactive-receive/product/imgs/icon-job.png new file mode 100644 index 0000000000000000000000000000000000000000..8dbdfb91ca30a17e66b69ceeba827654d97fca42 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/product/imgs/icon-job.png differ diff --git a/src/components/page-watch-common/interactive-receive/product/imgs/icon-shop-car.svga b/src/components/page-watch-common/interactive-receive/product/imgs/icon-shop-car.svga new file mode 100644 index 0000000000000000000000000000000000000000..3391672384f2c73d5ab295c252280c01249d4d08 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/product/imgs/icon-shop-car.svga differ diff --git a/src/components/page-watch-common/interactive-receive/product/mobile-product-bubble.vue b/src/components/page-watch-common/interactive-receive/product/mobile-product-bubble.vue new file mode 100644 index 0000000000000000000000000000000000000000..ec5a435d2f29de6e924d3868feaf665887ff5cd6 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/mobile-product-bubble.vue @@ -0,0 +1,40 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/mobile-product-job-detail-popup.vue b/src/components/page-watch-common/interactive-receive/product/mobile-product-job-detail-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..d2f6081ff76cce41ca36b019dd8b6c5a1b35c911 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/mobile-product-job-detail-popup.vue @@ -0,0 +1,31 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/mobile-product-list.vue b/src/components/page-watch-common/interactive-receive/product/mobile-product-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..d68951892b8db0ff43282de313cdc92c8e72aa40 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/mobile-product-list.vue @@ -0,0 +1,114 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/pc-product-bubble.vue b/src/components/page-watch-common/interactive-receive/product/pc-product-bubble.vue new file mode 100644 index 0000000000000000000000000000000000000000..36ff4c0d481df5a88660ead70a29a4f3b18b54bd --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/pc-product-bubble.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/pc-product-job-detail-dialog.vue b/src/components/page-watch-common/interactive-receive/product/pc-product-job-detail-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..65894f72e9c748adc230a0039ed204a6b241421b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/pc-product-job-detail-dialog.vue @@ -0,0 +1,30 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/pc-product-list.vue b/src/components/page-watch-common/interactive-receive/product/pc-product-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..69918ed07b3592f24b1fdfdbca7597120582ddfb --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/pc-product-list.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/portrait-product-bubble.vue b/src/components/page-watch-common/interactive-receive/product/portrait-product-bubble.vue new file mode 100644 index 0000000000000000000000000000000000000000..f634d9b98d80b8f264236cfa58563290b6330b0b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/portrait-product-bubble.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/portrait-product-job-detail-popup.vue b/src/components/page-watch-common/interactive-receive/product/portrait-product-job-detail-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..02b09b75ed8987c647f6b3594a596cd8613bcf3e --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/portrait-product-job-detail-popup.vue @@ -0,0 +1,32 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/portrait-product-list-entrance.vue b/src/components/page-watch-common/interactive-receive/product/portrait-product-list-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..b09f8a6975973057708e877839e45fc6d5935974 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/portrait-product-list-entrance.vue @@ -0,0 +1,66 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/portrait-product-list-popup.vue b/src/components/page-watch-common/interactive-receive/product/portrait-product-list-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..b35d8d3cd220e37a6cfba766103a2edae46a2728 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/portrait-product-list-popup.vue @@ -0,0 +1,191 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/product/types/product-types.ts b/src/components/page-watch-common/interactive-receive/product/types/product-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e36c89cce380eafe3185dff60db5174c3d6dc4f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/product/types/product-types.ts @@ -0,0 +1,25 @@ +/** + * 商品数据 + */ +export interface ProductGoodData { + /** 商品名称 */ + name: string; + /** 商品 id */ + productId: string; + /** 商品原价 */ + realPrice: number; + /** 商品价格 */ + price: number; + /** 商品类型 */ + productType: string; + /** 商品提示 */ + productDesc: string; +} + +/** + * 互动接收端商品列表实例类型 + */ +export interface IarProductListInstance { + /** 重置列表 */ + init?: () => unknown; +} diff --git a/src/components/page-watch-common/interactive-receive/push-card/hooks/use-push-card.ts b/src/components/page-watch-common/interactive-receive/push-card/hooks/use-push-card.ts new file mode 100644 index 0000000000000000000000000000000000000000..b97b53080e386dcc51f27cbbd0392a34346c97cb --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/push-card/hooks/use-push-card.ts @@ -0,0 +1,21 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; + +/** 卡片推送 hook */ +export const usePushCard = () => { + const interactReceiveStore = useInteractReceiveStore(); + + const watchCore = getWatchCore(); + /** 卡片推送 SDK 实例 */ + const pushCardSdk = watchCore.interactReceive.getPushCard(); + + /** 处理卡片推送入口显示状态改变 */ + const onEntryVisibleChanged = (visible = true) => { + interactReceiveStore.pushCardPendantVisible = visible; + }; + + return { + pushCardSdk, + onEntryVisibleChanged, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/push-card/mobile-push-card.vue b/src/components/page-watch-common/interactive-receive/push-card/mobile-push-card.vue new file mode 100644 index 0000000000000000000000000000000000000000..ea5c6d1437713739c6380bf3cd27cbeed12e709f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/push-card/mobile-push-card.vue @@ -0,0 +1,23 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/push-card/pc-push-card.vue b/src/components/page-watch-common/interactive-receive/push-card/pc-push-card.vue new file mode 100644 index 0000000000000000000000000000000000000000..f35c03290df3703bdf8e9a81fb634f8267ff209a --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/push-card/pc-push-card.vue @@ -0,0 +1,23 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/question-answer/hooks/use-question-answer.ts b/src/components/page-watch-common/interactive-receive/question-answer/hooks/use-question-answer.ts new file mode 100644 index 0000000000000000000000000000000000000000..678b05227e1b969caed135b13aa0d92bbc554eb3 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/question-answer/hooks/use-question-answer.ts @@ -0,0 +1,55 @@ +import { onBeforeUnmount, onMounted } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { TAB_NAME_QA } from '@/assets/constants/tab-name'; +import { previewImage } from '@/hooks/components/use-image-preview'; + +/** + * @hook 互动-问答 + */ +export const useQuestionAnswer = () => { + const interactReceiveStore = useInteractReceiveStore(); + + const watchCore = getWatchCore(); + const questionAnswerSdk = watchCore.interactReceive.getQuestionAnswer(); + + /** + * 处理设置昵称 + */ + function onSetNick() { + eventBus.$emit(appEvents.chat.OpenSetNick, true); + } + + /** + * 处理图片浏览 + * @param data + */ + function onPreviewImage(data: { url: string }) { + previewImage([data.url]); + } + + function onTabSwitch(name: string) { + // 切换到问答 tab,取消红点 + if (name === TAB_NAME_QA) { + interactReceiveStore.qaReminderVisible = false; + } + } + + onMounted(() => { + eventBus.$on(appEvents.global.PcAsideMenuSwitch, onTabSwitch); + eventBus.$on(appEvents.global.MobileBottomMenuSwitch, onTabSwitch); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.global.PcAsideMenuSwitch, onTabSwitch); + eventBus.$off(appEvents.global.MobileBottomMenuSwitch, onTabSwitch); + }); + + return { + questionAnswerSdk, + onSetNick, + onPreviewImage, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-btn-edit.png b/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-btn-edit.png new file mode 100644 index 0000000000000000000000000000000000000000..d9fbccc5ce7e9b7dbcae7a77db0757e394c06a7c Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-btn-edit.png differ diff --git a/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-close-black.png b/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-close-black.png new file mode 100644 index 0000000000000000000000000000000000000000..f0d1997401d5f25933de2798bb23a3dc0e6ee2da Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-close-black.png differ diff --git a/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-emotion-black.png b/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-emotion-black.png new file mode 100644 index 0000000000000000000000000000000000000000..29c7e082e7f2857a276ab566a638137730e19ac8 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/question-answer/imgs/icon-emotion-black.png differ diff --git a/src/components/page-watch-common/interactive-receive/question-answer/mobile-question-answer.vue b/src/components/page-watch-common/interactive-receive/question-answer/mobile-question-answer.vue new file mode 100644 index 0000000000000000000000000000000000000000..0691952b119087bfd40863032a39c45e3ed8d23d --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/question-answer/mobile-question-answer.vue @@ -0,0 +1,57 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/question-answer/pc-question-answer.vue b/src/components/page-watch-common/interactive-receive/question-answer/pc-question-answer.vue new file mode 100644 index 0000000000000000000000000000000000000000..53a3c54c59031f59499f5b43f33c27d4261e09f8 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/question-answer/pc-question-answer.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/question-answer/portrait-question-answer.vue b/src/components/page-watch-common/interactive-receive/question-answer/portrait-question-answer.vue new file mode 100644 index 0000000000000000000000000000000000000000..b06f2d243c8150cdc6ea01b5825245ce6d692d09 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/question-answer/portrait-question-answer.vue @@ -0,0 +1,108 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/hooks/imgs/icon-questionnaire.png b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/imgs/icon-questionnaire.png new file mode 100644 index 0000000000000000000000000000000000000000..816d3255fb42780b38f03a92a89f788e498f3308 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/imgs/icon-questionnaire.png differ diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/hooks/imgs/icon-questionnaire_p.png b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/imgs/icon-questionnaire_p.png new file mode 100644 index 0000000000000000000000000000000000000000..3b047691b82d1ebce5b7dd24fdb5d5abfd32c2e0 Binary files /dev/null and b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/imgs/icon-questionnaire_p.png differ diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/hooks/use-questionnaire-entrance.ts b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/use-questionnaire-entrance.ts new file mode 100644 index 0000000000000000000000000000000000000000..e53292cc264674e177562d039aa44ba2a029a9c1 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/use-questionnaire-entrance.ts @@ -0,0 +1,37 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { + InteractiveEntranceData, + useInteractReceiveStore, +} from '@/store/use-interact-receive-store'; + +/** + * @hook 问卷入口 + */ +export const useQuestionnaireEntrance = () => { + const interactReceiveStore = useInteractReceiveStore(); + + /** 问卷入口数据 */ + const questionnaireEntranceData: InteractiveEntranceData = { + type: 'questionnaire', + name: 'interact.questionnaire.title', + icon: require('./imgs/icon-questionnaire.png'), + icon2: require('./imgs/icon-questionnaire_p.png'), + onClick: () => eventBus.$emit(appEvents.interaction.OpenQuestionnaire, true), + }; + + /** 设置问卷二次入口 */ + const setQuestionnaireEntrance = () => { + interactReceiveStore.setInteractiveEntrance(questionnaireEntranceData); + }; + + /** 移除问卷二次入口 */ + const removeQuestionnaireEntrance = () => { + interactReceiveStore.removeInteractiveEntrance(questionnaireEntranceData.type); + }; + + return { + questionnaireEntranceData, + setQuestionnaireEntrance, + removeQuestionnaireEntrance, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/hooks/use-questionnaire.ts b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/use-questionnaire.ts new file mode 100644 index 0000000000000000000000000000000000000000..75c7378168f6b14213426fc28dac968f74ba67bb --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/questionnaire/hooks/use-questionnaire.ts @@ -0,0 +1,109 @@ +import { computed, ref } from 'vue'; +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; +import { useQuestionnaireEntrance } from './use-questionnaire-entrance'; +import { AllQuestionnaireStatus, IarQuestionnaireInstance } from '../types/questionnaire-type'; + +/** + * @hook 互动-问卷 + */ +export const useQuestionnaire = () => { + const { setQuestionnaireEntrance, removeQuestionnaireEntrance } = useQuestionnaireEntrance(); + const watchCore = getWatchCore(); + + /** 问卷组件节点 */ + const questionnaireRef = ref(); + /** 问卷 SDK 实例 */ + const questionnaireSdk = watchCore.interactReceive.getQuestionnaire(); + /** 问卷是否显示 */ + const questionnaireVisible = ref(false); + /** 问卷状态 */ + const questionnaireStatus = ref(AllQuestionnaireStatus.isShowQuestion); + + /** 问卷标题 */ + const questionnaireTitle = computed(() => { + let title = ''; + switch (questionnaireStatus.value) { + case AllQuestionnaireStatus.isShowQuestion: + title = translate('interact.questionnaire.title'); + break; + case AllQuestionnaireStatus.isShowResult: + case AllQuestionnaireStatus.isShowAnswer: + title = translate('interact.questionnaire.result'); + break; + case AllQuestionnaireStatus.isShowResultRank: + title = ' '; + break; + case AllQuestionnaireStatus.isShowQuestionnaireList: + title = translate('interact.questionnaire.result.list'); + } + return title; + }); + + /** + * 处理问卷状态修改 + * */ + function onStatusChanged(status: AllQuestionnaireStatus) { + questionnaireStatus.value = status; + } + + /** 问卷状态副作用处理 */ + function questionnaireStatusEffectHandler() { + if (!questionnaireRef.value) { + console.warn('questionnaireRef value is undefined'); + return; + } + + // 需要直接获取组件内部的问卷状态 + const status = questionnaireRef.value.status; + + if (status === null) { + // 空状态下需要手动展示问卷列表 + questionnaireRef.value.showList(); + return; + } + + if ( + status === AllQuestionnaireStatus.isShowQuestion || + status === AllQuestionnaireStatus.isShowQuestionnaireList + ) { + // 设置互动二次入口 + setQuestionnaireEntrance(); + } + } + + /** 设置问卷弹窗显示状态 */ + function setQuestionnaireVisible(visible = true) { + if (visible) { + questionnaireStatusEffectHandler(); + } + + questionnaireVisible.value = visible; + } + + /** + * 处理问卷列表中有无"问卷"的情况 + */ + function onHasQuestionnaire(hasQuestionnaire: boolean) { + if (hasQuestionnaire) { + setQuestionnaireEntrance(); + } else { + removeQuestionnaireEntrance(); + } + } + + useEventBusListener(appEvents.interaction.OpenQuestionnaire, setQuestionnaireVisible); + + return { + questionnaireRef, + questionnaireSdk, + questionnaireVisible, + questionnaireTitle, + questionnaireStatus, + setQuestionnaireVisible, + onStatusChanged, + onHasQuestionnaire, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/mobile-questionnaire-popup.vue b/src/components/page-watch-common/interactive-receive/questionnaire/mobile-questionnaire-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..b7abc31a9276fb3b806f7f044c9b7acecd88748f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/questionnaire/mobile-questionnaire-popup.vue @@ -0,0 +1,58 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/pc-questionnaire-dialog.vue b/src/components/page-watch-common/interactive-receive/questionnaire/pc-questionnaire-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..bad8a583154bf6b4de285b5651995f7514a6c464 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/questionnaire/pc-questionnaire-dialog.vue @@ -0,0 +1,38 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/questionnaire/types/questionnaire-type.ts b/src/components/page-watch-common/interactive-receive/questionnaire/types/questionnaire-type.ts new file mode 100644 index 0000000000000000000000000000000000000000..3a5dd3872d763d982058e9877b90b65ba5b1ccf4 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/questionnaire/types/questionnaire-type.ts @@ -0,0 +1,23 @@ +/** 所有问卷状态 */ +export enum AllQuestionnaireStatus { + /** 正在显示答题中的问卷 */ + isShowQuestion = 'isShowQuestion', + /** 正在显示问卷结果 */ + isShowResult = 'isShowResult', + /** 正在显示问卷答案 */ + isShowAnswer = 'isShowAnswer', + /** 正在显示问卷排行榜 */ + isShowResultRank = 'isShowResultRank', + /** 正在展示问卷列表 */ + isShowQuestionnaireList = 'isShowQuestionnaireList', +} + +/** + * 互动接收端-问卷 vue 实例类型 + */ +export interface IarQuestionnaireInstance { + /** 组件内的问卷状态 */ + status: AllQuestionnaireStatus | null; + /** 显示问卷列表 */ + showList: () => void; +} diff --git a/src/components/page-watch-common/interactive-receive/redpack-rain/hooks/use-redpack-rain.ts b/src/components/page-watch-common/interactive-receive/redpack-rain/hooks/use-redpack-rain.ts new file mode 100644 index 0000000000000000000000000000000000000000..9aff1f78394f6df3f927751b85885716106232e1 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack-rain/hooks/use-redpack-rain.ts @@ -0,0 +1,92 @@ +import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useChannelStore } from '@/store/use-channel-store'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; + +import { useRedpackStatus } from '../../redpack/hooks/use-redpack'; +import { RedpackStatusData } from '../types/redpack-rain-types'; + +/** + * @hook 互动-红包雨 + * */ +export const useRedpackRain = () => { + const interactReceiveStore = useInteractReceiveStore(); + const channelStore = useChannelStore(); + + const watchCore = getWatchCore(); + /** 红包雨 SDK 实例 */ + const redpackRainSdk = watchCore.interactReceive.getRedpackRain(); + + /** 自定义红包雨皮肤 */ + const customImgConfig = computed(() => { + const redpackRainSkin = channelStore.channelDetail?.interactionSetting.redpackRainSkin; + return { + rainThemeBottomBgImg: redpackRainSkin?.backgroundImage, + rainThemeBollImgArray: redpackRainSkin?.images, + }; + }); + + /** 红包雨组件实例 */ + const redpackRainRef = ref(); + + /** + * 打开红包雨 + * @param data 红包雨数据 + * @param source 打开来源,默认:msg + */ + const openRedpackRain = (data: UniversalParams, source = 'msg') => { + if (!redpackRainRef.value) { + console.warn('openRedpackRain fail, redpackRainRef is undefined'); + return; + } + + redpackRainRef.value.showRedpackRain({ + ...data, + source, + }); + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenRedpackRain, openRedpackRain); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenRedpackRain, openRedpackRain); + }); + + const { setRedpackStatus } = useRedpackStatus(); + + /** 处理红包雨状态改变 */ + const onStateChange = (data: RedpackStatusData) => { + const { redpackId, state } = data; + setRedpackStatus(redpackId, state); + }; + + /** 处理红包雨显示状态改变 */ + const onVisibleChange = (visible = false) => { + interactReceiveStore.isGoOnRedpackRain = !!visible; + }; + + /** 处理点击提现按钮 */ + const onClickWithdraw = () => { + eventBus.$emit(appEvents.withdraw.OpenWithdrawApply, true); + }; + + /** 处理点击积分记录 */ + const onClickScoreRecord = () => { + eventBus.$emit(appEvents.interaction.OpenScoreRecord, true); + }; + + return { + redpackRainSdk, + customImgConfig, + redpackRainRef, + openRedpackRain, + onStateChange, + onVisibleChange, + onClickWithdraw, + onClickScoreRecord, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/redpack-rain/mobile-redpack-rain.vue b/src/components/page-watch-common/interactive-receive/redpack-rain/mobile-redpack-rain.vue new file mode 100644 index 0000000000000000000000000000000000000000..e2092a3f7c1a6256df3d67023bbe4b03b65aa674 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack-rain/mobile-redpack-rain.vue @@ -0,0 +1,46 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/redpack-rain/pc-redpack-rain.vue b/src/components/page-watch-common/interactive-receive/redpack-rain/pc-redpack-rain.vue new file mode 100644 index 0000000000000000000000000000000000000000..09be3dd47b52a613ed0c720e6bdda2fb80f1bf5b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack-rain/pc-redpack-rain.vue @@ -0,0 +1,23 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/redpack-rain/types/redpack-rain-types.ts b/src/components/page-watch-common/interactive-receive/redpack-rain/types/redpack-rain-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..a293f0ce7080b1a0d315cb86b7f1bea466a3a782 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack-rain/types/redpack-rain-types.ts @@ -0,0 +1,4 @@ +export interface RedpackStatusData { + redpackId: string; + state: string; +} diff --git a/src/components/page-watch-common/interactive-receive/redpack/hooks/use-redpack-pendant.ts b/src/components/page-watch-common/interactive-receive/redpack/hooks/use-redpack-pendant.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd522e33a3929fd1ad61b1fe492a59f6ed0da384 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack/hooks/use-redpack-pendant.ts @@ -0,0 +1,36 @@ +import { computed } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useChannelStore } from '@/store/use-channel-store'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; + +import { ynToBool } from '@utils-ts/boolean'; +import { YN } from '@polyv/live-watch-sdk'; + +/** + * @hook 互动-红包挂件 + * */ +export const useRedpackPendant = () => { + const channelStore = useChannelStore(); + const interactReceiveStore = useInteractReceiveStore(); + + const watchCore = getWatchCore(); + /** 红包 SDK 实例 */ + const redpackSdk = watchCore.interactReceive.getRedpack(); + + /** 自动请求详情 */ + const autoFetchDetail = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.userRedpackEnabled, YN.N); + }); + + /** 处理红包挂件入口显示状态改变 */ + function onEntryVisibleChanged(visible = true) { + interactReceiveStore.redpackPendantVisible = visible; + } + + return { + redpackSdk, + autoFetchDetail, + onEntryVisibleChanged, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/redpack/hooks/use-redpack.ts b/src/components/page-watch-common/interactive-receive/redpack/hooks/use-redpack.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2dc872f2c13249aae451b0cb6dc677c26f1ecf4 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack/hooks/use-redpack.ts @@ -0,0 +1,114 @@ +/** + * @file 红包相关 hook + */ +import { computed, onBeforeMount, onBeforeUnmount, ref } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useChannelStore } from '@/store/use-channel-store'; + +/** + * @hook 互动-红包状态 + * */ +export const useRedpackStatus = () => { + /** 设置某个红包的状态 */ + const setRedpackStatus = (redpackId: string, status: string) => { + eventBus.$emit(appEvents.interaction.SetRedpackStatus, { + redpackId, + status, + }); + }; + + return { + setRedpackStatus, + }; +}; + +/** + * @hook 互动-红包 + */ +export const useRedpack = () => { + const channelStore = useChannelStore(); + + const watchCore = getWatchCore(); + /** 红包 SDK 实例 */ + const redpackSdk = watchCore.interactReceive.getRedpack(); + + /** 自定义红包皮肤 */ + const customImgConfig = computed(() => { + return { + redpackCoverImg: channelStore.channelDetail?.interactionSetting.redpackSkin?.redpackImage, + }; + }); + + /** 当前显示的红包数据 */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const redpackData = ref>(); + + /** 打开某个红包 */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function openRedpack(data: UniversalParams) { + if (data.type.includes('rain')) { + // 类型是红包雨,则发送事件打开红包雨 + eventBus.$emit(appEvents.interaction.OpenRedpackRain, { + redCacheId: data.redCacheId, + redpackId: data.redpackId, + avatar: data.user.pic, + nickname: data.user.nick, + greeting: data.content, + type: data.type, + }); + } else { + redpackData.value = data; + } + } + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenRedpack, openRedpack); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenRedpack, openRedpack); + }); + + const { setRedpackStatus } = useRedpackStatus(); + /** 处理红包状态改变 */ + function onStatusChange(redpackId: string, status: string) { + setRedpackStatus(redpackId, status); + } + + /** 处理关闭红包 */ + function onCloseRedpack() { + redpackData.value = undefined; + } + + /** 处理点击提现按钮 */ + function onClickWithdraw() { + redpackData.value = undefined; + eventBus.$emit(appEvents.withdraw.OpenWithdrawApply, true); + } + + /** 处理点击积分记录 */ + function onClickScoreRecord() { + redpackData.value = undefined; + eventBus.$emit(appEvents.interaction.OpenScoreRecord, true); + } + + /** 处理口令红包领取 */ + function onPasswordReceived(pwd: string) { + // 发送口令到聊天室 + pwd && eventBus.$emit(appEvents.chat.SendMessageToChat, pwd); + } + + return { + redpackSdk, + redpackData, + customImgConfig, + openRedpack, + onStatusChange, + onCloseRedpack, + onClickWithdraw, + onClickScoreRecord, + onPasswordReceived, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/redpack/mobile-redpack-popup.vue b/src/components/page-watch-common/interactive-receive/redpack/mobile-redpack-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..97e5a782925667de83e33bdfca2647e72a090487 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack/mobile-redpack-popup.vue @@ -0,0 +1,56 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/redpack/pc-redpack-dialog.vue b/src/components/page-watch-common/interactive-receive/redpack/pc-redpack-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..8bd5a3c85f4b30867eaa310f6f6a92fd74e32018 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack/pc-redpack-dialog.vue @@ -0,0 +1,24 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/redpack/redpack-pendant.vue b/src/components/page-watch-common/interactive-receive/redpack/redpack-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..17ec98d4d980da563b187b70b2abc10c2d83e93d --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/redpack/redpack-pendant.vue @@ -0,0 +1,19 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/_hooks/use-risk-evaluation-main.ts b/src/components/page-watch-common/interactive-receive/risk-evaluation/_hooks/use-risk-evaluation-main.ts new file mode 100644 index 0000000000000000000000000000000000000000..00a040f80c10d20523f9cd1d0eb3738bcc802972 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/_hooks/use-risk-evaluation-main.ts @@ -0,0 +1,152 @@ +import { ref, computed, watchEffect } from 'vue'; +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { useFinanceStore } from '@/store/use-finance-store'; +import { useAuthStore } from '@/store/use-auth-store'; +import { useEnrollStore } from '@/store/use-enroll-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; + +import { + QuestionnaireEvaluationResult, + RiskEvaluationMainDialogOpenMode, +} from '../types/risk-evaluation-types'; + +/** 风险测评-副作用处理 */ +const useRiskEvaluationMainEffect = ({ + mainDialogShowEventHandler, +}: { + mainDialogShowEventHandler: () => void; +}) => { + const authStore = useAuthStore(); + const enrollStore = useEnrollStore(); + const financeStore = useFinanceStore(); + + // 通过副作用函数来监听主弹窗展示 + watchEffect(() => { + // 需要授权通过获取用户信息后才能展示弹窗 + if (!authStore.isAuthorized || enrollStore.needEnrollByEnter) return; + + if (financeStore.shouldShowSplashByRiskEvaluation) { + mainDialogShowEventHandler(); + } + }); +}; + +/** + * @hook 风险测评-主要弹窗 + */ +export const useRiskEvaluationMainHook = () => { + const watchCore = getWatchCore(); + const watchAppStore = useWatchAppStore(); + + const financeStore = useFinanceStore(); + const { riskEvaluation } = storeDefinitionToRefs(useFinanceStore); + + /** 问卷 SDK 实例 */ + const questionnaireSdk = watchCore.interactReceive.getQuestionnaire(); + + /** 风险测评-问卷结果 */ + const riskEvaluationResult = ref(null); + + /** 控制弹窗显示 */ + const dialogVisible = ref(false); + + /** 是否手动触发 */ + const isManualTrigger = ref(false); + + /** 是否强制开始 */ + const isForceStart = ref(false); + + /** + * 弹窗关闭按钮可见性 + * @desc 仅在“自愿填写”,“测评完成且符合等级要求”才能展示 + * */ + const dialogCloseable = computed(() => { + const isPass = watchCore.financeRiskEvaluation.judgeIsPassRiskEvaluation(riskEvaluation.value); + return isManualTrigger.value || isPass; + }); + + /** 打开弹窗钩子 */ + function handleOpenDialog({ openMode }: { openMode?: RiskEvaluationMainDialogOpenMode }) { + isManualTrigger.value = openMode === RiskEvaluationMainDialogOpenMode.ManualOpen; + isForceStart.value = openMode === RiskEvaluationMainDialogOpenMode.TimeoutAutoOpen; + dialogVisible.value = true; + } + + /** 关闭弹窗钩子 */ + function handleCloseDialog() { + dialogVisible.value = false; + isManualTrigger.value = false; + isForceStart.value = false; + } + + /** 监听问卷结果回调 */ + function onQuestionnaireEvaluationResult(result: QuestionnaireEvaluationResult) { + riskEvaluationResult.value = result; + } + + /** 监听问卷关闭回调 */ + function onQuestionnaireHide() { + if (!riskEvaluationResult.value) { + console.warn('无问卷评测结果'); + return; + } + + const { passEvaluation, score } = riskEvaluationResult.value; + const isPass = watchCore.financeRiskEvaluation.judgeIsPassRiskEvaluation({ + ...riskEvaluation.value, + passEvaluation, + score, + }); + if (!isPass && !dialogCloseable.value) return; + + handleCloseDialog(); + + const isPreLimitType = watchCore.financeRiskEvaluation.judgeIsPreLimitType( + riskEvaluation.value, + ); + if (isPreLimitType) { + watchAppStore.resetUpWatchCore(); + } else { + financeStore.syncRiskEvaluation({ + passEvaluation, + score, + }); + } + } + + /** + * 主要弹窗事件钩子函数 + * @desc 对应 RiskEvaluationMainDialogShow + */ + function mainDialogShowEventHandler({ + openMode, + }: { openMode?: RiskEvaluationMainDialogOpenMode } = {}) { + handleOpenDialog({ openMode }); + } + + // 副作用打开弹窗 + useRiskEvaluationMainEffect({ mainDialogShowEventHandler }); + + // 打开弹窗 + useEventBusListener(appEvents.finance.RiskEvaluationMainDialogShow, mainDialogShowEventHandler); + + // 关闭弹窗 + useEventBusListener(appEvents.finance.RiskEvaluationMainDialogHidden, () => { + handleCloseDialog(); + }); + + return { + questionnaireSdk, + + dialogVisible, + dialogCloseable, + isForceStart, + + handleCloseDialog, + onQuestionnaireEvaluationResult, + onQuestionnaireHide, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-entrance.vue b/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..25f57e8149c992e2a7db05c83ed9b850e4b521eb --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-entrance.vue @@ -0,0 +1,101 @@ + + + + + diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-icon.svg b/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-icon.svg new file mode 100644 index 0000000000000000000000000000000000000000..bdcbb2886df50a419432bae1c5856c90baad1e6f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-redirect-dialog.vue b/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-redirect-dialog.vue new file mode 100644 index 0000000000000000000000000000000000000000..520e97e6cad933f06efc0286a46f2bf99c945fdd --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/common/risk-evaluation-redirect-dialog.vue @@ -0,0 +1,116 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/mobile-risk-evaluation.vue b/src/components/page-watch-common/interactive-receive/risk-evaluation/mobile-risk-evaluation.vue new file mode 100644 index 0000000000000000000000000000000000000000..d8160c3a12d3c79afa01bf2a14c616cff138fff1 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/mobile-risk-evaluation.vue @@ -0,0 +1,65 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/pc-risk-evaluation.vue b/src/components/page-watch-common/interactive-receive/risk-evaluation/pc-risk-evaluation.vue new file mode 100644 index 0000000000000000000000000000000000000000..d90b422e3da517a564df09766eb616b0a48a9503 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/pc-risk-evaluation.vue @@ -0,0 +1,62 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/types/risk-evaluation-types.ts b/src/components/page-watch-common/interactive-receive/risk-evaluation/types/risk-evaluation-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..9aa7bf07f684011a15bab882b3bbdf780b14842f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/types/risk-evaluation-types.ts @@ -0,0 +1,29 @@ +/** 风险测评-主要弹窗-打开方式 */ +export enum RiskEvaluationMainDialogOpenMode { + /** 手动打开 */ + ManualOpen = 'manual-open', + /** 超时自动打开 */ + TimeoutAutoOpen = 'timeout-auto-open', +} + +/** 风险测评-测评结果 */ +export interface QuestionnaireEvaluationResult { + /** 频道 Id */ + channelId: number; + /** 用户唯一标识 */ + viewerId: string; + /** 场次 Id */ + sessionId: string; + /** 用户昵称 */ + nickname: string; + /** 测评问卷 Id */ + questionnaireId: string; + /** 观众测评得分(-1为未测评) */ + score: number; + /** 当前得分对应的标签描述 */ + scoreLabel: string; + /** 分数限制对应的标签描述 */ + scoreLabelLimit: string; + /** 通过直播间风险测评条件(1:通过,0:不通过) */ + passEvaluation: 0 | 1; +} diff --git a/src/components/page-watch-common/interactive-receive/risk-evaluation/use-risk-evaluation-reset.ts b/src/components/page-watch-common/interactive-receive/risk-evaluation/use-risk-evaluation-reset.ts new file mode 100644 index 0000000000000000000000000000000000000000..0ba5c41e9ff3063c1612e5e229a8a016772d1859 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/risk-evaluation/use-risk-evaluation-reset.ts @@ -0,0 +1,183 @@ +import { FinanceRiskEvaluationSocketData } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useFinanceStore } from '@/store/use-finance-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { appEvents, eventBus } from '@/app/app-events'; + +/** + * @hooks 风险测评-测评重置 + */ +export const useRiskEvaluationResetHook = () => { + const watchCore = getWatchCore(); + + const financeStore = useFinanceStore(); + + /** + * 风险测评-更改测评开关 + */ + function riskEvaluationResetEnableHandler(socketData: FinanceRiskEvaluationSocketData) { + const riskEvaluation = financeStore.riskEvaluation; + + if (ynToBool(socketData.evaluationEnabled)) { + // 关 -> 开 + financeStore.syncRiskEvaluation({ + evaluationEnabled: socketData.evaluationEnabled, + }); + + const isPass = watchCore.financeRiskEvaluation.judgeIsPassRiskEvaluation(riskEvaluation); + if (isPass) { + // 原本通过测评,则需要继续处理 socketData 中其他数据 + onRiskEvaluationReset({ socketData }); + return; + } + + // 没有通过测评,需要判断当前测评时机 + const isAfterLimitType = watchCore.financeRiskEvaluation.judgeIsAfterLimitType(socketData); + if (isAfterLimitType) { + financeStore.syncRiskEvaluation({ + evaluationLimitType: socketData.evaluationLimitType, + }); + return; + } + const isPreLimitType = watchCore.financeRiskEvaluation.judgeIsPreLimitType(socketData); + if (isPreLimitType) { + eventBus.$emit(appEvents.finance.RiskEvaluationRedirectDialogShow); + } + } else { + // 开 -> 关 + financeStore.syncRiskEvaluation({ + evaluationEnabled: socketData.evaluationEnabled, + }); + } + } + + /** + * 风险测评-更改测评分数标准,只同步更改 riskEvaluation 数据 + */ + function riskEvaluationResetScoreLimitHandler(socketData: FinanceRiskEvaluationSocketData) { + const riskEvaluation = financeStore.riskEvaluation; + const isScorePass = riskEvaluation.score >= socketData.scoreLimit; + financeStore.syncRiskEvaluation({ + scoreLimit: socketData.scoreLimit, + passEvaluation: isScorePass ? 1 : 0, + }); + } + + /** + * 风险测评-更改测评时机 + */ + function riskEvaluationResetLimitTypeHandler(socketData: FinanceRiskEvaluationSocketData) { + const riskEvaluation = financeStore.riskEvaluation; + + const isPass = watchCore.financeRiskEvaluation.judgeIsPassRiskEvaluation(riskEvaluation); + if (isPass) { + financeStore.syncRiskEvaluation({ + evaluationLimitType: socketData.evaluationLimitType, + }); + return; + } + + const { judgeIsPreLimitType, judgeIsAfterLimitType } = watchCore.financeRiskEvaluation; + + // 后置 -> 前置 + const isAfter2Pre = judgeIsAfterLimitType(riskEvaluation) && judgeIsPreLimitType(socketData); + if (isAfter2Pre) { + eventBus.$emit(appEvents.finance.RiskEvaluationPendantTimeoutCacheClear); + eventBus.$emit(appEvents.finance.RiskEvaluationRedirectDialogShow); + return; + } + + // 后置 -> 后置 + const isAfter2After = + judgeIsAfterLimitType(riskEvaluation) && judgeIsAfterLimitType(socketData); + if (isAfter2After) { + financeStore.syncRiskEvaluation({ + evaluationLimitType: socketData.evaluationLimitType, + }); + return; + } + + // 前置 -> 前置 + const isPre2Pre = judgeIsPreLimitType(riskEvaluation) && judgeIsPreLimitType(socketData); + if (isPre2Pre) { + eventBus.$emit(appEvents.finance.RiskEvaluationRedirectDialogShow); + return; + } + + // 前置 -> 后置 + const isPre2After = judgeIsPreLimitType(riskEvaluation) && judgeIsAfterLimitType(socketData); + if (isPre2After) { + riskEvaluationResetScoreLimitHandler(socketData); + riskEvaluationMainModalShowByNotPass(); + } + } + + /** + * 风险测评-不通过测评时展示"去测评"弹窗 + */ + function riskEvaluationRedirectModalShowByNotPass() { + const riskEvaluation = financeStore.riskEvaluation; + + const isPass = watchCore.financeRiskEvaluation.judgeIsPassRiskEvaluation(riskEvaluation); + if (!isPass) { + eventBus.$emit(appEvents.finance.RiskEvaluationRedirectDialogShow); + } + } + + /** + * 风险测评-不通过测评时展示测评弹窗 + */ + function riskEvaluationMainModalShowByNotPass() { + const riskEvaluation = financeStore.riskEvaluation; + + const isPass = watchCore.financeRiskEvaluation.judgeIsPassRiskEvaluation(riskEvaluation); + if (!isPass) { + eventBus.$emit(appEvents.finance.RiskEvaluationMainDialogShow); + } + } + + /** + * 处理"风险测评-测评重置"事件钩子 + */ + function onRiskEvaluationReset({ + socketData, + }: { + socketData: FinanceRiskEvaluationSocketData; + }): void { + const riskEvaluation = financeStore.riskEvaluation; + + // 更改测评开关 + if (socketData.evaluationEnabled !== riskEvaluation.evaluationEnabled) { + return riskEvaluationResetEnableHandler(socketData); + } + + // 测评开关开启时才会响应其他更改 + if (ynToBool(riskEvaluation.evaluationEnabled)) { + // 同时更改:更改测评分数标准 + 更改测评时机 + if ( + socketData.scoreLimit !== riskEvaluation.scoreLimit && + socketData.evaluationLimitType !== riskEvaluation.evaluationLimitType + ) { + riskEvaluationResetScoreLimitHandler(socketData); + return riskEvaluationResetLimitTypeHandler(socketData); + } + + // 只更改测评时机 + if (socketData.evaluationLimitType !== riskEvaluation.evaluationLimitType) { + return riskEvaluationResetLimitTypeHandler(socketData); + } + + // 只更改测评分数标准 + if (socketData.scoreLimit !== riskEvaluation.scoreLimit) { + riskEvaluationResetScoreLimitHandler(socketData); + // 目前只有前置时机有分数限制,不通过测评的话需要展示"去测评"弹窗 + riskEvaluationRedirectModalShowByNotPass(); + } + } + } + + return { + onRiskEvaluationReset, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/score/hooks/use-score-record.ts b/src/components/page-watch-common/interactive-receive/score/hooks/use-score-record.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb0940eefc9e5e29d8a5017b51ba950f99ed666f --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/score/hooks/use-score-record.ts @@ -0,0 +1,51 @@ +import { ref, onBeforeMount, onBeforeUnmount, computed } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; + +/** + * @hook 互动-红包-积分记录弹层 + * */ +export const useScoreRecord = () => { + const watchCore = getWatchCore(); + /** 红包 SDK 实例 */ + const redpackSdk = watchCore.interactReceive.getRedpack(); + + /** 积分记录组件是否创建,用于控制刷新数据、在首次没打开时不请求接口 */ + const scoreRecordRender = ref(false); + function setScoreRecordRender(needRender = true) { + scoreRecordRender.value = needRender; + } + + /** 积分记录显示状态 */ + const scoreRecordVisible = ref(false); + /** 设置显示状态 */ + function setScoreRecordVisible(visible = true) { + scoreRecordVisible.value = visible; + + if (visible) { + setScoreRecordRender(true); + } + } + + /** 积分领取记录标题 */ + const scoreRecordTitle = computed(() => translate('interact.scoreRecord.title')); + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.OpenScoreRecord, setScoreRecordVisible); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.OpenScoreRecord, setScoreRecordVisible); + }); + + return { + redpackSdk, + scoreRecordRender, + setScoreRecordRender, + scoreRecordVisible, + scoreRecordTitle, + setScoreRecordVisible, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/score/mobile-score-record-popup.vue b/src/components/page-watch-common/interactive-receive/score/mobile-score-record-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..8df529fc7ac112158e14fda1d4322e8324e5aa1c --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/score/mobile-score-record-popup.vue @@ -0,0 +1,43 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/topic-pk/hook/use-topic-pk.ts b/src/components/page-watch-common/interactive-receive/topic-pk/hook/use-topic-pk.ts new file mode 100644 index 0000000000000000000000000000000000000000..bccff32d327357c73d3e1fbe6e13fe242abccc7e --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/topic-pk/hook/use-topic-pk.ts @@ -0,0 +1,11 @@ +import { getWatchCore } from '@/core/watch-sdk'; + +export const useTopicPk = () => { + const watchCore = getWatchCore(); + + const topicPkSdk = watchCore.interactReceive.getTopicPK(); + + return { + topicPkSdk, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/topic-pk/mobile-topic-pk.vue b/src/components/page-watch-common/interactive-receive/topic-pk/mobile-topic-pk.vue new file mode 100644 index 0000000000000000000000000000000000000000..c64661ff5477afdc5ec290e8154e2bcf68cd76fb --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/topic-pk/mobile-topic-pk.vue @@ -0,0 +1,16 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/topic-pk/pc-topic-pk.vue b/src/components/page-watch-common/interactive-receive/topic-pk/pc-topic-pk.vue new file mode 100644 index 0000000000000000000000000000000000000000..c4aaba84e001fa7baa040e7637112f18feedcf2a --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/topic-pk/pc-topic-pk.vue @@ -0,0 +1,16 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/tuwen-live/hooks/use-tuwen-live.ts b/src/components/page-watch-common/interactive-receive/tuwen-live/hooks/use-tuwen-live.ts new file mode 100644 index 0000000000000000000000000000000000000000..fbb45d9f88bc18483499819c898dd2e70a9dfde8 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/tuwen-live/hooks/use-tuwen-live.ts @@ -0,0 +1,109 @@ +import { ref, computed, unref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { translate } from '@/assets/lang'; +import { previewImage } from '@/hooks/components/use-image-preview'; + +import { AllTuwenMode, IarTuwenLiveInstance } from '../types/tuwen-live-types'; + +interface PreviewTuwenImageData { + /** 图片地址列表 */ + images: string[]; + /** 显示的下标 */ + index: number; +} + +/** + * @hook 互动-图文直播 + * */ +export const useTuwenLive = () => { + const watchCore = getWatchCore(); + /** 图文直播 SDK 实例 */ + const tuwenSdk = watchCore.interactReceive.getTuwen(); + + /** 图文总条数 */ + const messageTotal = ref(0); + /** 处理图文总条数改变 */ + const onMessageTotalChange = (total = 0) => { + messageTotal.value = total; + }; + + /** 图文直播组件实例 */ + const tuwenRef = ref(); + /** 设置图文直播组件监听 window 的事件 */ + const setupScrollListen = () => { + const tuwenInstance = unref(tuwenRef); + + if (!tuwenInstance) { + console.warn('setupScrollListen fail, tuwenRef is undefined!'); + return; + } + if (tuwenInstance.initScrollLister) { + tuwenInstance.initScrollLister(); + } + }; + + /** 预览图文直播的图片 */ + const previewTuwenImage = (data: PreviewTuwenImageData) => { + const { images, index } = data; + previewImage(images, { + initialIndex: index, + }); + }; + + return { + tuwenSdk, + + messageTotal, + onMessageTotalChange, + + tuwenRef, + setupScrollListen, + + previewTuwenImage, + }; +}; + +/** 图文直播展示模式 hook */ +export const useTuwenLiveMode = () => { + /** 图文展示模式,1-图文,2-图片 */ + const tuwenMode = ref(AllTuwenMode.Tuwen); + + /** 图文展示模式选项 */ + const tuwenModeOptions = computed(() => [ + { + name: translate('interact.tuwen.tuwenMode'), + value: AllTuwenMode.Tuwen, + isCurrent: tuwenMode.value === AllTuwenMode.Tuwen, + }, + { + name: translate('interact.tuwen.picMode'), + value: AllTuwenMode.Image, + isCurrent: tuwenMode.value === AllTuwenMode.Image, + }, + ]); + + /** 当前图文展示模式文案 */ + const currentTuwenModeText = computed(() => { + const options = tuwenModeOptions.value; + let text = ''; + options.forEach(option => { + if (option.isCurrent) { + text = option.name; + } + }); + return text; + }); + + /** 修改图文展示模式 */ + const changeTuwenMode = (mode = AllTuwenMode.Tuwen) => { + tuwenMode.value = mode; + }; + + return { + tuwenMode, + tuwenModeOptions, + currentTuwenModeText, + changeTuwenMode, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/tuwen-live/mobile-tuwen-live.vue b/src/components/page-watch-common/interactive-receive/tuwen-live/mobile-tuwen-live.vue new file mode 100644 index 0000000000000000000000000000000000000000..9d6510cde237a430cf07bbeba44fd5592091e301 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/tuwen-live/mobile-tuwen-live.vue @@ -0,0 +1,97 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/tuwen-live/pc-tuwen-live.vue b/src/components/page-watch-common/interactive-receive/tuwen-live/pc-tuwen-live.vue new file mode 100644 index 0000000000000000000000000000000000000000..929b1acd7435f74f70f0930a1107390c89c120e7 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/tuwen-live/pc-tuwen-live.vue @@ -0,0 +1,109 @@ + + + + + + diff --git a/src/components/page-watch-common/interactive-receive/tuwen-live/types/tuwen-live-types.ts b/src/components/page-watch-common/interactive-receive/tuwen-live/types/tuwen-live-types.ts new file mode 100644 index 0000000000000000000000000000000000000000..8b15d63e69abc13612fa3d3c3165474ff22c07d0 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/tuwen-live/types/tuwen-live-types.ts @@ -0,0 +1,12 @@ +/** 图文展示模式枚举 */ +export enum AllTuwenMode { + /** 图文模式 */ + Tuwen = 1, + /** 图片模式 */ + Image = 2, +} + +export interface IarTuwenLiveInstance { + /** 设置页面滚动条监听 */ + initScrollLister?: () => void; +} diff --git a/src/components/page-watch-common/interactive-receive/use-iar-global-config.ts b/src/components/page-watch-common/interactive-receive/use-iar-global-config.ts new file mode 100644 index 0000000000000000000000000000000000000000..9837f1060d488707febfa8377fb6dc6a23a3d09d --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/use-iar-global-config.ts @@ -0,0 +1,28 @@ +import { useLangStore } from '@/store/use-lang-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { provide, reactive, watchEffect } from 'vue'; + +/** + * 互动功能全局配置注入的 key + */ +const INTERACT_GLOBAL_PROVIDE_KEY = 'interactGlobalConfig'; + +/** + * @hook 互动功能全局配置 + */ +export const useIarGlobalConfigHook = () => { + const langStore = useLangStore(); + const playerStore = usePlayerStore(); + + const provideData = reactive({ + globalLang: langStore.currentLang, + globalDelayTime: playerStore.delayTime, + }); + + watchEffect(() => { + provideData.globalLang = langStore.currentLang; + provideData.globalDelayTime = playerStore.delayTime; + }); + + provide(INTERACT_GLOBAL_PROVIDE_KEY, provideData); +}; diff --git a/src/components/page-watch-common/interactive-receive/vote/hooks/use-vote.ts b/src/components/page-watch-common/interactive-receive/vote/hooks/use-vote.ts new file mode 100644 index 0000000000000000000000000000000000000000..00df1dbe767c662ef939ae0d46b13fce51f4807a --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/vote/hooks/use-vote.ts @@ -0,0 +1,19 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; + +/** 投票 hook */ +export const useVote = () => { + const watchCore = getWatchCore(); + + /** 投票 SDK 实例 */ + const voteSdk = watchCore.interactReceive.getVote(); + + /** 当前用户已投票的列表 */ + const { votedList } = storeDefinitionToRefs(useInteractReceiveStore); + + return { + voteSdk, + votedList, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/vote/mobile-vote-panel.vue b/src/components/page-watch-common/interactive-receive/vote/mobile-vote-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..0b8c10dcdef4ff2883a0f76e789efd141b4e3b41 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/vote/mobile-vote-panel.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/vote/pc-vote-panel.vue b/src/components/page-watch-common/interactive-receive/vote/pc-vote-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..c5eb09b50b8ee484418cc38c94eda0009fd9b33b --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/vote/pc-vote-panel.vue @@ -0,0 +1,22 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/welfare-lottery/hooks/use-welfare-lottery.ts b/src/components/page-watch-common/interactive-receive/welfare-lottery/hooks/use-welfare-lottery.ts new file mode 100644 index 0000000000000000000000000000000000000000..353c65bd5f858b1181603995d7ce1dc1f98c4fc3 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/welfare-lottery/hooks/use-welfare-lottery.ts @@ -0,0 +1,74 @@ +import { computed, onBeforeMount, onBeforeUnmount } from 'vue'; +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useChannelStore } from '@/store/use-channel-store'; +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; + +import { useSendMsg } from '@/components/page-watch-common/chat/chat-msg-list/hooks/send-msg-hook'; +import { YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; + +/** + * @hook 互动-抽奖-条件抽奖 + * */ +export const useWelfareLottery = () => { + const channelStore = useChannelStore(); + const interactReceiveStore = useInteractReceiveStore(); + const { sendSpeakMsg } = useSendMsg(); + + const watchCore = getWatchCore(); + /** 条件抽奖 SDK 实例 */ + const welfareLotterySdk = watchCore.interactReceive.getWelfareLottery(); + + /** 是否自动弹窗 */ + const conditionLotterySPAutoPopEnabled = computed(() => { + return ynToBool( + channelStore.channelDetail?.channelConfig.conditionLotterySPAutoPopEnabled, + YN.N, + ); + }); + + /** + * 处理抽奖弹窗中的文本发言 + * @param comment 发言的文本 + */ + const onCommentSuccess = (comment: string) => { + sendSpeakMsg({ + content: comment, + onlyLocalMsg: true, + }); + }; + + /** + * 检查发言文本是否符合评论抽奖条件后参与进抽奖中 + * @param comment 发言的文本 + */ + const checkLotteryComment = async (comment: string) => { + const isSatisfy = await welfareLotterySdk.isBooleanToCommentLottery(comment); + if (isSatisfy) { + welfareLotterySdk.finishCommentLottery(); + } + }; + + onBeforeMount(() => { + eventBus.$on(appEvents.interaction.CheckCommentLotteryComment, checkLotteryComment); + }); + + onBeforeUnmount(() => { + eventBus.$off(appEvents.interaction.CheckCommentLotteryComment, checkLotteryComment); + }); + + /** 处理条件抽奖入口显示状态改变 */ + const onEntryVisibleChanged = (visible = true) => { + interactReceiveStore.welfareLotteryPendantVisible = visible; + }; + + return { + welfareLotterySdk, + conditionLotterySPAutoPopEnabled, + onCommentSuccess, + checkLotteryComment, + onEntryVisibleChanged, + }; +}; diff --git a/src/components/page-watch-common/interactive-receive/welfare-lottery/mobile-welfare-lottery.vue b/src/components/page-watch-common/interactive-receive/welfare-lottery/mobile-welfare-lottery.vue new file mode 100644 index 0000000000000000000000000000000000000000..99cbeb4aa889f01e7bb01026831165ac3756b411 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/welfare-lottery/mobile-welfare-lottery.vue @@ -0,0 +1,40 @@ + + + + diff --git a/src/components/page-watch-common/interactive-receive/welfare-lottery/pc-welfare-lottery.vue b/src/components/page-watch-common/interactive-receive/welfare-lottery/pc-welfare-lottery.vue new file mode 100644 index 0000000000000000000000000000000000000000..743ae7c50697c4f0115b2fb319812964321fe4a6 --- /dev/null +++ b/src/components/page-watch-common/interactive-receive/welfare-lottery/pc-welfare-lottery.vue @@ -0,0 +1,34 @@ + + + + diff --git a/src/components/page-watch-common/invite/hooks/use-invite-poster-entry.ts b/src/components/page-watch-common/invite/hooks/use-invite-poster-entry.ts new file mode 100644 index 0000000000000000000000000000000000000000..f1af9dfa8c6f06ea7ba0b98128033f63038cfcf7 --- /dev/null +++ b/src/components/page-watch-common/invite/hooks/use-invite-poster-entry.ts @@ -0,0 +1,16 @@ +/** @file 邀请海报入口 hook 钩子 */ + +import { getIosVersion } from '@/assets/utils/browser'; +import { useInviteStore } from '@/store/use-invite-store'; +import { toast } from '@/hooks/components/use-toast'; + +export function redirectToPosterPage() { + const { generatePosterUrl } = useInviteStore(); + + const iosVersion = getIosVersion(); + if (iosVersion && iosVersion <= 9) { + toast.error('当前设备系统版本过低,请更新版本后重试'); + } else { + window.location.href = generatePosterUrl; + } +} diff --git a/src/components/page-watch-common/invite/hooks/use-invite-rank.ts b/src/components/page-watch-common/invite/hooks/use-invite-rank.ts new file mode 100644 index 0000000000000000000000000000000000000000..fa2539db2273d3d0e95d2b623f1182465d6cd129 --- /dev/null +++ b/src/components/page-watch-common/invite/hooks/use-invite-rank.ts @@ -0,0 +1,203 @@ +/** @file 邀请榜 hook 钩子 */ + +import debounce from 'lodash-es/debounce'; +import { computed, ref, unref } from 'vue'; + +import { getWatchCore } from '@/core/watch-sdk'; +import { useInviteStore } from '@/store/use-invite-store'; +import { useViewerStore } from '@/store/use-viewer-store'; + +import { translate } from '@/assets/lang'; +import { isWeixin, isWorkWeixin } from '@/assets/utils/browser'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { InviteRankCurrentViewer, InviteRankItem } from '@polyv/live-watch-sdk'; + +const isGeneralWeixin = isWeixin && !isWorkWeixin; + +interface InviteRankHookOptions { + /** 需要筛选邀请人数为 0 的邀请者 */ + needFilterZeroInvitee: boolean; +} + +export const useInviteRankHook = ( + hookOptions: InviteRankHookOptions = { + needFilterZeroInvitee: false, + }, +) => { + const page = ref(0); + const isLoading = ref(false); + const isNoMore = ref(false); + /** 接口获取的邀请榜列表 */ + const resRankList = ref([]); + /** 当前可见的邀请榜列表,除去前三名 */ + const rankList = ref([]); + /** 邀请榜前三名 */ + const topThreeRankItems = ref([]); + /** 接口获取的当前用户邀请数据 */ + const currentRankInfo = ref(null); + /** 是否加载接口失败 */ + const isLoadFailed = ref(false); + + const viewerStore = useViewerStore(); + const { invitePosterEnabled } = storeDefinitionToRefs(useInviteStore); + + /** 是否显示底部信息 */ + const showBottomInfo = computed(() => { + return isGeneralWeixin && currentRankInfo.value && invitePosterEnabled.value; + }); + + /** 当前用户与第一名的距离 */ + const currentToFirstNumber = computed(() => { + if (!currentRankInfo.value || !topThreeRankItems.value.length) { + return 0; + } + return Math.abs(topThreeRankItems.value[0].inviteNum - currentRankInfo.value.inviteNum); + }); + + /** 当前用户与第二名的距离 */ + const currentToSecondNumber = computed(() => { + if (!currentRankInfo.value || topThreeRankItems.value.length < 2) { + return 0; + } + return Math.abs(topThreeRankItems.value[1].inviteNum - currentRankInfo.value.inviteNum); + }); + + /** 当前用户的排名文案 */ + const currentDes = computed(() => { + const rankInfo = unref(currentRankInfo); + if (!rankInfo) { + return ''; + } + + // 我是第一名 + if (rankInfo.rank === 1) { + // 只有我一个人 + if (unref(topThreeRankItems).length === 1) { + return translate('invite.rank.desc1'); + } + + // 有第二名 + return translate('invite.rank.desc2', { + number: String(currentToSecondNumber.value), + }); + } + + return translate('invite.rank.desc3', { + number: String(currentToFirstNumber.value), + }); + }); + + /** 判断是否为当前用户 */ + function isCurrentViewer(item: InviteRankItem) { + return item.openId === viewerStore.openId; + } + + /** 初始化邀请榜数据 */ + async function initInviteRank() { + page.value = 0; + isLoading.value = false; + isNoMore.value = false; + await getRankList(true); + if (isGeneralWeixin) { + await getCurrentRankInfo(); + } + } + + const debounceTime = 300; + /** 邀请榜滚动钩子 */ + const handleScroll = debounce(function (e) { + if (isNoMore.value) { + return; + } + // 获取下一页数据 + const target = e.target; + const scrollHeight = parseInt(target.scrollHeight); + const scrollTop = parseInt(target.scrollTop); + const clientHeight = parseInt(target.clientHeight); + if (scrollTop >= scrollHeight - clientHeight - 150) { + if (resRankList.value.length > 0) { + rankList.value.push(...resRankList.value.splice(0, 20)); + } else { + getRankList(); + } + } + }, debounceTime); + + /** 获取邀请榜列表数据 */ + async function getRankList(isFirst = false) { + if (isLoading.value || isNoMore.value) return; + + try { + isLoadFailed.value = false; + isLoading.value = true; + + const watchCore = getWatchCore(); + const data = await watchCore.invite.getRankList({ + pageNumber: page.value + 1, + }); + + isLoading.value = false; + page.value = data.pageNumber; + + let contents = data.contents; + // 过滤掉邀请数为 0 的节点 + if (hookOptions.needFilterZeroInvitee) { + contents = data.contents.filter(item => { + return item.inviteNum !== 0; + }); + } + resRankList.value = contents; + + if (isFirst) { + rankList.value = []; + } + if (resRankList.value.length > 0) { + rankList.value.push(...resRankList.value.splice(0, 20)); + // 单独获取前三名数据 + if (isFirst) { + topThreeRankItems.value = rankList.value.splice(0, 3); + } + } + if (data.totalPages <= page.value) { + isNoMore.value = true; + } + } catch (err) { + isLoadFailed.value = true; + console.error('getRankList', err); + } finally { + isLoading.value = false; + } + } + + /** 获取当前用户的排行信息 */ + async function getCurrentRankInfo() { + try { + const watchCore = getWatchCore(); + const data = await watchCore.invite.getCurrentRankInfo(); + if (!data) { + console.warn('获取当前用户的排行榜信息为空'); + } else { + currentRankInfo.value = { ...data }; + } + } catch (error) { + console.error('getCurrentRankInfo', error); + } + } + + return { + currentRankInfo, + rankList, + topThreeRankItems, + isLoading, + isNoMore, + isLoadFailed, + + showBottomInfo, + currentDes, + invitePosterEnabled, + + isCurrentViewer, + initInviteRank, + handleScroll, + }; +}; diff --git a/src/components/page-watch-common/invite/mobile-invite-poster-entry/imgs/invite-poster-entry.png b/src/components/page-watch-common/invite/mobile-invite-poster-entry/imgs/invite-poster-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..a2b15c2de08600338884c82275cd60e6a7ebb6ad Binary files /dev/null and b/src/components/page-watch-common/invite/mobile-invite-poster-entry/imgs/invite-poster-entry.png differ diff --git a/src/components/page-watch-common/invite/mobile-invite-poster-entry/mobile-invite-poster-entry-capsule.vue b/src/components/page-watch-common/invite/mobile-invite-poster-entry/mobile-invite-poster-entry-capsule.vue new file mode 100644 index 0000000000000000000000000000000000000000..8afb377ab9aa4b19d4a7efc14ed893e451826c3e --- /dev/null +++ b/src/components/page-watch-common/invite/mobile-invite-poster-entry/mobile-invite-poster-entry-capsule.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/mobile-invite-poster-entry/mobile-invite-poster-entry.vue b/src/components/page-watch-common/invite/mobile-invite-poster-entry/mobile-invite-poster-entry.vue new file mode 100644 index 0000000000000000000000000000000000000000..d213af08d50ee494a579d5a8c121e25343b04961 --- /dev/null +++ b/src/components/page-watch-common/invite/mobile-invite-poster-entry/mobile-invite-poster-entry.vue @@ -0,0 +1,24 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-1.png b/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-1.png new file mode 100644 index 0000000000000000000000000000000000000000..e32a4d0fc8e6859dc3352ee65765e7c69f3b6f01 Binary files /dev/null and b/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-1.png differ diff --git a/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-2.png b/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-2.png new file mode 100644 index 0000000000000000000000000000000000000000..7986a10d0e6b4677ecefd08a63d93af6cd468bce Binary files /dev/null and b/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-2.png differ diff --git a/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-3.png b/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-3.png new file mode 100644 index 0000000000000000000000000000000000000000..4d643fbee1ceaae2b09b88a0ef21c6e470a08f41 Binary files /dev/null and b/src/components/page-watch-common/invite/mobile-invite-rank/imgs/rank-3.png differ diff --git a/src/components/page-watch-common/invite/mobile-invite-rank/mobile-invite-rank-item.vue b/src/components/page-watch-common/invite/mobile-invite-rank/mobile-invite-rank-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..7aaa3243620b97d8fd34de95a983d206e38f85b9 --- /dev/null +++ b/src/components/page-watch-common/invite/mobile-invite-rank/mobile-invite-rank-item.vue @@ -0,0 +1,140 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/mobile-invite-rank/mobile-invite-rank.vue b/src/components/page-watch-common/invite/mobile-invite-rank/mobile-invite-rank.vue new file mode 100644 index 0000000000000000000000000000000000000000..d0ddcc64cfcb40999f77eca7334e1053b9af2a7c --- /dev/null +++ b/src/components/page-watch-common/invite/mobile-invite-rank/mobile-invite-rank.vue @@ -0,0 +1,201 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/portrait-invite-poster-entry/portrait-invite-poster-entry.vue b/src/components/page-watch-common/invite/portrait-invite-poster-entry/portrait-invite-poster-entry.vue new file mode 100644 index 0000000000000000000000000000000000000000..fdb82e6abd3e4b03c6927de81c5d4c47aa055f4c --- /dev/null +++ b/src/components/page-watch-common/invite/portrait-invite-poster-entry/portrait-invite-poster-entry.vue @@ -0,0 +1,23 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/fonts/BarlowSemiCondensed-Medium.ttf b/src/components/page-watch-common/invite/portrait-invite-rank/fonts/BarlowSemiCondensed-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..fdcaddecf5ce9223343a26fc0d8ab66be74ba998 Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/fonts/BarlowSemiCondensed-Medium.ttf differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/fonts/BarlowSemiCondensed-SemiBoldItalic.ttf b/src/components/page-watch-common/invite/portrait-invite-rank/fonts/BarlowSemiCondensed-SemiBoldItalic.ttf new file mode 100644 index 0000000000000000000000000000000000000000..754dd37a6806aa9407ca718c9b7aacb7800ece10 Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/fonts/BarlowSemiCondensed-SemiBoldItalic.ttf differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-champion.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-champion.png new file mode 100644 index 0000000000000000000000000000000000000000..f74a72ec8d2c47c4f0348b45bf3d54154c72ce48 Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-champion.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-default-avatar.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-default-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..62cfc4a1321181aec958297bcfc98463db73d3be Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-default-avatar.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-invite.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-invite.png new file mode 100644 index 0000000000000000000000000000000000000000..7c2d12591dadce9a9ccea97bd78d064380247b5f Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-invite.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no-invite.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no-invite.png new file mode 100644 index 0000000000000000000000000000000000000000..2865789fa5b72a80b2f3092d6cd4cce5ef17d84a Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no-invite.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.1.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.1.png new file mode 100644 index 0000000000000000000000000000000000000000..e9032f12fc39906b5fe1e42aaa7a38404855a36a Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.1.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.2.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.2.png new file mode 100644 index 0000000000000000000000000000000000000000..7bbbe63f332af767de7be931be77b2082d949bc2 Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.2.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.3.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.3.png new file mode 100644 index 0000000000000000000000000000000000000000..d67d24201872cf6868524117582a088866e4a92e Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-no.3.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-second-place.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-second-place.png new file mode 100644 index 0000000000000000000000000000000000000000..3f2017aff21998a5fefc9a88954af1c398977eaf Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-second-place.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-third-place.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-third-place.png new file mode 100644 index 0000000000000000000000000000000000000000..406f2897643e4e2d60d58665fba300abebb0024b Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/icon-third-place.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/imgs/invite-bg.png b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/invite-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..5978fd124559c9831a3fe64b0fd9fc56d28dc037 Binary files /dev/null and b/src/components/page-watch-common/invite/portrait-invite-rank/imgs/invite-bg.png differ diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank-entrance.vue b/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..95803d0ca245fc26d85ff9ef74dc80d4a391945e --- /dev/null +++ b/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank-entrance.vue @@ -0,0 +1,44 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank-top3-item.vue b/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank-top3-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..a50f263595f956f0c44725ed8fb78e51b4793e4b --- /dev/null +++ b/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank-top3-item.vue @@ -0,0 +1,166 @@ + + + + + + diff --git a/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank.vue b/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank.vue new file mode 100644 index 0000000000000000000000000000000000000000..13e5f7867c5d22060b6d6465573b4ffa35d2b96d --- /dev/null +++ b/src/components/page-watch-common/invite/portrait-invite-rank/portrait-invite-rank.vue @@ -0,0 +1,350 @@ + + + + + + diff --git a/src/components/page-watch-common/live-start-tips/mobile-live-start-tips.vue b/src/components/page-watch-common/live-start-tips/mobile-live-start-tips.vue new file mode 100644 index 0000000000000000000000000000000000000000..f858ac6d67f41bc7294069bde959ec1ae44eec48 --- /dev/null +++ b/src/components/page-watch-common/live-start-tips/mobile-live-start-tips.vue @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/components/page-watch-common/live-start-tips/pc-live-start-tips.vue b/src/components/page-watch-common/live-start-tips/pc-live-start-tips.vue new file mode 100644 index 0000000000000000000000000000000000000000..7304e88db55c0991b576ed40b884be7f8310194b --- /dev/null +++ b/src/components/page-watch-common/live-start-tips/pc-live-start-tips.vue @@ -0,0 +1,87 @@ + + + + + + diff --git a/src/components/page-watch-common/live-start-tips/use-live-start-tips.ts b/src/components/page-watch-common/live-start-tips/use-live-start-tips.ts new file mode 100644 index 0000000000000000000000000000000000000000..a8793345c0ea95c549fe1683c6699dd5f6272b2e --- /dev/null +++ b/src/components/page-watch-common/live-start-tips/use-live-start-tips.ts @@ -0,0 +1,47 @@ +import { ref, unref, watch } from 'vue'; +import { LiveStatus } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; + +/** + * 直播开始提示 hook + */ +export const useLiveStartTips = () => { + const watchCore = getWatchCore(); + const channelStore = useChannelStore(); + + /** 提示是否显示 */ + const tipsVisible = ref(false); + + /** 打开提示 */ + function openTips() { + tipsVisible.value = true; + } + + /** 关闭提示 */ + function closeTips() { + tipsVisible.value = false; + watchCore.player.play(); + } + + watch( + () => unref(channelStore.liveStatus), + (newStatus, oldStatus) => { + if (!oldStatus) return; + + if (newStatus === LiveStatus.Live) { + openTips(); + } else { + closeTips(); + } + }, + { + immediate: true, + }, + ); + + return { + tipsVisible, + closeTips, + }; +}; diff --git a/src/components/page-watch-common/member-list/imgs/loading.png b/src/components/page-watch-common/member-list/imgs/loading.png new file mode 100644 index 0000000000000000000000000000000000000000..8a2d5b705698d63ce54f18b469b30eab095949b8 Binary files /dev/null and b/src/components/page-watch-common/member-list/imgs/loading.png differ diff --git a/src/components/page-watch-common/member-list/member-list-item.vue b/src/components/page-watch-common/member-list/member-list-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..6831650546cab19938e66504e3d0fd44b6acb02d --- /dev/null +++ b/src/components/page-watch-common/member-list/member-list-item.vue @@ -0,0 +1,90 @@ + + + + + + + diff --git a/src/components/page-watch-common/member-list/member-list.vue b/src/components/page-watch-common/member-list/member-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..8b29995fe974121c9d82ab7e20b1408057945a8f --- /dev/null +++ b/src/components/page-watch-common/member-list/member-list.vue @@ -0,0 +1,145 @@ + + + + + + diff --git a/src/components/page-watch-common/member-list/use-member-list.ts b/src/components/page-watch-common/member-list/use-member-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..97e5f76e3a5db4cbbc51ee5040a15487c17ace85 --- /dev/null +++ b/src/components/page-watch-common/member-list/use-member-list.ts @@ -0,0 +1,262 @@ +import { ref, reactive, unref } from 'vue'; +import { + ChatUserTypes, + ChatMessageUser, + ChatEventsRelations, + ChatEvents, + UserEventsRelations, + UserEvents, +} from '@polyv/live-watch-sdk'; +import { useViewerStore } from '@/store/use-viewer-store'; +import { getWatchCore } from '@/core/watch-sdk'; + +/** 新增的成员组类型 */ +export enum MemberAdditionalGroupTypes { + SELF = 'self', + OTHER = 'other', +} +/** 成员组类型 */ +type MemberGroupType = ChatUserTypes | MemberAdditionalGroupTypes; + +/** 成员信息 */ +export interface MemberInfo extends ChatMessageUser { + groupType: MemberGroupType; +} + +/** 成员组 */ +type MemberGroup = { [groupType in MemberGroupType]: MemberInfo[] }; + +export const useMemberListHook = () => { + const watchCore = getWatchCore(); + + const viewerStore = useViewerStore(); + + /** 页码 */ + const pageNumber = ref(1); + + /** 是否没有更多数据 */ + const isNoMore = ref(false); + + /** 成员组类型(按先后顺序) */ + const memberGroupTypes = ref([ + MemberAdditionalGroupTypes.SELF, + ChatUserTypes.Host, + ChatUserTypes.Manager, + ChatUserTypes.Teacher, + ChatUserTypes.Guest, + ChatUserTypes.Assistant, + ChatUserTypes.Attendee, + ChatUserTypes.Student, + ChatUserTypes.Slice, + MemberAdditionalGroupTypes.OTHER, + ]); + + /** 成员组数据 */ + const memberGroupMap = reactive( + unref(memberGroupTypes).reduce((accu, cur) => { + accu[cur] = []; + return accu; + }, {} as MemberGroup), + ); + + /** 按id记录观众信息,方便移除分类列表中的用户(因为收到的LOGOUT消息只有userId和uid,此处记录整个user信息) */ + const memberIdMap = reactive<{ [userId: string]: MemberInfo }>({}); + + /** 新登录的成员队列 */ + let loginMemberQueue: MemberInfo[] = []; + /** 登出的成员队列 */ + let logoutMemberQueue: MemberInfo[] = []; + /** 队列处理定时器 */ + const memberQueueTimer = ref(undefined); + + /** 获取下一页用户列表 */ + async function getNextUserList() { + try { + /** 首次加载200条 */ + const FIRST_PAGE_SIZE = 200; + const PAGE_SIZE = 100; + const isFirstFetch = pageNumber.value === 1; + const pageSize = isFirstFetch ? FIRST_PAGE_SIZE : PAGE_SIZE; + + const data = await watchCore.chat.getUserList({ + pageNumber: unref(pageNumber), + pageSize, + }); + const userList = data.userlist || []; + + addMembers(userList); + pageNumber.value += isFirstFetch ? 2 : 1; + isNoMore.value = userList.length < PAGE_SIZE; + } catch (error) { + console.error(error); + throw error; + } + } + + /** 将用户转换为成员 */ + function convertUser2Member(user: ChatMessageUser): MemberInfo { + const isExistType = unref(memberGroupTypes).includes(user.userType); + const isSelf = user.userId === viewerStore.viewerId; + + let groupType: MemberGroupType = MemberAdditionalGroupTypes.OTHER; + if (isExistType) { + groupType = user.userType; + } + if (isSelf) { + groupType = MemberAdditionalGroupTypes.SELF; + } + + return { + ...user, + groupType, + }; + } + + /** 将一组成员插入到在线列表进行显示 */ + function addMembers(userList: ChatMessageUser[]) { + userList.forEach(user => { + const member = convertUser2Member(user); + + if (!memberIdMap[member.userId]) { + memberGroupMap[member.groupType].push(member); + memberIdMap[member.userId] = member; + } + }); + } + + /** 移除一组成员 */ + function removeMembers(memberList: MemberInfo[]) { + memberList + .filter(member => memberIdMap[member.userId]) + .forEach(member => { + const targetMemberGroup = memberGroupMap[member.groupType]; + + const targetUserId = member.userId; + const idx = targetMemberGroup.findIndex(item => item.userId === targetUserId); + + delete memberIdMap[targetUserId]; + if (idx > -1) { + // 已进入到显示列表 + targetMemberGroup.splice(idx, 1); + } else { + // 还在待显示队列 + const queueIndex = loginMemberQueue.findIndex(item => item.userId === targetUserId); + queueIndex > -1 && loginMemberQueue.splice(queueIndex, 1); + } + }); + } + + /** + * 成员队列处理函数 + * 通过定时器 memberQueueTimer 来处理登录和登出用户队列 + * */ + function memberQueueHandler() { + const POP_COUNT = 20; + + let popMembers: MemberInfo[] = []; + + // 将队列中一部分新登录用户加入到显示的在线列表(最多同时加入20个) + if (loginMemberQueue.length) { + if (loginMemberQueue.length > POP_COUNT) { + popMembers = loginMemberQueue.splice(0, POP_COUNT); + } else { + popMembers = loginMemberQueue.splice(0, loginMemberQueue.length); + } + addMembers(popMembers); + } + + // 将已离开的用户队列数据,移除展示 + if (logoutMemberQueue.length) { + const leftUsers = logoutMemberQueue.splice(0, logoutMemberQueue.length); + removeMembers(leftUsers); + } + + // 队列无数据,清除定时器 + if (!loginMemberQueue.length && !logoutMemberQueue.length) { + clearMemberQueueTimer(); + } + } + + function clearMemberQueueTimer() { + clearInterval(memberQueueTimer.value); + memberQueueTimer.value = undefined; + } + + function initMemberQueueTimer() { + memberQueueTimer.value = setInterval(memberQueueHandler, 200); + } + + /** 处理用户登录 */ + function onUserLogin(event: ChatEventsRelations[ChatEvents.ChatUserLogin]) { + const user = event.userLoginMsg.user; + if (!user) return; + + const member = convertUser2Member(user); + loginMemberQueue.push(member); + + // 新登入观众,存在于待移除的logout观众队列中,移除该队列对应数据。 + if (logoutMemberQueue.length) { + const index = logoutMemberQueue.findIndex(item => item.userId === member.userId); + logoutMemberQueue.splice(index, 1); + } + if (!memberQueueTimer.value) { + initMemberQueueTimer(); + } + } + + /** 处理用户登出 */ + function onUserLogout(event: ChatEventsRelations[ChatEvents.ChatUserLogout]) { + const userId = event.userLogoutMsg.userId; + const member = memberIdMap[userId]; + if (!member) return; + + logoutMemberQueue.push(member); + + // 如果登出的观众存在于待显示的观众队列中,则先移除该队列对应数据。 + if (loginMemberQueue.length) { + const index = loginMemberQueue.findIndex(item => item.userId === member.userId); + loginMemberQueue.splice(index, 1); + } + if (!memberQueueTimer.value) { + initMemberQueueTimer(); + } + } + + /** 处理用户修改名称 */ + function onSetNick({ userId, nickname }: UserEventsRelations[UserEvents.UserSetNick]) { + let targetMember = memberIdMap[userId]; + + if (targetMember) { + targetMember.nick = nickname; + } else { + // 还在待显示队列中 + const targetQueueUserIndex = loginMemberQueue.findIndex(item => item.userId === userId); + if (targetQueueUserIndex > -1) { + targetMember = { + ...loginMemberQueue[targetQueueUserIndex], + nick: nickname, + }; + } + } + } + + /** 重置成员队列 */ + function resetMemberQueue() { + loginMemberQueue = []; + logoutMemberQueue = []; + clearMemberQueueTimer(); + } + + return { + isNoMore, + memberGroupTypes, + memberGroupMap, + + onUserLogin, + onUserLogout, + onSetNick, + initMemberQueueTimer, + getNextUserList, + resetMemberQueue, + }; +}; diff --git a/src/components/page-watch-common/micro-activity/hooks/use-micro-activity.ts b/src/components/page-watch-common/micro-activity/hooks/use-micro-activity.ts new file mode 100644 index 0000000000000000000000000000000000000000..09e93d3457f2852e351a19aab00ac3f10848a75e --- /dev/null +++ b/src/components/page-watch-common/micro-activity/hooks/use-micro-activity.ts @@ -0,0 +1,127 @@ +import { onMounted, onBeforeUnmount, ref, unref } from 'vue'; +import { + HxcGeneralIframeEventData, + HxcIframeEventData, + HxcRequestDevicePermissionIframeEventData, + LoggerName, + MicroActivityIframeEventType, +} from '@polyv/live-watch-sdk'; + +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { useViewerStore } from '@/store/use-viewer-store'; +import { MicroActivityDeviceMotion } from './window-device-motion'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; + +type IframeMessageHandleCb = (data: T) => void; + +interface MicroActivityHookOption { + /** 全屏消息事件钩子 */ + fullScreenMessageHandler?: IframeMessageHandleCb; + /** 返回首页事件钩子 */ + backHomeMessageHandler?: IframeMessageHandleCb; + /** 不支持 PC 端访问事件钩子 */ + onlyMobileMessageHandler?: IframeMessageHandleCb; + /** 强制全屏事件钩子 */ + forceFullScreenMessageHandler?: IframeMessageHandleCb; +} + +export const useMicroActivityHook = (hookOptions: MicroActivityHookOption) => { + const { + fullScreenMessageHandler = () => {}, + backHomeMessageHandler = () => {}, + onlyMobileMessageHandler = () => {}, + forceFullScreenMessageHandler = () => {}, + } = hookOptions; + + const { nickname } = storeDefinitionToRefs(useViewerStore); + const watchCore = getWatchCore(); + + /** iframe 主体 */ + const microActivityIframeRef = ref(null); + + /** iframe 链接 */ + const iframeUrl = ref(''); + + /** 缓存 nickname */ + let cacheNickName = ''; + + /** 请求设备权限钩子 */ + function requestDevicePermissionHandler( + iframeEventData: HxcRequestDevicePermissionIframeEventData, + ) { + if (!MicroActivityDeviceMotion.$iframe) { + const $iframe = unref(microActivityIframeRef); + MicroActivityDeviceMotion.setIframe($iframe); + } + + const enable = iframeEventData.data.enable === true; + if (enable) { + MicroActivityDeviceMotion.bindDeviceMotionEvent(); + } else { + MicroActivityDeviceMotion.unbindDeviceMotionEvent(); + } + } + + /** + * iframe 消息监听钩子 + */ + function handleIframeMessage(event: MessageEvent) { + if (!/game.polyv/.test(event.origin)) return; + + const watchCore = getWatchCore(); + watchCore.logger.info(LoggerName.MicroActivity, 'iframe-message', JSON.stringify(event.data)); + + const eventData: HxcIframeEventData = event.data; + const eventType = eventData.type; + + switch (eventType) { + case MicroActivityIframeEventType.HXC_FULL_SCREEN: + fullScreenMessageHandler(eventData); + break; + case MicroActivityIframeEventType.HXC_BACK_HOME: + backHomeMessageHandler(eventData); + break; + case MicroActivityIframeEventType.HXC_ONLY_MOBILE: + onlyMobileMessageHandler(eventData); + break; + case MicroActivityIframeEventType.HXC_FORCE_FULL_SCREEN: + forceFullScreenMessageHandler(eventData); + break; + case MicroActivityIframeEventType.HXC_REQUEST_DEVICE_PERMISSION: + requestDevicePermissionHandler(eventData); + break; + default: + break; + } + } + + /** 更新 iframe 链接 */ + async function updateIframeUrl() { + try { + // 当昵称不同时,需要重新获取 iframe 链接 + if (cacheNickName !== nickname.value || !iframeUrl.value) { + const url = await watchCore.microActivity.getMicroActivityUrl(); + iframeUrl.value = url; + cacheNickName = nickname.value; + } + } catch (error) { + toast.error((error as Error).message); + } + } + + onMounted(() => { + window.addEventListener('message', handleIframeMessage, false); + }); + + onBeforeUnmount(() => { + window.removeEventListener('message', handleIframeMessage, false); + }); + + return { + microActivityIframeRef, + iframeUrl, + + updateIframeUrl, + }; +}; diff --git a/src/components/page-watch-common/micro-activity/hooks/window-device-motion.ts b/src/components/page-watch-common/micro-activity/hooks/window-device-motion.ts new file mode 100644 index 0000000000000000000000000000000000000000..2a1165ba93c1372f14dd44cd15e32163f27cb768 --- /dev/null +++ b/src/components/page-watch-common/micro-activity/hooks/window-device-motion.ts @@ -0,0 +1,87 @@ +/* eslint-disable promise/prefer-await-to-then */ +import { + MicroActivityIframeEventType, + PlvDevicemotionSwitchEventMessage, +} from '@polyv/live-watch-sdk'; + +type DeviceMotionEventRequestPermission = () => Promise; + +declare global { + interface Window { + DeviceMotionEvent: { + requestPermission: DeviceMotionEventRequestPermission | undefined; + }; + } +} + +/** + * @class 微活动 iframe 所需设备陀螺仪事件/数据处理逻辑 + */ +export class MicroActivityDeviceMotion { + static $iframe: HTMLIFrameElement | null = null; + + static hasGranted = false; + + static setIframe($iframe: HTMLIFrameElement | null) { + MicroActivityDeviceMotion.$iframe = $iframe; + } + + static bindDeviceMotionEvent() { + if (typeof window.DeviceMotionEvent !== 'function') { + alert('您的手机不支持摇一摇功能~'); + return; + } + + if (typeof window.DeviceMotionEvent.requestPermission === 'function') { + // 该方法在 ios13 及以上才能使用 + window.DeviceMotionEvent.requestPermission() + .then(permissionState => { + if (permissionState === 'granted') { + this.setDeviceMotion(); + } else { + alert('很遗憾,您已拒绝启用摇一摇!'); + } + }) + .catch(error => { + alert(error); + }); + } else { + this.setDeviceMotion(); + } + } + + static setDeviceMotion() { + this.hasGranted = true; + window.addEventListener('devicemotion', this.handleDevicemotionMessage, false); + } + + static unbindDeviceMotionEvent() { + if (this.hasGranted) { + window.removeEventListener('devicemotion', this.handleDevicemotionMessage, false); + this.hasGranted = false; + } + } + + static handleDevicemotionMessage(e: DeviceMotionEvent) { + console.log(e); + const $iframe = MicroActivityDeviceMotion.$iframe; + $iframe && + $iframe.contentWindow && + $iframe.contentWindow.postMessage( + { + type: MicroActivityIframeEventType.PLV_DEVICEMOTION_SWITCH, + data: + e && e.accelerationIncludingGravity + ? { + accelerationIncludingGravity: { + x: e.accelerationIncludingGravity.x, + y: e.accelerationIncludingGravity.y, + z: e.accelerationIncludingGravity.z, + }, + } + : null, + } as PlvDevicemotionSwitchEventMessage, + '*', + ); + } +} diff --git a/src/components/page-watch-common/micro-activity/mobile-micro-activity/mobile-micro-activity.vue b/src/components/page-watch-common/micro-activity/mobile-micro-activity/mobile-micro-activity.vue new file mode 100644 index 0000000000000000000000000000000000000000..83acb32473932ae94e669a68172c9e5ff7c8c831 --- /dev/null +++ b/src/components/page-watch-common/micro-activity/mobile-micro-activity/mobile-micro-activity.vue @@ -0,0 +1,170 @@ + + + + + + diff --git a/src/components/page-watch-common/micro-activity/pc-micro-activity/imgs/icon_close.png b/src/components/page-watch-common/micro-activity/pc-micro-activity/imgs/icon_close.png new file mode 100644 index 0000000000000000000000000000000000000000..8c03227f8e3fb819459d37129e783d76deab34c9 Binary files /dev/null and b/src/components/page-watch-common/micro-activity/pc-micro-activity/imgs/icon_close.png differ diff --git a/src/components/page-watch-common/micro-activity/pc-micro-activity/pc-micro-activity.vue b/src/components/page-watch-common/micro-activity/pc-micro-activity/pc-micro-activity.vue new file mode 100644 index 0000000000000000000000000000000000000000..bf7b0a7a2f721d5c545ba6106291000404ce82df --- /dev/null +++ b/src/components/page-watch-common/micro-activity/pc-micro-activity/pc-micro-activity.vue @@ -0,0 +1,238 @@ + + + + + + diff --git a/src/components/page-watch-common/micro-activity/portrait-micro-activity/portrait-micro-activity-pendant.vue b/src/components/page-watch-common/micro-activity/portrait-micro-activity/portrait-micro-activity-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..1e6371ad09c6a94c26364bc84d6609b354e8669b --- /dev/null +++ b/src/components/page-watch-common/micro-activity/portrait-micro-activity/portrait-micro-activity-pendant.vue @@ -0,0 +1,40 @@ + + + + + + diff --git a/src/components/page-watch-common/micro-activity/portrait-micro-activity/portrait-micro-activity.vue b/src/components/page-watch-common/micro-activity/portrait-micro-activity/portrait-micro-activity.vue new file mode 100644 index 0000000000000000000000000000000000000000..b574566710d290e24b6a47258f498aa570d657d6 --- /dev/null +++ b/src/components/page-watch-common/micro-activity/portrait-micro-activity/portrait-micro-activity.vue @@ -0,0 +1,99 @@ + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/auto-textarea/mobile-auto-textarea.vue b/src/components/page-watch-common/msg-input-wrap/auto-textarea/mobile-auto-textarea.vue new file mode 100644 index 0000000000000000000000000000000000000000..3cb2ceb3952ff931db056d7eb6f37aa94b43b6a5 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/auto-textarea/mobile-auto-textarea.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/auto-textarea/pc-auto-textarea.vue b/src/components/page-watch-common/msg-input-wrap/auto-textarea/pc-auto-textarea.vue new file mode 100644 index 0000000000000000000000000000000000000000..3303014a3203092f32dbc53c86f5a9274678166a --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/auto-textarea/pc-auto-textarea.vue @@ -0,0 +1,101 @@ + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/auto-textarea/use-auto-textarea.ts b/src/components/page-watch-common/msg-input-wrap/auto-textarea/use-auto-textarea.ts new file mode 100644 index 0000000000000000000000000000000000000000..e5a34b3e711e3084bbc6fca15816a691c148736b --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/auto-textarea/use-auto-textarea.ts @@ -0,0 +1,245 @@ +import { isDesktopIPad } from '@/assets/utils/browser'; +import { emitFunc, updateModelEmit, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, useProps, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { PlvInputContent } from '@/plugins/polyv-ui/types'; +import { ref, computed, unref, nextTick, onMounted, onBeforeUnmount, watch } from 'vue'; + +export const autoTextareaProps = () => ({ + /** 绑定值 */ + value: PropUtils.string.def(''), + /** 空占位文本 */ + placeholder: PropUtils.string, + /** 最大长度 */ + maxlength: PropUtils.number.def(200), + /** 行高,默认:1.6 */ + lineHeight: PropUtils.number.def(1.6), + /** 字体大小,默认:14 */ + fontSize: PropUtils.number.def(14), + /** 是否禁用 */ + disabled: PropUtils.bool.def(false), + /** 是否文字溢出,.sync */ + isOverflow: PropUtils.bool.def(false), + /** 背景 */ + background: PropUtils.string, +}); + +export const autoTextareaEmits = () => ({ + ...updateModelEmit<'isOverflow', boolean>('isOverflow'), + /** 绑定值更改 */ + input: emitFunc(), + /** 失焦事件 */ + blur: emitFunc(), + /** 聚焦事件 */ + focus: emitFunc(), + /** 回车事件 */ + enter: emitFunc(), + /** 提交事件 */ + submit: emitFunc(), +}); + +export interface AutoTextareaInstance { + /** 失焦输入框 */ + blurTextarea(): void; + /** 聚焦输入框 */ + focusTextarea(): void; + /** 删除一个字符 */ + deleteWord(): void; + /** 清除输入框内容 */ + clearTextarea(): void; + /** 获取内容列表 */ + getContentList(): PlvInputContent[]; +} + +export const useAutoTextarea = (options: { + props: VueProps; + emit: VueEmit; +}) => { + const { props, emit } = options; + const { value, maxlength, lineHeight, fontSize, placeholder } = useProps(props); + + const textareaRef = ref(); + + /** 是否聚焦中 */ + const isFocus = ref(false); + + /** 当前高度 */ + const clientHeight = ref(0); + + const textareaPlaceholder = computed(() => { + if (unref(isFocus)) { + return ''; + } + + return unref(placeholder); + }); + + /** 输入框最大高度 */ + const textareaMaxHeight = computed(() => { + const fontSizeVal = unref(fontSize); + const lineHeightVal = unref(lineHeight); + // 三行文字+两行间距的高度 + return fontSizeVal + fontSizeVal * lineHeightVal * 2; + }); + + /** 输入框真实 value */ + const realValue = computed(() => { + let valueText = unref(value) || ''; + if (unref(maxlength)) { + valueText = valueText.slice(0, unref(maxlength)); + } + return valueText; + }); + + /** + * 重置高度 + */ + async function resetHeight() { + const textareaElem = unref(textareaRef); + if (!textareaElem) return; + + textareaElem.style.height = 'auto'; + await nextTick(); + if (textareaElem.scrollHeight) { + textareaElem.style.height = `${textareaElem.scrollHeight}px`; + } + clientHeight.value = textareaElem.clientHeight; + + if (textareaElem.scrollHeight > textareaElem.clientHeight) { + emit('update:isOverflow', true); + } else { + emit('update:isOverflow', false); + } + } + + watch( + () => unref(value), + () => resetHeight(), + ); + + function onBlur() { + isFocus.value = false; + emit('blur'); + } + + function onFocus() { + isFocus.value = true; + emit('focus'); + } + + function onInput() { + if (props.disabled) { + return; + } + const textareaElem = unref(textareaRef); + if (!textareaElem) return; + const newValue = textareaElem.value; + emit('input', newValue); + } + + function onEnter() { + emit('enter'); + } + + function onSubmit() { + emit('submit', props.value); + } + + /** + * 失焦输入框 + */ + function blurTextarea() { + unref(textareaRef)?.blur(); + } + + /** + * 聚焦输入框 + */ + function focusTextarea() { + unref(textareaRef)?.focus(); + } + + /** 清除输入框内容 */ + function clearTextarea() { + emit('input', ''); + } + + /** + * 删除一个字符 + */ + function deleteWord() { + const length = unref(props.value).length; + const newValue = props.value.substr(0, length - 1); + emit('input', newValue); + } + + /** + * 获取内容列表 + */ + function getContentList(): PlvInputContent[] { + const textareaElem = unref(textareaRef); + if (!textareaElem) return []; + + const newValue = textareaElem.value; + return [ + { + type: 'text', + content: newValue, + }, + ]; + } + + function onOrientationChange() { + blurTextarea(); + } + + onMounted(() => { + resetHeight(); + + if (isDesktopIPad) { + // 兼容ipad横竖屏切换,系统键盘遮挡观看页输入框的问题。 + // 这里监听屏幕方向改变的事件,用于系统横屏时使播放器满屏显示。 + window.addEventListener( + 'onorientationchange' in window ? 'orientationchange' : 'resize', + onOrientationChange, + false, + ); + } + }); + + onBeforeUnmount(() => { + if (isDesktopIPad) { + window.removeEventListener( + 'onorientationchange' in window ? 'orientationchange' : 'resize', + onOrientationChange, + false, + ); + } + }); + + const textareaInstance: AutoTextareaInstance = { + blurTextarea, + focusTextarea, + deleteWord, + clearTextarea, + getContentList, + }; + + return { + textareaPlaceholder, + textareaRef, + textareaMaxHeight, + realValue, + clientHeight, + onBlur, + onFocus, + onInput, + onEnter, + onSubmit, + blurTextarea, + focusTextarea, + deleteWord, + clearTextarea, + getContentList, + textareaInstance, + }; +}; diff --git a/src/components/page-watch-common/msg-input-wrap/mobile-msg-input-wrap/mobile-msg-input-wrap.vue b/src/components/page-watch-common/msg-input-wrap/mobile-msg-input-wrap/mobile-msg-input-wrap.vue new file mode 100644 index 0000000000000000000000000000000000000000..8d656783aa7073c1436990cd62d6e2ac27093c84 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/mobile-msg-input-wrap/mobile-msg-input-wrap.vue @@ -0,0 +1,285 @@ + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/mobile-msg-input-wrap/var.scss b/src/components/page-watch-common/msg-input-wrap/mobile-msg-input-wrap/var.scss new file mode 100644 index 0000000000000000000000000000000000000000..d7c3837979692d2ef91de4e7637c18299b8212da --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/mobile-msg-input-wrap/var.scss @@ -0,0 +1 @@ +$--mobile-msg-input-wrap-base-height: 40px; diff --git a/src/components/page-watch-common/msg-input-wrap/pc-msg-input-wrap/pc-msg-input-wrap.vue b/src/components/page-watch-common/msg-input-wrap/pc-msg-input-wrap/pc-msg-input-wrap.vue new file mode 100644 index 0000000000000000000000000000000000000000..6ae16ae79a8524b7d0c2ea1670f8bbc17dc2bd16 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/pc-msg-input-wrap/pc-msg-input-wrap.vue @@ -0,0 +1,360 @@ + + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/pc-msg-input-wrap/pc-set-nick-placeholder.vue b/src/components/page-watch-common/msg-input-wrap/pc-msg-input-wrap/pc-set-nick-placeholder.vue new file mode 100644 index 0000000000000000000000000000000000000000..a80eb9ca56ceed759e27c71ade67a51255a6d7d6 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/pc-msg-input-wrap/pc-set-nick-placeholder.vue @@ -0,0 +1,47 @@ + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/portrait-msg-input-wrap/portrait-msg-input-wrap.vue b/src/components/page-watch-common/msg-input-wrap/portrait-msg-input-wrap/portrait-msg-input-wrap.vue new file mode 100644 index 0000000000000000000000000000000000000000..73551034183425324a46b35661677b25fae34d83 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/portrait-msg-input-wrap/portrait-msg-input-wrap.vue @@ -0,0 +1,156 @@ + + + + + + diff --git a/src/components/page-watch-common/msg-input-wrap/use-msg-input-controller.ts b/src/components/page-watch-common/msg-input-wrap/use-msg-input-controller.ts new file mode 100644 index 0000000000000000000000000000000000000000..867a005f7ac1ada52168977a7e065ad7acb5f777 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/use-msg-input-controller.ts @@ -0,0 +1,222 @@ +import { computed, ref, unref } from 'vue'; +import { AutoTextareaInstance } from './auto-textarea/use-auto-textarea'; +import { + PlvInputContent, + PlvInputContentText, + PlvInputImagePasteInstance, +} from '@/plugins/polyv-ui/types'; +import { eventBus, appEvents } from '@/app/app-events'; + +/** + * @hook 对消息输入框的封装,统一提供给到组件外部使用的的方法 + * @desc 内部优先级: imagePasteInputRef > autoTextareaRef > inputElemRef + */ +export const useMsgInputController = () => { + /** 图片粘贴输入框组件实例 */ + const imagePasteInputRef = ref(); + + /** 文本输入框组件实例 */ + const autoTextareaRef = ref(); + /** 文本输入框组件绑定值 */ + const autoTextareaInputValue = ref(''); + + /** HTML 输入框 */ + const inputElemRef = ref(); + /** HTML 输入框绑定值 */ + const inputElmValue = ref(''); + + /** 当前输入框是否聚焦 */ + const isInputFocus = ref(false); + + function onInputFocus() { + isInputFocus.value = true; + eventBus.$emit(appEvents.global.MsgInputFocus); + } + + function onInputBlur() { + /** + * 这里延迟 1 毫秒的原因是如果在输入框没有输入内容时,点击表情, + * 输入框因为长度缩短导致触点无法达到表情入口处,进而无法打开表情 + */ + setTimeout(() => { + isInputFocus.value = false; + }, 1); + } + + /** 聚焦输入框 */ + function focusInput() { + if (imagePasteInputRef.value) { + imagePasteInputRef.value.focus(); + return; + } + + if (autoTextareaRef.value) { + autoTextareaRef.value.focusTextarea(); + return; + } + + if (inputElemRef.value) { + inputElemRef.value.focus(); + } + } + + /** 当前图片粘贴输入框文本长度 */ + const curImagePasteInputTextLength = ref(0); + + /** 最多支持输入的文本长度 */ + const maxInputTextLength = 200; + + /** 当前输入文本长度 */ + const curInputTextLength = computed(() => { + if (imagePasteInputRef.value) { + return curImagePasteInputTextLength.value; + } + + if (autoTextareaRef.value) { + return unref(autoTextareaInputValue).length; + } + + if (inputElemRef.value) { + return unref(inputElmValue).length; + } + + return 0; + }); + + /** 图片粘贴输入框内容区域是否为空 */ + const isEmptyImagePasteInput = ref(true); + + /** 监听图片粘贴输入框输入变更 */ + function onImagePasteInputChange(contents: PlvInputContent[]) { + isEmptyImagePasteInput.value = contents.length === 0; + curImagePasteInputTextLength.value = ( + contents.filter(i => i.type === 'text') as PlvInputContentText[] + ).reduce((accu, cur) => { + if (!accu) { + accu = cur.content.length; + } else { + accu += cur.content.length; + } + return accu; + }, 0); + } + + /** 输入框内容是否为空 */ + const isEmptyInputContent = computed(() => { + if (imagePasteInputRef.value) { + return unref(isEmptyImagePasteInput); + } + + if (autoTextareaRef.value) { + return !unref(autoTextareaInputValue); + } + + if (inputElemRef.value) { + return !unref(inputElmValue); + } + + return true; + }); + + /** 删除单个字符 */ + function deleteWord() { + if (imagePasteInputRef.value) { + console.warn('暂不支持 deleteWord'); + return; + } + + if (autoTextareaRef.value) { + autoTextareaRef.value.deleteWord(); + return; + } + + if (unref(inputElemRef)) { + const length = unref(inputElmValue.value).length; + const newValue = unref(inputElmValue).substr(0, length - 1); + inputElmValue.value = newValue; + } + } + + /** 清除输入框内容 */ + function clearInput() { + if (imagePasteInputRef.value) { + imagePasteInputRef.value.clear(); + return; + } + + if (autoTextareaRef.value) { + autoTextareaInputValue.value = ''; + return; + } + + if (unref(inputElemRef)) { + inputElmValue.value = ''; + } + } + + /** 插入表情 */ + function insertEmotion(title: string) { + if (imagePasteInputRef.value) { + // 插入后会自动聚焦 + imagePasteInputRef.value.insertEmotion(title); + return; + } + + if (autoTextareaRef.value) { + const newValue = (unref(autoTextareaInputValue) + title).slice(0, maxInputTextLength); + autoTextareaInputValue.value = newValue; + return; + } + + if (inputElemRef.value) { + const newValue = (unref(inputElmValue) + title).slice(0, maxInputTextLength); + inputElmValue.value = newValue; + } + } + + /** 获取输入框内容 */ + function getInputContentList(): PlvInputContent[] { + if (imagePasteInputRef.value) { + return imagePasteInputRef.value.getContentList(); + } + + if (autoTextareaRef.value) { + return autoTextareaRef.value.getContentList(); + } + + if (inputElemRef.value) { + return [ + { + type: 'text', + content: inputElmValue.value, + }, + ]; + } + + return []; + } + + return { + imagePasteInputRef, + autoTextareaRef, + inputElemRef, + + autoTextareaInputValue, + inputElmValue, + + isInputFocus, + focusInput, + onInputFocus, + onInputBlur, + + curInputTextLength, + maxInputTextLength, + isEmptyInputContent, + onImagePasteInputChange, + + clearInput, + deleteWord, + insertEmotion, + getInputContentList, + }; +}; diff --git a/src/components/page-watch-common/msg-input-wrap/use-msg-input-wrap.ts b/src/components/page-watch-common/msg-input-wrap/use-msg-input-wrap.ts new file mode 100644 index 0000000000000000000000000000000000000000..4916c5a5ee69666fad4cfa0549f2c6110a46ded7 --- /dev/null +++ b/src/components/page-watch-common/msg-input-wrap/use-msg-input-wrap.ts @@ -0,0 +1,177 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { usePanelVisible } from '@/hooks/behaviors/use-panel-visible'; +import { useViewerStore } from '@/store/use-viewer-store'; +import { ref, unref, watch } from 'vue'; +import { PlvInputContent } from '@/plugins/polyv-ui/types'; +import { useMsgInputController } from './use-msg-input-controller'; + +export const msgInputWrapProps = () => ({ + /** 占位符 */ + placeholder: PropUtils.string, + /** 是否禁用输入框 */ + disabledInput: PropUtils.bool.def(false), + /** 是否禁用表情选择 */ + disabledEmotionPicker: PropUtils.bool.def(false), + /** 是否显示图片表情发送 */ + imageEmotionEnabled: PropUtils.bool.def(true), + /** 是否显示图片发送(仅 PC) */ + sendImageEnabled: PropUtils.bool.def(true), + /** 是否禁用图片发送(仅 PC) */ + disabledSendImage: PropUtils.bool.def(false), + /** 是否使用图片粘贴输入框(仅 PC) */ + imagePasteInputEnabled: PropUtils.bool.def(false), +}); + +export const msgInputWrapEmits = () => ({ + input: emitFunc(), + /** 提交事件 */ + submit: emitFunc<{ contentList: PlvInputContent[] }>(), + /** 点击发送图片 */ + 'click-send-image': emitFunc(), + /** 关闭 */ + 'click-image-emotion': emitFunc(), +}); + +export interface MsgInputWrapInstance { + /** 聚焦输入框 */ + focusInput(): void; + /** 清除输入框 */ + clearInput(): void; + /** 获取输入内容 */ + getInputContentList(): PlvInputContent[]; + /** 重置状态 */ + resetStatus(): void; +} + +/** + * @hook 消息输入框包装层 + */ +export const useMsgInputWrap = (options: { + /** 点击表情后是否聚焦输入框,默认:true */ + clickFaceToFocus?: boolean; + props: VueProps; + emit: VueEmit; +}) => { + const viewerStore = useViewerStore(); + /** 消息输入框控制器 */ + const msgInputController = useMsgInputController(); + + const { props, emit, clickFaceToFocus = false } = options; + + // 监听是否禁用输入框 + watch( + () => props.disabledInput, + () => { + if (props.disabledInput) { + msgInputController.clearInput(); + } + }, + ); + + /** 是否显示字数统计 */ + const wordLimitVisible = ref(false); + + /** 监听输入框溢出事件 */ + function onInputWidgetOverflow(isOverflow: boolean) { + wordLimitVisible.value = isOverflow; + } + + /** 表情面板节点 */ + const emotionRef = ref(); + + /** 表情面板显示 */ + const { + visible: emotionVisible, + togglePanel: toggleEmotionPanel, + hidePanel: hideEmotionPanel, + } = usePanelVisible([emotionRef, '[data-emotion-picker-btn']); + + /** 处理点击表情面板开关 */ + function onClickEmotionPickerBtn() { + if (props.disabledEmotionPicker) { + return; + } + if (!viewerStore.hasNickname) { + eventBus.$emit(appEvents.chat.OpenSetNick, true); + return; + } + toggleEmotionPanel(); + } + + /** 处理点击 face 表情 */ + function onClickFace(title: string) { + msgInputController.insertEmotion(title); + + if (clickFaceToFocus) { + msgInputController.focusInput(); + } + } + + /** 处理点击图片表情 */ + function onClickImageEmotion() { + emit('click-image-emotion'); + } + + /** 处理点击图片发送 */ + function onClickSendImage() { + if (props.disabledSendImage) { + return; + } + if (!viewerStore.hasNickname) { + eventBus.$emit(appEvents.chat.OpenSetNick, true); + return; + } + emit('click-send-image'); + } + + /** 发送按钮是否禁用 */ + const sendBtnDisabled = msgInputController.isEmptyInputContent; + + /** + * 输入框触发提交/发送按钮点击处理 + * @desc 提交后,由外部自行去重置输入框内容,清空组件状态 + * */ + function submitInput() { + if (unref(sendBtnDisabled)) return; + + emit('submit', { + contentList: msgInputController.getInputContentList(), + }); + } + + /** 重置组件状态 */ + function resetStatus() { + hideEmotionPanel(); + } + + /** 消息包装层组件实例 */ + const msgInputWrapInstance: MsgInputWrapInstance = { + focusInput: msgInputController.focusInput, + clearInput: msgInputController.clearInput, + getInputContentList: msgInputController.getInputContentList, + resetStatus, + }; + + return { + ...msgInputController, + msgInputWrapInstance, + + wordLimitVisible, + onInputWidgetOverflow, + + emotionRef, + emotionVisible, + toggleEmotionPanel, + hideEmotionPanel, + onClickEmotionPickerBtn, + onClickFace, + onClickImageEmotion, + + onClickSendImage, + + sendBtnDisabled, + submitInput, + }; +}; diff --git a/src/components/page-watch-common/multi-meeting/imgs/playing.gif b/src/components/page-watch-common/multi-meeting/imgs/playing.gif new file mode 100644 index 0000000000000000000000000000000000000000..fad2d0fa0f779bf15fdea2f659a69602ad59bd79 Binary files /dev/null and b/src/components/page-watch-common/multi-meeting/imgs/playing.gif differ diff --git a/src/components/page-watch-common/multi-meeting/mobile-multi-meeting.vue b/src/components/page-watch-common/multi-meeting/mobile-multi-meeting.vue new file mode 100644 index 0000000000000000000000000000000000000000..511bdaa8fa892ab9f327c3ad1326699980b1d097 --- /dev/null +++ b/src/components/page-watch-common/multi-meeting/mobile-multi-meeting.vue @@ -0,0 +1,211 @@ + + + + + + diff --git a/src/components/page-watch-common/multi-meeting/pc-multi-meeting.vue b/src/components/page-watch-common/multi-meeting/pc-multi-meeting.vue new file mode 100644 index 0000000000000000000000000000000000000000..a828e711ae2b4ffa4c50c3fa844da857aea12d08 --- /dev/null +++ b/src/components/page-watch-common/multi-meeting/pc-multi-meeting.vue @@ -0,0 +1,242 @@ + + + + + + diff --git a/src/components/page-watch-common/multi-meeting/use-multi-meeting.ts b/src/components/page-watch-common/multi-meeting/use-multi-meeting.ts new file mode 100644 index 0000000000000000000000000000000000000000..4db4a87967740bceeaa32a61bfd2c91d525ebc19 --- /dev/null +++ b/src/components/page-watch-common/multi-meeting/use-multi-meeting.ts @@ -0,0 +1,212 @@ +import { DEFAULT_SPLASH_IMG } from '@/assets/constants/defaults'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { + LiveStatus, + ChannelMultiMeetingItem, + MultiMeetingLiveStatusData, + YN, +} from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { computed, ComputedRef, onBeforeUnmount, ref, unref, watch } from 'vue'; + +export interface MultiMeetingData extends Required { + /** 是否为当前频道 */ + isCurrent: boolean; + /** 直播状态 */ + liveStatus: LiveStatus; + /** 显示图标 */ + showIcon: boolean; + /** 跳转地址 */ + watchUrl: string; +} + +export const useMultiMeeting = () => { + const channelStore = useChannelStore(); + + /** 分会场开关 */ + const multiMeetingEnabled = computed(() => + ynToBool(channelStore.channelDetail?.multiMeetingSetting?.multiMeetingEnabled, YN.N), + ); + + /** 分会场频道状态 */ + const multiMeetingLiveStatus = ref([]); + + /** 根据频道号获取频道的直播状态 */ + function findLiveStatus(channelId: string): LiveStatus { + const statusList = unref(multiMeetingLiveStatus); + const index = statusList.findIndex(item => item.channelId === channelId); + return index === -1 ? LiveStatus.End : statusList[index].liveStatus; + } + + /** 分会场列表 */ + const multiMeetingList = computed(() => { + const watchCore = getWatchCore(); + const multiMeetings = channelStore.channelDetail?.multiMeetingSetting?.multiMeetings || []; + + return multiMeetings.map(item => { + const splashImg = item.splashImg || DEFAULT_SPLASH_IMG; + const meetingChannelId = item.channelId.toString(); + const liveStatus = findLiveStatus(meetingChannelId); + const isCurrent = meetingChannelId === channelStore.channelId; + const showIcon = isCurrent && [LiveStatus.Live, LiveStatus.Playback].includes(liveStatus); + const watchUrl = watchCore.multiMeeting.generateMultiMeetingWatchUrl(meetingChannelId); + + return { + ...item, + splashImg, + isCurrent, + liveStatus, + showIcon, + watchUrl, + }; + }); + }); + + /** 会场总数 */ + const multiMeetingTotal = computed(() => unref(multiMeetingList).length); + + /** 当前频道位置 */ + const currentMultiMeetingSeat = computed(() => { + const index = unref(multiMeetingList).findIndex(item => item.isCurrent); + + return index + 1; + }); + + /** + * 获取分会场的直播状态 + */ + async function getMultiMeetingLiveStatus(): Promise { + const watchCore = getWatchCore(); + const data = await watchCore.multiMeeting.getMultiMeetingLiveStatus(); + multiMeetingLiveStatus.value = data; + } + + let pollingTimer: number | undefined; + /** 开始直播状态轮询 */ + function startLiveStatusPolling(): void { + closeLiveStatusPolling(); + + pollingTimer = window.setInterval(() => getMultiMeetingLiveStatus(), 60 * 1000); + getMultiMeetingLiveStatus(); + } + + /** 关闭直播状态轮询 */ + function closeLiveStatusPolling(): void { + if (pollingTimer) { + window.clearInterval(pollingTimer); + pollingTimer = undefined; + } + } + + watch( + () => unref(multiMeetingTotal), + () => { + if (unref(multiMeetingTotal)) { + startLiveStatusPolling(); + } else { + closeLiveStatusPolling(); + } + }, + { + immediate: true, + }, + ); + + onBeforeUnmount(() => { + closeLiveStatusPolling(); + }); + + return { + multiMeetingEnabled, + multiMeetingList, + multiMeetingTotal, + currentMultiMeetingSeat, + }; +}; + +/** + * 分会场分页 hook + */ +export const useMultiMeetingPage = (options: { + pageSize?: number; + multiMeetingList: ComputedRef; + currentMultiMeetingSeat: ComputedRef; +}) => { + const { pageSize = 5, multiMeetingList, currentMultiMeetingSeat } = options; + + const wrapLeftPercent = ref(0); + + /** 列表外层百分比 */ + const wrapWidthPercent = computed(() => { + const total = unref(multiMeetingList).length; + return Math.ceil(total / pageSize) * 100; + }); + + const wrapStyle = computed(() => { + return { + width: `${unref(wrapWidthPercent)}%`, + left: `-${unref(wrapLeftPercent)}%`, + }; + }); + + const itemWidPercent = computed(() => { + const total = unref(multiMeetingList).length; + return 100 / (pageSize * Math.ceil(total / pageSize)); + }); + + const itemStyle = computed(() => { + return { + width: `${unref(itemWidPercent)}%`, + }; + }); + + /** 是否禁用上一页按钮 */ + const disabledPrev = ref(false); + /** 是否禁用下一个按钮 */ + const disabledNext = ref(false); + + function handlePageChange(toNextGroup = true): void { + if (toNextGroup) { + wrapLeftPercent.value += 100; + } else { + wrapLeftPercent.value -= 100; + } + checkDisabled(); + } + + function checkDisabled() { + disabledPrev.value = unref(wrapLeftPercent) <= 0; + disabledNext.value = unref(wrapLeftPercent) >= unref(wrapWidthPercent) - 100; + } + + watch( + () => unref(currentMultiMeetingSeat), + () => { + // 保持当前会场信息在会场列表中可见 + + checkDisabled(); + const currentSeat = unref(currentMultiMeetingSeat) - 1; + if (currentSeat < pageSize) return; + + const count = Math.floor(currentSeat / pageSize); + for (let i = 0; i < count; i++) { + handlePageChange(true); + } + }, + { + immediate: true, + }, + ); + + return { + pageSize, + wrapWidthPercent, + wrapStyle, + itemWidPercent, + itemStyle, + disabledPrev, + disabledNext, + handlePageChange, + }; +}; diff --git a/src/components/page-watch-common/page-advert/mobile-page-advert.vue b/src/components/page-watch-common/page-advert/mobile-page-advert.vue new file mode 100644 index 0000000000000000000000000000000000000000..af0e47a1f90af4b6df267d6e094a27aeb02f51e7 --- /dev/null +++ b/src/components/page-watch-common/page-advert/mobile-page-advert.vue @@ -0,0 +1,142 @@ + + + + + + diff --git a/src/components/page-watch-common/page-advert/pc-page-advert.vue b/src/components/page-watch-common/page-advert/pc-page-advert.vue new file mode 100644 index 0000000000000000000000000000000000000000..f655212977123b0fc2e587d1e252aa53b1dda37b --- /dev/null +++ b/src/components/page-watch-common/page-advert/pc-page-advert.vue @@ -0,0 +1,150 @@ + + + + + + diff --git a/src/components/page-watch-common/page-advert/use-page-advert.ts b/src/components/page-watch-common/page-advert/use-page-advert.ts new file mode 100644 index 0000000000000000000000000000000000000000..b8413597c19b4f5059afdeed7174e1a74c159ea4 --- /dev/null +++ b/src/components/page-watch-common/page-advert/use-page-advert.ts @@ -0,0 +1,52 @@ +import { computed, onMounted, ref, unref } from 'vue'; +import { PageAdvertItem, YN } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { useSimpleVisible } from '@/hooks/behaviors/use-simple-visible'; + +export const usePageAdvert = () => { + const channelStore = useChannelStore(); + + /** 页面广告开关 */ + const pageAdvertEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelAdvert?.pageAdvertEnabled, YN.N), + ); + + /** 广告是否显示 */ + const { visible: advertVisible, close: closeAdvert } = useSimpleVisible(true); + + /** 页面广告列表 */ + const pageAdvertList = ref([]); + + /** 关闭按钮是否显示 */ + const closeVisible = computed(() => { + return ynToBool(channelStore.channelDetail?.channelAdvert?.closeAdvertEnabled, YN.N); + }); + + /** 处理点击关闭 */ + function onClickClose() { + closeAdvert(); + } + + /** 打开链接 */ + function openLink(href?: string) { + if (!href) return; + window.open(href, '_blank'); + } + + onMounted(() => { + if (unref(pageAdvertEnabled)) { + const watchCore = getWatchCore(); + pageAdvertList.value = watchCore.channel.getPageAdvertList(); + } + }); + + return { + advertVisible, + pageAdvertList, + closeVisible, + onClickClose, + openLink, + }; +}; diff --git a/src/components/page-watch-common/playback-list/hooks/use-playback-list.ts b/src/components/page-watch-common/playback-list/hooks/use-playback-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..af804cb11da0425cb2608fef1f0367ae73ec4445 --- /dev/null +++ b/src/components/page-watch-common/playback-list/hooks/use-playback-list.ts @@ -0,0 +1,23 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { PlaybackTarget } from '@polyv/live-watch-sdk'; + +export const usePlaybackList = () => { + const playbackStore = usePlaybackStore(); + + /** + * 处理点击回放节点 + */ + async function onClickPlaybackItem(target: PlaybackTarget) { + const watchCore = getWatchCore(); + playbackStore.$patch({ + currentPlaybackTarget: target, + }); + await watchCore.player.changePlayback(target.playbackOptions); + watchCore.player.play(); + } + + return { + onClickPlaybackItem, + }; +}; diff --git a/src/components/page-watch-common/playback-list/mobile-playback-list/mobile-playback-list.vue b/src/components/page-watch-common/playback-list/mobile-playback-list/mobile-playback-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..338939883b74604aacd557285248286ebaaf6e5c --- /dev/null +++ b/src/components/page-watch-common/playback-list/mobile-playback-list/mobile-playback-list.vue @@ -0,0 +1,214 @@ + + + + + + diff --git a/src/components/page-watch-common/playback-list/pc-playback-list/pc-playback-list.vue b/src/components/page-watch-common/playback-list/pc-playback-list/pc-playback-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..c763c4670e839b083115d151b7f455e04bee0c19 --- /dev/null +++ b/src/components/page-watch-common/playback-list/pc-playback-list/pc-playback-list.vue @@ -0,0 +1,223 @@ + + + + + + + diff --git a/src/components/page-watch-common/playback-list/portrait-playback-list/portrait-playback-list.vue b/src/components/page-watch-common/playback-list/portrait-playback-list/portrait-playback-list.vue new file mode 100644 index 0000000000000000000000000000000000000000..4dd0acc26d6ac1ffe9ca19d23519f598c18c1e02 --- /dev/null +++ b/src/components/page-watch-common/playback-list/portrait-playback-list/portrait-playback-list.vue @@ -0,0 +1,214 @@ + + + + + + diff --git a/src/components/page-watch-common/player/common/audio-panel/imgs/audio-1.png b/src/components/page-watch-common/player/common/audio-panel/imgs/audio-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c15f41792d82a2186c8e2c9812b9012a50994a04 Binary files /dev/null and b/src/components/page-watch-common/player/common/audio-panel/imgs/audio-1.png differ diff --git a/src/components/page-watch-common/player/common/audio-panel/imgs/audio-2.png b/src/components/page-watch-common/player/common/audio-panel/imgs/audio-2.png new file mode 100644 index 0000000000000000000000000000000000000000..2beb6276085ecbf49788a899ae755f944ab3a198 Binary files /dev/null and b/src/components/page-watch-common/player/common/audio-panel/imgs/audio-2.png differ diff --git a/src/components/page-watch-common/player/common/audio-panel/imgs/audio-3.png b/src/components/page-watch-common/player/common/audio-panel/imgs/audio-3.png new file mode 100644 index 0000000000000000000000000000000000000000..6571f63bff361bc33c79a6da3ccd336d7b2f67e3 Binary files /dev/null and b/src/components/page-watch-common/player/common/audio-panel/imgs/audio-3.png differ diff --git a/src/components/page-watch-common/player/common/audio-panel/portrait-audio-panel.vue b/src/components/page-watch-common/player/common/audio-panel/portrait-audio-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..145213e1212c20b5a9604a74ebb67d3902a92430 --- /dev/null +++ b/src/components/page-watch-common/player/common/audio-panel/portrait-audio-panel.vue @@ -0,0 +1,88 @@ + + + + + + diff --git a/src/components/page-watch-common/player/common/none-live-panel/imgs/portrait-end-placeholder.png b/src/components/page-watch-common/player/common/none-live-panel/imgs/portrait-end-placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..0fed3839082eeb066191a9ad2bb34405650b2906 Binary files /dev/null and b/src/components/page-watch-common/player/common/none-live-panel/imgs/portrait-end-placeholder.png differ diff --git a/src/components/page-watch-common/player/common/none-live-panel/portrait-none-live-panel.vue b/src/components/page-watch-common/player/common/none-live-panel/portrait-none-live-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..a16e88d8b690285ab70caef05b135411a275ac4a --- /dev/null +++ b/src/components/page-watch-common/player/common/none-live-panel/portrait-none-live-panel.vue @@ -0,0 +1,28 @@ + + + + diff --git a/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn-2.png b/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn-2.png new file mode 100644 index 0000000000000000000000000000000000000000..e7c1c30d02d099489b161c4d88bd37a85127c5d1 Binary files /dev/null and b/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn-2.png differ diff --git a/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn-prefix.gif b/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn-prefix.gif new file mode 100644 index 0000000000000000000000000000000000000000..3656052738248a434817822211050e3ceceadf54 Binary files /dev/null and b/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn-prefix.gif differ diff --git a/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn.png b/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn.png new file mode 100644 index 0000000000000000000000000000000000000000..fafd0896e24c759f473de6f9cc4664cce6463571 Binary files /dev/null and b/src/components/page-watch-common/player/common/player-play-button/imgs/play-btn.png differ diff --git a/src/components/page-watch-common/player/common/player-play-button/player-play-button.vue b/src/components/page-watch-common/player/common/player-play-button/player-play-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..2e63ba9fa0afa9de4d2a80335d0314e6625a086a --- /dev/null +++ b/src/components/page-watch-common/player/common/player-play-button/player-play-button.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/src/components/page-watch-common/player/common/player-play-button/use-player-play-button.ts b/src/components/page-watch-common/player/common/player-play-button/use-player-play-button.ts new file mode 100644 index 0000000000000000000000000000000000000000..3f62fcfde00dffdb5e2737361e6db0815b5cb069 --- /dev/null +++ b/src/components/page-watch-common/player/common/player-play-button/use-player-play-button.ts @@ -0,0 +1,12 @@ +import { PropUtils } from '@/assets/utils/vue-utils/props-utils'; + +export enum PlayButtonMode { + /** 三角形的播放按钮 */ + Default = 'default', + /** 文字提示型的播放按钮 */ + Text = 'text', +} + +export const usePlayerPlayButtonProps = () => ({ + mode: PropUtils.enum().def(PlayButtonMode.Default), +}); diff --git a/src/components/page-watch-common/player/hooks/use-player-action.ts b/src/components/page-watch-common/player/hooks/use-player-action.ts new file mode 100644 index 0000000000000000000000000000000000000000..00b12ac4c51d359f12fcd2c043303cb12f7a46c8 --- /dev/null +++ b/src/components/page-watch-common/player/hooks/use-player-action.ts @@ -0,0 +1,207 @@ +/** + * @file 播放器操作 hook + */ +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { PlayStatus } from '@polyv/live-watch-sdk'; +import debounce from 'lodash-es/debounce'; + +/** + * 播放器操作 hook + */ +export const usePlayerAction = () => { + const playerStore = usePlayerStore(); + const layoutStore = useLayoutStore(); + + /** + * 是否不允许处理 + */ + function isNotAllowAction(): boolean { + return !playerStore.isPlayMode; + } + + /** 播放 */ + function toPlay(): void { + if (isNotAllowAction()) { + return; + } + + getWatchCore().player.play(); + } + + /** 暂停 */ + function toPause(): void { + if (isNotAllowAction()) { + return; + } + + getWatchCore().player.pause(); + } + + /** 如果当前是暂停则调用播放 */ + function toTryPlay() { + if (playerStore.playStatus === PlayStatus.Pause) { + toPlay(); + } + } + + /** 切换播放状态 */ + function toTogglePlay(): void { + if (playerStore.playStatus === PlayStatus.Pause) { + toPlay(); + } else { + toPause(); + } + } + + /** 刷新 */ + function toRefresh(): void { + if (isNotAllowAction()) { + return; + } + + getWatchCore().player.refresh(); + } + + /** 防抖设置音量 */ + const setVolumeDebounce = debounce((volume: number) => { + getWatchCore().player.setVolume(volume); + }, 300); + + /** 设置音量 */ + function toChangeVolume(volume = 1): void { + if (volume < 0) { + volume = 0; + } + if (volume > 1) { + volume = 1; + } + playerStore.$patch({ + currentVolume: volume, + }); + setVolumeDebounce(volume); + } + + /** + * 递增音量 + * @param diff 增加的音量,默认:0.05 + */ + function toIncreaseVolume(diff = 0.05) { + const currentVolume = playerStore.currentVolume + diff; + toChangeVolume(currentVolume); + } + + /** + * 递减音量 + * @param diff 减少的音量,默认:0.05 + */ + function toReductionVolume(diff = 0.05) { + const currentVolume = playerStore.currentVolume - diff; + toChangeVolume(currentVolume); + } + + /** 切换弹幕显示 */ + function toToggleBarrageShow(): void { + playerStore.barrageShow = !playerStore.barrageShow; + getWatchCore().player.setBarrageShow(playerStore.barrageShow); + } + + /** 切换线路 */ + function toChangeLine(line: number): void { + getWatchCore().player.changeLine(line); + toTryPlay(); + } + + /** 切换清晰度级别 */ + function toChangeQualityLevel(level: number): void { + getWatchCore().player.changeQualityLevel(level); + toTryPlay(); + } + + /** 切换倍速 */ + function toChangeRate(rate: number): void { + getWatchCore().player.changeRate(rate); + toTryPlay(); + } + + /** 设置播放进度 */ + function toSeekVideo(time: number): void { + if (time < 0) { + time = 0; + } + if (time > playerStore.durationTime) { + time = playerStore.durationTime; + } + getWatchCore().player.seekVideo(time); + playerStore.$patch({ + currentTime: time, + }); + } + + /** + * 视频前进 + * @param second 秒数,默认:15 + */ + function toForwardVideo(second = 15) { + const currentTime = playerStore.currentTime + second; + toSeekVideo(currentTime); + playerStore.$patch({ + currentTime, + }); + } + + /** + * 视频后退 + * @param second 秒数,默认:15 + */ + function toBackOffVideo(second = 15) { + const currentTime = playerStore.currentTime - second; + toSeekVideo(currentTime); + playerStore.$patch({ + currentTime, + }); + } + + /** 重新播放 */ + function toRestart(): void { + toSeekVideo(0); + toPlay(); + } + + /** 切换移动端播放器页面全屏 */ + function toToggleMobilePageFullscreen() { + layoutStore.$patch({ + mobilePlayerIsPageFullscreen: !layoutStore.mobilePlayerIsPageFullscreen, + }); + } + + /** 切换无延迟播放 */ + function toSetLowLowLatency(isLowLatency = true) { + playerStore.$patch({ + isLowLatency, + }); + eventBus.$emit(appEvents.player.ResetUpPlayer); + } + + return { + toPlay, + toPause, + toTogglePlay, + toRefresh, + toChangeVolume, + toIncreaseVolume, + toReductionVolume, + toToggleBarrageShow, + toChangeLine, + toChangeQualityLevel, + toChangeRate, + toSeekVideo, + toForwardVideo, + toBackOffVideo, + toRestart, + toToggleMobilePageFullscreen, + toSetLowLowLatency, + }; +}; diff --git a/src/components/page-watch-common/player/hooks/use-player-control.ts b/src/components/page-watch-common/player/hooks/use-player-control.ts new file mode 100644 index 0000000000000000000000000000000000000000..94122a537b308d7ee4a3acc92e0c1fb9ad22de4b --- /dev/null +++ b/src/components/page-watch-common/player/hooks/use-player-control.ts @@ -0,0 +1,335 @@ +/** + * @file 播放器控制栏 hook + */ +import { translate } from '@/assets/lang'; +import { numberAccuracy } from '@/assets/utils/number'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useConnectMicStore } from '@/store/use-connect-mic-store'; +import { useDocStore } from '@/store/use-doc-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { LiveStatus, PlayStatus, QualityLevelType, YN } from '@polyv/live-watch-sdk'; +import { formatSeconds } from '@utils-ts/date'; +import { ynToBool } from '@utils-ts/boolean'; +import { computed, unref } from 'vue'; + +/** 播放器设置节点 */ +type PlayerSettingItem = { + text: string; + value: V; + active: boolean; +}; + +export const usePlayerControl = () => { + const channelStore = useChannelStore(); + const playerStore = usePlayerStore(); + const playbackStore = usePlaybackStore(); + const connectMicStore = useConnectMicStore(); + const docStore = useDocStore(); + + /** 播放按钮提示文案 */ + const playButtonTips = computed(() => { + let text = ''; + if (playerStore.playStatus === PlayStatus.Pause) { + text = translate('player.play'); + } else { + text = translate('player.pause'); + } + + return text; + }); + + /** 是否显示暂停播放按钮 */ + const playButtonVisible = computed(() => { + return !connectMicStore.isConnectMicing; + }); + + /** + * 是否显示大的暂停播放按钮 + * @desc PC 端是只在第一次播放的时候才要显示,但是移动端无延迟的情况下又需要一直显示。所以这里暂时统一处理为都要展示 + * */ + const bigPlayButtonVisible = computed(() => { + return ( + playerStore.isPlayMode && + playerStore.playerInited && + (!playerStore.isPlayStarted || playerStore.playStatus === PlayStatus.Pause) && + !connectMicStore.isConnectMicing + ); + }); + + /** 常规的播放按钮是否显示 */ + const normalPlayButtonVisible = computed(() => { + return ( + // 播放器初始化完毕 + playerStore.playerInited && + // 暂停中状态 + playerStore.playStatus === PlayStatus.Pause && + // 可以进行播放 + playerStore.isPlayMode + ); + }); + + /** 是否显示手动播放提示 */ + const manualPlayTipsVisible = computed(() => { + return ( + channelStore.liveStatus === LiveStatus.Live && + !playerStore.supportAutoPlay && + !playerStore.isPlayStarted && + !connectMicStore.isConnectMicing + ); + }); + + /** RTC 播放器暂停播放的提示 */ + const rtcPlayerPauseTipVisible = computed(() => { + return ( + channelStore.liveStatus === LiveStatus.Live && + playerStore.isLowLatency && + playerStore.isPlayStarted && + playerStore.playStatus === PlayStatus.Pause + ); + }); + + /** 是否显示刷新按钮 */ + const refreshButtonVisible = computed(() => { + if (connectMicStore.isConnectMicing) { + return false; + } + + return ( + channelStore.liveStatus === LiveStatus.Live && + playerStore.playerInited && + playerStore.supportRefresh + ); + }); + + /** 音量设置是否显示 */ + const volumeSettingVisible = computed(() => { + return !connectMicStore.isConnectMicing; + }); + + /** 倍速设置是否显示 */ + const rateSettingVisible = computed(() => { + return playbackStore.isPlaybacking && playerStore.rateList.length > 0; + }); + + /** 可选的倍速列表 */ + const rateSettingList = computed(() => { + const rates = playerStore.rateList; + const settingList: PlayerSettingItem[] = []; + rates.forEach(rate => { + settingList.push({ + text: `${numberAccuracy(rate)}x`, + value: rate, + active: rate === playerStore.currentRate, + }); + }); + + return settingList; + }); + + /** 线路设置是否显示 */ + const lineSettingVisible = computed(() => { + return ( + playerStore.lineCount > 0 && + channelStore.liveStatus === LiveStatus.Live && + !channelStore.testModeStatus + ); + }); + + /** 线路设置列表 */ + const lineSettingList = computed(() => { + const settingList: PlayerSettingItem[] = []; + + for (let i = 0; i < playerStore.lineCount; i++) { + settingList.push({ + text: `${translate('player.line')}${i + 1}`, + value: i, + active: playerStore.currentLine === i, + }); + } + + return settingList; + }); + + /** 清晰度设置是否显示 */ + const qualitySettingVisible = computed(() => { + return ( + playerStore.qualityLevels.length > 1 && + [LiveStatus.Live, LiveStatus.Playback].includes(channelStore.liveStatus) && + !channelStore.testModeStatus + ); + }); + + /** 过滤清晰度切换文本 */ + function filterQualityText(levelType: QualityLevelType): string { + let text = '未知'; + switch (levelType) { + case QualityLevelType.auto: + text = translate('player.quality.auto'); + break; + case QualityLevelType.sd: + text = translate('player.quality.sd'); + break; + case QualityLevelType.hd: + text = translate('player.quality.hd'); + break; + case QualityLevelType.fhd: + text = translate('player.quality.fhd'); + break; + } + + return text; + } + + /** 清晰度设置列表 */ + const qualitySettingList = computed(() => { + const settingList: PlayerSettingItem[] = []; + + for (let i = 0; i < playerStore.qualityLevels.length; i++) { + const item = playerStore.qualityLevels[i]; + settingList.push({ + text: filterQualityText(item.type), + value: item.level, + active: playerStore.currentQualityLevel === item.level, + }); + } + + return settingList; + }); + + /** 延迟模式切换列表 */ + const latencyModeSettingList = computed[]>(() => { + return [ + { + text: translate('player.latencyMode.low'), + value: true, + active: playerStore.isLowLatency, + }, + { + text: translate('player.latencyMode.normal'), + value: false, + active: !playerStore.isLowLatency, + }, + ]; + }); + + /** 延迟模式切换是否显示 */ + const latencyModeSettingVisible = computed(() => { + return playerStore.supportLowLatency; + }); + + /** 控制栏设置是否显示(PC 端) */ + const playerSettingVisible = computed(() => { + if (connectMicStore.isConnectMicing) { + return false; + } + + return ( + unref(lineSettingVisible) || unref(qualitySettingVisible) || unref(latencyModeSettingVisible) + ); + }); + + /** 是否显示文档视频区域切换按钮 */ + const switchScreenButtonVisible = computed(() => { + return docStore.canRenderDoc; + }); + + /** 回放进度条是否显示 */ + const playbackProgressVisible = computed(() => { + return playbackStore.isPlaybacking && playerStore.durationTime !== 0; + }); + + /** 回放时间是否显示 */ + const playbackTimeVisible = computed(() => { + return playbackStore.isPlaybacking; + }); + + /** 回放时间文案 */ + const currentTimeText = computed(() => { + return formatSeconds(playerStore.currentTime, { segments: 3 }); + }); + + /** 播放总时长文案 */ + const durationTimeText = computed(() => { + return formatSeconds(playerStore.durationTime, { segments: 3 }); + }); + + /** 音视频模式切换是否显示 */ + const playerModeSwitcherVisible = computed(() => { + const isNotOnlyAudio = + !channelStore.isOnlyAudioLive && + ynToBool(channelStore.channelDetail?.channelConfig.isOnlyAudio, 'N') && + ynToBool(channelStore.channelDetail?.channelConfig.mobileAudioEnabled, YN.N); + + const isSupportPlayScene = + channelStore.liveStatus === LiveStatus.Live || playbackStore.isPlaybacking; + + return isNotOnlyAudio && isSupportPlayScene && !playerStore.isLowLatency; + }); + + return { + playButtonTips, + playButtonVisible, + bigPlayButtonVisible, + normalPlayButtonVisible, + manualPlayTipsVisible, + rtcPlayerPauseTipVisible, + refreshButtonVisible, + volumeSettingVisible, + + rateSettingVisible, + rateSettingList, + + lineSettingVisible, + lineSettingList, + + filterQualityText, + qualitySettingVisible, + qualitySettingList, + + latencyModeSettingList, + latencyModeSettingVisible, + + playerSettingVisible, + switchScreenButtonVisible, + playbackProgressVisible, + playbackTimeVisible, + currentTimeText, + durationTimeText, + + playerModeSwitcherVisible, + }; +}; + +/** + * 播放器控制栏连麦控制 hook + */ +export const usePlayerControlConnectMic = () => { + const connectMicStore = useConnectMicStore(); + + /** 主讲头衔 */ + const masterActor = computed(() => { + if (connectMicStore.masterIsSelf) { + return translate('connectMic.me'); + } + if (connectMicStore.masterMicItem?.isTeacher) { + return translate('connectMic.teacher'); + } + + return ''; + }); + + /** + * 主讲昵称 + */ + const masterNickname = computed(() => { + const nickname = connectMicStore.masterMicItem?.nickname || ''; + const actor = unref(masterActor) ? `(${unref(masterActor)})` : ''; + return `${nickname}${actor}`; + }); + + return { + masterActor, + masterNickname, + }; +}; diff --git a/src/components/page-watch-common/player/hooks/use-player-event.ts b/src/components/page-watch-common/player/hooks/use-player-event.ts new file mode 100644 index 0000000000000000000000000000000000000000..c56b983343549f3efd1163fb347374aa3af97250 --- /dev/null +++ b/src/components/page-watch-common/player/hooks/use-player-event.ts @@ -0,0 +1,123 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { PlaybackMode, PlaybackTarget, PlayerEvents } from '@polyv/live-watch-sdk'; +import { onBeforeUnmount } from 'vue'; + +/** + * 播放器相关事件 hook + */ +export const usePlayerEvent = () => { + const playerStore = usePlayerStore(); + const channelStore = useChannelStore(); + const playbackStore = usePlaybackStore(); + + function onPlayerInited(): void { + playerStore.$patch({ + playerInited: true, + }); + } + + function onPlayerPlaying(): void { + playerStore.$patch({ + isPlayStarted: true, + }); + } + + function onHistoryPlay(evt: { historyTime: number }): void { + eventBus.$emit(appEvents.player.ShowHistoryTips, evt.historyTime); + } + + /** 处理回放播放结束 */ + async function onPlaybackOver() { + const watchCore = getWatchCore(); + const channelDetail = channelStore.channelDetail; + + const toPlayTarget = async (target: PlaybackTarget) => { + playbackStore.$patch({ + currentPlaybackTarget: target, + }); + await watchCore.player.changePlayback(target.playbackOptions); + watchCore.player.play(); + }; + + // 单个回放 + if ( + channelDetail?.playbackInfo?.type === PlaybackMode.Single && + playbackStore.currentPlaybackTarget + ) { + await toPlayTarget(playbackStore.currentPlaybackTarget); + return; + } + + if (playbackStore.playbackList.length === 0) { + return; + } + + let playbackTarget: PlaybackTarget | undefined; + const playbackLength = playbackStore.playbackList.length; + const currentIndex = playbackStore.playbackList.findIndex(target => { + return target.playbackId === playbackStore.currentPlaybackTarget?.playbackId; + }); + + if (currentIndex === playbackLength - 1) { + // 当前播放的是列表的最后一个 + if (playbackStore.isNoMore) { + // 已经拿完了回放列表,则播放第一个 + playbackTarget = playbackStore.playbackList[0]; + } else { + // 加载下一页 + await playbackStore.loadNextPlaybackList(); + const nextTarget = playbackStore.playbackList[currentIndex + 1]; + playbackTarget = nextTarget; + } + } else { + // 下一个是在本页 + const nextTarget = playbackStore.playbackList[currentIndex + 1]; + playbackTarget = nextTarget; + } + + if (playbackTarget) { + await toPlayTarget(playbackTarget); + } + } + + /** 音量修改 */ + function onVolumeChanged() { + const watchCore = getWatchCore(); + const currentVolume = watchCore.player.getCurrentVolume(); + playerStore.$patch({ currentVolume }); + } + + /** 绑定播放器事件 */ + function bindPlayerEvents() { + unbindPlayerEvents(); + const watchCore = getWatchCore(); + watchCore.player.eventEmitter.on(PlayerEvents.PlayerInited, onPlayerInited); + watchCore.player.eventEmitter.on(PlayerEvents.PlayerPlaying, onPlayerPlaying); + watchCore.player.eventEmitter.on(PlayerEvents.HistoryPlay, onHistoryPlay); + watchCore.player.eventEmitter.on(PlayerEvents.PlaybackOver, onPlaybackOver); + watchCore.player.eventEmitter.on(PlayerEvents.VolumeChanged, onVolumeChanged); + } + + /** 解绑播放器事件 */ + function unbindPlayerEvents() { + const watchCore = getWatchCore(); + watchCore.player.eventEmitter.off(PlayerEvents.PlayerInited, onPlayerInited); + watchCore.player.eventEmitter.off(PlayerEvents.PlayerPlaying, onPlayerPlaying); + watchCore.player.eventEmitter.off(PlayerEvents.HistoryPlay, onHistoryPlay); + watchCore.player.eventEmitter.off(PlayerEvents.PlaybackOver, onPlaybackOver); + watchCore.player.eventEmitter.off(PlayerEvents.VolumeChanged, onVolumeChanged); + } + + onBeforeUnmount(() => { + unbindPlayerEvents(); + }); + + return { + bindPlayerEvents, + unbindPlayerEvents, + }; +}; diff --git a/src/components/page-watch-common/player/hooks/use-player-keyboard.ts b/src/components/page-watch-common/player/hooks/use-player-keyboard.ts new file mode 100644 index 0000000000000000000000000000000000000000..a2b0195a892030e1b44f46fee1ca4f45089b2834 --- /dev/null +++ b/src/components/page-watch-common/player/hooks/use-player-keyboard.ts @@ -0,0 +1,117 @@ +import { isFocusActiveForm } from '@/assets/utils/dom'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { onBeforeUnmount, onMounted, ref, Ref, watchEffect } from 'vue'; +import { usePlayerAction } from './use-player-action'; +import { MainScreenContent, WarmUpType } from '@polyv/live-watch-sdk'; +import { KeyCodeMap } from '@/assets/constants/key-code'; +import { usePlayerStore } from '@/store/use-player-store'; + +/** 副作用元素收集数组 */ +let _effectElements: Array> = []; + +/** 收集需要响应播放器键盘事件的元素 */ +export const collectPlayerKeyboardEffectElements = (el: Ref) => { + _effectElements.push(el); +}; + +/** + * PC 端播放器键盘按键 hook + */ +export const usePlayerKeyboard = (playerContainer: Ref) => { + collectPlayerKeyboardEffectElements(playerContainer); + + const { toTogglePlay, toForwardVideo, toBackOffVideo, toIncreaseVolume, toReductionVolume } = + usePlayerAction(); + + const playerStore = usePlayerStore(); + const playbackStore = usePlaybackStore(); + const layoutStore = useLayoutStore(); + + /** 焦点是否在副作用元素 */ + const isFocusOnEffectEl = ref(false); + + watchEffect(() => { + const canPlayWarmVideo = playerStore.warmUpType === WarmUpType.Video; + if (playbackStore.isPlaybacking || canPlayWarmVideo) { + listenKeydownEvent(); + } else { + removeListenKeydownEvent(); + } + }); + + onMounted(() => { + window.addEventListener( + 'click', + event => { + const targetElement = event.target as HTMLElement; + const isContainTarget = _effectElements.find(el => el.value?.contains(targetElement)); + isFocusOnEffectEl.value = !!isContainTarget; + }, + true, + ); + }); + + onBeforeUnmount(() => { + removeListenKeydownEvent(); + _effectElements = []; + }); + + /** 监听键盘事件 */ + function listenKeydownEvent() { + removeListenKeydownEvent(); + window.addEventListener('keydown', onKeydownEvent, false); + } + + /** 移除键盘事件监听 */ + function removeListenKeydownEvent() { + window.removeEventListener('keydown', onKeydownEvent, false); + } + + /** 处理键盘事件 */ + function onKeydownEvent(event: KeyboardEvent) { + // 没有聚焦在副作用元素 || 当前存在被聚焦的输入框 + if (!isFocusOnEffectEl.value || isFocusActiveForm()) { + return; + } + + /** 当前按键 */ + const keyCode = event.keyCode; + /** 文档是否处于主屏 */ + const docInMainScreen = layoutStore.mainScreen === MainScreenContent.Doc; + + // 空格键:播放暂停 + if (keyCode === KeyCodeMap.Space) { + event.preventDefault(); + toTogglePlay(); + return; + } + + // 右箭头:控制视频前进 + if (keyCode === KeyCodeMap.ArrowRight && !docInMainScreen) { + event.preventDefault(); + toForwardVideo(); + return; + } + + // 左箭头:控制视频后退 + if (keyCode === KeyCodeMap.ArrowLeft && !docInMainScreen) { + event.preventDefault(); + toBackOffVideo(); + return; + } + + // 上箭头:控制音量递增 + if (keyCode === KeyCodeMap.ArrowUp && !docInMainScreen) { + event.preventDefault(); + toIncreaseVolume(); + return; + } + + // 下箭头:控制音量递减 + if (keyCode === KeyCodeMap.ArrowDown && !docInMainScreen) { + event.preventDefault(); + toReductionVolume(); + } + } +}; diff --git a/src/components/page-watch-common/player/hooks/use-player-main.ts b/src/components/page-watch-common/player/hooks/use-player-main.ts new file mode 100644 index 0000000000000000000000000000000000000000..da31a169b87de9fc03c62772c7dda37f493d528e --- /dev/null +++ b/src/components/page-watch-common/player/hooks/use-player-main.ts @@ -0,0 +1,203 @@ +/** + * @file 播放器 hook + */ +import { onBeforeUnmount, onMounted, ref, unref, watch } from 'vue'; +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { getImgSize } from '@/assets/utils/image'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { usePlaybackStore } from '@/store/use-playback-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { useLangStore } from '@/store/use-lang-store'; +import { LiveStatus, SetupPlayerOptions } from '@polyv/live-watch-sdk'; +import { usePlayerEvent } from './use-player-event'; +import { LangType } from '@/assets/lang/lang-enum'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; + +export type InitPlayerOptions = Omit; + +export type InitPlayerOptionsType = InitPlayerOptions | (() => InitPlayerOptions); + +export const usePlayerMain = ({ + initPlayerOptions = {}, +}: { + initPlayerOptions?: InitPlayerOptionsType; +} = {}) => { + const { bindPlayerEvents, unbindPlayerEvents } = usePlayerEvent(); + + const channelStore = useChannelStore(); + const playerStore = usePlayerStore(); + const playbackStore = usePlaybackStore(); + const { currentPlaybackTarget } = storeDefinitionToRefs(usePlaybackStore); + const langStore = useLangStore(); + + /** 播放器渲染容器 */ + const playerContainer = ref(); + + /** + * 根据图片地址设置流尺寸 + * @param imageUrl 图片地址 + */ + async function setResolutionSizeByImage(imageUrl: string) { + try { + const { width, height } = await getImgSize(imageUrl); + if (width && height) { + playerStore.$patch({ + resolutionWidth: width, + resolutionHeight: height, + }); + } + } catch (e) {} + } + + /** + * 获取播放器的回放参数 + * @warn 副作用:修改回放视频尺寸,非回放状态清空 currentPlaybackTarget + */ + async function getPlayerPaybackOptions() { + if (channelStore.liveStatus !== LiveStatus.Playback) { + playbackStore.$patch({ + currentPlaybackTarget: undefined, + }); + return; + } + + if (!currentPlaybackTarget.value) { + console.error('处于回放状态,但不存在回放对象'); + return; + } + + // 先处理回放的视频尺寸 + if (currentPlaybackTarget.value.width && currentPlaybackTarget.value.height) { + playerStore.$patch({ + resolutionWidth: currentPlaybackTarget.value.width, + resolutionHeight: currentPlaybackTarget.value.height, + }); + } else if (currentPlaybackTarget.value.firstImage) { + // 通过首帧图来设置流的尺寸 + await setResolutionSizeByImage(currentPlaybackTarget.value.firstImage); + } + + return currentPlaybackTarget.value.playbackOptions; + } + + /** + * 获取推流信息 + */ + async function getPushStreamInfo() { + if (channelStore.liveStatus !== LiveStatus.Live) { + return; + } + + const watchCore = getWatchCore(); + const pushInfo = await watchCore.channel.getPushInfo(); + if (pushInfo.resolutionWidth && pushInfo.resolutionHeight) { + playerStore.$patch({ + resolutionWidth: pushInfo.resolutionWidth, + resolutionHeight: pushInfo.resolutionHeight, + }); + } + + channelStore.$patch({ + currentStreamType: pushInfo.streamType, + }); + } + + /** 初始化播放器 */ + async function initPlayer(): Promise { + const container = unref(playerContainer); + if (!container) { + return; + } + + if (!playerStore.playerEnabled) return; + + const watchCore = getWatchCore(); + + playerStore.$patch({ + playerInited: false, + isPlayStarted: false, + }); + playbackStore.$patch({ + isPlaybacking: false, + }); + + let playerOptions: SetupPlayerOptions = { + container, + lowLatency: playerStore.isLowLatency, + language: langStore.currentLang === LangType.Chinese ? 0 : 1, + }; + + // 其他参数 + if (typeof initPlayerOptions === 'function') { + playerOptions = { + ...playerOptions, + ...initPlayerOptions(), + }; + } else { + playerOptions = { + ...playerOptions, + ...initPlayerOptions, + }; + } + + // 获取回放参数 + const playbackOptions = await getPlayerPaybackOptions(); + if (playbackOptions) { + playerOptions.playbackOptions = playbackOptions; + } else { + await getPushStreamInfo(); + } + + await watchCore.player.setupPlayer(playerOptions); + + bindPlayerEvents(); + + if (playerOptions.playbackOptions) { + playbackStore.$patch({ + isPlaybacking: true, + }); + } + } + + /** 销毁播放器 */ + function destroyPlayer(): void { + const watchCore = getWatchCore(); + watchCore.player.destroyPlayer(); + unbindPlayerEvents(); + } + + watch( + () => channelStore.liveStatus, + () => { + const watchCore = getWatchCore(); + const supportLowLatency = watchCore.player.getLowLatencySupport(); + playerStore.$patch({ + supportLowLatency, + }); + if (channelStore.liveStatus === LiveStatus.Live && supportLowLatency) { + playerStore.$patch({ + isLowLatency: true, + }); + } + + initPlayer(); + }, + ); + + useEventBusListener(appEvents.player.ResetUpPlayer, () => { + initPlayer(); + }); + + onMounted(() => { + initPlayer(); + }); + + onBeforeUnmount(() => { + destroyPlayer(); + }); + + return { + playerContainer, + }; +}; diff --git a/src/components/page-watch-common/player/hooks/use-resolution-size.ts b/src/components/page-watch-common/player/hooks/use-resolution-size.ts new file mode 100644 index 0000000000000000000000000000000000000000..e779b6c32e907d397fe3e912bd7913c0f25435c1 --- /dev/null +++ b/src/components/page-watch-common/player/hooks/use-resolution-size.ts @@ -0,0 +1,124 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { usePlayerStore } from '@/store/use-player-store'; +import { PlayerEvents } from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, onMounted, Ref, unref } from 'vue'; + +/** 获取视频宽度 */ +function getVideoWidth(videoElement: HTMLVideoElement) { + if (typeof videoElement.videoWidth !== 'undefined') { + return videoElement.videoWidth; + } else { + return videoElement.offsetWidth; + } +} + +/** 获取视频高度 */ +function getVideoHeight(videoElement: HTMLVideoElement) { + if (typeof videoElement.videoWidth !== 'undefined') { + return videoElement.videoHeight; + } else { + return videoElement.offsetHeight; + } +} + +/** + * 播放流尺寸 hook + */ +export const useResolutionSize = (options: { + playerContainer: Ref; +}) => { + const { playerContainer } = options; + const playerStore = usePlayerStore(); + + /** 获取 video 节点 */ + function getVideoElement(): HTMLVideoElement | null { + const containerElem = unref(playerContainer); + if (!containerElem) { + return null; + } + + return containerElem.querySelector('video'); + } + + /** 重试定时器 */ + let retryTimer: number | undefined; + /** 重试次数 */ + let retryCount = 0; + + /** 设置重试定时器 */ + function startRetryTimer() { + removeRetryTimer(); + retryTimer = window.setTimeout(() => { + refreshResolution(); + }, 100); + } + + /** 移除重试定时器 */ + function removeRetryTimer() { + retryCount = 0; + if (retryTimer) { + clearTimeout(retryTimer); + retryTimer = undefined; + } + } + + /** + * 刷新 video 标签的 videoWidth、videoHeight,改变视频流尺寸 + */ + function refreshResolutionByVideoSize(videoElem: HTMLVideoElement) { + const videoWidth = getVideoWidth(videoElem); + const videoHeight = getVideoHeight(videoElem); + + const notNeedRefresh = + videoWidth === playerStore.resolutionWidth && videoHeight === playerStore.resolutionHeight; + if (notNeedRefresh) { + removeRetryTimer(); + return; + } + + if (videoWidth > 0 && videoHeight > 0) { + removeRetryTimer(); + playerStore.$patch({ + resolutionWidth: videoWidth, + resolutionHeight: videoHeight, + }); + } else { + // 部分流还是需要通过重试轮询来获取尺寸,设置轮询最大次数为 100 次 + if (retryCount >= 100) { + removeRetryTimer(); + console.error('无法正常获取视频流的尺寸'); + return; + } + retryCount += 1; + startRetryTimer(); + } + } + + function refreshResolution() { + const videoElem = getVideoElement(); + if (!videoElem) { + return; + } + + const isVideoDataLoaded = videoElem.readyState >= 1; + if (isVideoDataLoaded) { + refreshResolutionByVideoSize(videoElem); + } else { + videoElem.addEventListener('loadeddata', () => { + refreshResolutionByVideoSize(videoElem); + }); + } + } + + onMounted(() => { + const watchCore = getWatchCore(); + watchCore.player.eventEmitter.on(PlayerEvents.PlayerPlaying, refreshResolution); + }); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.player.eventEmitter.off(PlayerEvents.PlayerPlaying, refreshResolution); + + removeRetryTimer(); + }); +}; diff --git a/src/components/page-watch-common/player/imgs/icon-pause.png b/src/components/page-watch-common/player/imgs/icon-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..4134bd114e417ed83bf769aea6173e9aab98de6b Binary files /dev/null and b/src/components/page-watch-common/player/imgs/icon-pause.png differ diff --git a/src/components/page-watch-common/player/imgs/icon-play.png b/src/components/page-watch-common/player/imgs/icon-play.png new file mode 100644 index 0000000000000000000000000000000000000000..d9b3381525b60f5d99da26e2b71fe241ad0ea2f7 Binary files /dev/null and b/src/components/page-watch-common/player/imgs/icon-play.png differ diff --git a/src/components/page-watch-common/player/imgs/play-btn.png b/src/components/page-watch-common/player/imgs/play-btn.png new file mode 100644 index 0000000000000000000000000000000000000000..fafd0896e24c759f473de6f9cc4664cce6463571 Binary files /dev/null and b/src/components/page-watch-common/player/imgs/play-btn.png differ diff --git a/src/components/page-watch-common/player/imgs/player-audio-mode.png b/src/components/page-watch-common/player/imgs/player-audio-mode.png new file mode 100644 index 0000000000000000000000000000000000000000..3b6b20aac0017ff0409791731b23dffe23c5f365 Binary files /dev/null and b/src/components/page-watch-common/player/imgs/player-audio-mode.png differ diff --git a/src/components/page-watch-common/player/imgs/player-no-live.png b/src/components/page-watch-common/player/imgs/player-no-live.png new file mode 100644 index 0000000000000000000000000000000000000000..0634450d7647c55ca7da54a6392dc33d0d83b00d Binary files /dev/null and b/src/components/page-watch-common/player/imgs/player-no-live.png differ diff --git a/src/components/page-watch-common/player/imgs/player-stop.png b/src/components/page-watch-common/player/imgs/player-stop.png new file mode 100644 index 0000000000000000000000000000000000000000..da641688bca80b67716894701fa8fb71b42fd2ba Binary files /dev/null and b/src/components/page-watch-common/player/imgs/player-stop.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/low-latency-active.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/low-latency-active.png new file mode 100644 index 0000000000000000000000000000000000000000..66f04849d54be077177ba5609ac7081058fd97c9 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/low-latency-active.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-fullscreen-exit.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-fullscreen-exit.png new file mode 100644 index 0000000000000000000000000000000000000000..5e38df8176534b9ddd37b53f1be2774f19b3272f Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-fullscreen-exit.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-fullscreen.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..38521a938e135cbe93b68423cb4422bb1e8764eb Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-fullscreen.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-pause.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..bde8ec6aed37222a4c1c145cf236b9594dcebc5a Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-pause.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-play.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-play.png new file mode 100644 index 0000000000000000000000000000000000000000..3f8bfe4734883b041ddea54aefb7b423ab6cbebc Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-play.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-refresh.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..774c19b4850f2d90e0156084689c0220f3bcefbf Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-refresh.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-setting.png b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-setting.png new file mode 100644 index 0000000000000000000000000000000000000000..59b62eab909a376eb5685a7bab8cb6685dc3632d Binary files /dev/null and b/src/components/page-watch-common/player/player-control/mobile-player-control/imgs/mobile-player-ui-setting.png differ diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-low-latency-switch.vue b/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-low-latency-switch.vue new file mode 100644 index 0000000000000000000000000000000000000000..4953fc05663efbd7cd2a6319ec82616ce360d887 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-low-latency-switch.vue @@ -0,0 +1,129 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-player-control-setting.vue b/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-player-control-setting.vue new file mode 100644 index 0000000000000000000000000000000000000000..6f96af22de33fd196b82b89603a54b8b3ee86864 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-player-control-setting.vue @@ -0,0 +1,101 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-player-control.vue b/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-player-control.vue new file mode 100644 index 0000000000000000000000000000000000000000..710a939e11b835ebe8a7c364c7af6a4a654c1409 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/mobile-player-control/mobile-player-control.vue @@ -0,0 +1,280 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/connect-mic-setting.vue b/src/components/page-watch-common/player/player-control/pc-player-control/connect-mic-setting.vue new file mode 100644 index 0000000000000000000000000000000000000000..6d53bb9d68d7654ad2790dc65033a434714cebb5 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/pc-player-control/connect-mic-setting.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-close-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-close-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..055fa5156997e6cbafa0a883888cf1df0fc0ec18 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-close-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-close.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-close.png new file mode 100644 index 0000000000000000000000000000000000000000..0b58ea6c6fa29f744b5be2bad2385e5be1e19bc1 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-close.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..579c3f4742457129b55371a1a26702b2b01cc178 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio.png new file mode 100644 index 0000000000000000000000000000000000000000..6ee9b95603da8b056ba853673ac060780a4cf41a Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-audio.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-close-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-close-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..235386c2522a8aba256467b652246abd27398358 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-close-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-close.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-close.png new file mode 100644 index 0000000000000000000000000000000000000000..196debe9e5f17148051529bd00a45ff42b22b065 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-close.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..517c61de2252ee2705db733f6b69d6de8722844a Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video.png new file mode 100644 index 0000000000000000000000000000000000000000..be8d0c48b1b8e2ba457922d905b4ec1ecd8b5c70 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/icon-call-video.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-barrage-close.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-barrage-close.png new file mode 100644 index 0000000000000000000000000000000000000000..6e8c328042c495a2c72e6f16bc90dc4c21b77a8e Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-barrage-close.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-barrage-open.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-barrage-open.png new file mode 100644 index 0000000000000000000000000000000000000000..65e254bfd78f29d82d40b4b20dbee7ab5c2ca9be Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-barrage-open.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-cancel-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-cancel-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..71bd930c4faa5df845c4b306edd84067b32f68c5 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-cancel-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-cancel.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..7a04fca00452812e92462c547733d4e0223df5f8 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-cancel.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..d70135347ce6258c50e77eab991eb217a67e87ae Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen.png new file mode 100644 index 0000000000000000000000000000000000000000..926876608aaf44c05f88f8adc227db00d48aea40 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-fullscreen.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-pause-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-pause-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..f6b94401fb1c8c9d9493e0f14361fcd13b7723af Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-pause-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-pause.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..0b0e1fa1ecf98711e3d9cdc1862fff4bd56f4d9f Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-pause.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-play-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-play-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..8a9a3bd073d8f5c5cb45fd61e75960f24a1af0e7 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-play-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-play.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-play.png new file mode 100644 index 0000000000000000000000000000000000000000..025f9310d05b936277252fff6d3416f52f3217f0 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-play.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-refresh-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-refresh-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c02f524f13f2162afe534692af5781ffd4d731 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-refresh-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-refresh.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..8928d8764fee1cebc6a65dd28c468219fe117d3d Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-refresh.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-setting-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-setting-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..e8e09f61852841d4106c17da2471c494fac61400 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-setting-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-setting.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-setting.png new file mode 100644 index 0000000000000000000000000000000000000000..5c7653272fcf64c021f563cbb5ee74773613644e Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-setting.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..2003e371d1f5701b6683e33970ebf93706624ffd Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-mute-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-mute-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..f6b05597064b4a45ac4689de240cb732f82ca8d6 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-mute-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-mute.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-mute.png new file mode 100644 index 0000000000000000000000000000000000000000..64139ad30626031d571271acc3911f4b936b5bb6 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound-mute.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound.png new file mode 100644 index 0000000000000000000000000000000000000000..ebba2e8071591a73173d621cb3c66cf782556f86 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-sound.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-switch-screen-hover.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-switch-screen-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..5a20831e457201f9528f4c397f369137cd98dd19 Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-switch-screen-hover.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-switch-screen.png b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-switch-screen.png new file mode 100644 index 0000000000000000000000000000000000000000..c81fc05b0f9a819713a0ed8ee50fadfe4e5e91be Binary files /dev/null and b/src/components/page-watch-common/player/player-control/pc-player-control/imgs/pc-player-ui-switch-screen.png differ diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/player-control.vue b/src/components/page-watch-common/player/player-control/pc-player-control/player-control.vue new file mode 100644 index 0000000000000000000000000000000000000000..9fce0e5650602288449ee6d56730edb73e540046 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/pc-player-control/player-control.vue @@ -0,0 +1,524 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/player-setting.vue b/src/components/page-watch-common/player/player-control/pc-player-control/player-setting.vue new file mode 100644 index 0000000000000000000000000000000000000000..65d507404471a376b81790adb340d17ff7004dc1 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/pc-player-control/player-setting.vue @@ -0,0 +1,177 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/rate-setting.vue b/src/components/page-watch-common/player/player-control/pc-player-control/rate-setting.vue new file mode 100644 index 0000000000000000000000000000000000000000..57d045006e955db0617b5832136dc7498454849e --- /dev/null +++ b/src/components/page-watch-common/player/player-control/pc-player-control/rate-setting.vue @@ -0,0 +1,30 @@ + + + + diff --git a/src/components/page-watch-common/player/player-control/pc-player-control/volume-setting.vue b/src/components/page-watch-common/player/player-control/pc-player-control/volume-setting.vue new file mode 100644 index 0000000000000000000000000000000000000000..dcc2da93fe3f60e8b0563e9971bab067d82fc66b --- /dev/null +++ b/src/components/page-watch-common/player/player-control/pc-player-control/volume-setting.vue @@ -0,0 +1,174 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/_styles/control-select-options.scss b/src/components/page-watch-common/player/player-control/portrait-player-control/_styles/control-select-options.scss new file mode 100644 index 0000000000000000000000000000000000000000..1f0aac1e23158f550813e6d755cad9b18af658c4 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/_styles/control-select-options.scss @@ -0,0 +1,48 @@ +.c-portrait-player-control__select-list { + display: flex; + flex-wrap: wrap; + padding: 14px 16px 46px; +} + +.c-portrait-player-control__select-item { + box-sizing: border-box; + flex-shrink: 0; + width: 66px; + height: 26px; + margin-right: 30px; + font-size: 14px; + line-height: 26px; + color: $--color-white; + text-align: center; + border-radius: 13px; + + &:last-of-type { + margin-right: 0; + } +} + +.c-portrait-player-control__select-item--active { + color: #FFC815; + border: 1px solid #FFC815; +} + +.c-portrait-player-control__select-list--large { + .c-portrait-player-control__select-item { + width: 80px; + } +} + +.c-portrait-player-control__select-list--mini { + .c-portrait-player-control__select-item { + width: 56px; + margin-right: 20px; + } +} + +.c-portrait-player-control__select-list--space-between { + display: flex; + justify-content: space-between; + .c-portrait-player-control__select-item { + margin-right: 0; + } +} \ No newline at end of file diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-latency-setting-popup.vue b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-latency-setting-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..5e9edd453aecdf7f761d6caa111f5ff327c4f27f --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-latency-setting-popup.vue @@ -0,0 +1,57 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-line-setting-popup.vue b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-line-setting-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..1c3d33c1666c1e8bc197d4d540127b15bc3d117a --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-line-setting-popup.vue @@ -0,0 +1,55 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-player-control-popup.vue b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-player-control-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..347c62cbf95ead395a0d166d4290efbd28cb2b7e --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-player-control-popup.vue @@ -0,0 +1,26 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-player-progress.vue b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-player-progress.vue new file mode 100644 index 0000000000000000000000000000000000000000..1e63dff4faa312bf813ab8860f5f75a8f6c05a23 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-player-progress.vue @@ -0,0 +1,75 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-quality-setting-popup.vue b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-quality-setting-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..769c59f9efdb7bf74698b3b8fcc7d532c257ec70 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-quality-setting-popup.vue @@ -0,0 +1,55 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-rate-setting-popup.vue b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-rate-setting-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..fb4a7b7588fd03cb01a1234efefe7c8899430726 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/portrait-rate-setting-popup.vue @@ -0,0 +1,57 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-control/portrait-player-control/use-portrait-setting-popup.ts b/src/components/page-watch-common/player/player-control/portrait-player-control/use-portrait-setting-popup.ts new file mode 100644 index 0000000000000000000000000000000000000000..85582063a53b581e3f847cbed6c5acb0af3e6c06 --- /dev/null +++ b/src/components/page-watch-common/player/player-control/portrait-player-control/use-portrait-setting-popup.ts @@ -0,0 +1,40 @@ +import { appEvents, eventBus, useEventBusListener } from '@/app/app-events'; +import { useSimpleVisible } from '@/hooks/behaviors/use-simple-visible'; +import { ref } from 'vue'; + +export const usePortraitSettingPopup = (event: string) => { + const { visible, setVisible } = useSimpleVisible(); + + const useTransition = ref(false); + + useEventBusListener(event, (params: { _visible?: boolean; _useTransition: boolean }) => { + const { _visible = true, _useTransition = true } = params; + useTransition.value = _useTransition; + setVisible(_visible); + }); + + /** 关闭弹层 */ + function closePopup(_useTransition = true) { + useTransition.value = _useTransition; + setVisible(false); + } + + /** 点击返回 */ + function onClickBack() { + eventBus.$emit(appEvents.portrait.OpenMenuPopup, { + _visible: true, + _useTransition: false, + }); + + useTransition.value = false; + setVisible(false); + } + + return { + visible, + setVisible, + useTransition, + closePopup, + onClickBack, + }; +}; diff --git a/src/components/page-watch-common/player/player-cover-img/mobile-player-cover-img.vue b/src/components/page-watch-common/player/player-cover-img/mobile-player-cover-img.vue new file mode 100644 index 0000000000000000000000000000000000000000..f5d56f459eca79a9e4619f6dd3b65613b9f3fbd9 --- /dev/null +++ b/src/components/page-watch-common/player/player-cover-img/mobile-player-cover-img.vue @@ -0,0 +1,56 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-cover-img/pc-player-cover-img.vue b/src/components/page-watch-common/player/player-cover-img/pc-player-cover-img.vue new file mode 100644 index 0000000000000000000000000000000000000000..44a8c183b9b2dd60d8768a6192eb76fd86349f3e --- /dev/null +++ b/src/components/page-watch-common/player/player-cover-img/pc-player-cover-img.vue @@ -0,0 +1,62 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-cover-img/portrait-player-cover-img.vue b/src/components/page-watch-common/player/player-cover-img/portrait-player-cover-img.vue new file mode 100644 index 0000000000000000000000000000000000000000..70688f4b960ce87253aeda2ffca3fb8059b6b436 --- /dev/null +++ b/src/components/page-watch-common/player/player-cover-img/portrait-player-cover-img.vue @@ -0,0 +1,87 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-logo/mobile-player-logo.vue b/src/components/page-watch-common/player/player-logo/mobile-player-logo.vue new file mode 100644 index 0000000000000000000000000000000000000000..8f8e51baa1dfc962518c29fe6eabfd873143ba61 --- /dev/null +++ b/src/components/page-watch-common/player/player-logo/mobile-player-logo.vue @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-logo/pc-player-logo.vue b/src/components/page-watch-common/player/player-logo/pc-player-logo.vue new file mode 100644 index 0000000000000000000000000000000000000000..9fc7250ab967c5fba30977f753d8dbe382dcb49e --- /dev/null +++ b/src/components/page-watch-common/player/player-logo/pc-player-logo.vue @@ -0,0 +1,69 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-main/mobile-player-main/mobile-player-main.vue b/src/components/page-watch-common/player/player-main/mobile-player-main/mobile-player-main.vue new file mode 100644 index 0000000000000000000000000000000000000000..c772d4f4bda5ab52aa9d1e1d3efb892544deaf65 --- /dev/null +++ b/src/components/page-watch-common/player/player-main/mobile-player-main/mobile-player-main.vue @@ -0,0 +1,166 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-main/mobile-player-main/styles/cover-mobile-player.scss b/src/components/page-watch-common/player/player-main/mobile-player-main/styles/cover-mobile-player.scss new file mode 100644 index 0000000000000000000000000000000000000000..e9eb6530ed2c3d4f0cb759f145f29ac9ae46b737 --- /dev/null +++ b/src/components/page-watch-common/player/player-main/mobile-player-main/styles/cover-mobile-player.scss @@ -0,0 +1,5 @@ +// 隐藏播放按钮 +.plv-live-big-play-btn, +.plv-live-cover__btn { + display: none; +} diff --git a/src/components/page-watch-common/player/player-main/pc-player-main/pc-player-main.vue b/src/components/page-watch-common/player/player-main/pc-player-main/pc-player-main.vue new file mode 100644 index 0000000000000000000000000000000000000000..17b2e76558c6fde10214621d2f32a861410113f7 --- /dev/null +++ b/src/components/page-watch-common/player/player-main/pc-player-main/pc-player-main.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-main/pc-player-main/styles/cover-pc-player.scss b/src/components/page-watch-common/player/player-main/pc-player-main/styles/cover-pc-player.scss new file mode 100644 index 0000000000000000000000000000000000000000..8df2aea18aa866e895a98a0bc5ae3eb880fab370 --- /dev/null +++ b/src/components/page-watch-common/player/player-main/pc-player-main/styles/cover-pc-player.scss @@ -0,0 +1,24 @@ +// @file 覆盖 PC 端播放器样式 + +// 隐藏点播播放器的续播提示 +.pv-start-time { + display: none !important; +} + +// 隐藏直播播放器的一个小图标 +.plv-icon.plv-icon-volume.plv-cutoff-volume { + display: none; +} + +.plv-live-player-password { + z-index: 999 !important; +} + +.plv-live-player-media-pre-volume > .plv-icon.plv-icon-volume { + width: 20px !important; + height: 20px !important; +} + +.plv-live-player-media-pause { + z-index: 999 !important; +} diff --git a/src/components/page-watch-common/player/player-main/portrait-player-main/_hooks/use-portrait-click-player.ts b/src/components/page-watch-common/player/player-main/portrait-player-main/_hooks/use-portrait-click-player.ts new file mode 100644 index 0000000000000000000000000000000000000000..989b29dbdd0c70b902e44b680f3a09b70be3842e --- /dev/null +++ b/src/components/page-watch-common/player/player-main/portrait-player-main/_hooks/use-portrait-click-player.ts @@ -0,0 +1,92 @@ +import { appEvents, useEventBusListener } from '@/app/app-events'; +import { usePlayerStore } from '@/store/use-player-store'; +import { usePlayerAction } from '../../../hooks/use-player-action'; +import { PlayStatus } from '@polyv/live-watch-sdk'; +import { onBeforeUnmount } from 'vue'; + +/** + * 判断是否点击dom区域 + * @param dom 节点 + * @param event 原生事件 + */ +export const checkDomClick = (event: MouseEvent, dom: Element) => { + if (!dom || !event) return; + const clickX = event.clientX; + const clickY = event.clientY; + + const rect = dom.getBoundingClientRect(); + const startX = rect.x; + const endX = startX + rect.width; + const startY = rect.y; + const endY = startY + rect.height; + + return clickX >= startX && clickX <= endX && clickY >= startY && clickY <= endY; +}; + +/** + * 竖屏播放器点击处理 hook + */ +export const usePortraitClickPlayer = () => { + const playerStore = usePlayerStore(); + const { toPlay, toPause } = usePlayerAction(); + + /** 记录点击次数 */ + let clickCount = 0; + /** 清楚点击记录定时器 */ + let clickTimer: number | undefined; + + function clearClickTimer() { + if (clickTimer) { + clearTimeout(clickTimer); + clickTimer = undefined; + } + } + + function openUrl(url?: string) { + if (url) { + window.open(url, '_blank', 'noopenner=true'); + } + } + + /** 处理点击播放器区域 */ + function onClickPlayerRegion(event: MouseEvent) { + // 判断logo点击 + const logoDom = document.querySelector('.plv-live-logo') ?? undefined; + if (logoDom && checkDomClick(event, logoDom)) { + openUrl(playerStore.logoHref); + return; + } + + // 当前处于暂停状态,触发播放 + if (playerStore.isPlayMode && playerStore.playStatus === PlayStatus.Pause) { + toPlay(); + return; + } + + // 点击的是暖场图片的区域 + const warmupImgElem = document.querySelector('[data-warmup-img]') ?? undefined; + if (warmupImgElem && checkDomClick(event, warmupImgElem)) { + openUrl(playerStore.warmUpHref); + return; + } + + clickCount++; + // 双击了播放器区域,暂停播放 + if (clickCount === 2) { + toPause(); + clickCount = 0; + } + + clearClickTimer(); + clickTimer = window.setTimeout(() => { + clearClickTimer(); + clickCount = 0; + }, 200); + } + + onBeforeUnmount(() => { + clearClickTimer(); + }); + + useEventBusListener(appEvents.portrait.ClickPlayerRegion, onClickPlayerRegion); +}; diff --git a/src/components/page-watch-common/player/player-main/portrait-player-main/_styles/cover-player-style.scss b/src/components/page-watch-common/player/player-main/portrait-player-main/_styles/cover-player-style.scss new file mode 100644 index 0000000000000000000000000000000000000000..a4770a3d680ef62d320b45025e5beddeeab3c628 --- /dev/null +++ b/src/components/page-watch-common/player/player-main/portrait-player-main/_styles/cover-player-style.scss @@ -0,0 +1,43 @@ +// @file 覆盖播放器的样式 +.c-portrait-player-main__container { + // ------ CDN ------ // + // 覆盖背景图 + .plwrap { + background: none !important; + } + + // 隐藏自带的播放按钮 + .plv-live-big-play-btn, + .plv-live-cover__btn { + display: none !important; + } + + // .plv-cover-box { + // background-color: rgb(33, 33, 33); + // } + + // 隐藏控制栏的渐变背景 + .plv-player-skin { + display: none; + } + + .plv-live-panel { + background-color: rgba(0, 0, 0, 0) !important; + } + + // 隐藏暖场图 + .plv-live-warmUp { + display: none; + } + + // ------ 点播 ------ // + .plv-player-big-play-btn { + display: none !important; + } + + // ------ 快直播 ------ // + // 覆盖快直播背景图 + .vcp-player { + background: none !important; + } +} diff --git a/src/components/page-watch-common/player/player-main/portrait-player-main/portrait-player-main.vue b/src/components/page-watch-common/player/player-main/portrait-player-main/portrait-player-main.vue new file mode 100644 index 0000000000000000000000000000000000000000..71dd9f38aebe4811a470cf758f6377a1e14dcbcc --- /dev/null +++ b/src/components/page-watch-common/player/player-main/portrait-player-main/portrait-player-main.vue @@ -0,0 +1,189 @@ + + + + + + diff --git a/src/components/page-watch-common/player/player-main/portrait-player-main/use-portrait-player-size.ts b/src/components/page-watch-common/player/player-main/portrait-player-main/use-portrait-player-size.ts new file mode 100644 index 0000000000000000000000000000000000000000..aa3e4cdd951c01a9522a3043ad0f7d9ea7bc75ea --- /dev/null +++ b/src/components/page-watch-common/player/player-main/portrait-player-main/use-portrait-player-size.ts @@ -0,0 +1,156 @@ +import { usePageStore } from '@/store/use-page-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { usePortraitLayoutStore } from '@/store/use-portrait-layout-store'; +import { computed, unref } from 'vue'; +import { CSSProperties } from 'vue/types/jsx'; + +export interface PlayerContainerSize { + width: number; + height: number; + top: number; + left: number; +} + +export interface PlayerSizeData { + width?: number; + height?: number; +} + +/** + * 获取视频区域高度 + */ +function getVideoAreaHeight() { + const pageStore = usePageStore(); + const topStopHeight = 51; + const bottomStopHeight = 218; + + return { + /** 顶部阻挡区域高度 */ + topStopHeight, + /** 底部阻挡区域高度 */ + bottomStopHeight, + /** 视频区域高度 */ + videoAreaHeight: pageStore.pageClientHeight - topStopHeight - bottomStopHeight, + }; +} + +/** + * 计算纯直播下的播放器的尺寸位置 + */ +export function computedPlayerSize(targetSize: PlayerSizeData): PlayerContainerSize | undefined { + const pageStore = usePageStore(); + + const { width: targetWidth, height: targetHeight } = targetSize; + + const pageClientWidth = pageStore.pageClientWidth; + const pageClientHeight = pageStore.pageClientHeight; + + if (!targetWidth || !targetHeight) { + return; + } + + const { topStopHeight, videoAreaHeight } = getVideoAreaHeight(); + + let sizeData: PlayerContainerSize | undefined; + + // 视频宽度占满屏宽的等比高度 + const _videoHeight = (targetHeight / targetWidth) * pageClientWidth; + // 视频高度占满屏高的等比宽度 + const _videoWidth = (targetWidth / targetHeight) * pageClientHeight; + + // 等比高度超出屏幕高度,宽度占满,上下裁边 + if (_videoHeight >= pageClientHeight) { + sizeData = { + width: pageClientWidth, + height: _videoHeight, + top: (pageClientHeight - _videoHeight) / 2, + left: 0, + }; + return sizeData; + } + + // 等比高度小于视频区域高度,宽度占满,视频区域内上下居中 + if (_videoHeight <= videoAreaHeight) { + sizeData = { + width: pageClientWidth, + height: _videoHeight, + top: (videoAreaHeight - _videoHeight) / 2 + topStopHeight, + left: 0, + }; + return sizeData; + } + + // 高度占满,左右裁边 + sizeData = { + width: _videoWidth, + height: pageClientHeight, + top: 0, + left: (pageClientWidth - _videoWidth) / 2, + }; + + return sizeData; +} + +/** + * 计算显示文档下播放器的尺寸位置 + */ +export function computedDocPlayerSize(targetSize: PlayerSizeData): PlayerContainerSize | undefined { + const pageStore = usePageStore(); + + const { width: targetWidth, height: targetHeight } = targetSize; + + const pageClientWidth = pageStore.pageClientWidth; + + if (!targetWidth || !targetHeight) { + return; + } + + // 视频宽度占满屏宽的等比高度 + const _videoHeight = (targetHeight / targetWidth) * pageClientWidth; + + return { + width: pageClientWidth, + height: _videoHeight, + top: 0, + left: 0, + }; +} + +/** + * 竖屏播放器尺寸 hook + */ +export const usePortraitPlayerSize = (targetSize?: ResponsiveRef) => { + const playerStore = usePlayerStore(); + const portraitLayoutStore = usePortraitLayoutStore(); + + /** 播放器容器样式 */ + const playerContainerStyle = computed(() => { + const styles: CSSProperties = {}; + + let sizeData: PlayerContainerSize | undefined; + + const targetSizeVal: PlayerSizeData = unref(targetSize) || { + width: playerStore.resolutionWidth, + height: playerStore.resolutionHeight, + }; + + if (portraitLayoutStore.portraitDocWrapHeight !== 0) { + sizeData = computedDocPlayerSize(targetSizeVal); + } else { + sizeData = computedPlayerSize(targetSizeVal); + } + + if (sizeData) { + styles.width = `${sizeData.width}px`; + styles.height = `${sizeData.height}px`; + styles.top = `${sizeData.top}px`; + styles.left = `${sizeData.left}px`; + } + + return styles; + }); + + return { + playerContainerStyle, + }; +}; diff --git a/src/components/page-watch-common/send-redpack/_hooks/use-send-redpack.ts b/src/components/page-watch-common/send-redpack/_hooks/use-send-redpack.ts new file mode 100644 index 0000000000000000000000000000000000000000..0940ad2890053ca300553571392256f2a00e1e62 --- /dev/null +++ b/src/components/page-watch-common/send-redpack/_hooks/use-send-redpack.ts @@ -0,0 +1,170 @@ +import { useWeixinPay } from '@/hooks/platform/use-weixin/use-weixin-pay'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { useSendRedpackStore } from '@/store/use-send-redpack-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { computed, reactive, unref } from 'vue'; +import { SubmitSendRedpackParams, WatchDomainPayType } from '@polyv/live-watch-sdk'; +import { usePolyvWatchDomain } from '@/hooks/platform/use-polyv-watch-domain'; + +/** "发送红包"表单类型 */ +interface SendRedpackFormData { + /** 红包金额 */ + amount: number | string; + /** 红包个数 */ + count: number | string; + /** 祝福语 */ + greeting: string; +} + +export const useSendRedpackHook = ( + options: { + successCallback?: () => unknown; + } = {}, +) => { + const { polyvWatchDomainEnabled, skipWatchDomainPay } = usePolyvWatchDomain(); + const { chooseWXPay } = useWeixinPay(); + const watchCore = getWatchCore(); + const { redPackViewerMaxAmount, redPackViewerMaxCount, redPackViewerMinAverage } = + storeDefinitionToRefs(useSendRedpackStore); + + /** "发送红包"表单数据 */ + const formData = reactive({ + amount: '', + count: '', + greeting: '', + }); + + /** 表单规则 */ + // eslint-disable-next-line sonarjs/cognitive-complexity + const formRules = computed(() => { + return { + amount: [ + { + validator: (rule, val: number | string) => { + let errorMsg: string | undefined; + // 必填判断 + if (val === '') { + errorMsg = translate('redpack.send.sum.placeholder'); + } + + // 总金额不得小于 1 + if (val < 1) { + errorMsg = translate('redpack.send.amount.low'); + } + + // 最大值判断 + if (val > unref(redPackViewerMaxAmount)) { + errorMsg = translate('redpack.single.amount.limit', { + amount: `${unref(redPackViewerMaxAmount)}`, + }); + } + + // 均分判断 + const average = getCurrentAverage(); + if (formData.amount && formData.count && average < unref(redPackViewerMinAverage)) { + errorMsg = translate('redpack.equal.amount.limit', { + average: `${unref(redPackViewerMinAverage)}`, + }); + } + + if (errorMsg) { + return [errorMsg]; + } + + return true; + }, + }, + ], + count: [ + { + validator: (rule, val: number | string) => { + let errorMsg: string | undefined; + // 必填判断 + if (val === '') { + errorMsg = translate('redpack.send.num.placeholder'); + } + + // 最大值判断 + if (val > unref(redPackViewerMaxCount)) { + errorMsg = translate('redpack.single.num.limit', { + count: `${unref(redPackViewerMaxCount)}`, + }); + } + + // 均分判断 + const average = getCurrentAverage(); + if (formData.amount && formData.count && average < unref(redPackViewerMinAverage)) { + errorMsg = translate('redpack.equal.amount.limit', { + average: `${unref(redPackViewerMinAverage)}`, + }); + } + + if (errorMsg) { + return [errorMsg]; + } + + return true; + }, + }, + ], + }; + }); + + /** 获取当前的红包均分金额 */ + function getCurrentAverage() { + return Number(formData.amount) / Number(formData.count); + } + + /** 提交表单 */ + async function submitForm() { + const params: SubmitSendRedpackParams = { + amount: Number(formData.amount), + count: Number(formData.count), + greeting: formData.greeting, + }; + + // 泛域名支付 + if (unref(polyvWatchDomainEnabled)) { + skipWatchDomainPay({ + payType: WatchDomainPayType.Redpack, + params, + }); + return; + } + + const result = await watchCore.redpack.submitSendRedpack(params); + + if (result.success) { + chooseWXPay(result.wxPaySignData, { + successCb: () => { + toast.success(translate('weixin.pay.success')); + }, + }); + + options.successCallback && options.successCallback(); + } else { + const failMessage = result.failMessage; + if (failMessage) { + toast.error(failMessage); + } + } + } + + /** 初始化发送红包数据 */ + function initSendRedpackData() { + formData.amount = ''; + formData.count = ''; + formData.greeting = ''; + } + + return { + formData, + formRules, + + submitForm, + initSendRedpackData, + }; +}; diff --git a/src/components/page-watch-common/send-redpack/mobile-send-redpack.vue b/src/components/page-watch-common/send-redpack/mobile-send-redpack.vue new file mode 100644 index 0000000000000000000000000000000000000000..14780ba69f5514e38a954f33d5737928a9cb804a --- /dev/null +++ b/src/components/page-watch-common/send-redpack/mobile-send-redpack.vue @@ -0,0 +1,129 @@ + + + + + + diff --git a/src/components/page-watch-common/withdraw/_hooks/use-receive-record.ts b/src/components/page-watch-common/withdraw/_hooks/use-receive-record.ts new file mode 100644 index 0000000000000000000000000000000000000000..002c2d2216bebf61e6f907d1e32a9c9f66d1e420 --- /dev/null +++ b/src/components/page-watch-common/withdraw/_hooks/use-receive-record.ts @@ -0,0 +1,109 @@ +import { DefineComponent, ref, shallowRef } from 'vue'; +import { formatDate } from '@utils-ts/date'; +import { PageContent } from '@polyv/live-watch-sdk'; +import { toast } from '@/hooks/components/use-toast'; +import { getWatchCore } from '@/core/watch-sdk'; +import { DATE_FORMAT_SLASH } from '@/assets/constants/date-format'; + +export const useReceiveRecordHook = (hookOption: { + fetchMethod: ({ + pageNumber, + }: { + pageNumber: number; + [key: string]: unknown; + }) => Promise>; + fetchMethodParams?: UniversalParams; + errMsg: string; +}) => { + const { fetchMethod, fetchMethodParams = {}, errMsg = 'ReceiveRecordError' } = hookOption; + + const loadingVisible = ref(false); + + /** 没有更多了/无数据 */ + const isNotMore = ref(false); + + /** 领取记录列表 */ + const receiveRecordList = shallowRef([]); + + /** 总页数 */ + let totalPages = 1; + + /** 当前页数 */ + let pageNumber = 0; + + /** 转换领取日期 */ + function convertReceiveDate(ts: string | number) { + return formatDate(new Date(ts), DATE_FORMAT_SLASH); + } + + /** 转换金额,转成保留 2 位小数的数字字符串 */ + function convertAmount(val: number) { + let amount = String(Math.round(val * 100) / 100); + + const xsd = amount.split('.'); + if (xsd.length === 1) { + return `${amount}.00`; + } + + if (xsd.length > 1) { + if (xsd[1].length < 2) { + amount = `${amount}0`; + } + return amount; + } + + return amount; + } + + /** 获取领取记录 */ + async function getReceiveRecordList() { + if (typeof fetchMethod !== 'function') return; + + if (loadingVisible.value || isNotMore.value) { + return; + } + + if (totalPages <= pageNumber) { + isNotMore.value = true; + return; + } + + loadingVisible.value = true; + try { + const pageResult = await fetchMethod.bind(getWatchCore())({ + pageNumber: pageNumber + 1, + ...fetchMethodParams, + }); + + receiveRecordList.value = receiveRecordList.value.concat(pageResult.contents); + totalPages = pageResult.totalPages; + pageNumber = pageResult.pageNumber; + } catch (e) { + console.error(e); + toast.error(errMsg); + } finally { + loadingVisible.value = false; + } + } + + return { + loadingVisible, + isNotMore, + receiveRecordList, + + convertReceiveDate, + convertAmount, + /** 组件会在 setup 阶段 expose 该方法 */ + getReceiveRecordList, + }; +}; + +type ReceiveRecordInstanceExpose = Pick< + ReturnType, + 'getReceiveRecordList' +>; + +/** 领取记录组件实例类型 */ +export type ReceiveRecordInstance = InstanceType< + DefineComponent, ReceiveRecordInstanceExpose> +>; diff --git a/src/components/page-watch-common/withdraw/_hooks/use-withdraw-apply.ts b/src/components/page-watch-common/withdraw/_hooks/use-withdraw-apply.ts new file mode 100644 index 0000000000000000000000000000000000000000..2fdf12944e240f245b4dbe859c14c1491392b2f1 --- /dev/null +++ b/src/components/page-watch-common/withdraw/_hooks/use-withdraw-apply.ts @@ -0,0 +1,178 @@ +import { computed, ref, unref, reactive } from 'vue'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { useWithdraw } from '@/components/page-watch-common/withdraw/use-withdraw'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ValidatorRules } from '@/plugins/async-validator'; +import { WeixinPayType } from '@polyv/live-watch-sdk'; + +/** 历史提现统计错误响应 */ +export function handleStatisticsErr(e: Error) { + switch (e.message) { + case 'blank_openid': + return translate('weixin.error.openInClient'); + case 'user_not_found': + return translate('withdraw.error.noUser'); + default: + return 'ERROR7: ' + e.message; + } +} + +export const useWithdrawApplyHook = () => { + const watchCore = getWatchCore(); + const { weixinPayType, freePayAmount } = useWithdraw(); + + /** 提现表单数据 */ + const formData = reactive<{ + amount: string | number; + }>({ + amount: '', + }); + + /** 个人可提现金额 */ + const totalAmount = ref(0); + + /** 商家提现是否大于自定义限额 */ + const isMerchantAmount = computed(() => { + return ( + unref(weixinPayType) === WeixinPayType.Merchant && unref(totalAmount) > unref(freePayAmount) + ); + }); + + /** 表单规则 */ + const formRules = computed(() => { + return { + amount: [ + { + required: true, + message: translate('withdraw.apply.rules.amount.required'), + }, + { + validator: (rule, val: number | string) => { + let errorMsg: string | undefined; + + // 最小值判断,总金额不得小于 1 + if (val < 1) { + errorMsg = translate('withdraw.apply.rules.amount.low'); + } + + // 最大值判断 + if (unref(isMerchantAmount)) { + if (Number(formData.amount) > unref(freePayAmount)) { + errorMsg = translate('withdraw.apply.amount.most.merchant', { + amount: String(unref(freePayAmount)), + }); + } + } else { + if (Number(formData.amount) > unref(totalAmount)) { + errorMsg = translate('withdraw.apply.amount.most', { + amount: String(unref(totalAmount)), + }); + } + } + + const reg = /^\d+(\.\d{0,2})?$/; + const matchResult = `${val}`.match(reg); + if (matchResult === null) { + errorMsg = '提现金额为小数位最多两位的数字'; + } + + if (errorMsg) { + return [errorMsg]; + } + + return true; + }, + }, + ], + }; + }); + + /** 提现金额输入框-默认展示 */ + const amountInputPlaceHolder = computed(() => { + if (unref(weixinPayType) === WeixinPayType.Merchant) { + return translate('withdraw.apply.amount.most.merchant', { + amount: String(freePayAmount.value), + }); + } else { + return translate('withdraw.apply.amount.most', { + amount: String(totalAmount.value), + }); + } + }); + + /** 提示语 */ + const withdrawApplyTips = computed(() => + translate('withdraw.apply.tips', { amount: `1`, day: '3' }), + ); + + /** 设置最大提现额 */ + function setMaxAmount() { + formData.amount = unref(isMerchantAmount) ? unref(freePayAmount) : unref(totalAmount); + } + + /** 获取总金额数 */ + async function setTotalAmount() { + try { + const result = await watchCore.withdraw.getUserStatistics(); + totalAmount.value = result.totalAmount; + } catch (error) { + const errMsg = handleStatisticsErr(error as Error); + toast.error(errMsg); + } + } + + /** 提交表单 */ + async function submitForm() { + const result = await watchCore.withdraw.applyWithdraw({ + amount: Number(formData.amount), + }); + if (result.success) { + toast.success(translate('withdraw.apply.success.msg')); + } else { + throw new Error(handleApplyErr(result.failMessage)); + } + } + + /** 提现相关接口错误返回异常时的错误提示 */ + function handleApplyErr(errMessage?: string) { + switch (errMessage) { + case 'user_not_found': + return translate('withdraw.error.noUser'); + case 'amount_over_limit': + return translate('withdraw.error.getCashExceed'); + case 'set_data_fail': + return translate('withdraw.error.getCashFailed'); + case 'blank_openid': + return translate('weixin.error.openInClient'); + case 'invalid_amount': + default: + return errMessage; + // return translate('withdraw.error.getCashFailed'); + } + } + + /** 初始化-申请提现数据 */ + async function initWithdrawApply() { + resetWithdrawApply(); + await setTotalAmount(); + } + + /** 重置-申请提现数据 */ + function resetWithdrawApply() { + formData.amount = ''; + } + + return { + formData, + formRules, + amountInputPlaceHolder, + withdrawApplyTips, + + setMaxAmount, + submitForm, + + initWithdrawApply, + resetWithdrawApply, + }; +}; diff --git a/src/components/page-watch-common/withdraw/_hooks/use-withdraw-detail.ts b/src/components/page-watch-common/withdraw/_hooks/use-withdraw-detail.ts new file mode 100644 index 0000000000000000000000000000000000000000..0cc53ef2a3b14dd8630e81c35cb59b281c90c967 --- /dev/null +++ b/src/components/page-watch-common/withdraw/_hooks/use-withdraw-detail.ts @@ -0,0 +1,116 @@ +import debounce from 'lodash-es/debounce'; +import { ref } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { handleStatisticsErr } from './use-withdraw-apply'; +import { ReceiveRecordInstance } from './use-receive-record'; + +export enum WithdrawDetailTab { + /** 红包领取记录/历史红包领取 */ + RedpackReceiveRecord = 'redpackReceiveRecord', + /** 提现记录/历史提现 */ + CashReceiveRecord = 'cashReceiveRecord', +} + +export const useWithdrawDetailHook = () => { + /** 个人领取总金额 */ + const totalReceive = ref(0); + + /** 个人提现总金额 */ + const totalWithdraw = ref(0); + + /** 个人可提现金额 */ + const totalAmount = ref(0); + + /** 当前 tab 值 */ + const curTab = ref(''); + + /** 详情页 Tab 数据 */ + const detailTabs = ref([ + { + label: translate('withdraw.redpack.receive.record'), + value: WithdrawDetailTab.RedpackReceiveRecord, + }, + { + label: translate('withdraw.cash.receive.record'), + value: WithdrawDetailTab.CashReceiveRecord, + }, + ]); + + /** 获取历史提现统计 */ + async function getWithdrawStatistics() { + const watchCore = getWatchCore(); + + try { + const statisticsData = await watchCore.withdraw.getUserStatistics(); + + totalReceive.value = statisticsData.totalReceive; + totalWithdraw.value = statisticsData.totalWithdraw; + totalAmount.value = statisticsData.totalAmount; + } catch (error) { + const errMsg = handleStatisticsErr(error as Error); + toast.error(errMsg); + } + } + + /** 初始化-提现详情 */ + async function initWithdrawDetail() { + resetWithdrawDetail(); + + curTab.value = WithdrawDetailTab.RedpackReceiveRecord; + await getWithdrawStatistics(); + } + + /** 重置-提现详情 */ + function resetWithdrawDetail() { + curTab.value = ''; + totalReceive.value = 0; + totalWithdraw.value = 0; + totalAmount.value = 0; + } + + /** 红包领取记录组件实例 */ + const redpackReceiveRecordRef = ref(); + /** 提现领取记录组件实例 */ + const cashReceiveRecordRef = ref(); + + /** 获取当前 Tab 对应的领取记录组件实例 */ + function getCurReceiveRecordRef() { + if (curTab.value === WithdrawDetailTab.RedpackReceiveRecord) { + return redpackReceiveRecordRef; + } + if (curTab.value === WithdrawDetailTab.CashReceiveRecord) { + return cashReceiveRecordRef; + } + } + + /** 提现详情页滚动监听钩子 */ + function handleDetailMainScroll(e: UIEvent) { + const target = e.target as HTMLElement; + const scrollHeight = Math.floor(target.scrollHeight); + const scrollTop = Math.floor(target.scrollTop); + const clientHeight = Math.floor(target.clientHeight); + if (scrollTop >= scrollHeight - clientHeight - 100) { + const curReceiveRecordRef = getCurReceiveRecordRef(); + curReceiveRecordRef?.value?.getReceiveRecordList(); + } + } + const debounceHandleDetailMainScroll = debounce(handleDetailMainScroll, 300); + + return { + totalAmount, + totalReceive, + totalWithdraw, + curTab, + detailTabs, + + getWithdrawStatistics, + initWithdrawDetail, + resetWithdrawDetail, + + redpackReceiveRecordRef, + cashReceiveRecordRef, + debounceHandleDetailMainScroll, + }; +}; diff --git a/src/components/page-watch-common/withdraw/mobile-withdraw-apply/mobile-withdraw-apply.vue b/src/components/page-watch-common/withdraw/mobile-withdraw-apply/mobile-withdraw-apply.vue new file mode 100644 index 0000000000000000000000000000000000000000..f0974661f82a42f5b4117bd73a6353825854a48b --- /dev/null +++ b/src/components/page-watch-common/withdraw/mobile-withdraw-apply/mobile-withdraw-apply.vue @@ -0,0 +1,168 @@ + + + + + + diff --git a/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-cash-receive-record.vue b/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-cash-receive-record.vue new file mode 100644 index 0000000000000000000000000000000000000000..c7c203fc59cc78149e66f27db744a9ac55e7dad4 --- /dev/null +++ b/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-cash-receive-record.vue @@ -0,0 +1,83 @@ + + + + + + diff --git a/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-redpack-receive-record.vue b/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-redpack-receive-record.vue new file mode 100644 index 0000000000000000000000000000000000000000..26851524a86b549da19b8915800c1af416dc1838 --- /dev/null +++ b/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-redpack-receive-record.vue @@ -0,0 +1,93 @@ + + + + + + diff --git a/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-withdraw-detail.vue b/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-withdraw-detail.vue new file mode 100644 index 0000000000000000000000000000000000000000..18ef4c6b9bc5c0212305f911f8f76e112eb14d15 --- /dev/null +++ b/src/components/page-watch-common/withdraw/mobile-withdraw-detail/mobile-withdraw-detail.vue @@ -0,0 +1,214 @@ + + + + + + diff --git a/src/components/page-watch-common/withdraw/portrait-withdraw-apply/portrait-withdraw-apply.vue b/src/components/page-watch-common/withdraw/portrait-withdraw-apply/portrait-withdraw-apply.vue new file mode 100644 index 0000000000000000000000000000000000000000..7d7c95e859941a37ee5d88b4e2a050db02bd4a33 --- /dev/null +++ b/src/components/page-watch-common/withdraw/portrait-withdraw-apply/portrait-withdraw-apply.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-cash-receive-record.vue b/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-cash-receive-record.vue new file mode 100644 index 0000000000000000000000000000000000000000..3379af52a169b5e4adc9451693de0401f4fb0540 --- /dev/null +++ b/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-cash-receive-record.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-redpack-receive-record.vue b/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-redpack-receive-record.vue new file mode 100644 index 0000000000000000000000000000000000000000..cea56ff6d3b0a1b3a0ac7a740f36ee6235f9d8cd --- /dev/null +++ b/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-redpack-receive-record.vue @@ -0,0 +1,58 @@ + + + diff --git a/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-withdraw-detail.vue b/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-withdraw-detail.vue new file mode 100644 index 0000000000000000000000000000000000000000..7a3c8907005c0cac0fc1dab7df21801cf9b116fa --- /dev/null +++ b/src/components/page-watch-common/withdraw/portrait-withdraw-detail/portrait-withdraw-detail.vue @@ -0,0 +1,182 @@ + + + + + diff --git a/src/components/page-watch-common/withdraw/use-withdraw.ts b/src/components/page-watch-common/withdraw/use-withdraw.ts new file mode 100644 index 0000000000000000000000000000000000000000..c62e22b2040919b4c2ffeb07f64264efd7e7674c --- /dev/null +++ b/src/components/page-watch-common/withdraw/use-withdraw.ts @@ -0,0 +1,45 @@ +import { computed, unref } from 'vue'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { ynToBool } from '@utils-ts/boolean'; +import { YN, WeixinPayType } from '@polyv/live-watch-sdk'; + +/** + * @hook 提现 + * */ +export const useWithdraw = () => { + const { channelDetail } = storeDefinitionToRefs(useChannelStore); + const { weixinAccountFunctionEnabled, weixinPayEnabled } = storeDefinitionToRefs(useWeixinStore); + + /** 提现开关 */ + const withdrawEnabled = computed(() => { + // 后台开关 - 是否关闭提现 + const backstageSwitch = ynToBool( + unref(channelDetail)?.channelConfig.closeWithdrawEnabled, + YN.Y, + ); + // 超管微信功能开关 + const superSwitch = unref(weixinAccountFunctionEnabled); + // 超管微信支付开关 + const superPaySwitch = unref(weixinPayEnabled); + + return !backstageSwitch && superSwitch && superPaySwitch; + }); + + /** 微信支付类型 */ + const weixinPayType = computed(() => { + return channelDetail.value?.channelConfig.weixinPayType ?? WeixinPayType.Enterprise; + }); + + /** 最大提现额度 */ + const freePayAmount = computed(() => { + return channelDetail.value?.channelConfig.freePayAmount || 0; + }); + + return { + withdrawEnabled, + weixinPayType, + freePayAmount, + }; +}; diff --git a/src/core/bind-sdk-events/bind-channel-events.ts b/src/core/bind-sdk-events/bind-channel-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce075448997e8ae7129c150a945637b15c2ff49f --- /dev/null +++ b/src/core/bind-sdk-events/bind-channel-events.ts @@ -0,0 +1,19 @@ +import { ChannelEvents, MenuEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useChannelMenuStore } from '@/store/use-channel-menu-store'; +import { useChannelStore } from '@/store/use-channel-store'; + +export function bindChannelModuleEvents(): void { + const channelStore = useChannelStore(); + const ChannelMenuStore = useChannelMenuStore(); + + const watchCore = getWatchCore(); + const channelEventEmitter = watchCore.channel.eventEmitter; + + channelEventEmitter.on(ChannelEvents.LiveStatusChange, channelStore.syncChannelStore); + channelEventEmitter.on(ChannelEvents.SessionIdChange, channelStore.syncChannelStore); + + const menuEventEmitter = watchCore.menu.eventEmitter; + menuEventEmitter.on(MenuEvents.ChannelMenuChange, ChannelMenuStore.syncChannelMenus); +} diff --git a/src/core/bind-sdk-events/bind-chat-events.ts b/src/core/bind-sdk-events/bind-chat-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..657ca5a0a59eeccc5e619ca4d36e670c9631155a --- /dev/null +++ b/src/core/bind-sdk-events/bind-chat-events.ts @@ -0,0 +1,33 @@ +import { redirectToErrorVerify } from '@/hooks/core/use-error-verify'; +import { useChatStore } from '@/store/use-chat-store'; +import { ChatEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '../watch-sdk'; + +/** + * 监听聊天室模块事件 + */ +export function bindChatModuleEvents(): void { + const watchCore = getWatchCore(); + const chatStore = useChatStore(); + + // 聊天室连接失败 + watchCore.chat.eventEmitter.on(ChatEvents.ChatConnectFail, () => { + chatStore.connectError = true; + }); + + watchCore.chat.eventEmitter.on(ChatEvents.ChatStoreInfoChange, () => { + chatStore.syncChatStore(); + }); + + // 重复登录,页面跳走 + watchCore.chat.eventEmitter.on(ChatEvents.CurrentUserRelogin, () => { + redirectToErrorVerify(); + }); + + // 点赞数修改 + watchCore.chat.eventEmitter.on(ChatEvents.ChatLikeCountChange, data => { + chatStore.$patch({ + realtimeLikes: data.realtimeLikes, + }); + }); +} diff --git a/src/core/bind-sdk-events/bind-connect-mic-events.ts b/src/core/bind-sdk-events/bind-connect-mic-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..163be98b4e0698c6cc46d3e019e924f74fd708b2 --- /dev/null +++ b/src/core/bind-sdk-events/bind-connect-mic-events.ts @@ -0,0 +1,16 @@ +import { useConnectMicStore } from '@/store/use-connect-mic-store'; +import { ConnectMicEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '../watch-sdk'; + +export function bindConnectMicModuleEvents(): void { + const watchCore = getWatchCore(); + const connectMicStore = useConnectMicStore(); + + watchCore.connectMic.eventEmitter.on(ConnectMicEvents.ConnectMicStoreInfoChanged, () => { + connectMicStore.syncConnectMicInfo(); + }); + + watchCore.connectMic.eventEmitter.on(ConnectMicEvents.ConnectMicListChange, () => { + connectMicStore.syncConnectMicList(); + }); +} diff --git a/src/core/bind-sdk-events/bind-enroll-events.ts b/src/core/bind-sdk-events/bind-enroll-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..420dd3df6ca378e807e25f541cbe474d705304aa --- /dev/null +++ b/src/core/bind-sdk-events/bind-enroll-events.ts @@ -0,0 +1,12 @@ +import { useEnrollStore } from '@/store/use-enroll-store'; +import { EnrollEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '../watch-sdk'; + +export function bindEnrollModuleEvents(): void { + const watchCore = getWatchCore(); + const enrollStore = useEnrollStore(); + + watchCore.enroll.eventEmitter.on(EnrollEvents.EnrollStoreInfoChanged, () => { + enrollStore.syncEnrollInfo(); + }); +} diff --git a/src/core/bind-sdk-events/bind-finance-risk-evaluation-events.ts b/src/core/bind-sdk-events/bind-finance-risk-evaluation-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e56553a8aa6b7d14ab3c638d4065c0414ed9ed6 --- /dev/null +++ b/src/core/bind-sdk-events/bind-finance-risk-evaluation-events.ts @@ -0,0 +1,18 @@ +import { FinanceRiskEvaluationEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useRiskEvaluationResetHook } from '@/components/page-watch-common/interactive-receive/risk-evaluation/use-risk-evaluation-reset'; + +/** + * 绑定金融风险测评模块事件 + */ +export function bindFinanceRiskEvaluationEvents() { + const watchCore = getWatchCore(); + + const { onRiskEvaluationReset } = useRiskEvaluationResetHook(); + + // 金融风险测评-重置 + watchCore.financeRiskEvaluation.eventEmitter.on( + FinanceRiskEvaluationEvents.Reset, + onRiskEvaluationReset, + ); +} diff --git a/src/core/bind-sdk-events/bind-interact-receive-events.ts b/src/core/bind-sdk-events/bind-interact-receive-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..58230967c4de6555c91c2c57dee5d83cf768ffc5 --- /dev/null +++ b/src/core/bind-sdk-events/bind-interact-receive-events.ts @@ -0,0 +1,15 @@ +import { useInteractReceiveStore } from '@/store/use-interact-receive-store'; +import { InteractReceiveEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; + +export function bindInteractReceiveEvents() { + const watchCore = getWatchCore(); + const interactReceiveStore = useInteractReceiveStore(); + + /** + * 投票 id 列表 + */ + watchCore.interactReceive.eventEmitter.on(InteractReceiveEvents.VotedListChange, data => { + interactReceiveStore.votedList = data.votedList; + }); +} diff --git a/src/core/bind-sdk-events/bind-invite-events.ts b/src/core/bind-sdk-events/bind-invite-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..4bcfa22b4744972622116f7baef9399d21a79348 --- /dev/null +++ b/src/core/bind-sdk-events/bind-invite-events.ts @@ -0,0 +1,10 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useInviteStore } from '@/store/use-invite-store'; +import { InviteEvents } from '@polyv/live-watch-sdk'; + +export function bindInviteEvents() { + const inviteStore = useInviteStore(); + const watchCore = getWatchCore(); + + watchCore.invite.eventEmitter.on(InviteEvents.InviteUserInfoUpdate, inviteStore.syncInviteInfo); +} diff --git a/src/core/bind-sdk-events/bind-player-events.ts b/src/core/bind-sdk-events/bind-player-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f8c7d1a18a603fca91028a6d0cee61b6e48fe2e --- /dev/null +++ b/src/core/bind-sdk-events/bind-player-events.ts @@ -0,0 +1,37 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { useChannelStore } from '@/store/use-channel-store'; +import { usePlayerStore } from '@/store/use-player-store'; +import { MarqueeData, PlayerEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; + +export function bindPlayerModuleEvents(): void { + const watchCore = getWatchCore(); + const playerStore = usePlayerStore(); + const channelStore = useChannelStore(); + + watchCore.player.eventEmitter.on(PlayerEvents.PlayerStoreInfoChanged, () => { + playerStore.syncPlayerInfo(); + }); + + /** 无延迟错误,自动切回 CDN 播放器 */ + watchCore.player.eventEmitter.on(PlayerEvents.LowLatencyError, (evt: { errorCode: string }) => { + toast.error(`${evt.errorCode} ${translate('player.latency.error')}`); + + playerStore.$patch({ + isLowLatency: false, + }); + eventBus.$emit(appEvents.player.ResetUpPlayer); + }); + + /** 设置跑马灯数据 */ + watchCore.player.eventEmitter.on( + PlayerEvents.MarqueeDataUpdate, + (evt: { marqueeData: MarqueeData }) => { + channelStore.$patch({ + marqueeData: evt.marqueeData, + }); + }, + ); +} diff --git a/src/core/bind-sdk-events/bind-user-events.ts b/src/core/bind-sdk-events/bind-user-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..275f41ba8da971349c30953670c5ead528ddaffb --- /dev/null +++ b/src/core/bind-sdk-events/bind-user-events.ts @@ -0,0 +1,10 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { UserEvents } from '@polyv/live-watch-sdk'; +import { useViewerStore } from '@/store/use-viewer-store'; + +export function bindUserEvents() { + const viewerStore = useViewerStore(); + const watchCore = getWatchCore(); + + watchCore.user.eventEmitter.on(UserEvents.UserInfoChange, viewerStore.syncViewerInfo); +} diff --git a/src/core/bind-sdk-events/bind-watch-core-events.ts b/src/core/bind-sdk-events/bind-watch-core-events.ts new file mode 100644 index 0000000000000000000000000000000000000000..27b268473591ec535536f4cad8eed62c45cb7deb --- /dev/null +++ b/src/core/bind-sdk-events/bind-watch-core-events.ts @@ -0,0 +1,30 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { PolyvWatchCoreEvents } from '@polyv/live-watch-sdk'; + +import { useChannelStore } from '@/store/use-channel-store'; +import { useFinanceStore } from '@/store/use-finance-store'; + +export function bindWatchCoreEvents(): void { + const watchCore = getWatchCore(); + + const channelStore = useChannelStore(); + const financeStore = useFinanceStore(); + + // 观看核心已安装 + watchCore.eventEmitter.on(PolyvWatchCoreEvents.WatchCoreSetuped, () => { + channelStore.syncChannelDetail(); + + financeStore.initRiskConfirm(); + financeStore.initRiskEvaluation(); + }); + + // 观看核心已连接聊天室 + watchCore.eventEmitter.on(PolyvWatchCoreEvents.WatchCoreConnected, () => { + console.log('观看核心已连接聊天室'); + }); + + // 观看核心已销毁 + watchCore.eventEmitter.on(PolyvWatchCoreEvents.WatchCoreDestroyed, () => { + console.log('观看核心已销毁'); + }); +} diff --git a/src/core/watch-sdk.ts b/src/core/watch-sdk.ts new file mode 100644 index 0000000000000000000000000000000000000000..62920e1cc63e9ab0e737ec54ef8e8d9ceff56d39 --- /dev/null +++ b/src/core/watch-sdk.ts @@ -0,0 +1,72 @@ +import { AppConfig, PolyvWatchCore, createWatchCore } from '@polyv/live-watch-sdk'; +import { isSpecialUserType } from '@/assets/constants/special-user-types'; +import { useChatStore } from '@/store/use-chat-store'; +import { useViewerStore } from '@/store/use-viewer-store'; + +import { bindWatchCoreEvents } from '@/core/bind-sdk-events/bind-watch-core-events'; +import { bindChannelModuleEvents } from '@/core/bind-sdk-events/bind-channel-events'; +import { bindChatModuleEvents } from '@/core/bind-sdk-events/bind-chat-events'; +import { bindConnectMicModuleEvents } from '@/core/bind-sdk-events/bind-connect-mic-events'; +import { bindEnrollModuleEvents } from '@/core/bind-sdk-events/bind-enroll-events'; +import { bindInteractReceiveEvents } from '@/core/bind-sdk-events/bind-interact-receive-events'; +import { bindInviteEvents } from '@/core/bind-sdk-events/bind-invite-events'; +import { bindPlayerModuleEvents } from '@/core/bind-sdk-events/bind-player-events'; +import { bindUserEvents } from '@/core/bind-sdk-events/bind-user-events'; +import { bindFinanceRiskEvaluationEvents } from '@/core/bind-sdk-events/bind-finance-risk-evaluation-events'; + +/** 观看页核心模块实例 */ +let watchCore: PolyvWatchCore | undefined; + +/** 获取观看页核心模块实例 */ +export function getWatchCore(): PolyvWatchCore { + if (!watchCore) { + throw new Error('watchCore in not define!'); + } + return watchCore; +} + +/** 初始化观看页应用对象 */ +export function initWatchSdk(appConfig: AppConfig): PolyvWatchCore { + if (watchCore) { + return watchCore; + } + const chatStore = useChatStore(); + const viewerStore = useViewerStore(); + + watchCore = createWatchCore(appConfig); + + // 绑定 SDK 相关事件的监听回调 + bindWatchCoreEvents(); + bindChannelModuleEvents(); + bindChatModuleEvents(); + bindConnectMicModuleEvents(); + bindEnrollModuleEvents(); + bindInteractReceiveEvents(); + bindInviteEvents(); + bindPlayerModuleEvents(); + bindUserEvents(); + bindFinanceRiskEvaluationEvents(); + + // 设置弹幕添加拦截器,如果当前只看主持人,则不添加弹幕 + watchCore.barrage.setBarrageAddIntercept(data => { + const userType = data.user?.userType; + const userId = data.user?.userId; + + if ( + chatStore.onlySpecialMsg && + !isSpecialUserType(userType) && + userId !== viewerStore.viewerId + ) { + return true; + } + + return false; + }); + + return watchCore; +} + +window.debugWatchSdk = () => { + window.watchCore = watchCore; + console.info(watchCore); +}; diff --git a/src/hooks/README.md b/src/hooks/README.md new file mode 100644 index 0000000000000000000000000000000000000000..0006ec29d6fd4ea29b530cf8cd0a3cf1801613ac --- /dev/null +++ b/src/hooks/README.md @@ -0,0 +1,13 @@ +# 说明 + +存放一些公共使用的 `hook`,如果只是用一次,可以考虑放在组件中,而不是放在该目录 + +| 目录 | 说明 | +| :--------- | :----------------------------------------------------------------------------- | +| animation | 存放一些动画相关的 `hook` | +| behaviors | 存放一些 `DOM` 行为交互相关的 `hook` | +| business | 存放一些业务相关的公用 `hook`,比如页面数据上报 | +| components | 存放不需要 `.vue` 组件单独引入 `Vue` 文件就可以直接使用的 `hook`,比如图片预览 | +| core | 存放和项目核心相关的 `hook` | +| platforms | 存放一些和平台相关的 `hook`,比如泛域名,微信环境 | +| tools | 存放一些工具类的 `hook`,比如格式化 | diff --git a/src/hooks/animation/use-svga-animation/index.ts b/src/hooks/animation/use-svga-animation/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..09759a5763f0d56078ec7d640c1b611add39224f --- /dev/null +++ b/src/hooks/animation/use-svga-animation/index.ts @@ -0,0 +1,109 @@ +import { onBeforeUnmount, onMounted, Ref, unref } from 'vue'; +import { + loadSvgaPlayer, + SVGAParser, + SVGAPlayer, +} from '@/plugins/external-lib-loaders/load-svga-player'; + +export interface UseSvgaAnimationOptions { + /** 容器节点 */ + containerRef: Ref; + /** svga 动画文件地址 */ + svgaUrl: string; + /** 时间间隔,默认:20 * 1000 */ + timeInterval?: number; +} + +/** + * @hook svga 动画 + */ +export const useSvgaAnimation = (options: UseSvgaAnimationOptions) => { + const { containerRef, svgaUrl, timeInterval = 20 * 1000 } = options; + + let svgaPlayer: SVGAPlayer | undefined; + let svgaParser: SVGAParser | undefined; + let intervalTimer: number | undefined; + + /** 创建播放器 */ + async function createPlayer() { + const containerElem = unref(containerRef); + if (!containerElem) { + return; + } + try { + const SVGA = await loadSvgaPlayer(); + svgaPlayer = new SVGA.Player(containerElem); + svgaParser = new SVGA.Parser(); + setupSvgaUrl(svgaUrl); + } catch (e) {} + } + + /** 设置 svga 地址 */ + function setupSvgaUrl(url: string) { + try { + svgaParser?.load(url, videoItem => { + svgaPlayer?.setVideoItem(videoItem); + startAnimation(); + }); + } catch (e) { + console.error(e); + } + } + + /** 开始动画 */ + function startAnimation() { + clearAnimation(); + if (!svgaPlayer) { + return; + } + + try { + // 循环一次 + svgaPlayer.loops = 1; + // 开始播放动画 + svgaPlayer.startAnimation(); + // 播放结束后,停留在100%的状态,并 n 秒后重新播放 + svgaPlayer.onFinished(() => { + svgaPlayer?.stepToPercentage(1, false); + intervalTimer = window.setTimeout(function () { + startAnimation(); + }, timeInterval); + }); + } catch (e) { + console.error(e); + } + } + + /** 清空动画 */ + function clearAnimation() { + try { + clearIntervalTimer(); + if (!svgaPlayer) { + return; + } + svgaPlayer.clear(); + svgaPlayer.clearDynamicObjects(); + } catch (e) { + console.error(e); + } + } + + /** 清空 */ + function clearIntervalTimer() { + clearTimeout(intervalTimer); + intervalTimer = undefined; + } + + onMounted(() => { + createPlayer(); + }); + + onBeforeUnmount(() => { + clearIntervalTimer(); + }); + + return { + setupSvgaUrl, + clearAnimation, + }; +}; diff --git a/src/hooks/animation/use-svga-player/index.ts b/src/hooks/animation/use-svga-player/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..fb4813e9c7a55c45dcb8ab925eeb43d33af6a18a --- /dev/null +++ b/src/hooks/animation/use-svga-player/index.ts @@ -0,0 +1,252 @@ +import { $ } from '@just4/dom'; +import { getFileSuffix } from '@/assets/utils/file'; +import { onBeforeUnmount, onMounted, Ref, ref, unref, watch } from 'vue'; +import { DOMWrap } from '@just4/dom/dom-wrap'; +import { + loadSvgaPlayer, + SVGAParser, + SVGAPlayer, + SVGAVideoEntity, +} from '@/plugins/external-lib-loaders/load-svga-player'; + +// 记录加载的promise,避免重复加载 +const loadSvgaPromises: UniversalParams> = {}; + +/** + * @hook svga 动画播放器 + */ +export const useSvgaPlayer = (wrapRef: Ref) => { + let svgaParser: SVGAParser | undefined; + let svgaPlayer: SVGAPlayer | undefined; + + /** + * 判断是否为 svga 文件地址 + * @param url 目标文件地址 + */ + function isSvgaFileUrl(url: string) { + const suffix = getFileSuffix(url); + return suffix === 'svga'; + } + + /** 是否正在播放动画 */ + const isPlayingAnimation = ref(false); + + /** 创建 svga 播放器 */ + async function createSvgaPlayer() { + if (!wrapRef.value) { + console.warn('createSvgaPlayer fail, wrapRef is undefined'); + return; + } + const SVGA = await loadSvgaPlayer(); + svgaParser = new SVGA.Parser(); + svgaPlayer = new SVGA.Player(wrapRef.value); + } + + /** 清空 svga 动画 */ + function clearSvgaAnimation() { + try { + if (svgaPlayer) { + svgaPlayer.clear(); + svgaPlayer.clearDynamicObjects(); + isPlayingAnimation.value = false; + } + } catch (e) { + console.warn('clearSvgaAnimation 清除动画失败'); + } + } + + /** + * 加载 svga 动画文件地址 + * @param url svga 文件地址 + */ + function loadSvgaFile(url: string) { + if (!loadSvgaPromises[url]) { + loadSvgaPromises[url] = new Promise((resolve, reject) => { + try { + svgaParser?.load(url, videoItem => { + resolve(videoItem); + }); + } catch (e) { + reject(e); + } + }); + } + return loadSvgaPromises[url]; + } + + /** + * 播放 svga 动画文件 + * @param url svga 文件地址 + */ + async function playSvgaAnimation(url: string) { + clearSvgaAnimation(); + + try { + if (!svgaPlayer) { + console.warn('playSvgaAnimation 播放失败,未创建播放器'); + return; + } + + isPlayingAnimation.value = true; + // 加载文件 + const videoItem = await loadSvgaFile(url); + // 设置循环次数 + svgaPlayer.loops = 1; + // 设置播放器文件 + svgaPlayer.setVideoItem(videoItem); + // 开始播放动画 + svgaPlayer.startAnimation(); + // 侦听播放结束,播放下一个动画 + svgaPlayer.onFinished(() => { + isPlayingAnimation.value = false; + checkAnimationQueue(); + }); + } catch (e) { + console.warn('playSvgaAnimation 播放失败'); + isPlayingAnimation.value = false; + checkAnimationQueue(); + } + } + + onMounted(() => { + createSvgaPlayer(); + }); + + onBeforeUnmount(() => { + clearSvgaAnimation(); + }); + + /** 当前显示的图片 */ + const currentImage = ref(); + /** 隐藏图片定时器 */ + let hideImageTimer: number | undefined; + /** 图片节点 */ + let $imageElem: DOMWrap | undefined; + + watch( + () => unref(currentImage), + () => { + const imageUrl = unref(currentImage); + const wrapElem = unref(wrapRef); + if (!wrapElem) return; + + // 没有动画图片,移除节点 + if (!imageUrl) { + $imageElem?.remove(); + $imageElem = undefined; + return; + } + + if (!$imageElem) { + const divElem = document.createElement('div'); + $imageElem = $(divElem); + } + + $imageElem.css({ + width: '61.2%', + height: '80%', + 'background-size': '100% 100%', + 'background-repeat': 'no-repeat', + 'background-image': `url(${imageUrl})`, + position: 'absolute', + left: '50%', + top: '50%', + transform: 'translate(-50%, -50%)', + }); + + $(wrapElem).append($imageElem); + }, + ); + + /** + * 播放图片动画 + * @param url 图片地址 + * @param time 显示时间,默认 2 秒 + */ + function playImageAnimation(url: string, time = 2000) { + clearImageAnimation(); + currentImage.value = url; + isPlayingAnimation.value = true; + hideImageTimer = window.setTimeout(() => { + clearImageAnimation(); + checkAnimationQueue(); + }, time); + } + + /** + * 清空图片动画 + */ + function clearImageAnimation() { + isPlayingAnimation.value = false; + currentImage.value = undefined; + if (hideImageTimer) { + clearTimeout(hideImageTimer); + hideImageTimer = undefined; + } + } + + onBeforeUnmount(() => { + clearImageAnimation(); + }); + + /** 当前动画是否暂停 */ + const isPaused = ref(false); + + /** 暂停当前的动画队列 */ + function pauseAnimationQueue() { + isPaused.value = true; + } + + /** 开始当前的动画队列 */ + function startAnimationQueue() { + isPaused.value = false; + checkAnimationQueue(); + } + + /** svga 动画文件队列 */ + const animationQueue = ref([]); + + /** + * 插入 svga 文件或图片到动画队列中 + * @param url 地址 + * @param inHeader 是否从头部插入 + */ + function pushAnimationQueue(url: string, inHeader = false) { + if (!url) return; + + if (inHeader) { + animationQueue.value.unshift(url); + } else { + animationQueue.value.push(url); + } + checkAnimationQueue(); + } + + /** 检查动画队列 */ + function checkAnimationQueue() { + // 队列为空、正在播放动画、当前暂停播放 + if (animationQueue.value.length === 0 || isPlayingAnimation.value || isPaused.value) { + return; + } + + const url = animationQueue.value.shift(); + if (!url) return; + + if (isSvgaFileUrl(url)) { + playSvgaAnimation(url); + } else { + playImageAnimation(url); + } + } + + return { + animationQueue, + loadSvgaFile, + clearSvgaAnimation, + pushAnimationQueue, + playSvgaAnimation, + playImageAnimation, + pauseAnimationQueue, + startAnimationQueue, + }; +}; diff --git a/src/hooks/behaviors/use-append-to/index.ts b/src/hooks/behaviors/use-append-to/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a335f3ca0dcca5206ffe8fb402b117e28de35f70 --- /dev/null +++ b/src/hooks/behaviors/use-append-to/index.ts @@ -0,0 +1,30 @@ +import { getRefElem } from '@/assets/utils/vue-utils'; +import { onBeforeUnmount, onMounted, Ref } from 'vue'; + +/** + * @hook 处理将节点插入到某个容器中 + * @param elemRef 对应的节点 + * @param parentRef 父节点,默认:document.body + */ +export const useAppendTo = ( + elemRef: Ref | HTMLElement, + parentRef: Ref | HTMLElement = document.body, +) => { + onMounted(() => { + const elem = getRefElem(elemRef); + const parentElem = getRefElem(parentRef); + if (!elem || !parentElem) { + console.warn('useAppendTo fail, elem is undefined'); + return; + } + + parentElem.appendChild(elem); + }); + + onBeforeUnmount(() => { + const elem = getRefElem(elemRef); + if (elem && elem.parentNode) { + elem.parentNode.removeChild(elem); + } + }); +}; diff --git a/src/hooks/behaviors/use-aside-drag/index.ts b/src/hooks/behaviors/use-aside-drag/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8569cd06c4833c8fbae4780daea4aca0c81b950a --- /dev/null +++ b/src/hooks/behaviors/use-aside-drag/index.ts @@ -0,0 +1,92 @@ +import { getRefElem } from '@/assets/utils/vue-utils'; +import { onBeforeUnmount, onMounted, Ref, ref } from 'vue'; + +export interface UseAsideDragOption { + /** 目标元素节点 */ + targetRef: Ref | HTMLElement; + /** 获取初始定位 */ + getInitTop?: () => number; +} + +/** + * @hook 侧边拖拽 + */ +export const useAsideDrag = (options: UseAsideDragOption) => { + const { getInitTop } = options; + + /** top 定位 */ + const top = ref(0); + + let prevPageY: number | undefined; + + /** 初始化定位 */ + function initTop() { + if (getInitTop) { + top.value = getInitTop(); + } + } + + onMounted(() => { + initTop(); + + const targetElem = getRefElem(options.targetRef); + if (!targetElem) { + return; + } + targetElem.addEventListener('touchstart', () => { + prevPageY = undefined; + bindBodyEvent(); + }); + }); + + function bindBodyEvent() { + unbindBodyEvent(); + const body = document.body; + body.addEventListener('touchmove', onBodyTouchMove); + body.addEventListener('touchend', onBodyTouchEnd); + body.addEventListener('touchcancel', onBodyTouchEnd); + } + + function unbindBodyEvent() { + const body = document.body; + body.removeEventListener('touchmove', onBodyTouchMove); + } + + /** 处理 body touchmove 事件 */ + function onBodyTouchMove(event: TouchEvent) { + const pageY = event.touches[0].pageY; + + if (typeof prevPageY === 'undefined') { + prevPageY = pageY; + return; + } + + const diffY = pageY - prevPageY; + + let newTop = top.value + diffY; + if (newTop < 0) { + newTop = 0; + } + + const targetElem = getRefElem(options.targetRef); + if (targetElem && newTop > document.body.clientHeight - targetElem.clientHeight) { + newTop = document.body.clientHeight - targetElem.clientHeight; + } + + top.value = newTop; + prevPageY = pageY; + } + + function onBodyTouchEnd() { + prevPageY = undefined; + unbindBodyEvent(); + } + + onBeforeUnmount(() => { + unbindBodyEvent(); + }); + + return { + top, + }; +}; diff --git a/src/hooks/behaviors/use-click-outside/index.ts b/src/hooks/behaviors/use-click-outside/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..45d81403c94d257f52b6147e96982e577d1d7878 --- /dev/null +++ b/src/hooks/behaviors/use-click-outside/index.ts @@ -0,0 +1,105 @@ +import Vue, { onBeforeUnmount, onMounted, Ref } from 'vue'; +import { getRefElem } from '@/assets/utils/vue-utils'; +import { isFunction } from '@/assets/utils/function'; + +export type ClickOutsideTargetType = RefOrElement | string | Ref; + +export type ClickOutsideTargets = ClickOutsideTargetType | ClickOutsideTargetType[]; + +export type ClickOutsideTargetsFunction = () => ClickOutsideTargets; + +/** + * @hook 点击节点外部事件 hook + * @param targets 响应式节点数组或获取方法 + * @param callback 回调方法 + * @param autoListen 自动监听,默认:true + * @returns + * ``` + * listenClickOutSide - 监听点击外部事件 + * removeListenClickOutSide - 移除点击外部监听事件 + * ``` + */ +export const useClickOutside = ( + targets: ClickOutsideTargets | ClickOutsideTargetsFunction, + callback: EmptyParamsFunc, + autoListen = true, +) => { + /** 获取用于比较的 dom 节点列表 */ + function getCompareElements(): HTMLElement[] { + const targetElements: HTMLElement[] = []; + + const joinElem = (refTarget: ClickOutsideTargetType) => { + // 传入的是 dom 选择器 + if (typeof refTarget === 'string') { + const res = document.querySelectorAll(refTarget); + res && res.forEach(e => targetElements.push(e as HTMLElement)); + return; + } + + const elemVal = getRefElem(refTarget); + // 已经是 dom 节点 + if (elemVal instanceof HTMLElement) { + targetElements.push(elemVal); + } + }; + + const res = isFunction(targets) ? targets() : targets; + const elements = Array.isArray(res) ? res : [res]; + elements.forEach(joinElem); + + return targetElements; + } + + /** 处理 document click 事件 */ + const handleDocumentClick = (event: MouseEvent) => { + const clickTarget = event.target as HTMLElement; + + if (clickTarget) { + let isInTarget = false; + + const compareElements = getCompareElements(); + + compareElements.forEach(targetRef => { + const target = getRefElem(targetRef); + if (target instanceof HTMLElement && target.contains(clickTarget)) { + isInTarget = true; + } + }); + + if (!isInTarget) { + callback(); + } + } + }; + let isListen = false; + + /** 监听点击外层 */ + const listenClickOutSide = () => { + if (isListen) { + return; + } + isListen = true; + document.addEventListener('mouseup', handleDocumentClick); + }; + + /** 取消监听 */ + const removeListenClickOutSide = () => { + document.removeEventListener('mouseup', handleDocumentClick); + isListen = false; + }; + + onMounted(() => { + if (autoListen) { + listenClickOutSide(); + } + }); + + onBeforeUnmount(() => { + removeListenClickOutSide(); + }); + + return { + listenClickOutSide, + removeListenClickOutSide, + }; +}; diff --git a/src/hooks/behaviors/use-panel-visible/index.ts b/src/hooks/behaviors/use-panel-visible/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..59e8c96ac54f0c59104c0e4c94638a4f282bc1c1 --- /dev/null +++ b/src/hooks/behaviors/use-panel-visible/index.ts @@ -0,0 +1,62 @@ +import { ref, unref, watch } from 'vue'; +import { ClickOutsideTargets, useClickOutside } from '../use-click-outside'; + +/** + * @hook 面板显隐 + * @description 在点击面板外部时自动关闭 + */ +export const usePanelVisible = (target: ClickOutsideTargets) => { + /** 显示状态 */ + const visible = ref(false); + + /** 显示面板 */ + function showPanel() { + visible.value = true; + } + + /** 隐藏面板 */ + function hidePanel() { + visible.value = false; + } + + /** 切换面板显示 */ + function togglePanel() { + if (visible.value) { + hidePanel(); + } else { + showPanel(); + } + } + + /** 处理点击外层 */ + function onClickOutside() { + hidePanel(); + } + + const { listenClickOutSide, removeListenClickOutSide } = useClickOutside( + target, + onClickOutside, + false, + ); + + watch( + () => unref(visible), + () => { + if (unref(visible)) { + listenClickOutSide(); + } else { + removeListenClickOutSide(); + } + }, + { + immediate: true, + }, + ); + + return { + visible, + showPanel, + hidePanel, + togglePanel, + }; +}; diff --git a/src/hooks/behaviors/use-scroll-arrow/index.ts b/src/hooks/behaviors/use-scroll-arrow/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc21abe280e7e3f704871afb347a49c144eff369 --- /dev/null +++ b/src/hooks/behaviors/use-scroll-arrow/index.ts @@ -0,0 +1,72 @@ +import { ref, nextTick } from 'vue'; +import { useWindowResizeListener } from '../../core/use-window-resize-listener'; + +/** + * @hook 根据滚动内容处理横向滚动条与箭头显示 + * */ +export const useHorizontalScrollArrow = () => { + /** 滚动节点 */ + const scrollRef = ref(); + /** 左箭头是否显示 */ + const leftArrowVisible = ref(false); + /** 右箭头是否显示 */ + const rightArrowVisible = ref(false); + + /** 重置箭头显示状态 */ + async function resetArrowVisible() { + await nextTick(); + + if (!scrollRef.value) { + return; + } + + const { scrollLeft, scrollWidth, clientWidth } = scrollRef.value; + + if (scrollWidth > clientWidth) { + rightArrowVisible.value = true; + } else { + rightArrowVisible.value = false; + } + + if (scrollLeft > 0) { + leftArrowVisible.value = true; + } else { + leftArrowVisible.value = false; + } + } + + /** 处理节点滚动事件 */ + function onScrollEvent() { + resetArrowVisible(); + } + + /** 处理左箭头点击 */ + function onLeftArrowClick() { + if (!leftArrowVisible.value || !scrollRef.value) { + return; + } + + scrollRef.value.scrollLeft = 0; + } + + /** 处理右箭头点击 */ + function onRightArrowClick() { + if (!rightArrowVisible.value || !scrollRef.value) { + return; + } + + scrollRef.value.scrollLeft = scrollRef.value.scrollWidth; + } + + useWindowResizeListener(resetArrowVisible, true); + + return { + scrollRef, + leftArrowVisible, + rightArrowVisible, + resetArrowVisible, + onScrollEvent, + onLeftArrowClick, + onRightArrowClick, + }; +}; diff --git a/src/hooks/behaviors/use-scroll-list/index.ts b/src/hooks/behaviors/use-scroll-list/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..77372a6513155fba895ab1df719253679aa66a3e --- /dev/null +++ b/src/hooks/behaviors/use-scroll-list/index.ts @@ -0,0 +1,122 @@ +import { nextTick, ref, unref } from 'vue'; + +export interface ScrollListInstance { + /** 是否靠近底部 */ + isNearBottom(): boolean; + /** 滚动到最底部 */ + scrollToBottom(): void; + /** 检查当前滚动条是否靠近底部,靠近则滚到底部 */ + checkScrollToBottom(): void; + /** 记录当前的第一个节点 */ + recordFirstElem(): void; + /** 滚动到记录到首个节点 */ + scrollToFirstRecord(): Promise; +} + +export interface UseScrollListOptions { + /** 首个节点的索引,默认:0 */ + firstElemIndex?: number; + /** 靠近顶部回调 */ + nearTopCallback?: () => void; +} + +/** + * @hook 滚动列表 + */ +export const useScrollList = (options: UseScrollListOptions = {}) => { + const { firstElemIndex = 0 } = options; + /** 滚动节点 */ + const scrollRef = ref(); + + /** 是否靠近底部 */ + function isNearBottom(): boolean { + const scrollElem = unref(scrollRef); + if (!scrollElem) { + return false; + } + + const clientRect = Object.assign({}, scrollElem.getBoundingClientRect()); + clientRect.width = clientRect.width || clientRect.right - clientRect.left; + clientRect.height = clientRect.height || clientRect.bottom - clientRect.top; + + // 消息列表处于非可见状态 + if (clientRect.width === 0 && clientRect.height === 0) { + return false; + } + + return scrollElem.scrollHeight - scrollElem.clientHeight - scrollElem.scrollTop < 120; + } + + /** 滚动到最底部 */ + async function scrollToBottom() { + await nextTick(); + const scrollElem = unref(scrollRef); + if (!scrollElem) { + return; + } + scrollElem.scrollTop = scrollElem.scrollHeight; + } + + /** 检查当前滚动条是否靠近底部,靠近则滚到底部 */ + function checkScrollToBottom() { + if (isNearBottom()) { + scrollToBottom(); + } + } + + /** 处理滚动事件 */ + function onScrollEvent() { + const scrollElem = unref(scrollRef); + if (!scrollElem) { + return; + } + + const top = scrollElem.scrollTop; + if (top === 0 && options.nearTopCallback) { + options.nearTopCallback(); + } + } + + let firstElem: HTMLElement | undefined; + + /** 记录当前的第一个节点 */ + function recordFirstElem() { + const scrollElem = unref(scrollRef); + if (!scrollElem) { + return; + } + const childElements = scrollElem.childNodes; + if (childElements.length > 1) { + firstElem = childElements[firstElemIndex] as HTMLElement; + } + } + + /** 滚动到记录到首个节点 */ + async function scrollToFirstRecord() { + const scrollElem = unref(scrollRef); + if (!scrollElem || !firstElem) { + return; + } + await nextTick(); + const scrollTop = firstElem.offsetTop - firstElem.clientHeight + firstElem.clientHeight; + scrollElem.scrollTop = scrollTop; + } + + /** 导出对象 */ + const scrollListInstance: ScrollListInstance = { + isNearBottom, + scrollToBottom, + checkScrollToBottom, + recordFirstElem, + scrollToFirstRecord, + }; + + return { + scrollRef, + isNearBottom, + scrollToBottom, + checkScrollToBottom, + scrollListInstance, + onScrollEvent, + }; +}; diff --git a/src/hooks/behaviors/use-simple-visible/index.ts b/src/hooks/behaviors/use-simple-visible/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a352aab10977f701f5b32a79705688e5a6342b1 --- /dev/null +++ b/src/hooks/behaviors/use-simple-visible/index.ts @@ -0,0 +1,98 @@ +/** + * @file 简单的显隐 hook + */ + +import { onBeforeUnmount, ref, unref, watch } from 'vue'; + +export interface SimpleVisibleOptions { + /** 是否自动关闭,默认:false */ + autoClose?: boolean; + /** 自动关闭时间,单位:毫秒,默认:5 * 1000 */ + autoCloseTime?: number; +} + +/** + * 简单的显隐 hook + * @param defaultVisible 默认显示状态,默认:false + */ +export const useSimpleVisible = (defaultVisible = false, options: SimpleVisibleOptions = {}) => { + const { autoClose = false, autoCloseTime = 5 * 1000 } = options; + + const visible = ref(defaultVisible); + + /** + * 设置显示状态 + * @param _visible 显示状态,默认:true + */ + function setVisible(_visible = true) { + visible.value = !!_visible; + } + + /** 显示 */ + function show() { + setVisible(true); + } + + /** 关闭 */ + function close() { + setVisible(false); + } + + /** 切换显示状态 */ + function toggle() { + if (unref(visible)) { + close(); + } else { + show(); + } + } + + let closeTimer: number | undefined; + + /** 设置关闭定时器 */ + function setCloseTimer() { + removeCloseTimer(); + closeTimer = window.setTimeout(() => { + close(); + }, autoCloseTime); + } + + /** 移除关闭定时器 */ + function removeCloseTimer() { + if (closeTimer) { + clearTimeout(closeTimer); + closeTimer = undefined; + } + } + + // 自动关闭处理 + watch( + () => unref(visible), + () => { + if (!autoClose) { + return; + } + + if (!visible.value) { + removeCloseTimer(); + return; + } + setCloseTimer(); + }, + { + immediate: true, + }, + ); + + onBeforeUnmount(() => { + removeCloseTimer(); + }); + + return { + visible, + setVisible, + show, + close, + toggle, + }; +}; diff --git a/src/hooks/business/use-pv-track/index.ts b/src/hooks/business/use-pv-track/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..426982b9e19ca01eb1bee507e27a07295f496fa0 --- /dev/null +++ b/src/hooks/business/use-pv-track/index.ts @@ -0,0 +1,12 @@ +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { RtasTrackPageType } from '@polyv/live-watch-sdk'; +import { watchEffect } from 'vue'; + +export const usePvTrack = (pageType: RtasTrackPageType) => { + const channelStore = useChannelStore(); + watchEffect(() => { + const watchCore = getWatchCore(); + watchCore.rtas.trackPageUV(pageType, channelStore.liveStatus); + }); +}; diff --git a/src/hooks/components/use-image-preview/index.ts b/src/hooks/components/use-image-preview/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..a8fb36de1e35cffa5eb19a0e38ebd55bdd20c00d --- /dev/null +++ b/src/hooks/components/use-image-preview/index.ts @@ -0,0 +1,23 @@ +import { ImagePreviewOptions } from '@/plugins/polyv-ui/types'; +import { PlvImagePreview } from '@/plugins/polyv-ui/admin-import'; + +/** + * 浏览图片 + * @param images 图片地址 + * @param options 选项 + */ +export const previewImage = (images: string | string[] = [], options: ImagePreviewOptions = {}) => { + return PlvImagePreview(images, options); +}; + +/** + * 点击图片进行预览 + * @param event 事件对象 + */ +export function clickImgToPreview(event: Event) { + const target = event.target; + + if (target instanceof HTMLImageElement) { + previewImage(target.src); + } +} diff --git a/src/hooks/components/use-popper/_components/popper-arrow.vue b/src/hooks/components/use-popper/_components/popper-arrow.vue new file mode 100644 index 0000000000000000000000000000000000000000..1d30b82f899e385f51bf814d16edb73016c384c9 --- /dev/null +++ b/src/hooks/components/use-popper/_components/popper-arrow.vue @@ -0,0 +1,58 @@ + + + + + + diff --git a/src/hooks/components/use-popper/_hooks/use-popper-arrow.ts b/src/hooks/components/use-popper/_hooks/use-popper-arrow.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb93ff0fe77bc9e9e210689ddf3f8bae9ba66d67 --- /dev/null +++ b/src/hooks/components/use-popper/_hooks/use-popper-arrow.ts @@ -0,0 +1,50 @@ +/** + * @file popper 箭头 hook + */ +import Vue, { ComponentOptions, onMounted, unref } from 'vue'; +import { ExtendedVue } from 'vue/types/vue'; + +import PopperArrow from '../_components/popper-arrow.vue'; +import { PopperOptions } from '../_popper-type'; + +export interface PopperArrowOptions { + /** 颜色,默认:#fff */ + color?: string; + /** 尺寸,默认:6 */ + size?: number; +} + +export type PopperArrowComponentOptions = ComponentOptions; +export type PopperArrowCtorType = ExtendedVue< + Vue, + Record, + Record, + Record, + PopperArrowOptions, + Record +>; +const PopperArrowConstructor = Vue.extend(PopperArrow as PopperArrowComponentOptions); + +export const usePopperArrow = (popperOptions: PopperOptions) => { + const { getElement, arrow } = popperOptions; + + /** 创建箭头节点 */ + function createArrowElem() { + const arrowVal = unref(arrow); + const propsData = typeof arrowVal === 'boolean' ? {} : arrowVal; + const instance = new PopperArrowConstructor({ + propsData, + }); + instance.$mount(); + + const { popperElem } = getElement(); + + popperElem && popperElem.appendChild(instance.$el); + } + + onMounted(() => { + if (unref(arrow)) { + createArrowElem(); + } + }); +}; diff --git a/src/hooks/components/use-popper/_hooks/use-popper-core.ts b/src/hooks/components/use-popper/_hooks/use-popper-core.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1bbfe07276c48f9517250b8a52d54c40b4212a4 --- /dev/null +++ b/src/hooks/components/use-popper/_hooks/use-popper-core.ts @@ -0,0 +1,192 @@ +/** + * @file @popperjs/core 相关的 hook + */ +import { ref, unref, watch, computed, onMounted, onUnmounted, nextTick } from 'vue'; +import { createPopper, Instance as PopperInstance, OptionsGeneric, Modifier } from '@popperjs/core'; +import type { EventListenersModifier } from '@popperjs/core/lib/modifiers/eventListeners'; +import type { OffsetModifier } from '@popperjs/core/lib/modifiers/offset'; +import type { ComputeStylesModifier } from '@popperjs/core/lib/modifiers/computeStyles'; +import type { ArrowModifier } from '@popperjs/core/lib/modifiers/arrow'; +import { PopperOptions } from '../_popper-type'; + +/** + * 构建 popperjs 的配置 + */ +export const usePopperCoreOptions = ( + popperOptions: PopperOptions, + getPopperCtx: () => PopperInstance | undefined, +) => { + const { onPopperEvent, placement, popperOffset } = popperOptions; + + const popperCoreOptions = computed>>(() => { + // 样式计算,使用 top/left/right/bottom 进行定位 + const computeStylesMod: Pick = { + name: 'computeStyles', + options: { + gpuAcceleration: false, + adaptive: false, + }, + }; + + // 事件监听 + const eventListenersMod: Pick = { + name: 'eventListeners', + fn: ({ state }) => { + const isReferenceHidden = !!state.modifiersData.hide?.isReferenceHidden; + if (typeof onPopperEvent === 'function') { + onPopperEvent({ isReferenceHidden }); + } + }, + }; + + // 偏移量 + const offsetMod: Pick = { + name: 'offset', + options: { + offset: ({ placement }) => { + let skidding = 0; + let distance = 0; + + const offsetVal = unref(popperOffset); + if (offsetVal) { + const offsetValSkidding = offsetVal[0] ?? 0; + const offsetValDistance = offsetVal[1] ?? 0; + skidding += offsetValSkidding; + distance += offsetValDistance; + } + + // 根据箭头的大小自动叠加 offset + const ctx = getPopperCtx(); + const arrowElem = ctx?.state?.elements?.arrow; + if (arrowElem instanceof HTMLElement) { + const width = arrowElem.offsetWidth; + const height = arrowElem.offsetHeight; + + if (placement.includes('top') || placement.includes('bottom')) { + distance += height / 2; + } + + if (placement.includes('left') || placement.includes('right')) { + distance += width / 2; + } + } + + return [skidding, distance]; + }, + }, + }; + + // 箭头 + const arrowMod: Pick = { + name: 'arrow', + options: { + padding: 6, + }, + }; + + return { + placement: unref(placement), + strategy: 'absolute', + modifiers: [computeStylesMod, eventListenersMod, offsetMod, arrowMod], + } as OptionsGeneric>; + }); + + return { + /** 弹层选项 */ + popperCoreOptions, + }; +}; + +export const usePopperCore = (popperOptions: PopperOptions) => { + const { getElement, popperVisible } = popperOptions; + + const { popperCoreOptions } = usePopperCoreOptions(popperOptions, getPopperCtx); + + /** 弹层 sdk 实例 */ + const popperCtx = ref(undefined); + + /** 获取弹层 sdk 实例 */ + function getPopperCtx(): PopperInstance | undefined { + return unref(popperCtx); + } + + /** 更新弹层位置 */ + async function updatePopper() { + await nextTick(); + + const ctx = getPopperCtx(); + if (ctx) { + ctx.update(); + } + } + + /** 创建弹窗 SDK */ + function createPopperCore() { + destroyPopperCore(); + + const { referenceElem, popperElem } = getElement(); + if (referenceElem && popperElem) { + popperCtx.value = createPopper(referenceElem, popperElem, unref(popperCoreOptions)); + } + } + + /** 销毁弹层 SDK */ + function destroyPopperCore() { + const ctx = getPopperCtx(); + if (ctx) { + ctx.destroy(); + } + } + + // 是否第一次 update + let isFirstUpdate = true; + + watch( + () => unref(popperVisible), + () => { + if (unref(popperVisible)) { + updatePopper(); + + /** + * 在 popperjs 中,横向弹层在首次 update 时会不准,垂直方向定位会有一点偏差,第二次之后才准 + * 这里判断在首次 update 且横向弹出时,延迟 10ms 再次进行 update 来解决这个问题 + */ + const ctx = getPopperCtx(); + if (ctx && isFirstUpdate && /left|right/.test(ctx.state.placement)) { + setTimeout(() => { + updatePopper(); + }, 10); + } + isFirstUpdate = false; + } + }, + ); + + watch( + () => unref(popperCoreOptions), + () => { + const ctx = getPopperCtx(); + if (ctx) { + ctx.setOptions(unref(popperCoreOptions)); + } + }, + ); + + watch(getElement, () => createPopperCore()); + + onMounted(() => { + createPopperCore(); + }); + + onUnmounted(() => { + destroyPopperCore(); + }); + + return { + popperCtx, + getPopperCtx, + updatePopper, + createPopperCore, + destroyPopperCore, + }; +}; diff --git a/src/hooks/components/use-popper/_hooks/use-popper-interactive.ts b/src/hooks/components/use-popper/_hooks/use-popper-interactive.ts new file mode 100644 index 0000000000000000000000000000000000000000..4b615c00962922e2d13251356444295ca5e413f0 --- /dev/null +++ b/src/hooks/components/use-popper/_hooks/use-popper-interactive.ts @@ -0,0 +1,104 @@ +import { onMounted, unref } from 'vue'; +import { useClickOutside } from '@/hooks/behaviors/use-click-outside'; +import { PopperOptions } from '../_popper-type'; + +/** + * 弹层交互方式 hook + */ +export const usePopperInteractive = (popperOptions: PopperOptions) => { + const { + trigger, + popperDisabled, + getElement, + mouseEnterDelay, + mouseLeaveDelay, + showPopper, + hidePopper, + togglePopper, + delayShowPopper, + delayHidePopper, + forceOutSideClickHide, + outSideClickDelayHide, + } = popperOptions; + + // ============== click 交互 ============== // + useClickOutside( + () => { + const { referenceElem, popperElem } = getElement(); + const elements: HTMLElement[] = []; + referenceElem && elements.push(referenceElem); + popperElem && elements.push(popperElem); + return elements; + }, + () => { + if (unref(forceOutSideClickHide) || unref(trigger).includes('click')) { + unref(outSideClickDelayHide) ? delayHidePopper() : hidePopper(); + } + }, + ); + const listenElemClickEvent = () => { + if (unref(popperDisabled)) { + return; + } + const { referenceElem } = getElement(); + if (referenceElem) { + referenceElem.addEventListener('click', togglePopper); + } + }; + + // ============== hover 交互 ============== // + const mouseEnterToShow = () => { + if (unref(popperDisabled)) { + return; + } + delayShowPopper(unref(mouseEnterDelay)); + }; + const mouseLeaverToHide = () => { + delayHidePopper(unref(mouseLeaveDelay)); + }; + const listenElemHoverEvent = () => { + const { referenceElem, popperElem } = getElement(); + if (referenceElem && popperElem) { + referenceElem.addEventListener('mouseenter', mouseEnterToShow); + referenceElem.addEventListener('mouseleave', mouseLeaverToHide); + popperElem.addEventListener('mouseenter', mouseEnterToShow); + popperElem.addEventListener('mouseleave', mouseLeaverToHide); + } + }; + + // ============== focus 交互 ============== // + const focusToShow = () => { + if (unref(popperDisabled)) { + return; + } + showPopper(); + }; + const blurToHide = () => { + hidePopper(); + }; + const listenElemFocusEvent = () => { + const { inputElem } = getElement(); + if (inputElem) { + inputElem.addEventListener('focus', focusToShow); + inputElem.addEventListener('blur', blurToHide); + } + }; + + /** 设置交互 */ + const setInteractive = () => { + const triggerVal = unref(trigger); + if (triggerVal.includes('click')) { + listenElemClickEvent(); + } + if (triggerVal.includes('hover')) { + listenElemHoverEvent(); + } + if (triggerVal.includes('focus')) { + listenElemFocusEvent(); + } + }; + + onMounted(() => { + setInteractive(); + }); +}; diff --git a/src/hooks/components/use-popper/_hooks/use-popper-visible.ts b/src/hooks/components/use-popper/_hooks/use-popper-visible.ts new file mode 100644 index 0000000000000000000000000000000000000000..dc27c66d2a9e27af42c3e44e689c84ce59072df6 --- /dev/null +++ b/src/hooks/components/use-popper/_hooks/use-popper-visible.ts @@ -0,0 +1,91 @@ +/** + * @file popper 弹层显示状态 hook + */ +import { isRef, onBeforeUnmount, Ref, ref, unref } from 'vue'; +import { PopperVisibleResult } from '../_popper-type'; + +export type UsePopperVisibleOptions = { + popperVisible?: boolean | Ref; +}; + +export const usePopperVisible = (options: UsePopperVisibleOptions = {}): PopperVisibleResult => { + /** 弹层显示状态 */ + const popperVisible = isRef(options.popperVisible) + ? options.popperVisible + : ref(!!options.popperVisible); + + // 设置弹层显示状态 + function setVisible(visible = false) { + destroyDelayShowTimer(); + destroyDelayHideTimer(); + popperVisible.value = visible; + } + + /** 显示弹层 */ + function showPopper() { + setVisible(true); + } + + /** 隐藏弹层 */ + function hidePopper() { + setVisible(false); + } + + /** 切换弹层显示状态 */ + function togglePopper() { + setVisible(!unref(popperVisible)); + } + + /** 显示延迟计时器 */ + let delayShowTimer: number | undefined; + /** 隐藏延迟计时器 */ + let delayHideTimer: number | undefined; + + /** 延迟显示弹层 */ + function delayShowPopper(delay = 100) { + destroyDelayShowTimer(); + destroyDelayHideTimer(); + if (!delay) { + showPopper(); + return; + } + delayShowTimer = window.setTimeout(() => showPopper(), delay); + } + + /** 延迟隐藏弹层 */ + function delayHidePopper(delay = 100) { + destroyDelayShowTimer(); + destroyDelayHideTimer(); + if (!delay) { + hidePopper(); + return; + } + delayHideTimer = window.setTimeout(() => hidePopper(), delay); + } + + /** 销毁显示延迟定时器 */ + function destroyDelayShowTimer() { + clearTimeout(delayShowTimer); + delayShowTimer = undefined; + } + + /** 销毁隐藏延迟定时器 */ + function destroyDelayHideTimer() { + clearTimeout(delayHideTimer); + delayHideTimer = undefined; + } + + onBeforeUnmount(() => { + destroyDelayShowTimer(); + destroyDelayHideTimer(); + }); + + return { + popperVisible, + showPopper, + hidePopper, + togglePopper, + delayShowPopper, + delayHidePopper, + }; +}; diff --git a/src/hooks/components/use-popper/_popper-type.ts b/src/hooks/components/use-popper/_popper-type.ts new file mode 100644 index 0000000000000000000000000000000000000000..ded3271e4918cbd4b7175d794e5f4909ae63d5bf --- /dev/null +++ b/src/hooks/components/use-popper/_popper-type.ts @@ -0,0 +1,73 @@ +import { Ref } from 'vue'; +import type { Placement } from '@popperjs/core'; +import { PopperArrowOptions } from './_hooks/use-popper-arrow'; + +export type PopperOffset = [number, number]; + +/** 弹层触发方式 */ +export type PopperTrigger = 'click' | 'hover' | 'focus' | 'custom'; + +export interface UsePopperOptions { + popperVisible?: boolean | Ref; + /** 触发节点 ref */ + referenceRef?: HTMLElement | Ref; + /** 弹层节点 ref */ + popperRef?: HTMLElement | Ref; + /** 输入框节点 ref,用于聚焦显示的交互 */ + inputRef?: HTMLInputElement | Ref; + /** 弹层是否禁用 */ + popperDisabled?: boolean | Ref; + /** 触发方式 */ + trigger?: PopperTrigger | Ref; + /** 鼠标移入延迟显示时间 */ + mouseEnterDelay?: number | Ref; + /** 鼠标移出延迟隐藏时间 */ + mouseLeaveDelay?: number | Ref; + /** 弹层位置 */ + placement?: Placement | Ref; + /** 弹层偏移量 */ + popperOffset?: PopperOffset | Ref; + /** 是否显示箭头,默认:false */ + arrow?: boolean | PopperArrowOptions | Ref; + /** 强制 outSideClick 关闭,默认:false */ + forceOutSideClickHide?: boolean | Ref; + /** outSideClick 关闭时是否延迟,默认:false */ + outSideClickDelayHide?: boolean | Ref; + /** 弹层宽度跟随触发节点,默认:false */ + followReferenceWidth?: boolean | Ref; +} + +export interface PopperVisibleResult { + /** 弹层显示状态 */ + popperVisible: Ref; + /** 显示弹层 */ + showPopper: () => void; + /** 隐藏弹窗 */ + hidePopper: () => void; + /** 切换弹层显示状态 */ + togglePopper: () => void; + /** 延迟显示弹层 */ + delayShowPopper: (delay?: number) => void; + /** 延迟隐藏弹层 */ + delayHidePopper: (delay?: number) => void; +} + +export interface PopperElements { + referenceElem: HTMLElement | undefined; + popperElem: HTMLElement | undefined; + inputElem: HTMLElement | undefined; +} + +export interface OnPopperEventParams { + /** 触发节点是否已隐藏 */ + isReferenceHidden: boolean; +} + +export interface PopperOptions + extends Required>, + PopperVisibleResult { + /** 获取相关 dom 节点 */ + getElement: () => PopperElements; + /** 处理弹层事件 */ + onPopperEvent: (params: OnPopperEventParams) => void; +} diff --git a/src/hooks/components/use-popper/index.ts b/src/hooks/components/use-popper/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..d36394eddff578d5f864f71af083101b7ec37ef7 --- /dev/null +++ b/src/hooks/components/use-popper/index.ts @@ -0,0 +1,113 @@ +/** + * @file 弹层相关 hook + */ +import { $ } from '@just4/dom'; +import { ref, unref, watchEffect } from 'vue'; + +import { getRefElem } from '@/assets/utils/vue-utils'; +import { useAppendTo } from '@/hooks/behaviors/use-append-to'; +import { useAutoTopmostToShow } from '@/hooks/components/use-popper/use-auto-topmost-show'; + +import { usePopperCore } from './_hooks/use-popper-core'; +import { usePopperInteractive } from './_hooks/use-popper-interactive'; +import { usePopperVisible } from './_hooks/use-popper-visible'; +import { usePopperArrow } from './_hooks/use-popper-arrow'; +import { OnPopperEventParams, PopperOffset, PopperOptions, UsePopperOptions } from './_popper-type'; + +/** + * @hook 弹出层 hook + */ +export const usePopper = (options: UsePopperOptions = {}) => { + /** 触发节点 ref */ + const referenceRef = options.referenceRef ?? ref(); + /** 弹层节点 ref */ + const popperRef = options.popperRef ?? ref(); + /** 输入框节点 ref,用于聚焦显示的交互 */ + const inputRef = options.inputRef ?? ref(); + /** 弹层是否禁用 */ + const popperDisabled = options.popperDisabled ?? ref(false); + /** 触发方式 */ + const trigger = options.trigger ?? ref('click'); + /** hover 触发鼠标移入延迟显示 */ + const mouseEnterDelay = options.mouseEnterDelay ?? ref(100); + /** hover 触发鼠标移入延迟显示 */ + const mouseLeaveDelay = options.mouseLeaveDelay ?? ref(100); + /** 弹层位置 */ + const placement = options.placement ?? ref('bottom'); + /** 弹层偏移量 */ + const popperOffset = options.popperOffset ?? ref([0, 0]); + /** 是否创建箭头 */ + const arrow = options.arrow ?? ref(false); + /** 强制 outSideClick 关闭 */ + const forceOutSideClickHide = options.forceOutSideClickHide ?? ref(false); + /** outSideClick 关闭时是否延迟 */ + const outSideClickDelayHide = options.outSideClickDelayHide ?? ref(false); + /** 弹层宽度跟随触发节点 */ + const followReferenceWidth = options.followReferenceWidth ?? ref(false); + + const { popperVisible, showPopper, hidePopper, togglePopper, delayShowPopper, delayHidePopper } = + usePopperVisible(options); + + // 获取节点信息 + function getElement() { + return { + referenceElem: getRefElem(referenceRef), + popperElem: getRefElem(popperRef), + inputElem: getRefElem(inputRef), + }; + } + + /** 处理弹层 SDK 的事件 */ + function onPopperEvent(params: OnPopperEventParams) { + const { isReferenceHidden } = params; + // 滚动中触发层已隐藏,则将弹层一起隐藏 + if (isReferenceHidden && unref(popperVisible)) { + hidePopper(); + } + } + + watchEffect(() => { + const { referenceElem, popperElem } = getElement(); + if (unref(followReferenceWidth) && unref(popperVisible) && referenceElem && popperElem) { + const clientWidth = referenceElem.clientWidth; + $(popperElem).css({ + width: `${clientWidth}px`, + }); + } + }); + + const popperOptions: PopperOptions = { + referenceRef, + popperRef, + inputRef, + popperDisabled, + trigger, + mouseEnterDelay, + mouseLeaveDelay, + placement, + popperOffset, + arrow, + forceOutSideClickHide, + outSideClickDelayHide, + followReferenceWidth, + popperVisible, + showPopper, + hidePopper, + togglePopper, + delayShowPopper, + delayHidePopper, + getElement, + onPopperEvent, + }; + + useAppendTo(popperRef); + usePopperArrow(popperOptions); + usePopperInteractive(popperOptions); + useAutoTopmostToShow(popperVisible, popperRef); + const { updatePopper } = usePopperCore(popperOptions); + + return { + ...popperOptions, + updatePopper, + }; +}; diff --git a/src/hooks/components/use-popper/use-auto-topmost-show.ts b/src/hooks/components/use-popper/use-auto-topmost-show.ts new file mode 100644 index 0000000000000000000000000000000000000000..719547ab69bebec039c7b8d1a8c506aa55a8e64b --- /dev/null +++ b/src/hooks/components/use-popper/use-auto-topmost-show.ts @@ -0,0 +1,34 @@ +import { PlvPopperManager } from '@/plugins/polyv-ui/admin-import'; +import { Ref, unref, watch } from 'vue'; +import { getRefElem } from '@/assets/utils/vue-utils'; + +/** 弹出层层叠数 z-index 的 hook */ +const usePopperZIndex = () => { + /** 设置弹出层元素到最顶层 */ + const setTopmostElem = (elemRef: RefOrElement) => { + const elem = getRefElem(elemRef); + elem && PlvPopperManager.openPopper(elem); + }; + + return { + setTopmostElem, + }; +}; + +/** + * 设置根据显隐状态自动将节点层叠数置顶 + * @param visibleRef 显隐响应式 + * @param elemRef 弹出层节点响应式 + */ +export const useAutoTopmostToShow = (visibleRef: Ref, elemRef: RefOrElement) => { + const { setTopmostElem } = usePopperZIndex(); + + watch( + () => unref(visibleRef), + () => { + if (unref(visibleRef)) { + setTopmostElem(elemRef); + } + }, + ); +}; diff --git a/src/hooks/components/use-toast/_components/imgs/toast-error.svg b/src/hooks/components/use-toast/_components/imgs/toast-error.svg new file mode 100644 index 0000000000000000000000000000000000000000..d109e14633f68ee7131ee5b7676e178dfa6ae717 --- /dev/null +++ b/src/hooks/components/use-toast/_components/imgs/toast-error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/components/use-toast/_components/imgs/toast-help.svg b/src/hooks/components/use-toast/_components/imgs/toast-help.svg new file mode 100644 index 0000000000000000000000000000000000000000..8b92a223f2a829ed5190632841d6615b5bdcd722 --- /dev/null +++ b/src/hooks/components/use-toast/_components/imgs/toast-help.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/components/use-toast/_components/imgs/toast-info.svg b/src/hooks/components/use-toast/_components/imgs/toast-info.svg new file mode 100644 index 0000000000000000000000000000000000000000..8b93ea538fdb22e6f84b95135c7483714bff01c6 --- /dev/null +++ b/src/hooks/components/use-toast/_components/imgs/toast-info.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/components/use-toast/_components/imgs/toast-loading.svg b/src/hooks/components/use-toast/_components/imgs/toast-loading.svg new file mode 100644 index 0000000000000000000000000000000000000000..cda1d23a0b1bab3f0db9c16a0f8de026841a3ad0 --- /dev/null +++ b/src/hooks/components/use-toast/_components/imgs/toast-loading.svg @@ -0,0 +1,18 @@ + + + icon 加载中 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/hooks/components/use-toast/_components/imgs/toast-success.svg b/src/hooks/components/use-toast/_components/imgs/toast-success.svg new file mode 100644 index 0000000000000000000000000000000000000000..291a09d29e1e5597a76636751e1c1f470c88e80e --- /dev/null +++ b/src/hooks/components/use-toast/_components/imgs/toast-success.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/components/use-toast/_components/imgs/toast-warning.svg b/src/hooks/components/use-toast/_components/imgs/toast-warning.svg new file mode 100644 index 0000000000000000000000000000000000000000..2e1fa4ce77fce10cd3010de8b1cad1fe4a5622ad --- /dev/null +++ b/src/hooks/components/use-toast/_components/imgs/toast-warning.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/hooks/components/use-toast/_components/mobile-toast.vue b/src/hooks/components/use-toast/_components/mobile-toast.vue new file mode 100644 index 0000000000000000000000000000000000000000..68f66bf5afb73d0dec51acba1c053aa092ac8d71 --- /dev/null +++ b/src/hooks/components/use-toast/_components/mobile-toast.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/src/hooks/components/use-toast/_components/pc-toast.vue b/src/hooks/components/use-toast/_components/pc-toast.vue new file mode 100644 index 0000000000000000000000000000000000000000..296223d201290050db35904864c0ecaf19980f5b --- /dev/null +++ b/src/hooks/components/use-toast/_components/pc-toast.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/hooks/components/use-toast/_hooks/use-toast-comp.ts b/src/hooks/components/use-toast/_hooks/use-toast-comp.ts new file mode 100644 index 0000000000000000000000000000000000000000..6c6ab914291d9f0afdf231936843236f70eaddbe --- /dev/null +++ b/src/hooks/components/use-toast/_hooks/use-toast-comp.ts @@ -0,0 +1,154 @@ +import { useVue } from '@/hooks/core/use-vue'; +import { getElement } from '@/assets/utils/dom'; +import { emitFunc, VueEmit } from '@/assets/utils/vue-utils/emit-utils'; +import { FormatProps, PropUtils, VueProps } from '@/assets/utils/vue-utils/props-utils'; +import { computed, onBeforeUnmount, onMounted, ref } from 'vue'; +import { useAutoTopmostToShow } from '@/hooks/components/use-popper/use-auto-topmost-show'; +import { ToastInstance, ToastType } from '../_toast_type'; + +export const toastProps = () => ({ + /** 提示内容 */ + msg: PropUtils.string.def('提示'), + /** 提示类型,默认:info */ + type: PropUtils.enum().def(ToastType.Info), + /** 关闭时间,默认:3000 */ + duration: PropUtils.number.def(3000), + /** 渲染的父容器节点,默认:document.body */ + mountEl: PropUtils.oneOfType([String, HTMLElement]).def( + () => document.body, + ), +}); + +export type ToastPropsType = FormatProps; + +export const toastEmits = () => ({ + /** toast 打开 */ + open: emitFunc(), + /** toast 关闭 */ + close: emitFunc(), + /** 进入之后 */ + 'after-enter': emitFunc(), + /** 离开之后 */ + 'after-leave': emitFunc(), +}); + +/** + * @hook Toast 组件 hook + */ +export const useToastComp = (options: { + props: VueProps; + emit: VueEmit; + /** class 前缀 */ + prefixCls: string; +}) => { + const { getInstance } = useVue(); + + const { props, emit, prefixCls } = options; + const toastRef = ref(); + + /** 元素的 top 样式值 */ + const top = ref(0); + /** 设置提示元素的 top 样式值 */ + function setToastTop(_top: number) { + top.value = _top; + } + + /** 提示的 class */ + const toastClasses = computed(() => { + const classes: string[] = []; + + if (props.type) { + classes.push(`${prefixCls}--${props.type}`); + } + + return classes; + }); + + /** 提示的样式 */ + const toastStyle = computed(() => { + return { + top: `${top.value}px`, + }; + }); + + /** 提示是否显示 */ + const toastVisible = ref(false); + + /** 显示提示 */ + function openToast() { + toastVisible.value = true; + startCloseTimer(); + emit('open'); + } + + /** 关闭提示 */ + function closeToast() { + toastVisible.value = false; + clearCloseTimer(); + emit('close'); + } + + /** 关闭定时器 */ + let closeTimer: number | undefined; + + /** 开启关闭定时器 */ + function startCloseTimer() { + closeTimer = window.setTimeout(() => { + closeToast(); + }, props.duration); + } + + /** 清空关闭定时器 */ + function clearCloseTimer() { + window.clearTimeout(closeTimer); + closeTimer = undefined; + } + + /** 处理动画进入之后 */ + function onAfterEnter() { + emit('after-enter'); + } + + /** 处理动画离开之后 */ + function onAfterLeave() { + emit('after-leave'); + // 销毁组件 + const instance = getInstance(); + const $el = instance?.$el; + $el?.parentElement?.removeChild($el); + + instance?.$destroy(); + } + + onMounted(() => { + const instance = getInstance(); + const parentEl = getElement(props.mountEl); + if (instance?.$el) { + parentEl?.appendChild(instance.$el); + } + }); + + onBeforeUnmount(() => { + clearCloseTimer(); + }); + + useAutoTopmostToShow(toastVisible, toastRef); + + const toastInstance: ToastInstance = { + setToastTop, + openToast, + closeToast, + }; + + return { + toastRef, + toastVisible, + toastClasses, + toastStyle, + openToast, + closeToast, + onAfterEnter, + onAfterLeave, + toastInstance, + }; +}; diff --git a/src/hooks/components/use-toast/_toast_type.ts b/src/hooks/components/use-toast/_toast_type.ts new file mode 100644 index 0000000000000000000000000000000000000000..75ae28d10c0b4a4c31f9c745047a356ba155cfb5 --- /dev/null +++ b/src/hooks/components/use-toast/_toast_type.ts @@ -0,0 +1,29 @@ +/** + * 提示类型 + */ +export enum ToastType { + /** 信息类提示 */ + Info = 'info', + /** 成功类提示 */ + Success = 'success', + /** 警告类提示 */ + Warning = 'warning', + /** 异常类提示 */ + Error = 'error', + /** 帮助类提示 */ + Help = 'help', + /** 加载中提示 */ + Loading = 'loading', +} + +/** + * toast 提示实例 + */ +export interface ToastInstance { + /** 打开提示 */ + openToast(): void; + /** 关闭提示 */ + closeToast(): void; + /** 设置提示 top 样式 */ + setToastTop(top: number): void; +} diff --git a/src/hooks/components/use-toast/index.ts b/src/hooks/components/use-toast/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..06c85df8b0285c67d05ae73cb3b07df0f337f0ad --- /dev/null +++ b/src/hooks/components/use-toast/index.ts @@ -0,0 +1,158 @@ +import Vue from 'vue'; +import { CombinedVueInstance, ExtendedVue } from 'vue/types/vue'; + +import { isMobile } from '@/assets/utils/browser'; + +import PcToast from './_components/pc-toast.vue'; +import MobileToast from './_components/mobile-toast.vue'; +import { ToastPropsType } from './_hooks/use-toast-comp'; +import { ToastInstance, ToastType } from './_toast_type'; + +type ToastConstructorCtorType = ExtendedVue< + Vue, + Record, + ToastInstance, + Record, + Record, + Record +>; + +type ToastManagerOptions = { + /** 提示组件构造器 */ + ToastConstructor: ToastConstructorCtorType; + /** 最大显示数量,默认:5 */ + maxCount?: number; + /** 渲染的父容器 */ + mountEl?: string; +}; + +type ToastInstanceVue = CombinedVueInstance< + Vue, + Record, + ToastInstance, + Record, + Record, + Record +>; +/** 提示管理器 */ +class ToastManager { + /** 用于创建的组件 */ + private __ToastConstructor: ToastConstructorCtorType; + /** 提示队列 */ + private __toastQueue: ToastPropsType[] = []; + /** 当前正在显示的 toast 实例 */ + private __toasts: ToastInstanceVue[] = []; + /** 第一个 toast 的 top */ + private __startTop = 20; + /** 两个 toast 之间的距离 */ + private __instanceSpacing = 16; + /** 最大显示数量 */ + private __maxCount: number; + /** 渲染的父容器 */ + private _mountEl?: string; + + constructor(options: ToastManagerOptions) { + this.__ToastConstructor = options.ToastConstructor; + this.__maxCount = options.maxCount ?? 5; + this._mountEl = options.mountEl; + } + + /** 插入一个提示 */ + private __push(props: ToastPropsType) { + this.__toastQueue.push(props); + this.__checkQueue(); + } + + /** 检查队列 */ + private __checkQueue() { + if (this.__toasts.length >= this.__maxCount || this.__toastQueue.length === 0) { + return; + } + + const joins = this.__toastQueue.splice(0, this.__maxCount - this.__toasts.length); + this.__toastList(joins); + } + + /** + * 提示列表 + */ + private __toastList(list: ToastPropsType[]) { + list.forEach(props => { + const ToastConstructor = this.__ToastConstructor; + + const instance = new ToastConstructor({ + propsData: props, + }); + instance.$mount(); + instance.openToast(); + + this.__toasts.push(instance); + this.__resetTop(); + + instance.$once('close', () => { + this.__remove(instance); + }); + }); + } + + /** 移除提示实例 */ + private __remove(instance: ToastInstance) { + this.__toasts = this.__toasts.filter(_instance => _instance !== instance); + this.__resetTop(); + this.__checkQueue(); + } + + /** 重置提示实例的 top 定位 */ + private __resetTop() { + let toastTop = this.__startTop; + this.__toasts.forEach(instance => { + instance.setToastTop(toastTop); + const elem = instance.$el as HTMLElement; + + toastTop += elem.offsetHeight + this.__instanceSpacing; + }); + } + + /** 信息类提示 */ + public info(msg: string) { + this.__push({ msg, type: ToastType.Info }); + } + + /** 成功类提示 */ + public success(msg: string) { + this.__push({ msg, type: ToastType.Success }); + } + + /** 警告类提示 */ + public warning(msg: string) { + this.__push({ msg, type: ToastType.Warning }); + } + + /** 异常类提示 */ + public error(msg: string) { + this.__push({ msg, type: ToastType.Error }); + } + + /** 帮助类提示 */ + public help(msg: string) { + this.__push({ msg, type: ToastType.Help }); + } + + /** 加载中提示 */ + public loading(msg: string) { + this.__push({ msg, type: ToastType.Loading }); + } +} + +function getToastConstructor() { + if (isMobile) { + return MobileToast; + } + + return PcToast; +} + +/** 主屏消息提示对象 */ +export const toast = new ToastManager({ + ToastConstructor: Vue.extend(getToastConstructor() as unknown as ToastConstructorCtorType), +}); diff --git a/src/hooks/core/use-error-catch/index.ts b/src/hooks/core/use-error-catch/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..bf247a5aecc72246707360340a1f7e4bafecf0bd --- /dev/null +++ b/src/hooks/core/use-error-catch/index.ts @@ -0,0 +1,138 @@ +import { VueConstructor } from 'vue'; + +type VueIns = InstanceType; + +interface ErrorCatchModel { + /** 错误名称 */ + name: string; + /** 错误类型 */ + type: string; + /** 错误消息 */ + msg: string | Event; + /** 对应的目标 */ + target: string; + /** 额外信息 */ + ext: string; +} + +/** 处理 Error 对堆栈信息返回为 msg */ +const processStackMsg = (error: Error) => { + let stack = error.stack || ''; + stack = stack + .replace(/\n/gi, '') + .split(/\bat\b/) + .slice(0, 9) + .join('@') + .replace(/\?[^:]+/gi, ''); + const msg = error.toString(); + stack = stack.indexOf(msg) < 0 ? `${msg}@${stack}` : stack; + return stack; +}; + +/** + * @hook 错误捕获钩子 + * @param cb 回调函数 + */ +export const useErrorCatch = (cb: (data: ErrorCatchModel) => void) => { + /** + * 捕获"暴露到全局错误"的钩子函数 + */ + function windowErrorHandler() { + window.onerror = ( + message: string | Event, + target?: string, + row?: number, + col?: number, + error?: Error, + ) => { + const msg = error && error.stack ? processStackMsg(error) : message; + + const data: ErrorCatchModel = { + name: (error && error.name) || 'uncatchErrorName', + type: 'windowError', + msg, + target: target || 'global', + ext: JSON.stringify({ + row, + col, + }), + }; + cb(data); + }; + } + + /** + * 捕获"promise 暴露到全局错误"的钩子函数 + */ + function promiseErrorHandler() { + window.addEventListener('unhandledrejection', event => { + if (event.reason instanceof Error) { + event.preventDefault(); + throw event.reason; + } + const data: ErrorCatchModel = { + name: event.type, + type: 'promiseError', + msg: event.reason, + target: 'global', + ext: '', + }; + cb(data); + }); + } + + /** + * 提供接管 Vue 错误的钩子函数 + */ + function vueErrorHandler(error: Error, vm: VueIns, info: string) { + const msg = error && error.stack ? processStackMsg(error) : error.toString(); + + const target = vm.$options.name || vm.$options.__name || 'notFound'; + + const data: ErrorCatchModel = { + name: error ? error.name : 'uncatchErrorName', + type: 'vueCatchError', + msg, + target: `Component/Tag:${target}`, + ext: JSON.stringify({ + info: `Error In ${info}`, + }), + }; + cb(data); + + throw error; // 抛还给 Vue,这里抛出的错误不会造成重复记错 + } + + /** + * 提供在 try catch 进行错误捕获的钩子函数,,可以挂在 Vue.prototype 上 + * vm 支持用 vue 实例,也可以传一个字符串之类的 + */ + function tryCatchHandler(error: Error, vm: VueIns | string, info: string) { + const msg = error && error.stack ? processStackMsg(error) : error.toString(); + + const target = + typeof vm === 'string' + ? vm + : vm && vm.$options + ? `Component/Tag:${vm.$options.name || vm.$options.__name || 'notFound'}` + : String(vm); + + const data: ErrorCatchModel = { + name: error && error.name, + type: 'tryCatchError', + msg, + target, + ext: JSON.stringify({ + info: `Error In ${info}`, + }), + }; + cb(data); + } + + return { + windowErrorHandler, + promiseErrorHandler, + vueErrorHandler, + tryCatchHandler, + }; +}; diff --git a/src/hooks/core/use-error-verify/index.ts b/src/hooks/core/use-error-verify/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..65f9eda622ce1b427f2a1f5df28ca558db8cc004 --- /dev/null +++ b/src/hooks/core/use-error-verify/index.ts @@ -0,0 +1,29 @@ +import { useChannelStore } from '@/store/use-channel-store'; +import { useLangStore } from '@/store/use-lang-store'; +import { appendToURL } from '@just4/querystring'; + +/** 错误页类型 */ +export enum ErrorVerifyType { + /** 超出最大并发数 */ + Restrict = 'restrict', +} + +export interface RedirectErrorParams { + /** 异常类型 */ + errorType?: ErrorVerifyType; +} + +/** + * 跳转到错误页 + */ +export function redirectToErrorVerify(params: RedirectErrorParams = {}) { + const channelStore = useChannelStore(); + const langStore = useLangStore(); + + const errorPageUrl = appendToURL('/error-verify', { + channelId: channelStore.channelId, + lang: langStore.currentLang, + ...params, + }); + window.location.replace(errorPageUrl); +} diff --git a/src/hooks/core/use-query-params/index.ts b/src/hooks/core/use-query-params/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ce5e2bfc6e9fc133dcfb88457af95220b8fc3ffb --- /dev/null +++ b/src/hooks/core/use-query-params/index.ts @@ -0,0 +1,116 @@ +/** + * @file 当前观看页地址上的参数 + */ + +import { ChannelWatchPageSkin, PlaybackOrigin, ZoneType } from '@polyv/live-watch-sdk'; +import { parse } from '@just4/querystring'; + +/** 获取默认参数值 */ +const getDefaultQueryParams = (): UniversalParams => ({}); + +/** 地址上所有的参数 */ +export const queryParams: { + [key: string]: string | string[] | undefined; +} = parse(window.location.search && window.location.search.slice(1)) || {}; + +/** + * 获取当前观看页地址上的所有参数的对象 + * @desc 如果从地址上获取不到,会从默认值中获取,如果默认值也获取不到,则返回 undefined + * */ +export function getQueryParam(key: string): string | undefined { + const result = queryParams[key]; + + if (typeof result !== 'undefined') { + if (Array.isArray(result)) { + console.warn(`${key} 的值为数组,当前仅返回数组中的第一个元素`); + return result[0]; + } + return result; + } + + return getDefaultQueryParams()[key]; +} + +const buildGetters = (key: string) => { + return (): T | undefined => { + return getQueryParam(key) as T | undefined; + }; +}; + +/** + * @hook 参数获取器 + * @desc 需要获取地址上的参数都必须通过该获取器获取 + */ +export const paramGetter = { + /** 频道号 */ + channelId: buildGetters('channelId'), + /** 会议模式 */ + polyvMeeting: buildGetters<'true'>('polyvMeeting'), + /** 是否加载 VConsole */ + console: buildGetters<'1'>('console'), + /** 多语言 */ + lang: buildGetters('lang'), + /** 回放 vid */ + vid: buildGetters('vid'), + /** 回放类型 */ + playbackType: buildGetters('playbackType'), + /** 渠道 id */ + promoteId: buildGetters('promoteId'), + /** 企业微信分享海报的企微员工 ID */ + wxInviteId: buildGetters('wxInviteId'), + /** 邀请海报唯一ID */ + invitePosterId: buildGetters('invitePosterId'), + /** 受邀者 ID */ + invitee: buildGetters('invitee'), + /** 测试模式 token */ + testModeToken: buildGetters('testModeToken'), + /** 观众 id */ + viewerid: buildGetters('viewerid'), + /** 观众昵称 */ + name: buildGetters('name'), + /** 观众昵称 */ + nickname: buildGetters('nickname'), + /** 跑马灯名称 */ + marqueeName: buildGetters('marqueeName'), + /** 观众头像 */ + avatar: buildGetters('avatar'), + /** 网络类型 */ + zone: buildGetters('zone'), + /** 是否被 iframe */ + hasFrame: buildGetters<'1'>('hasFrame'), + /** 自定义授权 & 外部授权 & 独立授权中的用户 id */ + userid: buildGetters('userid'), + /** 参与者头衔 */ + actor: buildGetters('actor'), + /** 参与者头衔背景色 */ + actorBgColor: buildGetters('actorBgColor'), + /** 参与者头衔字体颜色 */ + actorFColor: buildGetters('actorFColor'), + /** 自定义授权 & 外部授权 & 独立授权中的时间戳 */ + ts: buildGetters('ts'), + /** 自定义授权 & 外部授权 & 独立授权中的签名 */ + sign: buildGetters('sign'), + /** 微信 openId */ + openid: buildGetters('openid'), + /** 微信 unionId */ + unionid: buildGetters('unionid'), + /** 微信用户 id */ + wechatUserId: buildGetters('wechatUserId'), + /** 播放器 param4 */ + param4: buildGetters('param4'), + /** 播放器 param5 */ + param5: buildGetters('param5'), + /** 真实姓名 */ + realName: buildGetters('realName'), + /** 多皮肤 */ + skin: buildGetters('skin'), +}; + +window.paramGetter = paramGetter; + +export const useQueryParam = () => { + return { + paramGetter, + getQueryParam, + }; +}; diff --git a/src/hooks/core/use-screen-orient/index.ts b/src/hooks/core/use-screen-orient/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..054b4afbea1dd375348c506b32f3b25732bc7670 --- /dev/null +++ b/src/hooks/core/use-screen-orient/index.ts @@ -0,0 +1,79 @@ +import { computed, onMounted, ref } from 'vue'; + +/** + * 屏幕旋转模式 + * @desc 不使用 portrait 和 landscape 是不想和业务名称重合 + * */ +export enum ScreenOrientationMode { + /** 屏幕纵向 */ + Vertical = 'Vertical', + /** 屏幕横向 */ + Horizontal = 'Horizontal', +} + +/** + * @hook 屏幕旋转 + */ +export const useScreenOrientHook = (params: { + /** 自动监听事件 */ + autoListen?: boolean; + /** 自动监听回调 */ + autoListenCb?: (screenOrientationMode: ScreenOrientationMode) => void; +}) => { + const { autoListen = true, autoListenCb = () => {} } = params; + + /** 屏幕旋转模式 */ + const screenOrientationMode = ref(ScreenOrientationMode.Vertical); + + /** 当前屏幕旋转角度是否为纵向 */ + const isVerticalScreenOrientation = computed(() => { + return screenOrientationMode.value === ScreenOrientationMode.Vertical; + }); + + /** 当前屏幕旋转角度是否为横向 */ + const isHorizontalScreenOrientation = computed(() => { + return screenOrientationMode.value === ScreenOrientationMode.Horizontal; + }); + + /** 手动更新当前屏幕的旋转方向 */ + function updateScreenOrientationModeManually() { + if (window.orientation === 180 || window.orientation === 0) { + screenOrientationMode.value = ScreenOrientationMode.Vertical; + } + if (window.orientation === 90 || window.orientation === -90) { + screenOrientationMode.value = ScreenOrientationMode.Horizontal; + } + } + + /** 监听屏幕旋转方向的改变 */ + function onOrientationChange() { + setTimeout(() => { + const mql = window.matchMedia('(orientation: portrait)'); + if (mql?.matches) { + screenOrientationMode.value = ScreenOrientationMode.Vertical; + } else { + screenOrientationMode.value = ScreenOrientationMode.Horizontal; + } + autoListenCb(screenOrientationMode.value); + }, 200); + } + + onMounted(() => { + if (autoListen) { + // 处理屏幕旋转 + window.addEventListener( + 'onorientationchange' in window ? 'orientationchange' : 'resize', + onOrientationChange, + false, + ); + onOrientationChange(); + } + }); + + return { + screenOrientationMode, + isVerticalScreenOrientation, + isHorizontalScreenOrientation, + updateScreenOrientationModeManually, + }; +}; diff --git a/src/hooks/core/use-vue/index.ts b/src/hooks/core/use-vue/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5cfc856d9e2cccb280dbac656b9b56d30c5e2784 --- /dev/null +++ b/src/hooks/core/use-vue/index.ts @@ -0,0 +1,38 @@ +import { getCurrentInstance, VNode } from 'vue'; + +/** + * @hook 对 Vue 一些方法的简单封装 + */ +export const useVue = () => { + /** 当前 vue 实例 */ + const instanceVm = getCurrentInstance(); + + /** 获取当前 vue 实例对象 */ + function getInstance() { + return instanceVm?.proxy; + } + + /** 获取当前组件的 dom 节点 */ + function getCurrentElem() { + const instance = getInstance(); + return instance?.$el; + } + + /** 获取当前组件的子组件 */ + function getCurrentChildrens() { + return getInstance()?.$children; + } + + /** 获取指定的 slot */ + function getSlot(slotName = 'default'): VNode[] { + const instance = getInstance(); + return (instance?.$slots && instance.$slots[slotName]) || []; + } + + return { + getInstance, + getCurrentElem, + getCurrentChildrens, + getSlot, + }; +}; diff --git a/src/hooks/core/use-window-resize-listener/index.ts b/src/hooks/core/use-window-resize-listener/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..227c2831e5c3141dbbbed4167220acb33cd71fc8 --- /dev/null +++ b/src/hooks/core/use-window-resize-listener/index.ts @@ -0,0 +1,39 @@ +import { onBeforeUnmount, onMounted } from 'vue'; +import { debounce } from 'lodash-es'; + +/** + * @hook 监听 windows resize 事件 + */ +export const useWindowResizeListener = ( + callback: () => unknown, + autoListen = false, + debounceTime = 0, +) => { + const debounceFunc = debounceTime === 0 ? callback : debounce(callback, debounceTime); + + /** 监听 resize 事件 */ + function bindWindowResize() { + unbindWindowResize(); + window.addEventListener('resize', debounceFunc); + } + + /** 移除 resize 事件监听 */ + function unbindWindowResize() { + window.removeEventListener('resize', debounceFunc); + } + + if (autoListen) { + onMounted(() => { + bindWindowResize(); + }); + } + + onBeforeUnmount(() => { + unbindWindowResize(); + }); + + return { + bindWindowResize, + unbindWindowResize, + }; +}; diff --git a/src/hooks/platform/use-polyv-watch-domain/index.ts b/src/hooks/platform/use-polyv-watch-domain/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ef804d57d069c032b2ef2cec8f6db131f65c7a8 --- /dev/null +++ b/src/hooks/platform/use-polyv-watch-domain/index.ts @@ -0,0 +1,37 @@ +/** + * @file 泛域名 hook + */ + +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { RedirectWatchDomainPayOptions, WatchDomainPayType, YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { computed, unref } from 'vue'; + +/** + * @hook 泛域名处理 + * */ +export const usePolyvWatchDomain = () => { + const { channelDetail } = storeDefinitionToRefs(useChannelStore); + + /** 泛域名开关 */ + const polyvWatchDomainEnabled = computed(() => { + return ynToBool(unref(channelDetail)?.channelConfig.polyvWatchDomainEnabled, YN.N); + }); + + /** + * 跳转到泛域名支付 - 仅支持微信支付 + */ + async function skipWatchDomainPay( + options: RedirectWatchDomainPayOptions, + ) { + const watchCore = getWatchCore(); + watchCore.weixin.redirectWatchDomainPay(options); + } + + return { + polyvWatchDomainEnabled, + skipWatchDomainPay, + }; +}; diff --git a/src/hooks/platform/use-weixin/use-weixin-authorize.ts b/src/hooks/platform/use-weixin/use-weixin-authorize.ts new file mode 100644 index 0000000000000000000000000000000000000000..9490ba3a1ea381451b4ac20fcc83503995d09b94 --- /dev/null +++ b/src/hooks/platform/use-weixin/use-weixin-authorize.ts @@ -0,0 +1,118 @@ +/** + * @file 微信授权 hook + */ +import { isWeixin, isWorkWeixin } from '@/assets/utils/browser'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { computed } from 'vue'; + +export interface WxAuthorizeOptions { + /** 是否使用静默授权,默认:true */ + snsApiBase?: boolean; +} + +/** 微信授权跳转参数 */ +export interface WxAuthorizeParams { + /** 频道号 */ + channelId: string; + /** 授权 token */ + xAuthToken?: string; + /** 是否非静默授权 */ + watch?: 1; +} + +/** + * @hook 微信授权 + */ +export function useWeixinAuthorize() { + const channelStore = useChannelStore(); + + /** 能否进行微信授权 */ + const canWeixinAuthorize = computed(() => { + // 非微信环境 + if (!isWeixin) { + return false; + } + + // 当前是企业微信 + if (isWorkWeixin) { + return false; + } + + // 是否忽略微信授权 + const ignoreWx = ynToBool(channelStore.channelDetail?.channelConfig.ignoreWx, YN.N); + if (ignoreWx) { + return false; + } + + return true; + }); + + /** + * 跳转微信授权地址 + */ + async function redirectWeixinAuthorize(authorizeOptions: WxAuthorizeOptions = {}) { + const watchCore = getWatchCore(); + await watchCore.weixin.redirectWeixinAuthorizeUrl({ + snsApiBase: authorizeOptions.snsApiBase, + }); + } + + return { + /** 能否进行微信授权 */ + canWeixinAuthorize, + /** 跳转微信授权地址 */ + redirectWeixinAuthorize, + }; +} + +/** + * 企业微信授权 hook + */ +export function useWorkWeixinAuthorize() { + const channelStore = useChannelStore(); + + /** 能否进行企业微信授权 */ + const canWorkWeixinAuthorize = computed(() => { + // 非企业微信 + if (!isWorkWeixin) { + return false; + } + + // 没有绑定企微 + if (!ynToBool(channelStore.channelDetail?.channelConfig.wxWorkStatusSwitch, YN.N)) { + return false; + } + + // 没开启企业微信授权开关 + if (!ynToBool(channelStore.channelDetail?.channelConfig.wxWorkAuthSwitch, YN.N)) { + return false; + } + + // 没开启员工邀请 + if (!ynToBool(channelStore.channelDetail?.channelConfig.wxWorkInviteEnabled, YN.N)) { + return false; + } + + return true; + }); + + /** + * 跳转企业微信授权地址 + */ + async function redirectWorkWeixinAuthorize(authorizeOptions: WxAuthorizeOptions = {}) { + const watchCore = getWatchCore(); + await watchCore.weixin.redirectWorkWeixinAuthorizeUrl({ + snsApiBase: authorizeOptions.snsApiBase, + }); + } + + return { + /** 能否进行企业微信授权 */ + canWorkWeixinAuthorize, + /** 跳转企业微信授权地址 */ + redirectWorkWeixinAuthorize, + }; +} diff --git a/src/hooks/platform/use-weixin/use-weixin-pay.ts b/src/hooks/platform/use-weixin/use-weixin-pay.ts new file mode 100644 index 0000000000000000000000000000000000000000..2803c7450845a1a87715ab85ffe3478ffb64ad01 --- /dev/null +++ b/src/hooks/platform/use-weixin/use-weixin-pay.ts @@ -0,0 +1,58 @@ +import { isFunction } from '@/assets/utils/function'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useWeixinSdk } from './use-weixin-sdk'; +import { LoggerName } from '@polyv/live-watch-sdk'; + +/** 微信的回调方法 */ +type WeixinGeneralCallback = (res?: WechatJsSdk.GeneralCallbackResult) => void; + +export interface WxPayCallback { + /** 成功的回调 */ + successCb?: WeixinGeneralCallback; + /** 取消的回调 */ + cancelCb?: WeixinGeneralCallback; +} + +/** + * @hook 微信支付 + */ +export function useWeixinPay() { + const { getWeixinSdk } = useWeixinSdk(); + + /** + * 唤起或监听微信 sdk 初始化完成后唤起微信支付 + * @param payInfo 微信支付相关信息 + * @param callback 支付相关回调 + */ + async function chooseWXPay(payInfo: WechatJsSdk.ChooseWXPayOption, callback: WxPayCallback = {}) { + const { successCb, cancelCb } = callback; + const wx = await getWeixinSdk(); + + wx && + wx.chooseWXPay({ + timestamp: payInfo.timestamp, + nonceStr: payInfo.nonceStr, + package: payInfo.package, + signType: payInfo.signType, + paySign: payInfo.paySign, + success: res => { + if (isFunction(successCb)) { + successCb(res); + } + }, + cancel: res => { + if (isFunction(cancelCb)) { + cancelCb(res); + } + }, + fail: res => { + const watchCore = getWatchCore(); + watchCore.logger.error(LoggerName.Weixin, 'choose-wx-pay-fail', JSON.stringify(res)); + }, + }); + } + + return { + chooseWXPay, + }; +} diff --git a/src/hooks/platform/use-weixin/use-weixin-sdk.ts b/src/hooks/platform/use-weixin/use-weixin-sdk.ts new file mode 100644 index 0000000000000000000000000000000000000000..03125e46fb0eb044d6884bf04250ea5990fe2016 --- /dev/null +++ b/src/hooks/platform/use-weixin/use-weixin-sdk.ts @@ -0,0 +1,119 @@ +import { isWorkWeixin } from '@/assets/utils/browser'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { loadWorkWxSdk, loadWxSdk } from '@/plugins/external-lib-loaders/load-wx-sdk'; +import { LoggerName } from '@polyv/live-watch-sdk'; + +const wxLoaders: Record | undefined> = {}; + +// eslint-disable-next-line sonarjs/cognitive-complexity +export function getWx(channelId: string): Promise { + let promiseTarget = wxLoaders[channelId]; + if (promiseTarget) { + return promiseTarget; + } + + promiseTarget = new Promise(resolve => { + (async () => { + const watchCore = getWatchCore(); + const [wx, signRes] = await Promise.all([ + isWorkWeixin ? loadWorkWxSdk() : loadWxSdk(), + isWorkWeixin ? watchCore.weixin.getWorkWeixinSign() : watchCore.weixin.getWeixinSign(), + ]); + + // 保存微信分享信息 + const weixinStore = useWeixinStore(); + if (signRes) { + weixinStore.saveWxShareInfo({ + isUseWxShareUrl: signRes.isUseWxShareUrl, + wxShareUrl: signRes.wxShareUrl, + shareTitle: signRes.shareTitle, + shareDesc: signRes.shareDesc, + }); + } + + // 配置微信分享 + const { appId, timestamp, nonceStr, signature } = signRes?.wxSign || {}; + + const configData = { + appId, + timestamp, + nonceStr, + signature, + }; + + console.info('wx.config', configData); + + wx.config({ + appId, + timestamp, + nonceStr, + signature, + jsApiList: [ + 'updateAppMessageShareData', + 'updateTimelineShareData', + 'hideAllNonBaseMenuItem', + 'chooseWXPay', + 'previewImage', + 'onMenuShareAppMessage', + 'onMenuShareWechat', + 'shareAppMessage', + 'shareWechatMessage', + 'onMenuShareTimeline', + ], + openTagList: ['wx-open-launch-weapp'], + }); + + wx.error(error => { + console.error('Wx sdk: error', error); + + const watchCore = getWatchCore(); + const logMsg = { + href: location.href, + error: JSON.stringify(error), + configData, + }; + watchCore.logger.error(LoggerName.Weixin, 'config-js-sdk-fail', JSON.stringify(logMsg)); + }); + + wx.ready(() => { + console.info('Wx sdk: success'); + resolve(wx); + + // 获取是否微信小程序环境 + try { + wx.miniProgram?.getEnv(res => { + if (res?.miniprogram === true) { + const watchAppStore = useWatchAppStore(); + watchAppStore.isWxMiniProgram = true; + } + }); + } catch (e) { + console.error('miniProgram error', e); + } + }); + })(); + }); + + wxLoaders[channelId] = promiseTarget; + + return promiseTarget; +} + +/** + * @hook 使用微信 JS-SDK + */ +export const useWeixinSdk = () => { + const channelStore = useChannelStore(); + + /** 获取微信 js-sdk */ + const getWeixinSdk = () => { + return getWx(channelStore.channelId); + }; + + return { + getWeixinSdk, + }; +}; diff --git a/src/hooks/platform/use-weixin/use-weixin-share.ts b/src/hooks/platform/use-weixin/use-weixin-share.ts new file mode 100644 index 0000000000000000000000000000000000000000..93187dd1f6a8f08b265b8de38c5134b1f0864fd4 --- /dev/null +++ b/src/hooks/platform/use-weixin/use-weixin-share.ts @@ -0,0 +1,177 @@ +import { isWeixin, isWorkWeixin } from '@/assets/utils/browser'; +import { fitProtocol } from '@/assets/utils/url'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelInfoStore } from '@/store/use-channel-info-store'; +import { useInviteStore } from '@/store/use-invite-store'; +import { useShareStore } from '@/store/use-share-store'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { appendToURL } from '@just4/querystring'; +import { YN, LoggerName } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { computed, unref } from 'vue'; +import { useWeixinSdk } from './use-weixin-sdk'; + +/** 微信分享地址中附带的参数 */ +interface WxShareUrlParams { + /** 企微邀请的员工 id */ + wxInviteId?: string; + /** 邀请人 id */ + invitePosterId?: string; +} + +/** + * @hook 微信分享 + * */ +export const useWeixinShare = () => { + const { getWeixinSdk } = useWeixinSdk(); + + const channelInfoStore = useChannelInfoStore(); + const shareStore = useShareStore(); + const weixinStore = useWeixinStore(); + const inviteStore = useInviteStore(); + + /** 微信分享地址上需要携带的参数 */ + const wxShareUrlParams = computed(() => { + const params: WxShareUrlParams = {}; + + // 企微邀请的员工 id + if (inviteStore.wxInviteId) { + params.wxInviteId = inviteStore.wxInviteId; + } + + // 针对开启邀请的频道添加邀请参数到微信分享 url + if (inviteStore.invitePosterEnabled && inviteStore.invitePosterId) { + params.invitePosterId = inviteStore.invitePosterId; + } + + return params; + }); + + /** 微信分享中的分享地址 */ + const wxShareUrl = computed(() => { + const shareInfo = weixinStore.wxShareInfo; + // 使用后台设置的自定义分享地址 + if (ynToBool(shareInfo.isUseWxShareUrl, YN.N)) { + return shareInfo.wxShareUrl; + } + + return appendToURL(window.location.href, unref(wxShareUrlParams)); + }); + + /** 隐藏微信分享 */ + async function hideWeixinShare() { + const wx = await getWeixinSdk(); + wx && wx.hideAllNonBaseMenuItem(); + } + + /** 设置微信分享 */ + async function configureWeixinShare() { + // 后台没开启分享开关,隐藏微信分享 + if (!shareStore.shareBtnEnabled) { + return hideWeixinShare(); + } + + const wx = await getWeixinSdk(); + const watchCore = getWatchCore(); + + const shareInfo = weixinStore.wxShareInfo; + const shareOptions = { + title: shareInfo.shareTitle, + desc: shareInfo.shareDesc || '', + link: unref(wxShareUrl), + imgUrl: fitProtocol(channelInfoStore.channelCoverImg), + }; + + console.info('weixin share info', shareOptions); + + if (isWeixin && !isWorkWeixin) { + // 自定义“分享给朋友”及“分享到QQ”按钮的分享内容 + wx && + wx.updateAppMessageShareData({ + ...shareOptions, + success() { + console.info('Wx sdk: updateAppMessageShareData success'); + }, + fail(e) { + watchCore.logger.error( + LoggerName.Weixin, + 'update-app-message-share-fail', + JSON.stringify(e), + ); + }, + }); + + // 自定义“分享到朋友圈”及“分享到 QQ 空间”按钮的分享内容 + wx && + wx.updateTimelineShareData({ + ...shareOptions, + success() { + console.info('Wx sdk: updateTimelineShareData success'); + }, + fail(e) { + watchCore.logger.error( + LoggerName.Weixin, + 'update-timeline-share-fail', + JSON.stringify(e), + ); + }, + }); + } + + if (isWorkWeixin) { + // 自定义转发 + wx.onMenuShareAppMessage && + wx.onMenuShareAppMessage({ + ...shareOptions, + success() { + console.info('Wx sdk: onMenuShareAppMessage success'); + }, + fail(e) { + watchCore.logger.error( + LoggerName.Weixin, + 'on-menu-share-app-message-fail', + JSON.stringify(e), + ); + }, + }); + + wx.onMenuShareWechat && + wx.onMenuShareWechat({ + ...shareOptions, + success() { + console.info('Wx sdk: onMenuShareWechat success'); + }, + fail(e) { + watchCore.logger.error( + LoggerName.Weixin, + 'on-menu-share-share-wechat-fail', + JSON.stringify(e), + ); + }, + }); + + // 自定义分享到微信朋友圈 + wx.onMenuShareTimeline && + wx.onMenuShareTimeline({ + ...shareOptions, + success() { + console.info('Wx sdk: onMenuShareTimeline success'); + }, + fail(e) { + watchCore.logger.error( + LoggerName.Weixin, + 'on-menu-share-timeline-fail', + JSON.stringify(e), + ); + }, + }); + } + } + + return { + wxShareUrlParams, + wxShareUrl, + hideWeixinShare, + configureWeixinShare, + }; +}; diff --git a/src/hooks/tools/use-count-down/index.ts b/src/hooks/tools/use-count-down/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..4ff6af7ac5a8ead12a2d8daadb57b9644484fd20 --- /dev/null +++ b/src/hooks/tools/use-count-down/index.ts @@ -0,0 +1,114 @@ +import { SimilarResponsive } from '@/assets/utils/vue-utils'; +import { Countdown, IRemaining as CountDownSurplusData } from '@utils-ts/countdown'; +import { computed, onUnmounted, ref, unref } from 'vue'; + +export interface UseSecondCountDownOptions { + /** 总秒数,单位秒 */ + second?: SimilarResponsive; + /** 结束的时间戳 */ + endTimestamp?: SimilarResponsive; + /** 倒计时剩余时间改变回调 */ + onCountDownChange?: (surplusData: CountDownSurplusData) => void; + /** 倒计时结束回调 */ + onCountDownFinish?: () => void; +} + +/** + * @hook 简单的秒数倒计时 + */ +export const useSecondCountDown = (options: UseSecondCountDownOptions = {}) => { + const second = options.second ?? ref(); + const endTimestamp = options.endTimestamp ?? ref(); + const onCountDownChange = options.onCountDownChange; + const onCountDownFinish = options.onCountDownFinish; + + /** 默认的倒计时数据 */ + const defaultSurplusData: CountDownSurplusData = { + // 天数 + days: 0, + // 小时数 + hours: 0, + // 分钟数 + minutes: 0, + // 秒数 + seconds: 0, + // 剩余总毫秒数 + totalMsecs: 0, + }; + + /** 剩余时间数据 */ + const surplusTime = ref({ ...defaultSurplusData }); + + /** 根据入参选项获取需要计算的秒数 */ + const computedSecond = computed(() => { + const secondVal = unref(second); + const endTimestampVal = unref(endTimestamp); + + if (typeof secondVal === 'number') { + return secondVal; + } else if (typeof endTimestampVal === 'number') { + const diffTime = (endTimestampVal - Date.now()) / 1000; + return diffTime < 0 ? 0 : diffTime; + } + + return 0; + }); + + /** 倒计时对象实例 */ + let countDownInstance: Countdown | undefined; + + /** 初始化倒计时 */ + function initCountDown(): void { + surplusTime.value = { ...defaultSurplusData }; + destroyCountDown(); + countDownInstance = new Countdown(unref(computedSecond), onCountDownCallback); + startCountDown(); + } + + /** 销毁倒计时 */ + function destroyCountDown(): void { + stopCountDown(); + countDownInstance = undefined; + } + + /** 开始倒计时 */ + function startCountDown(): void { + if (countDownInstance) { + countDownInstance.start(); + } else { + // 如果倒计时对象不存在则自动创建一个 + initCountDown(); + } + } + + /** 暂停倒计时 */ + function stopCountDown(): void { + countDownInstance && countDownInstance.stop(); + } + + /** + * 处理倒计时时间改变 + * @param rest 倒计时剩余时间数据 + */ + function onCountDownCallback(rest: CountDownSurplusData) { + surplusTime.value = rest; + onCountDownChange && onCountDownChange(unref(surplusTime)); + + if (rest.totalMsecs === 0) { + onCountDownFinish && onCountDownFinish(); + } + } + + onUnmounted(() => { + destroyCountDown(); + }); + + return { + surplusTime, + computedSecond, + initCountDown, + startCountDown, + stopCountDown, + destroyCountDown, + }; +}; diff --git a/src/hooks/tools/use-format/index.ts b/src/hooks/tools/use-format/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..c828b8447b59ae9733460aef7bed7d3e8c69ffc6 --- /dev/null +++ b/src/hooks/tools/use-format/index.ts @@ -0,0 +1,37 @@ +/** + * @file 格式化相关的 hook + */ + +import { unref, watch, Ref } from 'vue'; + +/** + * @hook 格式化价格 + * */ +export const useFormatPrice = (priceRef: Ref) => { + /** 监听并格式化成正确的价格格式 */ + watch( + () => unref(priceRef), + val => { + let newVal = `${val}`; + + newVal = newVal && newVal.replace(/[^0-9.]/g, ''); + + if (newVal.indexOf('.') !== newVal.lastIndexOf('.')) { + newVal = + newVal.substring(0, newVal.lastIndexOf('.')) + + newVal.substring(newVal.lastIndexOf('.') + 1); + } + + priceRef.value = newVal; + }, + ); + + /** 将当前价格随机 */ + function randomPrice(): void { + priceRef.value = (Math.random() * 10).toFixed(2); + } + + return { + randomPrice, + }; +}; diff --git a/src/hooks/tools/use-fullscreen/index.ts b/src/hooks/tools/use-fullscreen/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..10f48bad8c93212a0c724cf1706b644a1f3603c6 --- /dev/null +++ b/src/hooks/tools/use-fullscreen/index.ts @@ -0,0 +1,148 @@ +/** + * @file 浏览器全屏 hook + */ +import { useLayoutStore } from '@/store/use-layout-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { computed, onBeforeUnmount, Ref, unref } from 'vue'; + +export interface FullscreenFieldData { + /** 浏览器是否支持全屏模式的 api 字段 */ + enabledField: 'fullscreenEnabled'; + /** 获取浏览器正在全屏的 dom 节点 api 字段 */ + elementField: 'fullscreenElement'; + /** 浏览器全屏的 api 字段 */ + requestApiField: 'requestFullscreen'; + /** 浏览器退出全屏的 api 字段 */ + exitApiField: 'exitFullscreen'; + /** 浏览器全屏状态改变事件 */ + fullscreenChangeEvent: 'fullscreenchange'; +} + +// 各浏览器全屏接口 +const apis = [ + { + enabledField: 'fullscreenEnabled', + elementField: 'fullscreenElement', + requestApiField: 'requestFullscreen', + exitApiField: 'exitFullscreen', + fullscreenChangeEvent: 'fullscreenchange', + }, + { + enabledField: 'webkitFullscreenEnabled', + elementField: 'webkitCurrentFullScreenElement', + requestApiField: 'webkitRequestFullscreen', + exitApiField: 'webkitExitFullscreen', + fullscreenChangeEvent: 'webkitfullscreenchange', + }, + { + enabledField: 'mozFullScreenEnabled', + elementField: 'mozFullScreenElement', + requestApiField: 'mozRequestFullScreen', + exitApiField: 'mozCancelFullScreen', + fullscreenChangeEvent: 'mozfullscreenchange', + }, + { + enabledField: 'msFullscreenEnabled', + elementField: 'msFullscreenElement', + requestApiField: 'msRequestFullscreen', + exitApiField: 'msExitFullscreen', + fullscreenChangeEvent: 'MSFullscreenChange', + }, +]; + +let fieldData: FullscreenFieldData | undefined; + +// 遍历 apis,获取当前可以用的全屏 api +for (let i = 0; i < apis.length; i++) { + const item = apis[i]; + if (item.enabledField in document) { + fieldData = item as FullscreenFieldData; + break; + } +} + +export type FullscreenElement = Element | Ref; + +export type GetFullscreenElement = () => FullscreenElement; + +/** + * @hook 全屏处理 + */ +export const useFullscreen = ( + elementRef: FullscreenElement | GetFullscreenElement = document.documentElement, + // eslint-disable-next-line sonarjs/cognitive-complexity +) => { + const { isFullscreen } = storeDefinitionToRefs(useLayoutStore); + + /** 是否支持浏览器全屏 */ + const supportFullscreen = computed(() => !!fieldData); + + const fullscreenElement = computed(() => { + if (typeof elementRef === 'function') { + const res = elementRef(); + return unref(res) || document.documentElement; + } + + return unref(elementRef) || document.documentElement; + }); + + /** 全屏 */ + function requestFullscreen() { + const element = unref(fullscreenElement); + if (!fieldData || !element[fieldData.requestApiField]) { + return; + } + element[fieldData.requestApiField](); + } + + /** 退出全屏 */ + function exitFullscreen() { + if (!fieldData) { + return; + } + document[fieldData.exitApiField](); + } + + /** 切换全屏状态 */ + function toggleFullscreen() { + if (!fieldData) { + return; + } + if (!document[fieldData.elementField]) { + requestFullscreen(); + } else { + exitFullscreen(); + } + } + + /** 处理全屏事件 */ + function onFullscreenChange() { + if (!fieldData) { + return; + } + if (document[fieldData.elementField]) { + isFullscreen.value = true; + } else { + isFullscreen.value = false; + } + } + + if (fieldData) { + document.addEventListener(fieldData.fullscreenChangeEvent, onFullscreenChange); + } + + onBeforeUnmount(() => { + if (!fieldData) { + return; + } + document.removeEventListener(fieldData.fullscreenChangeEvent, onFullscreenChange); + }); + + return { + isFullscreen, + supportFullscreen, + requestFullscreen, + exitFullscreen, + toggleFullscreen, + }; +}; diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000000000000000000000000000000000000..21c26b9e67306a33947fc0c0d96657572ee51bd4 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,44 @@ +import Vue from 'vue'; +import WatchApp from './app/watch-app.vue'; +import { pinia } from '@/plugins/pinia'; + +import { i18nInstall } from '@/assets/lang'; +import { useLangStore } from '@/store/use-lang-store'; + +import '@/assets/styles/animation.scss'; +import '@/plugins/vconsole'; + +console.info( + '%c WatchApp build info: %s, %s', + 'color: white;background-color: #0070ed;padding: 5px 5px', + PROJECT_VERSION, + PROJECT_BUILD_TIME, +); + +/** + * 互动功能通过 umd 引入,需要获取 window 的 Vue 对象 + * dev 环境下 vue 使用 esModule 引入,不会挂载到 window + * 因此在 window 没有 Vue 的情况下手动挂载一次 + */ +if (!window.Vue) { + (window.Vue as unknown) = Vue; +} + +Vue.config.productionTip = false; + +Vue.use(i18nInstall, { + locale: () => { + const langStore = useLangStore(); + return langStore.currentLang; + }, +}); + +new Vue({ + pinia, + components: { + App: WatchApp, + }, + render(createElement) { + return createElement('app'); + }, +}).$mount('#app'); diff --git a/src/pages/splash/mobile-splash/components/mobile-splash-btn.vue b/src/pages/splash/mobile-splash/components/mobile-splash-btn.vue new file mode 100644 index 0000000000000000000000000000000000000000..3b353be4c4b195fcae7efaf5d2f382945dfc3d02 --- /dev/null +++ b/src/pages/splash/mobile-splash/components/mobile-splash-btn.vue @@ -0,0 +1,31 @@ + + + + diff --git a/src/pages/splash/mobile-splash/mobile-splash-full.vue b/src/pages/splash/mobile-splash/mobile-splash-full.vue new file mode 100644 index 0000000000000000000000000000000000000000..7046c756b8da88bb27624a8f5da5eda3c9c77e43 --- /dev/null +++ b/src/pages/splash/mobile-splash/mobile-splash-full.vue @@ -0,0 +1,125 @@ + + + + + + diff --git a/src/pages/splash/mobile-splash/mobile-splash.vue b/src/pages/splash/mobile-splash/mobile-splash.vue new file mode 100644 index 0000000000000000000000000000000000000000..2c11b90966a64c401726465dd36fa56d26abf530 --- /dev/null +++ b/src/pages/splash/mobile-splash/mobile-splash.vue @@ -0,0 +1,150 @@ + + + + + + diff --git a/src/pages/splash/pc-splash/pc-splash.vue b/src/pages/splash/pc-splash/pc-splash.vue new file mode 100644 index 0000000000000000000000000000000000000000..ca131e075bbc0c107f4ddd293193cd1e841692d8 --- /dev/null +++ b/src/pages/splash/pc-splash/pc-splash.vue @@ -0,0 +1,310 @@ + + + + + + diff --git a/src/pages/watch/_hooks/use-watch-page/_hooks/use-connect-mic-setup.ts b/src/pages/watch/_hooks/use-watch-page/_hooks/use-connect-mic-setup.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd350fcdf2d36fbbef9952207d09f95bd531c1c5 --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-page/_hooks/use-connect-mic-setup.ts @@ -0,0 +1,30 @@ +import { onMounted } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useConnectMicStore } from '@/store/use-connect-mic-store'; +import { useEventBusListener, appEvents } from '@/app/app-events'; + +/** + * 连麦 hook + */ +export const useConnectMicSetup = () => { + const connectMicStore = useConnectMicStore(); + + /** + * 初始化连麦 + */ + async function initConnectCall() { + if (!connectMicStore.connectMicEnabled) return; + + const watchCore = getWatchCore(); + await watchCore.connectMic.setupConnectMic(); + } + + onMounted(() => { + initConnectCall(); + }); + + useEventBusListener(appEvents.connectMic.ResetUpConnectMic, () => { + initConnectCall(); + }); +}; diff --git a/src/pages/watch/_hooks/use-watch-page/_hooks/use-main-screen.ts b/src/pages/watch/_hooks/use-watch-page/_hooks/use-main-screen.ts new file mode 100644 index 0000000000000000000000000000000000000000..cdc4e773b1e6aadc0d9a64fc2824436027764594 --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-page/_hooks/use-main-screen.ts @@ -0,0 +1,84 @@ +import { isMobile } from '@/assets/utils/browser'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useDocStore } from '@/store/use-doc-store'; +import { useLayoutStore } from '@/store/use-layout-store'; +import { + ChannelDocWatchLayout, + DocEvents, + LiveStatus, + MainScreenContent, +} from '@polyv/live-watch-sdk'; +import { onBeforeUnmount, onMounted } from 'vue'; + +/** + * 主副屏切换响应逻辑 + */ +export const useMainScreen = () => { + const channelStore = useChannelStore(); + const docStore = useDocStore(); + const layoutStore = useLayoutStore(); + + /** ------ 默认位置 ------ */ + /** + * 设置 PC 端默认位置 + */ + function setPcDefaultMainScreen() { + const docWatchLayout = channelStore.channelDetail?.theme.watchLayout; + + if (!docStore.canRenderDoc || !docWatchLayout) { + return; + } + + switch (docWatchLayout) { + case ChannelDocWatchLayout.Video: + case ChannelDocWatchLayout.OnlyVideo: + layoutStore.$patch({ + mainScreen: MainScreenContent.Player, + }); + break; + case ChannelDocWatchLayout.FollowTeacher: + case ChannelDocWatchLayout.Ppt: + layoutStore.$patch({ + mainScreen: MainScreenContent.Doc, + }); + break; + } + } + + if (!isMobile) { + setPcDefaultMainScreen(); + } + + /** ------ 事件响应 ------ */ + function onMainScreenChange(evt: { mainScreen: MainScreenContent }) { + const docWatchLayout = channelStore.channelDetail?.theme.watchLayout; + + if (!docStore.canRenderDoc || !docWatchLayout) { + return; + } + + // 针对移动端,如果后台设置非跟随讲师,则不处理事件 + if ( + isMobile && + docWatchLayout !== ChannelDocWatchLayout.FollowTeacher && + channelStore.liveStatus === LiveStatus.Live + ) { + return; + } + + layoutStore.$patch({ + mainScreen: evt.mainScreen, + }); + } + + onMounted(() => { + const watchCore = getWatchCore(); + watchCore.doc.eventEmitter.on(DocEvents.MainScreenChange, onMainScreenChange); + }); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.doc.eventEmitter.off(DocEvents.MainScreenChange, onMainScreenChange); + }); +}; diff --git a/src/pages/watch/_hooks/use-watch-page/_hooks/use-online.ts b/src/pages/watch/_hooks/use-watch-page/_hooks/use-online.ts new file mode 100644 index 0000000000000000000000000000000000000000..96c9b82e5bc9502872d1ed36219d9c4cb2a5301b --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-page/_hooks/use-online.ts @@ -0,0 +1,92 @@ +import { throttle } from 'lodash-es'; +import { useChatStore } from '@/store/use-chat-store'; +import { onMounted, onBeforeUnmount } from 'vue'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ChatEvents, ChatEventsRelations, YN } from '@polyv/live-watch-sdk'; +import { isNumber } from '@/assets/utils/number'; +import { ynToBool } from '@utils-ts/boolean'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useViewerStore } from '@/store/use-viewer-store'; +import { ErrorVerifyType, redirectToErrorVerify } from '@/hooks/core/use-error-verify'; + +export const useOnlineHook = () => { + const channelStore = useChannelStore(); + const chatStore = useChatStore(); + const viewerStore = useViewerStore(); + + const watchCore = getWatchCore(); + + /** 设置在线人数 */ + const setOnlineUserCount = throttle((onlineUserCount: number) => { + chatStore.$patch({ + onlineUserCount, + }); + }, 1000); + + /** + * 检查登录的人数是否超出了最大在线人数 + * @param onlineUserCount 在线人数 + */ + function checkMaxViewerCount(onlineUserCount: number, userId: string) { + const channelDetail = channelStore.channelDetail; + // 未聊天室并发限制开关 + if (!ynToBool(channelDetail?.channelConfig.restrictChatEnabled, YN.N)) { + return false; + } + + // 如果开启了虚拟人数开关,在线人数是假的,所以不需要踢出 + if (ynToBool(channelDetail?.channelConfig.chatRobotEnabled, YN.N)) { + return false; + } + + // 登录的非当前观众 + if (viewerStore.viewerId !== userId) { + return false; + } + + // 在线人数超出最大并发数则踢出当前用户 + const maxViewers = channelDetail?.channelConfig.maxViewers ?? Infinity; + return onlineUserCount > maxViewers; + } + + /** 处理 login 消息 */ + function onUserLogin(evt: ChatEventsRelations[ChatEvents.ChatUserLogin]) { + const { userLoginMsg } = evt; + const userId = userLoginMsg.user.userId; + + if (isNumber(userLoginMsg.onlineUserNumber)) { + // 非分房间下才通过 Login 消息获取在线人数 + if (!chatStore.isSibRoom) { + setOnlineUserCount(userLoginMsg.onlineUserNumber); + } + + // 是否超出最大并发数,踢出当前用户 + const needKickUser = checkMaxViewerCount(userLoginMsg.onlineUserNumber, userId); + if (needKickUser) { + redirectToErrorVerify({ + errorType: ErrorVerifyType.Restrict, + }); + } + } + } + + /** 处理 logout 消息 */ + function onUserLogout(evt: ChatEventsRelations[ChatEvents.ChatUserLogout]) { + const { userLogoutMsg } = evt; + + // 非分房间下才通过 Login 消息获取在线人数 + if (!chatStore.isSibRoom && isNumber(userLogoutMsg.onlineUserNumber)) { + setOnlineUserCount(userLogoutMsg.onlineUserNumber); + } + } + + onMounted(() => { + watchCore.chat.eventEmitter.on(ChatEvents.ChatUserLogin, onUserLogin); + watchCore.chat.eventEmitter.on(ChatEvents.ChatUserLogout, onUserLogout); + }); + + onBeforeUnmount(() => { + watchCore.chat.eventEmitter.off(ChatEvents.ChatUserLogin, onUserLogin); + watchCore.chat.eventEmitter.off(ChatEvents.ChatUserLogout, onUserLogout); + }); +}; diff --git a/src/pages/watch/_hooks/use-watch-page/_hooks/use-polling.ts b/src/pages/watch/_hooks/use-watch-page/_hooks/use-polling.ts new file mode 100644 index 0000000000000000000000000000000000000000..255bd8eceba93400bb22bad7a70b7620b4a309fa --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-page/_hooks/use-polling.ts @@ -0,0 +1,37 @@ +/** + * @file 观看页轮询相关的 hook + */ +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { onBeforeUnmount, watchEffect } from 'vue'; +import { LiveStatus } from '@polyv/live-watch-sdk'; +import { useChannelInfoStore } from '@/store/use-channel-info-store'; + +/** + * 根据 liveStatus 设置 pv 轮询 + */ +export const usePageViewPolling = () => { + const channelStore = useChannelStore(); + const channelInfoStore = useChannelInfoStore(); + + watchEffect(() => { + const watchCore = getWatchCore(); + + if (channelStore.liveStatus === LiveStatus.Live) { + watchCore.channel.startPageViewPolling((pageViewCount: number) => { + if (channelInfoStore.pageViewCount !== pageViewCount) { + channelInfoStore.$patch({ + pageViewCount, + }); + } + }); + } else { + watchCore.channel.stopPageViewPolling(); + } + }); + + onBeforeUnmount(() => { + const watchCore = getWatchCore(); + watchCore.channel.stopPageViewPolling(); + }); +}; diff --git a/src/pages/watch/_hooks/use-watch-page/_hooks/use-single-session-verify.ts b/src/pages/watch/_hooks/use-watch-page/_hooks/use-single-session-verify.ts new file mode 100644 index 0000000000000000000000000000000000000000..d4ce34938ef53507ac5bc12c25f32b30a149b0c3 --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-page/_hooks/use-single-session-verify.ts @@ -0,0 +1,89 @@ +import { DEFAULT_VERIFY_QUERY_FREQUENCY } from '@/assets/constants/defaults'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useWatchAppStore } from '@/store/use-watch-app-store'; +import { onBeforeUnmount, onMounted } from 'vue'; +import { ynToBool } from '@utils-ts/boolean'; +import { VerifyViewerSessionFail, YN } from '@polyv/live-watch-sdk'; +import { translate } from '@/assets/lang'; +import { toast } from '@/hooks/components/use-toast'; +import { redirectToErrorVerify } from '@/hooks/core/use-error-verify'; + +/** + * 唯一性登录验证 + */ +export const useSingleSessionVerify = () => { + const watchCore = getWatchCore(); + const watchAppStore = useWatchAppStore(); + const channelStore = useChannelStore(); + + function setChannelVerifyQueryFrequency(channelVerifyQueryFrequency?: number) { + channelStore.$patch({ + channelVerifyQueryFrequency: channelVerifyQueryFrequency || DEFAULT_VERIFY_QUERY_FREQUENCY, + }); + } + + let pollingTimer: number | undefined; + + /** 设置轮训器 */ + function setSingleVerifyPolling() { + removeSingleVerifyPolling(); + + pollingTimer = window.setTimeout(() => { + verifySingleSession(); + }, channelStore.channelVerifyQueryFrequency * 1000); + } + + /** 移除轮训器 */ + function removeSingleVerifyPolling() { + if (pollingTimer) { + clearTimeout(pollingTimer); + pollingTimer = undefined; + } + } + + /** 验证观众登录的唯一性 */ + async function verifySingleSession() { + const result = await watchCore.channel.verifyViewerSession(); + + if (result.success) { + setChannelVerifyQueryFrequency(result.channelVerifyQueryFrequency); + setSingleVerifyPolling(); + } else { + const failReason = result.failReason; + let tips = ''; + + if (failReason === VerifyViewerSessionFail.Unknow) { + tips = translate('global.singleSession.error.unknow'); + } + if (failReason === VerifyViewerSessionFail.RemoteLogin) { + tips = translate('global.singleSession.error.relogin'); + } + + if (tips) { + toast.error(tips); + } + + setTimeout(() => { + redirectToErrorVerify(); + }, 5 * 1000); + } + } + + onMounted(() => { + setChannelVerifyQueryFrequency( + channelStore.channelDetail?.channelConfig.channelVerifyQueryFrequency, + ); + + if ( + ynToBool(channelStore.channelDetail?.channelConfig.singleSessionVerify, YN.Y) && + !watchAppStore.isWatchBackUrl + ) { + verifySingleSession(); + } + }); + + onBeforeUnmount(() => { + removeSingleVerifyPolling(); + }); +}; diff --git a/src/pages/watch/_hooks/use-watch-page/index.ts b/src/pages/watch/_hooks/use-watch-page/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..393b5fad9469173fe3b44965e0c8b6f565030ed2 --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-page/index.ts @@ -0,0 +1,27 @@ +import { useIarGlobalConfigHook } from '@/components/page-watch-common/interactive-receive/use-iar-global-config'; + +import { usePageViewPolling } from './_hooks/use-polling'; +import { useConnectMicSetup } from './_hooks/use-connect-mic-setup'; +import { useMainScreen } from './_hooks/use-main-screen'; +import { useOnlineHook } from './_hooks/use-online'; +import { useSingleSessionVerify } from './_hooks/use-single-session-verify'; + +export const useWatchPage = () => { + // 使用 pv 轮训 + usePageViewPolling(); + + // 使用连麦 + useConnectMicSetup(); + + // 设置互动功能全局配置 + useIarGlobalConfigHook(); + + // 主副屏响应切换逻辑 + useMainScreen(); + + // 设置在线数据 + useOnlineHook(); + + // 唯一性登录验证 + useSingleSessionVerify(); +}; diff --git a/src/pages/watch/_hooks/use-watch-tab/index.ts b/src/pages/watch/_hooks/use-watch-tab/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ec2777cb8df1c9fe920287ea96e22f57fcdbe483 --- /dev/null +++ b/src/pages/watch/_hooks/use-watch-tab/index.ts @@ -0,0 +1,99 @@ +import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { translate } from '@/assets/lang'; + +import { ChatEvents } from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; + +import { useLayoutStore } from '@/store/use-layout-store'; +import { useChannelMenuStore } from '@/store/use-channel-menu-store'; +import { useChatStore } from '@/store/use-chat-store'; + +import { TAB_NAME_CHAT } from '@/assets/constants/tab-name'; +import { TabPaneAdditionalData } from '@/components/common-base/tabs/hooks/types'; + +/** + * Tab/Menu 菜单附加数据 Hook + */ +export const useWatchTabHook = ({ mode = 'pc' }: { mode: 'pc' | 'mobile' }) => { + const layoutStore = useLayoutStore(); + const { chatMenuData } = storeDefinitionToRefs(useChannelMenuStore); + + const { chatOnlineNumberEnable, onlineUserCount } = storeDefinitionToRefs(useChatStore); + + /** 获取默认的附加菜单数据 */ + function getDefaultMenuAdditionData(): TabPaneAdditionalData { + return { + subLabel: '', + showReminder: false, + }; + } + + /** 聊天消息红点提示是否可见 */ + const chatReminderVisible = ref(false); + + /** 当前活动的 Tab Name */ + const curActiveTabName = computed(() => { + if (mode === 'pc') return layoutStore.pcAsideTabCurrentName; + if (mode === 'mobile') return layoutStore.mobileMenuCurrentName; + + return layoutStore.pcAsideTabCurrentName; + }); + + /** 聊天Tab附加菜单数据 */ + const chatMenuAdditionData = computed(() => { + if (!chatMenuData.value.visible) return getDefaultMenuAdditionData(); + + const subLabel = chatOnlineNumberEnable.value + ? `(${onlineUserCount.value}${translate('base.people')})` + : ''; + + const showReminder = chatReminderVisible.value; + + return { + subLabel, + showReminder, + }; + }); + + watch( + () => curActiveTabName.value, + () => { + if (curActiveTabName.value === TAB_NAME_CHAT) { + chatReminderVisible.value = false; + } + }, + ); + + function onChatMsg() { + if (curActiveTabName.value !== TAB_NAME_CHAT) { + chatReminderVisible.value = true; + } + } + + /** + * 监听聊天室消息 + */ + function listenChatMsg() { + unlistenChatMsg(); + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.on(ChatEvents.ChatMessage, onChatMsg); + } + + function unlistenChatMsg() { + const watchCore = getWatchCore(); + watchCore.chat.eventEmitter.off(ChatEvents.ChatMessage, onChatMsg); + } + + onMounted(() => { + listenChatMsg(); + }); + + onBeforeUnmount(() => { + unlistenChatMsg(); + }); + + return { + chatMenuAdditionData, + }; +}; diff --git a/src/pages/watch/mobile-watch/layout/mobile-watch-layout.vue b/src/pages/watch/mobile-watch/layout/mobile-watch-layout.vue new file mode 100644 index 0000000000000000000000000000000000000000..392d2ed9fbd3588ee007584a20e427ee0d1a0f6c --- /dev/null +++ b/src/pages/watch/mobile-watch/layout/mobile-watch-layout.vue @@ -0,0 +1,321 @@ + + + + + + diff --git a/src/pages/watch/mobile-watch/layout/mobile-watch-menu.vue b/src/pages/watch/mobile-watch/layout/mobile-watch-menu.vue new file mode 100644 index 0000000000000000000000000000000000000000..9af4293f44509e25c61aa85a13576f8d73ebd467 --- /dev/null +++ b/src/pages/watch/mobile-watch/layout/mobile-watch-menu.vue @@ -0,0 +1,292 @@ + + + + + + diff --git a/src/pages/watch/mobile-watch/layout/mobile-watch-pendant.vue b/src/pages/watch/mobile-watch/layout/mobile-watch-pendant.vue new file mode 100644 index 0000000000000000000000000000000000000000..e46f423152cb392e249e84e2b0f8e066577d67f1 --- /dev/null +++ b/src/pages/watch/mobile-watch/layout/mobile-watch-pendant.vue @@ -0,0 +1,44 @@ + + + + + + diff --git a/src/pages/watch/mobile-watch/mobile-watch.vue b/src/pages/watch/mobile-watch/mobile-watch.vue new file mode 100644 index 0000000000000000000000000000000000000000..440274d24ed2b16a23df13459d4527da9b12fafd --- /dev/null +++ b/src/pages/watch/mobile-watch/mobile-watch.vue @@ -0,0 +1,103 @@ + + + + diff --git a/src/pages/watch/pc-watch/layout/pc-watch-aside-menu.vue b/src/pages/watch/pc-watch/layout/pc-watch-aside-menu.vue new file mode 100644 index 0000000000000000000000000000000000000000..c3247bafc58cbba6cff3ace582dcbb1785c3382a --- /dev/null +++ b/src/pages/watch/pc-watch/layout/pc-watch-aside-menu.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/src/pages/watch/pc-watch/layout/pc-watch-bottom-menu.vue b/src/pages/watch/pc-watch/layout/pc-watch-bottom-menu.vue new file mode 100644 index 0000000000000000000000000000000000000000..58f9a834faea6236f19d11a5e94f039a7dbe1f88 --- /dev/null +++ b/src/pages/watch/pc-watch/layout/pc-watch-bottom-menu.vue @@ -0,0 +1,114 @@ + + + + diff --git a/src/pages/watch/pc-watch/layout/pc-watch-layout.vue b/src/pages/watch/pc-watch/layout/pc-watch-layout.vue new file mode 100644 index 0000000000000000000000000000000000000000..9826aa5eb18ae253f81fac671c1443e653c683fd --- /dev/null +++ b/src/pages/watch/pc-watch/layout/pc-watch-layout.vue @@ -0,0 +1,391 @@ + + + + + + diff --git a/src/pages/watch/pc-watch/layout/pc-watch-sub-pack-up.vue b/src/pages/watch/pc-watch/layout/pc-watch-sub-pack-up.vue new file mode 100644 index 0000000000000000000000000000000000000000..432e3f9f8725091dbd7ba1bc77cdd242c3f8ea72 --- /dev/null +++ b/src/pages/watch/pc-watch/layout/pc-watch-sub-pack-up.vue @@ -0,0 +1,115 @@ + + + + + + diff --git a/src/pages/watch/pc-watch/pc-watch.vue b/src/pages/watch/pc-watch/pc-watch.vue new file mode 100644 index 0000000000000000000000000000000000000000..f2224acf823c8a1385c81ecc44ca9fce2ade2958 --- /dev/null +++ b/src/pages/watch/pc-watch/pc-watch.vue @@ -0,0 +1,60 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/channel-description/images/icon-desc.png b/src/pages/watch/portrait-watch/components/channel-description/images/icon-desc.png new file mode 100644 index 0000000000000000000000000000000000000000..4755ff285ec7ce064721d7514a62d27073a940de Binary files /dev/null and b/src/pages/watch/portrait-watch/components/channel-description/images/icon-desc.png differ diff --git a/src/pages/watch/portrait-watch/components/channel-description/portrait-channel-description.vue b/src/pages/watch/portrait-watch/components/channel-description/portrait-channel-description.vue new file mode 100644 index 0000000000000000000000000000000000000000..5ad7b8756f4d731f31b57205c82decb659ed6d04 --- /dev/null +++ b/src/pages/watch/portrait-watch/components/channel-description/portrait-channel-description.vue @@ -0,0 +1,71 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/channel-info/images/icon-eye.png b/src/pages/watch/portrait-watch/components/channel-info/images/icon-eye.png new file mode 100644 index 0000000000000000000000000000000000000000..3a1e138b12f020c57722b5443ad7eed73827716c Binary files /dev/null and b/src/pages/watch/portrait-watch/components/channel-info/images/icon-eye.png differ diff --git a/src/pages/watch/portrait-watch/components/channel-info/portrait-channel-info-capsule.vue b/src/pages/watch/portrait-watch/components/channel-info/portrait-channel-info-capsule.vue new file mode 100644 index 0000000000000000000000000000000000000000..da7ef0d2e4edfc092f4d7634e0d3fab910225f51 --- /dev/null +++ b/src/pages/watch/portrait-watch/components/channel-info/portrait-channel-info-capsule.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/chat/chat-input-placeholder/images/icon-send-msg.png b/src/pages/watch/portrait-watch/components/chat/chat-input-placeholder/images/icon-send-msg.png new file mode 100644 index 0000000000000000000000000000000000000000..d58748ac425c92c2b6f4beb98c85ead37f36d3f7 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/chat/chat-input-placeholder/images/icon-send-msg.png differ diff --git a/src/pages/watch/portrait-watch/components/chat/chat-input-placeholder/portrait-chat-input-placeholder.vue b/src/pages/watch/portrait-watch/components/chat/chat-input-placeholder/portrait-chat-input-placeholder.vue new file mode 100644 index 0000000000000000000000000000000000000000..916b280c79a4862d2b55e1568c5afa52505961c0 --- /dev/null +++ b/src/pages/watch/portrait-watch/components/chat/chat-input-placeholder/portrait-chat-input-placeholder.vue @@ -0,0 +1,88 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-audio.png b/src/pages/watch/portrait-watch/components/menu/images/icon-audio.png new file mode 100644 index 0000000000000000000000000000000000000000..30c79d681d462792fba50b60183d9520ed84d66e Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-audio.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-donate-hide.png b/src/pages/watch/portrait-watch/components/menu/images/icon-donate-hide.png new file mode 100644 index 0000000000000000000000000000000000000000..7cc76b071fab34b0fb0fee817f88fc2fd5ce856e Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-donate-hide.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-donate-show.png b/src/pages/watch/portrait-watch/components/menu/images/icon-donate-show.png new file mode 100644 index 0000000000000000000000000000000000000000..09c6daf8cc178fe7a99afaf5a4bd974be652f3be Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-donate-show.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-feed.png b/src/pages/watch/portrait-watch/components/menu/images/icon-feed.png new file mode 100644 index 0000000000000000000000000000000000000000..ac92a839709d3091b9a111fdf38ce6215d123af4 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-feed.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-more.png b/src/pages/watch/portrait-watch/components/menu/images/icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..610ec483925f267d0bf4dc2d4d1323b7dd59a520 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-more.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-only-host.png b/src/pages/watch/portrait-watch/components/menu/images/icon-only-host.png new file mode 100644 index 0000000000000000000000000000000000000000..f46a86cc643c47ec347f31a3d5b7cde3428a1b30 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-only-host.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-qa.png b/src/pages/watch/portrait-watch/components/menu/images/icon-qa.png new file mode 100644 index 0000000000000000000000000000000000000000..b10c01fbfb309b8c11088b18169b1b09b4228415 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-qa.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-rate.png b/src/pages/watch/portrait-watch/components/menu/images/icon-rate.png new file mode 100644 index 0000000000000000000000000000000000000000..203a21f3744171d969b26b6304058a832dad2f2f Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-rate.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-score-withdraw.png b/src/pages/watch/portrait-watch/components/menu/images/icon-score-withdraw.png new file mode 100644 index 0000000000000000000000000000000000000000..30ea1dd110cd4e5c72446b638d4664c1a0bf6606 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-score-withdraw.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-set-latency.png b/src/pages/watch/portrait-watch/components/menu/images/icon-set-latency.png new file mode 100644 index 0000000000000000000000000000000000000000..a0d98dcc8608f91b924bdc592b1e76234224805d Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-set-latency.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-set-level.png b/src/pages/watch/portrait-watch/components/menu/images/icon-set-level.png new file mode 100644 index 0000000000000000000000000000000000000000..56b2e7d8e7809791fbe61737a81eeaf3eb5febec Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-set-level.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-set-line.png b/src/pages/watch/portrait-watch/components/menu/images/icon-set-line.png new file mode 100644 index 0000000000000000000000000000000000000000..2af1c225840617f4cc2cd4e7d2f70616e3953e8d Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-set-line.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-show-all.png b/src/pages/watch/portrait-watch/components/menu/images/icon-show-all.png new file mode 100644 index 0000000000000000000000000000000000000000..ecb231985a27e4c75850e661596c785938e90cd4 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-show-all.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-video.png b/src/pages/watch/portrait-watch/components/menu/images/icon-video.png new file mode 100644 index 0000000000000000000000000000000000000000..c1397890a4af2718011fb5ad571737e256d9edff Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-video.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/images/icon-withdraw.png b/src/pages/watch/portrait-watch/components/menu/images/icon-withdraw.png new file mode 100644 index 0000000000000000000000000000000000000000..497f2c2da1dd73640b5b541ed15cd56b836c6740 Binary files /dev/null and b/src/pages/watch/portrait-watch/components/menu/images/icon-withdraw.png differ diff --git a/src/pages/watch/portrait-watch/components/menu/portrait-menu-entrance.vue b/src/pages/watch/portrait-watch/components/menu/portrait-menu-entrance.vue new file mode 100644 index 0000000000000000000000000000000000000000..97e5ec25424067b4dbc57116deb1902ca1b843c6 --- /dev/null +++ b/src/pages/watch/portrait-watch/components/menu/portrait-menu-entrance.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/menu/portrait-menu-item.vue b/src/pages/watch/portrait-watch/components/menu/portrait-menu-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..7eadee5ca3ef3b07e08c198544b18579d493645e --- /dev/null +++ b/src/pages/watch/portrait-watch/components/menu/portrait-menu-item.vue @@ -0,0 +1,138 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/menu/portrait-menu-popup.vue b/src/pages/watch/portrait-watch/components/menu/portrait-menu-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..b42853281e399bb29c2038fb8c72bfb7480c4092 --- /dev/null +++ b/src/pages/watch/portrait-watch/components/menu/portrait-menu-popup.vue @@ -0,0 +1,313 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/components/playback-chapter-popup/portrait-playback-chapter-popup.vue b/src/pages/watch/portrait-watch/components/playback-chapter-popup/portrait-playback-chapter-popup.vue new file mode 100644 index 0000000000000000000000000000000000000000..51e5073ef1f135ac62d152bd690b67c713e703a0 --- /dev/null +++ b/src/pages/watch/portrait-watch/components/playback-chapter-popup/portrait-playback-chapter-popup.vue @@ -0,0 +1,82 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/layout/boundary-wrap.vue b/src/pages/watch/portrait-watch/layout/boundary-wrap.vue new file mode 100644 index 0000000000000000000000000000000000000000..e73fcf16d1ca2cf1ac7b51c32772003341fa4818 --- /dev/null +++ b/src/pages/watch/portrait-watch/layout/boundary-wrap.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/pages/watch/portrait-watch/layout/carousel-main-screen.vue b/src/pages/watch/portrait-watch/layout/carousel-main-screen.vue new file mode 100644 index 0000000000000000000000000000000000000000..fd1677a72e0c193b0e9c4c4f15704cf71d20fdee --- /dev/null +++ b/src/pages/watch/portrait-watch/layout/carousel-main-screen.vue @@ -0,0 +1,311 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/layout/carousel-page-container.vue b/src/pages/watch/portrait-watch/layout/carousel-page-container.vue new file mode 100644 index 0000000000000000000000000000000000000000..5378daeb88493598e06a20ecda644a1129d696ef --- /dev/null +++ b/src/pages/watch/portrait-watch/layout/carousel-page-container.vue @@ -0,0 +1,25 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/layout/carousel-sub-screen.vue b/src/pages/watch/portrait-watch/layout/carousel-sub-screen.vue new file mode 100644 index 0000000000000000000000000000000000000000..65e130c1851b7302f8261004dacd9f0e62ada59a --- /dev/null +++ b/src/pages/watch/portrait-watch/layout/carousel-sub-screen.vue @@ -0,0 +1,50 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/layout/images/icon-donate.png b/src/pages/watch/portrait-watch/layout/images/icon-donate.png new file mode 100644 index 0000000000000000000000000000000000000000..16445fa17efc2968456834d62b2fdd2a9909e21a Binary files /dev/null and b/src/pages/watch/portrait-watch/layout/images/icon-donate.png differ diff --git a/src/pages/watch/portrait-watch/layout/images/icon-playback.png b/src/pages/watch/portrait-watch/layout/images/icon-playback.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8a84d5ca9161a4bf5d4d81dee7966dc993f781 Binary files /dev/null and b/src/pages/watch/portrait-watch/layout/images/icon-playback.png differ diff --git a/src/pages/watch/portrait-watch/layout/images/portrait-bg.png b/src/pages/watch/portrait-watch/layout/images/portrait-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..27c98c8202a0706e10798fb15df7b493052204af Binary files /dev/null and b/src/pages/watch/portrait-watch/layout/images/portrait-bg.png differ diff --git a/src/pages/watch/portrait-watch/layout/portrait-watch-layout-carousel.vue b/src/pages/watch/portrait-watch/layout/portrait-watch-layout-carousel.vue new file mode 100644 index 0000000000000000000000000000000000000000..dba1688c566a0096318fc75c15c8945a204c5af5 --- /dev/null +++ b/src/pages/watch/portrait-watch/layout/portrait-watch-layout-carousel.vue @@ -0,0 +1,70 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/layout/portrait-watch-layout.vue b/src/pages/watch/portrait-watch/layout/portrait-watch-layout.vue new file mode 100644 index 0000000000000000000000000000000000000000..f8db0a524b6c20f299480147d4193b272f2eabeb --- /dev/null +++ b/src/pages/watch/portrait-watch/layout/portrait-watch-layout.vue @@ -0,0 +1,85 @@ + + + + + + diff --git a/src/pages/watch/portrait-watch/portrait-watch.vue b/src/pages/watch/portrait-watch/portrait-watch.vue new file mode 100644 index 0000000000000000000000000000000000000000..ab23153949959c5919910714b4546000fb20805d --- /dev/null +++ b/src/pages/watch/portrait-watch/portrait-watch.vue @@ -0,0 +1,167 @@ + + + + + + diff --git a/src/plugins/alloy-finger/transform.js b/src/plugins/alloy-finger/transform.js new file mode 100644 index 0000000000000000000000000000000000000000..fead4b1962d0546e8a43be53b425b1424baccb1e --- /dev/null +++ b/src/plugins/alloy-finger/transform.js @@ -0,0 +1,322 @@ +// eslint-disable-next-line sonarjs/cognitive-complexity +const Matrix3D = function ( + n11, + n12, + n13, + n14, + n21, + n22, + n23, + n24, + n31, + n32, + n33, + n34, + n41, + n42, + n43, + n44, +) { + this.elements = window.Float32Array ? new Float32Array(16) : []; + const te = this.elements; + te[0] = n11 !== undefined ? n11 : 1; + te[4] = n12 || 0; + te[8] = n13 || 0; + te[12] = n14 || 0; + te[1] = n21 || 0; + te[5] = n22 !== undefined ? n22 : 1; + te[9] = n23 || 0; + te[13] = n24 || 0; + te[2] = n31 || 0; + te[6] = n32 || 0; + te[10] = n33 !== undefined ? n33 : 1; + te[14] = n34 || 0; + te[3] = n41 || 0; + te[7] = n42 || 0; + te[11] = n43 || 0; + te[15] = n44 !== undefined ? n44 : 1; +}; + +Matrix3D.DEG_TO_RAD = Math.PI / 180; + +Matrix3D.prototype = { + set: function (n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44) { + const te = this.elements; + te[0] = n11; + te[4] = n12; + te[8] = n13; + te[12] = n14; + te[1] = n21; + te[5] = n22; + te[9] = n23; + te[13] = n24; + te[2] = n31; + te[6] = n32; + te[10] = n33; + te[14] = n34; + te[3] = n41; + te[7] = n42; + te[11] = n43; + te[15] = n44; + return this; + }, + identity: function () { + this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return this; + }, + multiplyMatrices: function (a, be) { + const ae = a.elements; + const te = this.elements; + const a11 = ae[0]; + const a12 = ae[4]; + const a13 = ae[8]; + const a14 = ae[12]; + const a21 = ae[1]; + const a22 = ae[5]; + const a23 = ae[9]; + const a24 = ae[13]; + const a31 = ae[2]; + const a32 = ae[6]; + const a33 = ae[10]; + const a34 = ae[14]; + const a41 = ae[3]; + const a42 = ae[7]; + const a43 = ae[11]; + const a44 = ae[15]; + + const b11 = be[0]; + const b12 = be[1]; + const b13 = be[2]; + const b14 = be[3]; + const b21 = be[4]; + const b22 = be[5]; + const b23 = be[6]; + const b24 = be[7]; + const b31 = be[8]; + const b32 = be[9]; + const b33 = be[10]; + const b34 = be[11]; + const b41 = be[12]; + const b42 = be[13]; + const b43 = be[14]; + const b44 = be[15]; + + te[0] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; + te[4] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; + te[8] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; + te[12] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; + + te[1] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; + te[5] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; + te[9] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; + te[13] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; + + te[2] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; + te[6] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; + te[10] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; + te[14] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; + + te[3] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; + te[7] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; + te[11] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; + te[15] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; + + return this; + }, + // 解决角度为90的整数倍导致Math.cos得到极小的数,其实是0。导致不渲染 + _rounded: function (value, i) { + i = Math.pow(10, i || 15); + // default + return Math.round(value * i) / i; + }, + appendTransform: function ( + x, + y, + z, + scaleX, + scaleY, + scaleZ, + rotateX, + rotateY, + rotateZ, + skewX, + skewY, + originX, + originY, + originZ, + ) { + const rx = rotateX * Matrix3D.DEG_TO_RAD; + const cosx = this._rounded(Math.cos(rx)); + const sinx = this._rounded(Math.sin(rx)); + const ry = rotateY * Matrix3D.DEG_TO_RAD; + const cosy = this._rounded(Math.cos(ry)); + const siny = this._rounded(Math.sin(ry)); + const rz = rotateZ * Matrix3D.DEG_TO_RAD; + const cosz = this._rounded(Math.cos(rz * -1)); + const sinz = this._rounded(Math.sin(rz * -1)); + + this.multiplyMatrices(this, [1, 0, 0, x, 0, cosx, sinx, y, 0, -sinx, cosx, z, 0, 0, 0, 1]); + + this.multiplyMatrices(this, [cosy, 0, siny, 0, 0, 1, 0, 0, -siny, 0, cosy, 0, 0, 0, 0, 1]); + + this.multiplyMatrices(this, [ + cosz * scaleX, + sinz * scaleY, + 0, + 0, + -sinz * scaleX, + cosz * scaleY, + 0, + 0, + 0, + 0, + 1 * scaleZ, + 0, + 0, + 0, + 0, + 1, + ]); + + if (skewX || skewY) { + this.multiplyMatrices(this, [ + this._rounded(Math.cos(skewX * Matrix3D.DEG_TO_RAD)), + this._rounded(Math.sin(skewX * Matrix3D.DEG_TO_RAD)), + 0, + 0, + -1 * this._rounded(Math.sin(skewY * Matrix3D.DEG_TO_RAD)), + this._rounded(Math.cos(skewY * Matrix3D.DEG_TO_RAD)), + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + ]); + } + + if (originX || originY || originZ) { + this.elements[12] -= + originX * this.elements[0] + originY * this.elements[4] + originZ * this.elements[8]; + this.elements[13] -= + originX * this.elements[1] + originY * this.elements[5] + originZ * this.elements[9]; + this.elements[14] -= + originX * this.elements[2] + originY * this.elements[6] + originZ * this.elements[10]; + } + return this; + }, +}; + +function observe(target, props, callback) { + for (let i = 0, len = props.length; i < len; i++) { + const prop = props[i]; + watch(target, prop, callback); + } +} + +function watch(target, prop, callback) { + Object.defineProperty(target, prop, { + get: function () { + return this['__' + prop]; + }, + set: function (value) { + if (value !== this['__' + prop]) { + this['__' + prop] = value; + callback(); + } + }, + }); +} + +/** + * 在 DOM 元素上增加 css-transform 相关的属性,监听到属性变化时,更改 DOM 的样式 + * @param {HTMLElement} element + * @returns {HTMLElement & AlloyFingerTransformHTMLElement } + */ +export const Transform = function (element) { + observe( + element, + [ + 'translateX', + 'translateY', + 'translateZ', + 'scaleX', + 'scaleY', + 'scaleZ', + 'rotateX', + 'rotateY', + 'rotateZ', + 'skewX', + 'skewY', + 'originX', + 'originY', + 'originZ', + ], + function () { + const mtx = element.matrix3D + .identity() + .appendTransform( + element.translateX, + element.translateY, + element.translateZ, + element.scaleX, + element.scaleY, + element.scaleZ, + element.rotateX, + element.rotateY, + element.rotateZ, + element.skewX, + element.skewY, + element.originX, + element.originY, + element.originZ, + ); + element.style.transform = + element.style.msTransform = + element.style.OTransform = + element.style.MozTransform = + element.style.webkitTransform = + 'perspective(' + + element.perspective + + 'px) matrix3d(' + + Array.prototype.slice.call(mtx.elements).join(',') + + ')'; + }, + ); + + observe(element, ['perspective'], function () { + element.style.transform = + element.style.msTransform = + element.style.OTransform = + element.style.MozTransform = + element.style.webkitTransform = + 'perspective(' + + element.perspective + + 'px) matrix3d(' + + Array.prototype.slice.call(element.matrix3D.elements).join(',') + + ')'; + }); + + element.matrix3D = new Matrix3D(); + element.perspective = 500; + element.scaleX = element.scaleY = element.scaleZ = 1; + // 由于image自带了x\y\z,所有加上translate前缀 + element.translateX = + element.translateY = + element.translateZ = + element.rotateX = + element.rotateY = + element.rotateZ = + element.skewX = + element.skewY = + element.originX = + element.originY = + element.originZ = + 0; + + return element; +}; diff --git a/src/plugins/async-validator/index.ts b/src/plugins/async-validator/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8c651001cd0c799d774d073f3deb0a1bdbbe62e6 --- /dev/null +++ b/src/plugins/async-validator/index.ts @@ -0,0 +1,9 @@ +import Schema from 'async-validator'; + +// 将内部的 console.waring 禁用掉 +// eslint-disable-next-line @typescript-eslint/no-empty-function +Schema.warning = () => {}; + +export type { Rules as ValidatorRules, Rule as ValidatorRule } from 'async-validator'; + +export default Schema; diff --git a/src/plugins/external-lib-loaders/load-ali-awsc.ts b/src/plugins/external-lib-loaders/load-ali-awsc.ts new file mode 100644 index 0000000000000000000000000000000000000000..66a1372f524191c1b58ca339f512fc6c26409666 --- /dev/null +++ b/src/plugins/external-lib-loaders/load-ali-awsc.ts @@ -0,0 +1,28 @@ +/** + * @file 加载阿里云 awsc.js + */ +import { loadScript } from '@just4/load-script'; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +let loadPromise: Promise | undefined; + +/** + * 加载阿里云 awsc.js + */ +export const loadAliAwsc = () => { + if (!loadPromise) { + loadPromise = new Promise((resolve, reject) => { + (async () => { + await loadScript('https://g.alicdn.com/AWSC/AWSC/awsc.js'); + + if (window.AWSC) { + resolve(window.AWSC); + } else { + reject(new Error('loadAliAwsc fail, window.AWSC is undefined')); + } + })(); + }); + } + + return loadPromise; +}; diff --git a/src/plugins/external-lib-loaders/load-svga-player.ts b/src/plugins/external-lib-loaders/load-svga-player.ts new file mode 100644 index 0000000000000000000000000000000000000000..b81a3c8349efbc20a7e9af72c8bea99c5a448653 --- /dev/null +++ b/src/plugins/external-lib-loaders/load-svga-player.ts @@ -0,0 +1,48 @@ +import { loadScript } from '@just4/load-script'; +import type { + Player as SVGAPlayer, + Parser as SVGAParser, + VideoEntity as SVGAVideoEntity, +} from 'svgaplayerweb'; + +/** svga 加载地址 */ +const SVGA_PLAYER_URL = 'https://s4.videocc.net/library/svgaplayerweb/2.x/svga-2.3.1.min.js'; + +export { SVGAPlayer, SVGAParser, SVGAVideoEntity }; + +export interface SVGASdk { + Player: typeof SVGAPlayer; + Parser: typeof SVGAParser; +} + +declare global { + interface Window { + SVGA?: SVGASdk; + } +} + +let svgaPlayerLoader: Promise | undefined; + +/** + * 加载 svga 播放器 + */ +export function loadSvgaPlayer(): Promise { + if (window.SVGA) { + return Promise.resolve(window.SVGA); + } + + if (!svgaPlayerLoader) { + svgaPlayerLoader = new Promise((resolve, reject) => { + (async () => { + await loadScript(SVGA_PLAYER_URL); + if (window.SVGA) { + resolve(window.SVGA); + } else { + reject(new Error('loadSvgaPlayer fail, window.SVGA is undefined')); + } + })(); + }); + } + + return svgaPlayerLoader; +} diff --git a/src/plugins/external-lib-loaders/load-work-weixin-login.ts b/src/plugins/external-lib-loaders/load-work-weixin-login.ts new file mode 100644 index 0000000000000000000000000000000000000000..32a69fb75aee549048c6ca8fddcfc90a88c6497e --- /dev/null +++ b/src/plugins/external-lib-loaders/load-work-weixin-login.ts @@ -0,0 +1,60 @@ +/** + * @file 加载企业微信扫码授权 js 文件 + */ +import { loadScript } from '@just4/load-script'; + +export interface WwLoginConfig { + /** 渲染的 id 选择器 */ + id: string; + /** 企业微信的 corpId */ + appid: string; + /** 授权方的网页应用 Id */ + agentid: string; + /** 重定向地址 */ + redirect_uri: string; + /** 回调状态 */ + state?: string; + /** 语言 */ + lang?: 'zh' | 'en'; + /** 自定义样式 */ + href?: string; +} + +export interface WwLoginInstance { + /** 销毁 */ + destroyed(): void; +} + +export interface WwLoginCtor { + new (config: WwLoginConfig): WwLoginInstance; +} + +declare global { + interface Window { + WwLogin?: WwLoginCtor; + } +} + +let loadPromise: Promise | undefined; + +export const loadWorkWeixinLoginSdk = (): Promise => { + if (window.WwLogin) { + return Promise.resolve(window.WwLogin); + } + + if (!loadPromise) { + loadPromise = new Promise((resolve, reject) => { + (async () => { + await loadScript('//wwcdn.weixin.qq.com/node/wework/wwopen/js/wwLogin-1.2.7.js'); + + if (window.WwLogin) { + resolve(window.WwLogin); + } else { + reject(new Error('loadWorkWeixinLoginSdk fail, window.WwLogin is undefined')); + } + })(); + }); + } + + return loadPromise; +}; diff --git a/src/plugins/external-lib-loaders/load-wx-sdk.ts b/src/plugins/external-lib-loaders/load-wx-sdk.ts new file mode 100644 index 0000000000000000000000000000000000000000..22122316aab303d22c5ad4a40e3d74cc94668528 --- /dev/null +++ b/src/plugins/external-lib-loaders/load-wx-sdk.ts @@ -0,0 +1,57 @@ +/** + * @file 加载微信 js-sdk 文件 + */ +import { loadScript } from '@just4/load-script'; + +let loadPromise: Promise | undefined; + +/** + * 加载微信 js-sdk + */ +export const loadWxSdk = () => { + if (window.wx) { + return Promise.resolve(window.wx); + } + + if (!loadPromise) { + loadPromise = new Promise((resolve, reject) => { + (async () => { + await loadScript('//res.wx.qq.com/open/js/jweixin-1.6.0.js'); + + if (window.wx) { + resolve(window.wx); + } else { + reject(new Error('loadWxSdk fail, window.wx is undefined')); + } + })(); + }); + } + + return loadPromise; +}; + +/** + * 加载企业微信的 js-sdk + * https://developer.work.weixin.qq.com/document/path/90514 + */ +export const loadWorkWxSdk = () => { + if (window.wx) { + return Promise.resolve(window.wx); + } + + if (!loadPromise) { + loadPromise = new Promise((resolve, reject) => { + (async () => { + await loadScript('//res.wx.qq.com/open/js/jweixin-1.2.0.js'); + + if (window.wx) { + resolve(window.wx); + } else { + reject(new Error('loadWorkWxSdk fail, window.wx is undefined')); + } + })(); + }); + } + + return loadPromise; +}; diff --git a/src/plugins/pinia/index.ts b/src/plugins/pinia/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..52a39a9546eb7fb84d1e9d8dfef1f19542c94ccd --- /dev/null +++ b/src/plugins/pinia/index.ts @@ -0,0 +1,5 @@ +import Vue from 'vue'; +import { PiniaVuePlugin, createPinia } from 'pinia'; + +Vue.use(PiniaVuePlugin); +export const pinia = createPinia(); diff --git a/src/plugins/pinia/util.ts b/src/plugins/pinia/util.ts new file mode 100644 index 0000000000000000000000000000000000000000..d72f26708da994bff9affb91c6793de85a4e4b60 --- /dev/null +++ b/src/plugins/pinia/util.ts @@ -0,0 +1,23 @@ +import { + PiniaCustomStateProperties, + StoreDefinition, + StoreGetters, + StoreState, + storeToRefs, +} from 'pinia'; +import { ToRefs } from 'vue'; + +type StoreDefinitionToRefsRet> = ToRefs< + StoreState & StoreGetters & PiniaCustomStateProperties> +>; + +/** + * 将 store 定义转成 refs + * @param storeDefinition store 定义 + */ +export const storeDefinitionToRefs = >( + storeDefinition: SD, +): StoreDefinitionToRefsRet => { + const store = storeDefinition(); + return storeToRefs(store) as StoreDefinitionToRefsRet; +}; diff --git a/src/plugins/polyv-ui/admin-import.ts b/src/plugins/polyv-ui/admin-import.ts new file mode 100644 index 0000000000000000000000000000000000000000..e8fd6fec7c4838b6692e1bf87ad4340bcdff3994 --- /dev/null +++ b/src/plugins/polyv-ui/admin-import.ts @@ -0,0 +1,19 @@ +/* eslint-disable import/first */ +import '@polyv/polyv-ui/lib-admin/theme/iconfont.css'; +export { default as PlvAdminIcon } from '@polyv/polyv-ui/lib-admin/icon'; + +import '@polyv/polyv-ui/lib-admin/theme/image-preview.css'; +export { default as PlvImagePreview } from '@polyv/polyv-ui/lib-admin/image-preview'; + +import '@polyv/polyv-ui/lib-admin/theme/input-paste-image.css'; +export { default as PlvInputPasteImage } from '@polyv/polyv-ui/lib-admin/input-paste-image'; + +import '@polyv/polyv-ui/lib-admin/theme/collapse-transition.css'; +export { default as PlvCollapseTransition } from '@polyv/polyv-ui/lib-admin/collapse-transition'; + +import PlvPopperManager from '@polyv/polyv-ui/lib-admin/popper-manager'; +PlvPopperManager.zIndex = 3000; +export { PlvPopperManager }; + +import '@polyv/polyv-ui/lib-admin/theme/pagination.css'; +export { default as PlvPagination } from '@polyv/polyv-ui/lib-admin/pagination'; diff --git a/src/plugins/polyv-ui/area-utils.ts b/src/plugins/polyv-ui/area-utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e82cd20943f5ede5b04b03b7524c6937717e053 --- /dev/null +++ b/src/plugins/polyv-ui/area-utils.ts @@ -0,0 +1,41 @@ +import { AreaData, AreaDataJson } from './types'; + +/** + * 通过value与级联数据获取选择中的数据 + * @param value 绑定值 + * @param data 地区数据 + */ +export function getSelectData(value: string[], data: AreaDataJson): AreaData[] { + let res: AreaData[] = []; + const [first, ...otherValue] = value; + if (typeof first === 'undefined') { + return res; + } + + const selected = data.find(item => item.code === first); + + if (selected) { + res = [selected]; + } + + if (selected && Array.isArray(selected.childrens)) { + res = [...res, ...getSelectData(otherValue, selected.childrens as AreaDataJson)]; + } + return res; +} + +export function getAreaSelectNames(value: string[], data: AreaDataJson): string[] { + const selectData = getSelectData(value, data); + return selectData.map(item => item.name); +} + +/** + * 判断是否选择完成 + * @param value 已选值数组 + */ +export function isSelectFinish(value: string[], data: AreaDataJson): boolean { + const selectData = getSelectData(value, data); + const lastData = selectData[selectData.length - 1] || {}; + + return !!(selectData.length && (!lastData.childrens || !lastData.childrens.length)); +} diff --git a/src/plugins/polyv-ui/cover-styles.scss b/src/plugins/polyv-ui/cover-styles.scss new file mode 100644 index 0000000000000000000000000000000000000000..a3b0d13373b99d9005b5cc7a88ea562757d1091b --- /dev/null +++ b/src/plugins/polyv-ui/cover-styles.scss @@ -0,0 +1,6 @@ +.plv-area-picker__tab__item { + white-space: nowrap; +} +.plv-area-picker__tab__item + .plv-area-picker__tab__item { + margin-left: 20px !important; +} diff --git a/src/plugins/polyv-ui/mobile-import.ts b/src/plugins/polyv-ui/mobile-import.ts new file mode 100644 index 0000000000000000000000000000000000000000..1e6e22f7e6364e0359b25224406f8f3528c03ca8 --- /dev/null +++ b/src/plugins/polyv-ui/mobile-import.ts @@ -0,0 +1,14 @@ +/* eslint-disable import/first */ +import '@polyv/polyv-ui/lib-front-mob/theme/like.css'; +export { default as PlvLike } from '@polyv/polyv-ui/lib-front-mob/like'; + +import '@polyv/polyv-ui/lib-front-mob/theme/carousel.css'; +import '@polyv/polyv-ui/lib-front-mob/theme/carousel-item.css'; +export { default as PlvCarousel } from '@polyv/polyv-ui/lib-front-mob/carousel'; +export { default as PlvCarouselItem } from '@polyv/polyv-ui/lib-front-mob/carousel-item'; + +import '@polyv/polyv-ui/lib-front-mob/theme/area-picker.css'; +export { default as PlvAreaPicker } from '@polyv/polyv-ui/lib-front-mob/area-picker'; + +import '@polyv/polyv-ui/lib-front-mob/theme/emotional-feedback.css'; +export { default as PlvEmotionalFeedback } from '@polyv/polyv-ui/lib-front-mob/emotional-feedback'; diff --git a/src/plugins/polyv-ui/pc-import.ts b/src/plugins/polyv-ui/pc-import.ts new file mode 100644 index 0000000000000000000000000000000000000000..812847be3d50867919513f742b652c61def97704 --- /dev/null +++ b/src/plugins/polyv-ui/pc-import.ts @@ -0,0 +1,19 @@ +/* eslint-disable import/first */ +/** + * @file polyv-ui PC 端组件引入与导出入口 + */ +import './cover-styles.scss'; + +import '@polyv/polyv-ui/lib-front/theme/like.css'; +export { default as PlvLike } from '@polyv/polyv-ui/lib-front/like'; + +import '@polyv/polyv-ui/lib-front/theme/carousel.css'; +import '@polyv/polyv-ui/lib-front/theme/carousel-item.css'; +export { default as PlvCarousel } from '@polyv/polyv-ui/lib-front/carousel'; +export { default as PlvCarouselItem } from '@polyv/polyv-ui/lib-front/carousel-item'; + +import '@polyv/polyv-ui/lib-front/theme/area-picker.css'; +export { default as PlvAreaPicker } from '@polyv/polyv-ui/lib-front/area-picker'; + +import '@polyv/polyv-ui/lib-front/theme/emotional-feedback.css'; +export { default as PlvEmotionalFeedback } from '@polyv/polyv-ui/lib-front/emotional-feedback'; diff --git a/src/plugins/polyv-ui/types.ts b/src/plugins/polyv-ui/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..f02445bc430c52fb3b4286c386f90621227ad908 --- /dev/null +++ b/src/plugins/polyv-ui/types.ts @@ -0,0 +1,75 @@ +import type areaData from '@polyv/polyv-ui/resources/china-area.json'; + +export interface ImagePreviewOptions { + /** 模式显示的下标 */ + initialIndex?: number; +} + +export interface AreaData { + name: string; + code: string; + childrens?: AreaData[]; +} + +export type AreaDataJson = typeof areaData; + +export interface AreaPickerComponent { + /** 通过value与级联数据获取选择中的数据 */ + getSelectData: (value: string[], areaData: object) => AreaData[]; + /** 是否已选择完成 */ + isSelectFinish: (value: string[], areaData: object) => boolean; + /** 加载中国地区数据 */ + loadAreaDataUrl: () => Promise; +} + +/** + * 点赞动画选项 + */ +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface LikeAnimationOptions {} + +/** + * 点赞组件实例 + */ +export interface PlvLikeInstance { + /** 插入动画 */ + pushAnimation: (options?: LikeAnimationOptions) => void; +} + +/** + * 情绪反馈组件实例 + */ +export interface PlvEmotionalFeedbackInstance { + /** 开始一个动画 */ + start: (type: number, arg: true) => void; +} + +/** + * 轮播组件实例 + */ +export interface PlvCarouselInstance { + handleWindowReszie?: () => void; +} + +export interface PlvInputContentText { + type: 'text'; + content: string; +} + +export interface PlvInputContentImage { + type: 'img'; + file: File; +} + +export type PlvInputContent = PlvInputContentText | PlvInputContentImage; + +export interface PlvInputImagePasteInstance { + /** 输入框聚焦 */ + focus: () => void; + /** 插入表情 */ + insertEmotion: (emotion: string) => void; + /** 获取内容列表 */ + getContentList: () => PlvInputContent[]; + /** 清除输入框内容 */ + clear: () => void; +} diff --git a/src/plugins/vconsole/index.ts b/src/plugins/vconsole/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..33a0e7410e11c0f143b05b04f2f98f71781da070 --- /dev/null +++ b/src/plugins/vconsole/index.ts @@ -0,0 +1,21 @@ +/** + * @file 加载 vconsole + */ +import VConsole from 'vconsole'; +import { paramGetter } from '@/hooks/core/use-query-params'; + +let vConsole: VConsole | undefined; + +// 当存在 url 参数 console=1,会自动加载 vConsole 脚本 +export async function createVConsole() { + if (!vConsole) { + vConsole = new VConsole(); + } + return vConsole; +} + +(() => { + if (paramGetter.console()) { + createVConsole(); + } +})(); diff --git a/src/skins/_common/basic-iar-style.scss b/src/skins/_common/basic-iar-style.scss new file mode 100644 index 0000000000000000000000000000000000000000..b4f4880db985e8614e4cdd2e8f21fe54873024bb --- /dev/null +++ b/src/skins/_common/basic-iar-style.scss @@ -0,0 +1,328 @@ +// 生成皮肤的互动功能样式(公用) +@mixin generate-skin-basic-iar-common-style($--skin-configs) { + // -------- 互动功能 -------- // + .pws-interactive-entrance-button { + color: map.get($--skin-configs, 'interactive-entrance-button:text-color') !important; + background-color: map.get($--skin-configs, 'interactive-entrance-button:bg-color') !important; + &:hover { + background-color: map.get($--skin-configs, 'interactive-entrance-button--hover:bg-color') !important; + } + } + .pws-interactive-entrance-arrow { + color: map.get($--skin-configs, 'interactive-entrance-arrow:text-color') !important; + } + + // --------- 按钮 --------- // + .pws-iar-primary-button { + background-color: map.get($--skin-configs, 'primary-button:bg-color') !important; + &.is-disabled { + opacity: 0.5; + } + } + + // --------- 挂件 --------- // + .pws-iar-pendant-text { + color: map.get($--skin-configs, 'iar-pendant:text-color') !important; + } + + // --------- 商品库 --------- // + .pws-iar-product-list-wrap { + background-color: map.get($--skin-configs, 'main-tab-body:bg-color') !important; + } + .pws-iar-product-list-content { + background-color: map.get($--skin-configs, 'iar-product-list-content:bg-color') !important; + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'main-tab-body:scrollbar-color') !important; + } + } + .pws-iar-product-list-total { + color: map.get($--skin-configs, 'main-text-color') !important; + background-color: inherit !important; + } + .pws-iar-product-list-icon { + background-image: url(map.get($--skin-configs, 'iar-product-list-shop:icon-image')) !important; + } + .pws-iar-product-list-icon-recruitment { + background-image: url(map.get($--skin-configs, 'iar-product-list-recruitment:icon-image')) !important; + } + .pws-iar-product-list-total-highlight { + color: map.get($--skin-configs, 'main-highlight-text-color') !important; + } + .pws-iar-product-list-name { + color: map.get($--skin-configs, 'iar-product-list-good-name:text-color') !important; + } + .pws-iar-product-list-desc-label { + color: map.get($--skin-configs, 'main-text-color') !important; + opacity: 0.8; + } + .pws-iar-product-list-desc { + color: map.get($--skin-configs, 'describe-text-color') !important; + opacity: 1; + } + .pws-iar-product-list-label { + color: map.get($--skin-configs, 'iar-product-list-label:text-color') !important; + background-color: map.get($--skin-configs, 'iar-product-list-label:bg-color') !important; + } + .pws-iar-product-list-number { + background-color: map.get($--skin-configs, 'iar-product-list-name-number:bg-color') !important; + } + .pws-iar-product-list-empty-text { + color: map.get($--skin-configs, 'describe-text-color') !important; + } + .pws-iar-product-list-empty-img { + background-image: url(map.get($--skin-configs, 'iar-product-list-empty:icon-image')) !important; + } + .pws-iar-product-list-item { + border-bottom-color: map.get($--skin-configs, 'iar-product-list-item:border-color') !important; + } + + // ---------- 中奖记录 --------- // + .pws-iar-lottery-record-button { + background-color: map.get($--skin-configs, 'primary-color') !important; + } + .pws-iar-lottery-record-button-received { + color: $--color-white !important; + background-color: rgba(map.get($--skin-configs, 'primary-color'), 0.5) !important; + } + + // --------- 置顶公告 ---------- // + .pws-iar-bulletin-top-banner { + background-color: map.get($--skin-configs, 'iar-bulletin-top-banner:bg-color') !important; + } + .pws-iar-bulletin-top-banner-icon { + fill: map.get($--skin-configs, 'iar-bulletin-top-banner:text-color') !important; + } + .pws-iar-bulletin-top-banner-text, + .pws-iar-bulletin-top-banner-close { + &, + & * { + color: map.get($--skin-configs, 'iar-bulletin-top-banner:text-color') !important; + } + } + + // --------- 快速答题卡 --------- // + .pws-iar-quick-answer-option.is-active { + color: map.get($--skin-configs, 'primary-color') !important; + border-color: map.get($--skin-configs, 'primary-color') !important; + } + + // --------- 问答 --------- // + .pws-iar-qa-filter { + background-color: map.get($--skin-configs, 'iar-qa-filter:bg-color') !important; + border-bottom-color: map.get($--skin-configs, 'iar-qa-filter:border-color') !important; + } + .pws-iar-qa-filter-desc { + color: map.get($--skin-configs, 'iar-qa-filter-desc:text-color') !important; + } + .pws-iar-qa-new-msg-button { + color: map.get($--skin-configs, 'iar-qa-new-msg-button:text-color') !important; + background-color: map.get($--skin-configs, 'iar-qa-new-msg-button:bg-color') !important; + } + .pws-iar-qa-select { + color: map.get($--skin-configs, 'iar-qa-select:text-color') !important; + } + .pws-iar-qa-select-arrow { + border-color: map.get($--skin-configs, 'iar-qa-select:border-color') !important; + } +} + +// 生成皮肤的互动功能样式(PC 端) +@mixin generate-skin-basic-iar-pc-style($--skin-configs) { + @include generate-skin-basic-iar-common-style($--skin-configs); + + // --------- 中奖记录 ---------- // + .pws-iar-pc-lottery-record { + background: none !important; + } + .pws-interactive-msg__content { + color: map.get($--skin-configs, 'iar-pc-lottery-record-msg:text-color') !important; + } + + // --------- 图文直播 ---------- // + .pws-pc-tuwen-live-menu-item:hover, + .pws-pc-tuwen-live-menu-item.is-active { + color: map.get($--skin-configs, 'primary-color') !important; + } + .pws-pc-tuwen-live-menu-item.is-active { + background-color: rgba(map.get($--skin-configs, 'primary-color'), 0.15) !important; + } + .pws-iar-pc-tuwen-live-content-name { + color: map.get($--skin-configs, 'primary-color') !important; + } + + // ---------- 问答 ---------- // + .pws-iar-pc-qa-body { + background-color: map.get($--skin-configs, 'iar-qa-body:bg-color') !important; + } + .pws-iar-pc-qa-body-scroll { + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'main-tab-body:scrollbar-color') !important; + } + } + .pws-iar-pc-qa-empty-text { + color: map.get($--skin-configs, 'describe-text-color') !important; + } + .pws-iar-pc-qa-select-option-popper { + background-color: map.get($--skin-configs, 'iar-pc-qa-select-option:bg-color') !important; + } + .pws-iar-pc-qa-select-option { + color: map.get($--skin-configs, 'iar-pc-qa-select-option:text-color') !important; + background-color: map.get($--skin-configs, 'iar-pc-qa-select-option:bg-color') !important; + border-color: map.get($--skin-configs, 'iar-pc-qa-select-option:border-color') !important; + &:hover { + background-color: map.get($--skin-configs, 'iar-pc-qa-select-option--hover:bg-color') !important; + } + &.is-active { + color: map.get($--skin-configs, 'iar-pc-qa-select-option--active:text-color') !important; + } + } + .pws-iar-pc-qa-ask { + background-color: map.get($--skin-configs, 'pc-msg-bottom-input-wrap:bg-color') !important; + } + .pws-iar-pc-qa-ask-input { + background-color: map.get($--skin-configs, 'pc-msg-input:bg-color') !important; + } + .pws-iar-pc-qa-ask-input-textarea { + color: map.get($--skin-configs, 'pc-msg-input:text-color') !important; + &::placeholder { + color: map.get($--skin-configs, 'pc-msg-input-placeholder:text-color') !important; + } + &::-webkit-scrollbar { + width: 4px; + } + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'pc-msg-input:scrollbar-color') !important; + } + } + .pws-iar-pc-qa-ask-emotion-icon { + color: map.get($--skin-configs, 'iar-pc-qa-ask-emotion:text-color') !important; + filter: map.get($--skin-configs, 'iar-pc-qa-ask-emotion:background-filter') + } + .pws-iar-pc-qa-ask-emotion-popper { + background-color: map.get($--skin-configs, 'pc-emotion-panel:bg-color') !important; + box-shadow: none !important; + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'panel-scrollbar-color') !important; + } + &::-webkit-scrollbar-track { + box-shadow: none; + } + } + .pws-iar-pc-qa-ask-send-button { + color: map.get($--skin-configs, 'pc-msg-send-button:text-color') !important; + background-color: map.get($--skin-configs, 'pc-msg-send-button:bg-color') !important; + &.is-disabled { + color: map.get($--skin-configs, 'pc-msg-send-button--disabled:text-color') !important; + background-color: map.get($--skin-configs, 'pc-msg-send-button--disabled:bg-color') !important; + } + } + .pws-iar-pc-qa-item { + color: map.get($--skin-configs, 'iar-qa-item-content:text-color') !important; + background-color: map.get($--skin-configs, 'iar-qa-item:bg-color') !important; + } + .pws-iar-pc-qa-item-more { + color: map.get($--skin-configs, 'iar-qa-item-content:text-color') !important; + background-color: map.get($--skin-configs, 'iar-qa-item:bg-color') !important; + .pws-iar-multiline-btn-shadow { + background: linear-gradient(270deg, map.get($--skin-configs, 'iar-qa-item:bg-color') 0%, rgba(225, 238, 252, 0) 100%) !important; + } + .pws-iar-multiline-btn-text { + color: map.get($--skin-configs, 'iar-qa-item-more:text-color') !important; + } + .pws-iar-multiline-btn-arrow { + border-color: map.get($--skin-configs, 'iar-qa-item-more:text-color') !important; + } + } + .pws-iar-pc-qa-item-data { + color: map.get($--skin-configs, 'iar-qa-item-info:text-color') !important; + } + .pws-iar-pc-qa-item-answer { + border-color: map.get($--skin-configs, 'iar-qa-item:border-color') !important; + } +} + +// 生成皮肤的互动功能样式(Mobile 端) +@mixin generate-skin-basic-iar-mobile-style($--skin-configs) { + @include generate-skin-basic-iar-common-style($--skin-configs); + + // --------- 问答 --------- // + .pws-iar-mobile-qa-body { + background-color: map.get($--skin-configs, 'iar-qa-body:bg-color') !important; + } + .pws-iar-mobile-qa-no-data { + color: map.get($--skin-configs, 'describe-text-color') !important; + background-color: map.get($--skin-configs, 'main-tab-body:bg-color') !important; + &::before { + background-image: url(map.get($--skin-configs, 'iar-mobile-qa-no-data:icon-image')) !important; + } + } + .pws-iar-mobile-qa-ask-entry { + background-image: url(map.get($--skin-configs, 'iar-mobile-qa-ask-entry:icon-image')) !important; + background-size: 100% 100%; + } + .pws-iar-mobile-qa-item { + color: map.get($--skin-configs, 'iar-qa-item-content:text-color') !important; + background-color: map.get($--skin-configs, 'iar-qa-item:bg-color') !important; + } + .pws-iar-mobile-qa-item-question, + .pws-iar-mobile-qa-item-answer { + background: none !important; + } + .pws-iar-mobile-qa-item-more { + color: map.get($--skin-configs, 'iar-qa-item-content:text-color') !important; + background-color: map.get($--skin-configs, 'iar-qa-item:bg-color') !important; + .pws-iar-multiline-btn-shadow { + background: linear-gradient(270deg, map.get($--skin-configs, 'iar-qa-item:bg-color') 0%, rgba(225, 238, 252, 0) 100%) !important; + } + .pws-iar-multiline-btn-text { + color: map.get($--skin-configs, 'iar-qa-item-more:text-color') !important; + } + .pws-iar-multiline-btn-arrow { + border-color: map.get($--skin-configs, 'iar-qa-item-more:text-color') !important; + } + } + .pws-iar-mobile-qa-item-data { + color: map.get($--skin-configs, 'iar-qa-item-info:text-color') !important; + } + .pws-iar-mobile-qa-item-answer-border { + border-color: map.get($--skin-configs, 'iar-qa-item:border-color') !important; + } + .pws-iar-mobile-no-more { + color: map.get($--skin-configs, 'iar-qa-no-more:text-color') !important; + } + .pws-iar-mobile-qa-ask { + background-color: map.get($--skin-configs, 'iar-mobile-qa-input-wrap:bg-color') !important; + } + .pws-iar-mobile-qa-ask-input-textarea { + color: map.get($--skin-configs, 'iar-mobile-qa-input:text-color') !important; + background-color: map.get($--skin-configs, 'iar-mobile-qa-input:bg-color') !important; + &::placeholder { + color: inherit !important; + opacity: 0.5; + } + } + .pws-iar-mobile-qa-ask-emotion-icon { + color: map.get($--skin-configs, 'iar-mobile-qa-ask-emotion:text-color') !important; + filter: map.get($--skin-configs, 'iar-mobile-qa-ask-emotion:background-filter') + } + .pws-iar-mobile-qa-ask-send-button { + color: map.get($--skin-configs, 'iar-mobile-qa-ask-send-button:text-color') !important; + } + + // ---------- 图文直播 --------- // + .pws-iar-mobile-tuwen-live-header { + color: map.get($--skin-configs, 'iar-mobile-tuwen-live-header:text-color') !important; + } + .pws-iar-mobile-tuwen-live-content-name { + color: map.get($--skin-configs, 'iar-mobile-tuwen-live-content-name:text-color') !important; + } + .pws-iar-mobile-tuwen-live-content-time { + color: map.get($--skin-configs, 'iar-mobile-tuwen-live-content-time:text-color') !important; + } + .pws-iar-mobile-tuwen-live-content-text { + color: map.get($--skin-configs, 'iar-mobile-tuwen-live-content:text-color') !important; + } + .pws-iar-mobile-tuwen-live-no-more { + color: map.get($--skin-configs, 'iar-mobile-tuwen-live-no-more:text-color') !important; + } +} diff --git a/src/skins/_common/basic-mobile-style.scss b/src/skins/_common/basic-mobile-style.scss new file mode 100644 index 0000000000000000000000000000000000000000..2eaf4bb383a562f0ba9819fb9f28e0f5537aee8a --- /dev/null +++ b/src/skins/_common/basic-mobile-style.scss @@ -0,0 +1,260 @@ +@import './basic-style.scss'; +@import './basic-iar-style.scss'; + +/** + * 生成皮肤的 basic mobile 端 样式 + * 规则:pws-mobile-* + */ +@mixin generate-skin-basic-mobile-style($--skin-configs) { + .pws-skin-#{map.get($--skin-configs, 'skin-type')} { + @include generate-skin-basic-style($--skin-configs); + @include generate-skin-basic-iar-mobile-style($--skin-configs); + + // 直播状态标签 + @each $status, $color in map.get($--skin-configs, 'mobile-live-status-tag:color') { + .pws-mobile-live-status-tag-#{$status} { + color: $color !important; + border-color: $color !important; + } + } + + // --------- 富文本 --------- // + .pws-mobile-rich-text-content { + color: map.get($--skin-configs, 'mobile-rich-text-content:text-color') !important; + } + + // --------- 引导页 --------- // + .pws-mobile-splash-page { + background-color: map.get($--skin-configs, 'mobile-splash:bg-color') !important; + } + .pws-mobile-splash-page-live-count-down-wrap { + background-color: map.get($--skin-configs, 'mobile-splash-live-count-down-wrap:bg-color') !important; + } + + // --------- 页脚 --------- // + .pws-mobile-page-footer { + color: map.get($--skin-configs, 'mobile-page-footer:text-color') !important; + } + + // --------- 直播预约 ---------- // + .pws-mobile-live-booking-button { + background-color: map.get($--skin-configs, 'mobile-live-booking-button:bg-color') !important; + } + + // --------- 倒计时 ---------- // + .pws-mobile-count-down-square-item { + background-color: map.get($--skin-configs, 'mobile-count-down-square-item:bg-color') !important; + .pws-count-down-square-item-count { + color: map.get($--skin-configs, 'mobile-count-down-square-item:text-color') !important; + border-color: map.get($--skin-configs, 'mobile-count-down-square-item:border-color') !important; + } + .pws-count-down-square-item-word { + color: map.get($--skin-configs, 'mobile-count-down-square-item-word:text-color') !important; + } + &::before, + &::after { + background-color: map.get($--skin-configs, 'mobile-count-down-square-item-dot:bg-color') !important; + } + } + + // --------- 基础信息 ---------- // + .pws-mobile-basic-info-channel-title { + color: map.get($--skin-configs, 'mobile-basic-info-channel-title:text-color') !important; + } + .pws-mobile-basic-info-start-time { + color: map.get($--skin-configs, 'mobile-basic-info-start-time:text-color') !important; + } + .pws-mobile-basic-info-page-view { + color: map.get($--skin-configs, 'mobile-basic-info-page-view:text-color') !important; + border-color: map.get($--skin-configs, 'main-border-color') !important; + } + .pws-mobile-basic-info-author { + border-color: map.get($--skin-configs, 'main-border-color') !important; + } + .pws-mobile-basic-info-publisher { + color: map.get($--skin-configs, 'mobile-basic-info-publisher:text-color') !important; + } + .pws-mobile-basic-info-like { + color: map.get($--skin-configs, 'mobile-basic-info-like:text-color') !important; + } + + // --------- 关注 --------- // + .pws-mobile-follow-button { + color: map.get($--skin-configs, 'mobile-follow-button:text-color') !important; + background-color: map.get($--skin-configs, 'mobile-follow-button:bg-color') !important; + } + + // --------- 消息输入框 --------- // + .pws-mobile-chat-msg-input { + background-color: map.get($--skin-configs, 'mobile-msg-input:bg-color') !important; + } + .pws-mobile-chat-msg-input-textarea { + color: map.get($--skin-configs, 'mobile-msg-input:text-color') !important; + &::placeholder { + color: map.get($--skin-configs, 'mobile-msg-input-placeholder:text-color') !important; + } + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'mobile-msg-input:scrollbar-color') !important; + } + } + .pws-mobile-msg-input-suffix-icon { + color: map.get($--skin-configs, 'mobile-msg-input-suffix-icon:text-color') !important; + } + .pws-mobile-msg-send-button { + color: map.get($--skin-configs, 'mobile-msg-send-button:text-color') !important; + } + + // --------- 表情选择 --------- // + .pws-mobile-msg-input-wrap-emotion-show { + .pws-mobile-msg-input-wrap-main { + background-color: map.get($--skin-configs, 'mobile-emotion-panel:bg-color') !important; + } + .pws-mobile-msg-send-button { + color: map.get($--skin-configs, 'mobile-emotion-panel-send-button:bg-color') !important; + } + .pws-mobile-chat-msg-input { + background-color: map.get($--skin-configs, 'mobile-emotion-panel-msg-input:bg-color') !important; + } + .pws-mobile-chat-msg-input-textarea { + color: map.get($--skin-configs, 'mobile-emotion-panel-msg-input:text-color') !important; + &::placeholder { + color: map.get($--skin-configs, 'mobile-emotion-panel-msg-input-placeholder:text-color') !important; + } + } + .pws-mobile-msg-input-suffix-icon { + color: map.get($--skin-configs, 'mobile-emotion-panel-msg-input-suffix-icon:text-color') !important; + } + } + .pws-mobile-emotion-picker { + background-color: map.get($--skin-configs, 'mobile-emotion-panel:bg-color') !important; + } + .pws-mobile-emotion-picker-tabs { + background-color: map.get($--skin-configs, 'mobile-emotion-panel-type:bg-color') !important; + } + .pws-mobile-emotion-picker-tabs-item { + color: map.get($--skin-configs, 'mobile-emotion-panel-type:text-color') !important; + &.is-active { + color: map.get($--skin-configs, 'mobile-emotion-panel-type--active:text-color') !important; + } + } + .pws-mobile-emotion-picker-tabs-active { + background-color: map.get($--skin-configs, 'mobile-emotion-panel-type--active:bg-color') !important; + } + .pws-mobile-emotion-picker-del-button { + color: map.get($--skin-configs, 'mobile-emotion-panel-del-button:text-color') !important; + background-color: map.get($--skin-configs, 'mobile-emotion-panel-del-button:bg-color') !important; + } + + // --------- 菜单 --------- // + .pws-mobile-more-button { + background-color: map.get($--skin-configs, 'mobile-more-button:bg-color') !important; + &::after { + background-image: url(map.get($--skin-configs, 'mobile-more-button:icon-image')) !important; + } + } + .pws-mobile-more-panel { + background-color: map.get($--skin-configs, 'mobile-more-panel:bg-color') !important; + } + .pws-more-panel-text { + color: map.get($--skin-configs, 'mobile-more-panel:text-color') !important; + } + + // --------- 打赏 --------- // + .pws-mobile-donate-button { + background-color: map.get($--skin-configs, 'mobile-donate-button:bg-color') !important; + } + .pws-mobile-donate-panel { + background-color: map.get($--skin-configs, 'mobile-donate-panel:bg-color') !important; + } + .pws-mobile-panel-header-title { + color: map.get($--skin-configs, 'mobile-donate-panel-header-title:text-color') !important; + } + .pws-mobile-panel-header-close { + color: map.get($--skin-configs, 'mobile-donate-panel-header-close:text-color') !important; + } + .pws-mobile-donate-good-item.is-active { + background-color: map.get($--skin-configs, 'mobile-donate-good--active:bg-color') !important; + border-color: map.get($--skin-configs, 'mobile-donate-good--active:border-color') !important; + } + .pws-mobile-donate-good-name { + color: map.get($--skin-configs, 'mobile-donate-good-name:text-color') !important; + } + .pws-mobile-donate-good-price { + color: map.get($--skin-configs, 'mobile-donate-good-price:text-color') !important; + } + .pws-mobile-donate-carousel-indicator-item { + background-color: map.get($--skin-configs, 'mobile-donate-indicator:bg-color') !important; + &.is-active { + background-color: map.get($--skin-configs, 'mobile-donate-indicator--active:bg-color') !important; + } + } + .pws-mobile-donate-panel-option { + color: map.get($--skin-configs, 'mobile-donate-panel-option:text-color') !important; + background-color: map.get($--skin-configs, 'mobile-donate-panel-option:bg-color') !important; + &.is-active { + color: map.get($--skin-configs, 'mobile-donate-panel-option--active:text-color') !important; + background-color: map.get($--skin-configs, 'mobile-donate-panel-option--active:bg-color') !important; + border-color: map.get($--skin-configs, 'mobile-donate-panel-option--active:border-color') !important; + } + } + .pws-mobile-donate-send-button { + color: map.get($--skin-configs, 'mobile-donate-panel-send-button:text-color') !important; + background-color: map.get($--skin-configs, 'mobile-donate-panel-send-button:bg-color') !important; + } + .pws-mobile-donate-point-data { + color: map.get($--skin-configs, 'mobile-donate-panel-point:text-color') !important; + } + .pws-mobile-donate-point-data-count { + color: map.get($--skin-configs, 'mobile-donate-panel-point-count:text-color') !important; + } + .pws-mobile-donate-cash-custom-cash { + color: map.get($--skin-configs, 'mobile-donate-panel-custom-cash:text-color') !important; + } + .pws-mobile-donate-custom-cash-input { + .pws-mobile-form-input-main { + background-color: map.get($--skin-configs, 'mobile-donate-custom-cash-input:bg-color') !important; + border: none; + } + .pws-mobile-form-input-main-title { + color: map.get($--skin-configs, 'mobile-donate-custom-cash-input-title:text-color') !important; + } + .pws-mobile-form-input-main-input { + color: map.get($--skin-configs, 'mobile-donate-custom-cash-input:text-color') !important; + &::placeholder { + color: inherit; + opacity: 0.7; + } + } + } + .pws-mobile-donate-custom-cash-random { + color: map.get($--skin-configs, 'mobile-donate-custom-cash-random:text-color') !important; + } + + // -------- 回放列表 -------- // + .pws-mobile-playback-list-playing { + background-color: map.get($--skin-configs, 'pc-playback-list-playing:bg-color') !important; + } + .pws-mobile-playback-list-title, + .pws-mobile-playback-list-time { + color: map.get($--skin-configs, 'mobile-playback-list:text-color') !important; + } + .pws-mobile-playback-list-item.is-active { + .pws-mobile-playback-list-title, + .pws-mobile-playback-list-time { + color: map.get($--skin-configs, 'mobile-playback-list--active:text-color') !important; + } + } + + // -------- 多会场 -------- // + .pws-mobile-multi-meeting { + color: map.get($--skin-configs, 'mobile-multi-meeting:text-color'); + background-color: map.get($--skin-configs, 'mobile-multi-meeting:bg-color'); + } + .pws-mobile-multi-meeting-count { + color: rgba(map.get($--skin-configs, 'mobile-multi-meeting:text-color'), 0.6); + } + .pws-mobile-multi-meeting-pack-up { + color: rgba(map.get($--skin-configs, 'mobile-multi-meeting:text-color'), 0.6); + } + } +} diff --git a/src/skins/_common/basic-pc-style.scss b/src/skins/_common/basic-pc-style.scss new file mode 100644 index 0000000000000000000000000000000000000000..512102a4c329821a6d045ce7cd5d0c292d40d5ee --- /dev/null +++ b/src/skins/_common/basic-pc-style.scss @@ -0,0 +1,216 @@ +@import './basic-style.scss'; +@import './basic-iar-style.scss'; + +@mixin generate-skin-basic-plv-ui-pc-style($--skin-configs) { + .pws-pc-image-paste-input-wrapper { + color: map.get($--skin-configs, 'pc-msg-input:text-color') !important; + background-color: map.get($--skin-configs, 'pc-msg-input:bg-color') !important; + .plv-input-paste-image { + color: inherit; + background: unset; + border: none; + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'pc-msg-input:scrollbar-color') !important; + } + &::before { + color: map.get($--skin-configs, 'pc-msg-input-placeholder:text-color') !important; + } + } + } +} + +/** + * 生成皮肤的 basic pc 端 样式 + * 规则:pws-pc-* + */ +@mixin generate-skin-basic-pc-style($--skin-configs) { + .pws-skin-#{map.get($--skin-configs, 'skin-type')} { + @include generate-skin-basic-style($--skin-configs); + @include generate-skin-basic-iar-pc-style($--skin-configs); + @include generate-skin-basic-plv-ui-pc-style($--skin-configs); + + // 直播状态标签 + @each $status, $color in map.get($--skin-configs, 'pc-live-status-tag:color') { + .pws-pc-live-status-tag-#{$status} { + color: $color !important; + border-color: $color !important; + } + } + + // ---------- 观看页背景 ---------- // + .pws-pc-watch-bg { + @include skin-setup-bg(( + 'bg-type': map.get($--skin-configs, 'pc-watch-page:bg-type'), + 'bg-color': map.get($--skin-configs, 'pc-watch-page:bg-color'), + 'bg-image': map.get($--skin-configs, 'pc-watch-page:bg-image'), + )); + } + + .pws-pc-sub-pack-up-placeholder { + color: map.get($--skin-configs, 'pc-sub-pack-up:text-color'); + background-color: map.get($--skin-configs, 'main-tab-body:bg-color'); + } + + // --------- 倒计时 ---------- // + .pws-pc-count-down-square-item { + background-color: map.get($--skin-configs, 'pc-count-down-square-item:bg-color') !important; + .pws-count-down-square-item-count { + color: map.get($--skin-configs, 'pc-count-down-square-item:text-color') !important; + border-color: map.get($--skin-configs, 'pc-count-down-square-item:border-color') !important; + } + .pws-count-down-square-item-word { + color: map.get($--skin-configs, 'pc-count-down-square-item-word:text-color') !important; + } + &::before, + &::after { + background-color: map.get($--skin-configs, 'pc-count-down-square-item-dot:bg-color') !important; + } + } + + // 聊天消息底部输入框层 + .pws-pc-msg-bottom-input-wrap { + background-color: map.get($--skin-configs, 'pc-msg-bottom-input-wrap:bg-color') !important; + } + .pws-pc-msg-bottom-input-wrap-item { + color: map.get($--skin-configs, 'pc-msg-bottom-input-wrap-item:text-color') !important; + &:not(.is-disabled):hover { + color: map.get($--skin-configs, 'pc-msg-bottom-input-wrap-item--hover:text-color') !important; + } + } + .pws-pc-msg-input-limit { + color: map.get($--skin-configs, 'pc-msg-bottom-input-wrap-item:text-color') !important;; + } + + // ---------- 聊天消息输入框 ---------- // + .pws-pc-chat-msg-input { + background-color: map.get($--skin-configs, 'pc-msg-input:bg-color') !important; + } + .pws-pc-chat-msg-input-textarea { + color: map.get($--skin-configs, 'pc-msg-input:text-color') !important; + &::placeholder { + color: map.get($--skin-configs, 'pc-msg-input-placeholder:text-color') !important; + } + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'pc-msg-input:scrollbar-color') !important; + } + } + + // ---------- 聊天消息发送按钮 ---------- // + .pws-pc-msg-send-button { + color: map.get($--skin-configs, 'pc-msg-send-button:text-color') !important; + background-color: map.get($--skin-configs, 'pc-msg-send-button:bg-color') !important; + &.is-disabled { + color: map.get($--skin-configs, 'pc-msg-send-button--disabled:text-color') !important; + background-color: map.get($--skin-configs, 'pc-msg-send-button--disabled:bg-color') !important; + } + } + + // ------------ 图片达到最大数量后的提示框 ----------- // + .pws-pc-msg-input-popper__max-image-tips { + color: map.get($--skin-configs, 'pws-pc-msg-input-popper__max-image-tips:text-color') !important; + background-color: map.get($--skin-configs, 'pws-pc-msg-input-popper__max-image-tips:bg-color') !important; + } + + // ---------- 设置昵称占位层 ---------- // + .pws-pc-set-nick-placeholder { + color: map.get($--skin-configs, 'pc-set-nick-placeholder:text-color') !important; + background-color: map.get($--skin-configs, 'pc-set-nick-placeholder:bg-color') !important; + } + .pws-pc-set-nick-placeholder-highlight { + color: map.get($--skin-configs, 'pc-set-nick-placeholder-highlight:text-color') !important; + } + + // ---------- 表情选择 ---------- // + .pws-pc-emotion-panel { + background-color: map.get($--skin-configs, 'pc-emotion-panel:bg-color') !important; + } + .pws-pc-emotion-panel-scrollbar { + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'panel-scrollbar-color') !important; + } + } + .pws-pc-emotion-panel-type-tab { + background-color: map.get($--skin-configs, 'pc-emotion-panel-select:bg-color') !important; + + .is-active { + background-color: map.get($--skin-configs, 'pc-emotion-panel:bg-color') !important; + } + } + + // ---------- 更多面板 ---------- // + .pws-pc-more-panel { + background-color: map.get($--skin-configs, 'pc-more-panel:bg-color') !important; + + .pws-pc-checkbox.is-checked { + .pws-pc-checkbox-inner { + background-color: map.get($--skin-configs, 'pc-more-panel-checkbox--checked:bg-color') !important; + } + .pws-pc-checkbox-inner-icon { + color: map.get($--skin-configs, 'pc-more-panel-checkbox-icon--checked:text-color') !important; + } + } + } + .pws-pc-more-panel-item { + color: map.get($--skin-configs, 'pc-more-panel:text-color') !important; + &:hover { + color: map.get($--skin-configs, 'pc-more-panel-text--hover:text-color') !important; + } + } + + // ---------- 中奖记录 ---------- // + .pws-pc-lottery-record { + background-color: map.get($--skin-configs, 'pc-lottery-record-panel:bg-color') !important; + } + .pws-pc-lottery-record-scrollbar { + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'panel-scrollbar-color') !important; + } + } + .pws-pc-lottery-record-close-icon { + color: map.get($--skin-configs, 'pc-lottery-record-close:text-color') !important; + } + + // ---------- 打赏 ---------- // + .pws-pc-donate-panel { + background-color: map.get($--skin-configs, 'pc-donate-panel:bg-color') !important; + } + .pws-pc-donate-panel-scrollbar { + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'panel-scrollbar-color') !important; + } + } + .pws-pc-donate-good-item:hover { + background-color: map.get($--skin-configs, 'pc-donate-good--hover:bg-color') !important; + border-color: map.get($--skin-configs, 'pc-donate-good--hover:border-color'); + } + .pws-pc-donate-good-name { + color: map.get($--skin-configs, 'pc-donate-good-name:text-color') !important; + } + .pws-pc-donate-good-price { + color: map.get($--skin-configs, 'pc-donate-good-price:text-color') !important; + } + + // -------- 连麦 -------- // + .pws-pc-connect-mic-desc-icon { + color: map.get($--skin-configs, 'pc-connect-mic-desc-icon:text-color') !important; + } + .pws-pc-connect-mic-desc-text { + color: map.get($--skin-configs, 'pc-connect-mic-desc:text-color') !important; + } + .pws-pc-connect-mic-button { + color: map.get($--skin-configs, 'pc-connect-mic-button:text-color') !important; + background-color: map.get($--skin-configs, 'pc-connect-mic-button:bg-color') !important; + } + .pws-pc-connect-mic-button-highlight-text { + color: map.get($--skin-configs, 'pc-connect-mic-button-highlight:text-color') !important; + } + .pws-pc-connect-mic-applying-icon { + background-image: url(map.get($--skin-configs, 'pc-connect-mic-applying:icon-image')) !important; + } + + // -------- 回放列表 -------- // + .pws-pc-playback-list-playing { + background-color: map.get($--skin-configs, 'pc-playback-list-playing:bg-color') !important; + } + } +} diff --git a/src/skins/_common/basic-style.scss b/src/skins/_common/basic-style.scss new file mode 100644 index 0000000000000000000000000000000000000000..cad9d0869648b130bd16cbcef72f6df73b09b57c --- /dev/null +++ b/src/skins/_common/basic-style.scss @@ -0,0 +1,248 @@ +@import './mixins.scss'; + +// 生成皮肤的 basic 样式 +@mixin generate-skin-basic-style($--skin-configs) { + // 在多皮肤下去除背景 + .pws-bg-none { + background: none !important; + } + + // --------- 按钮:主要 --------- // + .pws-normal-button-primary { + color: map.get($--skin-configs, 'primary-button:text-color'); + background: map.get($--skin-configs, 'primary-button:bg-color') !important; + + &:not(.is-mobile, .is-disabled) { + &:hover { + background: map.get($--skin-configs, 'primary-button--hover:bg-color') !important; + } + &:active { + background: map.get($--skin-configs, 'primary-button--active:bg-color') !important; + } + } + &.is-disabled { + color: map.get($--skin-configs, 'primary-button--disabled:text-color') !important; + background-color: map.get($--skin-configs, 'primary-button--disabled:bg-color') !important; + } + } + + // -------- 按钮:侧边栏 -------- // + .pws-normal-button-aside-menu { + color: map.get($--skin-configs, 'aside-menu-button:text-color') !important; + background-color: map.get($--skin-configs, 'aside-menu-button:bg-color') !important; + &:not(.is-mobile, .is-disabled) { + &:hover { + background-color: map.get($--skin-configs, 'aside-menu-button--hover:bg-color') !important; + } + &:active { + background-color: map.get($--skin-configs, 'aside-menu-button--active:bg-color') !important; + } + } + } + + // --------- 常规 tab --------- // + .pws-normal-tab-current-line { + background-color: map.get($--skin-configs, 'normal-tab-header-line--active:bg-color') !important; + } + + // --------- 主要 tab ---------- // + .pws-main-tab-header { + background-color: map.get($--skin-configs, 'main-tab-header:bg-color') !important; + } + .pws-main-tab-header-item { + color: map.get($--skin-configs, 'main-tab-header:text-color') !important; + &:hover { + color: map.get($--skin-configs, 'main-tab-header--hover:text-color') !important; + } + &.is-active { + color: map.get($--skin-configs, 'main-tab-header--active:text-color') !important; + } + } + .pws-main-tab-header-active-line { + background-color: map.get($--skin-configs, 'main-tab-header-line--active:bg-color') !important; + } + .pws-main-tab-header-shadow-left { + background: linear-gradient(90deg, map.get($--skin-configs, 'main-tab-header-shadow-start:color') 0, map.get($--skin-configs, 'main-tab-header-shadow-end:color') 100%) !important; + } + .pws-main-tab-header-shadow-right { + background: linear-gradient(90deg, map.get($--skin-configs, 'main-tab-header-shadow-end:color') 0, map.get($--skin-configs, 'main-tab-header-shadow-start:color') 100%) !important + } + .pws-main-tab-header-arrow-right, + .pws-main-tab-header-arrow-left { + color: map.get($--skin-configs, 'main-tab-header--active:text-color') !important; + background-color: map.get($--skin-configs, 'main-tab-header:bg-color') !important; + } + .pws-main-tab-header-item-reminder::after { + background-color: map.get($--skin-configs, 'main-tab-header-reminder:color'); + } + .pws-main-tab-body { + background-color: map.get($--skin-configs, 'main-tab-body:bg-color') !important; + } + .pws-main-tab-body-scrollbar { + &::-webkit-scrollbar-thumb { + background-color: map.get($--skin-configs, 'main-tab-body:scrollbar-color') !important; + } + } + + // --------- 聊天消息 ---------- // + // 聊天消息用户昵称 + .pws-chat-msg-user-nickname { + color: map.get($--skin-configs, 'chat-msg-user-nickname:text-color') !important; + } + .pws-msg-user-info-special .pws-chat-msg-user-nickname { + color: map.get($--skin-configs, 'chat-msg-user-special-nickname:text-color') !important; + } + + // 聊天消息用户头衔 + .pws-chat-msg-user-actor { + color: map.get($--skin-configs, 'chat-msg-user-actor:text-color') !important; + background-color: map.get($--skin-configs, 'chat-msg-user-actor:bg-color') !important; + } + + // 聊天消息用户设置昵称图标 + .pws-chat-msg-user-set-nick-icon { + color: map.get($--skin-configs, 'chat-msg-user-set-nick-icon:text-color') !important; + } + + // 聊天消息时间 + .pws-chat-msg-time { + color: map.get($--skin-configs, 'chat-msg-time:text-color') !important; + } + + .pws-chat-msg-list-tips { + color: map.get($--skin-configs, 'chat-msg-list-tips:text-color') !important; + } + + // 聊天消息气泡 + .pws-chat-msg-bubble { + color: map.get($--skin-configs, 'chat-msg-bubble:text-color') !important; + background-color: map.get($--skin-configs, 'chat-msg-bubble:bg-color') !important; + } + .pws-chat-msg-container-special .pws-chat-msg-bubble { + color: map.get($--skin-configs, 'chat-msg-bubble-special:text-color') !important; + background-color: map.get($--skin-configs, 'chat-msg-bubble-special:bg-color') !important; + } + + // 聊天消息回复内容 + .pws-chat-msg-quote-content { + color: map.get($--skin-configs, 'chat-msg-quote-content:text-color') !important; + border-bottom-color: map.get($--skin-configs, 'chat-msg-quote-content:border-color') !important; + } + .pws-chat-msg-container-special .pws-chat-msg-quote-content { + color: map.get($--skin-configs, 'chat-msg-special-quote-content:text-color') !important; + border-bottom-color: map.get($--skin-configs, 'chat-msg-special-quote-content:border-color') !important; + } + + // 聊天消息发送按钮 + .pws-chat-msg-quote-button { + color: map.get($--skin-configs, 'chat-msg-quote-button:text-color') !important; + } + + // 聊天引用回复消息 + .pws-chat-quote-msg { + color: map.get($--skin-configs, 'chat-quote-msg:text-color') !important; + background-color: map.get($--skin-configs, 'chat-quote-msg:bg-color') !important; + } + + // 聊天消息翻译按钮 + .pws-chat-msg-translate-button { + color: map.get($--skin-configs, 'chat-msg-translate-button:text-color') !important; + &:hover { + color: map.get($--skin-configs, 'chat-msg-translate-button--hover:text-color') !important; + } + } + + // 聊天消息翻译完成 + .pws-chat-msg-translate-finish { + color: map.get($--skin-configs, 'chat-msg-translate-finish:text-color') !important; + } + .pws-chat-msg-container-special .pws-chat-msg-translate-finish { + color: map.get($--skin-configs, 'chat-msg-translate-finish-special:text-color') !important; + } + + // 文本消息 + .pws-chat-speak-msg-control { + color: rgba(map.get($--skin-configs, 'chat-msg-bubble:text-color'), 0.8) !important; + } + .pws-chat-speak-msg-control-item::before { + border-color: rgba(map.get($--skin-configs, 'chat-msg-bubble:text-color'), 0.3) !important; + } + .pws-chat-msg-container-special .pws-chat-speak-msg-control { + color: rgba(map.get($--skin-configs, 'chat-msg-bubble-special:text-color'), 0.8) !important; + .pws-chat-speak-msg-control-item::before { + border-color: rgba(map.get($--skin-configs, 'chat-msg-bubble-special:text-color'), 0.3) !important; + } + } + + // 聊天室更多消息 + .pws-chat-msg-more-button { + color: map.get($--skin-configs, 'chat-msg-more-button:text-color'); + background-color: map.get($--skin-configs, 'chat-msg-more-button:bg-color'); + &:hover { + color: map.get($--skin-configs, 'chat-msg-more-button--hover:text-color'); + background-color: map.get($--skin-configs, 'chat-msg-more-button--hover:bg-color'); + } + } + + // 打赏消息 + .pws-chat-reward-msg { + color: map.get($--skin-configs, 'chat-reward-msg:text-color') !important; + } + + // 自定义消息 + .pws-chat-customer-msg { + color: map.get($--skin-configs, 'chat-customer-msg:text-color') !important; + } + + // 红包领取消息 + .pws-chat-redpaper-receive-msg { + color: map.get($--skin-configs, 'chat-redpaper-receive-msg:text-color') !important; + background-color: map.get($--skin-configs, 'chat-redpaper-receive-msg:bg-color') !important; + } + + // 系统消息 + .pws-chat-system-msg { + color: map.get($--skin-configs, 'chat-system-msg:text-color') !important; + } + + // -------- 章节 -------- // + .pws-chapter-item { + color: map.get($--skin-configs, 'chapter-item:text-color') !important; + border-bottom-color: map.get($--skin-configs, 'chapter-item:border-color') !important; + &.is-active { + color: map.get($--skin-configs, 'chapter-item--active:text-color') !important; + } + } + + // -------- 成员列表 ------- // + .pws-member-list-item { + color: map.get($--skin-configs, 'member-list-item:text-color') !important; + } + .pws-member-list-item-special { + color: map.get($--skin-configs, 'member-list-item-special:text-color') !important; + } + .pws-member-list-fail { + color: map.get($--skin-configs, 'describe-text-color') !important; + } + .pws-member-list-fail-highlight { + color: map.get($--skin-configs, 'main-highlight-text-color') !important; + } + + // -------- 点赞 -------- // + .pws-like-button { + background-image: url(map.get($--skin-configs, 'like-button:bg-image')) !important; + } + .pws-like-count-number { + color: map.get($--skin-configs, 'like-count-number:text-color') !important; + background-color: map.get($--skin-configs, 'like-count-number:bg-color') !important; + } + + // -------- 页面广告 -------- // + .pws-page-advert { + color: map.get($--skin-configs, 'page-advert:text-color'); + background-color: map.get($--skin-configs, 'page-advert:bg-color'); + } + .pws-page-advert-arrow { + color: map.get($--skin-configs, 'page-advert-arrow:text-color'); + } +} diff --git a/src/skins/_common/functions.scss b/src/skins/_common/functions.scss new file mode 100644 index 0000000000000000000000000000000000000000..a973dc1245a77389b2a95ada65229846f117adc0 --- /dev/null +++ b/src/skins/_common/functions.scss @@ -0,0 +1,38 @@ +/** + * 生成皮肤配置 + */ +@function generate-skin-setup-configs() { + $--new-configs: (); + + // 皮肤基础配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-basic-configs); + + // 皮肤组件配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-component-configs); + + // 皮肤公用页面配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-page-common-configs); + // 皮肤 PC 页面配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-page-pc-configs); + // 皮肤 Mobile 页面配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-page-mobile-configs); + + // 皮肤互动功能公用配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-iar-common-configs); + // 皮肤互动功能 PC 配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-iar-pc-configs); + // 皮肤互动功能 Mobile 配置 + $--new-configs: map.deep-merge($--new-configs, $--skin-iar-mobile-configs); + + @return $--new-configs; +} + +// @function merger-skin-configs($--config-1: (), $--config-2: (), $--config-3: ()) { +// $--new-configs: (); + +// $--new-configs: map.deep-merge($--new-configs, $--config-1); +// $--new-configs: map.deep-merge($--new-configs, $--config-2); +// $--new-configs: map.deep-merge($--new-configs, $--config-3); + +// @return $--new-configs; +// } diff --git a/src/skins/_common/mixins.scss b/src/skins/_common/mixins.scss new file mode 100644 index 0000000000000000000000000000000000000000..715d537eddc690ba8fe59f2e05430a164b1c06a9 --- /dev/null +++ b/src/skins/_common/mixins.scss @@ -0,0 +1,23 @@ +/** + * 皮肤背景混入 + */ +@mixin skin-setup-bg($options) { + // 背景类型 + $--bg-type: map.get($options, 'bg-type'); + // 背景图片 + $--bg-image: map.get($options, 'bg-image'); + // 背景颜色 + $--bg-color: map.get($options, 'bg-color'); + // 背景渐变色 + $--bg-gradient-color: map.get($options, 'bg-gradient-color'); + + @if ($--bg-type == 'image') { + background-image: url($--bg-image) !important; + } @else if ($--bg-type == 'color') { + background-color: $--bg-color !important; + } @else if ($--bg-type == 'line-gradient') { + background: $--bg-gradient-color; + } @else { + @error '缺少 bg-type'; + } +} diff --git a/src/skins/_debug/skin-select.vue b/src/skins/_debug/skin-select.vue new file mode 100644 index 0000000000000000000000000000000000000000..0e3cef7609c76f4e73a52ca5147455b872bb22c7 --- /dev/null +++ b/src/skins/_debug/skin-select.vue @@ -0,0 +1,64 @@ + + + + + + diff --git a/src/skins/black/_black-mobile.scss b/src/skins/black/_black-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..87c09f945a1836f57dd951bb4b17cc5963a48285 --- /dev/null +++ b/src/skins/black/_black-mobile.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-mobile-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-mobile-style($--skin-configs); diff --git a/src/skins/black/_black-pc.scss b/src/skins/black/_black-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a13764923fe827d4013ff68666fb5afd7f37d7d --- /dev/null +++ b/src/skins/black/_black-pc.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-pc-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-pc-style($--skin-configs); diff --git a/src/skins/black/config-common.scss b/src/skins/black/config-common.scss new file mode 100644 index 0000000000000000000000000000000000000000..9d03180fe4aee7f37993d9eacc28f109b9523507 --- /dev/null +++ b/src/skins/black/config-common.scss @@ -0,0 +1,244 @@ +// 主题名称 +$--skin-type: 'black'; + +// 主题色 +$--skin-primary-color: #3082FE; +// 次要主题色 +$--skin-sub-primary-color: #498CF1; + +// 皮肤基础配置 +$--skin-basic-configs: ( + // 主题名称 + 'skin-type': $--skin-type, + + // 主题色 + 'primary-color': $--skin-primary-color, + // 次要主题色 + 'sub-primary-color': $--skin-sub-primary-color, + // #458ffe + 'primary-light-1': get-opacity-color($--skin-primary-color, 1), + // #599bfe + 'primary-light-2': get-opacity-color($--skin-primary-color, 2), + // #6ea8fe + 'primary-light-3': get-opacity-color($--skin-primary-color, 3), + // #83b4fe + 'primary-light-4': get-opacity-color($--skin-primary-color, 4), + // #98c1ff + 'primary-light-5': get-opacity-color($--skin-primary-color, 5), + // #accdff + 'primary-light-6': get-opacity-color($--skin-primary-color, 6), + // #c1daff + 'primary-light-7': get-opacity-color($--skin-primary-color, 7), + // #d6e6ff + 'primary-light-8': get-opacity-color($--skin-primary-color, 8), + // #eaf3ff + 'primary-light-9': get-opacity-color($--skin-primary-color, 9), + + // ---------- 背景色 ---------- // + // 主要背景色(以侧边栏主体背景色定义) + 'main-bg-color': #202127, + // 次要背景色(以侧边栏头部背景色定义) + 'sub-bg-color': #3e3e4e, + // 面板背景色 + 'panel-bg-color': #2b2c35, + // 禁用背景色 + 'disabled-bg-color': #e9ecee, + + // ---------- 字体色 ---------- // + // 正文字体色 + 'main-text-color': #adadc0, + // 正文高亮字体色 + 'main-highlight-text-color': #5394f6, + // 描述字体色 + 'describe-text-color': #adadc0, + // 禁用字体色 + 'disabled-text-color': #9e9e9e, + // 面板字体色 + 'panel-text-color': $--color-white, + // 面板高亮字体色 + 'panel-highlight-text-color': $--skin-sub-primary-color, + + // ---------- 边框色 ---------- // + 'main-border-color': rgba($--color-white, 0.1), + + // ---------- 滚动条 ---------- // + 'panel-scrollbar-color': rgba($--color-white, 0.1), +); + +// 皮肤基础组件配置 +$--skin-component-configs: ( + // 主要 tab 头部背景色 + 'main-tab-header:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部字体色 + 'main-tab-header:text-color': #adadc0, + // 主要 tab 头部 hover 字体色 + 'main-tab-header--hover:text-color': $--color-white, + // 主要 tab 头部激活字体色 + 'main-tab-header--active:text-color': $--color-white, + // 主要 tab 头部激活选项线条背景色 + 'main-tab-header-line--active:bg-color': $--color-white, + // 主要 tab 头部阴影开始颜色 + 'main-tab-header-shadow-start:color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部阴影结束颜色 + 'main-tab-header-shadow-end:color': rgba(62, 62, 78, 0), + // 主要 tab 头部红点颜色 + 'main-tab-header-reminder:color': #f24453, + // 主要 tab 主体背景色 + 'main-tab-body:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 主要 tab 主体滚动栏颜色 + 'main-tab-body:scrollbar-color': #46464F, + + // normal tab 激活项线条背景色 + 'normal-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 主题按钮背景色 + 'primary-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主题按钮字体色 + 'primary-button:text-color': $--color-white, + // 主题按钮 hover 背景色 + 'primary-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-light-2'), + // 主题按钮 hover 字体色 + 'primary-button--hover:text-color': $--color-white, + // 主题按钮 active 背景色 + 'primary-button--active:bg-color': map.get($--skin-basic-configs, 'primary-light-1'), + // 主题按钮 active 字体色 + 'primary-button--active:text-color': $--color-white, + // 主题按钮 disabled 背景色 + 'primary-button--disabled:bg-color': map.get($--skin-basic-configs, 'disabled-bg-color'), + // 主题按钮 disabled 字体色 + 'primary-button--disabled:text-color': map.get($--skin-basic-configs, 'disabled-text-color'), + + // 侧边栏按钮背景色 + 'aside-menu-button:bg-color': rgba($--color-white, 0.1), + // 侧边栏按钮字体则 + 'aside-menu-button:text-color': $--color-white, + // 侧边栏按钮 hover 背景色 + 'aside-menu-button--hover:bg-color': rgba($--color-white, 0.2), + // 侧边栏按钮 active 背景色 + 'aside-menu-button--active:bg-color': rgba($--color-white, 0.15), + + // pc 端直播状态标签颜色 + 'pc-live-status-tag:color': ( + 'unStart' #ADADC0, + 'waiting' #78A7ED, + 'live' #F06E6E, + 'end' rgba(#ffffff, .6), + 'playback' #78A7ED, + 'stop' #F06E6E, + ), + // mobile 端直播状态标签颜色 + 'mobile-live-status-tag:color': ( + 'unStart' #ADADC0, + 'waiting' #78A7ED, + 'live' #F06E6E, + 'end' rgba(#ffffff, .6), + 'playback' #78A7ED, + 'stop' #F06E6E, + ), +); + +// 皮肤公用页面配置 +$--skin-page-common-configs: ( + // 聊天消息用户昵称字体色 + 'chat-msg-user-nickname:text-color': #adadc0, + // 聊天消息特殊身份昵称字体色 + 'chat-msg-user-special-nickname:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息用户头衔背景色 + 'chat-msg-user-actor:bg-color': #5394f6, + // 聊天消息用户头衔字体色 + 'chat-msg-user-actor:text-color': $--color-white, + // 聊天消息用户设置昵称图标字体色 + 'chat-msg-user-set-nick-icon:text-color': #78a7ed, + // 聊天消息时间字体色 + 'chat-msg-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 聊天提示文案字体色 + 'chat-msg-list-tips:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 聊天消息气泡背景色 + 'chat-msg-bubble:bg-color': #2b2c35, + // 聊天消息气泡字体色 + 'chat-msg-bubble:text-color': #e4e4e4, + // 聊天消息特殊身份气泡背景色 + 'chat-msg-bubble-special:bg-color': #5394f6, + // 聊天消息特殊身份气泡字体色 + 'chat-msg-bubble-special:text-color': $--color-white, + + // 聊天消息回复内容字体色 + 'chat-msg-quote-content:text-color': rgba($--color-white, 0.8), + // 聊天消息回复内容边框色 + 'chat-msg-quote-content:border-color': rgba($--color-white, 0.1), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:text-color': rgba($--color-white, 0.8), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:border-color': rgba($--color-white, 0.2), + // 聊天消息回复按钮字体色 + 'chat-msg-quote-button:text-color': $--color-white, + // 聊天回复引用背景色 + 'chat-quote-msg:bg-color': #36373d, + // 聊天回复引用字体色 + 'chat-quote-msg:text-color': #adadc0, + + // 聊天消息翻译按钮字体色 + 'chat-msg-translate-button:text-color': rgba($--color-white, 0.6), + // 聊天消息翻译按钮 hover 字体色 + 'chat-msg-translate-button--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息翻译成功提示字体色 + 'chat-msg-translate-finish:text-color': rgba(#adadc0, 0.6), + // 聊天消息特殊身份翻译成功提示字体色 + 'chat-msg-translate-finish-special:text-color': rgba($--color-white, 0.6), + + // 聊天室更多消息按钮背景色 + 'chat-msg-more-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮字体色 + 'chat-msg-more-button:text-color': #ffffff, + // 聊天室更多消息按钮 hover 背景色 + 'chat-msg-more-button--hover:bg-color': #4F95FE, + // 聊天室更多消息按钮 hover 字体色 + 'chat-msg-more-button--hover:text-color': #ffffff, + + // 打赏消息字体色 + 'chat-reward-msg:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 自定义消息字体色 + 'chat-customer-msg:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 红包领取消息背景色 + 'chat-redpaper-receive-msg:bg-color': rgba($--color-black, 0.3), + // 红包领取消息字体色 + 'chat-redpaper-receive-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 系统消息字体色 + 'chat-system-msg:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 章节字体色 + 'chapter-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 章节高亮字体色 + 'chapter-item--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 章节边框色 + 'chapter-item:border-color': rgba($--color-white, 0.1), + + // 成员列表字体色 + 'member-list-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 成员列表特殊身份字体色 + 'member-list-item-special:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 点赞按钮背景图 + 'like-button:bg-image': './imgs/pws-zan.png', + // 点赞数量字体色 + 'like-count-number:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 点赞数量背景色 + 'like-count-number:bg-color': rgba(map.get($--skin-basic-configs, 'main-bg-color'), 0.8), + + // 页面广告背景色 + 'page-advert:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 页面广告字体色 + 'page-advert:text-color': $--color-white, + // 页面广告箭头字体色 + 'page-advert-arrow:text-color': #d6d4d4, + + // 互动二次入口按钮背景色 + 'interactive-entrance-button:bg-color': rgba($--color-white, 0.1), + // 互动二次入口按钮 hover 背景色 + 'interactive-entrance-button--hover:bg-color': rgba($--color-white, 0.2), + // 互动二次入口按钮字体色 + 'interactive-entrance-button:text-color': $--color-white, + // 互动二次入口箭头字体色 + 'interactive-entrance-arrow:text-color': #e4e4e4, +); diff --git a/src/skins/black/config-iar.scss b/src/skins/black/config-iar.scss new file mode 100644 index 0000000000000000000000000000000000000000..f30c8edc11fcbe2d0d3ffed2e2fa9c09910f55eb --- /dev/null +++ b/src/skins/black/config-iar.scss @@ -0,0 +1,111 @@ +@import './config-common.scss'; +@import './config-pc.scss'; + +// 皮肤互动功能公用配置 +$--skin-iar-common-configs: ( + // 挂件字体色 + 'iar-pendant:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 商品库内容背景色 + 'iar-product-list-content:bg-color': #1a1a1f, + // 商品库图标图片 + 'iar-product-list-shop:icon-image': './imgs/pws-product-icon.png', + // 商品库图标图片(职位) + 'iar-product-list-recruitment:icon-image': './imgs/pws-product-icon-recruitment.png', + // 商品库标签背景色 + 'iar-product-list-label:bg-color': rgba($--color-white, 0.1), + // 商品库标签字体色 + 'iar-product-list-label:text-color': $--color-white, + // 商品库商品名称序号背景色 + 'iar-product-list-name-number:bg-color': rgba($--color-white, 0.35), + // 商品库空状态图标图片 + 'iar-product-list-empty:icon-image': './imgs/pws-product-shop-car.png', + // 商品库商品下边框颜色 + 'iar-product-list-item:border-color': rgba($--color-white, 0.1), + // 商品库商品名称字体色 + 'iar-product-list-good-name:text-color': $--color-white, + + // 置顶公告背景色 + 'iar-bulletin-top-banner:bg-color': #366bee, + // 置顶公告字体色 + 'iar-bulletin-top-banner:text-color': $--color-white, + + // 问答背景色 + 'iar-qa-body:bg-color': #17181c, + // 问答过滤层背景色 + 'iar-qa-filter:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层边框色 + 'iar-qa-filter:border-color': #17181c, + // 问答过滤层文案字体色 + 'iar-qa-filter-desc:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 问答选择字体色 + 'iar-qa-select:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答选择箭头边框色 + 'iar-qa-select:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 问答节点背景色 + 'iar-qa-item:bg-color': #202127, + // 问答节点内容字体色 + 'iar-qa-item-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答节点信息字体色 + 'iar-qa-item-info:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 问答节点更多字体色 + 'iar-qa-item-more:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 问答节点边框色 + 'iar-qa-item:border-color': #17181c, + // 问答新消息按钮背景色 + 'iar-qa-new-msg-button:bg-color': rgba(map.get($--skin-basic-configs, 'primary-color'), 0.85), + // 问答新消息按钮字体色 + 'iar-qa-new-msg-button:text-color': $--color-white, + // 问答没有更多文案字体色 + 'iar-qa-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); + +// 皮肤互动功能 PC 配置 +$--skin-iar-pc-configs: ( + // 问答选择选项背景色 + 'iar-pc-qa-select-option:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答选择选项 hover 背景色 + 'iar-pc-qa-select-option--hover:bg-color': rgba($--color-white, 0.1), + // 问答选择选项字体色 + 'iar-pc-qa-select-option:text-color': rgba(map.get($--skin-component-configs, 'main-tab-header:text-color'), 0.8), + // 问答选择选项激活字体色 + 'iar-pc-qa-select-option--active:text-color': map.get($--skin-component-configs, 'main-tab-header:text-color'), + // 问答选择选项边框色 + 'iar-pc-qa-select-option:border-color': rgba($--color-white, 0.1), + // 问答表情选择图标字体色 + 'iar-pc-qa-ask-emotion:text-color': $--color-white, + 'iar-pc-qa-ask-emotion:background-filter': none, + + // 中奖记录弹层文字颜色 + 'iar-pc-lottery-record-msg:text-color': #e4e4e4, +); + +// 皮肤互动功能 Mobile 配置 +$--skin-iar-mobile-configs: ( + // 问答空数据的图标图片 + 'iar-mobile-qa-no-data:icon-image': './imgs/pws-qa-no-data-icon.png', + // 问答消息发送入口图片 + 'iar-mobile-qa-ask-entry:icon-image': './imgs/pws-qa-entry.png', + // 问答输入框外层背景色 + 'iar-mobile-qa-input-wrap:bg-color': #202127, + // 问答输入框背景色 + 'iar-mobile-qa-input:bg-color': #17181c, + // 问答输入框字体色 + 'iar-mobile-qa-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答表情选择图标字体色 + 'iar-mobile-qa-ask-emotion:text-color': map.get($--skin-basic-configs, 'main-text-color'), + 'iar-pc-qa-ask-emotion:background-filter': none, + // 问答发送按钮字体色 + 'iar-mobile-qa-ask-send-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 图文直播头部字体色 + 'iar-mobile-tuwen-live-header:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 图文直播名称字体色 + 'iar-mobile-tuwen-live-content-name:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 图文直播时间字体色 + 'iar-mobile-tuwen-live-content-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 图文直播内容字体色 + 'iar-mobile-tuwen-live-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 图文直播暂无更多字体色 + 'iar-mobile-tuwen-live-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); diff --git a/src/skins/black/config-mobile.scss b/src/skins/black/config-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..af8d53a3b9ef4eeb977e87a00d277246dc95ab84 --- /dev/null +++ b/src/skins/black/config-mobile.scss @@ -0,0 +1,154 @@ +@import './config-common.scss'; + +// 皮肤 Mobile 端页面配置 +$--skin-page-mobile-configs: ( + // 富文本字体色 + 'mobile-rich-text-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 引导页背景色 + 'mobile-splash:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 引导页倒计时外层背景色 + 'mobile-splash-live-count-down-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 页脚字体色 + 'mobile-page-footer:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 基础信息频道标题字体色 + 'mobile-basic-info-channel-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息开始时间字体色 + 'mobile-basic-info-start-time:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息页面浏览次数字体色 + 'mobile-basic-info-page-view:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息主持人字体色 + 'mobile-basic-info-publisher:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息点赞数字体色 + 'mobile-basic-info-like:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 倒计时方块背景色 + 'mobile-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'mobile-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'mobile-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'mobile-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'mobile-count-down-square-item:border-color': $--color-black, + + // 直播预约按钮背景色 + 'mobile-live-booking-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 关注按钮背景色 + 'mobile-follow-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 关注按钮字体颜色 + 'mobile-follow-button:text-color': $--color-white, + + // 聊天消息输入框背景色 + 'mobile-msg-input:bg-color': rgba($--color-white, 0.1), + // 聊天消息输入框字体色 + 'mobile-msg-input:text-color': $--color-white, + // 聊天消息输入框占位符字体色 + 'mobile-msg-input-placeholder:text-color': rgba($--color-white, 0.6), + // 聊天消息输入框滚动条颜色 + 'mobile-msg-input:scrollbar-color': rgba($--color-white, 0.1), + // 聊天消息输入框图标字体色 + 'mobile-msg-input-suffix-icon:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 聊天消息发送按钮字体颜色 + 'mobile-msg-send-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'mobile-emotion-panel:bg-color': #2b2c35, + // 表情选择面板下的发送按钮字体色 + 'mobile-emotion-panel-send-button:bg-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 表情选择面板下的输入框背景色 + 'mobile-emotion-panel-msg-input:bg-color': rgba($--color-white, 0.1), + // 表情选择面板下的输入框字体色 + 'mobile-emotion-panel-msg-input:text-color': $--color-white, + // 表情选择面板下的输入框占位符字体色 + 'mobile-emotion-panel-msg-input-placeholder:text-color': rgba($--color-white, 0.6), + // 表情选择面板下的输入框图标字体色 + 'mobile-emotion-panel-msg-input-suffix-icon:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 表情选择类型选择背景色 + 'mobile-emotion-panel-type:bg-color': rgba($--color-white, 0.1), + // 表情选择类型选择选中背景色 + 'mobile-emotion-panel-type--active:bg-color': rgba($--color-white, 0.2), + // 表情选择类型选择字体色 + 'mobile-emotion-panel-type:text-color': rgba($--color-white, 0.5), + // 表情选择类型选择选中字体色 + 'mobile-emotion-panel-type--active:text-color': $--color-white, + // 表情选择删除按钮背景色 + 'mobile-emotion-panel-del-button:bg-color': rgba($--color-white, 0.1), + // 表情选择删除按钮字体色 + 'mobile-emotion-panel-del-button:text-color': $--color-white, + + // 更多按钮背景色 + 'mobile-more-button:bg-color': rgba($--color-white, 0.1), + // 更多按钮图标图片 + 'mobile-more-button:icon-image': './imgs/pws-icon-more.png', + // 更多面板背景色 + 'mobile-more-panel:bg-color': #2b2c35, + // 更多面板字体色 + 'mobile-more-panel:text-color': $--color-white, + + // 打赏按钮背景色 + 'mobile-donate-button:bg-color': rgba($--color-white, 0.1), + // 打赏面板背景色 + 'mobile-donate-panel:bg-color': #2b2c35, + // 打赏面板标题字体色 + 'mobile-donate-panel-header-title:text-color': $--color-white, + // 打赏面板返回字体色 + 'mobile-donate-panel-header-close:text-color': $--color-white, + // 打赏礼物选中背景色 + 'mobile-donate-good--active:bg-color': #3e3e4e, + // 打赏礼物选中边框色 + 'mobile-donate-good--active:border-color': #adadc0, + // 打赏礼物道具名称字体色 + 'mobile-donate-good-name:text-color': $--color-white, + // 打赏礼物道具价格字体色 + 'mobile-donate-good-price:text-color': rgba($--color-white, 0.5), + // 打赏切换指示点颜色 + 'mobile-donate-indicator:bg-color': rgba($--color-white, 0.2), + // 打赏切换指示点选中颜色 + 'mobile-donate-indicator--active:bg-color': $--color-white, + // 打赏面板选项背景色 + 'mobile-donate-panel-option:bg-color': #1a1b1f, + // 打赏面板选项字体色 + 'mobile-donate-panel-option:text-color': #adadc0, + // 打赏面板选项选中背景色 + 'mobile-donate-panel-option--active:bg-color': #3e3e4e, + // 打赏面板选项选中字体色 + 'mobile-donate-panel-option--active:text-color': $--color-white, + // 打赏面板选项选中边框色 + 'mobile-donate-panel-option--active:border-color': #adadc0, + // 打赏面板发送按钮背景色 + 'mobile-donate-panel-send-button:bg-color': #ff5353, + // 打赏面板发送按钮字体色 + 'mobile-donate-panel-send-button:text-color': $--color-white, + // 打赏面板剩余积分文本字体色 + 'mobile-donate-panel-point:text-color': #e4e4e4, + // 打赏面板剩余积分数值字体色 + 'mobile-donate-panel-point-count:text-color': #ff5353, + // 打赏自定义金额字体色 + 'mobile-donate-panel-custom-cash:text-color': #adadc0, + // 打赏自定义金额输入框背景色 + 'mobile-donate-custom-cash-input:bg-color': rgba($--color-white, 0.1), + // 打赏自定义金额输入框标题字体色 + 'mobile-donate-custom-cash-input-title:text-color': rgba($--color-white, 0.6), + // 打赏自定义金额输入框字体色 + 'mobile-donate-custom-cash-input:text-color': $--color-white, + // 打赏自定义金额随机按钮字体色 + 'mobile-donate-custom-cash-random:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 回放列表播放中按钮背景色 + 'mobile-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 回放列表字体色 + 'mobile-playback-list:text-color': rgba($--color-white, 0.7), + // 回放列表选中时的字体色 + 'mobile-playback-list--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 多会场背景色 + 'mobile-multi-meeting:bg-color': #191a1f, + // 多会场字体色 + 'mobile-multi-meeting:text-color': $--color-white, +); diff --git a/src/skins/black/config-pc.scss b/src/skins/black/config-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8f0458b56558c63785962419b8188642b928fa25 --- /dev/null +++ b/src/skins/black/config-pc.scss @@ -0,0 +1,110 @@ +@import './config-common.scss'; + +// 皮肤 PC 端页面配置 +$--skin-page-pc-configs: ( + // 观看页背景类型 + 'pc-watch-page:bg-type': 'color', + // 观看页背景颜色 + 'pc-watch-page:bg-color': #141518, + // 观看页背景图 + 'pc-watch-page:bg-image': '', + + // 副屏收起字体颜色 + 'pc-sub-pack-up:text-color': $--color-white, + + // 倒计时方块背景色 + 'pc-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'pc-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'pc-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'pc-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'pc-count-down-square-item:border-color': $--color-black, + + // 聊天消息底部输入框背景色 + 'pc-msg-bottom-input-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 聊天消息底部输入框节点字体色 + 'pc-msg-bottom-input-wrap-item:text-color': #e4e4e4, + // 聊天消息底部输入框节点 hover 字体色 + 'pc-msg-bottom-input-wrap-item--hover:text-color': map.get($--skin-basic-configs, 'sub-primary-color'), + + // 聊天消息输入框背景色 + 'pc-msg-input:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天消息输入框字体色 + 'pc-msg-input:text-color': #e4e4e4, + // 聊天消息输入框占位符字体色 + 'pc-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'pc-msg-input:scrollbar-color': #46464F, + + // 聊天消息发送按钮背景色 + 'pc-msg-send-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天消息发送按钮禁用状态下的背景色 + 'pc-msg-send-button--disabled:bg-color': #2b2c35, + // 聊天消息发送按钮字体颜色 + 'pc-msg-send-button:text-color': $--color-white, + // 聊天消息发送按钮禁用状态下的字体颜色 + 'pc-msg-send-button--disabled:text-color': rgba($--color-white, 0.5), + + // 图片达到最大数量后的提示框背景色 + 'pws-pc-msg-input-popper__max-image-tips:bg-color': rgb(47, 130, 254), + // 图片达到最大数量后的提示框字体色 + 'pws-pc-msg-input-popper__max-image-tips:text-color': $--color-white, + + // 聊天输入框设置昵称占位背景色 + 'pc-set-nick-placeholder:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天输入框设置昵称占位字体色 + 'pc-set-nick-placeholder:text-color': $--color-white, + // 聊天输入框设置昵称占位高亮字体色 + 'pc-set-nick-placeholder-highlight:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'pc-emotion-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 表情选择面板类型选择背景色 + 'pc-emotion-panel-select:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 更多面板背景色 + 'pc-more-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 更多面板字体色 + 'pc-more-panel:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 更多面板字体高亮色 + 'pc-more-panel-text--hover:text-color': map.get($--skin-basic-configs, 'panel-highlight-text-color'), + // 更多面板选中框背景色 + 'pc-more-panel-checkbox--checked:bg-color': $--color-white, + // 更多面板选中框图标色 + 'pc-more-panel-checkbox-icon--checked:text-color': map.get($--skin-basic-configs, 'panel-bg-color'), + + // 打赏面板背景色 + 'pc-donate-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 打赏礼物 hover 背景色 + 'pc-donate-good--hover:bg-color': rgba($--color-white, 0.1), + // 打赏礼物 hover 边框色 + 'pc-donate-good--hover:border-color': rgba($--color-white, 0.4), + // 打赏礼物道具名称字体色 + 'pc-donate-good-name:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 打赏礼物道具价格字体色 + 'pc-donate-good-price:text-color': rgba($--color-white, 0.5), + + // 中奖记录面板背景色 + 'pc-lottery-record-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 中奖记录面板关闭图标字体色 + 'pc-lottery-record-close:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + + // 连麦描述图标字体色 + 'pc-connect-mic-desc-icon:text-color': #3e3e4f, + // 连麦描述文案字体色 + 'pc-connect-mic-desc:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦按钮背景色 + 'pc-connect-mic-button:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 连麦按钮字体色 + 'pc-connect-mic-button:text-color': $--color-white, + // 连麦按钮高亮字体色 + 'pc-connect-mic-button-highlight:text-color': $--color-white, + // 连麦中图标背景图 + 'pc-connect-mic-applying:icon-image': './imgs/pws-pc-connect-mic.gif', + + // 回放列表播放中按钮背景色 + 'pc-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), +); diff --git a/src/skins/black/config.scss b/src/skins/black/config.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2135df8fc7d53e586d0667c1e22586bb313eae2 --- /dev/null +++ b/src/skins/black/config.scss @@ -0,0 +1,7 @@ +@import '../_common/functions.scss'; +@import './config-common.scss'; +@import './config-pc.scss'; +@import './config-mobile.scss'; +@import './config-iar.scss'; + +$--skin-configs: generate-skin-setup-configs(); diff --git a/src/skins/black/imgs/pws-icon-more.png b/src/skins/black/imgs/pws-icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..52b15f454c14a10fb8ddce948263649053987419 Binary files /dev/null and b/src/skins/black/imgs/pws-icon-more.png differ diff --git a/src/skins/black/imgs/pws-pc-connect-mic.gif b/src/skins/black/imgs/pws-pc-connect-mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..5de709033a2c75ddd91af7ad22c9838a3d2ecb40 Binary files /dev/null and b/src/skins/black/imgs/pws-pc-connect-mic.gif differ diff --git a/src/skins/black/imgs/pws-pc-watch-bg.png b/src/skins/black/imgs/pws-pc-watch-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..caf427e0e60f478c3d7d89c086930eb26ef661ab Binary files /dev/null and b/src/skins/black/imgs/pws-pc-watch-bg.png differ diff --git a/src/skins/black/imgs/pws-product-icon-recruitment.png b/src/skins/black/imgs/pws-product-icon-recruitment.png new file mode 100644 index 0000000000000000000000000000000000000000..f5950ab2bcd011fe8c031ec6d0c45ef801ff228c Binary files /dev/null and b/src/skins/black/imgs/pws-product-icon-recruitment.png differ diff --git a/src/skins/black/imgs/pws-product-icon.png b/src/skins/black/imgs/pws-product-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1afa080773f435fdb911ed63a2339956ab362fe5 Binary files /dev/null and b/src/skins/black/imgs/pws-product-icon.png differ diff --git a/src/skins/black/imgs/pws-product-shop-car.png b/src/skins/black/imgs/pws-product-shop-car.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb64ea1f3f8cac9b972a4972d0075f7d2963b7e Binary files /dev/null and b/src/skins/black/imgs/pws-product-shop-car.png differ diff --git a/src/skins/black/imgs/pws-qa-entry.png b/src/skins/black/imgs/pws-qa-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..933e25877fdd5a051444673837780da38f0b4dfd Binary files /dev/null and b/src/skins/black/imgs/pws-qa-entry.png differ diff --git a/src/skins/black/imgs/pws-qa-no-data-icon.png b/src/skins/black/imgs/pws-qa-no-data-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8cf9581b90ed3db35e8d50a02908e74bc07e099f Binary files /dev/null and b/src/skins/black/imgs/pws-qa-no-data-icon.png differ diff --git a/src/skins/black/imgs/pws-zan.png b/src/skins/black/imgs/pws-zan.png new file mode 100644 index 0000000000000000000000000000000000000000..1452c94d0d8967f552dfef74353a7fd6d4ed10fe Binary files /dev/null and b/src/skins/black/imgs/pws-zan.png differ diff --git a/src/skins/blue/_blue-mobile.scss b/src/skins/blue/_blue-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..87c09f945a1836f57dd951bb4b17cc5963a48285 --- /dev/null +++ b/src/skins/blue/_blue-mobile.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-mobile-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-mobile-style($--skin-configs); diff --git a/src/skins/blue/_blue-pc.scss b/src/skins/blue/_blue-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a13764923fe827d4013ff68666fb5afd7f37d7d --- /dev/null +++ b/src/skins/blue/_blue-pc.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-pc-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-pc-style($--skin-configs); diff --git a/src/skins/blue/config-common.scss b/src/skins/blue/config-common.scss new file mode 100644 index 0000000000000000000000000000000000000000..17451a947e735e9aeb8f4a735a03469c99f9b30a --- /dev/null +++ b/src/skins/blue/config-common.scss @@ -0,0 +1,244 @@ +// 主题名称 +$--skin-type: 'blue'; + +// 主题色 +$--skin-primary-color: #1571d0; +// 次要主题色 +$--skin-sub-primary-color: #269eff; + +// 皮肤基础配置 +$--skin-basic-configs: ( + // 主题名称 + 'skin-type': $--skin-type, + + // 主题色 + 'primary-color': $--skin-primary-color, + // 次要主题色 + 'sub-primary-color': $--skin-sub-primary-color, + // #2c7fd5 + 'primary-light-1': get-opacity-color($--skin-primary-color, 1), + // #448dd9 + 'primary-light-2': get-opacity-color($--skin-primary-color, 2), + // #5b9cde + 'primary-light-3': get-opacity-color($--skin-primary-color, 3), + // #73aae3 + 'primary-light-4': get-opacity-color($--skin-primary-color, 4), + // #8ab8e8 + 'primary-light-5': get-opacity-color($--skin-primary-color, 5), + // #a1c6ec + 'primary-light-6': get-opacity-color($--skin-primary-color, 6), + // #b9d4f1 + 'primary-light-7': get-opacity-color($--skin-primary-color, 7), + // #d0e3f6 + 'primary-light-8': get-opacity-color($--skin-primary-color, 8), + // #e8f1fa + 'primary-light-9': get-opacity-color($--skin-primary-color, 9), + + // ---------- 背景色 ---------- // + // 主要背景色(以侧边栏主体背景色定义) + 'main-bg-color': #bfddff, + // 次要背景色(以侧边栏头部背景色定义) + 'sub-bg-color': #1571d0, + // 面板背景色 + 'panel-bg-color': #1362b4, + // 禁用背景色 + 'disabled-bg-color': #e9ecee, + + // ---------- 字体色 ---------- // + // 正文字体色 + 'main-text-color': #1b3673, + // 正文高亮字体色 + 'main-highlight-text-color': $--skin-primary-color, + // 描述字体色 + 'describe-text-color': rgba(#1b3673, 0.5), + // 禁用字体色 + 'disabled-text-color': #9e9e9e, + // 面板字体色 + 'panel-text-color': rgba($--color-white, 0.8), + // 面板高亮字体色 + 'panel-highlight-text-color': $--color-white, + + // ---------- 边框色 ---------- // + 'main-border-color': rgba(#1b3673, 0.1), + + // ---------- 滚动条 ---------- // + 'panel-scrollbar-color': rgba($--color-white, 0.1), +); + +// 皮肤基础组件配置 +$--skin-component-configs: ( + // 主要 tab 头部背景色 + 'main-tab-header:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部字体色 + 'main-tab-header:text-color': $--color-white, + // 主要 tab 头部 hover 字体色 + 'main-tab-header--hover:text-color': $--color-white, + // 主要 tab 头部激活字体色 + 'main-tab-header--active:text-color': $--color-white, + // 主要 tab 头部激活选项线条背景色 + 'main-tab-header-line--active:bg-color': $--color-white, + // 主要 tab 头部阴影开始颜色 + 'main-tab-header-shadow-start:color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部阴影结束颜色 + 'main-tab-header-shadow-end:color': rgba(62, 62, 78, 0), + // 主要 tab 头部红点颜色 + 'main-tab-header-reminder:color': #f24453, + // 主要 tab 主体背景色 + 'main-tab-body:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 主要 tab 主体滚动栏颜色 + 'main-tab-body:scrollbar-color': rgba(map.get($--skin-basic-configs, 'primary-color'), 0.2), + + // 常规 tab 激活项线条背景色 + 'normal-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 主题按钮背景色 + 'primary-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主题按钮字体色 + 'primary-button:text-color': $--color-white, + // 主题按钮 hover 背景色 + 'primary-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-light-2'), + // 主题按钮 hover 字体色 + 'primary-button--hover:text-color': $--color-white, + // 主题按钮 active 背景色 + 'primary-button--active:bg-color': map.get($--skin-basic-configs, 'primary-light-1'), + // 主题按钮 active 字体色 + 'primary-button--active:text-color': $--color-white, + // 主题按钮 disabled 背景色 + 'primary-button--disabled:bg-color': map.get($--skin-basic-configs, 'disabled-bg-color'), + // 主题按钮 disabled 字体色 + 'primary-button--disabled:text-color': map.get($--skin-basic-configs, 'disabled-text-color'), + + // 侧边栏按钮背景色 + 'aside-menu-button:bg-color': rgba($--color-white, 0.6), + // 侧边栏按钮字体则 + 'aside-menu-button:text-color': #333333, + // 侧边栏按钮 hover 背景色 + 'aside-menu-button--hover:bg-color': rgba($--color-white, 0.8), + // 侧边栏按钮 active 背景色 + 'aside-menu-button--active:bg-color': rgba($--color-white, 0.7), + + // pc 端直播状态标签颜色 + 'pc-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #269eff, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #269eff, + 'stop' #f06e6e, + ), + // mobile 端直播状态标签颜色 + 'mobile-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #269eff, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #269eff, + 'stop' #f06e6e, + ), +); + +// 皮肤公用页面配置 +$--skin-page-common-configs: ( + // 聊天消息昵称字体色 + 'chat-msg-user-nickname:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份昵称字体色 + 'chat-msg-user-special-nickname:text-color': #2d85e2, + // 聊天消息用户头衔背景色 + 'chat-msg-user-actor:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天消息用户头衔字体色 + 'chat-msg-user-actor:text-color': #bfddff, + // 聊天消息用户设置昵称图标字体色 + 'chat-msg-user-set-nick-icon:text-color': #1362b4, + // 聊天消息时间字体色 + 'chat-msg-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 聊天提示文案字体色 + 'chat-msg-list-tips:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 聊天消息气泡背景色 + 'chat-msg-bubble:bg-color': #dfeeff, + // 聊天消息气泡字体色 + 'chat-msg-bubble:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份气泡背景色 + 'chat-msg-bubble-special:bg-color': #4092e7, + // 聊天消息特殊身份气泡字体色 + 'chat-msg-bubble-special:text-color': $--color-white, + + // 聊天消息回复内容字体色 + 'chat-msg-quote-content:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息回复内容边框色 + 'chat-msg-quote-content:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:text-color': rgba($--color-white, 0.8), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:border-color': rgba($--color-white, 0.1), + // 聊天消息回复按钮字体色 + 'chat-msg-quote-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天回复引用背景色 + 'chat-quote-msg:bg-color': #cbe3ff, + // 聊天回复引用字体色 + 'chat-quote-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 聊天消息翻译按钮字体色 + 'chat-msg-translate-button:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 聊天消息翻译按钮 hover 字体色 + 'chat-msg-translate-button--hover:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息翻译成功提示字体色 + 'chat-msg-translate-finish:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 聊天消息特殊身份翻译成功提示字体色 + 'chat-msg-translate-finish-special:text-color': rgba($--color-white, 0.8), + + // 聊天室更多消息按钮背景色 + 'chat-msg-more-button:bg-color': #A9D2FF, + // 聊天室更多消息按钮字体色 + 'chat-msg-more-button:text-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮 hover 背景色 + 'chat-msg-more-button--hover:bg-color': #A9D2FF, + // 聊天室更多消息按钮 hover 字体色 + 'chat-msg-more-button--hover:text-color': map.get($--skin-basic-configs, 'primary-color'), + + // 打赏消息字体色 + 'chat-reward-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 自定义消息字体色 + 'chat-customer-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 红包领取消息背景色 + 'chat-redpaper-receive-msg:bg-color': #a9d2ff, + // 红包领取消息字体色 + 'chat-redpaper-receive-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 系统消息字体色 + 'chat-system-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 章节字体色 + 'chapter-item:text-color': rgba($--color-black, 0.35), + // 章节高亮字体色 + 'chapter-item--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 章节边框色 + 'chapter-item:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + + // 成员列表字体色 + 'member-list-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 成员列表特殊身份字体色 + 'member-list-item-special:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 点赞按钮背景图 + 'like-button:bg-image': './imgs/pws-zan.png', + // 点赞数量字体色 + 'like-count-number:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 点赞数量背景色 + 'like-count-number:bg-color': rgba(map.get($--skin-basic-configs, 'main-bg-color'), 0.8), + + // 页面广告背景色 + 'page-advert:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 页面广告字体色 + 'page-advert:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 页面广告箭头字体色 + 'page-advert-arrow:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 互动二次入口按钮背景色 + 'interactive-entrance-button:bg-color': rgba($--color-white, 0.4), + // 互动二次入口按钮 hover 背景色 + 'interactive-entrance-button--hover:bg-color': rgba($--color-white, 0.3), + // 互动二次入口按钮字体色 + 'interactive-entrance-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 互动二次入口箭头字体色 + 'interactive-entrance-arrow:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.4), +); diff --git a/src/skins/blue/config-iar.scss b/src/skins/blue/config-iar.scss new file mode 100644 index 0000000000000000000000000000000000000000..50e5cc0e6889e8d3ed7350976ecde8898960c275 --- /dev/null +++ b/src/skins/blue/config-iar.scss @@ -0,0 +1,111 @@ +@import './config-common.scss'; +@import './config-pc.scss'; + +// 皮肤互动功能公用配置 +$--skin-iar-common-configs: ( + // 挂件字体色 + 'iar-pendant:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 商品库内容背景色 + 'iar-product-list-content:bg-color': #e5f1ff, + // 商品库图标图片 + 'iar-product-list-shop:icon-image': './imgs/pws-product-icon.png', + // 商品库图标图片(职位) + 'iar-product-list-recruitment:icon-image': './imgs/pws-product-icon-recruitment.png', + // 商品库标签背景色 + 'iar-product-list-label:bg-color': rgba(#333333, 0.1), + // 商品库标签字体色 + 'iar-product-list-label:text-color': #333333, + // 商品库商品名称序号背景色 + 'iar-product-list-name-number:bg-color': rgba($--color-black, 0.35), + // 商品库空状态图标图片 + 'iar-product-list-empty:icon-image': './imgs/pws-product-shop-car.png', + // 商品库商品下边框颜色 + 'iar-product-list-item:border-color': rgba(#666666, 0.1), + // 商品库商品名称字体色 + 'iar-product-list-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 置顶公告背景色 + 'iar-bulletin-top-banner:bg-color': #dfeeff, + // 置顶公告字体色 + 'iar-bulletin-top-banner:text-color': #1b3673, + + // 问答背景色 + 'iar-qa-body:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层背景色 + 'iar-qa-filter:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层边框色 + 'iar-qa-filter:border-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层文案字体色 + 'iar-qa-filter-desc:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 问答选择字体色 + 'iar-qa-select:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答选择箭头边框色 + 'iar-qa-select:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 问答节点背景色 + 'iar-qa-item:bg-color': #D2E7FF, + // 问答节点内容字体色 + 'iar-qa-item-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答节点信息字体色 + 'iar-qa-item-info:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 问答节点更多字体色 + 'iar-qa-item-more:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 问答节点边框色 + 'iar-qa-item:border-color': #BFDDFF, + // 问答新消息按钮背景色 + 'iar-qa-new-msg-button:bg-color': rgba(169, 210, 255, 0.85) , + // 问答新消息按钮字体色 + 'iar-qa-new-msg-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答没有更多文案字体色 + 'iar-qa-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); + +// 皮肤互动功能 PC 配置 +$--skin-iar-pc-configs: ( + // 问答选择选项背景色 + 'iar-pc-qa-select-option:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答选择选项 hover 背景色 + 'iar-pc-qa-select-option--hover:bg-color': rgba($--color-white, 0.1), + // 问答选择选项字体色 + 'iar-pc-qa-select-option:text-color': rgba(map.get($--skin-component-configs, 'main-tab-header:text-color'), 0.8), + // 问答选择选项激活字体色 + 'iar-pc-qa-select-option--active:text-color': map.get($--skin-component-configs, 'main-tab-header:text-color'), + // 问答选择选项边框色 + 'iar-pc-qa-select-option:border-color': rgba($--color-white, 0.1), + // 问答表情选择图标 + 'iar-pc-qa-ask-emotion:text-color': map.get($--skin-basic-configs, 'main-text-color'), + 'iar-pc-qa-ask-emotion:background-filter': grayscale(0%) brightness(150%), + + // 中奖记录弹层文字颜色 + 'iar-pc-lottery-record-msg:text-color': #e4e4e4, +); + +// 皮肤互动功能 Mobile 配置 +$--skin-iar-mobile-configs: ( + // 问答空数据的图标图片 + 'iar-mobile-qa-no-data:icon-image': './imgs/pws-qa-no-data-icon.png', + // 问答消息发送入口图片 + 'iar-mobile-qa-ask-entry:icon-image': './imgs/pws-qa-entry.png', + // 问答输入框外层背景色 + 'iar-mobile-qa-input-wrap:bg-color': #1571D0, + // 问答输入框背景色 + 'iar-mobile-qa-input:bg-color': #D5E7FB, + // 问答输入框字体色 + 'iar-mobile-qa-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答表情选择图标字体色 + 'iar-mobile-qa-ask-emotion:text-color': $--color-white, + 'iar-mobile-qa-ask-emotion:background-filter': grayscale(100%) brightness(200%), + // 问答发送按钮字体色 + 'iar-mobile-qa-ask-send-button:text-color': $--color-white, + + // 图文直播头部字体色 + 'iar-mobile-tuwen-live-header:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 图文直播名称字体色 + 'iar-mobile-tuwen-live-content-name:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 图文直播时间字体色 + 'iar-mobile-tuwen-live-content-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 图文直播内容字体色 + 'iar-mobile-tuwen-live-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 图文直播暂无更多字体色 + 'iar-mobile-tuwen-live-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); diff --git a/src/skins/blue/config-mobile.scss b/src/skins/blue/config-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..42008db08a33879e7ba9cac62433f87eecf85cab --- /dev/null +++ b/src/skins/blue/config-mobile.scss @@ -0,0 +1,154 @@ +@import './config-common.scss'; + +// 皮肤 Mobile 端页面配置 +$--skin-page-mobile-configs: ( + // 富文本字体色 + 'mobile-rich-text-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 引导页背景色 + 'mobile-splash:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 引导页倒计时外层背景色 + 'mobile-splash-live-count-down-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 页脚字体色 + 'mobile-page-footer:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 基础信息频道标题字体色 + 'mobile-basic-info-channel-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息开始时间字体色 + 'mobile-basic-info-start-time:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息页面浏览次数字体色 + 'mobile-basic-info-page-view:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息主持人字体色 + 'mobile-basic-info-publisher:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息点赞数字体色 + 'mobile-basic-info-like:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 倒计时方块背景色 + 'mobile-count-down-square-item:bg-color': #004285, + // 倒计时方块远点背景色 + 'mobile-count-down-square-item-dot:bg-color': #004285, + // 倒计时方块数字字体色 + 'mobile-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'mobile-count-down-square-item-word:text-color': rgba($--color-white, 0.7), + // 倒计时方块边框色 + 'mobile-count-down-square-item:border-color': #004A95, + + // 直播预约按钮背景色 + 'mobile-live-booking-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + + // 关注按钮背景色 + 'mobile-follow-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 关注按钮字体颜色 + 'mobile-follow-button:text-color': $--color-white, + + // 聊天消息输入框背景色 + 'mobile-msg-input:bg-color': rgba($--color-white, 0.6), + // 聊天消息输入框字体色 + 'mobile-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'mobile-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'mobile-msg-input:scrollbar-color': rgba(21, 113, 208, 0.2), + // 聊天消息输入框图标字体色 + 'mobile-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 聊天消息发送按钮字体颜色 + 'mobile-msg-send-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'mobile-emotion-panel:bg-color': #1362B4, + // 表情选择面板下的发送按钮字体色 + 'mobile-emotion-panel-send-button:bg-color': $--color-white, + // 表情选择面板下的输入框背景色 + 'mobile-emotion-panel-msg-input:bg-color': rgba($--color-white, 0.1), + // 表情选择面板下的输入框字体色 + 'mobile-emotion-panel-msg-input:text-color': $--color-white, + // 表情选择面板下的输入框占位符字体色 + 'mobile-emotion-panel-msg-input-placeholder:text-color': rgba($--color-white, 0.6), + // 表情选择面板下的输入框图标字体色 + 'mobile-emotion-panel-msg-input-suffix-icon:text-color': rgba($--color-white, 0.7), + // 表情选择类型选择背景色 + 'mobile-emotion-panel-type:bg-color': rgba($--color-white, 0.1), + // 表情选择类型选择选中背景色 + 'mobile-emotion-panel-type--active:bg-color': rgba($--color-white, 0.2), + // 表情选择类型选择字体色 + 'mobile-emotion-panel-type:text-color': rgba($--color-white, 0.5), + // 表情选择类型选择选中字体色 + 'mobile-emotion-panel-type--active:text-color': $--color-white, + // 表情选择删除按钮背景色 + 'mobile-emotion-panel-del-button:bg-color': rgba($--color-white, 0.1), + // 表情选择删除按钮字体色 + 'mobile-emotion-panel-del-button:text-color': $--color-white, + + // 更多按钮背景色 + 'mobile-more-button:bg-color': rgba($--color-white, 0.6), + // 更多按钮图标图片 + 'mobile-more-button:icon-image': './imgs/pws-icon-more.png', + // 更多面板背景色 + 'mobile-more-panel:bg-color': #1362B4, + // 更多面板字体色 + 'mobile-more-panel:text-color': $--color-white, + + // 打赏按钮背景色 + 'mobile-donate-button:bg-color': rgba($--color-white, 0.6), + // 打赏面板背景色 + 'mobile-donate-panel:bg-color': #1362B4, + // 打赏面板标题字体色 + 'mobile-donate-panel-header-title:text-color': $--color-white, + // 打赏面板返回字体色 + 'mobile-donate-panel-header-close:text-color': $--color-white, + // 打赏礼物选中背景色 + 'mobile-donate-good--active:bg-color': #1571D0, + // 打赏礼物选中边框色 + 'mobile-donate-good--active:border-color': rgba($--color-white, 0.6), + // 打赏礼物道具名称字体色 + 'mobile-donate-good-name:text-color': $--color-white, + // 打赏礼物道具价格字体色 + 'mobile-donate-good-price:text-color': rgba($--color-white, 0.5), + // 打赏切换指示点颜色 + 'mobile-donate-indicator:bg-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 打赏切换指示点选中颜色 + 'mobile-donate-indicator--active:bg-color': #127EF0, + // 打赏面板选项背景色 + 'mobile-donate-panel-option:bg-color': #1571D0, + // 打赏面板选项字体色 + 'mobile-donate-panel-option:text-color': $--color-white, + // 打赏面板选项选中背景色 + 'mobile-donate-panel-option--active:bg-color': #1571D0, + // 打赏面板选项选中字体色 + 'mobile-donate-panel-option--active:text-color': $--color-white, + // 打赏面板选项选中边框色 + 'mobile-donate-panel-option--active:border-color': rgba($--color-white, 0.6), + // 打赏面板发送按钮背景色 + 'mobile-donate-panel-send-button:bg-color': #269EFF, + // 打赏面板发送按钮字体色 + 'mobile-donate-panel-send-button:text-color': $--color-white, + // 打赏面板剩余积分文本字体色 + 'mobile-donate-panel-point:text-color': $--color-white, + // 打赏面板剩余积分数值字体色 + 'mobile-donate-panel-point-count:text-color': $--color-white, + // 打赏自定义金额字体色 + 'mobile-donate-panel-custom-cash:text-color': rgba($--color-white, 0.7), + // 打赏自定义金额输入框背景色 + 'mobile-donate-custom-cash-input:bg-color': rgba($--color-white, 0.1), + // 打赏自定义金额输入框标题字体色 + 'mobile-donate-custom-cash-input-title:text-color': rgba($--color-white, 0.7), + // 打赏自定义金额输入框字体色 + 'mobile-donate-custom-cash-input:text-color': $--color-white, + // 打赏自定义金额随机按钮字体色 + 'mobile-donate-custom-cash-random:text-color': $--color-white, + + // 回放列表播放中按钮背景色 + 'mobile-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 回放列表字体色 + 'mobile-playback-list:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 回放列表选中时的字体色 + 'mobile-playback-list--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 多会场背景色 + 'mobile-multi-meeting:bg-color': #1362B4, + // 多会场字体色 + 'mobile-multi-meeting:text-color': $--color-white, +); diff --git a/src/skins/blue/config-pc.scss b/src/skins/blue/config-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..3e460bf998530d45fba801871be519052a3a0e75 --- /dev/null +++ b/src/skins/blue/config-pc.scss @@ -0,0 +1,110 @@ +@import './config-common.scss'; + +// 皮肤 PC 端页面配置 +$--skin-page-pc-configs: ( + // 观看页背景类型 + 'pc-watch-page:bg-type': 'image', + // 观看页背景颜色 + 'pc-watch-page:bg-color': '', + // 观看页背景图 + 'pc-watch-page:bg-image': './imgs/pws-pc-watch-bg.png', + + // 副屏收起字体颜色 + 'pc-sub-pack-up:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 倒计时方块背景色 + 'pc-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'pc-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'pc-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'pc-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'pc-count-down-square-item:border-color': $--color-black, + + // 聊天消息底部输入框背景色 + 'pc-msg-bottom-input-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 聊天消息底部输入框节点字体色 + 'pc-msg-bottom-input-wrap-item:text-color': $--color-white, + // 聊天消息底部输入框节点 hover 字体色 + 'pc-msg-bottom-input-wrap-item--hover:text-color': #bfddff, + + // 聊天消息输入框背景色 + 'pc-msg-input:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天消息输入框字体色 + 'pc-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'pc-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'pc-msg-input:scrollbar-color': rgba(21, 113, 208, 0.2), + + // 聊天消息发送按钮背景色 + 'pc-msg-send-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息发送按钮禁用状态下的背景色 + 'pc-msg-send-button--disabled:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息发送按钮字体颜色 + 'pc-msg-send-button:text-color': $--color-white, + // 聊天消息发送按钮禁用状态下的字体颜色 + 'pc-msg-send-button--disabled:text-color': rgba($--color-white, 0.5), + + // 图片达到最大数量后的提示框背景色 + 'pws-pc-msg-input-popper__max-image-tips:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 图片达到最大数量后的提示框字体色 + 'pws-pc-msg-input-popper__max-image-tips:text-color': $--color-white, + + // 聊天输入框设置昵称占位背景色 + 'pc-set-nick-placeholder:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天输入框设置昵称占位字体色 + 'pc-set-nick-placeholder:text-color': $--color-white, + // 聊天输入框设置昵称占位高亮字体色 + 'pc-set-nick-placeholder-highlight:text-color': map.get($--skin-basic-configs, 'primary-color'), + + // 表情选择面板背景色 + 'pc-emotion-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 表情选择面板类型选择背景色 + 'pc-emotion-panel-select:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 更多面板背景色 + 'pc-more-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 更多面板字体色 + 'pc-more-panel:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 更多面板字体高亮色 + 'pc-more-panel-text--hover:text-color': map.get($--skin-basic-configs, 'panel-highlight-text-color'), + // 更多面板选中框背景色 + 'pc-more-panel-checkbox--checked:bg-color': $--color-white, + // 更多面板选中框图标色 + 'pc-more-panel-checkbox-icon--checked:text-color': map.get($--skin-basic-configs, 'panel-bg-color'), + + // 打赏面板背景色 + 'pc-donate-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 打赏礼物 hover 背景色 + 'pc-donate-good--hover:bg-color': rgba($--color-white, 0.1), + // 打赏礼物 hover 边框色 + 'pc-donate-good--hover:border-color': rgba($--color-white, 0.4), + // 打赏礼物道具名称字体色 + 'pc-donate-good-name:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 打赏礼物道具价格字体色 + 'pc-donate-good-price:text-color': rgba($--color-white, 0.5), + + // 中奖记录面板背景色 + 'pc-lottery-record-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 中奖记录面板关闭图标字体色 + 'pc-lottery-record-close:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + + // 连麦描述图标字体色 + 'pc-connect-mic-desc-icon:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦描述文案字体色 + 'pc-connect-mic-desc:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦按钮背景色 + 'pc-connect-mic-button:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 连麦按钮字体色 + 'pc-connect-mic-button:text-color': $--color-white, + // 连麦按钮高亮字体色 + 'pc-connect-mic-button-highlight:text-color': #ff5b5b, + // 连麦中图标背景图 + 'pc-connect-mic-applying:icon-image': './imgs/pws-pc-connect-mic.gif', + + // 回放列表播放中按钮背景色 + 'pc-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), +); diff --git a/src/skins/blue/config.scss b/src/skins/blue/config.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2135df8fc7d53e586d0667c1e22586bb313eae2 --- /dev/null +++ b/src/skins/blue/config.scss @@ -0,0 +1,7 @@ +@import '../_common/functions.scss'; +@import './config-common.scss'; +@import './config-pc.scss'; +@import './config-mobile.scss'; +@import './config-iar.scss'; + +$--skin-configs: generate-skin-setup-configs(); diff --git a/src/skins/blue/imgs/pws-icon-more.png b/src/skins/blue/imgs/pws-icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..0233f9635c6494aaa9fb152a993b3695267b564f Binary files /dev/null and b/src/skins/blue/imgs/pws-icon-more.png differ diff --git a/src/skins/blue/imgs/pws-pc-connect-mic.gif b/src/skins/blue/imgs/pws-pc-connect-mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..34a5ce6d1b13a0db1621ce12606c72f417b0cc90 Binary files /dev/null and b/src/skins/blue/imgs/pws-pc-connect-mic.gif differ diff --git a/src/skins/blue/imgs/pws-pc-watch-bg.png b/src/skins/blue/imgs/pws-pc-watch-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..290b12cd91b578039ffedb6d0cf5d7bf00527f94 Binary files /dev/null and b/src/skins/blue/imgs/pws-pc-watch-bg.png differ diff --git a/src/skins/blue/imgs/pws-product-icon-recruitment.png b/src/skins/blue/imgs/pws-product-icon-recruitment.png new file mode 100644 index 0000000000000000000000000000000000000000..36b4f6982dadea3018913e370ae05b39fba482a0 Binary files /dev/null and b/src/skins/blue/imgs/pws-product-icon-recruitment.png differ diff --git a/src/skins/blue/imgs/pws-product-icon.png b/src/skins/blue/imgs/pws-product-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b7169c431973b8a52ceefdd8f3f93af387f9645b Binary files /dev/null and b/src/skins/blue/imgs/pws-product-icon.png differ diff --git a/src/skins/blue/imgs/pws-product-shop-car.png b/src/skins/blue/imgs/pws-product-shop-car.png new file mode 100644 index 0000000000000000000000000000000000000000..55e4c53664b9b4eeec88398a9a40b3a5964bc248 Binary files /dev/null and b/src/skins/blue/imgs/pws-product-shop-car.png differ diff --git a/src/skins/blue/imgs/pws-qa-entry.png b/src/skins/blue/imgs/pws-qa-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..933e25877fdd5a051444673837780da38f0b4dfd Binary files /dev/null and b/src/skins/blue/imgs/pws-qa-entry.png differ diff --git a/src/skins/blue/imgs/pws-qa-no-data-icon.png b/src/skins/blue/imgs/pws-qa-no-data-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..772d92d4cf35a99dd79ffaac0abfb958d0ad2a99 Binary files /dev/null and b/src/skins/blue/imgs/pws-qa-no-data-icon.png differ diff --git a/src/skins/blue/imgs/pws-zan.png b/src/skins/blue/imgs/pws-zan.png new file mode 100644 index 0000000000000000000000000000000000000000..3b78fda0dbdfa7a7e9b6a0378a6676f2d5816f8f Binary files /dev/null and b/src/skins/blue/imgs/pws-zan.png differ diff --git a/src/skins/golden/_golden-mobile.scss b/src/skins/golden/_golden-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..87c09f945a1836f57dd951bb4b17cc5963a48285 --- /dev/null +++ b/src/skins/golden/_golden-mobile.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-mobile-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-mobile-style($--skin-configs); diff --git a/src/skins/golden/_golden-pc.scss b/src/skins/golden/_golden-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a13764923fe827d4013ff68666fb5afd7f37d7d --- /dev/null +++ b/src/skins/golden/_golden-pc.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-pc-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-pc-style($--skin-configs); diff --git a/src/skins/golden/config-common.scss b/src/skins/golden/config-common.scss new file mode 100644 index 0000000000000000000000000000000000000000..4b89b84cd597f892b583b6617ebc9d3a41a0f10f --- /dev/null +++ b/src/skins/golden/config-common.scss @@ -0,0 +1,244 @@ +// 主题名称 +$--skin-type: 'golden'; + +// 主题色 +$--skin-primary-color: #CEAE67; +// 次要主题色 +$--skin-sub-primary-color: #FFE4A5; + +// 皮肤基础配置 +$--skin-basic-configs: ( + // 主题名称 + 'skin-type': $--skin-type, + + // 主题色 + 'primary-color': $--skin-primary-color, + // 次要主题色 + 'sub-primary-color': $--skin-sub-primary-color, + // #d3b676 + 'primary-light-1': get-opacity-color($--skin-primary-color, 1), + // #d8be85 + 'primary-light-2': get-opacity-color($--skin-primary-color, 2), + // #ddc695 + 'primary-light-3': get-opacity-color($--skin-primary-color, 3), + // #e2cea4 + 'primary-light-4': get-opacity-color($--skin-primary-color, 4), + // #e7d7b3 + 'primary-light-5': get-opacity-color($--skin-primary-color, 5), + // #ebdfc2 + 'primary-light-6': get-opacity-color($--skin-primary-color, 6), + // #f0e7d1 + 'primary-light-7': get-opacity-color($--skin-primary-color, 7), + // #f5efe1 + 'primary-light-8': get-opacity-color($--skin-primary-color, 8), + // #faf7f0 + 'primary-light-9': get-opacity-color($--skin-primary-color, 9), + + // ---------- 背景色 ---------- // + // 主要背景色(以侧边栏主体背景色定义) + 'main-bg-color': #F4F4F4, + // 次要背景色(以侧边栏头部背景色定义) + 'sub-bg-color': $--color-white, + // 面板背景色 + 'panel-bg-color': #ededed, + // 禁用背景色 + 'disabled-bg-color': #e9ecee, + + // ---------- 字体色 ---------- // + // 正文字体色 + 'main-text-color': #666666, + // 正文高亮字体色 + 'main-highlight-text-color': #C89A2C, + // 描述字体色 + 'describe-text-color': rgba(#666666, 0.6), + // 禁用字体色 + 'disabled-text-color': #9e9e9e, + // 面板字体色 + 'panel-text-color': #666666, + // 面板高亮字体色 + 'panel-highlight-text-color': $--skin-primary-color, + + // ---------- 边框色 ---------- // + 'main-border-color': rgba(#666666, 0.1), + + // ---------- 滚动条 ---------- // + 'panel-scrollbar-color': rgba(#666666, 0.2), +); + +// 皮肤基础组件配置 +$--skin-component-configs: ( + // 主要 tab 头部背景色 + 'main-tab-header:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部字体色 + 'main-tab-header:text-color': #666666, + // 主要 tab 头部 hover 字体色 + 'main-tab-header--hover:text-color': #666666, + // 主要 tab 头部激活字体色 + 'main-tab-header--active:text-color': #666666, + // 主要 tab 头部激活选项线条背景色 + 'main-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主要 tab 头部阴影开始颜色 + 'main-tab-header-shadow-start:color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部阴影结束颜色 + 'main-tab-header-shadow-end:color': rgba(62, 62, 78, 0), + // 主要 tab 头部红点颜色 + 'main-tab-header-reminder:color': #f24453, + // 主要 tab 主体背景色 + 'main-tab-body:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 主要 tab 主体滚动栏颜色 + 'main-tab-body:scrollbar-color': rgba(#666666, 0.2), + + // 常规 tab 激活项线条背景色 + 'normal-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 主题按钮背景色 + 'primary-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主题按钮字体色 + 'primary-button:text-color': $--color-white, + // 主题按钮 hover 背景色 + 'primary-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-light-2'), + // 主题按钮 hover 字体色 + 'primary-button--hover:text-color': $--color-white, + // 主题按钮 active 背景色 + 'primary-button--active:bg-color': map.get($--skin-basic-configs, 'primary-light-1'), + // 主题按钮 active 字体色 + 'primary-button--active:text-color': $--color-white, + // 主题按钮 disabled 背景色 + 'primary-button--disabled:bg-color': map.get($--skin-basic-configs, 'disabled-bg-color'), + // 主题按钮 disabled 字体色 + 'primary-button--disabled:text-color': map.get($--skin-basic-configs, 'disabled-text-color'), + + // 侧边栏按钮背景色 + 'aside-menu-button:bg-color': rgba($--color-white, 0.6), + // 侧边栏按钮字体则 + 'aside-menu-button:text-color': #333333, + // 侧边栏按钮 hover 背景色 + 'aside-menu-button--hover:bg-color': rgba($--color-white, 0.8), + // 侧边栏按钮 active 背景色 + 'aside-menu-button--active:bg-color': rgba($--color-white, 0.7), + + // pc 端直播状态标签颜色 + 'pc-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #78A7ED, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #78A7ED, + 'stop' #f06e6e, + ), + // mobile 端直播状态标签颜色 + 'mobile-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #78A7ED, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #78A7ED, + 'stop' #f06e6e, + ), +); + +// 皮肤公用页面配置 +$--skin-page-common-configs: ( + // 聊天消息昵称字体色 + 'chat-msg-user-nickname:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份昵称字体色 + 'chat-msg-user-special-nickname:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息用户头衔背景色 + 'chat-msg-user-actor:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天消息用户头衔字体色 + 'chat-msg-user-actor:text-color': $--color-white, + // 聊天消息用户设置昵称图标字体色 + 'chat-msg-user-set-nick-icon:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息时间字体色 + 'chat-msg-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 聊天提示文案字体色 + 'chat-msg-list-tips:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 聊天消息气泡背景色 + 'chat-msg-bubble:bg-color': $--color-white, + // 聊天消息气泡字体色 + 'chat-msg-bubble:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份气泡背景色 + 'chat-msg-bubble-special:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息特殊身份气泡字体色 + 'chat-msg-bubble-special:text-color': #6C4A4A, + + // 聊天消息回复内容字体色 + 'chat-msg-quote-content:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息回复内容边框色 + 'chat-msg-quote-content:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:text-color': rgba(#6C4A4A, 0.8), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:border-color': rgba(#6C4A4A, 0.1), + // 聊天消息回复按钮字体色 + 'chat-msg-quote-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天回复引用背景色 + 'chat-quote-msg:bg-color': #FAFAFA, + // 聊天回复引用字体色 + 'chat-quote-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 聊天消息翻译按钮字体色 + 'chat-msg-translate-button:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 聊天消息翻译按钮 hover 字体色 + 'chat-msg-translate-button--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息翻译成功提示字体色 + 'chat-msg-translate-finish:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 聊天消息特殊身份翻译成功提示字体色 + 'chat-msg-translate-finish-special:text-color': rgba($--color-white, 0.8), + + // 聊天室更多消息按钮背景色 + 'chat-msg-more-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮字体色 + 'chat-msg-more-button:text-color': #ffffff, + // 聊天室更多消息按钮 hover 背景色 + 'chat-msg-more-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮 hover 字体色 + 'chat-msg-more-button--hover:text-color': #ffffff, + + // 打赏消息字体色 + 'chat-reward-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 自定义消息字体色 + 'chat-customer-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 红包领取消息背景色 + 'chat-redpaper-receive-msg:bg-color': #a9d2ff, + // 红包领取消息字体色 + 'chat-redpaper-receive-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 系统消息字体色 + 'chat-system-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 章节字体色 + 'chapter-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 章节高亮字体色 + 'chapter-item--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 章节边框色 + 'chapter-item:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + + // 成员列表字体色 + 'member-list-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 成员列表特殊身份字体色 + 'member-list-item-special:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 点赞按钮背景图 + 'like-button:bg-image': './imgs/pws-zan.png', + // 点赞数量字体色 + 'like-count-number:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 点赞数量背景色 + 'like-count-number:bg-color': rgba(map.get($--skin-basic-configs, 'main-bg-color'), 0.8), + + // 页面广告背景色 + 'page-advert:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 页面广告字体色 + 'page-advert:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 页面广告箭头字体色 + 'page-advert-arrow:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 互动二次入口按钮背景色 + 'interactive-entrance-button:bg-color': rgba($--color-white, 0.9), + // 互动二次入口按钮 hover 背景色 + 'interactive-entrance-button--hover:bg-color': rgba($--color-white, 0.8), + // 互动二次入口按钮字体色 + 'interactive-entrance-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 互动二次入口箭头字体色 + 'interactive-entrance-arrow:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.4), +); diff --git a/src/skins/golden/config-iar.scss b/src/skins/golden/config-iar.scss new file mode 100644 index 0000000000000000000000000000000000000000..8df4bb845474ece490801c8644f18dc5940213ba --- /dev/null +++ b/src/skins/golden/config-iar.scss @@ -0,0 +1,111 @@ +@import './config-common.scss'; +@import './config-pc.scss'; + +// 皮肤互动功能公用配置 +$--skin-iar-common-configs: ( + // 挂件字体色 + 'iar-pendant:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 商品库内容背景色 + 'iar-product-list-content:bg-color': #fefefe, + // 商品库图标图片 + 'iar-product-list-shop:icon-image': './imgs/pws-product-icon.png', + // 商品库图标图片(职位) + 'iar-product-list-recruitment:icon-image': './imgs/pws-product-icon-recruitment.png', + // 商品库标签背景色 + 'iar-product-list-label:bg-color': rgba(#333333, 0.1), + // 商品库标签字体色 + 'iar-product-list-label:text-color': #333333, + // 商品库商品名称序号背景色 + 'iar-product-list-name-number:bg-color': rgba($--color-black, 0.35), + // 商品库空状态图标图片 + 'iar-product-list-empty:icon-image': './imgs/pws-product-shop-car.png', + // 商品库商品下边框颜色 + 'iar-product-list-item:border-color': rgba(#666666, 0.1), + // 商品库商品名称字体色 + 'iar-product-list-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 置顶公告背景色 + 'iar-bulletin-top-banner:bg-color': #FFFAEE, + // 置顶公告字体色 + 'iar-bulletin-top-banner:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 问答背景色 + 'iar-qa-body:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层背景色 + 'iar-qa-filter:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层边框色 + 'iar-qa-filter:border-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层文案字体色 + 'iar-qa-filter-desc:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 问答选择字体色 + 'iar-qa-select:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答选择箭头边框色 + 'iar-qa-select:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 问答节点背景色 + 'iar-qa-item:bg-color': $--color-white, + // 问答节点内容字体色 + 'iar-qa-item-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答节点信息字体色 + 'iar-qa-item-info:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 问答节点更多字体色 + 'iar-qa-item-more:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 问答节点边框色 + 'iar-qa-item:border-color': #f4f4f4, + // 问答新消息按钮背景色 + 'iar-qa-new-msg-button:bg-color': rgba(map.get($--skin-basic-configs, 'primary-color'), 0.85) , + // 问答新消息按钮字体色 + 'iar-qa-new-msg-button:text-color': $--color-white, + // 问答没有更多文案字体色 + 'iar-qa-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); + +// 皮肤互动功能 PC 配置 +$--skin-iar-pc-configs: ( + // 问答选择选项背景色 + 'iar-pc-qa-select-option:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答选择选项 hover 背景色 + 'iar-pc-qa-select-option--hover:bg-color': rgba($--color-white, 0.1), + // 问答选择选项字体色 + 'iar-pc-qa-select-option:text-color': rgba(map.get($--skin-component-configs, 'main-tab-header:text-color'), 0.8), + // 问答选择选项激活字体色 + 'iar-pc-qa-select-option--active:text-color': map.get($--skin-component-configs, 'main-tab-header:text-color'), + // 问答选择选项边框色 + 'iar-pc-qa-select-option:border-color': rgba($--color-white, 0.1), + // 问答表情选择图标 + 'iar-pc-qa-ask-emotion:text-color': map.get($--skin-basic-configs, 'main-text-color'), + 'iar-pc-qa-ask-emotion:background-filter': none, + + // 中奖记录弹层文字颜色 + 'iar-pc-lottery-record-msg:text-color': #666, +); + +// 皮肤互动功能 Mobile 配置 +$--skin-iar-mobile-configs: ( + // 问答空数据的图标图片 + 'iar-mobile-qa-no-data:icon-image': './imgs/pws-qa-no-data-icon.png', + // 问答消息发送入口图片 + 'iar-mobile-qa-ask-entry:icon-image': './imgs/pws-qa-entry.png', + // 问答输入框外层背景色 + 'iar-mobile-qa-input-wrap:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答输入框背景色 + 'iar-mobile-qa-input:bg-color': rgba($--color-black, 0.05), + // 问答输入框字体色 + 'iar-mobile-qa-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答表情选择图标字体色 + 'iar-mobile-qa-ask-emotion:text-color': map.get($--skin-basic-configs, 'main-text-color'), + 'iar-mobile-qa-ask-emotion:background-filter': none, + // 问答发送按钮字体色 + 'iar-mobile-qa-ask-send-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 图文直播头部字体色 + 'iar-mobile-tuwen-live-header:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 图文直播名称字体色 + 'iar-mobile-tuwen-live-content-name:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 图文直播时间字体色 + 'iar-mobile-tuwen-live-content-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 图文直播内容字体色 + 'iar-mobile-tuwen-live-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 图文直播暂无更多字体色 + 'iar-mobile-tuwen-live-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); diff --git a/src/skins/golden/config-mobile.scss b/src/skins/golden/config-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..8afc361b022da8473f89afe23aaf97541d04c7f6 --- /dev/null +++ b/src/skins/golden/config-mobile.scss @@ -0,0 +1,154 @@ +@import './config-common.scss'; + +// 皮肤 Mobile 端页面配置 +$--skin-page-mobile-configs: ( + // 富文本字体色 + 'mobile-rich-text-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 引导页背景色 + 'mobile-splash:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 引导页倒计时外层背景色 + 'mobile-splash-live-count-down-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 页脚字体色 + 'mobile-page-footer:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 基础信息频道标题字体色 + 'mobile-basic-info-channel-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息开始时间字体色 + 'mobile-basic-info-start-time:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息页面浏览次数字体色 + 'mobile-basic-info-page-view:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息主持人字体色 + 'mobile-basic-info-publisher:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息点赞数字体色 + 'mobile-basic-info-like:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 倒计时方块背景色 + 'mobile-count-down-square-item:bg-color': #F4F4F4, + // 倒计时方块远点背景色 + 'mobile-count-down-square-item-dot:bg-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 倒计时方块数字字体色 + 'mobile-count-down-square-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块文案字体色 + 'mobile-count-down-square-item-word:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 倒计时方块边框色 + 'mobile-count-down-square-item:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + + // 直播预约按钮背景色 + 'mobile-live-booking-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 关注按钮背景色 + 'mobile-follow-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 关注按钮字体颜色 + 'mobile-follow-button:text-color': $--color-white, + + // 聊天消息输入框背景色 + 'mobile-msg-input:bg-color': rgba($--color-white, 0.9), + // 聊天消息输入框字体色 + 'mobile-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'mobile-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'mobile-msg-input:scrollbar-color': rgba(21, 113, 208, 0.2), + // 聊天消息输入框图标字体色 + 'mobile-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 聊天消息发送按钮字体颜色 + 'mobile-msg-send-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'mobile-emotion-panel:bg-color': #EDEDED, + // 表情选择面板下的发送按钮字体色 + 'mobile-emotion-panel-send-button:bg-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 表情选择面板下的输入框背景色 + 'mobile-emotion-panel-msg-input:bg-color': $--color-white, + // 表情选择面板下的输入框字体色 + 'mobile-emotion-panel-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 表情选择面板下的输入框占位符字体色 + 'mobile-emotion-panel-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 表情选择面板下的输入框图标字体色 + 'mobile-emotion-panel-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 表情选择类型选择背景色 + 'mobile-emotion-panel-type:bg-color': rgba($--color-white, 0.4), + // 表情选择类型选择选中背景色 + 'mobile-emotion-panel-type--active:bg-color': rgba($--color-white, 0.8), + // 表情选择类型选择字体色 + 'mobile-emotion-panel-type:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 表情选择类型选择选中字体色 + 'mobile-emotion-panel-type--active:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 表情选择删除按钮背景色 + 'mobile-emotion-panel-del-button:bg-color': rgba($--color-white, 0.4), + // 表情选择删除按钮字体色 + 'mobile-emotion-panel-del-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 更多按钮背景色 + 'mobile-more-button:bg-color': rgba($--color-white, 0.9), + // 更多按钮图标图片 + 'mobile-more-button:icon-image': './imgs/pws-icon-more.png', + // 更多面板背景色 + 'mobile-more-panel:bg-color': #EDEDED, + // 更多面板字体色 + 'mobile-more-panel:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 打赏按钮背景色 + 'mobile-donate-button:bg-color': rgba($--color-white, 0.9), + // 打赏面板背景色 + 'mobile-donate-panel:bg-color': #EDEDED, + // 打赏面板标题字体色 + 'mobile-donate-panel-header-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板返回字体色 + 'mobile-donate-panel-header-close:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏礼物选中背景色 + 'mobile-donate-good--active:bg-color': $--color-white, + // 打赏礼物选中边框色 + 'mobile-donate-good--active:border-color': rgba($--color-black, 0.3), + // 打赏礼物道具名称字体色 + 'mobile-donate-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏礼物道具价格字体色 + 'mobile-donate-good-price:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 打赏切换指示点颜色 + 'mobile-donate-indicator:bg-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.4), + // 打赏切换指示点选中颜色 + 'mobile-donate-indicator--active:bg-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板选项背景色 + 'mobile-donate-panel-option:bg-color': $--color-white, + // 打赏面板选项字体色 + 'mobile-donate-panel-option:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 打赏面板选项选中背景色 + 'mobile-donate-panel-option--active:bg-color': $--color-white, + // 打赏面板选项选中字体色 + 'mobile-donate-panel-option--active:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板选项选中边框色 + 'mobile-donate-panel-option--active:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.3), + // 打赏面板发送按钮背景色 + 'mobile-donate-panel-send-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 打赏面板发送按钮字体色 + 'mobile-donate-panel-send-button:text-color': $--color-white, + // 打赏面板剩余积分文本字体色 + 'mobile-donate-panel-point:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板剩余积分数值字体色 + 'mobile-donate-panel-point-count:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额字体色 + 'mobile-donate-panel-custom-cash:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额输入框背景色 + 'mobile-donate-custom-cash-input:bg-color': $--color-white, + // 打赏自定义金额输入框标题字体色 + 'mobile-donate-custom-cash-input-title:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 打赏自定义金额输入框字体色 + 'mobile-donate-custom-cash-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏自定义金额随机按钮字体色 + 'mobile-donate-custom-cash-random:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 回放列表播放中按钮背景色 + 'mobile-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 回放列表字体色 + 'mobile-playback-list:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 回放列表选中时的字体色 + 'mobile-playback-list--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 多会场背景色 + 'mobile-multi-meeting:bg-color': #EDEDED, + // 多会场字体色 + 'mobile-multi-meeting:text-color': map.get($--skin-basic-configs, 'main-text-color'), +); diff --git a/src/skins/golden/config-pc.scss b/src/skins/golden/config-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..85aa9906e2b22a5114552c1ac7235c23fa583db9 --- /dev/null +++ b/src/skins/golden/config-pc.scss @@ -0,0 +1,110 @@ +@import './config-common.scss'; + +// 皮肤 PC 端页面配置 +$--skin-page-pc-configs: ( + // 观看页背景类型 + 'pc-watch-page:bg-type': 'image', + // 观看页背景颜色 + 'pc-watch-page:bg-color': '', + // 观看页背景图 + 'pc-watch-page:bg-image': './imgs/pws-pc-watch-bg.png', + + // 副屏收起字体颜色 + 'pc-sub-pack-up:text-color': #333, + + // 倒计时方块背景色 + 'pc-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'pc-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'pc-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'pc-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'pc-count-down-square-item:border-color': $--color-black, + + // 聊天消息底部输入框背景色 + 'pc-msg-bottom-input-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 聊天消息底部输入框节点字体色 + 'pc-msg-bottom-input-wrap-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息底部输入框节点 hover 字体色 + 'pc-msg-bottom-input-wrap-item--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 聊天消息输入框背景色 + 'pc-msg-input:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天消息输入框字体色 + 'pc-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'pc-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'pc-msg-input:scrollbar-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.2), + + // 聊天消息发送按钮背景色 + 'pc-msg-send-button:bg-color': #A70000, + // 聊天消息发送按钮禁用状态下的背景色 + 'pc-msg-send-button--disabled:bg-color': rgba(#A70000, 0.4), + // 聊天消息发送按钮字体颜色 + 'pc-msg-send-button:text-color': $--color-white, + // 聊天消息发送按钮禁用状态下的字体颜色 + 'pc-msg-send-button--disabled:text-color': rgba($--color-white, 0.5), + + // 图片达到最大数量后的提示框背景色 + 'pws-pc-msg-input-popper__max-image-tips:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 图片达到最大数量后的提示框字体色 + 'pws-pc-msg-input-popper__max-image-tips:text-color': $--color-white, + + // 聊天输入框设置昵称占位背景色 + 'pc-set-nick-placeholder:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天输入框设置昵称占位字体色 + 'pc-set-nick-placeholder:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天输入框设置昵称占位高亮字体色 + 'pc-set-nick-placeholder-highlight:text-color': map.get($--skin-basic-configs, 'primary-color'), + + // 表情选择面板背景色 + 'pc-emotion-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 表情选择面板类型选择背景色 + 'pc-emotion-panel-select:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 更多面板背景色 + 'pc-more-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 更多面板字体色 + 'pc-more-panel:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 更多面板字体高亮色 + 'pc-more-panel-text--hover:text-color': map.get($--skin-basic-configs, 'panel-highlight-text-color'), + // 更多面板选中框背景色 + 'pc-more-panel-checkbox--checked:bg-color': #666666, + // 更多面板选中框图标色 + 'pc-more-panel-checkbox-icon--checked:text-color': #EDEDED, + + // 打赏面板背景色 + 'pc-donate-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 打赏礼物 hover 背景色 + 'pc-donate-good--hover:bg-color': $--color-white, + // 打赏礼物 hover 边框色 + 'pc-donate-good--hover:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.3), + // 打赏礼物道具名称字体色 + 'pc-donate-good-name:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 打赏礼物道具价格字体色 + 'pc-donate-good-price:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 中奖记录面板背景色 + 'pc-lottery-record-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 中奖记录面板关闭图标字体色 + 'pc-lottery-record-close:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + + // 连麦描述图标字体色 + 'pc-connect-mic-desc-icon:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦描述文案字体色 + 'pc-connect-mic-desc:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦按钮背景色 + 'pc-connect-mic-button:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 连麦按钮字体色 + 'pc-connect-mic-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 连麦按钮高亮字体色 + 'pc-connect-mic-button-highlight:text-color': #ff5b5b, + // 连麦中图标背景图 + 'pc-connect-mic-applying:icon-image': './imgs/pws-pc-connect-mic.gif', + + // 回放列表播放中按钮背景色 + 'pc-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), +); diff --git a/src/skins/golden/config.scss b/src/skins/golden/config.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2135df8fc7d53e586d0667c1e22586bb313eae2 --- /dev/null +++ b/src/skins/golden/config.scss @@ -0,0 +1,7 @@ +@import '../_common/functions.scss'; +@import './config-common.scss'; +@import './config-pc.scss'; +@import './config-mobile.scss'; +@import './config-iar.scss'; + +$--skin-configs: generate-skin-setup-configs(); diff --git a/src/skins/golden/imgs/pws-icon-more.png b/src/skins/golden/imgs/pws-icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..0233f9635c6494aaa9fb152a993b3695267b564f Binary files /dev/null and b/src/skins/golden/imgs/pws-icon-more.png differ diff --git a/src/skins/golden/imgs/pws-pc-connect-mic.gif b/src/skins/golden/imgs/pws-pc-connect-mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..07571e87781e59628f44c7f8da301de8b4b54c5f Binary files /dev/null and b/src/skins/golden/imgs/pws-pc-connect-mic.gif differ diff --git a/src/skins/golden/imgs/pws-pc-watch-bg.png b/src/skins/golden/imgs/pws-pc-watch-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..d6623690d13d416fca05ce735de12b5104bb26fc Binary files /dev/null and b/src/skins/golden/imgs/pws-pc-watch-bg.png differ diff --git a/src/skins/golden/imgs/pws-product-icon-recruitment.png b/src/skins/golden/imgs/pws-product-icon-recruitment.png new file mode 100644 index 0000000000000000000000000000000000000000..86a5a39e61943b070d4350f5edab5c9afe2567fc Binary files /dev/null and b/src/skins/golden/imgs/pws-product-icon-recruitment.png differ diff --git a/src/skins/golden/imgs/pws-product-icon.png b/src/skins/golden/imgs/pws-product-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9eaad5cdf3dfacdeb5336c5cd48d8d58fdd900bc Binary files /dev/null and b/src/skins/golden/imgs/pws-product-icon.png differ diff --git a/src/skins/golden/imgs/pws-product-shop-car.png b/src/skins/golden/imgs/pws-product-shop-car.png new file mode 100644 index 0000000000000000000000000000000000000000..55e4c53664b9b4eeec88398a9a40b3a5964bc248 Binary files /dev/null and b/src/skins/golden/imgs/pws-product-shop-car.png differ diff --git a/src/skins/golden/imgs/pws-qa-entry.png b/src/skins/golden/imgs/pws-qa-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..933e25877fdd5a051444673837780da38f0b4dfd Binary files /dev/null and b/src/skins/golden/imgs/pws-qa-entry.png differ diff --git a/src/skins/golden/imgs/pws-qa-no-data-icon.png b/src/skins/golden/imgs/pws-qa-no-data-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..772d92d4cf35a99dd79ffaac0abfb958d0ad2a99 Binary files /dev/null and b/src/skins/golden/imgs/pws-qa-no-data-icon.png differ diff --git a/src/skins/golden/imgs/pws-zan.png b/src/skins/golden/imgs/pws-zan.png new file mode 100644 index 0000000000000000000000000000000000000000..0c70d05f8d8bc7251ba01c8e5437b934161a4664 Binary files /dev/null and b/src/skins/golden/imgs/pws-zan.png differ diff --git a/src/skins/green/_green-mobile.scss b/src/skins/green/_green-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..87c09f945a1836f57dd951bb4b17cc5963a48285 --- /dev/null +++ b/src/skins/green/_green-mobile.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-mobile-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-mobile-style($--skin-configs); diff --git a/src/skins/green/_green-pc.scss b/src/skins/green/_green-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a13764923fe827d4013ff68666fb5afd7f37d7d --- /dev/null +++ b/src/skins/green/_green-pc.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-pc-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-pc-style($--skin-configs); diff --git a/src/skins/green/config-common.scss b/src/skins/green/config-common.scss new file mode 100644 index 0000000000000000000000000000000000000000..4a51f23d0c75b2b2b83f2c67216c3093afba17fa --- /dev/null +++ b/src/skins/green/config-common.scss @@ -0,0 +1,244 @@ +// 主题名称 +$--skin-type: 'green'; + +// 主题色 +$--skin-primary-color: #3CB599; +// 次要主题色 +$--skin-sub-primary-color: #52D2B4; + +// 皮肤基础配置 +$--skin-basic-configs: ( + // 主题名称 + 'skin-type': $--skin-type, + + // 主题色 + 'primary-color': $--skin-primary-color, + // 次要主题色 + 'sub-primary-color': $--skin-sub-primary-color, + // #50bca3 + 'primary-light-1': get-opacity-color($--skin-primary-color, 1), + // #63c4ad + 'primary-light-2': get-opacity-color($--skin-primary-color, 2), + // #77cbb8 + 'primary-light-3': get-opacity-color($--skin-primary-color, 3), + // #8ad3c2 + 'primary-light-4': get-opacity-color($--skin-primary-color, 4), + // #9edacc + 'primary-light-5': get-opacity-color($--skin-primary-color, 5), + // #b1e1d6 + 'primary-light-6': get-opacity-color($--skin-primary-color, 6), + // #c5e9e0 + 'primary-light-7': get-opacity-color($--skin-primary-color, 7), + // #d8f0eb + 'primary-light-8': get-opacity-color($--skin-primary-color, 8), + // #ecf8f5 + 'primary-light-9': get-opacity-color($--skin-primary-color, 9), + + // ---------- 背景色 ---------- // + // 主要背景色(以侧边栏主体背景色定义) + 'main-bg-color': #398775, + // 次要背景色(以侧边栏头部背景色定义) + 'sub-bg-color': #3CB599, + // 面板背景色 + 'panel-bg-color': #2b7866, + // 禁用背景色 + 'disabled-bg-color': #e9ecee, + + // ---------- 字体色 ---------- // + // 正文字体色 + 'main-text-color': #ffffff, + // 正文高亮字体色 + 'main-highlight-text-color': #FFCF63, + // 描述字体色 + 'describe-text-color': rgba($--color-white, 0.6), + // 禁用字体色 + 'disabled-text-color': #9e9e9e, + // 面板字体色 + 'panel-text-color': $--color-white, + // 面板高亮字体色 + 'panel-highlight-text-color': #FFCF63, + + // ---------- 边框色 ---------- // + 'main-border-color': rgba($--color-black, 0.1), + + // ---------- 滚动条 ---------- // + 'panel-scrollbar-color': rgba($--color-white, 0.1), +); + +// 皮肤基础组件配置 +$--skin-component-configs: ( + // 主要 tab 头部背景色 + 'main-tab-header:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部字体色 + 'main-tab-header:text-color': $--color-white, + // 主要 tab 头部 hover 字体色 + 'main-tab-header--hover:text-color': $--color-white, + // 主要 tab 头部激活字体色 + 'main-tab-header--active:text-color': $--color-white, + // 主要 tab 头部激活选项线条背景色 + 'main-tab-header-line--active:bg-color': $--color-white, + // 主要 tab 头部阴影开始颜色 + 'main-tab-header-shadow-start:color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部阴影结束颜色 + 'main-tab-header-shadow-end:color': rgba(62, 62, 78, 0), + // 主要 tab 头部红点颜色 + 'main-tab-header-reminder:color': #f24453, + // 主要 tab 主体背景色 + 'main-tab-body:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 主要 tab 主体滚动栏颜色 + 'main-tab-body:scrollbar-color': rgba($--color-white, 0.6), + + // normal tab 激活项线条背景色 + 'normal-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 主题按钮背景色 + 'primary-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主题按钮字体色 + 'primary-button:text-color': $--color-white, + // 主题按钮 hover 背景色 + 'primary-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-light-2'), + // 主题按钮 hover 字体色 + 'primary-button--hover:text-color': $--color-white, + // 主题按钮 active 背景色 + 'primary-button--active:bg-color': map.get($--skin-basic-configs, 'primary-light-1'), + // 主题按钮 active 字体色 + 'primary-button--active:text-color': $--color-white, + // 主题按钮 disabled 背景色 + 'primary-button--disabled:bg-color': map.get($--skin-basic-configs, 'disabled-bg-color'), + // 主题按钮 disabled 字体色 + 'primary-button--disabled:text-color': map.get($--skin-basic-configs, 'disabled-text-color'), + + // 侧边栏按钮背景色 + 'aside-menu-button:bg-color': rgba($--color-white, 0.1), + // 侧边栏按钮字体则 + 'aside-menu-button:text-color': $--color-white, + // 侧边栏按钮 hover 背景色 + 'aside-menu-button--hover:bg-color': rgba($--color-white, 0.2), + // 侧边栏按钮 active 背景色 + 'aside-menu-button--active:bg-color': rgba($--color-white, 0.15), + + // pc 端直播状态标签颜色 + 'pc-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #ffd595, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #ffd595, + 'stop' #f06e6e, + ), + // mobile 端直播状态标签颜色 + 'mobile-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #ffd595, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #ffd595, + 'stop' #f06e6e, + ), +); + +// 皮肤公用页面配置 +$--skin-page-common-configs: ( + // 聊天消息用户昵称字体色 + 'chat-msg-user-nickname:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份昵称字体色 + 'chat-msg-user-special-nickname:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息用户头衔背景色 + 'chat-msg-user-actor:bg-color': #FFDE95, + // 聊天消息用户头衔字体色 + 'chat-msg-user-actor:text-color': #398775, + // 聊天消息用户设置昵称图标字体色 + 'chat-msg-user-set-nick-icon:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息时间字体色 + 'chat-msg-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 聊天提示文案字体色 + 'chat-msg-list-tips:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 聊天消息气泡背景色 + 'chat-msg-bubble:bg-color': #67AC9C, + // 聊天消息气泡字体色 + 'chat-msg-bubble:text-color': $--color-white, + // 聊天消息特殊身份气泡背景色 + 'chat-msg-bubble-special:bg-color': #E1F4F0, + // 聊天消息特殊身份气泡字体色 + 'chat-msg-bubble-special:text-color': #3A907C, + + // 聊天消息回复内容字体色 + 'chat-msg-quote-content:text-color': rgba($--color-white, 0.6), + // 聊天消息回复内容边框色 + 'chat-msg-quote-content:border-color': rgba($--color-white, 0.2), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:text-color': rgba(#3A907C, 0.8), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:border-color': rgba(#3A907C, 0.2), + // 聊天消息回复按钮字体色 + 'chat-msg-quote-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天回复引用背景色 + 'chat-quote-msg:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 聊天回复引用字体色 + 'chat-quote-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 聊天消息翻译按钮字体色 + 'chat-msg-translate-button:text-color': $--color-white, + // 聊天消息翻译按钮 hover 字体色 + 'chat-msg-translate-button--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息翻译成功提示字体色 + 'chat-msg-translate-finish:text-color': rgba($--color-white, 0.6), + // 聊天消息特殊身份翻译成功提示字体色 + 'chat-msg-translate-finish-special:text-color': rgba(#3A907C, 0.6), + + // 聊天室更多消息按钮背景色 + 'chat-msg-more-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮字体色 + 'chat-msg-more-button:text-color': #ffffff, + // 聊天室更多消息按钮 hover 背景色 + 'chat-msg-more-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮 hover 字体色 + 'chat-msg-more-button--hover:text-color': #ffffff, + + // 打赏消息字体色 + 'chat-reward-msg:text-color': rgba($--color-white, 0.6), + // 自定义消息字体色 + 'chat-customer-msg:text-color': rgba($--color-white, 0.6), + // 红包领取消息背景色 + 'chat-redpaper-receive-msg:bg-color': rgba($--color-black, 0.3), + // 红包领取消息字体色 + 'chat-redpaper-receive-msg:text-color': $--color-white, + // 系统消息字体色 + 'chat-system-msg:text-color': rgba($--color-white, 0.6), + + // 章节字体色 + 'chapter-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 章节高亮字体色 + 'chapter-item--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 章节边框色 + 'chapter-item:border-color': rgba($--color-white, 0.1), + + // 成员列表字体色 + 'member-list-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 成员列表特殊身份字体色 + 'member-list-item-special:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 点赞按钮背景图 + 'like-button:bg-image': './imgs/pws-zan.png', + // 点赞数量字体色 + 'like-count-number:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 点赞数量背景色 + 'like-count-number:bg-color': rgba(map.get($--skin-basic-configs, 'main-bg-color'), 0.8), + + // 页面广告背景色 + 'page-advert:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 页面广告字体色 + 'page-advert:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 页面广告箭头字体色 + 'page-advert-arrow:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 互动二次入口按钮背景色 + 'interactive-entrance-button:bg-color': rgba($--color-white, 0.1), + // 互动二次入口按钮 hover 背景色 + 'interactive-entrance-button--hover:bg-color': rgba($--color-white, 0.2), + // 互动二次入口按钮字体色 + 'interactive-entrance-button:text-color': $--color-white, + // 互动二次入口箭头字体色 + 'interactive-entrance-arrow:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), +); diff --git a/src/skins/green/config-iar.scss b/src/skins/green/config-iar.scss new file mode 100644 index 0000000000000000000000000000000000000000..15d20b511a4c793a61904eea25745abd0894dbcc --- /dev/null +++ b/src/skins/green/config-iar.scss @@ -0,0 +1,111 @@ +@import './config-common.scss'; +@import './config-pc.scss'; + +// 皮肤互动功能公用配置 +$--skin-iar-common-configs: ( + // 挂件字体色 + 'iar-pendant:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 商品库内容背景色 + 'iar-product-list-content:bg-color': #2e6c5e, + // 商品库图标图片 + 'iar-product-list-shop:icon-image': './imgs/pws-product-icon.png', + // 商品库图标图片(职位) + 'iar-product-list-recruitment:icon-image': './imgs/pws-product-icon-recruitment.png', + // 商品库标签背景色 + 'iar-product-list-label:bg-color': rgba($--color-white, 0.1), + // 商品库标签字体色 + 'iar-product-list-label:text-color': $--color-white, + // 商品库商品名称序号背景色 + 'iar-product-list-name-number:bg-color': rgba($--color-white, 0.35), + // 商品库空状态图标图片 + 'iar-product-list-empty:icon-image': './imgs/pws-product-shop-car.png', + // 商品库商品下边框颜色 + 'iar-product-list-item:border-color': rgba($--color-white, 0.1), + // 商品库商品名称字体色 + 'iar-product-list-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 置顶公告背景色 + 'iar-bulletin-top-banner:bg-color': #38A68D, + // 置顶公告字体色 + 'iar-bulletin-top-banner:text-color': $--color-white, + + // 问答背景色 + 'iar-qa-body:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层背景色 + 'iar-qa-filter:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层边框色 + 'iar-qa-filter:border-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层文案字体色 + 'iar-qa-filter-desc:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 问答选择字体色 + 'iar-qa-select:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答选择箭头边框色 + 'iar-qa-select:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 问答节点背景色 + 'iar-qa-item:bg-color': #4d9383, + // 问答节点内容字体色 + 'iar-qa-item-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答节点信息字体色 + 'iar-qa-item-info:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 问答节点更多字体色 + 'iar-qa-item-more:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 问答节点边框色 + 'iar-qa-item:border-color': #398775, + // 问答新消息按钮背景色 + 'iar-qa-new-msg-button:bg-color': rgba($--color-white, 0.85), + // 问答新消息按钮字体色 + 'iar-qa-new-msg-button:text-color': #3A907C, + // 问答没有更多文案字体色 + 'iar-qa-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); + +// 皮肤互动功能 PC 配置 +$--skin-iar-pc-configs: ( + // 问答选择选项背景色 + 'iar-pc-qa-select-option:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答选择选项 hover 背景色 + 'iar-pc-qa-select-option--hover:bg-color': rgba($--color-white, 0.1), + // 问答选择选项字体色 + 'iar-pc-qa-select-option:text-color': rgba(map.get($--skin-component-configs, 'main-tab-header:text-color'), 0.8), + // 问答选择选项激活字体色 + 'iar-pc-qa-select-option--active:text-color': map.get($--skin-component-configs, 'main-tab-header:text-color'), + // 问答选择选项边框色 + 'iar-pc-qa-select-option:border-color': rgba($--color-white, 0.1), + // 问答表情选择图标字体色 + 'iar-pc-qa-ask-emotion:text-color': $--color-white, + 'iar-pc-qa-ask-emotion:background-filter': grayscale(0%) brightness(150%), + + // 中奖记录弹层文字颜色 + 'iar-pc-lottery-record-msg:text-color': #e4e4e4, +); + +// 皮肤互动功能 Mobile 配置 +$--skin-iar-mobile-configs: ( + // 问答空数据的图标图片 + 'iar-mobile-qa-no-data:icon-image': './imgs/pws-qa-no-data-icon.png', + // 问答消息发送入口图片 + 'iar-mobile-qa-ask-entry:icon-image': './imgs/pws-qa-entry.png', + // 问答输入框外层背景色 + 'iar-mobile-qa-input-wrap:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答输入框背景色 + 'iar-mobile-qa-input:bg-color': rgba($--color-black, 0.1), + // 问答输入框字体色 + 'iar-mobile-qa-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答表情选择图标字体色 + 'iar-mobile-qa-ask-emotion:text-color': $--color-white, + 'iar-mobile-qa-ask-emotion:background-filter': grayscale(100%) brightness(200%), + // 问答发送按钮字体色 + 'iar-mobile-qa-ask-send-button:text-color': $--color-white, + + // 图文直播头部字体色 + 'iar-mobile-tuwen-live-header:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 图文直播名称字体色 + 'iar-mobile-tuwen-live-content-name:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 图文直播时间字体色 + 'iar-mobile-tuwen-live-content-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 图文直播内容字体色 + 'iar-mobile-tuwen-live-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 图文直播暂无更多字体色 + 'iar-mobile-tuwen-live-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); diff --git a/src/skins/green/config-mobile.scss b/src/skins/green/config-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..c03d62d2aa438a9657af9dd2d2a7d47dea4d7b26 --- /dev/null +++ b/src/skins/green/config-mobile.scss @@ -0,0 +1,154 @@ +@import './config-common.scss'; + +// 皮肤 Mobile 端页面配置 +$--skin-page-mobile-configs: ( + // 富文本字体色 + 'mobile-rich-text-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 引导页背景色 + 'mobile-splash:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 引导页倒计时外层背景色 + 'mobile-splash-live-count-down-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 页脚字体色 + 'mobile-page-footer:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 基础信息频道标题字体色 + 'mobile-basic-info-channel-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息开始时间字体色 + 'mobile-basic-info-start-time:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.75), + // 基础信息页面浏览次数字体色 + 'mobile-basic-info-page-view:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.75), + // 基础信息主持人字体色 + 'mobile-basic-info-publisher:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.75), + // 基础信息点赞数字体色 + 'mobile-basic-info-like:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.75), + + // 倒计时方块背景色 + 'mobile-count-down-square-item:bg-color': #398F7B, + // 倒计时方块远点背景色 + 'mobile-count-down-square-item-dot:bg-color': #398F7B, + // 倒计时方块数字字体色 + 'mobile-count-down-square-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块文案字体色 + 'mobile-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'mobile-count-down-square-item:border-color': #3f9984, + + // 直播预约按钮背景色 + 'mobile-live-booking-button:bg-color': #F8BD35, + + // 关注按钮背景色 + 'mobile-follow-button:bg-color': #52D2B4, + // 关注按钮字体颜色 + 'mobile-follow-button:text-color': $--color-white, + + // 聊天消息输入框背景色 + 'mobile-msg-input:bg-color': rgba($--color-white, 0.1), + // 聊天消息输入框字体色 + 'mobile-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'mobile-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'mobile-msg-input:scrollbar-color': #d31a34, + // 聊天消息输入框图标字体色 + 'mobile-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 聊天消息发送按钮字体颜色 + 'mobile-msg-send-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'mobile-emotion-panel:bg-color': #2B7866, + // 表情选择面板下的发送按钮字体色 + 'mobile-emotion-panel-send-button:bg-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 表情选择面板下的输入框背景色 + 'mobile-emotion-panel-msg-input:bg-color': rgba($--color-white, 0.1), + // 表情选择面板下的输入框字体色 + 'mobile-emotion-panel-msg-input:text-color': $--color-white, + // 表情选择面板下的输入框占位符字体色 + 'mobile-emotion-panel-msg-input-placeholder:text-color': rgba($--color-white, 0.6), + // 表情选择面板下的输入框图标字体色 + 'mobile-emotion-panel-msg-input-suffix-icon:text-color': rgba($--color-white, 0.7), + // 表情选择类型选择背景色 + 'mobile-emotion-panel-type:bg-color': rgba($--color-white, 0.1), + // 表情选择类型选择选中背景色 + 'mobile-emotion-panel-type--active:bg-color': rgba($--color-white, 0.2), + // 表情选择类型选择字体色 + 'mobile-emotion-panel-type:text-color': rgba($--color-white, 0.5), + // 表情选择类型选择选中字体色 + 'mobile-emotion-panel-type--active:text-color': $--color-white, + // 表情选择删除按钮背景色 + 'mobile-emotion-panel-del-button:bg-color': rgba($--color-white, 0.1), + // 表情选择删除按钮字体色 + 'mobile-emotion-panel-del-button:text-color': $--color-white, + + // 更多按钮背景色 + 'mobile-more-button:bg-color': rgba($--color-white, 0.1), + // 更多按钮图标图片 + 'mobile-more-button:icon-image': './imgs/pws-icon-more.png', + // 更多面板背景色 + 'mobile-more-panel:bg-color': #2B7866, + // 更多面板字体色 + 'mobile-more-panel:text-color': $--color-white, + + // 打赏按钮背景色 + 'mobile-donate-button:bg-color': rgba($--color-white, 0.1), + // 打赏面板背景色 + 'mobile-donate-panel:bg-color': #2B7866, + // 打赏面板标题字体色 + 'mobile-donate-panel-header-title:text-color': $--color-white, + // 打赏面板返回字体色 + 'mobile-donate-panel-header-close:text-color': $--color-white, + // 打赏礼物选中背景色 + 'mobile-donate-good--active:bg-color': #3CB599, + // 打赏礼物选中边框色 + 'mobile-donate-good--active:border-color': rgba($--color-white, 0.6), + // 打赏礼物道具名称字体色 + 'mobile-donate-good-name:text-color': $--color-white, + // 打赏礼物道具价格字体色 + 'mobile-donate-good-price:text-color': rgba($--color-white, 0.5), + // 打赏切换指示点颜色 + 'mobile-donate-indicator:bg-color': rgba($--color-white, 0.5), + // 打赏切换指示点选中颜色 + 'mobile-donate-indicator--active:bg-color': $--color-white, + // 打赏面板选项背景色 + 'mobile-donate-panel-option:bg-color': #398775, + // 打赏面板选项字体色 + 'mobile-donate-panel-option:text-color': $--color-white, + // 打赏面板选项选中背景色 + 'mobile-donate-panel-option--active:bg-color': #398775, + // 打赏面板选项选中字体色 + 'mobile-donate-panel-option--active:text-color': $--color-white, + // 打赏面板选项选中边框色 + 'mobile-donate-panel-option--active:border-color': rgba($--color-white, 0.5), + // 打赏面板发送按钮背景色 + 'mobile-donate-panel-send-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 打赏面板发送按钮字体色 + 'mobile-donate-panel-send-button:text-color': $--color-white, + // 打赏面板剩余积分文本字体色 + 'mobile-donate-panel-point:text-color': $--color-white, + // 打赏面板剩余积分数值字体色 + 'mobile-donate-panel-point-count:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额字体色 + 'mobile-donate-panel-custom-cash:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额输入框背景色 + 'mobile-donate-custom-cash-input:bg-color': rgba($--color-white, 0.1), + // 打赏自定义金额输入框标题字体色 + 'mobile-donate-custom-cash-input-title:text-color': $--color-white, + // 打赏自定义金额输入框字体色 + 'mobile-donate-custom-cash-input:text-color': $--color-white, + // 打赏自定义金额随机按钮字体色 + 'mobile-donate-custom-cash-random:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 回放列表播放中按钮背景色 + 'mobile-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 回放列表字体色 + 'mobile-playback-list:text-color': rgba($--color-white, 0.7), + // 回放列表选中时的字体色 + 'mobile-playback-list--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 多会场背景色 + 'mobile-multi-meeting:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 多会场字体色 + 'mobile-multi-meeting:text-color': map.get($--skin-basic-configs, 'main-text-color'), +); diff --git a/src/skins/green/config-pc.scss b/src/skins/green/config-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..abec76ed7b2e2f6d4651c50001f7b6ed2297af82 --- /dev/null +++ b/src/skins/green/config-pc.scss @@ -0,0 +1,110 @@ +@import './config-common.scss'; + +// 皮肤 PC 端页面配置 +$--skin-page-pc-configs: ( + // 观看页背景类型 + 'pc-watch-page:bg-type': 'image', + // 观看页背景颜色 + 'pc-watch-page:bg-color': '', + // 观看页背景图 + 'pc-watch-page:bg-image': './imgs/pws-pc-watch-bg.png', + + // 副屏收起字体颜色 + 'pc-sub-pack-up:text-color': $--color-white, + + // 倒计时方块背景色 + 'pc-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'pc-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'pc-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'pc-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'pc-count-down-square-item:border-color': $--color-black, + + // 聊天消息底部输入框背景色 + 'pc-msg-bottom-input-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 聊天消息底部输入框节点字体色 + 'pc-msg-bottom-input-wrap-item:text-color': $--color-white, + // 聊天消息底部输入框节点 hover 字体色 + 'pc-msg-bottom-input-wrap-item--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 聊天消息输入框背景色 + 'pc-msg-input:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天消息输入框字体色 + 'pc-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'pc-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'pc-msg-input:scrollbar-color': rgba($--color-white, 0.6), + + // 聊天消息发送按钮背景色 + 'pc-msg-send-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息发送按钮禁用状态下的背景色 + 'pc-msg-send-button--disabled:bg-color': rgba(map.get($--skin-basic-configs, 'sub-primary-color'), 0.5), + // 聊天消息发送按钮字体颜色 + 'pc-msg-send-button:text-color': $--color-white, + // 聊天消息发送按钮禁用状态下的字体颜色 + 'pc-msg-send-button--disabled:text-color': rgba($--color-white, 0.8), + + // 图片达到最大数量后的提示框背景色 + 'pws-pc-msg-input-popper__max-image-tips:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 图片达到最大数量后的提示框字体色 + 'pws-pc-msg-input-popper__max-image-tips:text-color': $--color-white, + + // 聊天输入框设置昵称占位背景色 + 'pc-set-nick-placeholder:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天输入框设置昵称占位字体色 + 'pc-set-nick-placeholder:text-color': $--color-white, + // 聊天输入框设置昵称占位高亮字体色 + 'pc-set-nick-placeholder-highlight:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'pc-emotion-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 表情选择面板类型选择背景色 + 'pc-emotion-panel-select:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 更多面板背景色 + 'pc-more-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 更多面板字体色 + 'pc-more-panel:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 更多面板字体高亮色 + 'pc-more-panel-text--hover:text-color': map.get($--skin-basic-configs, 'panel-highlight-text-color'), + // 更多面板选中框背景色 + 'pc-more-panel-checkbox--checked:bg-color': $--color-white, + // 更多面板选中框图标色 + 'pc-more-panel-checkbox-icon--checked:text-color': map.get($--skin-basic-configs, 'panel-bg-color'), + + // 打赏面板背景色 + 'pc-donate-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 打赏礼物 hover 背景色 + 'pc-donate-good--hover:bg-color': rgba($--color-white, 0.1), + // 打赏礼物 hover 边框色 + 'pc-donate-good--hover:border-color': rgba($--color-white, 0.4), + // 打赏礼物道具名称字体色 + 'pc-donate-good-name:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 打赏礼物道具价格字体色 + 'pc-donate-good-price:text-color': rgba($--color-white, 0.5), + + // 中奖记录面板背景色 + 'pc-lottery-record-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 中奖记录面板关闭图标字体色 + 'pc-lottery-record-close:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + + // 连麦描述图标字体色 + 'pc-connect-mic-desc-icon:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦描述文案字体色 + 'pc-connect-mic-desc:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦按钮背景色 + 'pc-connect-mic-button:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 连麦按钮字体色 + 'pc-connect-mic-button:text-color': $--color-white, + // 连麦按钮高亮字体色 + 'pc-connect-mic-button-highlight:text-color': $--color-white, + // 连麦中图标背景图 + 'pc-connect-mic-applying:icon-image': './imgs/pws-pc-connect-mic.gif', + + // 回放列表播放中按钮背景色 + 'pc-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), +); diff --git a/src/skins/green/config.scss b/src/skins/green/config.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2135df8fc7d53e586d0667c1e22586bb313eae2 --- /dev/null +++ b/src/skins/green/config.scss @@ -0,0 +1,7 @@ +@import '../_common/functions.scss'; +@import './config-common.scss'; +@import './config-pc.scss'; +@import './config-mobile.scss'; +@import './config-iar.scss'; + +$--skin-configs: generate-skin-setup-configs(); diff --git a/src/skins/green/imgs/pws-icon-more.png b/src/skins/green/imgs/pws-icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..52b15f454c14a10fb8ddce948263649053987419 Binary files /dev/null and b/src/skins/green/imgs/pws-icon-more.png differ diff --git a/src/skins/green/imgs/pws-pc-connect-mic.gif b/src/skins/green/imgs/pws-pc-connect-mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..08c5c465014dfc7f6e006c1e6083b8c8df888a90 Binary files /dev/null and b/src/skins/green/imgs/pws-pc-connect-mic.gif differ diff --git a/src/skins/green/imgs/pws-pc-watch-bg.png b/src/skins/green/imgs/pws-pc-watch-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..2e4165bb4e8c2232fdc55ed2a4ff696eb54f86ff Binary files /dev/null and b/src/skins/green/imgs/pws-pc-watch-bg.png differ diff --git a/src/skins/green/imgs/pws-product-icon-recruitment.png b/src/skins/green/imgs/pws-product-icon-recruitment.png new file mode 100644 index 0000000000000000000000000000000000000000..f5950ab2bcd011fe8c031ec6d0c45ef801ff228c Binary files /dev/null and b/src/skins/green/imgs/pws-product-icon-recruitment.png differ diff --git a/src/skins/green/imgs/pws-product-icon.png b/src/skins/green/imgs/pws-product-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1afa080773f435fdb911ed63a2339956ab362fe5 Binary files /dev/null and b/src/skins/green/imgs/pws-product-icon.png differ diff --git a/src/skins/green/imgs/pws-product-shop-car.png b/src/skins/green/imgs/pws-product-shop-car.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb64ea1f3f8cac9b972a4972d0075f7d2963b7e Binary files /dev/null and b/src/skins/green/imgs/pws-product-shop-car.png differ diff --git a/src/skins/green/imgs/pws-qa-entry.png b/src/skins/green/imgs/pws-qa-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..cd75fb7c2c6ede322239ab130c1fd982469ebee4 Binary files /dev/null and b/src/skins/green/imgs/pws-qa-entry.png differ diff --git a/src/skins/green/imgs/pws-qa-no-data-icon.png b/src/skins/green/imgs/pws-qa-no-data-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8cf9581b90ed3db35e8d50a02908e74bc07e099f Binary files /dev/null and b/src/skins/green/imgs/pws-qa-no-data-icon.png differ diff --git a/src/skins/green/imgs/pws-zan.png b/src/skins/green/imgs/pws-zan.png new file mode 100644 index 0000000000000000000000000000000000000000..9075cba3388504d91d0c81224ccd25b07a0fa1d8 Binary files /dev/null and b/src/skins/green/imgs/pws-zan.png differ diff --git a/src/skins/red/_red-mobile.scss b/src/skins/red/_red-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..87c09f945a1836f57dd951bb4b17cc5963a48285 --- /dev/null +++ b/src/skins/red/_red-mobile.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-mobile-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-mobile-style($--skin-configs); diff --git a/src/skins/red/_red-pc.scss b/src/skins/red/_red-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a13764923fe827d4013ff68666fb5afd7f37d7d --- /dev/null +++ b/src/skins/red/_red-pc.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-pc-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-pc-style($--skin-configs); diff --git a/src/skins/red/config-common.scss b/src/skins/red/config-common.scss new file mode 100644 index 0000000000000000000000000000000000000000..38c88adae5bec3b78d513aec9f642d6ba41d06bd --- /dev/null +++ b/src/skins/red/config-common.scss @@ -0,0 +1,244 @@ +// 主题名称 +$--skin-type: 'red'; + +// 主题色 +$--skin-primary-color: #d31a34; +// 次要主题色 +$--skin-sub-primary-color: #ffd595; + +// 皮肤基础配置 +$--skin-basic-configs: ( + // 主题名称 + 'skin-type': $--skin-type, + + // 主题色 + 'primary-color': $--skin-primary-color, + // 次要主题色 + 'sub-primary-color': $--skin-sub-primary-color, + // #d73148 + 'primary-light-1': get-opacity-color($--skin-primary-color, 1), + // #dc485d + 'primary-light-2': get-opacity-color($--skin-primary-color, 2), + // #e05f71 + 'primary-light-3': get-opacity-color($--skin-primary-color, 3), + // #e57685 + 'primary-light-4': get-opacity-color($--skin-primary-color, 4), + // #e98d9a + 'primary-light-5': get-opacity-color($--skin-primary-color, 5), + // #eda3ae + 'primary-light-6': get-opacity-color($--skin-primary-color, 6), + // #f2bac2 + 'primary-light-7': get-opacity-color($--skin-primary-color, 7), + // #f6d1d6 + 'primary-light-8': get-opacity-color($--skin-primary-color, 8), + // #fbe8eb + 'primary-light-9': get-opacity-color($--skin-primary-color, 9), + + // ---------- 背景色 ---------- // + // 主要背景色(以侧边栏主体背景色定义) + 'main-bg-color': #a20a1f, + // 次要背景色(以侧边栏头部背景色定义) + 'sub-bg-color': #b60f26, + // 面板背景色 + 'panel-bg-color': #8d0707, + // 禁用背景色 + 'disabled-bg-color': #e9ecee, + + // ---------- 字体色 ---------- // + // 正文字体色 + 'main-text-color': #ffffff, + // 正文高亮字体色 + 'main-highlight-text-color': $--skin-sub-primary-color, + // 描述字体色 + 'describe-text-color': rgba($--color-white, 0.6), + // 禁用字体色 + 'disabled-text-color': #9e9e9e, + // 面板字体色 + 'panel-text-color': $--color-white, + // 面板高亮字体色 + 'panel-highlight-text-color': $--skin-sub-primary-color, + + // ---------- 边框色 ---------- // + 'main-border-color': rgba($--color-white, 0.1), + + // ---------- 滚动条 ---------- // + 'panel-scrollbar-color': rgba($--color-white, 0.1), +); + +// 皮肤基础组件配置 +$--skin-component-configs: ( + // 主要 tab 头部背景色 + 'main-tab-header:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部字体色 + 'main-tab-header:text-color': rgba($--color-white, 0.7), + // 主要 tab 头部 hover 字体色 + 'main-tab-header--hover:text-color': $--color-white, + // 主要 tab 头部激活字体色 + 'main-tab-header--active:text-color': $--color-white, + // 主要 tab 头部激活选项线条背景色 + 'main-tab-header-line--active:bg-color': $--color-white, + // 主要 tab 头部阴影开始颜色 + 'main-tab-header-shadow-start:color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部阴影结束颜色 + 'main-tab-header-shadow-end:color': rgba(62, 62, 78, 0), + // 主要 tab 头部红点颜色 + 'main-tab-header-reminder:color': #f24453, + // 主要 tab 主体背景色 + 'main-tab-body:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 主要 tab 主体滚动栏颜色 + 'main-tab-body:scrollbar-color': #d31a34, + + // normal tab 激活项线条背景色 + 'normal-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 主题按钮背景色 + 'primary-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主题按钮字体色 + 'primary-button:text-color': $--color-white, + // 主题按钮 hover 背景色 + 'primary-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-light-2'), + // 主题按钮 hover 字体色 + 'primary-button--hover:text-color': $--color-white, + // 主题按钮 active 背景色 + 'primary-button--active:bg-color': map.get($--skin-basic-configs, 'primary-light-1'), + // 主题按钮 active 字体色 + 'primary-button--active:text-color': $--color-white, + // 主题按钮 disabled 背景色 + 'primary-button--disabled:bg-color': map.get($--skin-basic-configs, 'disabled-bg-color'), + // 主题按钮 disabled 字体色 + 'primary-button--disabled:text-color': map.get($--skin-basic-configs, 'disabled-text-color'), + + // 侧边栏按钮背景色 + 'aside-menu-button:bg-color': rgba($--color-white, 0.1), + // 侧边栏按钮字体则 + 'aside-menu-button:text-color': $--color-white, + // 侧边栏按钮 hover 背景色 + 'aside-menu-button--hover:bg-color': rgba($--color-white, 0.2), + // 侧边栏按钮 active 背景色 + 'aside-menu-button--active:bg-color': rgba($--color-white, 0.15), + + // pc 端直播状态标签颜色 + 'pc-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #ffd595, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #ffd595, + 'stop' #f06e6e, + ), + // mobile 端直播状态标签颜色 + 'mobile-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #ffd595, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #ffd595, + 'stop' #f06e6e, + ), +); + +// 皮肤公用页面配置 +$--skin-page-common-configs: ( + // 聊天消息用户昵称字体色 + 'chat-msg-user-nickname:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份昵称字体色 + 'chat-msg-user-special-nickname:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息用户头衔背景色 + 'chat-msg-user-actor:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息用户头衔字体色 + 'chat-msg-user-actor:text-color': #630000, + // 聊天消息用户设置昵称图标字体色 + 'chat-msg-user-set-nick-icon:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息时间字体色 + 'chat-msg-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 聊天提示文案字体色 + 'chat-msg-list-tips:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 聊天消息气泡背景色 + 'chat-msg-bubble:bg-color': #ffefd7, + // 聊天消息气泡字体色 + 'chat-msg-bubble:text-color': #830b0b, + // 聊天消息特殊身份气泡背景色 + 'chat-msg-bubble-special:bg-color': #ffd892, + // 聊天消息特殊身份气泡字体色 + 'chat-msg-bubble-special:text-color': #830b0b, + + // 聊天消息回复内容字体色 + 'chat-msg-quote-content:text-color': rgba(#830b0b, 0.8), + // 聊天消息回复内容边框色 + 'chat-msg-quote-content:border-color': rgba(#830b0b, 0.2), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:text-color': rgba(#830b0b, 0.8), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:border-color': rgba(#830b0b, 0.2), + // 聊天消息回复按钮字体色 + 'chat-msg-quote-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天回复引用背景色 + 'chat-quote-msg:bg-color': #721a1a, + // 聊天回复引用字体色 + 'chat-quote-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 聊天消息翻译按钮字体色 + 'chat-msg-translate-button:text-color': rgba($--color-white, 0.6), + // 聊天消息翻译按钮 hover 字体色 + 'chat-msg-translate-button--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息翻译成功提示字体色 + 'chat-msg-translate-finish:text-color': rgba(#830b0b, 0.6), + // 聊天消息特殊身份翻译成功提示字体色 + 'chat-msg-translate-finish-special:text-color': rgba(#830b0b, 0.6), + + // 聊天室更多消息按钮背景色 + 'chat-msg-more-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮字体色 + 'chat-msg-more-button:text-color': #ffffff, + // 聊天室更多消息按钮 hover 背景色 + 'chat-msg-more-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮 hover 字体色 + 'chat-msg-more-button--hover:text-color': #ffffff, + + // 打赏消息字体色 + 'chat-reward-msg:text-color': rgba($--color-white, 0.6), + // 自定义消息字体色 + 'chat-customer-msg:text-color': rgba($--color-white, 0.6), + // 红包领取消息背景色 + 'chat-redpaper-receive-msg:bg-color': rgba($--color-black, 0.3), + // 红包领取消息字体色 + 'chat-redpaper-receive-msg:text-color': $--color-white, + // 系统消息字体色 + 'chat-system-msg:text-color': rgba($--color-white, 0.6), + + // 章节字体色 + 'chapter-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 章节高亮字体色 + 'chapter-item--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 章节边框色 + 'chapter-item:border-color': rgba($--color-white, 0.1), + + // 成员列表字体色 + 'member-list-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 成员列表特殊身份字体色 + 'member-list-item-special:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 点赞按钮背景图 + 'like-button:bg-image': './imgs/pws-zan.png', + // 点赞数量字体色 + 'like-count-number:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 点赞数量背景色 + 'like-count-number:bg-color': rgba(map.get($--skin-basic-configs, 'main-bg-color'), 0.8), + + // 页面广告背景色 + 'page-advert:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 页面广告字体色 + 'page-advert:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 页面广告箭头字体色 + 'page-advert-arrow:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 互动二次入口按钮背景色 + 'interactive-entrance-button:bg-color': rgba($--color-white, 0.1), + // 互动二次入口按钮 hover 背景色 + 'interactive-entrance-button--hover:bg-color': rgba($--color-white, 0.2), + // 互动二次入口按钮字体色 + 'interactive-entrance-button:text-color': $--color-white, + // 互动二次入口箭头字体色 + 'interactive-entrance-arrow:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), +); diff --git a/src/skins/red/config-iar.scss b/src/skins/red/config-iar.scss new file mode 100644 index 0000000000000000000000000000000000000000..f05521f180d027c5a6b18c53111df74540fb9c5f --- /dev/null +++ b/src/skins/red/config-iar.scss @@ -0,0 +1,111 @@ +@import './config-common.scss'; +@import './config-pc.scss'; + +// 皮肤互动功能公用配置 +$--skin-iar-common-configs: ( + // 挂件字体色 + 'iar-pendant:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 商品库内容背景色 + 'iar-product-list-content:bg-color': #820819, + // 商品库图标图片 + 'iar-product-list-shop:icon-image': './imgs/pws-product-icon.png', + // 商品库图标图片(职位) + 'iar-product-list-recruitment:icon-image': './imgs/pws-product-icon-recruitment.png', + // 商品库标签背景色 + 'iar-product-list-label:bg-color': rgba($--color-white, 0.1), + // 商品库标签字体色 + 'iar-product-list-label:text-color': $--color-white, + // 商品库商品名称序号背景色 + 'iar-product-list-name-number:bg-color': rgba($--color-white, 0.35), + // 商品库空状态图标图片 + 'iar-product-list-empty:icon-image': './imgs/pws-product-shop-car.png', + // 商品库商品下边框颜色 + 'iar-product-list-item:border-color': rgba($--color-white, 0.1), + // 商品库商品名称字体色 + 'iar-product-list-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 置顶公告背景色 + 'iar-bulletin-top-banner:bg-color': #ffefd7, + // 置顶公告字体色 + 'iar-bulletin-top-banner:text-color': #830b0b, + + // 问答背景色 + 'iar-qa-body:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层背景色 + 'iar-qa-filter:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层边框色 + 'iar-qa-filter:border-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层文案字体色 + 'iar-qa-filter-desc:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 问答选择字体色 + 'iar-qa-select:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答选择箭头边框色 + 'iar-qa-select:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 问答节点背景色 + 'iar-qa-item:bg-color': #93171d, + // 问答节点内容字体色 + 'iar-qa-item-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答节点信息字体色 + 'iar-qa-item-info:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 问答节点更多字体色 + 'iar-qa-item-more:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 问答节点边框色 + 'iar-qa-item:border-color': #A20A1F, + // 问答新消息按钮背景色 + 'iar-qa-new-msg-button:bg-color': rgba($--color-white, 0.85), + // 问答新消息按钮字体色 + 'iar-qa-new-msg-button:text-color': #830b0b, + // 问答没有更多文案字体色 + 'iar-qa-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); + +// 皮肤互动功能 PC 配置 +$--skin-iar-pc-configs: ( + // 问答选择选项背景色 + 'iar-pc-qa-select-option:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答选择选项 hover 背景色 + 'iar-pc-qa-select-option--hover:bg-color': rgba($--color-white, 0.1), + // 问答选择选项字体色 + 'iar-pc-qa-select-option:text-color': rgba(map.get($--skin-component-configs, 'main-tab-header:text-color'), 0.8), + // 问答选择选项激活字体色 + 'iar-pc-qa-select-option--active:text-color': map.get($--skin-component-configs, 'main-tab-header:text-color'), + // 问答选择选项边框色 + 'iar-pc-qa-select-option:border-color': rgba($--color-white, 0.1), + // 问答表情选择图标字体色 + 'iar-pc-qa-ask-emotion:text-color': $--color-white, + 'iar-pc-qa-ask-emotion:background-filter': grayscale(100%) brightness(200%), + + // 中奖记录弹层文字颜色 + 'iar-pc-lottery-record-msg:text-color': #e4e4e4, +); + +// 皮肤互动功能 Mobile 配置 +$--skin-iar-mobile-configs: ( + // 问答空数据的图标图片 + 'iar-mobile-qa-no-data:icon-image': './imgs/pws-qa-no-data-icon.png', + // 问答消息发送入口图片 + 'iar-mobile-qa-ask-entry:icon-image': './imgs/pws-qa-entry.png', + // 问答输入框外层背景色 + 'iar-mobile-qa-input-wrap:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答输入框背景色 + 'iar-mobile-qa-input:bg-color': rgba($--color-black, 0.1), + // 问答输入框字体色 + 'iar-mobile-qa-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答表情选择图标字体色 + 'iar-mobile-qa-ask-emotion:text-color': $--color-white, + 'iar-mobile-qa-ask-emotion:background-filter': grayscale(100%) brightness(200%), + // 问答发送按钮字体色 + 'iar-mobile-qa-ask-send-button:text-color': $--color-white, + + // 图文直播头部字体色 + 'iar-mobile-tuwen-live-header:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 图文直播名称字体色 + 'iar-mobile-tuwen-live-content-name:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 图文直播时间字体色 + 'iar-mobile-tuwen-live-content-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 图文直播内容字体色 + 'iar-mobile-tuwen-live-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 图文直播暂无更多字体色 + 'iar-mobile-tuwen-live-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); diff --git a/src/skins/red/config-mobile.scss b/src/skins/red/config-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..787709d288cb03cb9a9e73773b02cda798b7048c --- /dev/null +++ b/src/skins/red/config-mobile.scss @@ -0,0 +1,154 @@ +@import './config-common.scss'; + +// 皮肤 Mobile 端页面配置 +$--skin-page-mobile-configs: ( + // 富文本字体色 + 'mobile-rich-text-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 引导页背景色 + 'mobile-splash:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 引导页倒计时外层背景色 + 'mobile-splash-live-count-down-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 页脚字体色 + 'mobile-page-footer:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 基础信息频道标题字体色 + 'mobile-basic-info-channel-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息开始时间字体色 + 'mobile-basic-info-start-time:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息页面浏览次数字体色 + 'mobile-basic-info-page-view:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息主持人字体色 + 'mobile-basic-info-publisher:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息点赞数字体色 + 'mobile-basic-info-like:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 倒计时方块背景色 + 'mobile-count-down-square-item:bg-color': #a20a1f, + // 倒计时方块远点背景色 + 'mobile-count-down-square-item-dot:bg-color': #a20a1f, + // 倒计时方块数字字体色 + 'mobile-count-down-square-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块文案字体色 + 'mobile-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'mobile-count-down-square-item:border-color': #bb2724, + + // 直播预约按钮背景色 + 'mobile-live-booking-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 关注按钮背景色 + 'mobile-follow-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 关注按钮字体颜色 + 'mobile-follow-button:text-color': #830b0b, + + // 聊天消息输入框背景色 + 'mobile-msg-input:bg-color': rgba($--color-white, 0.1), + // 聊天消息输入框字体色 + 'mobile-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'mobile-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'mobile-msg-input:scrollbar-color': #d31a34, + // 聊天消息输入框图标字体色 + 'mobile-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 聊天消息发送按钮字体颜色 + 'mobile-msg-send-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'mobile-emotion-panel:bg-color': #B60F26, + // 表情选择面板下的发送按钮字体色 + 'mobile-emotion-panel-send-button:bg-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 表情选择面板下的输入框背景色 + 'mobile-emotion-panel-msg-input:bg-color': rgba($--color-white, 0.1), + // 表情选择面板下的输入框字体色 + 'mobile-emotion-panel-msg-input:text-color': $--color-white, + // 表情选择面板下的输入框占位符字体色 + 'mobile-emotion-panel-msg-input-placeholder:text-color': rgba($--color-white, 0.6), + // 表情选择面板下的输入框图标字体色 + 'mobile-emotion-panel-msg-input-suffix-icon:text-color': rgba($--color-white, 0.7), + // 表情选择类型选择背景色 + 'mobile-emotion-panel-type:bg-color': rgba($--color-white, 0.1), + // 表情选择类型选择选中背景色 + 'mobile-emotion-panel-type--active:bg-color': rgba($--color-white, 0.2), + // 表情选择类型选择字体色 + 'mobile-emotion-panel-type:text-color': rgba($--color-white, 0.5), + // 表情选择类型选择选中字体色 + 'mobile-emotion-panel-type--active:text-color': $--color-white, + // 表情选择删除按钮背景色 + 'mobile-emotion-panel-del-button:bg-color': rgba($--color-white, 0.1), + // 表情选择删除按钮字体色 + 'mobile-emotion-panel-del-button:text-color': $--color-white, + + // 更多按钮背景色 + 'mobile-more-button:bg-color': rgba($--color-white, 0.1), + // 更多按钮图标图片 + 'mobile-more-button:icon-image': './imgs/pws-icon-more.png', + // 更多面板背景色 + 'mobile-more-panel:bg-color': #B60F26, + // 更多面板字体色 + 'mobile-more-panel:text-color': $--color-white, + + // 打赏按钮背景色 + 'mobile-donate-button:bg-color': rgba($--color-white, 0.1), + // 打赏面板背景色 + 'mobile-donate-panel:bg-color': #B60F26, + // 打赏面板标题字体色 + 'mobile-donate-panel-header-title:text-color': $--color-white, + // 打赏面板返回字体色 + 'mobile-donate-panel-header-close:text-color': $--color-white, + // 打赏礼物选中背景色 + 'mobile-donate-good--active:bg-color': #CE1F37, + // 打赏礼物选中边框色 + 'mobile-donate-good--active:border-color': rgba($--color-white, 0.6), + // 打赏礼物道具名称字体色 + 'mobile-donate-good-name:text-color': $--color-white, + // 打赏礼物道具价格字体色 + 'mobile-donate-good-price:text-color': rgba($--color-white, 0.5), + // 打赏切换指示点颜色 + 'mobile-donate-indicator:bg-color': rgba($--color-white, 0.5), + // 打赏切换指示点选中颜色 + 'mobile-donate-indicator--active:bg-color': $--color-white, + // 打赏面板选项背景色 + 'mobile-donate-panel-option:bg-color': #D31A34, + // 打赏面板选项字体色 + 'mobile-donate-panel-option:text-color': $--color-white, + // 打赏面板选项选中背景色 + 'mobile-donate-panel-option--active:bg-color': #D31A34, + // 打赏面板选项选中字体色 + 'mobile-donate-panel-option--active:text-color': $--color-white, + // 打赏面板选项选中边框色 + 'mobile-donate-panel-option--active:border-color': #FFD595, + // 打赏面板发送按钮背景色 + 'mobile-donate-panel-send-button:bg-color': #FFD595, + // 打赏面板发送按钮字体色 + 'mobile-donate-panel-send-button:text-color': #830B0B, + // 打赏面板剩余积分文本字体色 + 'mobile-donate-panel-point:text-color': $--color-white, + // 打赏面板剩余积分数值字体色 + 'mobile-donate-panel-point-count:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额字体色 + 'mobile-donate-panel-custom-cash:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额输入框背景色 + 'mobile-donate-custom-cash-input:bg-color': rgba($--color-black, 0.1), + // 打赏自定义金额输入框标题字体色 + 'mobile-donate-custom-cash-input-title:text-color': $--color-white, + // 打赏自定义金额输入框字体色 + 'mobile-donate-custom-cash-input:text-color': $--color-white, + // 打赏自定义金额随机按钮字体色 + 'mobile-donate-custom-cash-random:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 回放列表播放中按钮背景色 + 'mobile-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 回放列表字体色 + 'mobile-playback-list:text-color': rgba($--color-white, 0.7), + // 回放列表选中时的字体色 + 'mobile-playback-list--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 多会场背景色 + 'mobile-multi-meeting:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 多会场字体色 + 'mobile-multi-meeting:text-color': map.get($--skin-basic-configs, 'main-text-color'), +); diff --git a/src/skins/red/config-pc.scss b/src/skins/red/config-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..027e63416cd06b69430acd2f27fefe9d826bb89f --- /dev/null +++ b/src/skins/red/config-pc.scss @@ -0,0 +1,110 @@ +@import './config-common.scss'; + +// 皮肤 PC 端页面配置 +$--skin-page-pc-configs: ( + // 观看页背景类型 + 'pc-watch-page:bg-type': 'image', + // 观看页背景颜色 + 'pc-watch-page:bg-color': '', + // 观看页背景图 + 'pc-watch-page:bg-image': './imgs/pws-pc-watch-bg.png', + + // 副屏收起字体颜色 + 'pc-sub-pack-up:text-color': $--color-white, + + // 倒计时方块背景色 + 'pc-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'pc-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'pc-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'pc-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'pc-count-down-square-item:border-color': $--color-black, + + // 聊天消息底部输入框背景色 + 'pc-msg-bottom-input-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 聊天消息底部输入框节点字体色 + 'pc-msg-bottom-input-wrap-item:text-color': $--color-white, + // 聊天消息底部输入框节点 hover 字体色 + 'pc-msg-bottom-input-wrap-item--hover:text-color': map.get($--skin-basic-configs, 'sub-primary-color'), + + // 聊天消息输入框背景色 + 'pc-msg-input:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天消息输入框字体色 + 'pc-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'pc-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'pc-msg-input:scrollbar-color': #d31a34, + + // 聊天消息发送按钮背景色 + 'pc-msg-send-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息发送按钮禁用状态下的背景色 + 'pc-msg-send-button--disabled:bg-color': rgba(map.get($--skin-basic-configs, 'sub-primary-color'), 0.5), + // 聊天消息发送按钮字体颜色 + 'pc-msg-send-button:text-color': #830b0b, + // 聊天消息发送按钮禁用状态下的字体颜色 + 'pc-msg-send-button--disabled:text-color': rgba(#830b0b, 0.8), + + // 图片达到最大数量后的提示框背景色 + 'pws-pc-msg-input-popper__max-image-tips:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 图片达到最大数量后的提示框字体色 + 'pws-pc-msg-input-popper__max-image-tips:text-color': #830b0b, + + // 聊天输入框设置昵称占位背景色 + 'pc-set-nick-placeholder:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天输入框设置昵称占位字体色 + 'pc-set-nick-placeholder:text-color': $--color-white, + // 聊天输入框设置昵称占位高亮字体色 + 'pc-set-nick-placeholder-highlight:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'pc-emotion-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 表情选择面板类型选择背景色 + 'pc-emotion-panel-select:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 更多面板背景色 + 'pc-more-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 更多面板字体色 + 'pc-more-panel:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 更多面板字体高亮色 + 'pc-more-panel-text--hover:text-color': map.get($--skin-basic-configs, 'panel-highlight-text-color'), + // 更多面板选中框背景色 + 'pc-more-panel-checkbox--checked:bg-color': $--color-white, + // 更多面板选中框图标色 + 'pc-more-panel-checkbox-icon--checked:text-color': map.get($--skin-basic-configs, 'panel-bg-color'), + + // 打赏面板背景色 + 'pc-donate-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 打赏礼物 hover 背景色 + 'pc-donate-good--hover:bg-color': rgba($--color-white, 0.1), + // 打赏礼物 hover 边框色 + 'pc-donate-good--hover:border-color': rgba($--color-white, 0.4), + // 打赏礼物道具名称字体色 + 'pc-donate-good-name:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 打赏礼物道具价格字体色 + 'pc-donate-good-price:text-color': rgba($--color-white, 0.5), + + // 中奖记录面板背景色 + 'pc-lottery-record-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 中奖记录面板关闭图标字体色 + 'pc-lottery-record-close:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + + // 连麦描述图标字体色 + 'pc-connect-mic-desc-icon:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦描述文案字体色 + 'pc-connect-mic-desc:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦按钮背景色 + 'pc-connect-mic-button:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 连麦按钮字体色 + 'pc-connect-mic-button:text-color': $--color-white, + // 连麦按钮高亮字体色 + 'pc-connect-mic-button-highlight:text-color': $--color-white, + // 连麦中图标背景图 + 'pc-connect-mic-applying:icon-image': './imgs/pws-pc-connect-mic.gif', + + // 回放列表播放中按钮背景色 + 'pc-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), +); diff --git a/src/skins/red/config.scss b/src/skins/red/config.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2135df8fc7d53e586d0667c1e22586bb313eae2 --- /dev/null +++ b/src/skins/red/config.scss @@ -0,0 +1,7 @@ +@import '../_common/functions.scss'; +@import './config-common.scss'; +@import './config-pc.scss'; +@import './config-mobile.scss'; +@import './config-iar.scss'; + +$--skin-configs: generate-skin-setup-configs(); diff --git a/src/skins/red/imgs/pws-icon-more.png b/src/skins/red/imgs/pws-icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..52b15f454c14a10fb8ddce948263649053987419 Binary files /dev/null and b/src/skins/red/imgs/pws-icon-more.png differ diff --git a/src/skins/red/imgs/pws-pc-connect-mic.gif b/src/skins/red/imgs/pws-pc-connect-mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..89c70d3ac6943bb1cc1fc6b398425036a79c5071 Binary files /dev/null and b/src/skins/red/imgs/pws-pc-connect-mic.gif differ diff --git a/src/skins/red/imgs/pws-pc-watch-bg.png b/src/skins/red/imgs/pws-pc-watch-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..caf427e0e60f478c3d7d89c086930eb26ef661ab Binary files /dev/null and b/src/skins/red/imgs/pws-pc-watch-bg.png differ diff --git a/src/skins/red/imgs/pws-product-icon-recruitment.png b/src/skins/red/imgs/pws-product-icon-recruitment.png new file mode 100644 index 0000000000000000000000000000000000000000..f5950ab2bcd011fe8c031ec6d0c45ef801ff228c Binary files /dev/null and b/src/skins/red/imgs/pws-product-icon-recruitment.png differ diff --git a/src/skins/red/imgs/pws-product-icon.png b/src/skins/red/imgs/pws-product-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1afa080773f435fdb911ed63a2339956ab362fe5 Binary files /dev/null and b/src/skins/red/imgs/pws-product-icon.png differ diff --git a/src/skins/red/imgs/pws-product-shop-car.png b/src/skins/red/imgs/pws-product-shop-car.png new file mode 100644 index 0000000000000000000000000000000000000000..cbb64ea1f3f8cac9b972a4972d0075f7d2963b7e Binary files /dev/null and b/src/skins/red/imgs/pws-product-shop-car.png differ diff --git a/src/skins/red/imgs/pws-qa-entry.png b/src/skins/red/imgs/pws-qa-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..834623c50b824f14021b442872732905b0ce4422 Binary files /dev/null and b/src/skins/red/imgs/pws-qa-entry.png differ diff --git a/src/skins/red/imgs/pws-qa-no-data-icon.png b/src/skins/red/imgs/pws-qa-no-data-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8cf9581b90ed3db35e8d50a02908e74bc07e099f Binary files /dev/null and b/src/skins/red/imgs/pws-qa-no-data-icon.png differ diff --git a/src/skins/red/imgs/pws-zan.png b/src/skins/red/imgs/pws-zan.png new file mode 100644 index 0000000000000000000000000000000000000000..53f9a7d1e1df78b6929d24fa983fefe43dc7ec40 Binary files /dev/null and b/src/skins/red/imgs/pws-zan.png differ diff --git a/src/skins/use-page-skin.ts b/src/skins/use-page-skin.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a80e45872bc6774269309594f09cd90b47394a1 --- /dev/null +++ b/src/skins/use-page-skin.ts @@ -0,0 +1,152 @@ +import { paramGetter } from '@/hooks/core/use-query-params'; +import { isMobile } from '@/assets/utils/browser'; +import { useChannelStore } from '@/store/use-channel-store'; +import { usePageStore } from '@/store/use-page-store'; +import { ChannelWatchLayout, ChannelWatchPageSkin } from '@polyv/live-watch-sdk'; +import { $ } from '@just4/dom'; +import { watch } from 'vue'; + +/** + * 页面皮肤 hook + */ +export const usePageSkin = () => { + const channelStore = useChannelStore(); + const pageStore = usePageStore(); + + /** + * 初始化观看页多皮肤 + */ + async function initPageSkin(): Promise { + const watchLayout = channelStore.channelDetail?.channelInfo.watchLayout; + + let pageSkin = channelStore.channelDetail?.theme.newPageSkin; + + // 在非普通直播频道下只加载黑色皮肤 + if (watchLayout !== ChannelWatchLayout.Normal) { + pageSkin = ChannelWatchPageSkin.Black; + } + + const querySkin = paramGetter.skin(); + if (querySkin) { + pageSkin = querySkin; + } + + return setPageSkin(pageSkin); + } + + /** + * 设置页面皮肤 + * @param pageSkin 页面皮肤 + */ + async function setPageSkin(pageSkin: ChannelWatchPageSkin | undefined): Promise { + pageStore.$patch({ + pageSkin, + }); + + return importPageSkin(); + } + + /** + * 加载观看页多皮肤 + */ + async function importPageSkin(): Promise { + const pageSkin = pageStore.pageSkin; + + if (!pageSkin) { + return; + } + + if (isMobile) { + await importMobilePageSkin(pageSkin); + } else { + await importPcPageSkin(pageSkin); + } + } + + /** + * 加载 PC 端观看页多皮肤 + */ + async function importPcPageSkin(pageSkin: ChannelWatchPageSkin): Promise { + switch (pageSkin) { + case ChannelWatchPageSkin.Red: + await import(/* webpackChunkName: "skin-red-pc" */ '@/skins/red/_red-pc.scss'); + break; + case ChannelWatchPageSkin.Blue: + await import(/* webpackChunkName: "skin-blue-pc" */ '@/skins/blue/_blue-pc.scss'); + break; + case ChannelWatchPageSkin.Green: + await import(/* webpackChunkName: "skin-green-pc" */ '@/skins/green/_green-pc.scss'); + break; + case ChannelWatchPageSkin.White: + await import(/* webpackChunkName: "skin-white-pc" */ '@/skins/white/_white-pc.scss'); + break; + case ChannelWatchPageSkin.Golden: + await import(/* webpackChunkName: "skin-golden-pc" */ '@/skins/golden/_golden-pc.scss'); + break; + default: + await import(/* webpackChunkName: "skin-black-pc" */ '@/skins/black/_black-pc.scss'); + break; + } + } + + /** + * 加载 Mobile 端观看页多皮肤 + */ + async function importMobilePageSkin(pageSkin: ChannelWatchPageSkin): Promise { + switch (pageSkin) { + case ChannelWatchPageSkin.Red: + await import(/* webpackChunkName: "skin-red-mobile" */ '@/skins/red/_red-mobile.scss'); + break; + case ChannelWatchPageSkin.Blue: + await import(/* webpackChunkName: "skin-blue-mobile" */ '@/skins/blue/_blue-mobile.scss'); + break; + case ChannelWatchPageSkin.Green: + await import( + /* webpackChunkName: "skin-green-mobile" */ '@/skins/green/_green-mobile.scss' + ); + break; + case ChannelWatchPageSkin.White: + await import( + /* webpackChunkName: "skin-white-mobile" */ '@/skins/white/_white-mobile.scss' + ); + break; + case ChannelWatchPageSkin.Golden: + await import( + /* webpackChunkName: "skin-golden-mobile" */ '@/skins/golden/_golden-mobile.scss' + ); + break; + default: + await import( + /* webpackChunkName: "skin-black-mobile" */ '@/skins/black/_black-mobile.scss' + ); + break; + } + } + + // 设置页面的 class + watch( + () => pageStore.pageSkin, + (pageSkin, oldPageSkin) => { + const skinClassPrefix = 'pws-skin-'; + const $document = $(document.documentElement); + + if (oldPageSkin) { + const oldPageSkinClass = `${skinClassPrefix}${oldPageSkin}`; + if ($document.hasClass(oldPageSkinClass)) { + $document.removeClass(oldPageSkinClass); + } + } + + if (pageSkin) { + const pageSkinClass = `${skinClassPrefix}${pageSkin}`; + $document.addClass(pageSkinClass); + } + }, + ); + + return { + initPageSkin, + setPageSkin, + importPageSkin, + }; +}; diff --git a/src/skins/white/_white-mobile.scss b/src/skins/white/_white-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..87c09f945a1836f57dd951bb4b17cc5963a48285 --- /dev/null +++ b/src/skins/white/_white-mobile.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-mobile-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-mobile-style($--skin-configs); diff --git a/src/skins/white/_white-pc.scss b/src/skins/white/_white-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..8a13764923fe827d4013ff68666fb5afd7f37d7d --- /dev/null +++ b/src/skins/white/_white-pc.scss @@ -0,0 +1,4 @@ +@import '../_common/basic-pc-style.scss'; +@import './config.scss'; + +@include generate-skin-basic-pc-style($--skin-configs); diff --git a/src/skins/white/config-common.scss b/src/skins/white/config-common.scss new file mode 100644 index 0000000000000000000000000000000000000000..fe2b5ebc9044457a4304475ac8a34f20f30c8761 --- /dev/null +++ b/src/skins/white/config-common.scss @@ -0,0 +1,244 @@ +// 主题名称 +$--skin-type: 'white'; + +// 主题色 +$--skin-primary-color: #3082FE; +// 次要主题色 +$--skin-sub-primary-color: #498CF1; + +// 皮肤基础配置 +$--skin-basic-configs: ( + // 主题名称 + 'skin-type': $--skin-type, + + // 主题色 + 'primary-color': $--skin-primary-color, + // 次要主题色 + 'sub-primary-color': $--skin-sub-primary-color, + // #458ffe + 'primary-light-1': get-opacity-color($--skin-primary-color, 1), + // #599bfe + 'primary-light-2': get-opacity-color($--skin-primary-color, 2), + // #6ea8fe + 'primary-light-3': get-opacity-color($--skin-primary-color, 3), + // #83b4fe + 'primary-light-4': get-opacity-color($--skin-primary-color, 4), + // #98c1ff + 'primary-light-5': get-opacity-color($--skin-primary-color, 5), + // #accdff + 'primary-light-6': get-opacity-color($--skin-primary-color, 6), + // #c1daff + 'primary-light-7': get-opacity-color($--skin-primary-color, 7), + // #d6e6ff + 'primary-light-8': get-opacity-color($--skin-primary-color, 8), + // #eaf3ff + 'primary-light-9': get-opacity-color($--skin-primary-color, 9), + + // ---------- 背景色 ---------- // + // 主要背景色(以侧边栏主体背景色定义) + 'main-bg-color': #F4F4F4, + // 次要背景色(以侧边栏头部背景色定义) + 'sub-bg-color': $--color-white, + // 面板背景色 + 'panel-bg-color': #ededed, + // 禁用背景色 + 'disabled-bg-color': #e9ecee, + + // ---------- 字体色 ---------- // + // 正文字体色 + 'main-text-color': #666666, + // 正文高亮字体色 + 'main-highlight-text-color': $--skin-primary-color, + // 描述字体色 + 'describe-text-color': rgba(#666666, 0.6), + // 禁用字体色 + 'disabled-text-color': #9e9e9e, + // 面板字体色 + 'panel-text-color': #666666, + // 面板高亮字体色 + 'panel-highlight-text-color': $--skin-primary-color, + + // ---------- 边框色 ---------- // + 'main-border-color': rgba(#FFE5D9, 0.1), + + // ---------- 滚动条 ---------- // + 'panel-scrollbar-color': rgba(#666666, 0.2), +); + +// 皮肤基础组件配置 +$--skin-component-configs: ( + // 主要 tab 头部背景色 + 'main-tab-header:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部字体色 + 'main-tab-header:text-color': #666666, + // 主要 tab 头部 hover 字体色 + 'main-tab-header--hover:text-color': #666666, + // 主要 tab 头部激活字体色 + 'main-tab-header--active:text-color': #666666, + // 主要 tab 头部激活选项线条背景色 + 'main-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主要 tab 头部阴影开始颜色 + 'main-tab-header-shadow-start:color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 主要 tab 头部阴影结束颜色 + 'main-tab-header-shadow-end:color': rgba(62, 62, 78, 0), + // 主要 tab 头部红点颜色 + 'main-tab-header-reminder:color': #f24453, + // 主要 tab 主体背景色 + 'main-tab-body:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 主要 tab 主体滚动栏颜色 + 'main-tab-body:scrollbar-color': rgba(#666666, 0.2), + + // 常规 tab 激活项线条背景色 + 'normal-tab-header-line--active:bg-color': map.get($--skin-basic-configs, 'primary-color'), + + // 主题按钮背景色 + 'primary-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 主题按钮字体色 + 'primary-button:text-color': $--color-white, + // 主题按钮 hover 背景色 + 'primary-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-light-2'), + // 主题按钮 hover 字体色 + 'primary-button--hover:text-color': $--color-white, + // 主题按钮 active 背景色 + 'primary-button--active:bg-color': map.get($--skin-basic-configs, 'primary-light-1'), + // 主题按钮 active 字体色 + 'primary-button--active:text-color': $--color-white, + // 主题按钮 disabled 背景色 + 'primary-button--disabled:bg-color': map.get($--skin-basic-configs, 'disabled-bg-color'), + // 主题按钮 disabled 字体色 + 'primary-button--disabled:text-color': map.get($--skin-basic-configs, 'disabled-text-color'), + + // 侧边栏按钮背景色 + 'aside-menu-button:bg-color': rgba($--color-white, 0.6), + // 侧边栏按钮字体则 + 'aside-menu-button:text-color': #333333, + // 侧边栏按钮 hover 背景色 + 'aside-menu-button--hover:bg-color': rgba($--color-white, 0.8), + // 侧边栏按钮 active 背景色 + 'aside-menu-button--active:bg-color': rgba($--color-white, 0.7), + + // pc 端直播状态标签颜色 + 'pc-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #269eff, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #269eff, + 'stop' #f06e6e, + ), + // mobile 端直播状态标签颜色 + 'mobile-live-status-tag:color': ( + 'unStart' rgba(#ffffff, .6), + 'waiting' #269eff, + 'live' #f06e6e, + 'end' rgba(#ffffff, .6), + 'playback' #269eff, + 'stop' #f06e6e, + ), +); + +// 皮肤公用页面配置 +$--skin-page-common-configs: ( + // 聊天消息昵称字体色 + 'chat-msg-user-nickname:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份昵称字体色 + 'chat-msg-user-special-nickname:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息用户头衔背景色 + 'chat-msg-user-actor:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天消息用户头衔字体色 + 'chat-msg-user-actor:text-color': $--color-white, + // 聊天消息用户设置昵称图标字体色 + 'chat-msg-user-set-nick-icon:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息时间字体色 + 'chat-msg-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 聊天提示文案字体色 + 'chat-msg-list-tips:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + + // 聊天消息气泡背景色 + 'chat-msg-bubble:bg-color': $--color-white, + // 聊天消息气泡字体色 + 'chat-msg-bubble:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息特殊身份气泡背景色 + 'chat-msg-bubble-special:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息特殊身份气泡字体色 + 'chat-msg-bubble-special:text-color': $--color-white, + + // 聊天消息回复内容字体色 + 'chat-msg-quote-content:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息回复内容边框色 + 'chat-msg-quote-content:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:text-color': rgba($--color-white, 0.8), + // 聊天消息特殊身份回复内容字体色 + 'chat-msg-special-quote-content:border-color': rgba($--color-white, 0.1), + // 聊天消息回复按钮字体色 + 'chat-msg-quote-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天回复引用背景色 + 'chat-quote-msg:bg-color': #FAFAFA, + // 聊天回复引用字体色 + 'chat-quote-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 聊天消息翻译按钮字体色 + 'chat-msg-translate-button:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 聊天消息翻译按钮 hover 字体色 + 'chat-msg-translate-button--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 聊天消息翻译成功提示字体色 + 'chat-msg-translate-finish:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 聊天消息特殊身份翻译成功提示字体色 + 'chat-msg-translate-finish-special:text-color': rgba($--color-white, 0.8), + + // 聊天室更多消息按钮背景色 + 'chat-msg-more-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮字体色 + 'chat-msg-more-button:text-color': #ffffff, + // 聊天室更多消息按钮 hover 背景色 + 'chat-msg-more-button--hover:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 聊天室更多消息按钮 hover 字体色 + 'chat-msg-more-button--hover:text-color': #ffffff, + + // 打赏消息字体色 + 'chat-reward-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 自定义消息字体色 + 'chat-customer-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 红包领取消息背景色 + 'chat-redpaper-receive-msg:bg-color': #a9d2ff, + // 红包领取消息字体色 + 'chat-redpaper-receive-msg:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 系统消息字体色 + 'chat-system-msg:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 章节字体色 + 'chapter-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 章节高亮字体色 + 'chapter-item--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 章节边框色 + 'chapter-item:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + + // 成员列表字体色 + 'member-list-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 成员列表特殊身份字体色 + 'member-list-item-special:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 点赞按钮背景图 + 'like-button:bg-image': './imgs/pws-zan.png', + // 点赞数量字体色 + 'like-count-number:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 点赞数量背景色 + 'like-count-number:bg-color': rgba(map.get($--skin-basic-configs, 'main-bg-color'), 0.8), + + // 页面广告背景色 + 'page-advert:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 页面广告字体色 + 'page-advert:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 页面广告箭头字体色 + 'page-advert-arrow:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 互动二次入口按钮背景色 + 'interactive-entrance-button:bg-color': rgba($--color-white, 0.9), + // 互动二次入口按钮 hover 背景色 + 'interactive-entrance-button--hover:bg-color': rgba($--color-white, 0.8), + // 互动二次入口按钮字体色 + 'interactive-entrance-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 互动二次入口箭头字体色 + 'interactive-entrance-arrow:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.4), +); diff --git a/src/skins/white/config-iar.scss b/src/skins/white/config-iar.scss new file mode 100644 index 0000000000000000000000000000000000000000..06c84b334c763d3a0d64fca6f300f4fe52cb4272 --- /dev/null +++ b/src/skins/white/config-iar.scss @@ -0,0 +1,111 @@ +@import './config-common.scss'; +@import './config-pc.scss'; + +// 皮肤互动功能公用配置 +$--skin-iar-common-configs: ( + // 挂件字体色 + 'iar-pendant:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 商品库内容背景色 + 'iar-product-list-content:bg-color': #fefefe, + // 商品库图标图片 + 'iar-product-list-shop:icon-image': './imgs/pws-product-icon.png', + // 商品库图标图片(职位) + 'iar-product-list-recruitment:icon-image': './imgs/pws-product-icon-recruitment.png', + // 商品库标签背景色 + 'iar-product-list-label:bg-color': rgba(#333333, 0.1), + // 商品库标签字体色 + 'iar-product-list-label:text-color': #333333, + // 商品库商品名称序号背景色 + 'iar-product-list-name-number:bg-color': rgba($--color-black, 0.35), + // 商品库空状态图标图片 + 'iar-product-list-empty:icon-image': './imgs/pws-product-shop-car.png', + // 商品库商品下边框颜色 + 'iar-product-list-item:border-color': rgba(#666666, 0.1), + // 商品库商品名称字体色 + 'iar-product-list-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 置顶公告背景色 + 'iar-bulletin-top-banner:bg-color': #F3F7FF, + // 置顶公告字体色 + 'iar-bulletin-top-banner:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 问答背景色 + 'iar-qa-body:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层背景色 + 'iar-qa-filter:bg-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层边框色 + 'iar-qa-filter:border-color': map.get($--skin-component-configs, 'main-tab-body:bg-color'), + // 问答过滤层文案字体色 + 'iar-qa-filter-desc:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 问答选择字体色 + 'iar-qa-select:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答选择箭头边框色 + 'iar-qa-select:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 问答节点背景色 + 'iar-qa-item:bg-color': $--color-white, + // 问答节点内容字体色 + 'iar-qa-item-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答节点信息字体色 + 'iar-qa-item-info:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 问答节点更多字体色 + 'iar-qa-item-more:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 问答节点边框色 + 'iar-qa-item:border-color': #f4f4f4, + // 问答新消息按钮背景色 + 'iar-qa-new-msg-button:bg-color': rgba(map.get($--skin-basic-configs, 'primary-color'), 0.85) , + // 问答新消息按钮字体色 + 'iar-qa-new-msg-button:text-color': $--color-white, + // 问答没有更多文案字体色 + 'iar-qa-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); + +// 皮肤互动功能 PC 配置 +$--skin-iar-pc-configs: ( + // 问答选择选项背景色 + 'iar-pc-qa-select-option:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答选择选项 hover 背景色 + 'iar-pc-qa-select-option--hover:bg-color': rgba($--color-white, 0.1), + // 问答选择选项字体色 + 'iar-pc-qa-select-option:text-color': rgba(map.get($--skin-component-configs, 'main-tab-header:text-color'), 0.8), + // 问答选择选项激活字体色 + 'iar-pc-qa-select-option--active:text-color': map.get($--skin-component-configs, 'main-tab-header:text-color'), + // 问答选择选项边框色 + 'iar-pc-qa-select-option:border-color': rgba($--color-white, 0.1), + // 问答表情选择图标 + 'iar-pc-qa-ask-emotion:text-color': map.get($--skin-basic-configs, 'main-text-color'), + 'iar-pc-qa-ask-emotion:background-filter': none, + + // 中奖记录弹层文字颜色 + 'iar-pc-lottery-record-msg:text-color': #666, +); + +// 皮肤互动功能 Mobile 配置 +$--skin-iar-mobile-configs: ( + // 问答空数据的图标图片 + 'iar-mobile-qa-no-data:icon-image': './imgs/pws-qa-no-data-icon.png', + // 问答消息发送入口图片 + 'iar-mobile-qa-ask-entry:icon-image': './imgs/pws-qa-entry.png', + // 问答输入框外层背景色 + 'iar-mobile-qa-input-wrap:bg-color': map.get($--skin-component-configs, 'main-tab-header:bg-color'), + // 问答输入框背景色 + 'iar-mobile-qa-input:bg-color': rgba($--color-black, 0.05), + // 问答输入框字体色 + 'iar-mobile-qa-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 问答表情选择图标字体色 + 'iar-mobile-qa-ask-emotion:text-color': map.get($--skin-basic-configs, 'main-text-color'), + 'iar-mobile-qa-ask-emotion:background-filter': none, + // 问答发送按钮字体色 + 'iar-mobile-qa-ask-send-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 图文直播头部字体色 + 'iar-mobile-tuwen-live-header:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.8), + // 图文直播名称字体色 + 'iar-mobile-tuwen-live-content-name:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 图文直播时间字体色 + 'iar-mobile-tuwen-live-content-time:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 图文直播内容字体色 + 'iar-mobile-tuwen-live-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 图文直播暂无更多字体色 + 'iar-mobile-tuwen-live-no-more:text-color': map.get($--skin-basic-configs, 'describe-text-color'), +); diff --git a/src/skins/white/config-mobile.scss b/src/skins/white/config-mobile.scss new file mode 100644 index 0000000000000000000000000000000000000000..72d404b5a32880451396b8ead3d9e5e1a4552b10 --- /dev/null +++ b/src/skins/white/config-mobile.scss @@ -0,0 +1,154 @@ +@import './config-common.scss'; + +// 皮肤 Mobile 端页面配置 +$--skin-page-mobile-configs: ( + // 富文本字体色 + 'mobile-rich-text-content:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 引导页背景色 + 'mobile-splash:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 引导页倒计时外层背景色 + 'mobile-splash-live-count-down-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 页脚字体色 + 'mobile-page-footer:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + + // 基础信息频道标题字体色 + 'mobile-basic-info-channel-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息开始时间字体色 + 'mobile-basic-info-start-time:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息页面浏览次数字体色 + 'mobile-basic-info-page-view:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息主持人字体色 + 'mobile-basic-info-publisher:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 基础信息点赞数字体色 + 'mobile-basic-info-like:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 倒计时方块背景色 + 'mobile-count-down-square-item:bg-color': #F4F4F4, + // 倒计时方块远点背景色 + 'mobile-count-down-square-item-dot:bg-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 倒计时方块数字字体色 + 'mobile-count-down-square-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块文案字体色 + 'mobile-count-down-square-item-word:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 倒计时方块边框色 + 'mobile-count-down-square-item:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.1), + + // 直播预约按钮背景色 + 'mobile-live-booking-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + + // 关注按钮背景色 + 'mobile-follow-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 关注按钮字体颜色 + 'mobile-follow-button:text-color': $--color-white, + + // 聊天消息输入框背景色 + 'mobile-msg-input:bg-color': rgba($--color-white, 0.9), + // 聊天消息输入框字体色 + 'mobile-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'mobile-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'mobile-msg-input:scrollbar-color': rgba(21, 113, 208, 0.2), + // 聊天消息输入框图标字体色 + 'mobile-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + + // 聊天消息发送按钮字体颜色 + 'mobile-msg-send-button:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 表情选择面板背景色 + 'mobile-emotion-panel:bg-color': #EDEDED, + // 表情选择面板下的发送按钮字体色 + 'mobile-emotion-panel-send-button:bg-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 表情选择面板下的输入框背景色 + 'mobile-emotion-panel-msg-input:bg-color': $--color-white, + // 表情选择面板下的输入框字体色 + 'mobile-emotion-panel-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 表情选择面板下的输入框占位符字体色 + 'mobile-emotion-panel-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 表情选择面板下的输入框图标字体色 + 'mobile-emotion-panel-msg-input-suffix-icon:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 表情选择类型选择背景色 + 'mobile-emotion-panel-type:bg-color': rgba($--color-white, 0.4), + // 表情选择类型选择选中背景色 + 'mobile-emotion-panel-type--active:bg-color': rgba($--color-white, 0.8), + // 表情选择类型选择字体色 + 'mobile-emotion-panel-type:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 表情选择类型选择选中字体色 + 'mobile-emotion-panel-type--active:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 表情选择删除按钮背景色 + 'mobile-emotion-panel-del-button:bg-color': rgba($--color-white, 0.4), + // 表情选择删除按钮字体色 + 'mobile-emotion-panel-del-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 更多按钮背景色 + 'mobile-more-button:bg-color': rgba($--color-white, 0.9), + // 更多按钮图标图片 + 'mobile-more-button:icon-image': './imgs/pws-icon-more.png', + // 更多面板背景色 + 'mobile-more-panel:bg-color': #EDEDED, + // 更多面板字体色 + 'mobile-more-panel:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 打赏按钮背景色 + 'mobile-donate-button:bg-color': rgba($--color-white, 0.9), + // 打赏面板背景色 + 'mobile-donate-panel:bg-color': #EDEDED, + // 打赏面板标题字体色 + 'mobile-donate-panel-header-title:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板返回字体色 + 'mobile-donate-panel-header-close:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏礼物选中背景色 + 'mobile-donate-good--active:bg-color': $--color-white, + // 打赏礼物选中边框色 + 'mobile-donate-good--active:border-color': rgba($--color-black, 0.3), + // 打赏礼物道具名称字体色 + 'mobile-donate-good-name:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏礼物道具价格字体色 + 'mobile-donate-good-price:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.5), + // 打赏切换指示点颜色 + 'mobile-donate-indicator:bg-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.4), + // 打赏切换指示点选中颜色 + 'mobile-donate-indicator--active:bg-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板选项背景色 + 'mobile-donate-panel-option:bg-color': $--color-white, + // 打赏面板选项字体色 + 'mobile-donate-panel-option:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 打赏面板选项选中背景色 + 'mobile-donate-panel-option--active:bg-color': $--color-white, + // 打赏面板选项选中字体色 + 'mobile-donate-panel-option--active:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板选项选中边框色 + 'mobile-donate-panel-option--active:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.3), + // 打赏面板发送按钮背景色 + 'mobile-donate-panel-send-button:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 打赏面板发送按钮字体色 + 'mobile-donate-panel-send-button:text-color': $--color-white, + // 打赏面板剩余积分文本字体色 + 'mobile-donate-panel-point:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏面板剩余积分数值字体色 + 'mobile-donate-panel-point-count:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额字体色 + 'mobile-donate-panel-custom-cash:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + // 打赏自定义金额输入框背景色 + 'mobile-donate-custom-cash-input:bg-color': $--color-white, + // 打赏自定义金额输入框标题字体色 + 'mobile-donate-custom-cash-input-title:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.7), + // 打赏自定义金额输入框字体色 + 'mobile-donate-custom-cash-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 打赏自定义金额随机按钮字体色 + 'mobile-donate-custom-cash-random:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 回放列表播放中按钮背景色 + 'mobile-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), + // 回放列表字体色 + 'mobile-playback-list:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 回放列表选中时的字体色 + 'mobile-playback-list--active:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 多会场背景色 + 'mobile-multi-meeting:bg-color': #EDEDED, + // 多会场字体色 + 'mobile-multi-meeting:text-color': map.get($--skin-basic-configs, 'main-text-color'), +); diff --git a/src/skins/white/config-pc.scss b/src/skins/white/config-pc.scss new file mode 100644 index 0000000000000000000000000000000000000000..2bc73683b7a39d42d1749fb6f54803be62dfdf68 --- /dev/null +++ b/src/skins/white/config-pc.scss @@ -0,0 +1,110 @@ +@import './config-common.scss'; + +// 皮肤 PC 端页面配置 +$--skin-page-pc-configs: ( + // 观看页背景类型 + 'pc-watch-page:bg-type': 'image', + // 观看页背景颜色 + 'pc-watch-page:bg-color': '', + // 观看页背景图 + 'pc-watch-page:bg-image': './imgs/pws-pc-watch-bg.png', + + // 副屏收起字体颜色 + 'pc-sub-pack-up:text-color': #333, + + // 倒计时方块背景色 + 'pc-count-down-square-item:bg-color': #141518, + // 倒计时方块远点背景色 + 'pc-count-down-square-item-dot:bg-color': #141518, + // 倒计时方块数字字体色 + 'pc-count-down-square-item:text-color': $--color-white, + // 倒计时方块文案字体色 + 'pc-count-down-square-item-word:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 倒计时方块边框色 + 'pc-count-down-square-item:border-color': $--color-black, + + // 聊天消息底部输入框背景色 + 'pc-msg-bottom-input-wrap:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 聊天消息底部输入框节点字体色 + 'pc-msg-bottom-input-wrap-item:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息底部输入框节点 hover 字体色 + 'pc-msg-bottom-input-wrap-item--hover:text-color': map.get($--skin-basic-configs, 'main-highlight-text-color'), + + // 聊天消息输入框背景色 + 'pc-msg-input:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天消息输入框字体色 + 'pc-msg-input:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天消息输入框占位符字体色 + 'pc-msg-input-placeholder:text-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.6), + // 聊天消息输入框滚动条颜色 + 'pc-msg-input:scrollbar-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.2), + + // 聊天消息发送按钮背景色 + 'pc-msg-send-button:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 聊天消息发送按钮禁用状态下的背景色 + 'pc-msg-send-button--disabled:bg-color': rgba(map.get($--skin-basic-configs, 'sub-primary-color'), 0.6), + // 聊天消息发送按钮字体颜色 + 'pc-msg-send-button:text-color': $--color-white, + // 聊天消息发送按钮禁用状态下的字体颜色 + 'pc-msg-send-button--disabled:text-color': rgba($--color-white, 0.5), + + // 图片达到最大数量后的提示框背景色 + 'pws-pc-msg-input-popper__max-image-tips:bg-color': rgb(47, 130, 254), + // 图片达到最大数量后的提示框字体色 + 'pws-pc-msg-input-popper__max-image-tips:text-color': $--color-white, + + // 聊天输入框设置昵称占位背景色 + 'pc-set-nick-placeholder:bg-color': map.get($--skin-basic-configs, 'main-bg-color'), + // 聊天输入框设置昵称占位字体色 + 'pc-set-nick-placeholder:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 聊天输入框设置昵称占位高亮字体色 + 'pc-set-nick-placeholder-highlight:text-color': map.get($--skin-basic-configs, 'primary-color'), + + // 表情选择面板背景色 + 'pc-emotion-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 表情选择面板类型选择背景色 + 'pc-emotion-panel-select:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + + // 更多面板背景色 + 'pc-more-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 更多面板字体色 + 'pc-more-panel:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 更多面板字体高亮色 + 'pc-more-panel-text--hover:text-color': map.get($--skin-basic-configs, 'panel-highlight-text-color'), + // 更多面板选中框背景色 + 'pc-more-panel-checkbox--checked:bg-color': map.get($--skin-basic-configs, 'sub-primary-color'), + // 更多面板选中框图标色 + 'pc-more-panel-checkbox-icon--checked:text-color': map.get($--skin-basic-configs, 'panel-bg-color'), + + // 打赏面板背景色 + 'pc-donate-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 打赏礼物 hover 背景色 + 'pc-donate-good--hover:bg-color': $--color-white, + // 打赏礼物 hover 边框色 + 'pc-donate-good--hover:border-color': rgba(map.get($--skin-basic-configs, 'main-text-color'), 0.3), + // 打赏礼物道具名称字体色 + 'pc-donate-good-name:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + // 打赏礼物道具价格字体色 + 'pc-donate-good-price:text-color': map.get($--skin-basic-configs, 'main-text-color'), + + // 中奖记录面板背景色 + 'pc-lottery-record-panel:bg-color': map.get($--skin-basic-configs, 'panel-bg-color'), + // 中奖记录面板关闭图标字体色 + 'pc-lottery-record-close:text-color': map.get($--skin-basic-configs, 'panel-text-color'), + + // 连麦描述图标字体色 + 'pc-connect-mic-desc-icon:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦描述文案字体色 + 'pc-connect-mic-desc:text-color': map.get($--skin-basic-configs, 'describe-text-color'), + // 连麦按钮背景色 + 'pc-connect-mic-button:bg-color': map.get($--skin-basic-configs, 'sub-bg-color'), + // 连麦按钮字体色 + 'pc-connect-mic-button:text-color': map.get($--skin-basic-configs, 'main-text-color'), + // 连麦按钮高亮字体色 + 'pc-connect-mic-button-highlight:text-color': #ff5b5b, + // 连麦中图标背景图 + 'pc-connect-mic-applying:icon-image': './imgs/pws-pc-connect-mic.gif', + + // 回放列表播放中按钮背景色 + 'pc-playback-list-playing:bg-color': map.get($--skin-basic-configs, 'primary-color'), +); diff --git a/src/skins/white/config.scss b/src/skins/white/config.scss new file mode 100644 index 0000000000000000000000000000000000000000..c2135df8fc7d53e586d0667c1e22586bb313eae2 --- /dev/null +++ b/src/skins/white/config.scss @@ -0,0 +1,7 @@ +@import '../_common/functions.scss'; +@import './config-common.scss'; +@import './config-pc.scss'; +@import './config-mobile.scss'; +@import './config-iar.scss'; + +$--skin-configs: generate-skin-setup-configs(); diff --git a/src/skins/white/imgs/pws-icon-more.png b/src/skins/white/imgs/pws-icon-more.png new file mode 100644 index 0000000000000000000000000000000000000000..0233f9635c6494aaa9fb152a993b3695267b564f Binary files /dev/null and b/src/skins/white/imgs/pws-icon-more.png differ diff --git a/src/skins/white/imgs/pws-pc-connect-mic.gif b/src/skins/white/imgs/pws-pc-connect-mic.gif new file mode 100644 index 0000000000000000000000000000000000000000..07571e87781e59628f44c7f8da301de8b4b54c5f Binary files /dev/null and b/src/skins/white/imgs/pws-pc-connect-mic.gif differ diff --git a/src/skins/white/imgs/pws-pc-watch-bg.png b/src/skins/white/imgs/pws-pc-watch-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..a58a5b29886d36e6eb5e4c56095abecf5af5f6d6 Binary files /dev/null and b/src/skins/white/imgs/pws-pc-watch-bg.png differ diff --git a/src/skins/white/imgs/pws-product-icon-recruitment.png b/src/skins/white/imgs/pws-product-icon-recruitment.png new file mode 100644 index 0000000000000000000000000000000000000000..86a5a39e61943b070d4350f5edab5c9afe2567fc Binary files /dev/null and b/src/skins/white/imgs/pws-product-icon-recruitment.png differ diff --git a/src/skins/white/imgs/pws-product-icon.png b/src/skins/white/imgs/pws-product-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9eaad5cdf3dfacdeb5336c5cd48d8d58fdd900bc Binary files /dev/null and b/src/skins/white/imgs/pws-product-icon.png differ diff --git a/src/skins/white/imgs/pws-product-shop-car.png b/src/skins/white/imgs/pws-product-shop-car.png new file mode 100644 index 0000000000000000000000000000000000000000..55e4c53664b9b4eeec88398a9a40b3a5964bc248 Binary files /dev/null and b/src/skins/white/imgs/pws-product-shop-car.png differ diff --git a/src/skins/white/imgs/pws-qa-entry.png b/src/skins/white/imgs/pws-qa-entry.png new file mode 100644 index 0000000000000000000000000000000000000000..933e25877fdd5a051444673837780da38f0b4dfd Binary files /dev/null and b/src/skins/white/imgs/pws-qa-entry.png differ diff --git a/src/skins/white/imgs/pws-qa-no-data-icon.png b/src/skins/white/imgs/pws-qa-no-data-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..772d92d4cf35a99dd79ffaac0abfb958d0ad2a99 Binary files /dev/null and b/src/skins/white/imgs/pws-qa-no-data-icon.png differ diff --git a/src/skins/white/imgs/pws-zan.png b/src/skins/white/imgs/pws-zan.png new file mode 100644 index 0000000000000000000000000000000000000000..3b78fda0dbdfa7a7e9b6a0378a6676f2d5816f8f Binary files /dev/null and b/src/skins/white/imgs/pws-zan.png differ diff --git a/src/store/use-auth-store.ts b/src/store/use-auth-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..50c69b5d8cd3e82fc6a53cb8d895493549bedcb0 --- /dev/null +++ b/src/store/use-auth-store.ts @@ -0,0 +1,88 @@ +import { AuthInfoItem, AuthSettingItem, AuthSettingItemMap, AuthType } from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; + +export interface AuthStoreState { + /** 观看条件列表 */ + authSettings: ComputedRef; + /** 观看条件数量 */ + authSettingLength: ComputedRef; + /** 是否只有一个观看条件 */ + onlyOneAuth: ComputedRef; + /** 观看条件按钮列表 */ + authButtonList: ComputedRef; + /** 登记观看表单设置 */ + authInfoFields: ComputedRef; + /** 是否已进行观看条件授权 */ + isAuthorized: ComputedRef; +} + +export interface AuthStoreActions { + /** 是否存在某个观看条件 */ + hasAuth(authType: AuthType): boolean; + /** 获取某个观看条件 */ + getAuthSettingItem: (authType: T) => AuthSettingItemMap | undefined; +} + +export interface AuthStore extends AuthStoreState, AuthStoreActions {} + +/** 观看条件 store */ +export const useAuthStore = defineStore<'auth', AuthStore>('auth', () => { + const channelStore = useChannelStore(); + + /** 观看条件列表 */ + const authSettings = computed(() => { + return channelStore.channelDetail?.authSetting?.authSettings ?? []; + }); + + /** 观看条件数量 */ + const authSettingLength = computed(() => { + return unref(authSettings).length; + }); + + /** 是否只有一个观看条件 */ + const onlyOneAuth = computed(() => { + return unref(authSettingLength) === 1; + }); + + /** 观看条件按钮列表 */ + const authButtonList = computed(() => { + return unref(authSettings).filter(authItem => { + return authItem.authType !== AuthType.Direct; + }); + }); + + /** 登记观看表单设置 */ + const authInfoFields = computed(() => { + return channelStore.channelDetail?.authSetting?.authInfoFields ?? []; + }); + + /** 是否已进行观看条件授权 */ + const isAuthorized = computed(() => { + return !!channelStore.channelDetail?.viewerInfo; + }); + + /** 是否存在某个观看条件 */ + function hasAuth(authType: AuthType): boolean { + return unref(authSettings).findIndex(authItem => authItem.authType === authType) !== -1; + } + + /** 获取某个观看条件 */ + function getAuthSettingItem(authType: T): AuthSettingItemMap | undefined { + return unref(authSettings).find(authItem => authItem.authType === authType) as + | AuthSettingItemMap + | undefined; + } + + return { + authSettings, + authSettingLength, + onlyOneAuth, + authButtonList, + authInfoFields, + isAuthorized, + hasAuth, + getAuthSettingItem, + }; +}); diff --git a/src/store/use-channel-info-store.ts b/src/store/use-channel-info-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..66af12ebab38cec54017084c93535bb9313b0247 --- /dev/null +++ b/src/store/use-channel-info-store.ts @@ -0,0 +1,299 @@ +/** + * @file 频道信息 store + */ +import { DATE_FORMAT_SLASH } from '@/assets/constants/date-format'; +import { DEFAULT_CHANNEL_LOGO, DEFAULT_SPLASH_IMG } from '@/assets/constants/defaults'; +import { LangType } from '@/assets/lang/lang-enum'; +import { translate } from '@/assets/lang'; +import { + isAndroid, + isBaiduApp, + isIOS, + isQQBrowser, + isUCBrowser, + isWeixin, + isWorkWeixin, +} from '@/assets/utils/browser'; +import { resizeOSSImg } from '@/assets/utils/image'; +import { htmlStr2Text } from '@/assets/utils/string'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ChannelBasicInfo, LiveStatus, PageViewShowLocation, YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { formatDate } from '@utils-ts/date'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, Ref, ref, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; +import { useLangStore } from './use-lang-store'; + +export interface ChannelInfoStoreState { + /** 频道信息(中文) */ + cnChannelBasicInfo: Ref; + /** 频道信息(英文) */ + enChannelBasicInfo: Ref; + /** 频道信息(根据多语言返回) */ + channelInfo: ComputedRef; + /** 频道标题(根据多语言返回) */ + channelTitle: ComputedRef; + /** 频道主持人(根据多语言返回) */ + channelPublisher: ComputedRef; + /** 频道描述(根据多语言返回) */ + channelDescription: ComputedRef; + /** 频道描述是否为空 */ + channelDescriptionIsEmpty: ComputedRef; + /** 频道图标 */ + channelCoverImg: ComputedRef; + /** 频道引导页封面图 */ + channelSplashImg: ComputedRef; + /** PC 端观看页背景图 */ + pcWatchBackgroundImage: ComputedRef; + /** 观看浏览次数显示开关 */ + pvShowEnabled: ComputedRef; + /** 页面浏览次数 */ + pageViewCount: Ref; + /** 页面浏览次数显示位置 */ + pageViewCountLocation: ComputedRef; + /** 当前频道的点赞数 */ + likeCount: Ref; + /** 直播开始时间 */ + liveStartTime: ComputedRef; + /** 是否直播开始时间倒计时结束 */ + isLiveStartCountDownEnd: Ref; + /** 直播开始时间显示文案 */ + liveStartTimeText: ComputedRef; + /** 是否显示直播倒计时 */ + showLiveCountDown: ComputedRef; +} + +export interface ChannelInfoStoreAction { + /** 刷新频道信息 */ + syncChannelInfo: () => void; +} + +/** 频道信息 store */ +export interface ChannelInfoStore extends ChannelInfoStoreState, ChannelInfoStoreAction {} + +export const useChannelInfoStore = defineStore<'channelInfo', ChannelInfoStore>( + 'channelInfo', + () => { + const langStore = useLangStore(); + const channelStore = useChannelStore(); + + /** 频道信息(中文) */ + const cnChannelBasicInfo = ref({ + title: '', + publisher: '', + description: '', + }); + + /** 频道信息(英文) */ + const enChannelBasicInfo = ref({ + title: '', + publisher: '', + description: '', + }); + + /** 频道信息(根据多语言返回) */ + const channelInfo = computed(() => { + let info: ChannelBasicInfo = unref(cnChannelBasicInfo); + + if (langStore.englishSettingEnabled && langStore.currentLang !== LangType.Chinese) { + info = unref(enChannelBasicInfo); + } + + return info; + }); + + /** 频道标题(根据多语言返回) */ + const channelTitle = computed(() => unref(channelInfo).title); + + /** 频道主持人(根据多语言返回) */ + const channelPublisher = computed(() => { + let publisher = unref(channelInfo).publisher; + + if (!publisher && channelStore.isSeminarChannel) { + publisher = translate('seminar.host'); + } + + return publisher; + }); + + /** 频道描述(根据多语言返回) */ + const channelDescription = computed(() => unref(channelInfo).description); + + /** 频道描述是否为空 */ + const channelDescriptionIsEmpty = computed(() => { + const htmlContent = unref(channelDescription); + if (!htmlContent) { + return true; + } + // 具有以下标签的则不为空 + const labelName = ['img']; + for (let i = 0; i <= labelName.length; i++) { + if (htmlContent.indexOf(labelName[i]) !== -1) { + return false; + } + } + // 纯文本为空 + const htmlText = htmlStr2Text(htmlContent); + if (!htmlText) { + return true; + } + return false; + }); + + /** 频道图标 */ + const channelCoverImg = computed( + () => channelStore.channelDetail?.channelInfo?.coverImg ?? DEFAULT_CHANNEL_LOGO, + ); + + /** 频道引导页封面图 */ + const channelSplashImg = computed(() => { + let splashImg = channelStore.channelDetail?.channelInfo?.splashImg; + + // 由于引导页图片默认是竖状图,这里针对该默认图替换成横屏的 + if ( + !splashImg || + splashImg.replace(/^([a-z]+:)?\/\//, '') === + 'livestatic.videocc.net/assets/wimages/default_guide.png' + ) { + splashImg = DEFAULT_SPLASH_IMG; + } + + return resizeOSSImg(splashImg, 1280, 720); + }); + + /** PC 端观看页背景图 */ + const pcWatchBackgroundImage = computed(() => { + return channelStore.channelDetail?.theme.customPCWatchBackgroundImage ?? undefined; + }); + + /** 观看浏览次数显示开关 */ + const pvShowEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig.pvShowEnabled || YN.N), + ); + + /** 页面浏览次数 */ + const pageViewCount = ref(0); + + /** 页面浏览次数显示位置,默认显示在频道介绍中 */ + const pageViewCountLocation = computed(() => { + const mobilePvShowLocation = channelStore.channelDetail?.channelConfig.mobilePvShowLocation; + + // 显示在频道介绍中 + if (mobilePvShowLocation === PageViewShowLocation.Desc) { + return PageViewShowLocation.Desc; + } + + // 显示在播放器 + if ( + mobilePvShowLocation === PageViewShowLocation.Player && + channelStore.liveStatus === LiveStatus.Live && + ((isAndroid && (isWeixin || isWorkWeixin)) || + (isIOS && !isQQBrowser && !isUCBrowser && !isBaiduApp)) + ) { + return PageViewShowLocation.Player; + } + + return PageViewShowLocation.Desc; + }); + + /** 当前频道的点赞数 */ + const likeCount = ref(0); + + /** 直播开始时间 */ + const liveStartTime = computed(() => { + return channelStore.channelDetail?.channelInfo.startTime ?? undefined; + }); + + /** 是否直播开始时间倒计时结束 */ + const isLiveStartCountDownEnd = ref(false); + + /** 直播开始时间显示文案 */ + const liveStartTimeText = computed(() => { + const time = unref(liveStartTime); + if (!time) { + return '- -'; + } + + return formatDate(time, DATE_FORMAT_SLASH); + }); + + /** 直播倒计时是否显示 */ + const showLiveCountDown = computed(() => { + const liveStartTimeVal = unref(liveStartTime); + // 后台开关 + if (!ynToBool(channelStore.channelDetail?.channelConfig.countEnabled, YN.N)) { + return false; + } + + // 没有返回开始时间 + if (!liveStartTimeVal) { + return false; + } + + // 直播中和已结束直播 + if ([LiveStatus.Live, LiveStatus.End].includes(channelStore.liveStatus)) { + return false; + } + + // 倒计时结束 + if (isLiveStartCountDownEnd.value) { + return false; + } + + return Date.now() < liveStartTimeVal; + }); + + /** 刷新频道信息 */ + function syncChannelInfo(): void { + const watchCore = getWatchCore(); + const channelDetail = watchCore.channel.getChannelDetail(); + const channelBasicInfo = watchCore.channel.getChannelBasicInfo('zh_CN'); + + cnChannelBasicInfo.value = { + title: channelBasicInfo.title, + publisher: channelBasicInfo.publisher, + description: channelBasicInfo.description, + }; + + const channelBasicEnInfo = watchCore.channel.getChannelBasicInfo('en'); + enChannelBasicInfo.value = { + title: channelBasicEnInfo.title, + publisher: channelBasicEnInfo.publisher, + description: channelBasicEnInfo.description, + }; + + pageViewCount.value = watchCore.channel.getPageViewCount(); + if (channelDetail.watchData.likes > likeCount.value) { + likeCount.value = channelDetail.watchData.likes; + } + } + + return { + cnChannelBasicInfo, + enChannelBasicInfo, + channelInfo, + + channelTitle, + channelPublisher, + channelDescription, + channelDescriptionIsEmpty, + channelCoverImg, + channelSplashImg, + pcWatchBackgroundImage, + + pvShowEnabled, + pageViewCount, + pageViewCountLocation, + + likeCount, + + liveStartTime, + isLiveStartCountDownEnd, + liveStartTimeText, + showLiveCountDown, + + syncChannelInfo, + }; + }, +); diff --git a/src/store/use-channel-menu-store.ts b/src/store/use-channel-menu-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..8ba8e67fc1d9e911621d40208731d495df96c7a9 --- /dev/null +++ b/src/store/use-channel-menu-store.ts @@ -0,0 +1,283 @@ +/** + * @file 频道菜单 store + */ +import { computed, ComputedRef, Ref, ref, unref } from 'vue'; +import { defineStore } from 'pinia'; +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { ChannelMenuItem, ChannelMenuType } from '@polyv/live-watch-sdk'; +import cloneDeep from 'lodash-es/cloneDeep'; + +import { useInteractReceiveStore } from './use-interact-receive-store'; +import { useLangStore } from './use-lang-store'; +import { useWatchAppStore } from './use-watch-app-store'; + +export interface MenuDataType { + /** 是否创建该菜单节点 */ + visible: boolean; + /** 菜单标题 */ + label: string; +} + +/** 频道菜单 store */ +export interface ChannelMenuStoreState { + /** 中文频道菜单 */ + cnChannelMenus: Ref; + /** 英文频道菜单 */ + enChannelMenus: Ref; + /** 频道菜单(根据多语言返回) */ + channelMenus: ComputedRef; + /** 聊天室菜单信息 */ + chatMenuData: ComputedRef; + /** 商品库菜单信息 */ + productMenuData: ComputedRef; + /** 用户列表菜单信息 */ + membersMenuData: ComputedRef; + /** 提问菜单信息 */ + askMenuData: ComputedRef; + /** 问答菜单信息 */ + qaMenuData: ComputedRef; + /** 投票活动菜单信息 */ + voteActiveMenuData: ComputedRef; + /** 邀请榜菜单信息 */ + inviteRankMenuData: ComputedRef; + /** 微活动菜单信息 */ + microActivityMenuData: ComputedRef; +} + +export interface ChannelMenuStoreAction { + /** 同步频道菜单数据 */ + syncChannelMenus: () => void; + /** 插入菜单 */ + addChannelMenu: (menuData: ChannelMenuItem, onlyType?: boolean) => void; + /** 移除菜单 */ + removeChannelMenu: (menuId: string) => void; + /** 根据菜单类型获取菜单数据 */ + getMenuData: (menuType: ChannelMenuType) => ChannelMenuItem | undefined; + /** 根据菜单类型获取菜单 Tab 名称 */ + filterMenuTabLabel: (menuType: ChannelMenuType, label: string) => string; +} + +/** 频道菜单 store */ +export interface ChannelMenuStore extends ChannelMenuStoreState, ChannelMenuStoreAction {} + +export const useChannelMenuStore = defineStore<'channelMenu', ChannelMenuStore>( + 'channelMenu', + () => { + const watchAppStore = useWatchAppStore(); + const langStore = useLangStore(); + const interactReceiveStore = useInteractReceiveStore(); + + /** 中文频道菜单 */ + const cnChannelMenus = ref([]); + /** 英文频道菜单 */ + const enChannelMenus = ref([]); + + /** 频道菜单(根据多语言返回) */ + const channelMenus = computed(() => { + let menus = unref(cnChannelMenus); + + if (langStore.englishSettingEnabled && !langStore.isChineseLang) { + menus = unref(enChannelMenus); + } + + if (watchAppStore.isWatchBackUrl) { + menus = menus.filter(menu => menu.menuType === ChannelMenuType.Chat); + } + + return menus.map(menu => { + return { + ...menu, + name: filterMenuTabLabel(menu.menuType, menu.name), + }; + }); + }); + + /** 同步频道菜单 */ + function syncChannelMenus(): void { + const watchCore = getWatchCore(); + cnChannelMenus.value = cloneDeep(watchCore.menu.getChannelMenus('zh_CN')); + enChannelMenus.value = cloneDeep(watchCore.menu.getChannelMenus('en')); + + const productMenu = getMenuData(ChannelMenuType.Buy); + interactReceiveStore.productEnabled = !!productMenu; + } + + /** + * 插入菜单 + * @param menuData 菜单数据 + * @param onlyType 是否唯一的 menuType + */ + function addChannelMenu(menuData: ChannelMenuItem, onlyType = false): void { + const findMenuIndex = (menu: ChannelMenuItem) => { + if (onlyType && menu.menuType === menuData.menuType) { + return true; + } + return menu.menuId === menuData.menuId; + }; + + // 插入中文菜单 + const cnIndex = unref(cnChannelMenus).findIndex(findMenuIndex); + if (cnIndex === -1) { + const newCnMenus = [...unref(cnChannelMenus), menuData]; + cnChannelMenus.value = newCnMenus; + } + // 插入英文菜单 + const enIndex = unref(enChannelMenus).findIndex(findMenuIndex); + if (enIndex === -1) { + const newEnMenus = [...unref(enChannelMenus), menuData]; + enChannelMenus.value = newEnMenus; + } + } + + /** + * 移除菜单 + * @param menuId 菜单 id + */ + function removeChannelMenu(menuId: string): void { + // 移除中文菜单 + const newCnMenus = unref(cnChannelMenus).filter(menu => menu.menuId !== menuId); + cnChannelMenus.value = newCnMenus; + // 移除英文菜单 + const newEnMenus = unref(enChannelMenus).filter(menu => menu.menuId !== menuId); + enChannelMenus.value = newEnMenus; + } + + /** + * 根据菜单类型获取菜单数据 + * @param menuType 菜单类型 + */ + function getMenuData(menuType: ChannelMenuType): ChannelMenuItem | undefined { + const menus = unref(channelMenus); + const index = menus.findIndex(menu => menu.menuType === menuType); + return index === -1 ? undefined : menus[index]; + } + + /** 获取菜单栏 */ + function filterMenuTabLabel(menuType: ChannelMenuType, label: string): string { + type TranslateOptionItem = [translateText: string, ...cnTexts: string[]]; + + const translateOptions: Record = { + [ChannelMenuType.Desc]: [translate('menu.tabName.desc'), '直播介绍'], + [ChannelMenuType.Chat]: [translate('menu.tabName.chat'), '聊天', '互动聊天'], + [ChannelMenuType.Text]: [translate('menu.tabName.text'), '图文'], + [ChannelMenuType.Seat]: [translate('menu.tabName.seat'), '云席'], + [ChannelMenuType.Quiz]: [translate('menu.tabName.quiz'), '提问'], + [ChannelMenuType.Iframe]: [translate('menu.tabName.iframe'), '链接'], + [ChannelMenuType.Previous]: [translate('menu.tabName.previous'), '往期'], + [ChannelMenuType.Tuwen]: [translate('menu.tabName.tuwen'), '图文直播'], + [ChannelMenuType.QA]: [translate('menu.tabName.qa'), '问答'], + [ChannelMenuType.Buy]: [translate('menu.tabName.buy'), '边看边买'], + [ChannelMenuType.Invite]: [translate('menu.tabName.invite'), '邀请榜'], + [ChannelMenuType.Members]: [translate('menu.tabName.members'), '成员'], + [ChannelMenuType.Vote]: [translate('menu.tabName.vote'), '投票'], + [ChannelMenuType.MicroActivity]: [translate('menu.tabName.microActivity'), '微活动'], + }; + + const [translateText, ...cnTexts] = translateOptions[menuType]; + if (!label || (!langStore.isChineseLang && cnTexts.includes(label))) { + label = translateText; + } + + return label; + } + + /** 聊天室菜单信息 */ + const chatMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.Chat); + + return { + visible: true, + label: menuData?.name ?? translate('menu.tabName.chat'), + }; + }); + + /** 商品库菜单信息 */ + const productMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.Buy); + + return { + visible: !!menuData && interactReceiveStore.productEnabled, + label: menuData?.name ?? translate('menu.tabName.buy'), + }; + }); + + /** 用户列表菜单信息 */ + const membersMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.Members); + + return { + visible: !!menuData, + label: menuData?.name ?? translate('menu.tabName.members'), + }; + }); + + /** 提问菜单信息 */ + const askMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.Quiz); + + return { + visible: !!menuData, + label: menuData?.name ?? translate('menu.tabName.quiz'), + }; + }); + + /** 问答菜单信息 */ + const qaMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.QA); + + return { + visible: !!menuData, + label: menuData?.name ?? translate('menu.tabName.qa'), + }; + }); + + /** 投票活动菜单信息 */ + const voteActiveMenuData = computed(() => { + return { + visible: interactReceiveStore.voteActiveEnabled, + label: translate('menu.tabName.vote'), + }; + }); + + /** 邀请榜菜单信息 */ + const inviteRankMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.Invite); + + return { + visible: !!menuData, + label: menuData?.name ?? translate('menu.tabName.invite'), + }; + }); + + /** 微活动菜单信息 */ + const microActivityMenuData = computed(() => { + const menuData = getMenuData(ChannelMenuType.MicroActivity); + + return { + visible: !!menuData, + label: menuData?.name ?? translate('menu.tabName.microActivity'), + }; + }); + + return { + cnChannelMenus, + enChannelMenus, + channelMenus, + chatMenuData, + productMenuData, + membersMenuData, + askMenuData, + qaMenuData, + voteActiveMenuData, + inviteRankMenuData, + microActivityMenuData, + + addChannelMenu, + removeChannelMenu, + syncChannelMenus, + getMenuData, + filterMenuTabLabel, + }; + }, +); diff --git a/src/store/use-channel-store.ts b/src/store/use-channel-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..42968a65587175b2ac2e7de4d9a3750e9fa99dae --- /dev/null +++ b/src/store/use-channel-store.ts @@ -0,0 +1,189 @@ +/** + * @file 频道 store + */ +import { DEFAULT_VERIFY_QUERY_FREQUENCY } from '@/assets/constants/defaults'; +import { isMobile } from '@/assets/utils/browser'; +import { getWatchCore } from '@/core/watch-sdk'; +import { + ChannelDetail, + ChannelScene, + ChannelWatchLayout, + ChatMessageUser, + ChatUserTypes, + LiveStatus, + YN, + StreamType, + MarqueeData, +} from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; + +export interface ChannelStoreState { + /** 频道详情数据 */ + channelDetail: Ref; + /** 频道号 */ + channelId: ComputedRef; + /** 频道是否处于测试模式 */ + testModeStatus: Ref; + /** 频道直播状态 */ + liveStatus: Ref; + /** 频道直播状态(根据测试模式返回) */ + liveStatusByDebug: ComputedRef; + /** 当前最新的直播场次号 */ + currentSessionId: Ref; + /** 观看页地址 */ + watchUrl: ComputedRef; + /** 是否为普通直播频道 */ + isAloneChannel: ComputedRef; + /** 是否为三分屏频道 */ + isPptChannel: ComputedRef; + /** 是否为研讨会频道 */ + isSeminarChannel: ComputedRef; + /** 渠道 id */ + promoteId: Ref; + /** 当前频道是否为音频直播 */ + isOnlyAudioLive: ComputedRef; + /** 讲师信息 */ + teacherInfo: ComputedRef; + /** 观看页移动端布局 */ + mobileWatchLayout: ComputedRef; + /** 是否为竖屏观看页布局 */ + isPortraitWatchLayout: ComputedRef; + /** 当前直播推流类型 */ + currentStreamType: Ref; + /** 跑马灯数据 */ + marqueeData: Ref; + /** 唯一登录校验轮训频率,单位:秒 */ + channelVerifyQueryFrequency: Ref; +} + +export interface ChannelStoreActions { + /** 同步频道详情 */ + syncChannelDetail: () => void; + /** 同步频道 store */ + syncChannelStore: () => void; +} + +/** 频道 store */ +export interface ChannelStore extends ChannelStoreState, ChannelStoreActions {} + +export const useChannelStore = defineStore<'channel', ChannelStore>('channel', () => { + const channelDetail = ref(); + + /** 频道号 */ + const channelId = computed(() => `${unref(channelDetail)?.channelInfo.channelId || ''}`); + + /** 频道是否处于测试模式 */ + const testModeStatus = ref(false); + + /** 频道直播状态 */ + const liveStatus = ref(LiveStatus.End); + + /** 频道直播状态(根据测试模式返回) */ + const liveStatusByDebug = computed(() => { + // 测试模式为避免暴露直播信息,固定设成暂无直播,testModeStatus 类似于 pageWatch.isDebug,用于判断测试模式。 + if (unref(testModeStatus)) { + return LiveStatus.End; + } + return unref(liveStatus); + }); + + /** 当前直播场次号 */ + const currentSessionId = ref(''); + + /** 观看页地址 */ + const watchUrl = computed(() => unref(channelDetail)?.channelInfo?.watchCodeUrl || ''); + + /** 是否为普通直播频道 */ + const isAloneChannel = computed( + () => unref(channelDetail)?.channelInfo?.scene === ChannelScene.Alone, + ); + + /** 是否为三分屏频道 */ + const isPptChannel = computed( + () => unref(channelDetail)?.channelInfo?.scene === ChannelScene.Ppt, + ); + + /** 是否为研讨会频道 */ + const isSeminarChannel = computed( + () => unref(channelDetail)?.channelInfo?.scene === ChannelScene.Seminar, + ); + + /** 观看页移动端布局 */ + const mobileWatchLayout = computed(() => { + return channelDetail.value?.channelInfo.watchLayout || ChannelWatchLayout.Normal; + // return ChannelWatchLayout.Normal; + }); + + /** 是否为竖屏观看页布局 */ + const isPortraitWatchLayout = computed(() => { + return unref(mobileWatchLayout) === ChannelWatchLayout.Portrait && isMobile; + }); + + /** 渠道 id */ + const promoteId = ref(); + + /** 当前频道是否为音频直播 */ + const isOnlyAudioLive = computed(() => { + return ynToBool(unref(channelDetail)?.channelConfig.audioOnlyEnabled, YN.N); + }); + + /** 当前直播推流类型 */ + const currentStreamType = ref(); + + /** 讲师信息 */ + const teacherInfo = computed(() => { + const detailVal = unref(channelDetail); + return { + userId: unref(channelId), + nick: detailVal?.channelTeacher?.nickName || '', + pic: detailVal?.channelTeacher?.avatar || '', + userType: ChatUserTypes.Teacher, + actor: detailVal?.channelTeacher?.actor, + }; + }); + + /** 跑马灯数据 */ + const marqueeData = ref(); + + const channelVerifyQueryFrequency = ref(DEFAULT_VERIFY_QUERY_FREQUENCY); + + /** 同步频道详情 */ + function syncChannelDetail() { + const watchCore = getWatchCore(); + channelDetail.value = watchCore.channel.getChannelDetail(); + } + + function syncChannelStore(): void { + const watchCore = getWatchCore(); + testModeStatus.value = watchCore.channel.getTestModeStatus(); + liveStatus.value = watchCore.channel.getLiveStatus(); + currentSessionId.value = watchCore.channel.getCurrentSessionId(); + promoteId.value = watchCore.channel.getPromoteId(); + } + + return { + channelDetail, + syncChannelDetail, + syncChannelStore, + + channelId, + testModeStatus, + liveStatus, + liveStatusByDebug, + currentSessionId, + watchUrl, + isAloneChannel, + isPptChannel, + isSeminarChannel, + mobileWatchLayout, + isPortraitWatchLayout, + promoteId, + isOnlyAudioLive, + teacherInfo, + currentStreamType, + marqueeData, + channelVerifyQueryFrequency, + }; +}); diff --git a/src/store/use-chat-msg-store.ts b/src/store/use-chat-msg-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..37540d10df93d415980c6c8e17ec4325739a0529 --- /dev/null +++ b/src/store/use-chat-msg-store.ts @@ -0,0 +1,435 @@ +import { isSpecialUserType } from '@/assets/constants/special-user-types'; +import { isUndefined } from '@/assets/utils/types'; +import { ChatMessageUser, ChatMsgSource, ChatMsgType } from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { ref, Ref, unref } from 'vue'; +import { useChatStore } from './use-chat-store'; +import { useViewerStore } from './use-viewer-store'; + +export type HistoryDataPosition = 'header' | 'tail'; + +export interface ChatMsgStoreState { + /** 不显示的消息来源 */ + hideChatMsgSources: Ref; + /** 一批消息的数量 */ + batchCount: Ref; + /** 是否已加载完聊天历史消息 */ + historyEnd: Ref; + + /** 是否显示更多消息 */ + moreMsgVisible: Ref; + /** 显示更多消息 */ + showMoreMsg(): void; + /** 隐藏更多消息 */ + hideMoreMsg(): void; +} + +export interface ChatMsgStoreAction { + /** 根据 id 获取下标 */ + getRealIndexById(id: string): number | undefined; + /** 根据 id 设置下标 */ + setRealIndexById(id: string, index: number): void; + /** 根据 id 删除下表 */ + deleteRealIndexById(id: string): void; + /** 递增所有的 realIndex */ + increaseRealIndex(): void; + /** 获取服务端最新消息的下标 */ + getRealStartIndex(): number; + /** 设置服务端最新消息的下标 */ + setRealStartIndex(index: number): void; + /** 获取服务端最旧消息的下标 */ + getRealEndIndex(): number; + /** 设置服务端最旧消息的下标 */ + setRealEndIndex(index: number): void; + + /** 获取消息列表 */ + getHistoryData(): ChatMsgType[]; + /** 设置消息列表 */ + resetHistoryData(list: ChatMsgType[]): void; + /** 设置历史列表的消息对象 */ + setHistoryData(index: number, chatMsg: ChatMsgType): void; + /** 从 historyData 中寻找下标索引 */ + findHistoryIndex(id: string): number; + /** 根据 id 从 historyData 中寻找消息对象 */ + findHistoryById( + id: string, + msgSource?: ChatMsgSource, + ): T | undefined; + /** 根据 id 移除历史数据 */ + removeHistoryData(id: string): void; + /** 合并到消息列表 */ + concatHistoryData(data: ChatMsgType[], position: HistoryDataPosition): void; + + /** 合并一批 queue 数据到 historyData 中 */ + concatQueueToHistoryData(isAll?: boolean): ChatMsgType[]; + /** 获取消息等待队列 */ + getMsgQueue(): ChatMsgType[]; + /** 插入消息等待队列 */ + pushMsgQueue(chatMsg: ChatMsgType): void; + /** 设置等待队列 */ + resetMsgQueue(list: ChatMsgType[]): void; + + /** 判断聊天消息是否为服务端中存在的消息 */ + isChatServiceMsg(chatMsg: ChatMsgType): boolean; + /** 获取消息对象中的用户信息 */ + getChatMsgUser(chatMsg: ChatMsgType): ChatMessageUser | undefined; + /** 是否本地发送的消息 */ + isLocalMsg(chatMsg: ChatMsgType): boolean; + /** 是否自己发送到消息 */ + isSelfMsg(chatMsg: ChatMsgType): boolean; + /** 是否为特殊用户发送的消息 */ + isSpecialUserMsg(chatMsg: ChatMsgType): boolean; + + /** 重置聊天消息 store */ + resetChatMsgStore(): void; +} + +export interface ChatMsgStore extends ChatMsgStoreState, ChatMsgStoreAction {} + +/** + * 聊天消息 store + */ +export const useChatMsgStore = defineStore<'chatMsg', ChatMsgStore>('chatMsg', () => { + const chatStore = useChatStore(); + const viewerStore = useViewerStore(); + + /** 不显示的消息来源 */ + const hideChatMsgSources = ref([]); + + /** 一批消息的数量 */ + const batchCount = ref(20); + + /** 是否已加载完聊天历史消息 */ + const historyEnd = ref(false); + + /** 是否显示更多消息 */ + const moreMsgVisible = ref(false); + + /** 显示更多消息 */ + function showMoreMsg() { + moreMsgVisible.value = true; + } + + /** 隐藏更多消息 */ + function hideMoreMsg() { + moreMsgVisible.value = false; + } + + /** ---------- realIndex - start ---------- */ + /** 服务端聊天消息下标 */ + let msgRealIndex: UniversalParams = {}; + + /** 服务端最新消息的下标 */ + let realStartIndex = -1; + + /** 服务端最旧消息的下标 */ + let realEndIndex = -1; + + /** 根据 id 获取下标 */ + function getRealIndexById(id: string): number | undefined { + return msgRealIndex[id]; + } + + /** 根据 id 设置下标 */ + function setRealIndexById(id: string, index: number): void { + msgRealIndex[id] = index; + } + + /** 根据 id 删除下表 */ + function deleteRealIndexById(id: string): void { + delete msgRealIndex[id]; + } + + /** + * 递增所有的 realIndex + */ + function increaseRealIndex() { + realStartIndex += 1; + realEndIndex += 1; + const msgRealIndexVal = msgRealIndex as UniversalParams; + Object.keys(msgRealIndexVal).forEach(key => { + if (!isUndefined(msgRealIndexVal[key])) { + msgRealIndexVal[key] += 1; + } + }); + } + + /** 获取服务端最新消息的下标 */ + function getRealStartIndex(): number { + return realStartIndex; + } + + /** 设置服务端最新消息的下标 */ + function setRealStartIndex(index: number): void { + realStartIndex = index; + } + + /** 获取服务端最旧消息的下标 */ + function getRealEndIndex(): number { + return realEndIndex; + } + + /** 设置服务端最旧消息的下标 */ + function setRealEndIndex(index: number): void { + realEndIndex = index; + } + /** ---------- realIndex - end ---------- */ + + /** ---------- msgQueue - start ---------- */ + /** 等待插入的消息队列 */ + let msgQueue: ChatMsgType[] = []; + + /** 获取消息等待队列 */ + function getMsgQueue(): ChatMsgType[] { + return msgQueue; + } + + /** 插入消息等待队列 */ + function pushMsgQueue(chatMsg: ChatMsgType): void { + msgQueue.push(chatMsg); + } + + /** 设置等待队列 */ + function resetMsgQueue(list: ChatMsgType[]): void { + msgQueue = list; + } + /** ---------- msgQueue - end ---------- */ + + /** ---------- historyData - start ---------- */ + /** 完整的消息列表 */ + let historyData: ChatMsgType[] = []; + + /** 获取消息列表 */ + function getHistoryData(): ChatMsgType[] { + return historyData; + } + + /** 设置消息列表 */ + function resetHistoryData(list: ChatMsgType[]): void { + historyData = list; + } + + /** + * 设置历史列表的消息对象 + * @param index 下标 + * @param chatMsg 消息对象 + */ + function setHistoryData(index: number, chatMsg: ChatMsgType) { + historyData[index] = chatMsg; + } + + /** + * 从 historyData 中寻找下标索引 + * @param id 消息 id + */ + function findHistoryIndex(id: string): number { + return historyData.findIndex(chatMsg => chatMsg.id === id); + } + + /** + * 根据 id 从 historyData 中寻找消息对象 + * @param id 消息 id + * @param msgSource 消息类型 + */ + function findHistoryById( + id: string, + msgSource?: ChatMsgSource, + ): T | undefined { + const historyList = historyData; + const index = historyList.findIndex(chatMsg => { + if (!msgSource) { + return chatMsg.id === id; + } + return chatMsg.id === id && chatMsg.msgSource === msgSource; + }); + return historyList[index] as T; + } + + /** + * 根据 id 移除历史数据 + * @param id 消息 id + */ + function removeHistoryData(id: string) { + // 从 realIndex 记录中删除,并将目标 id 之后的消息递减 + const refRealIndex = msgRealIndex[id]; + if (!isUndefined(refRealIndex)) { + Object.keys(msgRealIndex).forEach(realKey => { + const realIndex = msgRealIndex[realKey]; + if (!isUndefined(realIndex) && realIndex > refRealIndex) { + msgRealIndex[realKey] = realIndex - 1; + } + }); + + deleteRealIndexById(id); + + const _realEndIndex = getRealEndIndex(); + if (_realEndIndex >= refRealIndex) { + setRealEndIndex(_realEndIndex - 1); + } + + const _realStartIndex = getRealStartIndex(); + if (_realStartIndex > refRealIndex) { + setRealStartIndex(_realStartIndex - 1); + } + } + + // 删除消息列表中的值 + const historyIndex = findHistoryIndex(id); + if (historyIndex !== -1) { + historyData.splice(historyIndex, 1); + } + } + + /** + * 合并到消息列表 + * @param data 合并到消息列表 + * @param position 位置 + */ + function concatHistoryData(data: ChatMsgType[], position: HistoryDataPosition) { + if (position === 'header') { + historyData = data.concat(historyData); + } else { + historyData = historyData.concat(data); + } + } + /** ---------- historyData - end ---------- */ + + /** + * 获取消息对象中的用户信息 + * @param chatMsg 消息对象 + */ + function getChatMsgUser(chatMsg: ChatMsgType): ChatMessageUser | undefined { + if ('user' in chatMsg) { + return chatMsg.user; + } + } + + /** + * 是否本地发送的消息 + * @param chatMsg 消息 + */ + function isLocalMsg(chatMsg: ChatMsgType): boolean { + if ('isLocal' in chatMsg) { + return !!chatMsg.isLocal; + } + return false; + } + + /** + * 是否自己发送到消息 + * @param chatMsg 消息 + */ + function isSelfMsg(chatMsg: ChatMsgType): boolean { + return getChatMsgUser(chatMsg)?.userId === viewerStore.viewerId; + } + + /** + * 是否为特殊用户发送的消息 + * @param chatMsg 消息 + */ + function isSpecialUserMsg(chatMsg: ChatMsgType): boolean { + const user = getChatMsgUser(chatMsg); + const userType = user?.userType; + if (!userType) return false; + return isSpecialUserType(userType); + } + + /** + * 合并 queue 数据到 historyData 中 + * @param isAll 是否将所有消息插入 + */ + function concatQueueToHistoryData(isAll = false): ChatMsgType[] { + const queueLength = msgQueue.length; + let spliceCount = unref(batchCount); + + if (isAll) { + spliceCount = queueLength; + } else if (queueLength / 2 > spliceCount) { + spliceCount = Math.round(queueLength / 2); + } + + const concatData = msgQueue.splice(0, spliceCount); + if (concatData.length) { + concatHistoryData(concatData, 'tail'); + } + return concatData; + } + + /** + * 判断聊天消息是否为服务端中存在的消息 + * @param chatMsg 聊天消息 + */ + function isChatServiceMsg(chatMsg: ChatMsgType): boolean { + let isServiceMsg = true; + + const msgSource = chatMsg.msgSource; + // 特殊角色消息 + const isSpecial = isSpecialUserMsg(chatMsg); + + // 系统消息、红包领取消息 + const otherSource = [ChatMsgSource.System, ChatMsgSource.RedpaperReceive]; + if (otherSource.includes(msgSource)) { + isServiceMsg = false; + } + + // 只看我和主持人 + if (chatStore.onlySpecialMsg && !isSpecial) { + isServiceMsg = false; + } + + return isServiceMsg; + } + + /** + * 重置聊天消息 store + */ + function resetChatMsgStore() { + historyEnd.value = false; + msgRealIndex = {}; + resetMsgQueue([]); + resetHistoryData([]); + setRealStartIndex(-1); + setRealEndIndex(-1); + hideMoreMsg(); + } + + return { + hideChatMsgSources, + batchCount, + historyEnd, + + moreMsgVisible, + showMoreMsg, + hideMoreMsg, + + getRealIndexById, + setRealIndexById, + deleteRealIndexById, + increaseRealIndex, + getRealStartIndex, + setRealStartIndex, + getRealEndIndex, + setRealEndIndex, + + getMsgQueue, + pushMsgQueue, + resetMsgQueue, + concatQueueToHistoryData, + + getHistoryData, + resetHistoryData, + findHistoryIndex, + setHistoryData, + findHistoryById, + removeHistoryData, + concatHistoryData, + + isLocalMsg, + getChatMsgUser, + isSelfMsg, + isSpecialUserMsg, + isChatServiceMsg, + + resetChatMsgStore, + }; +}); diff --git a/src/store/use-chat-store.ts b/src/store/use-chat-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..2caed2ee8c3cb6141717df405367cca3c2ec3523 --- /dev/null +++ b/src/store/use-chat-store.ts @@ -0,0 +1,256 @@ +/** + * @file 聊天室 store + */ + +import { translate } from '@/assets/lang'; +import { isMobile } from '@/assets/utils/browser'; +import { getWatchCore } from '@/core/watch-sdk'; +import { PageErrorType } from '@/app/layout/page-error/page-error-type'; +import { ChannelMenuType, YN, ChatMsgQuoteOriginType } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref, watch } from 'vue'; +import { useChannelMenuStore } from './use-channel-menu-store'; +import { useChannelStore } from './use-channel-store'; +import { useViewerStore } from './use-viewer-store'; +import { useWatchAppStore } from './use-watch-app-store'; + +export interface ChatStoreState { + /** 配置是否启用聊天列表 */ + chatMsgListEnabled: Ref; + /** 聊天室是否连接失败 */ + connectError: Ref; + /** 聊天室是否被退出 */ + chatExited: Ref; + /** 当前聊天室的房间号 */ + channelRoomId: Ref; + /** 当前频道下所有的分房间列表 */ + chatRoomIds: Ref; + /** 当前是否为分房间 */ + isSibRoom: ComputedRef; + /** 聊天室是否已关闭 */ + chatRoomIsClosed: Ref; + /** 当前用户是否被踢出 */ + isKicked: Ref; + /** 当前用户是否被禁言 */ + isShield: Ref; + /** 聊天室点赞开关 */ + likeEnabled: ComputedRef; + /** 实时的点赞数 */ + realtimeLikes: Ref; + /** 是否过滤管理员的信息 */ + filterManagerMsgEnabled: ComputedRef; + /** 观众发送图片开关 */ + viewerSendImgEnabled: ComputedRef; + /** 只看特殊角色的消息(包括我本地发的) */ + onlySpecialMsg: Ref; + /** 聊天室开关 */ + watchChatEnabled: ComputedRef; + /** 欢迎语开关 */ + welcomeEnabled: ComputedRef; + /** 聊天室情绪反馈开关 */ + emotionalFeedbackEnabled: ComputedRef; + /** 聊天室在线人数开关 */ + chatOnlineNumberEnable: ComputedRef; + /** 聊天室在线人数 */ + onlineUserCount: Ref; + /** 聊天室输入框占位符 */ + chatInputPlaceholder: ComputedRef; + /** 是否禁用聊天室输入框 */ + chatInputDisabled: ComputedRef; + /** 当前正在引用的聊天消息 */ + currentQuoteMsg: Ref; +} + +export interface ChatStoreAction { + /** 切换 onlySpecialMsg */ + toggleOnlySpecialMsg(): void; + /** 同步聊天室 store */ + syncChatStore(): void; +} + +export interface ChatStore extends ChatStoreState, ChatStoreAction {} + +export const useChatStore = defineStore<'chat', ChatStore>('chat', () => { + const channelStore = useChannelStore(); + const watchAppStore = useWatchAppStore(); + const channelMenuStore = useChannelMenuStore(); + const viewerStore = useViewerStore(); + + /** 配置是否启用聊天列表 */ + const chatMsgListEnabled = ref(true); + + /** 聊天室是否连接失败 */ + const connectError = ref(false); + + /** 聊天室是否被退出 */ + const chatExited = ref(false); + + /** 当前聊天室的房间号 */ + const channelRoomId = ref(''); + + /** 当前频道下所有的分房间列表 */ + const chatRoomIds = ref([]); + + /** 当前是否为分房间 */ + const isSibRoom = computed(() => unref(chatRoomIds).length !== 0); + + /** 聊天室是否已关闭 */ + const chatRoomIsClosed = ref(false); + + /** 当前用户是否被踢出 */ + const isKicked = ref(false); + + /** 当前用户是否被禁言 */ + const isShield = ref(false); + + /** 聊天室点赞开关 */ + const likeEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.sendFlowersEnabled, YN.N); + }); + + /** 实时的点赞数 */ + const realtimeLikes = ref(0); + + /** 是否过滤管理员的信息 */ + const filterManagerMsgEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.filterManagerMsgEnabled, YN.N); + }); + + /** 观众发送图片开关 */ + const viewerSendImgEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.viewerSendImgEnabled, YN.N); + }); + + /** 只看特殊角色的消息(包括我本地发的) */ + const onlySpecialMsg = ref(false); + + watch( + () => unref(filterManagerMsgEnabled), + () => { + if (unref(filterManagerMsgEnabled)) { + onlySpecialMsg.value = true; + } + }, + { + immediate: true, + }, + ); + + /** 切换 onlySpecialMsg */ + function toggleOnlySpecialMsg() { + onlySpecialMsg.value = !unref(onlySpecialMsg); + } + + /** 聊天室开关 */ + const watchChatEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.watchChatEnabled, YN.Y); + }); + + /** 欢迎语开关 */ + const welcomeEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.welcomeEnabled, YN.N); + }); + + /** 聊天室情绪反馈开关 */ + const emotionalFeedbackEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.emotionEnabled, YN.N); + }); + + /** 聊天室在线人数开关 */ + const chatOnlineNumberEnable = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.chatOnlineNumberEnable, YN.N); + }); + + /** 聊天室在线人数 */ + const onlineUserCount = ref(0); + + /** 聊天室输入框占位符 */ + const chatInputPlaceholder = computed(() => { + // 聊天室被关闭 + if (unref(chatRoomIsClosed)) { + return translate('chat.closeRoom'); + } + + let placeholder = translate('chat.input.placeholder'); + + // 针对移动端在未设置昵称下更改文案 + if (isMobile && !viewerStore.hasNickname) { + placeholder = translate('chat.input.placeholder3'); + } + + // 针对竖屏观看页更改文案 + if (channelStore.isPortraitWatchLayout) { + placeholder = translate('chat.input.placeholder2'); + } + + // 后台中聊天室的占位设置 + const chatMenu = channelMenuStore.getMenuData(ChannelMenuType.Chat); + if (chatMenu && chatMenu.content) { + placeholder = chatMenu.content; + } + return placeholder; + }); + + /** 是否禁用聊天室输入框 */ + const chatInputDisabled = computed(() => { + // 聊天室被关闭 + if (unref(chatRoomIsClosed)) { + return true; + } + return false; + }); + + /** 当前正在引用的聊天消息 */ + const currentQuoteMsg = ref(); + + function syncChatStore(): void { + const watchCore = getWatchCore(); + const chatInfo = watchCore.chat.getChatInfo(); + channelRoomId.value = watchCore.chat.getChatRoomId(); + chatRoomIds.value = chatInfo.chatRoomIds; + chatExited.value = chatInfo.chatExited; + chatRoomIsClosed.value = chatInfo.chatRoomIsClosed; + isKicked.value = chatInfo.isKicked; + isShield.value = chatInfo.isShield; + + if (unref(realtimeLikes) === 0) { + realtimeLikes.value = channelStore.channelDetail?.watchData.likes ?? 0; + } + + // 如果被踢出了则跳走页面 + if (isKicked.value) { + watchAppStore.setPageError({ + type: PageErrorType.NotAuthorized, + title: translate('base.notAuthorized'), + }); + } + } + + return { + chatMsgListEnabled, + connectError, + chatExited, + channelRoomId, + chatRoomIds, + isSibRoom, + chatRoomIsClosed, + isKicked, + isShield, + realtimeLikes, + filterManagerMsgEnabled, + viewerSendImgEnabled, + onlySpecialMsg, + watchChatEnabled, + welcomeEnabled, + likeEnabled, + emotionalFeedbackEnabled, + chatOnlineNumberEnable, + onlineUserCount, + chatInputPlaceholder, + chatInputDisabled, + currentQuoteMsg, + syncChatStore, + toggleOnlySpecialMsg, + }; +}); diff --git a/src/store/use-common-store.ts b/src/store/use-common-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2b27d551c6f005276a91230c53460a5d4726af2 --- /dev/null +++ b/src/store/use-common-store.ts @@ -0,0 +1,17 @@ +import { AreaDataJson } from '@/plugins/polyv-ui/types'; +import { defineStore } from 'pinia'; +import { ref, Ref } from 'vue'; + +export interface CommonStoreState { + /** 中国地区数据 */ + areaData: Ref; +} + +export const useCommonStore = defineStore<'common', CommonStoreState>('common', () => { + /** 中国地区数据 */ + const areaData = ref([]); + + return { + areaData, + }; +}); diff --git a/src/store/use-connect-mic-store.ts b/src/store/use-connect-mic-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..55cc757b62133bcac63a4faa2b91a00164a41f61 --- /dev/null +++ b/src/store/use-connect-mic-store.ts @@ -0,0 +1,220 @@ +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { + ConnectMicItem, + ConnectMicStatus, + ConnectMicType, + NetworkStatus, + UplinkNetworkQuality, +} from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; + +export interface ConnectMicStoreState { + /** 配置是否使用连麦 */ + connectMicEnabled: Ref; + /** 当前环境是否支持连麦 */ + supportConnectMic: Ref; + /** 是否开启了连麦 */ + openMicStatus: Ref; + /** 连麦类型 */ + connectMicType: Ref; + /** 用户连麦状态 */ + connectMicStatus: Ref; + /** 当前用户是否正在连麦中 */ + isConnectMicing: ComputedRef; + /** 连麦列表 */ + connectMicList: Ref; + /** 过滤后的连麦列表(移除主讲流) */ + filterConnectMicList: ComputedRef; + /** 主讲流对象 */ + masterMicItem: ComputedRef; + /** 当前连麦用户是否为主讲 */ + masterIsSelf: ComputedRef; + /** 主讲流是否关闭摄像头 */ + masterIsVideoMuted: ComputedRef; + /** 主讲流是否关闭麦克风 */ + masterIsAudioMuted: ComputedRef; + /** 主讲头衔 */ + masterActor: ComputedRef; + /** 主讲昵称 */ + masterNickname: ComputedRef; + /** 主讲的连麦方式 */ + masterConnectMicType: ComputedRef; + /** 本地流对象 */ + localMicItem: ComputedRef; + /** 本地流是否关闭摄像头 */ + localIsVideoMuted: ComputedRef; + /** 本地流是否关闭麦克风 */ + localIsAudioMuted: ComputedRef; + /** 本地流的连麦方式 */ + localConnectMicType: ComputedRef; + /** 连麦上行网络质量 */ + uplinkNetworkQuality: Ref; + /** 连麦上行网络状态 */ + uplinkNetworkStatus: Ref; +} + +export interface ConnectMicStoreAction { + /** 同步连麦信息 */ + syncConnectMicInfo(): void; + /** 同步连麦列表 */ + syncConnectMicList(): void; +} + +export interface ConnectMicStore extends ConnectMicStoreState, ConnectMicStoreAction {} + +/** + * 连麦 store + */ +export const useConnectMicStore = defineStore<'connectMic', ConnectMicStore>('connectMic', () => { + /** 配置是否使用连麦 */ + const connectMicEnabled = ref(true); + + /** 当前环境是否支持连麦 */ + const supportConnectMic = ref(true); + + /** 是否开启了连麦 */ + const openMicStatus = ref(false); + + /** 连麦类型 */ + const connectMicType = ref(ConnectMicType.Video); + + /** 用户连麦状态 */ + const connectMicStatus = ref(ConnectMicStatus.NotConnect); + + /** 当前用户是否正在连麦中 */ + const isConnectMicing = computed(() => { + const status = unref(connectMicStatus); + return [ConnectMicStatus.Publishing, ConnectMicStatus.Connected].includes(status); + }); + + /** 连麦列表 */ + const connectMicList: Ref = ref([]); + + /** 过滤连麦列表 */ + const filterConnectMicList = computed(() => { + const micList = unref(connectMicList); + const streamList = micList.filter(micItem => { + return !micItem.isMaster; + }); + return streamList.sort((prevMicItem, nextMicItem) => { + if (nextMicItem.isLocal) return 1; + return -1; + }); + }); + + /** 主讲流对象 */ + const masterMicItem = computed(() => { + const micList = unref(connectMicList); + const masterIndex = micList.findIndex(micItem => micItem.isMaster); + return masterIndex === -1 ? undefined : micList[masterIndex]; + }); + + /** 当前连麦用户是否为主讲 */ + const masterIsSelf = computed(() => { + return !!unref(masterMicItem)?.isSelf; + }); + + /** 主讲流是否关闭摄像头 */ + const masterIsVideoMuted = computed(() => { + return unref(masterMicItem)?.isVideoMuted ?? false; + }); + + /** 主讲流是否关闭麦克风 */ + const masterIsAudioMuted = computed(() => { + return unref(masterMicItem)?.isAudioMuted ?? false; + }); + + /** 主讲头衔 */ + const masterActor = computed(() => { + if (unref(masterIsSelf)) { + return translate('connectMic.me'); + } + if (unref(masterMicItem)?.isTeacher) { + return translate('connectMic.teacher'); + } + return undefined; + }); + + /** 主讲昵称 */ + const masterNickname = computed(() => { + return unref(masterMicItem)?.nickname; + }); + + /** 主讲的连麦方式 */ + const masterConnectMicType = computed(() => { + return unref(masterMicItem)?.currentConnectMicType; + }); + + /** 本地流对象 */ + const localMicItem = computed(() => { + const micList = unref(connectMicList); + const localIndex = micList.findIndex(micItem => micItem.isLocal); + return localIndex === -1 ? undefined : micList[localIndex]; + }); + + /** 本地流是否关闭摄像头 */ + const localIsVideoMuted = computed(() => { + return unref(localMicItem)?.isVideoMuted ?? false; + }); + + /** 本地流是否关闭麦克风 */ + const localIsAudioMuted = computed(() => { + return unref(localMicItem)?.isAudioMuted ?? false; + }); + + /** 本地流的连麦方式 */ + const localConnectMicType = computed(() => { + return unref(localMicItem)?.currentConnectMicType; + }); + + /** 连麦上行网络质量 */ + const uplinkNetworkQuality = ref(0); + + /** 连麦上行网络状态 */ + const uplinkNetworkStatus = ref(NetworkStatus.Unknown); + + function syncConnectMicInfo(): void { + const watchCore = getWatchCore(); + const connectMicInfo = watchCore.connectMic.getConnectMicInfo(); + + supportConnectMic.value = connectMicInfo.supportConnectMic; + openMicStatus.value = connectMicInfo.openMicStatus; + connectMicType.value = connectMicInfo.connectMicType; + connectMicStatus.value = connectMicInfo.connectMicStatus; + uplinkNetworkQuality.value = connectMicInfo.uplinkNetworkQuality; + uplinkNetworkStatus.value = connectMicInfo.uplinkNetworkStatus; + } + + function syncConnectMicList(): void { + const watchCore = getWatchCore(); + connectMicList.value = watchCore.connectMic.getConnectMicList(); + } + + return { + connectMicEnabled, + supportConnectMic, + openMicStatus, + connectMicType, + connectMicStatus, + isConnectMicing, + connectMicList, + filterConnectMicList, + masterMicItem, + masterIsSelf, + masterIsVideoMuted, + masterIsAudioMuted, + masterActor, + masterNickname, + masterConnectMicType, + localMicItem, + localIsVideoMuted, + localIsAudioMuted, + localConnectMicType, + uplinkNetworkQuality, + uplinkNetworkStatus, + syncConnectMicInfo, + syncConnectMicList, + }; +}); diff --git a/src/store/use-doc-store.ts b/src/store/use-doc-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..c846aa02b239890c1b1900ad2b97245e97544135 --- /dev/null +++ b/src/store/use-doc-store.ts @@ -0,0 +1,63 @@ +import { isMobile } from '@/assets/utils/browser'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref } from 'vue'; +import { useChannelStore } from './use-channel-store'; +import { LiveStatus } from '@polyv/live-watch-sdk'; +import { usePlaybackStore } from './use-playback-store'; + +export interface DocStoreState { + /** 配置是否使用文档 */ + docEnabled: Ref; + /** 能否创建文档 */ + canRenderDoc: ComputedRef; + /** 文档比例 */ + documentProportion: Ref; + /** 文档开关 */ + documentSwitch: Ref; +} + +export interface DocStoreActions { + /** 切换文档开关 */ + toggleDocumentSwitch(): void; +} + +export interface DocStore extends DocStoreState, DocStoreActions {} + +/** + * 文档 store + */ +export const useDocStore = defineStore<'doc', DocStore>('doc', () => { + const channelStore = useChannelStore(); + const playbackStore = usePlaybackStore(); + + /** 配置是否使用文档 */ + const docEnabled = ref(true); + + /** 能否创建文档 */ + const canRenderDoc = computed(() => { + // 移动端下,非直播或回放下不显示 + if (isMobile && !(channelStore.liveStatus === LiveStatus.Live || playbackStore.isPlaybacking)) { + return false; + } + return channelStore.isPptChannel || channelStore.isSeminarChannel; + }); + + /** 文档比例,宽/高 */ + const documentProportion = ref(); + + /** 文档开关 */ + const documentSwitch = ref(true); + + /** 切换文档开关 */ + function toggleDocumentSwitch() { + documentSwitch.value = !documentSwitch.value; + } + + return { + docEnabled, + canRenderDoc, + documentProportion, + documentSwitch, + toggleDocumentSwitch, + }; +}); diff --git a/src/store/use-donate-store.ts b/src/store/use-donate-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..38550644e21a0330236db3c3a6fa6131035da4fa --- /dev/null +++ b/src/store/use-donate-store.ts @@ -0,0 +1,341 @@ +/** + * @file 打赏 store + */ +import { translate } from '@/assets/lang'; +import { + DonateSetting, + YN, + DonateType, + DonateGoodData, + PayDonateData, +} from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { useChannelStore } from '@/store/use-channel-store'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { ynToBool } from '@utils-ts/boolean'; +import { changeProtocol } from '@utils-ts/net'; +import { getWatchCore } from '@/core/watch-sdk'; +import { toast } from '@/hooks/components/use-toast'; +import { useWatchAppStore } from './use-watch-app-store'; + +export interface DonateGoodInfo extends DonateGoodData { + /** 翻译后的礼物名称 */ + goodTranslateName: string; + /** 礼物价格文案 */ + goodPriceText: string; + /** 礼物类型 */ + donateType: DonateType.Good; +} + +type DonatePageItem = + | DonateGoodInfo + | { + donateType: DonateType.Cash; + }; + +export interface DonateStoreState { + /** 打赏设置 */ + donateSetting: ComputedRef; + /** 现金打赏开关 */ + donateCashEnabled: ComputedRef; + /** 现金打赏最低的打赏金额 */ + donateCashMin: ComputedRef; + /** 现金打赏快捷选项 */ + donateCashQuickOptions: ComputedRef; + /** 现金打赏道具开关 */ + donateGoodEnabled: ComputedRef; + /** 积分打赏道具开关 */ + donatePointEnabled: ComputedRef; + /** 是否显示现金打赏 */ + showDonateCash: ComputedRef; + /** 是否显示现金道具打赏 */ + showDonateGood: ComputedRef; + /** 是否显示积分道具打赏 */ + showDonatePoint: ComputedRef; + /** 是否显示道具打赏 */ + showDonateGifts: ComputedRef; + /** 是否显示打赏功能 */ + showDonateFunction: ComputedRef; + /** 积分打赏礼物单位 */ + donatePointUnit: ComputedRef; + /** 所有打赏道具列表 */ + donateItemAll: ComputedRef; + /** 可以显示的打赏道具列表 */ + donateItemList: ComputedRef; + /** 将道具、现金打赏进行分页 */ + donateContentPages: ComputedRef; + /** 付费礼物列表 */ + payDonateGoods: ComputedRef; + /** PC 端下当前待支付的打赏数据 */ + waitPayDonateData: Ref; + /** 是否显示其他人的打赏特效 */ + donateAnimationVisible: Ref; +} + +export interface DonateStoreAction { + /** 切换打赏动画显示状态 */ + toggleDonateAnimationVisible: () => void; + /** 翻译打赏道具的道具名 */ + translateGoodName: (goodName: string) => string; + /** 获取打赏道具的价格文案 */ + getGoodPriceText: (goodPrice: string) => string; + /** 根据道具图片获取打赏道具信息 */ + findDonateGoodByImg: (targetGoodImg: string) => DonateGoodInfo | undefined; +} + +export interface DonateStore extends DonateStoreState, DonateStoreAction {} + +export const useDonateStore = defineStore<'donate', DonateStore>('donate', () => { + const watchAppStore = useWatchAppStore(); + const { channelDetail, isSeminarChannel } = storeDefinitionToRefs(useChannelStore); + const { weixinAccountFunctionEnabled, weixinPayEnabled } = storeDefinitionToRefs(useWeixinStore); + + /** 道具礼物中英文 */ + const goodNamesConfig = computed>(() => { + return { + 666: translate('donate.name.six'), + 小熊: translate('donate.name.bear'), + 麦克风: translate('donate.name.microphone'), + 别墅: translate('donate.name.villa'), + 咖啡: translate('donate.name.coffee'), + 奖杯: translate('donate.name.trophy'), + 小星星: translate('donate.name.star'), + 掌声: translate('donate.name.applause'), + 游艇: translate('donate.name.yacht'), + 火箭: translate('donate.name.rocket'), + 点赞: translate('donate.name.like'), + 烟火: translate('donate.name.fireworks'), + 皇冠: translate('donate.name.crown'), + 跑车: translate('donate.name.cars'), + 金蛋: translate('donate.name.goldenEggs'), + 钻石: translate('donate.name.diamonds'), + 飞机: translate('donate.name.plane'), + 鲜花: translate('donate.name.flower'), + }; + }); + + /** 打赏设置 */ + const donateSetting = computed(() => unref(channelDetail)?.donateSetting ?? undefined); + + /** 现金打赏开关 */ + const donateCashEnabled = computed(() => + ynToBool(unref(donateSetting)?.donateCashEnabled || YN.N), + ); + + /** 现金打赏最低的打赏金额 */ + const donateCashMin = computed(() => unref(donateSetting)?.cashMin || 0); + + /** 现金打赏快捷选项 */ + const donateCashQuickOptions = computed(() => unref(donateSetting)?.cashes || []); + + /** 现金打赏道具开关 */ + const donateGoodEnabled = computed(() => { + return ynToBool(unref(donateSetting)?.donateGoodEnabled || YN.N); + }); + + /** 积分打赏道具开关 */ + const donatePointEnabled = computed(() => { + return ynToBool(unref(donateSetting)?.donatePointEnabled || YN.N); + }); + + /** 是否显示现金打赏 */ + const showDonateCash = computed(() => { + return ( + unref(donateCashEnabled) && unref(weixinAccountFunctionEnabled) && unref(weixinPayEnabled) + ); + }); + + /** 是否显示现金道具打赏 */ + const showDonateGood = computed(() => { + return ( + unref(donateGoodEnabled) && unref(weixinAccountFunctionEnabled) && unref(weixinPayEnabled) + ); + }); + + /** 是否显示积分道具打赏 */ + const showDonatePoint = computed(() => { + return unref(donatePointEnabled); + }); + + /** 是否显示道具打赏 */ + const showDonateGifts = computed(() => { + return unref(showDonateGood) || unref(showDonatePoint); + }); + + /** 是否显示打赏功能 */ + const showDonateFunction = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + return (unref(showDonateCash) || unref(showDonateGifts)) && !unref(isSeminarChannel); + }); + + /** 积分打赏礼物单位 */ + const donatePointUnit = computed(() => unref(donateSetting)?.pointUnit); + + /** 所有打赏道具列表 */ + const donateItemAll = computed(() => { + const watchCore = getWatchCore(); + let goods: DonateGoodData[] = []; + const goodData = watchCore.donate.getDonateGoods(); + + if (unref(donatePointEnabled)) { + goods = goodData.pointGoods; + } else { + goods = goodData.goods; + } + + return goods.map(good => { + return { + ...good, + goodTranslateName: translateGoodName(good.goodName), + goodPriceText: getGoodPriceText(good.goodPrice), + donateType: DonateType.Good, + }; + }); + }); + + /** 可以显示的打赏道具列表 */ + const donateItemList = computed(() => { + const goods = unref(donateItemAll); + + return goods.filter(good => { + // 道具的开关 + if (ynToBool(good.goodEnabled || YN.N) === false) { + return false; + } + + // 现金道具 + 付费道具 + 没开启超管后台支付开关和微信公众号功能 + if ( + unref(donateGoodEnabled) && + good.goodPrice !== 0 && + (!unref(weixinPayEnabled) || !unref(weixinAccountFunctionEnabled)) + ) { + return false; + } + + return true; + }); + }); + + /** 将道具、现金打赏进行分页 */ + const donateContentPages = computed(() => { + // 所有打赏种类,道具+现金 + const donateList: DonatePageItem[] = []; + + // 插入礼物打赏节点(含现金、积分礼物) + if (unref(showDonateGifts)) { + donateList.push(...unref(donateItemList)); + } + + // 插入现金打赏节点 + if (unref(showDonateCash)) { + donateList.push({ + donateType: DonateType.Cash, + }); + } + + const pages: DonatePageItem[][] = []; + const totalPages = Math.ceil(donateList.length / 10); + for (let i = 0; i < totalPages; i++) { + pages.push(donateList.slice(i * 10, i * 10 + 10)); + } + + return pages; + }); + + /** 付费礼物列表 */ + const payDonateGoods = computed(() => { + const goods = unref(donateItemAll); + return goods.filter(good => good.goodPrice > 0); + }); + + /** PC 端下当前待支付的打赏数据 */ + const waitPayDonateData = ref(); + + /** 是否显示其他人的打赏特效 */ + const donateAnimationVisible = ref(true); + + /** 切换打赏动画显示状态 */ + function toggleDonateAnimationVisible() { + donateAnimationVisible.value = !unref(donateAnimationVisible); + + if (donateAnimationVisible.value) { + toast.success(translate('donate.showAnimation.tips')); + } else { + toast.success(translate('donate.hideAnimation.tips')); + } + } + + /** 翻译打赏道具的道具名 */ + function translateGoodName(goodName: string): string { + const translateName = unref(goodNamesConfig)[goodName]; + + if (translateName) { + return translateName; + } + + return goodName; + } + + /** 获取打赏道具的价格文案 */ + function getGoodPriceText(goodPrice: string | number): string { + if (!goodPrice) return translate('donate.free'); + + if (unref(donateGoodEnabled)) { + return `¥${goodPrice}`; + } + + return `${goodPrice} ${unref(donatePointUnit) || translate('donate.spot')}`; + } + + /** + * 根据道具图片获取打赏道具信息 + * @param targetGoodImg 道具图片 + */ + function findDonateGoodByImg(targetGoodImg: string): DonateGoodInfo | undefined { + let goodInfo: DonateGoodInfo | undefined; + const list = unref(donateItemAll); + + for (let i = 0; i < list.length; i++) { + const good = list[i]; + const goodImg = changeProtocol(good.goodImg, 'https'); + const dynamicImg = changeProtocol(good.dynamicImg ?? '', 'https'); + const _targetGoodImg = changeProtocol(targetGoodImg, 'https'); + if (_targetGoodImg === goodImg || _targetGoodImg === dynamicImg) { + goodInfo = good; + break; + } + } + + return goodInfo; + } + + return { + donateSetting, + donateCashEnabled, + donateCashMin, + donateCashQuickOptions, + donateGoodEnabled, + donatePointEnabled, + showDonateCash, + showDonateGood, + showDonatePoint, + showDonateGifts, + showDonateFunction, + donatePointUnit, + donateItemAll, + donateItemList, + donateContentPages, + payDonateGoods, + + waitPayDonateData, + donateAnimationVisible, + + toggleDonateAnimationVisible, + translateGoodName, + getGoodPriceText, + findDonateGoodByImg, + }; +}); diff --git a/src/store/use-enroll-store.ts b/src/store/use-enroll-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..e113b2b2666a63f6759d6e578143821827e5cb46 --- /dev/null +++ b/src/store/use-enroll-store.ts @@ -0,0 +1,199 @@ +import { translate } from '@/assets/lang'; +import { getWatchCore } from '@/core/watch-sdk'; +import { + EnrollAuthType, + EnrollFieldItem, + EnrollFieldItemMobile, + EnrollFieldType, + EnrollFillTime, +} from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; + +export interface EnrollStoreState { + /** 报名观看开关 */ + enrollEnabled: Ref; + /** 报名审核开关 */ + auditEnabled: Ref; + /** 是否已有报名记录 */ + hasEnrolled: Ref; + /** 是否已审核通过 */ + hasAudited: Ref; + /** 填写时机 */ + fillTime: Ref; + /** 报名表单字段 */ + enrollFields: Ref; + /** 验证方式 */ + enrollAuthType: Ref; + /** 是否需要短信验证 */ + smsVerifyEnabled: Ref; + /** 验证字段对象 */ + enrollAuthField: ComputedRef; + /** 验证字段名称 */ + enrollAuthName: ComputedRef; + /** 验证字段提示 */ + enrollAuthTips: ComputedRef; + /** 当前已有报名记录但未通过 */ + auditNotPass: ComputedRef; + /** 需要在入口中进行报名观看 */ + needEnrollByEnter: ComputedRef; + /** 需要在互动前进行报名观看 */ + needEnrollByChat: ComputedRef; + /** 入口文本 */ + entranceText: ComputedRef; + /** 报名观看标题 */ + enrollTitle: ComputedRef; + /** banner 头图片 */ + enrollBanner: ComputedRef; + /** 是否使用报名观看姓名作为昵称 */ + enrollNickEnabled: Ref; +} + +export interface EnrollStoreAction { + /** 同步报名观看信息 */ + syncEnrollInfo: () => void; +} + +export interface EnrollStore extends EnrollStoreState, EnrollStoreAction {} + +/** + * 报名观看 store + */ +export const useEnrollStore = defineStore<'enroll', EnrollStore>('enroll', () => { + const channelStore = useChannelStore(); + + /** 报名观看开关 */ + const enrollEnabled = ref(false); + + /** 报名审核开关 */ + const auditEnabled = ref(false); + + /** 是否已有报名记录 */ + const hasEnrolled = ref(false); + + /** 是否已审核通过 */ + const hasAudited = ref(false); + + /** 填写时机 */ + const fillTime = ref(EnrollFillTime.BeforeEnter); + + /** 报名表单字段 */ + const enrollFields = ref([]); + + /** 验证方式 */ + const enrollAuthType = ref(EnrollAuthType.Mobile); + + /** 是否需要短信验证 */ + const smsVerifyEnabled = ref(false); + + /** 验证字段对象 */ + const enrollAuthField = computed(() => { + const fields = unref(enrollFields); + const mobileIndex = fields.findIndex(fieldItem => fieldItem.type === EnrollFieldType.Mobile); + + return fields[mobileIndex] as EnrollFieldItemMobile; + }); + + /** 验证字段名称 */ + const enrollAuthName = computed(() => { + const fieldItem = unref(enrollAuthField); + + return fieldItem?.name ?? ''; + }); + + /** 验证字段提示 */ + const enrollAuthTips = computed(() => { + const fieldItem = unref(enrollAuthField); + + return fieldItem?.tips ?? ''; + }); + + /** 当前已有报名记录但未通过 */ + const auditNotPass = computed( + () => unref(hasEnrolled) && unref(auditEnabled) && !unref(hasAudited), + ); + + /** 需要在入口中进行报名观看 */ + const needEnrollByEnter = computed(() => { + return ( + // 需要开启报名观看 + unref(enrollEnabled) && + // 填写时间是“进入直播前” + unref(fillTime) === EnrollFillTime.BeforeEnter && + // [没有报名记录]或者[有记录但未通过] + (!unref(hasEnrolled) || unref(auditNotPass)) + ); + }); + + /** 需要在互动前进行报名观看 */ + const needEnrollByChat = computed( + () => + // 需要开启报名观看 + unref(enrollEnabled) && + // 填写时间是“进入直播前” + unref(fillTime) === EnrollFillTime.BeforeChat && + // 没有报名记录 + !unref(hasEnrolled), + ); + + /** 入口文本 */ + const entranceText = computed(() => { + const text = + channelStore.channelDetail?.enrollSetting?.entranceText ?? translate('enroll.text'); + return text === '报名观看' ? translate('enroll.text') : text; + }); + + /** 报名观看标题 */ + const enrollTitle = computed(() => { + return channelStore.channelDetail?.enrollSetting?.title ?? ''; + }); + + /** banner 头部图 */ + const enrollBanner = computed(() => { + return channelStore.channelDetail?.enrollSetting?.banner ?? undefined; + }); + + /** 是否使用报名观看姓名作为昵称 */ + const enrollNickEnabled = ref(false); + + /** 同步信息 */ + function syncEnrollInfo() { + const watchCore = getWatchCore(); + const enrollInfo = watchCore.enroll.getEnrollInfo(); + + enrollEnabled.value = enrollInfo.enrollEnabled; + auditEnabled.value = enrollInfo.auditEnabled; + hasEnrolled.value = enrollInfo.hasEnrolled; + hasAudited.value = enrollInfo.hasAudited; + fillTime.value = enrollInfo.fillTime; + enrollFields.value = enrollInfo.enrollFields; + enrollAuthType.value = enrollInfo.enrollAuthType; + smsVerifyEnabled.value = enrollInfo.smsVerifyEnabled; + enrollNickEnabled.value = enrollInfo.enrollNickEnabled; + } + + return { + enrollEnabled, + auditEnabled, + hasEnrolled, + hasAudited, + fillTime, + enrollFields, + enrollAuthType, + smsVerifyEnabled, + + enrollAuthField, + enrollAuthName, + enrollAuthTips, + auditNotPass, + needEnrollByEnter, + needEnrollByChat, + entranceText, + enrollTitle, + enrollBanner, + enrollNickEnabled, + + syncEnrollInfo, + }; +}); diff --git a/src/store/use-finance-store.ts b/src/store/use-finance-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..95a18c353ca472fbcf74b71fdd8cef513e29b26c --- /dev/null +++ b/src/store/use-finance-store.ts @@ -0,0 +1,148 @@ +import { computed, ComputedRef, ref, Ref } from 'vue'; +import { defineStore } from 'pinia'; + +import { + YN, + FinanceRiskConfirm, + FinanceRiskEvaluation, + FinanceRiskEvaluationLimitType, +} from '@polyv/live-watch-sdk'; + +import { ynToBool } from '@utils-ts/boolean'; +import { getWatchCore } from '@/core/watch-sdk'; + +/** + * 获取默认风险测评数据 + * @desc 设置一个默认值来走通流程,默认情况为“后置-自愿填写” + */ +function getDefaultRiskEvaluation(): FinanceRiskEvaluation { + return { + evaluationEnabled: YN.N, + questionnaireId: '1', + evaluationLimitType: FinanceRiskEvaluationLimitType.Pendant, + scoreLimit: 0, + score: -1, + passEvaluation: 1, + }; +} + +/** + * 获取默认的风险确认函数据 + */ +function getDefaultRiskConfirm(): FinanceRiskConfirm { + return { + riskConfirmEnabled: YN.N, + riskConfirmStatus: false, + }; +} + +export interface FinanceStoreState { + /** 风险确认函数据 */ + riskConfirm: Ref; + /** 风险确认函开关 */ + riskConfirmEnabled: ComputedRef; + /** 风险测评数据 */ + riskEvaluation: Ref; + /** 风险测评-是否展示引导页 */ + shouldShowSplashByRiskEvaluation: Ref; + /** 风险测评开关 */ + riskEvaluationEnabled: ComputedRef; + /** 风险测评-是否展示挂件入口 */ + riskEvaluationEntranceEnabled: ComputedRef; +} + +export interface FinanceStoreAction { + /** 初始化风险确认函数据 */ + initRiskConfirm: () => void; + /** 同步化风险确认函数据 */ + syncRiskConfirm: (riskEvaluation: Partial) => void; + /** 同步风险测评数据 */ + syncRiskEvaluation: (riskEvaluation: Partial) => void; + /** 初始化风险测评数据 */ + initRiskEvaluation: () => void; +} + +export interface FinanceStore extends FinanceStoreState, FinanceStoreAction {} + +/** @store 金融模块 */ +export const useFinanceStore = defineStore<'finance', FinanceStore>('finance', () => { + /** 风险确认函数据 */ + const riskConfirm = ref(getDefaultRiskConfirm()); + + /** 风险确认函开关 */ + const riskConfirmEnabled = computed(() => { + // 配置开关启用 && 未确认 + return ynToBool(riskConfirm.value.riskConfirmEnabled) && !riskConfirm.value.riskConfirmStatus; + }); + + /** 同步风险测评数据 */ + function syncRiskConfirm(data: Partial) { + const originData = riskConfirm.value; + riskConfirm.value = { + ...originData, + ...data, + }; + } + + /** 初始化风险确认函数据 */ + function initRiskConfirm() { + const watchCore = getWatchCore(); + const data = watchCore.channel.getChannelDetail().riskConfirm || getDefaultRiskConfirm(); + + riskConfirm.value = data; + } + + /** 风险测评数据 */ + const riskEvaluation = ref(getDefaultRiskEvaluation()); + + /** 风险测评-是否展示引导页 */ + const shouldShowSplashByRiskEvaluation = ref(false); + + /** 风险测评开关 */ + const riskEvaluationEnabled = computed(() => { + return ynToBool(riskEvaluation.value.evaluationEnabled); + }); + + /** 风险测评-是否展示挂件入口 */ + const riskEvaluationEntranceEnabled = computed(() => { + const watchCore = getWatchCore(); + + // 风险测评启用,且为后置条件时,需要展示挂件 + return watchCore.financeRiskEvaluation.judgeNeedShowRiskEvaluationInAfter(riskEvaluation.value); + }); + + /** 同步风险测评数据 */ + function syncRiskEvaluation(data: Partial) { + const originData = riskEvaluation.value; + riskEvaluation.value = { + ...originData, + ...data, + }; + } + + /** 初始化风险测评数据 */ + function initRiskEvaluation() { + const watchCore = getWatchCore(); + + const data = watchCore.channel.getChannelDetail().riskEvaluation || getDefaultRiskEvaluation(); + + riskEvaluation.value = data; + // 风险测评启用,且为前置条件时,需要强制展示引导页 + shouldShowSplashByRiskEvaluation.value = + watchCore.financeRiskEvaluation.judgeNeedShowRiskEvaluationInPre(riskEvaluation.value); + } + + return { + riskConfirm, + riskConfirmEnabled, + riskEvaluation, + shouldShowSplashByRiskEvaluation, + riskEvaluationEnabled, + riskEvaluationEntranceEnabled, + + initRiskConfirm, + syncRiskConfirm, + syncRiskEvaluation, + initRiskEvaluation, + }; +}); diff --git a/src/store/use-interact-receive-store.ts b/src/store/use-interact-receive-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..92bad88599109c61476f9275a85e8197d94c325c --- /dev/null +++ b/src/store/use-interact-receive-store.ts @@ -0,0 +1,195 @@ +/** + * @file 互动功能接收端 store + */ + +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { defineStore } from 'pinia'; + +import { useChannelStore } from './use-channel-store'; +import { useLayoutStore } from './use-layout-store'; + +import { + AnnouncementTipsStatus, + getStorageTipsStatus, +} from '@/components/page-watch-common/interactive-receive/announcement/hooks/use-announcement-tips'; +import { LotteryRecordData } from '@/components/page-watch-common/interactive-receive/lottery/types/lottery-types'; +import { LangKey } from '@/assets/lang/types'; +import { YN } from '@polyv/live-watch-sdk'; + +import { ynToBool } from '@utils-ts/boolean'; +import { useWatchAppStore } from './use-watch-app-store'; + +/** + * 互动功能二次入口数据 + */ +export interface InteractiveEntranceData { + /** 类型 */ + type: string; + /** 入口名称(多语言 key) */ + name: LangKey; + /** 入口文本 */ + text?: string; + /** 图标(常规模板用) */ + icon: string; + /** 图标(竖屏大图标用) */ + icon2: string; + /** 点击时的回调事件 */ + onClick: () => void; +} + +export interface InteractReceiveStoreState { + /** 互动功能是否全屏处理 */ + isIarFullScreen: ComputedRef; + /** 互动功能二次入口列表 */ + interactiveEntrance: Ref; + /** 投诉反馈开关 */ + watchFeedbackEnabled: ComputedRef; + /** 公告提示是否显示 */ + announcementTipsVisible: Ref; + /** 公告功能是否显示 */ + announcementFunctionVisible: ComputedRef; + /** 当前中奖记录列表 */ + lotteryWinRecords: Ref; + /** 是否存在未提交的中奖记录 */ + hasNotReceivedLottery: ComputedRef; + /** 卡片推送入口是否正在显示 */ + pushCardPendantVisible: Ref; + /** 条件抽奖入口是否显示 */ + welfareLotteryPendantVisible: Ref; + /** 报名抽奖挂件是否显示 */ + enrollLotteryPendantVisible: Ref; + /** 红包挂件是否显示 */ + redpackPendantVisible: Ref; + /** 红包雨是否正在显示 */ + isGoOnRedpackRain: Ref; + /** 商品库开关 */ + productEnabled: Ref; + /** 投票活动开关 */ + voteActiveEnabled: ComputedRef; + /** 已投票的 id 列表 */ + votedList: Ref; + /** 是否显示问答的红点提示 */ + qaReminderVisible: Ref; +} + +export interface InteractReceiveStoreAction { + /** 设置互动二次入口,如果已存在则不生效 */ + setInteractiveEntrance(entranceData: InteractiveEntranceData): void; + /** 根据类型移除互动二次入口 */ + removeInteractiveEntrance(type: string): void; +} + +export interface InteractReceiveStore + extends InteractReceiveStoreState, + InteractReceiveStoreAction {} + +export const useInteractReceiveStore = defineStore<'interactReceive', InteractReceiveStore>( + 'interactReceive', + () => { + const watchAppStore = useWatchAppStore(); + const channelStore = useChannelStore(); + const layoutStore = useLayoutStore(); + + /** 互动功能是否全屏处理 */ + const isIarFullScreen = computed(() => { + return layoutStore.mobilePlayerIsPageFullscreen && !layoutStore.isHorizontalScreenOrientation; + }); + + /** 互动功能二次入口列表 */ + const interactiveEntrance = ref([]); + + /** 设置互动二次入口,如果已存在则不生效 */ + function setInteractiveEntrance(entranceData: InteractiveEntranceData): void { + const entrance = unref(interactiveEntrance); + const index = entrance.findIndex(entrance => entrance.type === entranceData.type); + if (index >= 0) return; + interactiveEntrance.value.push(entranceData); + } + + /** 根据类型移除互动二次入口 */ + function removeInteractiveEntrance(type: string): void { + const entrance = unref(interactiveEntrance); + const index = entrance.findIndex(entrance => entrance.type === type); + if (index >= 0) { + interactiveEntrance.value.splice(index, 1); + } + } + + /** 投诉反馈开关 */ + const watchFeedbackEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.channelConfig.watchFeedbackEnabled, YN.N); + }); + + /** 公告提示是否显示 */ + const announcementTipsVisible = ref( + getStorageTipsStatus() === AnnouncementTipsStatus.showing && !watchAppStore.isWatchBackUrl, + ); + + /** 公告功能是否显示 */ + const announcementFunctionVisible = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + return !channelStore.isSeminarChannel; + }); + + /** 当前中奖记录列表 */ + const lotteryWinRecords = ref([]); + + /** 是否存在未提交的中奖记录 */ + const hasNotReceivedLottery = computed(() => { + return unref(lotteryWinRecords).some(item => !item.received); + }); + + /** 卡片推送入口是否正在显示 */ + const pushCardPendantVisible = ref(false); + + /** 条件抽奖入口是否显示 */ + const welfareLotteryPendantVisible = ref(false); + + /** 报名抽奖挂件是否显示 */ + const enrollLotteryPendantVisible = ref(false); + + /** 红包挂件是否显示 */ + const redpackPendantVisible = ref(false); + + /** 红包雨是否正在显示 */ + const isGoOnRedpackRain = ref(false); + + /** 商品库开关 */ + const productEnabled = ref(false); + + /** 投票活动开关 */ + const voteActiveEnabled = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + return ynToBool(channelStore.channelDetail?.channelConfig.voteActiveEnabled, YN.N); + }); + + /** 已投票的 id 列表 */ + const votedList = ref([]); + + /** 是否显示问答的红点提示 */ + const qaReminderVisible = ref(false); + + return { + isIarFullScreen, + interactiveEntrance, + setInteractiveEntrance, + removeInteractiveEntrance, + watchFeedbackEnabled, + announcementTipsVisible, + announcementFunctionVisible, + lotteryWinRecords, + hasNotReceivedLottery, + pushCardPendantVisible, + welfareLotteryPendantVisible, + enrollLotteryPendantVisible, + redpackPendantVisible, + isGoOnRedpackRain, + productEnabled, + voteActiveEnabled, + votedList, + qaReminderVisible, + }; + }, +); diff --git a/src/store/use-invite-store.ts b/src/store/use-invite-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..397da1ce54b1cbc27619c276c6d8b7aa9d8bbde7 --- /dev/null +++ b/src/store/use-invite-store.ts @@ -0,0 +1,144 @@ +/** + * @file 邀请 store + */ + +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; + +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelMenuStore } from '@/store/use-channel-menu-store'; +import { useChannelStore } from '@/store/use-channel-store'; + +import { + TAB_NAME_CHAT, + TAB_NAME_DOC_OR_VIDEO, + TAB_NAME_MICRO_ACTIVITY, + TAB_NAME_SEAT, +} from '@/assets/constants/tab-name'; +import { paramGetter } from '@/hooks/core/use-query-params'; +import { isWeixin } from '@/assets/utils/browser'; +import { YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { useLayoutStore } from './use-layout-store'; + +export interface InviteStoreState { + /** 企业微信分享海报的企微员工 ID */ + wxInviteId: Ref; + /** 邀请海报唯一ID */ + invitePosterId: Ref; + /** 当前用户邀请者 openId */ + inviteUserOpenId: Ref; + /** 当前用户邀请者 unionId */ + inviteUserUnionId: Ref; + /** 邀请海报开关 */ + invitePosterEnabled: ComputedRef; + /** 邀请榜开关 */ + inviteRankEnabled: ComputedRef; + /** 移动端聊天室 Tab 中邀请挂件是否显示 */ + mobileChatInvitePendantVisible: ComputedRef; + /** 移动端页面中常驻的邀请挂件是否显示 */ + mobileWatchInvitePendantVisible: ComputedRef; + /** 邀请海报选择页地址 */ + generatePosterUrl: ComputedRef; +} + +export interface InviteStoreAction { + /** 同步邀请信息 */ + syncInviteInfo: () => void; +} + +export interface InviteStore extends InviteStoreState, InviteStoreAction {} + +export const useInviteStore = defineStore<'invite', InviteStore>('invite', () => { + const channelStore = useChannelStore(); + const channelMenuStore = useChannelMenuStore(); + const layoutStore = useLayoutStore(); + + /** 企业微信分享海报的企微员工 ID */ + const wxInviteId = ref(paramGetter.wxInviteId()); + + /** 邀请海报唯一ID */ + const invitePosterId = ref(paramGetter.invitePosterId()); + + /** 邀请者 openId */ + const inviteUserOpenId = ref(); + + /** 邀请者 unionId */ + const inviteUserUnionId = ref(); + + /** 邀请海报开关 */ + const invitePosterEnabled = computed(() => { + return isWeixin && ynToBool(channelStore.channelDetail?.channelConfig.showInviteAccess, YN.N); + }); + + /** 邀请榜开关 */ + const inviteRankEnabled = computed(() => { + return channelMenuStore.inviteRankMenuData.visible; + }); + + /** 移动端聊天室 Tab 中邀请挂件是否显示 */ + const mobileChatInvitePendantVisible = computed(() => { + // 如果存在邀请榜的 tab 则不显示邀请榜挂件 + if (channelMenuStore.inviteRankMenuData.visible) { + return false; + } + + return unref(invitePosterEnabled); + }); + + /** 移动端页面中常驻的邀请挂件是否显示 */ + const mobileWatchInvitePendantVisible = computed(() => { + // 如果存在邀请榜的 tab 则不显示邀请榜挂件 + if (channelMenuStore.inviteRankMenuData.visible) { + return false; + } + + if (layoutStore.isPageFullscreenOrHorizontalScreen) { + return false; + } + + // 当前在以下的 tabName 不显示 + const excludeTabName: string[] = [ + TAB_NAME_CHAT, + TAB_NAME_SEAT, + TAB_NAME_MICRO_ACTIVITY, + TAB_NAME_DOC_OR_VIDEO, + ]; + if (excludeTabName.includes(layoutStore.mobileMenuCurrentName)) { + return false; + } + + return unref(invitePosterEnabled); + }); + + /** 邀请海报选择页地址 */ + const generatePosterUrl = computed(() => { + const watchCore = getWatchCore(); + return watchCore.invite.getInviteGeneratePosterUrl(); + }); + + /** 同步邀请信息 */ + function syncInviteInfo() { + const watchCore = getWatchCore(); + + const inviteUserInfo = watchCore.invite.getInviteUserInfo(false); + inviteUserOpenId.value = inviteUserInfo?.inviteOpenId; + inviteUserUnionId.value = inviteUserInfo?.inviteUnionId; + + invitePosterId.value = watchCore.invite.getInvitePosterId(); + wxInviteId.value = watchCore.invite.getWorkWxInviteId(); + } + + return { + wxInviteId, + invitePosterId, + inviteUserOpenId, + inviteUserUnionId, + invitePosterEnabled, + inviteRankEnabled, + mobileChatInvitePendantVisible, + mobileWatchInvitePendantVisible, + generatePosterUrl, + syncInviteInfo, + }; +}); diff --git a/src/store/use-lang-store.ts b/src/store/use-lang-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..7acf263ccc699de9e446df9cd3d36f0e78f59176 --- /dev/null +++ b/src/store/use-lang-store.ts @@ -0,0 +1,210 @@ +import { appEvents, eventBus } from '@/app/app-events'; +import { LangType } from '@/assets/lang/lang-enum'; +import { getWatchCore } from '@/core/watch-sdk'; +import { paramGetter } from '@/hooks/core/use-query-params'; +import { local } from '@just4/storage'; +import { YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; + +/** 判断目标是否为一个多语言 */ +const isAllowLang = (target: unknown): target is LangType => { + return Object.values(LangType).includes(target as LangType); +}; + +/** local 记录中的 key */ +const LOCAL_STORAGE_KEY = 'language'; + +/** 多语言选择节点类型 */ +export interface LangSwitchItem { + lang: LangType; + /** 文本 */ + text: string; +} + +export interface LangStoreState { + /** 英语设置开关 */ + englishSettingEnabled: ComputedRef; + /** 多语言开关 */ + langSwitchEnabled: ComputedRef; + /** 是否开启七国多语言 */ + isShowSevenLanguage: ComputedRef; + /** 是否显示日文语言切换(仅将英文的 text 换成日文文案) */ + japLangEnabled: ComputedRef; + /** 所有多语言选择列表 */ + allLangSwitchList: ComputedRef; + /** 多语言选择列表 */ + langSwitchList: ComputedRef; + /** 当前多语言 */ + currentLang: Ref; + /** 当前多语言文本 */ + currentLangText: ComputedRef; + /** 当前是否为中文语言 */ + isChineseLang: ComputedRef; +} + +export interface LangStoreAction { + /** 切换当前多语言 */ + switchLang: (lang: LangType) => void; + /** 根据当前语言转换游客名称 */ + translateNick: (nick: string) => string; +} + +/** 多语言 store */ +export interface LangStore extends LangStoreState, LangStoreAction {} + +/** 获取当前页面加载后的默认多语言 */ +function getDefaultLang(): LangType { + // 地址上的 lang 参数 + const queryLang = paramGetter.lang(); + if (isAllowLang(queryLang)) { + return queryLang; + } + + // localStorage 中记录的 + const localLang = local.get(LOCAL_STORAGE_KEY); + if (isAllowLang(localLang)) { + return localLang; + } + + // 系统浏览器的多语言 + const browserLang = (navigator.language || navigator.browserLanguage).toLowerCase(); + if (browserLang) { + // 中文以外的语言均判定为英文(逐一枚举工作量较大) + if (['zh', 'zh-cn'].includes(browserLang)) { + return LangType.Chinese; + } else { + return LangType.English; + } + } + + return LangType.Chinese; +} + +export const useLangStore = defineStore<'lang', LangStore>('lang', () => { + const channelStore = useChannelStore(); + + /** 英语设置开关 */ + const englishSettingEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig?.englishSettingEnabled, YN.N), + ); + + /** 多语言开关 */ + const langSwitchEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig.multilingualEnabled, YN.N), + ); + + /** 是否开启七国多语言 */ + const isShowSevenLanguage = computed(() => + ynToBool(channelStore.channelDetail?.customChannelSetting.isShowSevenLanguage, YN.N), + ); + + /** 是否显示日文语言切换(仅将英文的 text 换成日文文案) */ + const japLangEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig.japLangEnabled, YN.N), + ); + + /** 所有多语言选择列表 */ + const allLangSwitchList = computed(() => { + let englishText = 'English-EN'; + if (unref(japLangEnabled)) { + englishText = 'Japanese - JPN'; + } + + return [ + { text: '简体中文-ZH', lang: LangType.Chinese }, + { text: englishText, lang: LangType.English }, + { text: 'Русский', lang: LangType.Russian }, + { text: 'Português', lang: LangType.Portuguese }, + { text: 'Español', lang: LangType.Spanish }, + { text: 'العربية', lang: LangType.Arabic }, + { text: 'فارسی', lang: LangType.Farsi }, + ]; + }); + + /** 多语言选择列表 */ + const langSwitchList = computed(() => { + return unref(allLangSwitchList).filter(item => { + // 没有开启七国语言,则只显示中文、英文语言 + if ( + !unref(isShowSevenLanguage) && + ![LangType.Chinese, LangType.English].includes(item.lang) + ) { + return false; + } + + return true; + }); + }); + + /** 当前多语言 */ + const currentLang = ref(getDefaultLang()); + + /** 当前多语言的文本 */ + const currentLangText = computed(() => { + const list = unref(allLangSwitchList); + const index = list.findIndex(item => item.lang === unref(currentLang)); + return index === -1 ? '' : list[index].text; + }); + + /** 当前是否为中文语言 */ + const isChineseLang = computed(() => unref(currentLang) === LangType.Chinese); + + /** + * 设置当前多语言 + * @param lang 多语言 + */ + function switchLang(lang: LangType) { + currentLang.value = lang; + local.set(LOCAL_STORAGE_KEY, lang); + + /** + * URL 带有 lang 参数,且参数值与保存的语言不一致时, + * 要删除参数值,否则下次进来还是参数值指定的语言。 + * 注意:IE <= 9 无效,因为不支持 history.replaceState + */ + if (history.replaceState) { + const search = location.search.replace(/([?&]lang=)(\w*)/, (match, $1, $2) => { + return $2 === lang ? match : $1; + }); + if (search !== location.search) { + history.replaceState(null, document.title, location.pathname + search + location.hash); + } + } + + const watchCore = getWatchCore(); + watchCore.updateAppConfig({ + language: lang === LangType.Chinese ? 'zh_CN' : 'en', + }); + + eventBus.$emit(appEvents.player.ResetUpPlayer); + } + + /** 根据当前语言转换游客名称 */ + function translateNick(nick: string): string { + let transNick = nick; + const nickPrefix = '观众'; + const nickPattern = new RegExp(nickPrefix + '/[0-9]+$'); + if (unref(currentLang) !== LangType.Chinese && nickPattern.test(nick)) { + transNick = `Guest/${nick.split('/')[1]}`; + } + return transNick; + } + + return { + englishSettingEnabled, + langSwitchEnabled, + isShowSevenLanguage, + japLangEnabled, + allLangSwitchList, + langSwitchList, + currentLang, + currentLangText, + isChineseLang, + + switchLang, + translateNick, + }; +}); diff --git a/src/store/use-layout-store.ts b/src/store/use-layout-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..1fe1cc646e4764617b809795ed8ab703af33ffee --- /dev/null +++ b/src/store/use-layout-store.ts @@ -0,0 +1,160 @@ +/** + * @file 布局 store + */ + +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; +import { ChannelSplashLayout, MainScreenContent } from '@polyv/live-watch-sdk'; +import { ScreenOrientationMode } from '@/hooks/core/use-screen-orient'; + +export interface LayoutStoreState { + /** 屏幕旋转模式 */ + screenOrientationMode: Ref; + /** 当前屏幕旋转角度是否为纵向 */ + isVerticalScreenOrientation: ComputedRef; + /** 当前屏幕旋转角度是否为横向 */ + isHorizontalScreenOrientation: ComputedRef; + /** 移动端下的引导页是否使用大图布局 */ + mobileSplashIsLarge: ComputedRef; + /** 主屏的内容 */ + mainScreen: Ref; + /** 副屏是否被收起 */ + subScreenPackUp: Ref; + /** 当前是否正在系统全屏 */ + isFullscreen: Ref; + /** PC 端下侧边菜单栏的激活 name */ + pcAsideTabCurrentName: Ref; + /** 移动端下底部菜单栏的激活 name */ + mobileMenuCurrentName: Ref; + /** 移动端下菜单栏 header 高度 */ + mobileMenuHeaderHeight: Ref; + /** 移动端布局顶部区域的高度 */ + mobileLayoutTopHeight: Ref; + /** 移动端布局底部区域的高度 */ + mobileLayoutBottomHeight: Ref; + /** 移动端布局底部区域到顶部的距离 */ + mobileLayoutBottomTop: Ref; + /** 移动端下播放器是否正在页面全屏 */ + mobilePlayerIsPageFullscreen: Ref; + /** 文档是否处于页面全屏 */ + mobileDocIsPageFullscreen: Ref; + /** 移动端聊天室背景地址 */ + mobileChatRoomBgUrl: ComputedRef; + /** 当前是页面全屏或者横屏 */ + isPageFullscreenOrHorizontalScreen: ComputedRef; +} + +export interface LayoutStoreActions { + /** 切换文档视频区域 */ + toggleMainScreen(): void; +} + +export interface LayoutStore extends LayoutStoreState, LayoutStoreActions {} + +export const useLayoutStore = defineStore<'layout', LayoutStore>('layout', () => { + const channelStore = useChannelStore(); + + /** 屏幕旋转模式 */ + const screenOrientationMode = ref(ScreenOrientationMode.Vertical); + + /** 当前屏幕旋转角度是否为纵向 */ + const isVerticalScreenOrientation = computed(() => { + return screenOrientationMode.value === ScreenOrientationMode.Vertical; + }); + + /** 当前屏幕旋转角度是否为横向 */ + const isHorizontalScreenOrientation = computed(() => { + return screenOrientationMode.value === ScreenOrientationMode.Horizontal; + }); + + /** 移动端下的引导页是否使用大图布局 */ + const mobileSplashIsLarge = computed(() => { + return channelStore.channelDetail?.channelConfig.splashLayout === ChannelSplashLayout.Large; + }); + + /** 主屏的内容 */ + const mainScreen = ref(MainScreenContent.Player); + + /** 切换文档视频区域 */ + function toggleMainScreen(): void { + if (mainScreen.value === MainScreenContent.Doc) { + mainScreen.value = MainScreenContent.Player; + } else { + mainScreen.value = MainScreenContent.Doc; + } + } + + /** 副屏是否被收起 */ + const subScreenPackUp = ref(false); + + /** 当前是否正在系统全屏 */ + const isFullscreen = ref(false); + + /** PC 端下侧边菜单栏的激活 name */ + const pcAsideTabCurrentName = ref(''); + + /** 移动端下底部菜单栏的激活 name */ + const mobileMenuCurrentName = ref(''); + + /** 移动端下菜单栏 header 高度 */ + const mobileMenuHeaderHeight = ref(40); + + /** 移动端布局顶部区域的高度 */ + const mobileLayoutTopHeight = ref(0); + + /** 移动端布局底部区域的高度 */ + const mobileLayoutBottomHeight = ref(0); + + /** 移动端下是否正在页面全屏 */ + const mobilePlayerIsPageFullscreen = ref(false); + + /** 文档是否处于页面全屏 */ + const mobileDocIsPageFullscreen = ref(false); + + /** 移动端布局底部区域到顶部的距离 */ + const mobileLayoutBottomTop = ref(0); + + /** 移动端聊天室背景地址 */ + const mobileChatRoomBgUrl = computed(() => { + const channelDetail = channelStore.channelDetail; + const chatBackgroundImage = channelDetail?.channelInfo.chatBackgroundImage; + const chatBackgroundImageOpacity = channelDetail?.channelInfo.chatBackgroundImageOpacity; + + if (!chatBackgroundImage) { + return; + } + + return chatBackgroundImageOpacity + ? `${chatBackgroundImage}?x-oss-process=image/blur,r_${chatBackgroundImageOpacity},s_${chatBackgroundImageOpacity}` + : chatBackgroundImage; + }); + + /** 当前是页面全屏或者横屏 */ + const isPageFullscreenOrHorizontalScreen = computed(() => { + return unref(mobilePlayerIsPageFullscreen) || unref(isHorizontalScreenOrientation); + }); + + return { + screenOrientationMode, + isVerticalScreenOrientation, + isHorizontalScreenOrientation, + + mobileSplashIsLarge, + mainScreen, + toggleMainScreen, + subScreenPackUp, + isFullscreen, + pcAsideTabCurrentName, + mobileMenuCurrentName, + mobileMenuHeaderHeight, + mobileLayoutTopHeight, + mobileLayoutBottomHeight, + mobileLayoutBottomTop, + mobilePlayerIsPageFullscreen, + mobileDocIsPageFullscreen, + mobileChatRoomBgUrl, + + isPageFullscreenOrHorizontalScreen, + }; +}); diff --git a/src/store/use-live-booking-store.ts b/src/store/use-live-booking-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e3874d9c61a528d915f1895015416811b6fe435 --- /dev/null +++ b/src/store/use-live-booking-store.ts @@ -0,0 +1,138 @@ +/** + * @file 直播预约 store + */ + +import { defineStore } from 'pinia'; +import { computed, ComputedRef, Ref, ref, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { YN } from '@polyv/live-watch-sdk'; +import { isMobile, isWeixin, isWorkWeixin } from '@/assets/utils/browser'; +import { useChannelInfoStore } from './use-channel-info-store'; + +export interface LiveBookingStoreState { + /** 是否开启了短信预约 */ + smsBookingEnabled: ComputedRef; + /** 是否开启了微信预约 */ + wxBookingEnabled: ComputedRef; + /** 微信预约弹层提示文案 */ + bookingTips: ComputedRef; + /** 是否使用微信预约 */ + useWxBooking: ComputedRef; + /** 是否显示直播预约按钮 */ + showLiveBookingButton: ComputedRef; + /** 是否已短信预约 */ + isSmsLiveBooking: Ref; + /** 是否已微信预约 */ + isWxLiveBooking: Ref; + /** 是否已预约直播 */ + isLiveBooking: ComputedRef; + /** 微信预约人数 */ + wxBookingCount: Ref; + /** 是否已关注了微信公众号 */ + isWxSubscribed: ComputedRef; + /** 微信公众号图片 */ + weixinMpImgUrl: ComputedRef; +} + +export interface LiveBookingStoreAction { + /** 初始化直播预约数据 */ + initLiveBookingData(): void; +} + +export interface LiveBookingStore extends LiveBookingStoreState, LiveBookingStoreAction {} + +export const useLiveBookingStore = defineStore<'liveBooking', LiveBookingStore>( + 'liveBooking', + () => { + const channelStore = useChannelStore(); + const channelInfoStore = useChannelInfoStore(); + + /** 是否开启了短信预约 */ + const smsBookingEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.smsBooking?.bookingEnabled, YN.N); + }); + + /** 是否开启了微信预约 */ + const wxBookingEnabled = computed(() => { + return ynToBool(channelStore.channelDetail?.wxBooking?.bookingEnabled, YN.N); + }); + + /** 微信预约弹层提示文案 */ + const bookingTips = computed(() => { + return channelStore.channelDetail?.channelConfig.bookingTips || ''; + }); + + /** 是否使用微信预约 */ + const useWxBooking = computed(() => { + return isMobile && isWeixin && !isWorkWeixin && unref(wxBookingEnabled); + }); + + /** 是否显示直播预约按钮 */ + const showLiveBookingButton = computed(() => { + // 没有设置开始时间 + if (!channelInfoStore.liveStartTime) { + return false; + } + + // 倒计时结束 + if (channelInfoStore.isLiveStartCountDownEnd) { + return false; + } + + // 微信环境 + if (unref(useWxBooking)) { + return unref(wxBookingEnabled); + } + + // 非微信环境 + return unref(smsBookingEnabled); + }); + + /** 是否已短信预约 */ + const isSmsLiveBooking = ref(false); + + /** 是否已微信预约 */ + const isWxLiveBooking = ref(false); + + /** 是否已预约直播 */ + const isLiveBooking = computed(() => { + return unref(useWxBooking) ? unref(isWxLiveBooking) : unref(isSmsLiveBooking); + }); + + /** 微信预约人数 */ + const wxBookingCount = ref(0); + + /** 是否已关注了微信公众号 */ + const isWxSubscribed = computed(() => { + return ynToBool(channelStore.channelDetail?.wxBooking?.isSubscribed, YN.N); + }); + + /** 微信公众号图片 */ + const weixinMpImgUrl = computed(() => { + return channelStore.channelDetail?.wxBooking?.weixinMpImgUrl || ''; + }); + + /** 初始化直播预约数据 */ + function initLiveBookingData(): void { + isSmsLiveBooking.value = ynToBool(channelStore.channelDetail?.smsBooking?.booking, YN.N); + isWxLiveBooking.value = ynToBool(channelStore.channelDetail?.wxBooking?.isWxBooking, YN.N); + wxBookingCount.value = channelStore.channelDetail?.wxBooking?.bookingCount || 0; + } + + return { + smsBookingEnabled, + wxBookingEnabled, + bookingTips, + useWxBooking, + showLiveBookingButton, + isSmsLiveBooking, + isWxLiveBooking, + isLiveBooking, + wxBookingCount, + isWxSubscribed, + weixinMpImgUrl, + initLiveBookingData, + }; + }, +); diff --git a/src/store/use-micro-activity-store.ts b/src/store/use-micro-activity-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..db41ed2ed167c8e90c65b7b5ed634718e87a3cac --- /dev/null +++ b/src/store/use-micro-activity-store.ts @@ -0,0 +1,52 @@ +import { computed, ComputedRef } from 'vue'; +import { defineStore } from 'pinia'; + +import { useChannelStore } from './use-channel-store'; +import { useChannelMenuStore } from './use-channel-menu-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { YN } from '@polyv/live-watch-sdk'; + +export interface MicroActivityStoreState { + /** 微活动开关 */ + microActivityEnabled: ComputedRef; + /** 微活动名称 */ + microActivityTitle: ComputedRef; + /** 微活动-挂件Icon */ + microActivityPendantIcon: ComputedRef; +} + +export type MicroActivityStore = MicroActivityStoreState; + +export const useMicroActivityStore = defineStore<'microActivity', MicroActivityStore>( + 'microActivity', + () => { + const channelStore = useChannelStore(); + const channelMenuStore = useChannelMenuStore(); + + /** 微活动开关 */ + const microActivityEnabled = computed(() => { + return ( + channelMenuStore.microActivityMenuData.visible || + ynToBool( + channelStore.channelDetail?.interactionSetting.microActivity.microActivityEnabled || YN.N, + ) + ); + }); + + /** 微活动名称 */ + const microActivityTitle = computed(() => { + return channelMenuStore.microActivityMenuData.label; + }); + + /** 微活动-挂件Icon */ + const microActivityPendantIcon = computed(() => { + return channelStore.channelDetail?.interactionSetting.microActivity.pendantIcon || ''; + }); + + return { + microActivityEnabled, + microActivityTitle, + microActivityPendantIcon, + }; + }, +); diff --git a/src/store/use-page-store.ts b/src/store/use-page-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab7d8a2296fd25d1d9ef3b6be69d128b8adcb782 --- /dev/null +++ b/src/store/use-page-store.ts @@ -0,0 +1,53 @@ +import { defineStore } from 'pinia'; +import { ref, Ref } from 'vue'; +import { ChannelWatchPageSkin } from '@polyv/live-watch-sdk'; + +export interface PageStoreState { + /** 页面显示状态 */ + visibilityState: Ref; + /** 页面宽度 */ + pageClientWidth: Ref; + /** 页面高度 */ + pageClientHeight: Ref; + /** 页面皮肤 */ + pageSkin: Ref; +} + +export interface PageStoreAction { + /** 重新获取页面尺寸 */ + resetUpPageSize: () => void; +} + +export interface PageStore extends PageStoreState, PageStoreAction {} + +/** + * 页面相关的 store + */ +export const usePageStore = defineStore<'page', PageStore>('page', () => { + /** 页面显示状态 */ + const visibilityState = ref(true); + + /** 页面宽度 */ + const pageClientWidth = ref(document.documentElement.clientWidth); + + /** 页面高度 */ + const pageClientHeight = ref(document.documentElement.clientHeight); + + /** 页面皮肤 */ + const pageSkin = ref(); + + /** 重新获取页面尺寸 */ + function resetUpPageSize() { + pageClientWidth.value = document.documentElement.clientWidth; + pageClientHeight.value = document.documentElement.clientHeight; + } + + return { + visibilityState, + pageClientWidth, + pageClientHeight, + pageSkin, + + resetUpPageSize, + }; +}); diff --git a/src/store/use-playback-store.ts b/src/store/use-playback-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..b8de56cbea7c60dd6603aa043a6e543a4e4ce552 --- /dev/null +++ b/src/store/use-playback-store.ts @@ -0,0 +1,220 @@ +/** + * @file 回放 store + */ + +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { + LiveStatus, + PlaybackMode, + PlaybackOrigin, + PlaybackTarget, + YN, +} from '@polyv/live-watch-sdk'; +import { getWatchCore } from '@/core/watch-sdk'; +import { useChannelStore } from './use-channel-store'; +import { ynToBool } from '@utils-ts/boolean'; +import { useWatchAppStore } from './use-watch-app-store'; +import { paramGetter } from '@/hooks/core/use-query-params'; + +export interface PlaybackStoreState { + /** 当前是否正在播放回放 */ + isPlaybacking: Ref; + /** 回放方式 */ + playbackMode: ComputedRef; + /** 当前已加载的回放页数,默认:0 */ + pageNumber: Ref; + /** 每页数量 */ + pageSize: Ref; + /** 总页数 */ + totalPages: Ref; + /** 是否正在加载中 */ + isLoading: Ref; + /** 是否已加载完毕 */ + isNoMore: Ref; + /** 已加载的列表 */ + playbackList: Ref; + /** 当前页的回放列表 */ + currentPagePlaybackList: ComputedRef; + /** 当前播放的回放对象 */ + currentPlaybackTarget: Ref; + /** 章节是否显示 */ + chapterEnabled: ComputedRef; + /** 回放列表是否显示 */ + playbackListVisible: ComputedRef; +} + +export interface PlaybackStoreActions { + /** 初始化当前回放对象 */ + initCurrentPlaybackTarget(): Promise; + /** 初始化回放列表 */ + initPlaybackList(): Promise; + /** 加载下一批回放 */ + loadNextPlaybackList(): Promise; + /** 监听分页器切换事件 */ + onPageChange(nextPageNumber: number): Promise; +} + +export interface PlaybackStore extends PlaybackStoreState, PlaybackStoreActions {} + +export const usePlaybackStore = defineStore<'playback', PlaybackStore>('playback', () => { + const watchAppStore = useWatchAppStore(); + const channelStore = useChannelStore(); + + const isPlaybacking = ref(false); + + /** 回放方式 */ + const playbackMode = computed(() => { + if (watchAppStore.isWatchBackUrl) return PlaybackMode.Single; + + const channelDetail = channelStore.channelDetail; + return channelDetail?.playbackInfo?.type; + }); + + /** 当前已加载的回放页数,默认:0 */ + const pageNumber = ref(0); + + /** 每页数量 */ + const pageSize = ref(12); + + /** 总页数 */ + const totalPages = ref(1); + + /** 是否正在加载中 */ + const isLoading = ref(false); + + /** 是否已加载完毕 */ + const isNoMore = ref(false); + + /** 已加载的列表 */ + const playbackList = ref([]); + + /** 当前页的回放列表 */ + const currentPagePlaybackList = computed(() => { + if (pageNumber.value === 0) { + return []; + } + const pageNumberVal = unref(pageNumber); + const pageSizeVal = unref(pageSize); + const start = (pageNumberVal - 1) * pageSizeVal; + const end = start + pageSizeVal; + return playbackList.value.slice(start, end); + }); + + /** 当前播放的回放对象 */ + const currentPlaybackTarget = ref(); + + /** 章节是否显示 */ + const chapterEnabled = computed(() => { + const channelDetail = channelStore.channelDetail; + const sectionEnabled = channelDetail?.playbackInfo?.sectionEnabled; + return isPlaybacking.value && ynToBool(sectionEnabled, YN.N); + }); + + /** 回放列表是否显示 */ + const playbackListVisible = computed(() => { + if (watchAppStore.isWatchBackUrl) return false; + + return unref(isPlaybacking) && playbackMode.value === PlaybackMode.List; + }); + + /** + * 初始化当前回放对象 + * @warn 副作用:如果为列表回放,会初始化一次回放列表数据 + * */ + async function initCurrentPlaybackTarget() { + if (channelStore.liveStatus !== LiveStatus.Playback) { + currentPlaybackTarget.value = undefined; + return; + } + + // 列表回放时,则先获取一次回放列表并设置回放对象 + if (playbackMode.value === PlaybackMode.List) { + await initPlaybackList(); + if (playbackList.value.length) { + currentPlaybackTarget.value = playbackList.value[0]; + } + return; + } + + // 单个回放时使用 detail 中的回放数据 + if (playbackMode.value === PlaybackMode.Single) { + const watchCore = getWatchCore(); + const playbackTarget = watchCore.playback.getTargetPlaybackOption({ + playbackOrigin: watchAppStore.isWatchBackUrl + ? paramGetter.playbackType() || PlaybackOrigin.Playback // 回放地址的回放来源只有“回放列表”和“点播列表” + : undefined, + }); + currentPlaybackTarget.value = playbackTarget; + } + } + + /** 初始化回放列表 */ + async function initPlaybackList() { + pageNumber.value = 0; + isLoading.value = false; + isNoMore.value = false; + playbackList.value = []; + await loadNextPlaybackList(); + } + + /** 加载下一批回放 */ + async function loadNextPlaybackList() { + if (isNoMore.value || isLoading.value) { + return; + } + + isLoading.value = true; + try { + const watchCore = getWatchCore(); + const data = await watchCore.playback.getPlaybackList({ + pageNumber: unref(pageNumber) + 1, + pageSize: unref(pageSize), + }); + + pageNumber.value = data.pageNumber; + isNoMore.value = data.pageNumber >= data.totalPages; + totalPages.value = data.totalPages; + playbackList.value.push(...data.contents); + isLoading.value = false; + } catch (e) { + isLoading.value = false; + } + } + + /** 监听分页器切换事件 */ + async function onPageChange(nextPageNumber: number) { + if (nextPageNumber === pageNumber.value) return; + + if (nextPageNumber < pageNumber.value) { + pageNumber.value = pageNumber.value - 1; + return; + } + + if (isNoMore.value) { + pageNumber.value = pageNumber.value + 1; + } else { + await loadNextPlaybackList(); + } + } + + return { + isPlaybacking, + playbackMode, + pageNumber, + pageSize, + totalPages, + isLoading, + isNoMore, + playbackList, + playbackListVisible, + currentPagePlaybackList, + currentPlaybackTarget, + chapterEnabled, + + initCurrentPlaybackTarget, + initPlaybackList, + loadNextPlaybackList, + onPageChange, + }; +}); diff --git a/src/store/use-player-store.ts b/src/store/use-player-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..8084ebd3a10d09d17a9e379dc088ba32a7fce889 --- /dev/null +++ b/src/store/use-player-store.ts @@ -0,0 +1,341 @@ +/** + * @file 播放器 store + */ +import { getWatchCore } from '@/core/watch-sdk'; +import { + PlayStatus, + QualityLevelItem, + LiveStatus, + WarmUpType, + PlayerLogoPosition, +} from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; +import { usePlaybackStore } from './use-playback-store'; + +export interface PlayerStoreState { + /** 配置是否使用播放器 */ + playerEnabled: Ref; + /** 是否处于可播放的模式 */ + isPlayMode: ComputedRef; + /** 播放器是否已初始化 */ + playerInited: Ref; + /** 是否已开始播放 */ + isPlayStarted: Ref; + /** 当前播放状态 */ + playStatus: Ref; + /** 暖场类型 */ + warmUpType: Ref; + /** 暖场图片/播放器封面图 */ + warmUpImg: Ref; + /** 暖场图片跳转地址 */ + warmUpHref: Ref; + /** 是否显示播放器暖场图片 */ + showWarmUpImg: ComputedRef; + /** 是否显示播放器在“音频直播”场景下的占位区域 */ + showAudioLivePlaceholder: ComputedRef; + /** 播放器 logo 图片地址 */ + logoImage: ComputedRef; + /** 播放器 logo 跳转地址 */ + logoHref: ComputedRef; + /** 播放器 logo 透明度 */ + logoOpacity: ComputedRef; + /** 播放器 logo 位置 */ + logoPosition: ComputedRef; + /** 观看延迟时间,单位:毫秒 */ + delayTime: Ref; + /** 是否支持无延迟观看 */ + supportLowLatency: Ref; + /** 是否无延迟观看 */ + isLowLatency: Ref; + /** 是否支持刷新 api */ + supportRefresh: Ref; + /** 是否支持自动播放 */ + supportAutoPlay: Ref; + /** 当前音量 */ + currentVolume: Ref; + /** 后台中的弹幕开关 */ + barrageBackstageEnabled: Ref; + /** 弹幕显示状态 */ + barrageShow: Ref; + /** 总线路数 */ + lineCount: Ref; + /** 当前线路索引 */ + currentLine: Ref; + /** 可选的清晰度列表 */ + qualityLevels: Ref; + /** 当前清晰度级别 */ + currentQualityLevel: Ref; + /** 可选的倍速列表 */ + rateList: Ref; + /** 当前倍速 */ + currentRate: Ref; + /** 当前播放时间,单位:秒 */ + currentTime: Ref; + /** 当前播放总时长,单位:秒 */ + durationTime: Ref; + /** 视频流宽度,仅用作比例计算 */ + resolutionWidth: Ref; + /** 视频流高度,仅用作比例计算 */ + resolutionHeight: Ref; + /** 视频流是否为竖状比例 */ + isPortraitResolutionRate: ComputedRef; + /** 暂无直播是否显示 */ + noneLiveVisible: ComputedRef; + /** 播放器控制栏是否显示 */ + playerControlVisible: Ref; + /** 是否为音频播放器模式。注意,目前只有竖屏在使用 */ + isAudioPlayerMode: Ref; +} + +export interface PlayerStoreAction { + /** 同步播放器信息 */ + syncPlayerInfo: () => void; +} + +export interface PlayerStore extends PlayerStoreState, PlayerStoreAction {} + +export const usePlayerStore = defineStore<'player', PlayerStore>('player', () => { + const channelStore = useChannelStore(); + const playbackStore = usePlaybackStore(); + + /** 配置是否使用播放器 */ + const playerEnabled = ref(true); + + /** 播放器是否已初始化 */ + const playerInited = ref(false); + + /** 是否已开始播放 */ + const isPlayStarted = ref(false); + + /** 当前播放状态 */ + const playStatus = ref(PlayStatus.Pause); + + /** 是否支持无延迟观看 */ + const supportLowLatency = ref(false); + + /** 是否无延迟观看 */ + const isLowLatency = ref(false); + + /** 暖场类型 */ + const warmUpType = ref(); + + /** 暖场图片/播放器封面图 */ + const warmUpImg = ref(); + + /** 暖场图片跳转地址 */ + const warmUpHref = ref(); + + /** 是否显示播放器暖场图片 */ + const showWarmUpImg = computed(() => { + if (playbackStore.isPlaybacking || channelStore.liveStatus !== LiveStatus.Live) { + return false; + } + + const isNotPlaying = playStatus.value !== PlayStatus.Playing; + return unref(playerInited) && !!unref(warmUpImg) && (!unref(isPlayStarted) || isNotPlaying); + }); + + /** 是否显示播放器在“音频直播”场景下的占位区域 */ + const showAudioLivePlaceholder = computed(() => { + const isPlaying = playStatus.value === PlayStatus.Playing; + return unref(playerInited) && isPlaying && unref(channelStore.isOnlyAudioLive); + }); + + /** 播放器 logo 图片地址 */ + const logoImage = computed(() => { + return channelStore.channelDetail?.playerSetting?.logoImage ?? undefined; + }); + + /** 播放器 logo 跳转地址 */ + const logoHref = computed(() => { + return channelStore.channelDetail?.playerSetting?.logoHref ?? undefined; + }); + + /** 播放器 logo 透明度 */ + const logoOpacity = computed(() => { + return channelStore.channelDetail?.playerSetting?.logoOpacity ?? 1; + }); + + /** 播放器 logo 位置 */ + const logoPosition = computed(() => { + return channelStore.channelDetail?.playerSetting?.logoPosition ?? 'tl'; + }); + + /** 是否处于一个可播放的模式 */ + const isPlayMode = computed(() => { + if (!playerEnabled.value) return false; + + // 回放中 + if (playbackStore.isPlaybacking) { + return true; + } + + // 直播中 + if (channelStore.liveStatus === LiveStatus.Live) { + return true; + } + + // 没有直播中,但有暖场视频 + if (warmUpType.value === WarmUpType.Video) { + return true; + } + + return false; + }); + + /** 播放器延迟时间 */ + const delayTime = ref(0); + + /** 是否支持刷新 api */ + const supportRefresh = ref(false); + + /** 是否支持自动播放 */ + const supportAutoPlay = ref(false); + + /** 当前音量 */ + const currentVolume = ref(0.5); + + /** 后台中的弹幕开关 */ + const barrageBackstageEnabled = ref(false); + + /** 弹幕显示状态 */ + const barrageShow = ref(true); + + /** 总线路数 */ + const lineCount = ref(0); + + /** 当前线路索引 */ + const currentLine = ref(0); + + /** 可选的清晰度列表 */ + const qualityLevels = ref([]); + + /** 当前清晰度级别 */ + const currentQualityLevel = ref(0); + + /** 可选的倍速列表 */ + const rateList = ref([]); + + /** 当前倍速 */ + const currentRate = ref(0); + + /** 当前播放时间 */ + const currentTime = ref(0); + + /** 当前播放总时长 */ + const durationTime = ref(0); + + /** 视频流宽度,仅用作比例计算 */ + const resolutionWidth = ref(); + + /** 视频流高度,仅用作比例计算 */ + const resolutionHeight = ref(); + + /** 视频流是否为竖状比例 */ + const isPortraitResolutionRate = computed(() => { + const width = unref(resolutionWidth); + const height = unref(resolutionHeight); + if (!width || !height) { + return false; + } + return height > width; + }); + + /** 暂无直播是否显示 */ + const noneLiveVisible = computed(() => { + // 直播中 + if (channelStore.liveStatus === LiveStatus.Live) { + return false; + } + + // 回放中 + if (playbackStore.isPlaybacking) { + return false; + } + + // 非直播中但存在暖场视频 + if (warmUpType.value === WarmUpType.Video) { + return false; + } + + return ( + // 播放器初始化完毕 + unref(playerInited) && + // 没显示暖场 + !unref(showWarmUpImg) + ); + }); + + /** 是否为音频播放器模式 */ + const isAudioPlayerMode = ref(false); + + /** 播放器控制栏是否显示 */ + const playerControlVisible = ref(false); + + /** 同步播放器信息 */ + function syncPlayerInfo(): void { + const watchCore = getWatchCore(); + const playerInfo = watchCore.player.getPlayerInfo(); + + warmUpType.value = playerInfo.warmUpType; + warmUpImg.value = playerInfo.warmUpImg; + warmUpHref.value = playerInfo.warmUpHref; + delayTime.value = playerInfo.delayTime; + supportRefresh.value = playerInfo.supportRefresh; + supportAutoPlay.value = playerInfo.supportAutoPlay; + barrageBackstageEnabled.value = playerInfo.barrageBackstageEnabled; + playStatus.value = playerInfo.playStatus; + lineCount.value = playerInfo.lineCount; + currentLine.value = playerInfo.currentLine; + qualityLevels.value = playerInfo.qualityLevels; + currentQualityLevel.value = playerInfo.currentQualityLevel; + rateList.value = playerInfo.rateList; + currentRate.value = playerInfo.currentRate; + currentTime.value = playerInfo.currentTime; + durationTime.value = playerInfo.durationTime; + supportLowLatency.value = playerInfo.supportLowLatency; + isLowLatency.value = playerInfo.isLowLatency; + } + + return { + playerEnabled, + isAudioPlayerMode, + isPlayMode, + playerInited, + isPlayStarted, + playStatus, + warmUpType, + warmUpImg, + warmUpHref, + showWarmUpImg, + showAudioLivePlaceholder, + logoImage, + logoHref, + logoOpacity, + logoPosition, + delayTime, + supportLowLatency, + isLowLatency, + supportRefresh, + supportAutoPlay, + currentVolume, + barrageBackstageEnabled, + barrageShow, + lineCount, + currentLine, + qualityLevels, + currentQualityLevel, + rateList, + currentRate, + currentTime, + durationTime, + resolutionWidth, + resolutionHeight, + isPortraitResolutionRate, + noneLiveVisible, + playerControlVisible, + syncPlayerInfo, + }; +}); diff --git a/src/store/use-portrait-layout-store.ts b/src/store/use-portrait-layout-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..14dbddf1102b305b66ab8f73debcf75830f24fcc --- /dev/null +++ b/src/store/use-portrait-layout-store.ts @@ -0,0 +1,103 @@ +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref } from 'vue'; +import { LiveStatus } from '@polyv/live-watch-sdk'; +import { useChannelStore } from './use-channel-store'; +import { useDocStore } from './use-doc-store'; +import { usePageStore } from './use-page-store'; +import { usePlaybackStore } from './use-playback-store'; + +export interface PortraitLayoutStoreState { + /** 竖屏轮播屏当前索引 */ + carouselActiveIndex: Ref; + /** 竖屏页面背景地址 */ + portraitPageBgUrl: ComputedRef; + /** 竖屏文档容器内容渲染高度 */ + portraitDocContentHeight: ComputedRef; + /** 竖屏文档容器渲染高度 */ + portraitDocWrapHeight: ComputedRef; + /** 频道信息位置 */ + channelInfoSeat: ComputedRef<0 | 1>; +} + +/** + * 竖屏布局 store + */ +export const usePortraitLayoutStore = defineStore<'portraitLayout', PortraitLayoutStoreState>( + 'portraitLayout', + () => { + const channelStore = useChannelStore(); + const pageStore = usePageStore(); + const playbackStore = usePlaybackStore(); + const docStore = useDocStore(); + + /** 竖屏轮播屏当前索引 */ + const carouselActiveIndex = ref(1); + + /** 竖屏页面背景地址 */ + const portraitPageBgUrl = computed(() => { + const channelDetail = channelStore.channelDetail; + const portraitChatBgImg = channelDetail?.channelInfo.portraitChatBgImg; + const portraitChatBgImgOpacity = channelDetail?.channelInfo.portraitChatBgImgOpacity; + + if (!portraitChatBgImg) { + return; + } + + return portraitChatBgImgOpacity + ? `${portraitChatBgImg}?x-oss-process=image/blur,r_${portraitChatBgImgOpacity},s_${portraitChatBgImgOpacity}` + : portraitChatBgImg; + }); + + /** 竖屏文档容器内容渲染高度 */ + const portraitDocContentHeight = computed(() => { + const pageClientWidth = pageStore.pageClientWidth; + const documentProportion = docStore.documentProportion; + + // 最小高度为 9:16 + const minHeight = pageClientWidth * (9 / 16); + // 最大高度为 3:4 + const maxHeight = pageClientWidth * (3 / 4); + + if (!documentProportion) { + return minHeight; + } + + let wrapHeight = pageClientWidth / documentProportion; + if (wrapHeight >= maxHeight) { + wrapHeight = maxHeight; + } + if (wrapHeight <= minHeight) { + wrapHeight = minHeight; + } + return wrapHeight; + }); + + /** 竖屏文档容器渲染高度 */ + const portraitDocWrapHeight = computed(() => { + if (!docStore.canRenderDoc || !docStore.documentSwitch) { + return 0; + } + + return portraitDocContentHeight.value; + }); + + /** 频道信息位置 */ + const channelInfoSeat = computed<0 | 1>(() => { + if ( + channelStore.isPptChannel && + (channelStore.liveStatus === LiveStatus.Live || playbackStore.isPlaybacking) + ) { + return 0; + } + return 1; + }); + + return { + carouselActiveIndex, + portraitPageBgUrl, + portraitDocContentHeight, + portraitDocWrapHeight, + channelInfoSeat, + }; + }, +); diff --git a/src/store/use-score-store.ts b/src/store/use-score-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..acd84b775c0bdcf24b3d2f72e561a2f1d72247fc --- /dev/null +++ b/src/store/use-score-store.ts @@ -0,0 +1,40 @@ +/** + * @file 积分 store + */ +import { RedpackCurrencyType } from '@polyv/live-watch-sdk'; +import { defineStore } from 'pinia'; +import { ComputedRef, computed, unref } from 'vue'; +import { useChannelStore } from './use-channel-store'; +import { useDonateStore } from './use-donate-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; + +export interface ScoreStore { + /** 积分单位 */ + scoreUnit: ComputedRef; + /** 积分红包是否开启 */ + scoreRedpackEnabled: ComputedRef; +} + +export const useScoreStore = defineStore<'score', ScoreStore>('score', () => { + const { channelDetail } = storeDefinitionToRefs(useChannelStore); + const { donateSetting } = storeDefinitionToRefs(useDonateStore); + + /** 积分单位 */ + const scoreUnit = computed(() => { + return unref(donateSetting)?.scoreUnit || ''; + }); + + /** 积分红包是否开启 */ + const scoreRedpackEnabled = computed(() => { + const redPackCurrencyType = unref(channelDetail)?.channelConfig.redPackCurrencyType; + return ( + redPackCurrencyType === RedpackCurrencyType.Score || + redPackCurrencyType === RedpackCurrencyType.CashAndScore + ); + }); + + return { + scoreUnit, + scoreRedpackEnabled, + }; +}); diff --git a/src/store/use-send-redpack-store.ts b/src/store/use-send-redpack-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..e3a21e88d212f97e3448ae0bc6e1482df40510d4 --- /dev/null +++ b/src/store/use-send-redpack-store.ts @@ -0,0 +1,73 @@ +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref } from 'vue'; +import { ynToBool } from '@utils-ts/boolean'; +import { isWeixin, isWorkWeixin } from '@/assets/utils/browser'; +import { useWeixinStore } from '@/store/use-weixin-store'; +import { useChannelStore } from '@/store/use-channel-store'; +import { storeDefinitionToRefs } from '@/plugins/pinia/util'; +import { YN } from '@polyv/live-watch-sdk'; + +const isGeneralWeixin = isWeixin && !isWorkWeixin; + +export interface SendRedpackStoreState { + /** 是否隐藏"发送红包"功能 */ + sendRedpackHidden: ComputedRef; + /** 是否开启了红包金额、数量限制 */ + redPackCustomAmountEnabled: ComputedRef; + /** 观众发红包最大金额限制 */ + redPackViewerMaxAmount: ComputedRef; + /** 观众发红包最大数量限制 */ + redPackViewerMaxCount: ComputedRef; + /** 观众发红包最低均分 */ + redPackViewerMinAverage: Ref; +} + +export type SendRedpackStore = SendRedpackStoreState; + +export const useSendRedpackStore = defineStore<'sendRedpack', SendRedpackStore>( + 'sendRedpack', + () => { + const { channelDetail } = storeDefinitionToRefs(useChannelStore); + const { weixinPayEnabled, weixinAccountFunctionEnabled } = + storeDefinitionToRefs(useWeixinStore); + + /** "红包"功能是否在配置层面上可用 */ + const redPackEnabled = computed(() => { + return ( + ynToBool(channelDetail.value?.channelConfig.redpackEnabled || YN.N) && + weixinAccountFunctionEnabled.value + ); + }); + + /** 是否隐藏"发送红包"功能 */ + const sendRedpackHidden = computed(() => { + return !weixinPayEnabled.value || !isGeneralWeixin || !redPackEnabled.value; + }); + + /** 是否开启了红包金额、数量限制 */ + const redPackCustomAmountEnabled = computed(() => + ynToBool(channelDetail.value?.channelConfig.redPackCustomAmountEnabled || 'N'), + ); + + /** 观众发红包最大金额限制 */ + const redPackViewerMaxAmount = computed( + () => channelDetail.value?.channelConfig.redPackViewerMaxAmount || 300, + ); + + /** 观众发红包最大数量限制 */ + const redPackViewerMaxCount = computed( + () => channelDetail.value?.channelConfig.redPackViewerMaxCount || 50, + ); + + /** 观众发红包最低均分 */ + const redPackViewerMinAverage = ref(0.3); + + return { + sendRedpackHidden, + redPackCustomAmountEnabled, + redPackViewerMaxAmount, + redPackViewerMaxCount, + redPackViewerMinAverage, + }; + }, +); diff --git a/src/store/use-share-store.ts b/src/store/use-share-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..7efe2b84b826994e621bc02fe832c580cc36b306 --- /dev/null +++ b/src/store/use-share-store.ts @@ -0,0 +1,245 @@ +/** + * @file 分享 store + */ +import { + DEFAULT_SHARE_QQ_ICON, + DEFAULT_SHARE_QZONE_ICON, + DEFAULT_SHARE_WEIBO_ICON, +} from '@/assets/constants/defaults'; +import { paramGetter } from '@/hooks/core/use-query-params'; +import { translate } from '@/assets/lang'; +import { copyText, toastCopySuccess } from '@/assets/utils/copy'; +import { getWatchCore } from '@/core/watch-sdk'; +import { appendToURL } from '@just4/querystring'; +import { YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { changeProtocol } from '@utils-ts/net'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, unref } from 'vue'; +import { useChannelInfoStore } from './use-channel-info-store'; +import { useChannelStore } from './use-channel-store'; + +/** Web 分享平台枚举 */ +enum WebShareType { + /** QQ 分享 */ + QQ = 'qq', + /** QQ 空间分享 */ + Qzone = 'qzone', + /** 微博分享 */ + Weibo = 'weibo', + /** facebook 分享 */ + Facebook = 'facebook', + /** 推特分享 */ + Twitter = 'twitter', +} + +/** Web 分享数据类型 */ +type WebShareData = { + /** 标题 */ + title: string; + /** 类型 */ + type: WebShareType; + /** 分享链接 */ + url: string; + /** 图标地址 */ + icon: string; +}; + +/** 各分享平台需要的参数类型 */ +type WebShareProvideParams = { + [WebShareType.QQ]: Record<'url' | 'title' | 'desc' | 'source' | 'pics', string>; + [WebShareType.Qzone]: Record<'url' | 'title' | 'site', string>; + [WebShareType.Weibo]: Record<'url' | 'title' | 'pic', string>; + [WebShareType.Facebook]: Record<'u', string>; + [WebShareType.Twitter]: Record<'url' | 'text', string>; +}; + +/** 各分享平台的跳转地址 */ +const WebShareRedirectUrl: Record = { + [WebShareType.QQ]: 'http://connect.qq.com/widget/shareqq/index.html', + [WebShareType.Qzone]: 'http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey', + [WebShareType.Weibo]: 'https://service.weibo.com/share/share.php', + [WebShareType.Facebook]: 'https://www.facebook.com/sharer/sharer.php', + [WebShareType.Twitter]: 'https://twitter.com/intent/tweet', +}; + +export interface ShareStoreState { + /** 分享按钮开关 */ + shareBtnEnabled: ComputedRef; + /** PC web 自定义分享地址 */ + webShareCustomUrl: ComputedRef; + /** PC web 分享的观看地址 */ + shareWatchUrl: ComputedRef; + /** PC web 分享的观看地址二维码地址 */ + shareQrCodeUrl: ComputedRef; + /** QQ 分享跳转地址 */ + qqShareUrl: ComputedRef; + /** QQ 空间分享跳转地址 */ + qzoneShareUrl: ComputedRef; + /** 微博分享跳转地址 */ + weiboShareUrl: ComputedRef; + /** facebook 分享跳转地址 */ + facebookShareUrl: ComputedRef; + /** 推特分享跳转地址 */ + twitterShareUrl: ComputedRef; + /** PC web 分享列表 */ + webShareList: ComputedRef; +} + +export interface ShareStoreAction { + /** 生成 web 分享地址 */ + composeShareUrl: ( + webShareType: T, + provideParams: WebShareProvideParams[T], + ) => string; + /** 复制分享的观看页地址 */ + copyShareWatchUrl: () => Promise; +} + +/** 分享 store */ +export interface ShareStore extends ShareStoreState, ShareStoreAction {} + +export const useShareStore = defineStore<'share', ShareStore>('share', () => { + const channelStore = useChannelStore(); + + const channelInfoStore = useChannelInfoStore(); + + /** 分享按钮开关 */ + const shareBtnEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig?.shareBtnEnabled, YN.N), + ); + + /** PC web 自定义分享地址 */ + const webShareCustomUrl = computed( + () => channelStore.channelDetail?.channelInfo.webShareCustomUrl ?? undefined, + ); + + /** PC web 分享的观看地址 */ + const shareWatchUrl = computed(() => { + // 分享时需带上的参数(如果当前访问url存在这些参数的话) + const query = { + promoteId: channelStore.promoteId, + vid: paramGetter.vid(), + playbackType: paramGetter.playbackType(), + }; + + // 优先使用 web 自定义分享地址 + const customUrl = unref(webShareCustomUrl); + if (customUrl) { + return decodeURIComponent(customUrl); + } + + return appendToURL(channelStore.watchUrl, query, { + ignoreEmpty: true, + }); + }); + + /** PC web 分享的观看地址二维码地址 */ + const shareQrCodeUrl = computed(() => { + const watchCore = getWatchCore(); + return watchCore.utils.generateQrcodeUrl(unref(shareWatchUrl)); + }); + + /** QQ 分享跳转地址 */ + const qqShareUrl = computed(() => { + return composeShareUrl(WebShareType.QQ, { + url: unref(shareWatchUrl), + title: channelInfoStore.channelTitle, + source: channelInfoStore.channelTitle, + desc: '', // qq 分享中描述过长会分享失败 + pics: changeProtocol(channelInfoStore.channelCoverImg, 'http'), // qq 分享的图片需要协议头 + }); + }); + + /** QQ 空间分享跳转地址 */ + const qzoneShareUrl = computed(() => { + return composeShareUrl(WebShareType.Qzone, { + url: unref(shareWatchUrl), + title: channelInfoStore.channelTitle, + site: channelInfoStore.channelTitle, + }); + }); + + /** 微博分享跳转地址 */ + const weiboShareUrl = computed(() => { + return composeShareUrl(WebShareType.Weibo, { + url: unref(shareWatchUrl), + title: channelInfoStore.channelTitle, + pic: channelInfoStore.channelCoverImg, + }); + }); + + /** facebook 分享跳转地址 */ + const facebookShareUrl = computed(() => { + return composeShareUrl(WebShareType.Facebook, { + u: unref(shareWatchUrl), + }); + }); + + /** 推特分享跳转地址 */ + const twitterShareUrl = computed(() => { + return composeShareUrl(WebShareType.Twitter, { + url: unref(shareWatchUrl), + text: channelInfoStore.channelTitle, + }); + }); + + /** 分享列表 */ + const webShareList = computed(() => { + return [ + { + title: translate('share.QQ'), + type: WebShareType.QQ, + url: unref(qqShareUrl), + icon: DEFAULT_SHARE_QQ_ICON, + }, + { + title: translate('share.QZone'), + type: WebShareType.Qzone, + url: unref(qzoneShareUrl), + icon: DEFAULT_SHARE_QZONE_ICON, + }, + { + title: translate('share.weibo'), + type: WebShareType.Weibo, + url: unref(weiboShareUrl), + icon: DEFAULT_SHARE_WEIBO_ICON, + }, + ]; + }); + + /** + * 生成分享类型 + * @param webShareType 分享类型 + * @param provideParams 传入参数 + */ + function composeShareUrl( + webShareType: T, + provideParams: WebShareProvideParams[T], + ): string { + const baseUrl = WebShareRedirectUrl[webShareType]; + return appendToURL(baseUrl, provideParams); + } + + /** 复制分享的观看地址 */ + async function copyShareWatchUrl(): Promise { + await copyText(unref(shareWatchUrl)); + toastCopySuccess(); + } + + return { + shareBtnEnabled, + shareWatchUrl, + webShareCustomUrl, + shareQrCodeUrl, + qqShareUrl, + qzoneShareUrl, + weiboShareUrl, + facebookShareUrl, + twitterShareUrl, + webShareList, + + composeShareUrl, + copyShareWatchUrl, + }; +}); diff --git a/src/store/use-viewer-store.ts b/src/store/use-viewer-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..dbe27e25f563c16dc3cd676a9a97f994ced3970d --- /dev/null +++ b/src/store/use-viewer-store.ts @@ -0,0 +1,119 @@ +/** + * @file 观众信息 store + */ +import { AuthType } from '@polyv/live-watch-sdk'; +import { DEFAULT_VIEWER_AVATAR } from '@/assets/constants/defaults'; +import { getWatchCore } from '@/core/watch-sdk'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref, unref } from 'vue'; +import { useEnrollStore } from './use-enroll-store'; +import { isWeixin } from '@/assets/utils/browser'; +import { useWeixinStore } from './use-weixin-store'; + +export interface ViewerStoreState { + /** 用户唯一标识 */ + viewerId: Ref; + /** 用户昵称 */ + nickname: Ref; + /** 用户头像 */ + avatar: Ref; + /** 当前用户的微信 openid */ + openId: Ref; + /** 当前用户的微信 unionId */ + unionId: Ref; + /** 当前用户的授权方式 */ + authType: Ref; + /** 当前能否设置昵称 */ + canSetNick: ComputedRef; + /** 当前是否已设置昵称 */ + hasNickname: ComputedRef; +} + +export interface ViewerStoreAction { + /** 同步观众信息 */ + syncViewerInfo: () => void; +} + +export interface ViewerStore extends ViewerStoreState, ViewerStoreAction {} + +export const useViewerStore = defineStore<'viewer', ViewerStore>('viewer', () => { + const enrollStore = useEnrollStore(); + const weixinStore = useWeixinStore(); + + /** 用户唯一标识 */ + const viewerId = ref(); + /** 用户昵称 */ + const nickname = ref(''); + /** 用户头像 */ + const avatar = ref(DEFAULT_VIEWER_AVATAR); + /** openId */ + const openId = ref(); + /** unionId */ + const unionId = ref(); + /** 授权方式 */ + const authType = ref(AuthType.None); + + /** 当前能否设置昵称 */ + const canSetNick = computed(() => { + // 报名观看使用姓名作为昵称 + if (enrollStore.enrollNickEnabled) { + return false; + } + + // 允许的观看条件 + const allowAuthTypes = [AuthType.None, AuthType.Code, AuthType.Info]; + if (!allowAuthTypes.includes(unref(authType))) { + return false; + } + + // 微信中且微信授权了 + if (isWeixin && weixinStore.weixinAuthorized) { + return false; + } + + return true; + }); + + /** 当前是否已设置昵称 */ + const hasNickname = computed(() => { + // 如果当前不允许设置昵称,则默认已经设置过昵称了 + if (!unref(canSetNick)) { + return true; + } + + // 检测是否为默认用户名 [内网、外网、xx]观众/[数字] + const nicknameVal = unref(nickname); + const nicknamePrefix = '观众'; + const nickNamePattern = new RegExp(nicknamePrefix + '/[0-9]+$'); + + if (!nicknameVal) { + return false; + } + return !nickNamePattern.test(nicknameVal); + }); + + /** 同步观众信息 */ + function syncViewerInfo(): void { + const watchCore = getWatchCore(); + + const userInfo = watchCore.user.getCurrentUserInfo(); + viewerId.value = watchCore.user.getCurrentUserId(false); + nickname.value = watchCore.user.getCurrentUserNick(false) ?? ''; + avatar.value = watchCore.user.getCurrentUserAvatar(); + openId.value = watchCore.user.getCurrentUserOpenId(false); + unionId.value = userInfo.unionId; + authType.value = userInfo.authType ?? AuthType.None; + } + + return { + viewerId, + nickname, + avatar, + openId, + unionId, + authType, + canSetNick, + hasNickname, + syncViewerInfo, + }; +}); diff --git a/src/store/use-watch-app-store.ts b/src/store/use-watch-app-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..b381a6b40dadafb506f35f641f308ce4f0e89a59 --- /dev/null +++ b/src/store/use-watch-app-store.ts @@ -0,0 +1,206 @@ +import { computed, ComputedRef, ref, Ref } from 'vue'; +import { defineStore } from 'pinia'; + +import { paramGetter } from '@/hooks/core/use-query-params'; +import { appEvents, eventBus } from '@/app/app-events'; + +import { getWatchCore } from '@/core/watch-sdk'; +import { PageErrorData } from '@/app/layout/page-error/page-error-type'; + +import { useAuthStore } from './use-auth-store'; +import { useEnrollStore } from './use-enroll-store'; +import { usePlaybackStore } from './use-playback-store'; +import { usePlayerStore } from './use-player-store'; +import { useDocStore } from './use-doc-store'; +import { useChatStore } from './use-chat-store'; +import { useFinanceStore } from './use-finance-store'; +import { useConnectMicStore } from './use-connect-mic-store'; +import { useChannelMenuStore } from '@/store/use-channel-menu-store'; + +export interface WatchAppStoreState { + /** 当前是否处于回放地址 */ + isWatchBackUrl: Ref; + /** 当前是否为微信小程序 webview 环境 */ + isWxMiniProgram: Ref; + /** 是否为会议模式 */ + meetingMode: Ref; + /** 是否 iframe 模式 */ + iframeMode: Ref; + /** 页面异常数据 */ + pageError: Ref; + /** 观看页应用初始化完毕 */ + watchAppInited: Ref; + /** 是否显示引导页 - 引导页渲染标识位 */ + shouldShowSplash: ComputedRef; + /** 直播观看页是否初始化完毕 - 直播观看页渲染标识位 */ + liveWatchInited: Ref; +} + +export interface WatchAppStoreActions { + resetUpWatchCore(): Promise; + setupLiveWatch(): Promise; + /** 设置页面错误数据 */ + setPageError(param: PageErrorData): void; + pauseConnectLiveWatch(): void; + recoverConnectLiveWatch(): void; +} + +export interface WatchAppStore extends WatchAppStoreState, WatchAppStoreActions {} + +export const useWatchAppStore = defineStore<'watchApp', WatchAppStore>('watchApp', () => { + const authStore = useAuthStore(); + const enrollStore = useEnrollStore(); + + const docStore = useDocStore(); + const playbackStore = usePlaybackStore(); + const playerStore = usePlayerStore(); + const chatStore = useChatStore(); + const connectMicStore = useConnectMicStore(); + + const channelMenuStore = useChannelMenuStore(); + const financeStore = useFinanceStore(); + + /** 当前是否处于回放地址 */ + const isWatchBackUrl = ref(false); + + /** 当前是否为微信小程序 webview 环境 */ + const isWxMiniProgram = ref(false); + + /** 是否为会议模式 */ + const meetingMode = ref(paramGetter.polyvMeeting() === 'true'); + + /** 是否 iframe 模式 */ + const iframeMode = ref(paramGetter.hasFrame() === '1'); + + /** 页面异常数据 */ + const pageError = ref(); + + /** 观看页应用初始化完毕 */ + const watchAppInited = ref(false); + + /** 是否显示引导页 */ + const shouldShowSplash = computed(() => { + return ( + !authStore.isAuthorized || + enrollStore.needEnrollByEnter || + financeStore.shouldShowSplashByRiskEvaluation + ); + }); + + /** 直播观看页是否初始化完毕 */ + const liveWatchInited = ref(false); + + /** 设置页面异常数据 */ + function setPageError(param: PageErrorData) { + pageError.value = param; + } + + /** 重置 watchCore */ + async function resetUpWatchCore(): Promise { + const watchCore = getWatchCore(); + await watchCore.setup(); + } + + /** 设置观看页 */ + async function setupLiveWatch(): Promise { + const watchCore = getWatchCore(); + + // 优先判断是否为回放地址进入 + isWatchBackUrl.value = watchCore.getIsWatchBackByVid(); + + // 初始化当前回放对象 + await playbackStore.initCurrentPlaybackTarget(); + + // 播放器配置无延迟 + if (watchCore.player.getLowLatencySupport()) { + playerStore.$patch({ + isLowLatency: true, + }); + } + + // 同步相关 store 的数据 + channelMenuStore.syncChannelMenus(); + chatStore.syncChatStore(); + + // 根据不同场景设置标识位 + await __setLiveWatchInit(); + } + + /** 根据不同场景设置 liveWatchInited */ + async function __setLiveWatchInit() { + // 开启风险确认函时,需要暂停部分功能初始化和 Socket 链接 + // 等到观众同意确认后再进行恢复 + if (financeStore.riskConfirmEnabled) { + pauseConnectLiveWatch(); + liveWatchInited.value = true; + return; + } + + // 回放地址进入观看页,不需要连麦 + if (isWatchBackUrl.value) { + connectMicStore.$patch({ connectMicEnabled: false }); + liveWatchInited.value = true; + return; + } + + // 正常进入观看页,建立 socket 连接 + await setupWatchCoreConnect(); + liveWatchInited.value = true; + } + + /** + * 设置观看核心连接 Socket 处理 + */ + async function setupWatchCoreConnect() { + const watchCore = getWatchCore(); + await watchCore.connect(); + } + + /** + * 暂停直播观看页部分功能的初始化 + * */ + async function pauseConnectLiveWatch() { + playerStore.$patch({ playerEnabled: false }); + docStore.$patch({ docEnabled: false }); + chatStore.$patch({ chatMsgListEnabled: false }); + connectMicStore.$patch({ connectMicEnabled: false }); + } + + /** + * 恢复直播观看页部分功能的初始化 + */ + async function recoverConnectLiveWatch() { + playerStore.$patch({ playerEnabled: true }); + docStore.$patch({ docEnabled: true }); + chatStore.$patch({ chatMsgListEnabled: true }); + + // 回放地址进观看页不需要 socket 和连麦 + if (!isWatchBackUrl.value) { + connectMicStore.$patch({ connectMicEnabled: true }); + await setupWatchCoreConnect(); + } + + eventBus.$emit(appEvents.player.ResetUpPlayer); + eventBus.$emit(appEvents.doc.ResetUpDoc); + eventBus.$emit(appEvents.chat.ResetUpChatMsgList); + eventBus.$emit(appEvents.connectMic.ResetUpConnectMic); + } + + return { + isWatchBackUrl, + isWxMiniProgram, + meetingMode, + iframeMode, + pageError, + watchAppInited, + shouldShowSplash, + liveWatchInited, + + resetUpWatchCore, + setupLiveWatch, + setPageError, + + pauseConnectLiveWatch, + recoverConnectLiveWatch, + }; +}); diff --git a/src/store/use-webview-store.ts b/src/store/use-webview-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..46f547252c0aeace928a226bdd7424ff10f6dae0 --- /dev/null +++ b/src/store/use-webview-store.ts @@ -0,0 +1,72 @@ +import { defineStore } from 'pinia'; +import { computed, ComputedRef, ref, Ref } from 'vue'; +import type { WebViewBridge } from '@polyv/web-view-bridge'; +import { usePlayerStore } from './use-player-store'; + +export interface WebviewStoreState { + /** 是否基于保利威 webview 协议嵌入的观看页 */ + isPlvWebview: ComputedRef; + /** 是否 webview 内嵌小窗状态 */ + isSmallWindow: Ref; + /** 在 webview 中小窗化时,打开的小窗宽高 */ + plvWebviewDataSize: ComputedRef<{ width: number; height: number }>; +} + +export interface WebviewStoreActions { + /** 初始化桥接器 */ + initWebviewBridge(): Promise; + /** 获取桥接器对象 */ + getPlvWebviewBridge(): WebViewBridge | undefined; +} + +export interface WebviewStore extends WebviewStoreState, WebviewStoreActions {} + +export const useWebviewStore = defineStore<'webview', WebviewStore>('webview', () => { + const playerStore = usePlayerStore(); + + /** webview 敲击诶钱 */ + let webviewBridge: WebViewBridge | undefined; + + /** 是否基于保利威 webview 协议嵌入的观看页 */ + const isPlvWebview = computed(() => { + return false; + }); + + /** 是否 webview 内嵌小窗状态 */ + const isSmallWindow = ref(false); + + /** 在 webview 中小窗化时,打开的小窗宽高 */ + const plvWebviewDataSize = computed(() => ({ + width: playerStore.isPortraitResolutionRate ? 90 : 160, + height: playerStore.isPortraitResolutionRate ? 160 : 90, + })); + + /** 初始化桥接器 */ + async function initWebviewBridge() { + if (webviewBridge) { + return; + } + try { + const { WebViewBridge } = await import( + /* webpackChunkName: "web-view-bridge" */ '@polyv/web-view-bridge' + ); + webviewBridge = new WebViewBridge(); + await webviewBridge.connectWebViewBridge(); + } catch (e) { + console.warn(e); + } + } + + /** 获取桥接器对象 */ + function getPlvWebviewBridge() { + return webviewBridge; + } + + return { + isPlvWebview, + isSmallWindow, + plvWebviewDataSize, + initWebviewBridge, + getPlvWebviewBridge, + }; +}); diff --git a/src/store/use-weixin-store.ts b/src/store/use-weixin-store.ts new file mode 100644 index 0000000000000000000000000000000000000000..35135c1ad05cc0e44a502e281d3c32809a95523f --- /dev/null +++ b/src/store/use-weixin-store.ts @@ -0,0 +1,85 @@ +/** + * @file 微信 store + */ +import { WxShareInfo, YN } from '@polyv/live-watch-sdk'; +import { ynToBool } from '@utils-ts/boolean'; +import { defineStore } from 'pinia'; +import { computed, ComputedRef, Ref, ref } from 'vue'; +import { useChannelStore } from './use-channel-store'; + +export interface WeixinStoreState { + /** 是否已进行微信授权(是否存在 openId) */ + weixinAuthorized: ComputedRef; + /** 是否已进行微信非静默授权(是否存在 wxUser) */ + weixinWatchAuthorized: ComputedRef; + /** 是否已进行企业微信授权(是否存在 wxWorkUserId、cpOpenId 其中一个) */ + workWeixinAuthorized: ComputedRef; + /** 微信分享信息 */ + wxShareInfo: Ref; + /** 超管后台微信功能开关 */ + weixinAccountFunctionEnabled: ComputedRef; + /** 超管后台中微信支付开关 */ + weixinPayEnabled: ComputedRef; +} + +export interface WeixinStoreAction { + /** 保存微信分享信息 */ + saveWxShareInfo: (info: WxShareInfo) => void; +} + +export interface WeixinStore extends WeixinStoreState, WeixinStoreAction {} + +export const useWeixinStore = defineStore<'weixin', WeixinStore>('weixin', () => { + const channelStore = useChannelStore(); + + /** 是否已进行微信授权(是否存在 openId) */ + const weixinAuthorized = computed(() => { + return !!channelStore.channelDetail?.wxInfo?.openId; + }); + + /** 是否已进行微信非静默授权(是否存在 wxUser) */ + const weixinWatchAuthorized = computed(() => { + return !!channelStore.channelDetail?.wxInfo?.wxUser; + }); + + /** 是否已进行企业微信授权(是否存在 wxWorkUserId、cpOpenId 其中一个) */ + const workWeixinAuthorized = computed(() => { + return !!( + channelStore.channelDetail?.wxInfo?.wxWorkUserId || + channelStore.channelDetail?.wxInfo?.cpOpenId + ); + }); + + /** 微信分享信息 */ + const wxShareInfo = ref({ + isUseWxShareUrl: YN.N, + wxShareUrl: '', + shareTitle: '', + shareDesc: '', + }); + + /** 保存微信分享信息 */ + function saveWxShareInfo(info: WxShareInfo): void { + wxShareInfo.value = info; + } + + /** 超管后台微信功能开关 */ + const weixinAccountFunctionEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig.weixinAccountFunctionEnabled ?? YN.N), + ); + + /** 超管后台中微信支付开关 */ + const weixinPayEnabled = computed(() => + ynToBool(channelStore.channelDetail?.channelConfig?.weixinPayEnabled ?? YN.N), + ); + + return { + weixinAuthorized, + weixinWatchAuthorized, + workWeixinAuthorized, + wxShareInfo, + weixinAccountFunctionEnabled, + weixinPayEnabled, + saveWxShareInfo, + }; +}); diff --git a/stylelint.config.js b/stylelint.config.js new file mode 100644 index 0000000000000000000000000000000000000000..06fad13ff84a930ea1f5d53713b8bf6553802e11 --- /dev/null +++ b/stylelint.config.js @@ -0,0 +1,85 @@ +/* eslint-disable sonarjs/no-duplicate-string */ +module.exports = { + extends: [ + 'stylelint-config-recommended', + 'stylelint-config-recommended-scss', + 'stylelint-config-prettier', + 'stylelint-config-recess-order', + ], + rules: { + 'font-family-no-missing-generic-family-keyword': null, + 'no-descending-specificity': null, + 'no-duplicate-selectors': null, // 对 @import 支持不佳,禁用 + 'selector-pseudo-element-no-unknown': null, + 'font-family-name-quotes': 'always-where-recommended', + 'function-comma-newline-after': 'always-multi-line', + 'function-comma-newline-before': 'never-multi-line', + 'function-comma-space-after': 'always-single-line', + 'function-comma-space-before': 'never', + 'function-max-empty-lines': 0, + 'function-name-case': 'lower', + 'function-parentheses-newline-inside': 'always-multi-line', + 'function-parentheses-space-inside': 'never-single-line', + 'number-no-trailing-zeros': true, + 'string-quotes': 'single', + 'length-zero-no-unit': true, + 'value-keyword-case': 'lower', + 'value-list-comma-newline-after': 'always-multi-line', + 'value-list-comma-newline-before': 'never-multi-line', + 'value-list-comma-space-after': 'always-single-line', + 'value-list-comma-space-before': 'never', + 'value-list-max-empty-lines': 1, + 'custom-property-empty-line-before': 'never', + 'property-case': 'lower', + 'declaration-bang-space-after': 'never', + 'declaration-bang-space-before': 'always', + 'declaration-block-semicolon-newline-after': 'always-multi-line', + 'declaration-block-semicolon-newline-before': 'never-multi-line', + 'declaration-colon-space-after': 'always-single-line', + 'declaration-colon-space-before': 'never', + 'block-opening-brace-newline-after': 'always-multi-line', + 'block-opening-brace-space-before': 'always', + 'block-opening-brace-space-after': 'always-single-line', + 'block-closing-brace-empty-line-before': 'never', + 'block-closing-brace-newline-after': [ + 'always', + { + ignoreAtRules: ['if', 'else'], + }, + ], + 'block-closing-brace-newline-before': 'always-multi-line', + // 'block-closing-brace-space-after': 'never', 检查方式有问题,先禁用 + 'block-closing-brace-space-before': 'always-single-line', + 'selector-descendant-combinator-no-non-space': true, + 'selector-pseudo-class-case': 'lower', + 'selector-pseudo-class-parentheses-space-inside': 'never', + 'selector-pseudo-element-case': 'lower', + 'selector-type-case': 'lower', + 'selector-attribute-brackets-space-inside': 'never', + 'selector-attribute-operator-space-after': 'never', + 'selector-attribute-operator-space-before': 'never', + 'selector-list-comma-newline-after': 'always-multi-line', + 'selector-list-comma-newline-before': 'never-multi-line', + 'selector-list-comma-space-after': 'always-single-line', + 'selector-list-comma-space-before': 'never', + 'media-feature-colon-space-after': 'always', + 'media-feature-colon-space-before': 'never', + 'media-feature-name-case': 'lower', + 'media-feature-parentheses-space-inside': 'never', + 'media-feature-range-operator-space-after': 'always', + 'media-feature-range-operator-space-before': 'always', + 'media-query-list-comma-newline-after': 'always-multi-line', + 'media-query-list-comma-newline-before': 'never-multi-line', + 'media-query-list-comma-space-after': 'always-single-line', + 'media-query-list-comma-space-before': 'never', + 'at-rule-name-case': 'lower', + 'at-rule-name-newline-after': null, + 'at-rule-name-space-after': 'always-single-line', + 'at-rule-semicolon-newline-after': 'always', + 'at-rule-semicolon-space-before': 'never', + 'comment-whitespace-inside': 'always', + indentation: 2, + 'max-empty-lines': 2, + 'no-eol-whitespace': true, + }, +}; diff --git a/tsconfig.build.json b/tsconfig.build.json new file mode 100644 index 0000000000000000000000000000000000000000..ab2b6458099c2e8e8e300484a3c83dcb78886fc6 --- /dev/null +++ b/tsconfig.build.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "es5", + "paths": { + "@/*": [ + "src/*" + ], + "@utils-ts/*": [ + "node_modules/@polyv/utils/dist/es/*" + ] + } + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..99cc9451b977548978482817b054e1e5e8e1a9cb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "outDir": "./dist", + "baseUrl": "./", + "allowJs": true, + "checkJs": false, + "strict": true, + "alwaysStrict": true, + "moduleResolution": "node", + "module": "ESNext", + "target": "ESNext", + "declaration": false, + "esModuleInterop": true, + "resolveJsonModule": true, + "paths": { + "@/*": [ + "src/*" + ], + "@utils-ts/*": [ + "node_modules/@polyv/utils/dist/es/*" + ] + } + }, + "include": [ + "./src", + "./types" + ], + "vueCompilerOptions": { + "target": 2.7, + "strictTemplates": false + } +} \ No newline at end of file diff --git a/types/ali-awsc.d.ts b/types/ali-awsc.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..a188f58e5316491ff27743382bb34f5bd8947dc2 --- /dev/null +++ b/types/ali-awsc.d.ts @@ -0,0 +1,17 @@ +/** + * @file 阿里云滑块验证码类型 + */ + +/** + * 阿里云滑块验证数据 + */ +declare interface AliAwscSliceData { + /** 会话 id */ + sessionId: string; + /** 签名串 */ + sig: string; + /** 请求唯一标识 */ + token: string; + /** 场景 */ + scene: string; +} diff --git a/types/alloyfinger.d.ts b/types/alloyfinger.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7f57dcbd2c83ed3ebaa18e58877d6f66228e4f59 --- /dev/null +++ b/types/alloyfinger.d.ts @@ -0,0 +1,14 @@ +declare module 'alloyfinger' { + export default class AlloyFinger { + constructor($el: HTMLElement, options: AlloyFingerOption); + } +} + +interface AlloyFingerOption { + pressMove: (evt: TouchEvent & WheelEvent) => void; +} + +declare interface AlloyFingerTransformHTMLElement extends HTMLElement { + translateY: number; + translateX: number; +} diff --git a/types/emotion-sdk.d.ts b/types/emotion-sdk.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7b36b05730cfe5a797f37118db3fb681299c5066 --- /dev/null +++ b/types/emotion-sdk.d.ts @@ -0,0 +1,21 @@ +declare module '@polyv/emotion-sdk' { + /** + * 把指定内容中的表情占位符替换为表情图片。 + * @param content 指定内容。 + * @param mode 解析模式,默认:1: + * 1 为 HTML 模式,即替换为 img 标签; + * 2 为弹幕模式,即替换为两个中括号括起的图片地址。 + */ + export function parseEmotions(content: string, mode?: number): string; + + /** + * 移除表情 + * @param content 指定内容 + */ + export function removeEmotions(content: string): string; + + /** + * 获取 dom 节点列表 + */ + export function genDOMList(): HTMLUListElement; +} diff --git a/types/global.d.ts b/types/global.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2d673eae725fd2ff242f79598b29bb130438e65 --- /dev/null +++ b/types/global.d.ts @@ -0,0 +1,40 @@ +import type { SdkLoaderEnv } from '@polyv/live-watch-sdk'; +import type { ComputedRef, Ref } from 'vue'; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-empty-interface + interface Window extends Record {} + + interface HTMLElement { + __vueVm?: Vue; + } + + /** 代表任意的函数类型 */ + type AnyFunc = (...arg: unknown[]) => unknown; + + /** 响应式 Ref,包括 ComputedRef 和 Ref */ + type ResponsiveRef = ComputedRef | Ref; + + type RefOrElement = Ref | HTMLElement; + + /** 没有参数的方法类型 */ + type EmptyParamsFunc = () => unknown; + + /** 通用对象定义 */ + type UniversalParams = { + [key: string]: V; + }; + + /** 项目构建时间 */ + const PROJECT_BUILD_TIME: string; + + /** 项目版本号 */ + const PROJECT_VERSION: string; + + /** 观看页 SDK 加载的环境 */ + const WATCH_SDK_ENV: SdkLoaderEnv; + + interface NavigatorLanguage { + readonly browserLanguage: string; + } +} diff --git a/types/polyv-ui-admin.d.ts b/types/polyv-ui-admin.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..5555ec0c6f3ec51f50846b2205b8dcc85b8600a4 --- /dev/null +++ b/types/polyv-ui-admin.d.ts @@ -0,0 +1,80 @@ +/* eslint-disable no-duplicate-imports */ +/* eslint-disable @typescript-eslint/no-duplicate-imports */ +declare module '@polyv/polyv-ui/lib-admin/popper-manager' { + type PopperManager = { + /** 为元素设置 z-index 属性 */ + openPopper: (elem: HTMLElement) => void; + zIndex: number; + }; + const manager: PopperManager; + export default manager; +} + +/** + * 图片浏览 + */ +declare module '@polyv/polyv-ui/lib-admin/image-preview' { + import { ImagePreviewOptions } from '@/plugins/polyv-ui/types'; + + export interface ImagePreviewInstance { + /** 关闭 */ + close(): void; + } + + function ImagePreview( + images: string | string[], + options: ImagePreviewOptions, + ): ImagePreviewInstance; + + export default ImagePreview; +} + +/** + * 折叠动画 + */ +declare module '@polyv/polyv-ui/lib-admin/collapse-transition' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 图标 + */ +declare module '@polyv/polyv-ui/lib-admin/icon' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 图片粘贴输入框 + */ +declare module '@polyv/polyv-ui/lib-admin/input-paste-image' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 栅格布局 + */ +declare module '@polyv/polyv-ui/lib-admin/row' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} +declare module '@polyv/polyv-ui/lib-admin/col' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 分页器 + */ +declare module '@polyv/polyv-ui/lib-admin/pagination' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} diff --git a/types/polyv-ui-mobile.d.ts b/types/polyv-ui-mobile.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..1b91480ad34b3affa72148adaa436f5b98e3496a --- /dev/null +++ b/types/polyv-ui-mobile.d.ts @@ -0,0 +1,46 @@ +/* eslint-disable no-duplicate-imports */ +/* eslint-disable @typescript-eslint/no-duplicate-imports */ +/** + * 轮播图 + */ +declare module '@polyv/polyv-ui/lib-front-mob/carousel' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 轮播图节点 + */ +declare module '@polyv/polyv-ui/lib-front-mob/carousel-item' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 点赞 + */ +declare module '@polyv/polyv-ui/lib-front-mob/like' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 地区选择 + */ +declare module '@polyv/polyv-ui/lib-front-mob/area-picker' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 情绪反馈 + */ +declare module '@polyv/polyv-ui/lib-front-mob/emotional-feedback' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} diff --git a/types/polyv-ui-pc.d.ts b/types/polyv-ui-pc.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e1b189eed08942a8a0aadae502cefd08f0cc7cab --- /dev/null +++ b/types/polyv-ui-pc.d.ts @@ -0,0 +1,46 @@ +/* eslint-disable no-duplicate-imports */ +/* eslint-disable @typescript-eslint/no-duplicate-imports */ +/** + * 轮播图 + */ +declare module '@polyv/polyv-ui/lib-front/carousel' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 轮播图节点 + */ +declare module '@polyv/polyv-ui/lib-front/carousel-item' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 点赞 + */ +declare module '@polyv/polyv-ui/lib-front/like' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 地区选择 + */ +declare module '@polyv/polyv-ui/lib-front/area-picker' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +/** + * 情绪反馈 + */ +declare module '@polyv/polyv-ui/lib-front/emotional-feedback' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} diff --git a/types/shims-scss.d.ts b/types/shims-scss.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e80300c61b272ef538246d8eeb810b9fc86662a1 --- /dev/null +++ b/types/shims-scss.d.ts @@ -0,0 +1,14 @@ +declare module '*.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.ico' { + const icoUrl: string; + export default icoUrl; +} diff --git a/types/shims-vue.d.ts b/types/shims-vue.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..b953a6f71649644c82e16a8533c51016509eaf4c --- /dev/null +++ b/types/shims-vue.d.ts @@ -0,0 +1,10 @@ +declare module '*.vue' { + import Vue, { ComponentOptions } from 'vue'; + const component: ComponentOptions; + export default component; +} + +declare module '*.svga' { + const content: string; + export default content; +} diff --git a/types/vue-declare-extend.d.ts b/types/vue-declare-extend.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..178801fc0aa1b17e8dd66f703da9c53c6caa6d05 --- /dev/null +++ b/types/vue-declare-extend.d.ts @@ -0,0 +1,12 @@ +import { translate } from '../src/assets/lang'; +import { defineComponent as _defineComponent } from 'vue'; + +declare global { + const defineOptions: typeof _defineComponent; +} + +declare module 'vue/types/vue' { + interface Vue { + $lang: typeof translate; + } +} diff --git a/types/wx-jssdk.d.ts b/types/wx-jssdk.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e13102053725d17a2fbab844a24c3b5054e62b5f --- /dev/null +++ b/types/wx-jssdk.d.ts @@ -0,0 +1,1475 @@ +declare namespace WechatJsSdk { + type JsApiNames = + /* 分享接口 */ + | 'updateAppMessageShareData' + | 'updateTimelineShareData' + | 'onMenuShareTimeline' + | 'onMenuShareAppMessage' + | 'onMenuShareQQ' + | 'onMenuShareWeibo' + | 'onMenuShareQZone' + /* 图像接口 */ + | 'chooseImage' + | 'previewImage' + | 'uploadImage' + | 'downloadImage' + | 'getLocalImgData' + /* 音频接口 */ + | 'startRecord' + | 'stopRecord' + | 'onVoiceRecordEnd' + | 'playVoice' + | 'pauseVoice' + | 'stopVoice' + | 'onVoicePlayEnd' + | 'uploadVoice' + | 'downloadVoice' + /* 智能接口 */ + | 'translateVoice' + /* 设备信息 */ + | 'getNetworkType' + /* 地理位置 */ + | 'openLocation' + | 'getLocation' + /* 摇一摇周边 */ + | 'startSearchBeacons' + | 'stopSearchBeacons' + | 'onSearchBeacons' + /* 界面操作 */ + | 'closeWindow' + | 'hideOptionMenu' + | 'showOptionMenu' + | 'hideMenuItems' + | 'showMenuItems' + | 'hideAllNonBaseMenuItem' + | 'showAllNonBaseMenuItem' + /** 旧版的分享 */ + | 'onMenuShareWechat' + | 'shareAppMessage' + | 'shareWechatMessage' + /* 微信扫一扫 */ + | 'scanQRCode' + /* 微信小店 */ + | 'openProductSpecificView' + /* 微信卡券 */ + | 'chooseCard' + | 'addCard' + | 'openCard' + | 'consumeAndShareCard' + /* 微信支付 */ + | 'chooseWXPay' + | 'openBusinessView' + /* 快速输入 */ + | 'openAddress' + /* 小程序 */ + | 'launchMiniProgram' + /* 企业微信 */ + | 'openEnterpriseRedPacket' + | 'openEnterpriseChat'; + + type OpenTagList = + | 'wx-open-launch-weapp' + | 'wx-open-launch-app' + | 'wx-open-subscribe' + | 'wx-open-audio'; + + type MenuNames = + /* 基本类 */ + | 'menuItem:exposeArticle' + | 'menuItem:setFont' + | 'menuItem:dayMode' + | 'menuItem:nightMode' + | 'menuItem:refresh' + | 'menuItem:profile' + | 'menuItem:addContact' + /* 传播类 */ + | 'menuItem:share:appMessage' + | 'menuItem:share:wechat' + | 'menuItem:share:timeline' + | 'menuItem:share:qq' + | 'menuItem:share:weiboApp' + | 'menuItem:share:facebook' + | 'menuItem:share:QZone' + | 'menuItem:share:email' + | 'menuItem:share:brand' + | 'menuItem:favorite' + /* 保护类 */ + | 'menuItem:editTag' + | 'menuItem:delete' + | 'menuItem:copyUrl' + | 'menuItem:originPage' + | 'menuItem:readMode' + | 'menuItem:openWithQQBrowser' + | 'menuItem:openWithSafari'; + + type ShareAppMessageTypes = 'music' | 'video' | 'link'; + + type ImageSizeTypes = 'original' | 'compressed'; + + type ImageSourceTypes = 'album' | 'camera'; + + type LocationTypes = 'wgs84' | 'gcj02'; + + type ScanTypes = 'qrCode' | 'barCode'; + + type ProductSpecificViewTypes = 0 | 1 | 2; + + interface ConfigOption { + /** + * @property {Boolean} 是否开启调试模式。 + */ + debug?: boolean; + /** + * @requires + * @property {Boolean} 公众号的唯一标识。 + */ + appId: string; + /** + * @requires + * @property {Boolean} 生成签名的时间戳。 + */ + timestamp: number; + /** + * @requires + * @property {Boolean} 生成签名的随机串。 + */ + nonceStr: string; + /** + * @requires + * @property {Boolean} 签名。 + */ + signature: string; + /** + * @requires + * @property {Boolean} 需要使用的 JS 接口列表。 + */ + jsApiList: JsApiNames[]; + /** + * @property {Boolean} 需要使用的开放标签列表。 + */ + openTagList?: OpenTagList[]; + } + + interface GeneralCallbackResult { + /** + * @property {String} 错误信息。 + */ + errMsg?: string; + } + + interface CheckJsApiOption { + /** + * @property {Array} 需要检测的 JS 接口列表。 + */ + jsApiList: JsApiNames[]; + + success?: (res?: CheckJsApiSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface CheckJsApiSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {Object} JS 接口字典。 + */ + checkResult: Record; + } + + interface UpdateAppMessageShareDataOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享描述。 + */ + desc: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface UpdateTimelineShareDataOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnMenuShareTimelineOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + + trigger?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnMenuShareAppMessageOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享描述。 + */ + desc: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + /** + * @property {String} 分享类型。 + */ + type?: ShareAppMessageTypes; + /** + * @property {String} 分享数据链接。 + */ + dataUrl?: string; + + trigger?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnMenuShareQQOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享描述。 + */ + desc: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + + trigger?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnMenuShareWeiboOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享描述。 + */ + desc: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + + trigger?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnMenuShareQZoneOption { + /** + * @property {String} 分享标题。 + */ + title: string; + /** + * @property {String} 分享描述。 + */ + desc: string; + /** + * @property {String} 分享链接。 + */ + link: string; + /** + * @property {String} 分享图标。 + */ + imgUrl: string; + + trigger?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ChooseImageOption { + /** + * @property {Number} 一次可选择图片数量。 + */ + count?: number; + /** + * @property {Array} 指定是原图还是压缩图。 + */ + sizeType?: ImageSizeTypes[]; + /** + * @property {Array} 指定来源是相册还是相机。 + */ + sourceType?: ImageSourceTypes[]; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: ChooseImageSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ChooseImageSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {Array} 选定照片的本地 ID 列表。 + */ + localIds: string[]; + } + + interface PreviewImageOption { + /** + * @property {String} 当前显示图片的链接。 + */ + current: string; + /** + * @property {Array} 需要预览的图片链接列表。 + */ + urls: string[]; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface UploadImageOption { + /** + * @property {String} 需要上传的图片的本地 ID。 + */ + localId: string; + /** + * @property {Boolean} 是否显示进度提示。 + */ + isShowProgressTips?: boolean; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: UploadImageSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface UploadImageSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 图片的服务端 ID。 + */ + serverId: string; + } + + interface DownloadImageOption { + /** + * @property {String} 需要下载的图片的服务端 ID。 + */ + serverId: string; + /** + * @property {Boolean} 是否显示进度提示。 + */ + isShowProgressTips?: boolean; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: DownloadImageSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface DownloadImageSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 图片的本地 ID。 + */ + localId: string; + } + + interface GetLocalImgDataOption { + /** + * @property {String} 图片的本地 ID。 + */ + localId: string; + + success?: (res?: GetLocalImgDataSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface GetLocalImgDataSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 图片的 Base64 数据。 + */ + localData: string; + } + + interface StartRecordOption { + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface StopRecordOption { + success?: (res?: StopRecordSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface StopRecordSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 录音的本地 ID。 + */ + localId: string; + } + + interface OnVoiceRecordEndOption { + complete?: (res?: OnVoiceRecordEndCompleteCallbackResult) => void; + } + + interface OnVoiceRecordEndCompleteCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 录音的本地 ID。 + */ + localId: string; + } + + interface PlayVoiceOption { + /** + * @property {String} 需要播放的音频的本地 ID。 + */ + localId: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface PauseVoiceOption { + /** + * @property {String} 需要暂停的音频的本地 ID。 + */ + localId: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface StopVoiceOption { + /** + * @property {String} 需要停止的音频的本地 ID。 + */ + localId: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnVoicePlayEndOption { + complete?: (res?: OnVoicePlayEndSuccessCallbackResult) => void; + } + + interface OnVoicePlayEndSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 音频的本地 ID。 + */ + localId: string; + } + + interface UploadVoiceOption { + /** + * @property {String} 需要上传的音频的本地 ID。 + */ + localId: string; + /** + * @property {Boolean} 是否显示进度提示。 + */ + isShowProgressTips?: boolean; + + success?: (res?: UploadVoiceSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface UploadVoiceSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 音频的服务端 ID。 + */ + serverId: string; + } + + interface DownloadVoiceOption { + /** + * @property {String} 需要下载的音频的服务端 ID。 + */ + serverId: string; + /** + * @property {Boolean} 是否显示进度提示。 + */ + isShowProgressTips?: boolean; + + success?: (res?: DownloadVoiceSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface DownloadVoiceSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 音频的本地 ID。 + */ + localId: string; + } + + interface TranslateVoiceOption { + /** + * @property {String} 需要上传的音频的本地 ID。 + */ + localId: string; + /** + * @property {Boolean} 是否显示进度提示。 + */ + isShowProgressTips?: boolean; + + success?: (res?: TranslateVoiceSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface TranslateVoiceSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 语音识别的结果。 + */ + translateResult: string; + } + + interface GetNetworkTypeOption { + success?: (res?: GetNetworkTypeSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface GetNetworkTypeSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 网络类型。 + */ + networkType: string; + } + + interface OpenLocationOption { + /** + * @property {Number} 纬度。 + */ + latitude: number; + /** + * @property {Number} 经度。 + */ + longitude: number; + /** + * @property {String} 位置名。 + */ + name: string; + /** + * @property {String} 地址详情说明。 + */ + address: string; + /** + * @property {String} 地图缩放级别。 + */ + scale?: number; + /** + * @property {String} 在查看位置界面底部显示的超链接。 + */ + infoUrl?: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface GetLocationOption { + /** + * @property {String} 坐标类型。 + */ + type?: LocationTypes; + + success?: (res?: GetLocationSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface GetLocationSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {Number} 纬度。 + */ + latitude: number; + /** + * @property {Number} 经度。 + */ + longitude: number; + /** + * @property {Number} 速度(单位:米每秒)。 + */ + speed: number; + /** + * @property {Number} 位置精度。 + */ + accuracy: number; + } + + interface StartSearchBeaconsOption { + /** + * @property {String} 摇周边的业务 Ticket。 + */ + ticket: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface StopSearchBeaconsOption { + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OnSearchBeaconsOption { + complete?: (res?: OnSearchBeaconsCompleteCallbackResult) => void; + } + + interface OnSearchBeaconsCompleteCallbackResult extends GeneralCallbackResult { + /** + * @property {Array} 设备列表。 + */ + beacons: Array<{ + /** + * @property {String} UUID。 + */ + uuid: string; + /** + * @property {Number} Major。 + */ + major: number; + /** + * @property {Number} Minor。 + */ + minor: number; + /** + * @property {String} 距离(单位:米)。 + */ + accuracy: string; + /** + * @property {String} 精度。 + */ + proximity: string; + /** + * @property {String} 接收信号的强度指示。 + */ + rssi: string; + /** + * @property {String} 接收信号时设备的方向。 + */ + heading?: string; + }>; + } + + interface HideMenuItemsOption { + /** + * @property {Array} 要隐藏的菜单项。 + */ + menuList: MenuNames[]; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ShowMenuItemsOption { + /** + * @property {Array} 要隐藏的菜单项。 + */ + menuList: MenuNames[]; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface HideAllNonBaseMenuItemOption { + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ShowAllNonBaseMenuItemOption { + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ScanQRCodeOption { + /** + * @property {Number} 是否直接返回扫描结果。 + */ + needResult?: 0 | 1; + /** + * 指定扫二维码还是一维码。 + */ + scanType?: ScanTypes[]; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: ScanQRCodeSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ScanQRCodeSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 扫描结果。 + */ + resultStr?: string; + } + + interface OpenProductSpecificViewOption { + /** + * @property {String} 商品 ID。 + */ + productId: string; + /** + * @property {Number} 页面类型。 + */ + viewType?: ProductSpecificViewTypes; + /** + * @property {Object} 额外信息。 + */ + extInfo?: unknown; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ChooseCardOption { + /** + * @property {String} 门店 ID。 + */ + shopId: string; + /** + * @property {String} 卡券类型。 + */ + cardType: string; + /** + * @property {String} 卡券 ID。 + */ + cardId: string; + /** + * @property {String} 卡券签名时间戳。 + */ + timestamp: string; + /** + * @property {String} 卡券签名随机串。 + */ + nonceStr: string; + /** + * @property {String} 签名方式。 + */ + signType: string; + /** + * @property {String} 卡券签名。 + */ + cardSign: string; + + success?: (res?: ChooseCardSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ChooseCardSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 卡券列表的 JSON 格式字符串。 + */ + cardList: string; + } + + interface AddCardOption { + /** + * @property {Array} 要添加的卡券列表。 + */ + cardList: Array<{ + /** + * @property {String} 卡券 ID。 + */ + cardId: string; + /** + * @property {String} 卡券扩展参数。 + */ + cardExt?: string; + }>; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenCardOption { + /** + * @property {Array} 要打开的卡券列表。 + */ + cardList: Array<{ + /** + * @property {String} 卡券 ID。 + */ + cardId: string; + /** + * @property {String} 卡券 Code。 + */ + code: string; + }>; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ConsumeAndShareCardOption { + /** + * @property {String} 卡券 ID。 + */ + cardId?: string; + /** + * @property {String} 卡券 Code。 + */ + code?: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface ChooseWXPayOption { + /** + * @property {Number} 支付签名时间戳。 + */ + timestamp: number; + /** + * @property {String} 支付签名随机串。 + */ + nonceStr: string; + /** + * @property {String} 支付参数。 + */ + package: string; + /** + * @property {String} 签名方式。 + */ + signType: string; + /** + * @property {String} 支付签名。 + */ + paySign: string; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenBusinessViewOption { + /** + * @property {String} 跳转类型。 + */ + businessType: string; + /** + * @property {String} 页面参数。 + */ + queryString: string; + /** + * @property {String} 版本。 + */ + envVersion?: string; + + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: OpenAddressSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenAddressOption { + cancel?: (res?: GeneralCallbackResult) => void; + success?: (res?: OpenAddressSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenAddressSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 收货人姓名。 + */ + userName: string; + /** + * @property {String} 邮编。 + */ + postalCode: string; + /** + * @property {String} 国标收货地址第一级地址(省)。 + */ + provinceName: string; + /** + * @property {String} 国标收货地址第二级地址(市)。 + */ + cityName: string; + /** + * @property {String} 国标收货地址第三级地址(国家)。 + */ + countryName: string; + /** + * @property {String} 详细收货地址信息。 + */ + detailInfo: string; + /** + * @property {String} 收货地址国家码。 + */ + nationalCode?: string; + /** + * @property {String} 收货人手机号码。 + */ + telNumber: string; + } + + interface LaunchMiniProgramOption { + /** + * @property {String} 目标小程序 AppId。 + */ + targetAppId: string; + /** + * @property {String} 小程序页面路径。 + */ + path: string; + /** + * @property {String} 小程序版本。 + */ + envVersion?: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenEnterpriseRedPacketOption { + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenEnterpriseChatOption { + /** + * @property {String} 参与会话的企业成员列表,用逗号隔开。 + */ + userIds?: string; + /** + * @property {String} 参与会话的外部联系人列表,用逗号隔开。 + */ + externalUserIds?: string; + /** + * @property {String} 会话名称。 + */ + groupName?: string; + /** + * @property {String} 会话 ID。 + */ + chatId?: string; + + success?: (res?: OpenEnterpriseChatSuccessCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface OpenEnterpriseChatSuccessCallbackResult extends GeneralCallbackResult { + /** + * @property {String} 会话 ID。 + */ + chatId: string; + } + + interface MiniProgramNavigateToOption { + /** + * @property {String} 需要跳转的页面路径。 + */ + url: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface MiniProgramNavigateBackOption { + /** + * @property {Number} 返回的页面数。 + */ + delta?: number; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface MiniProgramSwitchTabOption { + /** + * @property {String} 需要跳转的页面路径。 + */ + url: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface MiniProgramReLaunchOption { + /** + * @property {String} 需要跳转的页面路径。 + */ + url: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface MiniProgramRedirectToOption { + /** + * @property {String} 需要跳转的页面路径。 + */ + url: string; + + success?: (res?: GeneralCallbackResult) => void; + fail?: (res?: GeneralCallbackResult) => void; + complete?: (res?: GeneralCallbackResult) => void; + } + + interface MiniProgramPostMessageOption { + data: unknown; + } + + interface MiniProgramGetEnvCallbackResult extends GeneralCallbackResult { + /** + * @property {Boolean} 是否在小程序环境下。 + */ + miniprogram?: boolean; + } + + export interface Wx { + /** + * 注入权限验证配置。 + * @param {Object} options 配置项。 + */ + config(options: ConfigOption): void; + + /** + * 处理成功的回调方法。 + * @param {Function} callback 回调函数。 + */ + ready(callback: () => void): void; + + /** + * 处理失败的回调方法。 + * @param {Function} callback 回调函数。 + */ + error(callback: (res?: GeneralCallbackResult) => void): void; + + /** + * 判断当前客户端版本是否支持指定 JS 接口。 + * @param {Object} options 配置项。 + */ + checkJsApi(options?: CheckJsApiOption): void; + + /** + * 自定义“分享给朋友”及“分享到 QQ”按钮的分享内容。 + * @param {Object} options 配置项。 + */ + updateAppMessageShareData(options?: UpdateAppMessageShareDataOption): void; + + /** + * 自定义“分享到朋友圈”及“分享到 QQ 空间”按钮的分享内容。 + * @param {Object} options 配置项。 + */ + updateTimelineShareData(options?: UpdateTimelineShareDataOption): void; + + /** + * 获取“分享到朋友圈”按钮点击状态及自定义分享内容。 + * @param {Object} options 配置项。 + * @deprecated + */ + onMenuShareTimeline(options?: OnMenuShareTimelineOption): void; + + /** + * 获取“分享给朋友”按钮点击状态及自定义分享内容。 + * @param {Object} options 配置项。 + * @deprecated + */ + onMenuShareAppMessage(options?: OnMenuShareAppMessageOption): void; + + /** + * 获取“微信”按钮点击状态及自定义分享内容(仅企业微信下支持)。 + * @param {Object} options 配置项。 + */ + onMenuShareWechat?: (options?: OnMenuShareAppMessageOption) => void; + + /** + * 获取“分享到 QQ”按钮点击状态及自定义分享内容。 + * @param {Object} options 配置项。 + * @deprecated + */ + onMenuShareQQ(options?: OnMenuShareQQOption): void; + + /** + * 获取“分享到腾讯微博”按钮点击状态及自定义分享内容。 + * @param {Object} options 配置项。 + * @deprecated + */ + onMenuShareWeibo(options?: OnMenuShareWeiboOption): void; + + /** + * 获取“分享到 QQ 空间”按钮点击状态及自定义分享内容。 + * @param {Object} options 配置项。 + * @deprecated + */ + onMenuShareQZone(options?: OnMenuShareQZoneOption): void; + + /** + * 拍照或从手机相册中选图。 + * @param {Object} options 配置项。 + */ + chooseImage(options?: ChooseImageOption): void; + + /** + * 预览图片。 + * @param {Object} options 配置项。 + */ + previewImage(options?: PreviewImageOption): void; + + /** + * 上传图片。 + * @param {Object} options 配置项。 + */ + uploadImage(options?: UploadImageOption): void; + + /** + * 下载图片。 + * @param {Object} options 配置项。 + */ + downloadImage(options?: DownloadImageOption): void; + + /** + * 获取本地图片。 + * @param {Object} options 配置项。 + */ + getLocalImgData(options?: GetLocalImgDataOption): void; + + /** + * 开始录音。 + * @param {Object} options 配置项。 + */ + startRecord(options?: StartRecordOption): void; + + /** + * 停止录音。 + * @param {Object} options 配置项。 + */ + stopRecord(options?: StopRecordOption): void; + + /** + * 监听录音自动停止。 + * @param {Object} options 配置项。 + */ + onVoiceRecordEnd(options?: OnVoiceRecordEndOption): void; + + /** + * 播放语音。 + * @param {Object} options 配置项。 + */ + playVoice(options?: PlayVoiceOption): void; + + /** + * 暂停播放。 + * @param {Object} options 配置项。 + */ + pauseVoice(options?: PauseVoiceOption): void; + + /** + * 停止播放接口。 + * @param {Object} options 配置项。 + */ + stopVoice(options?: StopVoiceOption): void; + + /** + * 监听语音播放完毕。 + * @param {Object} options 配置项。 + */ + onVoicePlayEnd(options?: OnVoicePlayEndOption): void; + + /** + * 上传语音。 + * @param {Object} options 配置项。 + */ + uploadVoice(options?: UploadVoiceOption): void; + + /** + * 下载语音。 + * @param {Object} options 配置项。 + */ + downloadVoice(options?: DownloadVoiceOption): void; + + /** + * 识别音频并返回识别结果。 + * @param {Object} options 配置项。 + */ + translateVoice(options?: TranslateVoiceOption): void; + + /** + * 获取网络状态。 + * @param {Object} options 配置项。 + */ + getNetworkType(options?: GetNetworkTypeOption): void; + + /** + * 使用微信内置地图查看位置。 + * @param {Object} options 配置项。 + */ + openLocation(options?: OpenLocationOption): void; + + /** + * 获取地理位置。 + * @param {Object} options 配置项。 + */ + getLocation(options?: GetLocationOption): void; + + /** + * 开启查找周边 iBeacon 设备。 + * @param {Object} options 配置项。 + */ + startSearchBeacons(options?: StartSearchBeaconsOption): void; + + /** + * 关闭查找周边 iBeacon 设备。 + * @param {Object} options 配置项。 + */ + stopSearchBeacons(options?: StopSearchBeaconsOption): void; + + /** + * 监听周边 iBeacon 设备。 + * @param {Object} options 配置项。 + */ + onSearchBeacons(options?: OnSearchBeaconsOption): void; + + /** + * 关闭当前网页窗口。 + */ + closeWindow(): void; + + /** + * 隐藏右上角菜单。 + */ + hideOptionMenu(): void; + + /** + * 显示右上角菜单。 + */ + showOptionMenu(): void; + + /** + * 批量隐藏功能按钮。 + * @param {Object} options 配置项。 + */ + hideMenuItems(options?: HideMenuItemsOption): void; + + /** + * 批量显示功能按钮。 + * @param {Object} options 配置项。 + */ + showMenuItems(options?: ShowMenuItemsOption): void; + + /** + * 隐藏所有非基础按钮。 + * @param {Object} options 配置项。 + */ + hideAllNonBaseMenuItem(options?: HideAllNonBaseMenuItemOption): void; + + /** + * 显示所有功能按钮。 + * @param {Object} options 配置项。 + */ + showAllNonBaseMenuItem(options?: ShowAllNonBaseMenuItemOption): void; + + /** + * 调起微信扫一扫。 + * @param {Object} options 配置项。 + */ + scanQRCode(options?: ScanQRCodeOption): void; + + /** + * 跳转微信商品页。 + * @param {Object} options 配置项。 + */ + openProductSpecificView(options?: OpenProductSpecificViewOption): void; + + /** + * 拉取适用卡券列表并获取用户选择信息。 + * @param {Object} options 配置项。 + */ + chooseCard(options?: ChooseCardOption): void; + + /** + * 批量添加卡券。 + * @param {Object} options 配置项。 + */ + addCard(options?: AddCardOption): void; + + /** + * 查看微信卡包中的卡券。 + * @param {Object} options 配置项。 + */ + openCard(options?: OpenCardOption): void; + + /** + * 使用微信卡包中的卡券。 + * @param {Object} options 配置项。 + */ + consumeAndShareCard(options?: ConsumeAndShareCardOption): void; + + /** + * 发起一个微信支付请求。 + * @param {Object} options 配置项。 + */ + chooseWXPay(options?: ChooseWXPayOption): void; + + /** + * 调起微信支付分。 + * @param {Object} options 配置项。 + */ + openBusinessView(options?: OpenBusinessViewOption): void; + + /** + * 共享收货地址。 + * @param {Object} options 配置项。 + */ + openAddress(options?: OpenAddressOption): void; + + /** + * 跳转到小程序。 + * @param {Object} options 配置项。 + */ + launchMiniProgramOption(options?: LaunchMiniProgramOption): void; + + /** + * 打开企业微信红包。 + * @param {Object} options 配置项。 + */ + openEnterpriseRedPacket(options?: OpenEnterpriseRedPacketOption): void; + + /** + * 打开企业微信聊天。 + * @param {Object} options 配置项。 + */ + openEnterpriseChat(options?: OpenEnterpriseChatOption): void; + + /** + * 小程序相关接口。 + */ + miniProgram?: { + /** + * 处理成功的回调方法。 + * @param {Object} options 配置项。 + */ + navigateTo(options?: MiniProgramNavigateToOption): void; + + /** + * 处理成功的回调方法。 + * @param {Object} options 配置项。 + */ + navigateBack(options?: MiniProgramNavigateBackOption): void; + + /** + * 处理成功的回调方法。 + * @param {Object} options 配置项。 + */ + switchTab(options?: MiniProgramSwitchTabOption): void; + + /** + * 处理成功的回调方法。 + * @param {Object} options 配置项。 + */ + reLaunch(options?: MiniProgramReLaunchOption): void; + + /** + * 处理成功的回调方法。 + * @param {Object} options 配置项。 + */ + redirectTo(options?: MiniProgramRedirectToOption): void; + + /** + * 向小程序发送消息。 + * @param {Object} options 配置项。 + */ + postMessage(options?: MiniProgramPostMessageOption): void; + + /** + * 获取当前环境 。 + * @param {Function} callback 回调函数。 + */ + getEnv(callback: (res?: MiniProgramGetEnvCallbackResult) => void): void; + }; + } +} + +interface Window { + /** 微信 sdk 实例 */ + wx: typeof wx; +} + +declare const wx: WechatJsSdk.Wx; diff --git a/vue.config.js b/vue.config.js new file mode 100644 index 0000000000000000000000000000000000000000..d2c3d6d1abcbd3d24a56f173549ce6da8b7cb658 --- /dev/null +++ b/vue.config.js @@ -0,0 +1,62 @@ +const webpackVueConfig = require('./build/webpack.vue.config'); +const { + devServerPort, + devServerHost, + devServerProxy, + publicPath, + transpileDependencies, + outputDir, +} = require('./build/build-config'); +const { coreJsImports } = require('./build/core-import'); +const path = require('path'); + +/** @type {import('@vue/cli-service').ProjectOptions} */ +module.exports = { + pages: { + index: { + entry: [...coreJsImports, path.resolve(__dirname, './src/main.ts')], + template: path.resolve(__dirname, './public/index.html'), + filename: 'index.html', + }, + }, + + publicPath, + + outputDir, + + assetsDir: 'assets', + + configureWebpack: webpackVueConfig, + + chainWebpack: config => { + // 观看页应用大部分 chunk 是不太需要 prefetch 的,为了减少带宽和请求数,可以去掉 + config.plugins.delete('prefetch-index'); + + // 某些移动端设备对文件编码识别有误,把非 ascii 字符都转成 unicode 编码 + config.optimization.minimizer('terser').tap(item => { + item[0].terserOptions.output = item[0].terserOptions.output || {}; + item[0].terserOptions.output.ascii_only = true; + return item; + }); + }, + + transpileDependencies, + + devServer: { + host: devServerHost, + port: devServerPort, + disableHostCheck: true, + hot: true, + // https: true, + overlay: { + warnings: false, + errors: true, + }, + stats: 'minimal', + + proxy: devServerProxy, + contentBase: path.resolve(__dirname, './map-file/'), + }, + + productionSourceMap: false, +};