Commit 3822b033 authored by 吴江涛's avatar 吴江涛

初版

parents
# 顶部的EditorConfig文件
root = true
# unix风格的换行符,每个文件都以换行符结尾
[*]
end_of_line = lf
insert_final_newline = true
# 设置默认字符集
charset = utf-8
# 去除行尾空白字符
trim_trailing_whitespace = true
# 使用空格缩进,设置2个空格缩进
indent_style = space
indent_size = 2
# 忽略eslint校验路径,例如:
# src/libs/@spark
\ No newline at end of file
module.exports = {
parser: '@babel/eslint-parser',
env: {
browser: true,
es6: true,
node: true,
},
globals: {
CFG: true,
wx: true,
FYGE: true,
SPARK_ESLINT_PLUGIN: true,
remScale: true,
},
plugins: ['html', 'react', '@spark/best-practices', '@spark/security'],
extends: ['eslint:recommended', 'plugin:react/recommended'],
settings: {
react: {
version: 'detect',
},
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 7,
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true,
},
babelOptions: {
configFile: './node_modules/@spark/code-inspector/static/babel.config.js',
},
},
rules: {
'no-undef': 'error',
'no-unused-vars': ['error', { vars: 'all', args: 'after-used', argsIgnorePattern: '^_', varsIgnorePattern: '^_', ignoreRestSiblings: true }],
'no-dupe-keys': 'error',
'no-fallthrough': 'error',
'no-global-assign': 'error',
'no-implied-eval': 'error',
'no-self-assign': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true, allowTaggedTemplates: true }],
'no-useless-escape': 'error',
'no-empty-pattern': 'error',
'no-empty-function': ['error', { allow: ['arrowFunctions', 'functions', 'methods'] }],
'no-var': 'error',
'no-dupe-class-members': 'error',
'no-unsafe-optional-chaining': 'error',
'no-const-assign': 'error',
'no-empty': ['error', { allowEmptyCatch: true }],
'prefer-const': 'warn',
'no-extra-boolean-cast': 'warn',
'no-mixed-spaces-and-tabs': 'warn',
'no-alert': 'warn',
'no-new-wrappers': 'warn',
'no-useless-concat': 'warn',
'no-useless-return': 'warn',
'prefer-promise-reject-errors': ['warn', { allowEmptyReject: true }],
'spaced-comment': 'warn',
'react/prop-types': 'off',
'react/display-name': 'off',
'react/jsx-pascal-case': 'error',
'jsx-quotes': 'warn',
'react/jsx-tag-spacing': 'error',
'react/require-render-return': 'error',
'semi': [1]
},
overrides: [
{
files: ['public/**/*.html'],
rules: {
'no-var': 'off',
'@spark/security/third-party-whitelist': 'error',
'@spark/best-practices/no-url-in-js': 'error',
'@spark/best-practices/no-arrow-function': 'error',
'@spark/best-practices/no-es6-variable-declaration': 'error',
},
},
{
files: ['src/**/*.{js,jsx}'],
rules: {
'@spark/best-practices/no-url-in-js': 'error',
},
},
],
};
.DS_Store
node_modules/
___cache/
__cache/
coverage/
npm-debug.log
selenium-debug.log
.idea
.builds
.project
.vscode
yarn-error.log
.yarn
.package-lock
yarn.lock
.cache
packages/**/package-lock.json
released
output.js
output.js.map
.psd
.psb
#src/assets/
\ No newline at end of file
registry = http://npm.dui88.com
\ No newline at end of file
module.exports = {
semi: true, // 结尾加分号
singleQuote: false, // 使用单引号
jsxSingleQuote: false, // jsx中使用单引号
bracketSpacing: true, // 括号和参数之间有空格
jsxBracketSameLine: true, // 标签属性较多时,标签箭头>另起一行
quoteProps: 'as-needed', // 属性加引号需要加时再加
printWidth: 120, // 每行字符个数
};
registry "http://npm.dui88.com"
\ No newline at end of file
import MD from 'spark-utils/out/md/index.js';
let appId = CFG.appID;
const dcm = '202.' + CFG.projectId + '.0.0';
const domain = '//embedlog.duiba.com.cn';
let MDList = [
{
ele: `.test-md1`,
data: {
dpm: `${appId}.110.5.1`,
dcm,
domain,
appId
},
once: false
}
];
export default () =>
MD({
show: MDList, // 曝光
click: MDList // 点击
});
### 注意事项
xxxxxxx
### 迭代日志
## 20230202 [大雁链接](https://www.bilibili.com)
+ haha
+ haha1
+ haha2
\ No newline at end of file
/*
将此文件放到project/config/scripts/assets/目录下
在package.json文件的"scripts"字段下,分别修改dev和build命令:
"dev": "node ./config/scripts/assets/generateAssetList.js && node ./config/webpack.dev.config.js"
"build": "node ./config/scripts/assets/generateAssetList.js && node ./config/scripts/assets/index.js imgmin imgup && node ./config/webpack.prod.config.js"
*/
const fs = require('fs')
const path = require('path')
/* 请先配置:预加载的资源文件夹名称,或者设置预加载、异步加载资源路径*/
const preloadFolder = []; // 在/src/assets文件夹下,请设置需要预加载的资源文件目录,默认值预加载为loading文件夹, 其他均为异步加载
const otherFolder = ['loadingDemo']; // 在/src/assets文件夹下,不做任务处理的文件夹,不需要预加载, 也不需要异步加载
const initAssetList = { // 初始化预设资源处理
preLoadImg:[], // 设置预加载图片,例如:["loading/bg174.png","loading/上面.png","loading/底部173.png"]
asyncLoadImg:[] // 设置异步加载图片
}
/**
* 搜索文件夹里的文件
* @param {*} folderList 预加载文件夹名称数组
* @param {*} folderPath 文件夹地址,绝对路径
* @param {*} regExp 正则表达式,用于匹配目标文件
* @returns {string[]} 返回文件相对路径地址
*/
function searchFileFromFolder(folderPath='/src/assets', regExp=/\.(png|jpg|jpeg|svga|spi|json|mp3|wav)$/i) {
const preLoadImg = [], asyncLoadImg = [];
const searchOneDir = (absolutePath, relativePath) => {
fs.readdirSync(absolutePath).forEach(v => {
const absPath = absolutePath + '/' + v;
const relPath = relativePath ? relativePath + '/' + v : v;
if(fs.statSync(absPath).isFile()) {
if(regExp.test(v)){
if(preloadFolder.includes(relPath.split('/')[0])){
preLoadImg.push(relPath);
}else if(!otherFolder.includes(relPath.split('/')[0])){
asyncLoadImg.push(relPath)
}
}
}else {
searchOneDir(absPath, relPath);
}
});
}
searchOneDir(path.resolve('.') + folderPath, '');
console.log('资源预处理成功~')
return {
preLoadImg: [
...initAssetList.preLoadImg,
...preLoadImg
],
asyncLoadImg: [
...initAssetList.asyncLoadImg,
...asyncLoadImg
]
};
}
// 读资源目录
const assetList = searchFileFromFolder();
// 写资源列表json
fs.writeFileSync(path.resolve('.') + '/src/assetList.json', JSON.stringify(assetList))
\ No newline at end of file
const { assets } = require("spark-assets");
const args = process.argv.splice(2);
let argsObj = {
imgmin: false,
imgup: false
}
if (args.length == 1) {
argsObj.imgmin = 'imgmin' == args[0];
argsObj.imgup = 'imgup' == args[0];
} else if (args.length == 2) {
argsObj.imgmin = 'imgmin' == args[0];
argsObj.imgup = 'imgup' == args[1];
}
assets(argsObj)
\ No newline at end of file
exports.SPARK_CONFIG_DIR_KEY = ['OUTPUT_DIR', 'SOURCE_DIR', 'TEMP_DIR', 'ENTRY', 'TEMPLATE']
exports.SPARK_CONFIG = 'sparkrc.js'
//对应项目在线素材存储的cdn配置,用于迭代开发从线上拉取素材到本地
exports.SPARK_CDN_RES_CFG='sparkrescfg.json'
\ No newline at end of file
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
let result = source;
if (options.arr) {
options.arr.map(op => {
result = result.replace(op.replaceFrom, op.replaceTo);
})
} else {
result = source.replace(options.replaceFrom, options.replaceTo);
}
return result
};
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { exec } = require('child_process');
class GitToHtmlPlugin {
process(htmlPluginData) {
return new Promise(function (resolve) {
let gitStr = '';
exec('git remote -v & git branch --show-current & git config --global user.name', (_err, stdout, _stderr) => {
if (stdout) {
gitStr = `<script>CFG.___G___='${encodeURIComponent(stdout.replace(/\n/g, ';'))}'</script>`
}
htmlPluginData.html = htmlPluginData.html.replace('</body>', `${gitStr}</body>`);
resolve();
});
});
};
apply(compiler) {
compiler.hooks.compilation.tap('GitToHtmlPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).afterTemplateExecution.tapAsync(
"GitToHtmlPlugin",
async (html, cb) => {
await this.process(html);
cb(null, html);
}
);
});
}
}
module.exports = GitToHtmlPlugin;
const babel = require('@babel/core');
const HtmlWebpackPlugin = require("html-webpack-plugin");
class HtmlJsToES5Plugin {
process(htmlPluginData) {
return new Promise(function (resolve) {
const scriptRegExp = /<script>[\s\S]*?<\/script>/gis;
htmlPluginData.html = htmlPluginData.html.replace(scriptRegExp, function (match) {
const code = match.replace("<script>", "").replace("</script>", "");
const es5Code = babel.transform(code, { 'presets': ['@babel/preset-env'] }).code;
return `<script>${es5Code}</script>`;
});
resolve();
});
};
apply(compiler){
compiler.hooks.compilation.tap('HtmlJsToES5Plugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).afterTemplateExecution.tapAsync(
"HtmlJsToES5Plugin",
async (html, cb) => {
await this.process(html);
cb(null, html);
}
);
});
}
}
// exports.default = HtmlJsToES5Plugin;
module.exports = HtmlJsToES5Plugin;
// 端口是否被占用
exports.getProcessIdOnPort=function(port) {
try {
const execOptions = {
encoding: 'utf8',
stdio: [
'pipe',
'pipe',
'ignore',
],
};
return execSync('lsof -i:' + port + ' -P -t -sTCP:LISTEN', execOptions)
.split('\n')[0]
.trim();
} catch (e) {
return null;
}
}
const childProcessSync=async function(cmd, params, cwd, printLog = true) {
return new Promise((resolve, reject) => {
let proc = childProcess(cmd, params, cwd, printLog);
proc.on('close', (code) => {
if (code === 0) {
resolve(proc['logContent']);
} else {
reject(code);
}
});
});
}
const getGitBranch=async function(cwd) {
try {
const result = await childProcessSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], cwd, false);
if (!result.startsWith('fatal:')) {
return result.trim();
}
} catch (e) {
return undefined;
}
}
const getProjectNameByPackage=function() {
return require(`${process.cwd()}/package.json`).name
}
/**
* 理论上每个项目独一无二的文件夹名字-默认取分支名
* 如果当前未创建分支,取包名+日期
* (实际很多情况是直接clone老项目,包名相同,以防资源被替换,所以用日期加一下)
*/
exports.getCdnFolderName=async function() {
const branch = await getGitBranch(process.cwd());
const date = Date.now();
if (branch) {
return branch + "/" + date;
}
let foldername = getProjectNameByPackage() + "/" + date;
return foldername;
}
\ No newline at end of file
const path = require('path');
const { SPARK_CONFIG_DIR_KEY, SPARK_CONFIG } = require('./scripts/constant');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
const ProgressBarPlugin = require("progress-bar-webpack-plugin");
module.exports = function (isProd) {
const appPath = process.cwd();
const sparkConfig = require(path.join(appPath, SPARK_CONFIG));
const cssReg = /\.(css|less)$/;
// 处理相对路径
SPARK_CONFIG_DIR_KEY.map((key) => {
sparkConfig[key] = path.resolve(appPath, sparkConfig[key]);
});
const stylePlugins = [
require("autoprefixer")({
overrideBrowserslist: ["> 1%", "last 2 versions", "not ie <= 8"],
})
];
if (sparkConfig.PX2REM) {
stylePlugins.push(
require("postcss-px2rem-exclude")({
remUnit: 100, // 注意算法,这是750设计稿,html的font-size按照750比例
exclude: /node_modules/i,
})
);
}
const styleLoader = (cssOptions = {}) => {
return [
{
loader: "style-loader",
},
isProd && {
loader: MiniCssExtractPlugin.loader,
options: {
esModule: false,
},
},
{
loader: "css-loader",
options: {
...cssOptions,
importLoaders: 2, // 如果遇到css里面的 @import 执行后面两个loader。 不然如果import了less,css-loader是解析不了
},
},
{
loader: "postcss-loader",
options: {
sourceMap: isProd,
plugins: stylePlugins,
},
},
{
loader: require.resolve("less-loader"),
options: {
sourceMap: isProd,
lessOptions: {
modifyVars: {
"@RES_PATH": `"${isProd ? sparkConfig.RES_PATH_PROD + '/' : sparkConfig.RES_PATH}"`,
},
}
},
},
].filter(Boolean);
};
return {
entry: sparkConfig.ENTRY,
mode: isProd ? 'production' : 'development',
devtool: isProd ? "source-map" : "cheap-module-source-map",
output: {
path: path.resolve(__dirname, sparkConfig.OUTPUT_DIR),
filename: "js/[name].js",
},
resolve: {
extensions: ['.js', '.jsx', '.json'],
alias: {
"@src": path.resolve(__dirname, sparkConfig.SOURCE_DIR),
},
},
module: {
strictExportPresence: true,
rules: [
{
test: cssReg,
use: styleLoader(),
include: sparkConfig.SOURCE_DIR,
},
{
test: /\.(js|jsx)$/,
loader: require.resolve("babel-loader"),
// exclude: [path.resolve("node_modules")],
options: {
presets: [
require("@babel/preset-env").default,
require("@babel/preset-react").default
],
plugins: [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": false }],
require("@babel/plugin-transform-runtime").default,
],
sourceType: 'unambiguous'
},
},
{
test: [/\.(jpg|jpeg|png|svg|bmp)$/, /\.(eot|woff2?|ttf|svg)$/],
loader: require.resolve("url-loader"),
options: {
name: "[path][name].[ext]", // name默认是加上hash值。这里做了更改,不让加
outputPath: "images",
limit: 10240, // url-loader处理图片默认是转成base64, 这里配置如果小于10kb转base64,否则使用file-loader打包到images文件夹下
},
},
].filter(Boolean),
},
plugins: [
isProd &&
new MiniCssExtractPlugin({
filename: "styles/[name].[hash].css",
}),
new HtmlWebpackPlugin({
template: sparkConfig.TEMPLATE,
minify: !sparkConfig.UNMINIFY_INDEX && isProd,
}),
new CleanWebpackPlugin({
// cleanOnceBeforeBuildPatterns:['**/*', 'dist'] // 这里不用写 是默认的。 路径会根据output 输出的路径去清除
}),
new ProgressBarPlugin(),
].filter(Boolean),
optimization: {
minimize: isProd,
minimizer: [
// 替换的js压缩 因为uglifyjs不支持es6语法,
new TerserPlugin({
cache: true,
sourceMap: isProd,
extractComments: false, // 提取注释
parallel: true, // 多线程
terserOptions: {
compress: {
pure_funcs: ["console.log"],
},
},
}),
// 压缩css
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require("cssnano"),
cssProcessorPluginOptions: {
preset: ["default", { discardComments: { removeAll: true } }],
},
canPrint: true,
}),
],
// 修改文件的ids的形成方式,避免单文件修改,会导致其他文件的hash值变化,影响缓存
moduleIds: "hashed",
splitChunks: {
chunks: "all",
minSize: 30000, // 小于这个限制的会打包进Main.js
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10, // 优先级权重,层级 相当于z-index。 谁值越大权会按照谁的规则打包
name: "vendors",
},
},
},
// chunks 映射关系的 list单独从 app.js里提取出来
runtimeChunk: {
name: (entrypoint) => `runtime-${entrypoint.name}`,
},
},
};
}
const { SPARK_CONFIG } = require("./scripts/constant");
const Webpack = require("webpack");
const webpackBaseConfig = require("./webpack.common.config");
const WebpackMerge = require("webpack-merge");
const WebpackDevServer = require("webpack-dev-server");
const opn = require("opn");
const apiMocker = require('mocker-api');
const path = require('path');
const { getProcessIdOnPort } = require("./scripts/utils");
const sparkConfig = require(path.resolve(SPARK_CONFIG));
const webpackDevConfig = function () {
return {
devServer: {
useLocalIp: true,
open: false,
hot: true,
host: "0.0.0.0",
// hotOnly: true
before(app) {
app.use(/^\/$/, async (req, res, next) => {
const send = res.send.bind(res);
res.send = (body) => {
let result = body.toString();
result = result.replace('${APPID}', 'test');
send(result);
}
next();
})
if (sparkConfig.API_MOCK) {
apiMocker(app, path.resolve('./mock/index.js'), {
changeHost: true,
})
}
}
},
plugins: [
// new Webpack.WatchIgnorePlugin([/[\\/]mock[\\/]/]),
new Webpack.HotModuleReplacementPlugin()
]
};
};
const buildDev = async function (config) {
const { port } = config;
return new Promise((resolve, reject) => {
const config = WebpackMerge(webpackBaseConfig(false), webpackDevConfig());
const compiler = Webpack(config);
const devServerOptions = Object.assign({}, config.devServer);
console.log('devServerOptions', devServerOptions);
const server = new WebpackDevServer(compiler, devServerOptions);
if (getProcessIdOnPort(port)) {
reject(`端口 ${port} 已被使用`);
} else {
server.listen(
port || 8088,
"0.0.0.0",
() => {
console.log(`Starting server on http://localhost:${port}`);
opn(`http://localhost:${port || 8088}`);
resolve();
},
(err) => {
if (err) console.error("server linsten err--", err);
reject();
}
);
}
});
};
const args = process.argv.splice(2);
const port = args[0] || 8088
buildDev({
port: Number(port)
})
const path = require("path");
const chalk = require("chalk");
const fs = require('fs-extra');
const Webpack = require("webpack");
const WebpackMerge = require("webpack-merge");
const webpackBaseConfig = require("./webpack.common.config");
const { Uploader } = require("spark-assets");
const isProd = true;
const { getCdnFolderName } = require("./scripts/utils");
const { SPARK_CONFIG } = require("./scripts/constant");
const HtmlJsToES5Plugin = require("./scripts/plugins/HtmlJsToES5Plugin");
const { DepReporter } = require('spark-log-event');
// const sparkConfig = require('../sparkrc');
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");
const GitToHtmlPlugin = require("./scripts/plugins/GitToHtmlPlugin");
const PATH_ROOT = 'spark/v2';
const webpackProdConfig = function (cdnFolderName, resPathProd) {
return {
output: {
publicPath: `//yun.duiba.com.cn/spark/v2/${cdnFolderName}/`,
filename: isProd ? "js/[name].[contenthash:8].js" : "js/[name].[contenthash:4].js",
},
resolveLoader: {
modules: ['node_modules', path.resolve(__dirname, './scripts/loaders')]
},
module: {
rules: [
{
test: /sparkrc\.js$/,
exclude: [path.resolve("node_modules")],
use: [
{
loader: 'replaceLoader',
options: {
arr: [
{
replaceFrom: /(MOCK_STATUS: true)|(MOCK_STATUS:true)|("MOCK_STATUS": true)|("MOCK_STATUS":true)/,
replaceTo: '"MOCK_STATUS": false'
},
{
replaceFrom: /(RES_PATH:'\/src\/assets\/')|(RES_PATH: '\/src\/assets\/')|("RES_PATH":"\/src\/assets\/")|("RES_PATH": "\/src\/assets\/")/,
replaceTo: `"RES_PATH":"${resPathProd}/"`
}
]
}
}
]
},
]
},
plugins: [
new Webpack.IgnorePlugin(/[\\/]mock[\\/]/),
new ScriptExtHtmlWebpackPlugin({
custom: {
test: /\.js$/,
attribute: 'crossorigin',
value: 'anonymous'
}
}),
new GitToHtmlPlugin(),
new HtmlJsToES5Plugin(),
new DepReporter(),
new Webpack.ContextReplacementPlugin(
/moment[/\\]locale$/,
/zh-cn/,
),
],
node: {
crypto: 'empty'
}
};
};
const buildProd = async function () {
const cdnFolderName = await getCdnFolderName();
const appPath = process.cwd();
const sparkConfig = require(path.join(appPath, SPARK_CONFIG));
const _webpackProdConfig = await webpackProdConfig(cdnFolderName, sparkConfig.RES_PATH_PROD || '');
//新增 JS_PATH_PROD 用作
const newSparkCfg = Object.assign({}, sparkConfig);
newSparkCfg['JS_PATH_PROD'] = `https://yun.duiba.com.cn/spark/v2/${cdnFolderName}/js`;
const str = `module.exports =${JSON.stringify(newSparkCfg, null, 2)}`;
fs.writeFileSync(path.join(appPath, SPARK_CONFIG), str);
return new Promise((resolve, reject) => {
const config = WebpackMerge(webpackBaseConfig(isProd), _webpackProdConfig);
const compiler = Webpack(config);
compiler.run(async (error, stats) => {
if (error) {
return reject(error);
}
console.log(
stats.toString({
chunks: false, // 使构建过程更静默无输出
colors: true, // 在控制台展示颜色
})
);
console.log(`${chalk.yellow("打包成功, 等待上传")}\n`);
const uploader = new Uploader();
await Promise.all([
await uploader.uploadDir(
config.output.path,
`${PATH_ROOT}/${cdnFolderName}`,
/.(html|map)$/
),
await uploader.uploadDir(
config.output.path + "/js",
`${PATH_ROOT}/${cdnFolderName}/js/map_123_map`,
/.(html|js|css|css\.map)$/
),
]);
resolve();
});
});
};
buildProd();
{
"version": "0.2",
"language": "en",
"words": [
"duiba",
"fyge",
"hanzi",
"projectx",
"webfonts",
"Weixin",
"webp"
]
}
{
"compilerOptions": {
"experimentalDecorators": true,
"baseUrl": "./",
"paths": {
"@src/*": ["src/*"]
}
},
"exclude": [
"node_modules"
]
}
\ No newline at end of file
{"numOfComponents":1259,"numOfProject":796}
\ No newline at end of file
module.exports = {
"success": true,
"message": "报错了~",
"code": null,
"data": {
"test_config_01": "前端配置项测试",
"test_config_03": null,
"test_config_02": "111"
}
}
\ No newline at end of file
module.exports = {
"data": 3,
"success": true
}
\ No newline at end of file
module.exports = {
"data": "<p>以下是游戏规则:手速要快,点击红包雨。。333。。。。。。。。。。。。。。。。。。。。11111111111111sadasdadadsad5555555557777777777799999999999911111111111111111111111222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333311111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333333331111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333</p>",
"success": true
}
\ No newline at end of file
const rule = require("./common/rule");
const drawNum = require("./common/drawNum");
const coopFrontVariable = require("./common/coopFrontVariable");
const proxy = {
...require("./project"),
"GET /projectRule.query": rule,
"GET /drawNum.query": drawNum,
"GET /coop_frontVariable.query": coopFrontVariable
};
module.exports = proxy;
const mockjs = require("mockjs");
module.exports = {
"GET /drawPrize/draw.do": {
code: null,
data: {
optionImg: "https://yun.dui88.com/polaris/luckybag.png",
optionName: "五元优惠券",
prizeId: "sss_5",
prizeType: "4",
sendCount: "1",
},
message: null,
success: true,
},
"GET /drawPrize/index.do": {
code: null,
data: {
actStatus: 2,
carouselList: [
{
avatar: "",
extra: null,
name: "D*y",
prizeName: "奖品002",
},
{
avatar: "",
extra: null,
name: "运*晟",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "运*盛",
prizeName: "奖品004",
},
{
avatar: "",
extra: null,
name: "昆*锐",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "晋*鹏",
prizeName: "奖品005",
},
{
avatar: "",
extra: null,
name: "S*y",
prizeName: "奖品003",
},
{
avatar: "",
extra: null,
name: "星*腾",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "浩*邈",
prizeName: "奖品004",
},
{
avatar: "",
extra: null,
name: "振*华",
prizeName: "奖品002",
},
{
avatar: "",
extra: null,
name: "运*莱",
prizeName: "奖品004",
},
{
avatar: "",
extra: null,
name: "昆*颉",
prizeName: "奖品005",
},
{
avatar: "",
extra: null,
name: "星*鹏",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "涵*煦",
prizeName: "奖品005",
},
{
avatar: "",
extra: null,
name: "鸿*煊",
prizeName: "奖品005",
},
{
avatar: "",
extra: null,
name: "嘉*澍",
prizeName: "奖品004",
},
{
avatar: "",
extra: null,
name: "俊*晖",
prizeName: "奖品003",
},
{
avatar: "",
extra: null,
name: "晨*朗",
prizeName: "奖品004",
},
{
avatar: "",
extra: null,
name: "瑞*渊",
prizeName: "奖品003",
},
{
avatar: "",
extra: null,
name: "德*曜",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "昆*锐",
prizeName: "奖品005",
},
{
avatar: "",
extra: null,
name: "景*浩",
prizeName: "奖品004",
},
{
avatar: "",
extra: null,
name: "昌*茂",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "瀚*钰",
prizeName: "奖品003",
},
{
avatar: "",
extra: null,
name: "昊*明",
prizeName: "奖品003",
},
{
avatar: "",
extra: null,
name: "运*升",
prizeName: "奖品005",
},
{
avatar: "",
extra: null,
name: "晨*朗",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "昆*纶",
prizeName: "奖品002",
},
{
avatar: "",
extra: null,
name: "运*莱",
prizeName: "奖品002",
},
{
avatar: "",
extra: null,
name: "昆*雄",
prizeName: "奖品001",
},
{
avatar: "",
extra: null,
name: "M*e",
prizeName: "奖品002",
},
],
creditCount: 100,
dailyFreeCredit: null,
luckyUser: [],
prizeList: [
{
icon: "//yun.dui88.com/images/202301/r8dx21qlx4.png",
icon2: null,
name: "小七测试兜底",
prizeId: "sss_3",
prizeType: 2,
sendCount: 1,
},
{
icon: "//yun.dui88.com/images/202104/doiosk1eh2.png",
icon2: null,
name: "lw-QQ音乐月卡",
prizeId: "sss_29",
prizeType: 2,
sendCount: 1,
},
{
icon: "//yun.dui88.com/projectxh5/alipay-250-250.png",
icon2: null,
name: "抽奖支付宝10元",
prizeId: "sss_37",
prizeType: 2,
sendCount: 1,
},
{
icon: "//yun.duiba.com.cn/polaris/金币.53dde7d612a66263937c26c32bc90d559de414a9.jpeg",
icon2: null,
name: "抽奖积分",
prizeId: "sp_1",
prizeType: 1,
sendCount: 10,
},
{
icon: "//yun.duiba.com.cn/polaris/2f66e035f4a0fdbe0bcc6ea4ef2bcd2e.f351d5b3349b786f7f2eace9a3095e272cee7366.jpeg",
icon2: null,
name: "snowy腾讯视频周卡",
prizeId: "sss_7",
prizeType: 2,
sendCount: 1,
},
{
icon: "//yun.dui88.com/images/202106/i7179pjcm8.png",
icon2: null,
name: "星空饭盒-刘文实物",
prizeId: "sss_9",
prizeType: 2,
sendCount: 1,
},
{
icon: "//yun.dui88.com/images/202209/826dj4ld9i.png",
icon2: null,
name: "小七测试优惠券测试5(复制)",
prizeId: "sss_1",
prizeType: 2,
sendCount: 1,
},
{
icon: "//yun.dui88.com/images/202209/826dj4ld9i.png",
icon2: null,
name: "小七测试优惠券测试5(复制)",
prizeId: "sss_5",
prizeType: 2,
sendCount: 1,
},
],
rule: "1111111党的&&%%***¥¥十八大以来在习近平总书记的重要讲话中人民二字是绝对的高频词人民对美好生活的向往就是我们的奋斗目标我是人民的勤务员江山就是人民人民就是江山是深入人心的肺腑之彰显总书记深厚真挚的人民情怀面向新征程使命任务习近平主席在讲话中进一步指明强国建设民族复兴与人民之间的紧密关系全面建成社会主义现代化强国人民是决定性力量让现代化建设成果更多更公平惠及全体人民凝聚起强国建设民族复兴的磅礴力量一席话深刻揭示了强国家党的&&%%***¥¥十八大以来在习近平总书记的重要讲话中人民二字是绝对的高频词人民对美好生活的向往就是我们的奋斗目标我是人民的勤务员江山就是人民人民就是江山是深入人心的肺腑之彰显总书记深厚真挚的人民情怀面向新征程使命任务习近平主席在讲话中进一步指明强国建设民族复兴与人民之间的紧密关系全面建成社会主义现代化强国人民是决定性力量让现代化建设成果更多更公平惠及全体人民凝聚起强国建设民族复兴的磅礴力量一席话深刻揭示了强国家",
userCreditCount: 274,
},
message: null,
success: true,
},
};
const fs = require('fs')
let proxy = {}
fs.readdirSync(__dirname)
.some(filename => {
if (filename !== 'index.js') {
proxy = Object.assign(proxy, require('./' + filename))
}
})
module.exports = proxy;
{
"name": "temp_base",
"version": "3.5.8",
"private": true,
"scripts": {
"dev": "cross-env NODE_ENV=development node ./config/scripts/assets/generateAssetList.js && node ./config/webpack.dev.config.js",
"imgmin": "node ./config/scripts/assets/index.js imgmin",
"imgup": "node ./config/scripts/assets/index.js imgup",
"imgminup": "node ./config/scripts/assets/index.js imgmin imgup",
"spark:prebuild": "ncu @spark/pre-build -u && yarn && npx pre-build",
"lint": "eslint --ext .js,jsx,.html src public",
"prod": "rimraf ./dist &&cross-env NODE_ENV=production node ./config/webpack.prod.config.js",
"build": "rimraf ./dist &&cross-env NODE_ENV=production node ./config/scripts/assets/generateAssetList.js && node ./config/scripts/assets/index.js imgmin imgup && node ./config/webpack.prod.config.js"
},
"dependencies": {
"@spark/api-base": "^2.0.7",
"@spark/projectx": "^2.0.5",
"@spark/svgaplayer": "^2.0.3",
"@spark/ui": "^2.0.8",
"@spark/utils": "^2.0.17",
"duiba-utils": "^1.0.2",
"history": "^4.10.1",
"mobx": "^6.2.0",
"mobx-react": "^7.1.0",
"qs": "^6.9.4",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"spark-utils": "^0.0.12"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/eslint-parser": "^7.17.0",
"@babel/plugin-proposal-decorators": "^7.13.15",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@babel/preset-react": "^7.12.10",
"@spark/eslint-plugin-best-practices": "^0.0.1-beta.2",
"@spark/eslint-plugin-security": "^0.0.1-beta.5",
"@spark/pre-build": "^0.0.1-beta.11",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.2.2",
"chalk": "^4.1.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.3",
"css-loader": "^3.6.0",
"eslint": "^8.22.0",
"eslint-plugin-html": "^6.2.0",
"eslint-plugin-react": "^7.29.4",
"fs-extra": "^9.0.1",
"html-webpack-plugin": "^4.5.1",
"less": "^4.1.0",
"less-loader": "^7.2.1",
"mini-css-extract-plugin": "^1.3.4",
"mocker-api": "^2.7.5",
"mockjs": "^1.1.0",
"npm-check-updates": "^12.5.4",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss-loader": "^3.0.0",
"postcss-px2rem-exclude": "0.0.6",
"prettier": "^2.0.5",
"progress-bar-webpack-plugin": "^2.1.0",
"rimraf": "^3.0.2",
"script-ext-html-webpack-plugin": "^2.1.5",
"spark-assets": "^2.3.10",
"spark-log-event": "^1.0.4",
"style-loader": "^1.2.1",
"url-loader": "^4.1.1",
"webpack": "^4.43.0",
"webpack-bundle-analyzer": "^4.5.0",
"webpack-cli": "^4.3.1",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2"
}
}
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="dns-prefetch" href="//yun.duiba.com.cn" />
<link rel="preconnect" href="//embedlog.duiba.com.cn">
<title>活动标题</title>
<script type="text/javascript">
if (localStorage && localStorage.isWebp) {
document
.getElementsByTagName('html')[0]
.setAttribute('duiba-webp', 'true');
}
</script>
<script src="//yun.duiba.com.cn/spark/v2/spark.base.fz.wxpollyfill.js"></script>
<script src="//yun.duiba.com.cn/js-libs/rem/1.1.3/rem.min.js"></script>
<script src="//yun.duiba.com.cn/h5/lib/zepto.min.js"></script>
<script>
var CFG = CFG || {};
CFG.projectId = location.pathname.split('/')[2] || '1';
function getUrlParam(name) {
var search = window.location.search;
var matched = search
.slice(1)
.match(new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i'));
return search.length ? matched && matched[2] : null;
}
CFG.appID = '${APPID}';
if (!getUrlParam("appID")) {
// alert("【警告】检测到活动url中没有appID参数\n缺少该参数会导致埋点、分享、app信息获取错误。")
}
</script>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
\ No newline at end of file
{"proName":"SquareTurntable","proDesc":"","proPath":"/Users/duibagroup/Desktop/练手项目/SquareTurntable","createTime":1690190208526}
module.exports ={
"OUTPUT_DIR": "dist",
"SOURCE_DIR": "src",
"TEMP_DIR": "./.temp",
"ENTRY": "src/app.jsx",
"TEMPLATE": "./public/index.html",
"API_MOCK": true,
"PX2REM": true,
"IMAGE_Q1": 0.6,
"IMAGE_Q2": 0.8,
"RES_PATH": "/src/assets/",
"RES_PATH_PROD": "//yun.duiba.com.cn/spark/v2/temp_base/1683686616155",
"JS_PATH_PROD": "https://yun.duiba.com.cn/spark/v2/temp_base/1685429239137/js"
}
\ No newline at end of file
{"assetsPathArr":["//yun.duiba.com.cn/spark/v2/temp_base/1678169645525/loadingDemo/上面.png","//yun.duiba.com.cn/spark/v2/temp_base/1678169645525/loadingDemo/底部173.png","//yun.duiba.com.cn/spark/v2/temp_base/1678169645525/loadingDemo/背景.jpg"]}
\ No newline at end of file
import { generateAPI } from "./utils.js"
const API = generateAPI({
/** 获取活动规则 */
getRule: "projectRule.query",
/** 获取前端配置项 */
getFrontVariable: "coop_frontVariable.query",
/** 参与接口 post请求 */
doJoin: {
uri: "join.do",
method: "post",
},
/** 签到 */
doSign: {
uri: "checkin_1/doSign.do",
withToken: true, // 携带token
},
doTurntableDraw: {
uri: "drawPrize/draw.do",
withToken: true,
},
/** 抽奖首页 */
getTurntableInfo: "drawPrize/index.do",
});
// console.log('======', API)
export default API
declare type Methods = 'get' | 'post';
declare interface APIArguments {
uri: string,
method?: Methods | Uppercase<Methods>,
headers?: any
withToken?: boolean
secret?: string,
secretKey?: string,
contentType?: string,
showMsg?:boolean,
showLoading?: boolean;
}
declare type APIConfig = APIArguments | string
declare interface GenerateAPIParams {
[key: string]: APIConfig
}
declare type RequestParamType = Record<string, any>
declare interface ResponseType<T = any> {
code: number,
success: boolean,
message: string,
data: T
}
export declare function generateAPI<
T extends GenerateAPIParams = GenerateAPIParams,
P = RequestParamType,
Q = ResponseType>
(apiList: T): {
[key in keyof T]: (args?: P) => Promise<Q>
}
import { getPxToken } from "@spark/projectx";
import { callApi } from '@spark/api-base'
import { Loading, Toast } from '@spark/ui'
import { isFromShare, newUser } from 'duiba-utils';
import { errorHandler } from "@src/utils/errorHandler";
const mergeData = {
user_type: newUser ? '0' : '1',
is_from_share: isFromShare ? '0' : '1',
}
/**
* 请求方法get、post处理
* @param {*} value
* @returns
*/
function getRequestParams(value) {
if (typeof value === 'string') {
return {
uri: value,
method: 'get'
}
} else if (typeof value === 'object') {
const { uri, method = 'get', showMsg = true, showLoading = false, headers, withToken, secret, secretKey, contentType = 'form' } = value;
return {
uri,
method,
headers,
withToken,
secret,
secretKey,
contentType,
showLoading,
showMsg
}
} else {
console.error('getRequestParams: 传参有误');
}
}
/**
* 请求API通用处理
* @param {*} value
* @returns
*/
export function generateAPI(apiList) {
const api = {};
for (const key in apiList) {
const value = apiList[key];
const { method, uri, headers: mHeaders, withToken, secret, secretKey, contentType, showLoading, showMsg = true } = getRequestParams(value);
api[key] = async (params = {}, headers) => {
// 根据接口配置showLoading展示loading
// 600ms内结束的请求不显示loading,避免闪烁
let query = false;
showLoading && setTimeout(() => {
if (!query) {
Loading.show();
}
}, 600);
let token;
if (withToken) { // 是否携带token
try {
token = await getPxToken(); // 获取token
} catch (e) {
Toast('网络异常,请稍后再试~');
query = true;
showLoading && Loading.hide();// 根据接口配置showLoading关闭loading
return ({ success: false, data: '' });
}
}
const mergedHeaders = { ...mHeaders, ...headers }
if (withToken && token) {
params.token = token;
}
params = { ...params, ...mergeData };
const result = await callApi(uri, params, method, mergedHeaders, false, secret, secretKey, contentType)
.catch(e => {
query = true;
// 捕获网络异常
showMsg && Toast(e.message || '网络异常,请稍后再试~');
showLoading && Loading.hide();
});
query = true;
showLoading && Loading.hide();// 根据接口配置showLoading关闭loading
return new Promise((resolve) => {
if (result) {
// 判断接口错误
if (!result.success && showMsg) {
errorHandler(result);
}
// 返回整个结果
resolve(result);
} else {
resolve({ success: false, data: '' });
}
})
}
}
return api;
}
/**
* 活动主入口
*/
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { observer } from "mobx-react";
import "./app.less";
import store from "./store/index";
import Modal from "./modal/modal";
import "./utils/checkwebp"; // webp检查
import MD from "../MD"; // 埋点
MD();
// 此处为spark-cli动态生成
import LoadingDemo from "@src/pages/loadingDemo/loadingDemo";
import HomeDemo from "@src/pages/homeDemo/homeDemo";
import { PAGE_MAP, SKIN_MAP } from "./utils/constants";
import { GetCurrSkinId } from "./utils/utils";
/**
* 所有页面场景
*/
const pageMap = {
[PAGE_MAP.LOADING_PAGE]: <LoadingDemo />,
[PAGE_MAP.HOME_PAGE]: <HomeDemo />,
};
/**
* 所有皮肤map
*/
const skinMap = {
[SKIN_MAP.HOME_PAGE]: <LoadingDemo />,
};
@observer
class App extends Component {
async componentDidMount() {
// 获取前端开发配置,依据项目需要,酌情添加 !!!
// await store.getFrontVariable();
}
render() {
const { curPage, pageData, isChangePage } = store;
const skinId = GetCurrSkinId();
const skinPage = skinMap[skinId];
return (
<>
{skinPage && !isChangePage
? { ...skinPage, props: { ...pageData } }
: { ...pageMap[curPage], props: { ...pageData } }}
<Modal />
</>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
* {
margin: 0;
padding: 0;
}
html,
body {
font-size: 24px;
width: 100%;
height: 100%;
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
}
.modal_center{
left: 0 !important;
top: 0 !important;
bottom: 0 !important;
right: 0 !important;
margin: auto;
}
{"preLoadImg":[],"asyncLoadImg":[]}
\ No newline at end of file
"use strict";
import React from "react";
import { observer } from "mobx-react";
import "./SquareTurntable.less";
@observer
class SquareTurntable extends React.Component {
constructor(props) {
super(props);
this.state = {
// 是否已点击
isClick: false,
};
// ref列表
this.prizeRefList = [];
// 记录上次抽奖的结束位置
this.oldIndex = null;
// 转盘时间控制的定时器
this.timer = null;
}
async componentDidMount() {}
/** 停顿时间 */
waitTime = async(time) => {
return new Promise((resolve) => setTimeout(resolve, time));
}
/** 抽奖 */
draw = async () => {
const {
moveTime = 100,
beginIndex = 0,
turnTime = 5000,
addTime = 5,
maxMoveTime = 1000,
drawApi = () => {},
matchApiName = "prizeId",
itemSelectImg = "",
updateInfoFunction = () => {},
} = this.props;
if (this.state.isClick) return false;
this.setState({ isClick: true });
// 初始速度
let _moveTime = moveTime;
// 起始位置
let _beginIndex = this.oldIndex || beginIndex;
// 判断转盘是否结束
let flag = true;
// 至少转动的时间
let _turnTime = turnTime;
// 当前被选中的节点
let select = {};
// 数组长度
const len = this.prizeRefList?.length || 0;
const res = await drawApi();
if (res?.success) {
!!updateInfoFunction && updateInfoFunction();
this.timer = setTimeout(() => {
flag = false;
}, _turnTime);
const move = async () => {
if (!flag && select.getAttribute("data-prize-id") === res?.data[matchApiName]) {
this.setState({ isClick: false });
this.oldIndex = _beginIndex;
return res;
}
if (len === 0) return false;
if (!!itemSelectImg) {
this.prizeRefList[_beginIndex].lastChild.style.visibility = "hidden";
}
_beginIndex ++;
if (_beginIndex >= len) _beginIndex = 0;
select = this.prizeRefList[_beginIndex];
if (!!itemSelectImg) {
this.prizeRefList[_beginIndex].lastChild.style.visibility = "visible";
}
_moveTime += addTime;
_moveTime = _moveTime > maxMoveTime? maxMoveTime: _moveTime;
await this.waitTime(_moveTime);
return await move();
};
return await move();
}
};
render() {
const {
prizeList = [],
className = "",
renderBg = "",
renderCenter = "",
renderItem = () => {},
children = "",
itemSelectImg = "",
matchInitName = "prizeId"
} = this.props;
return (
<>
<div className={`SquareTurntable ${className}`}>
{/* 渲染背景 */}
{!!renderBg && renderBg}
{/* 渲染格子 */}
<div className="option_group">
{(prizeList || []).map((item, index) => {
return (
<div
className={`option_basic option_${index + 1}`}
key={`@${index}`}
data-prize-id={item[matchInitName]}
ref={(ref) => (this.prizeRefList[index] = ref)}>
{renderItem(item, index)}
{/* 选中样式 - 图片 */}
{!!itemSelectImg && itemSelectImg}
</div>
);
})}
{/* 中间区域渲染 */}
{!!renderCenter && (
<div className="option_basic">
{renderCenter}
</div>
)}
</div>
{/* 插槽 */}
{children}
</div>
</>
);
}
}
export default SquareTurntable;
@import "../../res.less";
.SquareTurntable {
width: 100%;
height: 100%;
position: absolute;
.option_group {
width: 100%;
height: 100%;
.option_basic {
width: 33.33%;
height: 33.33%;
left: calc(50% - 16.665%);
top: calc(50% - 16.665%);
position: absolute;
// background-color: #ae1b0a;
}
.option_1 {
transform: translateX(-100%) translateY(-100%);
}
.option_2 {
transform: translateX(0%) translateY(-100%);
}
.option_3 {
transform: translateX(100%) translateY(-100%);
}
.option_4 {
transform: translateX(100%) translateY(0%);
}
.option_5 {
transform: translateX(100%) translateY(100%);
}
.option_6 {
transform: translateX(0%) translateY(100%);
}
.option_7 {
transform: translateX(-100%) translateY(100%);
}
.option_8 {
transform: translateX(-100%) translateY(0%);
}
}
}
\ No newline at end of file
import React, { Component } from "react";
import './modal.less';
import { observer } from 'mobx-react';
import modalStore from '../store/modal';
import { toJS } from 'mobx';
/**
* 弹窗配置
*/
export const cfg = {};
@observer
class Modal extends Component {
constructor(props) {
super(props);
}
componentDidMount() { }
render() {
const list = toJS(modalStore.popList);
if (!list.length) {
// TODO:此处根据需要自行修改
// document.body.style.overflow='auto';
return <section></section>;
}
let PopUpMulti, popUpMultiData;
if (list.length > 1 && list[list.length - 1].isMulti == true) {
const popObj2 = list[list.length - 1];
PopUpMulti = cfg[popObj2.key];
popUpMultiData = popObj2.data;
}
const popObj = list[0];
const PopUp = cfg[popObj.key];
const popData = popObj.data;
if (PopUp || PopUpMulti) {
document.body.style.overflow='hidden';
}
return <section className="modal-hoc-bg" style={{
zIndex: modalStore.popList.length ? 1000 : -1,
display: modalStore.popList.length ? 'block' : 'none'
}}>
{PopUp && <PopUp popData={popData} />}
{PopUpMulti && <section className="modal-hoc-bg" style={{
zIndex: modalStore.popList.length ? 1000 : -1,
display: modalStore.popList.length ? 'block' : 'none'
}}><PopUpMulti popData={popUpMultiData} />
</section>}
</section>;
}
}
export default Modal;
\ No newline at end of file
.modal-hoc-bg {
position: fixed;
top: 0;
bottom: 0;
right: 0;
left: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 1000;
}
\ No newline at end of file
/**
* 活动首页demo 开发自行修改或替换
*/
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './homeDemo.less';
import SquareTurntable from '@src/components/SquareTurntable/SquareTurntable';
import { _throttle } from '@src/utils/utils';
import API from '@src/api';
@observer
class HomeDemo extends React.Component {
constructor(props) {
super(props);
this.table = null;
this.state = {
prizeList: []
}
}
async componentDidMount() {
const res = await API.getTurntableInfo();
if(res?.success) {
this.setState({ prizeList: res?.data?.prizeList || []});
}
}
/** 点击抽奖 */
handleClick = _throttle(async () => {
console.log("table", this.table)
const result = await this.table.draw();
if (!!result) {
console.log("抽奖结束", result)
}
})
render() {
return (
/**
* ref 必须 获取对象
* drawApi 必须 抽奖接口
* prizeList 必须 奖品列表
* itemSelectImg 必须 奖品选中效果 这里的元素将覆盖在奖品格子上
* renderItem 必须 定义每个格子里的内容如何布局和展示
* className 非必须 默认全屏样式
* renderBg 非必须 用于渲染背景,也可用于其他元素渲染,元素位置位于底层
* renderCenter 非必须 转盘中心位置,可用于渲染抽奖按钮或logo啥的
* updateInfoFunction 非必须 抽奖接口成功后将调用
* matchInitName 非必须 默认prizeId,用于初始化时往dom上挂载该命名属性的值,用于后续与接口返回的字段匹配
* matchApiName 非必须 默认prizeId, 定义读取接口返回的哪个字段名用于字段匹配
*
* -- 转盘参数相关 --
* moveTime 非必须 默认100,每隔多少毫秒移动一个格子
* beginIndex 非必须 默认0,转盘从哪个格子开始转
* turnTime 非必须 默认5000,转盘至少转动多少毫秒
* addTime 非必须 默认5,转盘每移动一次增加的移动时间间隔
* maxMoveTime 非必须,默认1000,转盘最久以该时间移动一格
*
*
* 支持插槽自定义内容,写在标签体内部
*/
<div className="homeDemo">
<SquareTurntable
ref={(ref) => (this.table = ref)}
className="turntable"
renderBg={<span className="bg"></span>}
renderCenter={<span className="logo"></span>}
prizeList={this.state.prizeList}
itemSelectImg={<span className="mask"></span>}
drawApi={API.doTurntableDraw}
updateInfoFunction={() => {
console.log("刷新数据");
}}
matchInitName="prizeId"
matchApiName="prizeId"
moveTime={100}
beginIndex={0}
turnTime={5000}
addTime={5}
maxMoveTime={1000}
renderItem={(item, index) => {
return (
<div className="item" key={`@${index}`}>
<div className="item_img_box">
<img src={item?.icon} alt="" className="item_img" />
</div>
<span className="item_name">{item?.name || ""}</span>
</div>
);
}}>
<span className="main_btn" onClick={this.handleClick}></span>
</SquareTurntable>
</div>
);
}
}
export default HomeDemo;
@import "../../res.less";
.homeDemo {
.turntable {
width: 620px;
height: 620px;
left: calc(50% - 310px);
top: 300px;
position: absolute;
.bg {
width: 750px;
height: 1235px;
left: calc(50% - 375px);
top: -70px;
position: absolute;
.sparkBg("Game/machine.png")
}
.logo {
width: 200px;
height: 200px;
left: calc(50% - 100px);
top: calc(50% - 100px);
position: absolute;
.sparkBg("Game/logo.png");
}
.main_btn {
width: 393px;
height: 118px;
left: calc(50% - 196.5px);
top: 650px;
position: absolute;
.sparkBg("Game/btn_top.png");
}
.item {
width: 200px;
height: 200px;
left: calc(50% - 100px);
top: 0;
position: absolute;
.item_img_box {
width: 190px;
height: 190px;
left: calc(50% - 95px);
top: 0;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
}
.item_img {
max-width: 190px;
max-height: 190px;
}
.item_name {
width: 180px;
height: 30px;
left: calc(50% - 90px);
bottom: 10px;
position: absolute;
font-size: 24px;
text-align: center;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
}
// 选中样式 - 使用图片
// .mask {
// width: 191px;
// height: 191px;
// left: calc(50% - 90.5px);
// top: calc(44% - 90.5px);
// position: absolute;
// visibility: hidden;
// .sparkBg("Game/mask.png");
// }
// 选中样式 - 自定义css
.mask {
width: 191px;
height: 191px;
left: calc(50% - 90.5px);
top: calc(44% - 90.5px);
position: absolute;
visibility: hidden;
background: transparent;
border: 2px solid yellow;
}
}
}
/**
* loading页demo 开发自行修改或替换
*/
"use strict";
import React from "react";
import { observer } from "mobx-react";
import store from "../../store/index";
import "./loadingDemo.less";
import { preloadAsset } from "@src/utils/preload1.3.js";
import assetList from "@src/assetList.json";
import { PAGE_MAP } from "@src/utils/constants";
@observer
class LoadingDemo extends React.Component {
constructor(props) {
super(props);
this.curPercentage = 0;
this.intervalId = "";
this.isEvenLoad = true; // 是否匀速加载进度条
}
componentDidMount() {
this.preloadAssetInit();
}
/**
* 资源预加载
*/
preloadAssetInit = async () => {
const imageList = assetList.preLoadImg;
preloadAsset(imageList, 3, (progress) => this.onLoadingProgress(progress)).then(() => {
// 预加载资源完成
// 异步加载默认关闭
// setTimeout(() => {
// // 异步加载资源开始
// const asyncImageList = assetList.asyncLoadImg;
// preloadAsset(asyncImageList, 1)
// }, 5000);
});
};
/**
* 资源加载进度回调
* @param {*} index
* @param {*} progress
*/
onLoadingProgress = (progress) => {
const percentage = Math.floor(progress * 100);
console.log("progress", percentage);
if (this.isEvenLoad) {
this.setEvenProgress(percentage);
} else {
this.setProgress(percentage);
}
};
/**
* 非匀速加载进度
* @param {*} percentage
*/
setProgress = (percentage) => {
this.progressBar.style.transform = `translateX(${percentage - 100}%)`;
if (percentage == 100) {
setTimeout(() => {
store.changePage(PAGE_MAP.HOME_PAGE); // 跳转页面
}, 500);
}
};
/**
* 以1%匀速加载进度
* @param {*} percentage
*/
setEvenProgress = (percentage) => {
this.intervalId && clearInterval(this.intervalId);
let curPercentage = this.curPercentage;
this.intervalId = setInterval(() => {
if (curPercentage >= percentage) {
clearInterval(this.intervalId);
percentage == 100 && store.changePage(PAGE_MAP.HOME_PAGE); // 跳转页面
return;
}
curPercentage += 1;
this.curPercentage = curPercentage;
this.progressBar.style.transform = `translateX(${curPercentage - 100}%)`;
}, 10);
};
render() {
return (
<div className="loadingDemo">
<span className="bg174"></span>
<div className="progressBar">
<span className="atBottom"></span>
<span className="loadingBg">
<span className="above" ref={(el) => (this.progressBar = el)}></span>
</span>
</div>
<span className="inLoad">加载中···</span>
</div>
);
}
}
export default LoadingDemo;
@import "../../res.less";
.loadingDemo {
width: 750px;
height: 1624px;
left: 0px;
position: absolute;
top: 50%;
transform: translateY(-50%);
.bg174 {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.webpBg("loadingDemo/背景.jpg")
}
.progressBar {
width: 524px;
height: 22px;
left: 113px;
top: 717px;
position: absolute;
overflow: hidden;
border-radius: 6px;
.atBottom {
width: 524px;
height: 22px;
left: 0px;
top: 0px;
position: absolute;
.webpBg("loadingDemo/底部173.png")
}
.loadingBg{
width: 516px;
height: 14px;
left: 5px;
top: 4px;
overflow: hidden;
position: absolute;
border-radius: 6px;
}
.above {
display: block;
position: relative;
width: 516px;
height: 14px;
// animation: loading 1.6s linear 1;
.webpBg("loadingDemo/上面.png");
border-radius: 6px;
}
}
.inLoad {
width: 117px;
height: 23px;
left: 322px;
top: 758px;
position: absolute;
font-size: 24px;
line-height: 23px;
color: #ffffff;
}
}
@keyframes loading {
0% {
transform: translateX(-520px);
}
100% {
transform: translateX(0px);
}
}
@RES_PATH: '/src/assets/';
@webp: '?x-oss-process=image/format,webp';
.sparkBg(@value) {
background: url("@{RES_PATH}@{value}") no-repeat top left / 100% 100%;
}
.webpBg(@value){
.sparkBg("@{value}");
[duiba-webp='true'] & {
.sparkBg("@{value}@{webp}");
}
}
\ No newline at end of file
import { PAGE_MAP } from '@src/utils/constants';
import { makeAutoObservable } from 'mobx';
import API from '../api/index';
const store = makeAutoObservable({
/** 活动规则 */
ruleInfo: '',
/** 前端开发配置 */
frontVariable: {},
/** 当前页面 */
curPage: PAGE_MAP.LOADING_PAGE,
isChangePage: false,
pageData: {},
/** 场景切换 */
changePage(page, data = {}) {
if (!this.isChangePage) this.isChangePage = true;
this.pageData = data;
this.curPage = page;
},
/** 获取活动规则 */
async initRule() {
// 模拟获取远程的数据
const { data } = await API.getRule();
this.ruleInfo = data;
},
/** 获取前端配置项 */
async getFrontVariable() {
// 获取前端开发配置
const { data } = await API.getFrontVariable();
this.frontVariable = data || {};
console.log('前端开发配置', data)
}
})
export default store;
import { makeAutoObservable } from 'mobx';
// 此处配置页面的优先级,越大优先级越高
// PopIndex:11
/**
* 弹窗优先级 可以是负数, 不写默认是10, 数值越小,层级越高
*/
const modalIndex = {}
const modalStore = makeAutoObservable({
popList: [],
/**
*
* @param {*} key 弹窗名,一般是类名的字符串
* @param {*} data 需要传递的数据,弹窗中使用 const {popData} = props; 获取
* @param {*} isMulti 是否是二级弹窗,在不关闭已有弹窗的基础上,弹出当前弹窗。注意,如果是二级弹窗,关闭时必须传key
*/
pushPop(key, data = {}, isMulti = false) {
if (this.popList.length) {
let cacheList = this.popList.slice();
cacheList.push({ key, data, isMulti });
cacheList = cacheList.sort((a, b) => ((modalIndex[b.key] ? modalIndex[b.key] : 10) - (modalIndex[a.key] ? modalIndex[a.key] : 10)))
this.popList.clear();
this.popList.push(...cacheList);
} else {
this.popList.push({ key, data, isMulti });
}
// console.log("this.popList:::",toJS(this.popList));
},
/**
*
* @param {*} key 弹窗名,一般是类名的字符串,关闭指定弹窗,若未指定,则关闭当前弹窗
*/
closePop(key) {
if (key) {
let cacheList = this.popList.slice();
cacheList = cacheList.filter(obj => (obj.key != key));
this.popList.clear();
this.popList.push(...cacheList);
} else {
this.popList.shift();
}
},
/**
* 关闭所有弹窗
*/
closePopAll() {
this.popList.clear();
}
});
export default modalStore;
/**
* 检查是否支持.webp 格式图片
* 支持 webp CDN ?x-oss-process=image/format,webp
* 不支持 CDN ?x-oss-process=image/quality,Q_80
*/
(function () {
let urlArr = [];
let flag = false,
lowAdr = false;
const ua = navigator.userAgent.toLowerCase()
const isAndroid = ua.indexOf('android') > -1 || ua.indexOf('adr') > -1;
if (isAndroid) {
const ver = parseFloat(ua.substr(ua.indexOf('android') + 8, 3));
lowAdr = ver < 4.4;
}
if (lowAdr && localStorage) {
delete localStorage.isWebp;
}
if (localStorage && !localStorage.isWebp) {
const img = new Image()
img.onload = function () {
if (img.width === 1 && !lowAdr) {
localStorage.isWebp = true;
document.getElementsByTagName('html')[0].setAttribute('duiba-webp', 'true')
} else {
localStorage.isWebp = '';
}
}
img.onerror = function () {
flag = true
localStorage.isWebp = ''
}
img.src = 'data:image/webp;base64,UklGRiQAAABXRUJQVlA4IBgAAAAwAQCdASoBAAEAAwA0JaQAA3AA/vuUAAA='
}
/**
* 图片处理
*/
function getOssImg(url) {
if (!url) {
return url
}
if (!flag && localStorage && (typeof localStorage.isWebp === 'undefined')) {
getOssImg(url)
} else {
// gif 的图片不做处理
urlArr = url.split('.')
// if (urlArr.length > 0 && urlArr[urlArr.length - 1] === 'gif' ) {
// return url + ''
// }
if (urlArr.length > 0 && ['png', 'jpg', 'jpeg'].includes(urlArr[urlArr.length - 1])) { //指定'png', 'jpg', 'jpeg'等格式的图片进行处理
if (localStorage && localStorage.isWebp) { // 图片支持webp格式设置
url = url + '?x-oss-process=image/format,webp'
} else {
url = url + '?x-oss-process=image/quality,Q_80'
}
}
return url + '';
}
}
String.prototype.ossImg = function () {
return getOssImg(this)
}
Array.prototype.imgWebpArray = function (key) {
const array = []
const list = this
for (let i = 0; i < list.length; i++) {
if (key && list[i][key]) {
list[i][key] = list[i][key].ossImg()
} else if (list[i]) {
list[i] = list[i].ossImg()
}
array.push(list[i])
}
return array
}
})()
/*
* @Author: super
* @Date: 2021-01-20 14:08:27
* @LastEditTime: 2021-01-20 14:53:39
* @LastEditors: super
* @Description:
*/
/**
* 弹窗优先级 可以是负数, 不写默认是10, 数值越小,层级越高
*/
export const MODAL_INDEX = {
// rank: 1,
};
/** 网络异常默认展示 */
export const DEFAULT_NET_ERROR = '网络异常,请稍后再试';
export const PAGE_MAP = {
HOME_PAGE: "homePage",
LOADING_PAGE: "loadingPage",
};
export const SKIN_MAP = {
HOME_PAGE: 'index'
}
/** @Author: all
* @Date: 2022-04-12 11:26:44
**/
import { Toast } from "@spark/ui";
import { DEFAULT_NET_ERROR } from "./constants";
// 需要过滤的错误码
export const filterCode = ["600002"];
export const errMessageMap = {
1020: "活动未开始",
1021: "活动已结束",
1007: "活动太火爆了,奖品已抢完咯~",
};
/**
* 统一错误处理
* @param e
*/
export function errorHandler(e) {
if ((e.code == 0 && e.message == "请稍后再试") || filterCode.indexOf(`${e.code}`) >= 0) return;
switch (e.code) {
default: {
const msg = errMessageMap[e.code] || e.message || DEFAULT_NET_ERROR;
Toast(msg, 2000, { hideOthers: true });
break;
}
}
}
import { loadSvga } from '@spark/svgaplayer'
// import * as FYGE from 'fyge';
// import { Howl } from 'howler';
import { RES_PATH } from '../../sparkrc'
/**
* 预加载资源(/png|jpg|jpeg|svga|spi|json|mp3|wav/)
* @param {string[]} urlList 资源地址列表
* @param {number} batchNum 每批并行加载的资源个数(一般来说该数字越大整体加载速度越快,但加载前期会更卡顿)
* @param {Function} [onProgress] 加载进度回调,每加载完一个资源回调一次,入参为进度值(0,1]
* @returns {Promise} 返回一个只会resolve(loadedData)的promise,loadedData保存了所有预加载好的资源,可通过相对路径索引
* @example
* //例
* const loadedData = await PreloadAsset(urlList, 10, onProgress);
* const image = loadedData['image/fish.png'];
* const svgaData = loadedData['svga/fish.svga'];
* const spiData = loadedData['spine/fish.spi'];
* const lottieData = loadedData['lottie/fish.json'];
*/
export function preloadAsset(urlList, batchNum, onProgress) {
return new Promise((resolve) => {
/** 要加载资源总数 */
const totalNum = urlList.length;
/** 要加载的资源索引 */
let assetIndex = -1;
/** 已加载完毕的资源个数 */
let loadedNum = 0;
/** 存放加载好的数据,用地址索引 */
const loadedData = {};
/** 加载逻辑 */
const doLoad = async () => {
if (loadedNum >= totalNum) {
totalNum == 0 && onProgress && onProgress(1); // 无加载资源时,即为假loading
resolve(loadedData); // 加载完毕
} else {
assetIndex++;
if (assetIndex >= totalNum) return
const key = urlList[assetIndex];
const url = RES_PATH + urlList[assetIndex];
const result = await loadOneAsset(url);
if (!result) {
console.warn('加载异常', url);
}
loadedData[key] = result;
loadedNum++;
onProgress && onProgress(loadedNum / totalNum);
doLoad();
}
}
batchNum = batchNum || 1;
for (let index = 0; index < batchNum; index++) {
doLoad();
}
})
}
/**
* 加载一个资源
* @param {string} url 地址
*/
async function loadOneAsset(url) {
const fileType = url.split('.').pop();
switch (true) {
case (/png|jpg|jpeg/).test(fileType):
return await loadOneImg(url);
case (/svga/).test(fileType):
return await loadOneSvga(url);
// case (/spi/).test(fileType):
// return await loadOneSpi(url);
// case (/json/).test(fileType):
// return await loadOneJson(url);
// case (/mp3|wav/).test(fileType):
// return await loadOneAudio(url);
default:
console.warn('非法资源', url);
return false;
}
}
/**
* 加载一张图片
* @param {string} url 地址
*/
function loadOneImg(url) {
return new Promise(resolve => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = err => {
console.warn('load', url, err);
resolve(false)
};
img.crossOrigin = 'Anonymous'
img.src = url;
})
}
/**
* 加载一个svga
* @param {string} url 地址
*/
function loadOneSvga(url) {
return new Promise(resolve => {
loadSvga(url).then((data) => resolve(data[0])).catch(err => {
console.warn('load', url, err);
resolve(false)
});
})
}
// /**
// * 加载一个spine
// * @param {string} url 地址
// */
// function loadOneSpi(url) {
// return new Promise(resolve => {
// FYGE.loadSpine(url, spineData => {
// resolve(spineData);
// }, err => {
// console.warn('load', url, err);
// resolve(false);
// })
// })
// }
// /**
// * 加载一个Json
// * @param {string} url 地址
// */
// function loadOneJson(url) {
// return new Promise(resolve => {
// FYGE.GlobalLoader.loadJson((result, res) => {
// if (result) {
// resolve(res);
// } else {
// console.warn('load fail', url);
// resolve(false);
// }
// }, url)
// })
// }
// /**
// * 加载一个音频
// * @param {string} url 地址
// */
// function loadOneAudio(url) {
// return new Promise(resolve => {
// const sound = new Howl({
// src: url,
// onload: () => resolve(sound),
// onloaderror: err => {
// console.warn('load fail', url, err);
// resolve(false);
// },
// });
// })
// }
import {
start,
updateShare,
callShare,
Weixin,
} from "@spark/share"
/**
* @description: 小程序跳转
* @param {*}
* @return {*}
*/
export const miniGoUrl = (url) => {
wx.miniProgram.navigateTo({ url: url });
}
/**
* 判断是否为ios系统
*/
export function isIos() {
return navigator.userAgent.match(/iphone|ipod|ipad/gi)
}
/** 判断微信环境 */
export function isWeChat() {
const ua = window.navigator.userAgent.toLowerCase()
return ua.match(/MicroMessenger/i) == 'micromessenger'
}
/**
* 初始化分享
*/
export async function onInitShare(cb) {
await start([Weixin], function (success) {
console.log("share result:----", success)
cb && cb()
})
}
/**
* 更新分享
* @param {*} shareParams
* @param {*} justUpdate
*/
export function onUpdateShare(shareParams) {
console.info("更新分享", shareParams)
updateShare(shareParams)
}
/**
* 被动分享 - 北京银行
* @param {*} shareParams
*/
export function onCallShare(shareParams) {
console.info("分享链接", shareParams)
callShare(shareParams);
}
/**
* @description: 分享处理中心
* @param {Object} 分享信息
*/
export const requireShare = (opts) => {
const shareData = {
title: opts.shareTitle,
content: opts.shareContent,
url: opts.shareUrl,
images: [{ image: opts.shareThumbnail, type: "url" }],
};
console.log('分享数据', opts);
const shareStr = JSON.stringify(shareData);
return shareStr;
};
/**
* @description: 小程序分享
* @param {*}
* @return {*}
*/
export const miniDoShare = (opts) => {
console.log(opts);
wx.miniProgram.postMessage({
data: {
title: opts.title, // 标题
desc: opts.desc, // 描述
imgUrl: opts.imgUrl, // 图片
link: opts.link // 链接
}
});
}
/*
* @Author: all
* @Date: 2021-11-01 09:30:00
* @LastEditTime: 2021-11-02 18:30:00
* @LastEditors: all
* @Description:
*/
import { useRef, useEffect, useCallback } from "react";
export {
_throttle, // 节流
useThrottle, // 节流,函数组件
_debounce, // 防抖
getCookie, // 获取cookie的值
getUrlParam, // 获取url参数
delUrlParam, // 删除url中的参数
subStringCE, // 截取字符串 中2英1
check2Object, // 判断两个对象相等
getThousandToK, // 转换k
dateFormatter, // 日期格式化
dealTime, // 时间格式化
second2Date, // 秒转时间对象
waitTime, // 等待一段时间再执行
randomNum, // 获取区间随机数 [min,max)
shuffleArr, // 随机打乱数组
flatten, // 数据扁平化
onCtrScroll, // 控制滚动--兼容ios
trimSpace, // 去除字符串空格
GetCurrSkinId, // 获取当前链接皮肤id
windowJumpUrl // window的跳转链接
}
/**
* @description: 函数节流,普通防连点
* @param {(Function, number?)}
* @return {Function}
*/
const _throttle = (fun, delay = 2000) => {
let last, deferTimer;
return function () {
const now = +new Date();
if (last && now < last + delay) {
clearTimeout(deferTimer);
deferTimer = setTimeout(() => {
last = now;
}, delay);
} else {
last = now;
fun.apply(this, arguments);
}
};
};
function useThrottle(fn, delay = 2000, dep = []) {
const { current } = useRef({ fn, timer: null });
useEffect(function () {
current.fn = fn;
}, [fn]);
return useCallback(function f(...args) {
if (!current.timer) {
current.timer = setTimeout(() => {
delete current.timer;
}, delay);
current.fn.call(this, ...args);
}
}, dep);
}
/**
* @description: 函数防抖
* @param {(Function, number?, boolean? )}
* @return {Function}
*/
const _debounce = (fn, wait = 2000, immediate = false) => {
let timer = null
return function () {
const later = function () {
fn.apply(this, arguments)
}
if (immediate && !timer) {
later()
}
if (timer) clearTimeout(timer)
timer = setTimeout(later, wait)
}
}
/**
* 获取cookie的值
* @param {*} cookieName
*/
function getCookie(cookieName) {
const strCookie = document.cookie;
const arrCookie = strCookie.split('; ');
for (let i = 0; i < arrCookie.length; i++) {
const arr = arrCookie[i].split('=');
if (cookieName == arr[0]) {
return arr[1];
}
}
return '';
}
/**
* 获取url参数
* @param {string} name
*/
function getUrlParam(name) {
const search = window.location.search;
const matched = search
.slice(1)
.match(new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i'));
return search.length ? matched && matched[2] : null;
}
/**
* 删除url中的参数
* @param {*} url
* @param {*} arg
*/
function delUrlParam(url, paramKey) {
const _url = new URL(url)
const search = new URLSearchParams(_url.search)
search.delete(paramKey)
_url.search = search.toString();
return _url.href;
}
/**
* 日期格式化
* @param date 接收可以被new Date()方法转换的内容
* @param format 字符串,需要的格式例如:'yyyy/MM/dd hh:mm:ss'
* @returns {String}
*/
const dateFormatter = (date, format = "yyyy/MM/dd") => {
if (!date) return "-";
date = new Date(
typeof date === "string" && isNaN(date)
? date.replace(/-/g, "/")
: Number(date)
);
const o = {
"M+": date.getMonth() + 1,
"d+": date.getDate(),
"h+": date.getHours(),
"m+": date.getMinutes(),
"s+": date.getSeconds(),
"q+": Math.floor((date.getMonth() + 3) / 3),
S: date.getMilliseconds(),
};
if (/(y+)/.test(format)) {
format = format.replace(
RegExp.$1,
(date.getFullYear() + "").substr(4 - RegExp.$1.length)
);
}
for (const k in o) {
if (new RegExp("(" + k + ")").test(format)) {
format = format.replace(
RegExp.$1,
RegExp.$1.length === 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length)
);
}
}
return format;
};
/** 时间格式化 */
const dealTime = (msTime) => {
const time = msTime / 1000;
let hour = Math.floor(time / 60 / 60) % 24;
let minute = Math.floor(time / 60) % 60;
let second = Math.floor(time) % 60;
hour = hour > 9 ? hour : "0" + hour;
minute = minute > 9 ? minute : "0" + minute;
second = second > 9 ? second : "0" + second;
return `${hour}:${minute}:${second}`;
}
/**
* 转换k
* @param {*} num
*/
function getThousandToK(num) {
let s_x;
if (num >= 1000) {
let result = num / 1000;
result = Math.floor(result * 10) / 10;
s_x = result.toString();
let pos_decimal = s_x.indexOf(".");
if (pos_decimal < 0) {
pos_decimal = s_x.length;
s_x += ".";
}
while (s_x.length <= pos_decimal + 1) {
s_x += "0";
}
s_x += "k";
} else {
s_x = num;
}
return s_x;
}
/**
* 截取字符串 中2英1
* @param {*} str
* @param {*} sub_length
*/
function subStringCE(str, sub_length) {
const temp1 = str.replace(/[^\x20-\xff]/g, "**");
const temp2 = temp1.substring(0, sub_length);
const x_length = temp2.split("*").length - 1;
const hanzi_num = x_length / 2;
sub_length = sub_length - hanzi_num;
const res = str.substring(0, sub_length);
let endStr;
if (sub_length < str.length) {
endStr = res + "...";
} else {
endStr = res;
}
return endStr;
}
/**
* 随机打乱数组
* @param {*} arr
* @returns
*/
function shuffleArr(arr) {
for (let i = arr.length - 1; i >= 0; i--) {
const randomIndex = Math.floor(Math.random() * (i + 1))
const itemAtIndex = arr[randomIndex]
arr[randomIndex] = arr[i]
arr[i] = itemAtIndex
}
return arr
}
/**
* 获取区间随机数 [min,max)
* @export
* @param {*} min
* @param {*} max
* @return {*}
*/
function randomNum(min, max) {
return Math.floor(Math.random() * (max - min)) + min
}
/**
* 数据扁平化
* @export
* @param {*} arr
* @return {*}
*/
function flatten(arr) {
return arr.reduce((result, item) => {
return result.concat(Array.isArray(item) ? flatten(item) : item)
}, [])
}
/** 判断两个对象相等 */
const check2Object = (obj1, obj2) => {
const o1 = obj1 instanceof Object
const o2 = obj2 instanceof Object
if (!o1 || !o2) { /* 判断不是对象 */
return obj1 === obj2
}
if (Object.keys(obj1).length !== Object.keys(obj2).length) {
return false
}
for (const attr in obj1) {
const t1 = obj1[attr] instanceof Object
const t2 = obj2[attr] instanceof Object
if (t1 && t2) {
return check2Object(obj1[attr], obj2[attr])
} else if (obj1[attr] !== obj2[attr]) {
return false
}
}
return true
}
/**
* 秒转时间对象
* @param {Number} totalSecond 总秒数
* @return {{
* day: String,
* hour: String,
* minute: String,
* second: String
* }}
*/
const second2Date = (totalSecond) => {
const millisecond = totalSecond % 1000
totalSecond = totalSecond / 1000
// 获得总分钟数
const totalMinute = totalSecond / 60
// 获得剩余秒数
const second = totalSecond % 60
// 获得小时数
const totalHour = totalMinute / 60
// 获得分钟数
const minute = totalMinute % 60
// 获得天数
const day = totalHour / 24
// 获得剩余小时数
const hour = totalHour % 24
// 格式化的键值
const includesKey = ['month', 'day', 'hour', 'minute', 'second', 'totalHour', 'totalMinute']
// 日期对象
const dateObj = { day, hour, minute, second, millisecond, totalHour, totalMinute }
return Object.keys(dateObj).reduce((preVal, key) => {
// 值取整
const value = parseInt(dateObj[key])
if (includesKey.includes(key) && value < 10) {
if (value < 0) {
preVal[key] = '00'
} else {
preVal[key] = '0' + value
}
} else {
if (value.toString() === 'NaN') {
preVal[key] = '0'
} else {
preVal[key] = value.toString()
}
}
return preVal
}, {})
}
/**
* 等待一段时间再执行
* @param {number} time 等待的时间ms
*/
function waitTime(time) {
return new Promise(resolve => setTimeout(resolve, time))
}
/** 控制滚动--兼容ios */
const bodyScroll = (event) => {
event.preventDefault();
}
const onCtrScroll = (flag = true) => {
if (flag) { // 禁止滚动
document.body.addEventListener('touchmove', bodyScroll, { passive: false });
} else { // 开启滚动
document.body.removeEventListener('touchmove', bodyScroll, { passive: false });
}
}
/**
* 删除中间所有的空格
* @param {string} str
* @returns
*/
const trimSpace = (str) => {
let result;
result = str.replace(/(^\s+)|(\s+$)/g, "");
result = result.replace(/\s/g, "");
return result;
}
/**
* 获取当前皮肤id
* @returns
*/
const GetCurrSkinId = () => {
// eslint-disable-next-line no-useless-escape
const matched = location.pathname.match(/\/([^\/]*).html/);
const currSkinId = matched ? matched && matched[1] : ''
console.log('当前皮肤id', currSkinId)
return currSkinId
}
/** 跳转 */
const windowJumpUrl = (url) => {
url && (location.href = url);
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment