Commit f54f70da authored by Friends233's avatar Friends233

圆形转盘

parent 928186d3
# 忽略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'
},
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
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 // 点击
});
# best-code
# spark-template
最好的代码
\ No newline at end of file
烽火台 app3.0 中的静态模板配置
## 目录结构描述
## 项目内链接、前端配置
## 项目注意点 ⚠️
## 版本更新迭代内容
/*
将此文件放到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 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: {
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}`,
},
},
};
}
\ No newline at end of file
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) {
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 { uploadFiles } = 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 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 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`);
// await uploadFiles(config.output.path, '', cdnFolderName);
await uploadFiles(config.output.path, '', cdnFolderName, /.map$/);
// 上传map到不同路径,避免泄漏源码
await uploadFiles(
config.output.path + "/js",
'js/map_123_map',
cdnFolderName,
/.(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":775}
\ 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;
module.exports = {
"GET /drawPrize/drawPrize.do": {
"code": "200",
"data": {
"extra": "", // 扩展值
"options": [ // 奖品
{
"optionId": "1",
"optionName": "50万苏银豆", // 限制4个字
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"prizeId": "exercitation ea magna qui Excepteur",
"ruleId": "est ad aliquip",
"url": "id nulla",
"position": 0,
"prizeType": 61020165.35533035
}
]
},
"message": "活动已结束~",
"success": true
},
"GET /drawPrize/query.do":{
"code": null,
"data": {
"addLeftCount": -30822490.49901539,
"addTotalSize": -56900033.92918888,
"configFreeSize": -35037392.70375301,
"configJoinSpName": "irure",
"configJoinSpId": "labore nulla ea",
"configJoinType": 98192747.5435833,
"configJoinValue": -39461936.912254415,
"configLimitSize": 34400155.84736517,
"endTimestamp": 65520315.11996573,
"extra": "aute esse",
"freeLeftCount": 80, // 免费剩余次数,null = 不限制 【总剩余次数为:免费剩余次数+其他参与可用的值】
"joinCount": 88394659.5564234,
"myCreditsOrSpValue": 0, // 其他参与可用的值(当前道具数或当前积分数)【总剩余次数为:免费剩余次数+其他参与可用的值】
"options": [ // 奖品
{
"extra": null,
"optionId": "1",
"optionName": "00万苏银豆",
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "2",
"optionName": "10万苏银豆",
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "3",
"optionName": "20万苏银豆",
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "4",
"optionName": "30万苏银豆",
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "5",
"optionName": "40万苏银豆",
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "6",
"optionName": "50万苏银豆",
"optionImg": "//yun.dui88.com/wangqiang/test/%E6%AD%A3%E9%9D%A2%E5%8D%A1.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
],
"otherLeftCount": 19415568.966938406,
"leftCount": 14, // 距离上限的次数,==0时表示已达到抽奖次数上限
"ruleId": "officia non fugiat veniam",
"startTimestamp": 69028983.98718262,
"timestamp": -99325287.99439883
},
"message": null,
"success": true
}
}
\ No newline at end of file
module.exports = {
"code": null,
"data": {
"addLeftCount": -30822490.49901539,
"addTotalSize": -56900033.92918888,
"configFreeSize": -35037392.70375301,
"configJoinSpName": "irure",
"configJoinSpId": "labore nulla ea",
"configJoinType": 98192747.5435833,
"configJoinValue": -39461936.912254415,
"configLimitSize": 34400155.84736517,
"endTimestamp": 65520315.11996573,
"extra": "aute esse",
"freeLeftCount": 80, // 免费剩余次数,null = 不限制 【总剩余次数为:免费剩余次数+其他参与可用的值】
"joinCount": 88394659.5564234,
"myCreditsOrSpValue": 0, // 其他参与可用的值(当前道具数或当前积分数)【总剩余次数为:免费剩余次数+其他参与可用的值】
"options": [ // 奖品
{
"extra": null,
"optionId": "1",
"optionName": "00万苏银豆",
"optionImg": "http://localhost:8088/src/assets/%E8%BD%AC%E7%9B%98%E6%8A%BD%E5%A5%96/%E6%8A%BD%E5%A5%96.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "2",
"optionName": "10万苏银豆",
"optionImg": "http://localhost:8088/src/assets/%E8%BD%AC%E7%9B%98%E6%8A%BD%E5%A5%96/%E6%8A%BD%E5%A5%96.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "3",
"optionName": "20万苏银豆",
"optionImg": "http://localhost:8088/src/assets/%E8%BD%AC%E7%9B%98%E6%8A%BD%E5%A5%96/%E6%8A%BD%E5%A5%96.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "4",
"optionName": "30万苏银豆",
"optionImg": "http://localhost:8088/src/assets/%E8%BD%AC%E7%9B%98%E6%8A%BD%E5%A5%96/%E6%8A%BD%E5%A5%96.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "5",
"optionName": "40万苏银豆",
"optionImg": "http://localhost:8088/src/assets/%E8%BD%AC%E7%9B%98%E6%8A%BD%E5%A5%96/%E6%8A%BD%E5%A5%96.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
{
"extra": null,
"optionId": "6",
"optionName": "50万苏银豆",
"optionImg": "http://localhost:8088/src/assets/%E8%BD%AC%E7%9B%98%E6%8A%BD%E5%A5%96/%E6%8A%BD%E5%A5%96.png",
"position": -1061898.5569369644,
"prizeId": "exercitation consectetur",
"prizeType": 35747052.22155318,
"ruleId": "laborum ut",
"sendCount": null,
"url": null,
"userRecordId": null
},
],
"otherLeftCount": 19415568.966938406,
"leftCount": 14, // 距离上限的次数,==0时表示已达到抽奖次数上限
"ruleId": "officia non fugiat veniam",
"startTimestamp": 69028983.98718262,
"timestamp": -99325287.99439883
},
"message": null,
"success": true
}
\ No newline at end of file
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.6",
"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 && yarn spark:prebuild && cross-env NODE_ENV=production node ./config/webpack.prod.config.js",
"build": "rimraf ./dist && yarn spark:prebuild && 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.36",
"@spark/api-common": "^2.0.22",
"@spark/circle-turntable": "^1.0.3",
"@spark/projectx": "^2.0.13",
"@spark/svgaplayer": "^2.0.3",
"@spark/ui": "^2.0.8",
"@spark/utils": "^2.0.17",
"axios": "^0.19.2",
"css-loader": "^3.6.0",
"duiba-utils": "^1.0.2",
"history": "^4.10.1",
"mobx": "^6.2.0",
"mobx-react": "^7.1.0",
"postcss-loader": "^3.0.0",
"prettier": "^2.0.5",
"qs": "^6.9.4",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-redux": "^5.0.7",
"react-router-dom": "^5.2.0",
"redux": "^4.0.0",
"redux-thunk": "^2.3.0",
"rimraf": "^3.0.2",
"spark-utils": "^0.0.12",
"style-loader": "^1.2.1",
"webpack-bundle-analyzer": "^4.5.0"
},
"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.10",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.2.2",
"chalk": "^4.1.0",
"clean-webpack-plugin": "^3.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.12.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-px2rem-exclude": "0.0.6",
"progress-bar-webpack-plugin": "^2.1.0",
"script-ext-html-webpack-plugin": "^2.1.5",
"spark-assets": "^1.1.11",
"spark-log-event": "^1.0.4",
"url-loader": "^4.1.1",
"webpack": "^4.43.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":"最好的代码","proDesc":"","proPath":"/Users/friends233/duiba/最好的代码","createTime":1667272246330}
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/1653913591096",
"JS_PATH_PROD": "https://yun.duiba.com.cn/spark/v2/temp_base/1655709354034/js"
}
\ No newline at end of file
{"assetsPathArr":["//yun.duiba.com.cn/spark/v2/temp_base/1653913591096/.DS_Store","//yun.duiba.com.cn/spark/v2/temp_base/1653913591096/loadingDemo/上面.png","//yun.duiba.com.cn/spark/v2/temp_base/1653913591096/loadingDemo/底部173.png","//yun.duiba.com.cn/spark/v2/temp_base/1653913591096/loadingDemo/背景.jpg"]}
\ No newline at end of file
const apiCfg = {
/** 获取活动规则 */
getRule: 'projectRule.query',
/** 获取前端配置项 */
getFrontVariable: 'coop_frontVariable.query',
/** 参与接口 post请求 */
doJoin: {
uri: 'join.do',
method: "post"
},
/** 签到 */
doSign: {
uri: 'checkin_1/doSign.do',
withToken: true, // 携带token
},
/** 抽奖组件-抽奖 */
drawPrize: {
uri: 'drawPrize/drawPrize.do',
method: 'get',
},
/** 抽奖组件-查询首页接口 */
drawPrizeHome: 'drawPrize/query.do',
}
export default apiCfg;
import apiCfg from './apicfg';
import {getPxToken} from "@spark/projectx";
import {callApi} from '@spark/api-base'
import {Toast} from '@spark/ui'
import {isFromShare, newUser} from 'duiba-utils';
const mergeData = {
user_type: newUser ? '0' : '1',
is_from_share: isFromShare ? '0' : '1',
}
const apiList = {
...apiCfg
}
const API = generateAPI(apiList);
export default API;
/**
* 请求方法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', headers, withToken, secret, secretKey, contentType = 'form'} = value;
return {
uri,
method,
headers,
withToken,
secret,
secretKey,
contentType,
}
} else {
console.error('getRequestParams: 传参有误');
}
}
/**
* 请求API通用处理
* @param {*} value
* @returns
*/
function generateAPI(apiList) {
const api = {};
for (const key in apiList) {
const value = apiList[key];
const {method, uri, headers: mHeaders, withToken, secret, secretKey, contentType} = getRequestParams(value);
api[key] = async (params = {}, headers) => {
let token;
if (withToken) { // 是否携带token
try {
token = await getPxToken(); // 获取token
} catch (e) {
Toast('网络异常,请稍后再试~');
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 => {
// 捕获网络异常
Toast(e.message || '网络异常,请稍后再试~');
});
return new Promise((resolve) => {
if (result) {
// 判断接口错误
if (!result.success) {
Toast(result.message || '网络异常,请稍后再试~');
}
// 返回整个结果
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 CircleTurntableDemo from "./pages/CircleTurntableDemo/CircleTurntableDemo";
/**
* 所有页面场景
*/
export const pageMap = {
loading: <LoadingDemo />,
homePage: <HomeDemo/>,
CircleTurntableDemo:<CircleTurntableDemo />
}
@observer
class App extends Component {
async componentDidMount(){
// 获取前端开发配置,依据项目需要,酌情添加 !!!
// await store.getFrontVariable();
}
render() {
const { curPage } = store;
return (
<div>
{{...pageMap[curPage], props: {changePage: 'homePage'}}}
<Modal />
</div>
);
}
}
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;
}
{"preLoadImg":[],"asyncLoadImg":["转盘抽奖/主标题.png","转盘抽奖/关闭.png","转盘抽奖/抽奖.png","转盘抽奖/抽奖背景.png","转盘抽奖/转盘背景.png"]}
\ 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
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import store from '../../store/index';
import { CircleTurntable } from '@spark/circle-turntable'; // 引入圆形转盘组件
import { _throttle } from '@src/utils/utils.js';
import API from '../../api/index';
import './CircleTurntableDemo.less';
@observer
class CircleTurntableDemo extends React.Component {
constructor(props) {
super(props);
this.isDraw = false;
}
async componentDidMount() {
await store.drawPrizeHome(); // 调用接口,获取抽奖数据
}
handleApi = _throttle(async () => {
})
// 点击「抽奖」按钮
onClickStartButton = _throttle(async () => {
if (this.isDraw) {
return
}
this.isDraw = true
const { prizeInfo } = store;
const totalDrawNum = prizeInfo?.freeLeftCount * 1 + prizeInfo?.myCreditsOrSpValue * 1; // 今日剩余抽奖次数
if (totalDrawNum <= 0) {
return
}
const { data, success } = await API.drawPrize(); // 调用抽奖接口
if (success && data) {
store.setDrawPrizeInfo(data); // 设置抽奖数据
if (data?.options?.length > 0) {
const selectedId = data?.options[0].optionId; // 抽中奖品的id
this.ttRef.launch();
const optionsIdArr = prizeInfo?.options?.map(_ => _.optionId); // 奖品id数组
const selectedIndex = optionsIdArr.indexOf(selectedId); // 抽中奖品的索引值
console.log("获奖的索引0-7", selectedIndex);
// optionIndex: 最终落在奖项的索引 immediately: 是否立刻停止 randomOffset: 是否模拟随机角度偏移(效果更加真实)
this.ttRef.braking(selectedIndex, { immediately: false, randomOffset: false });
}
}
})
// 抽奖结束回调
onEnd = async () => {
await store.drawPrizeHome(); // 刷新抽奖组件首页数据
const { drawPrizeInfo } = store; // 抽奖数据
if (drawPrizeInfo?.options?.length > 0) {
const selectedId = drawPrizeInfo?.options[0].optionId; // 抽中奖品的id
if (selectedId == "thanks") { // 未抽中奖
console.log('未中奖')
} else { // 抽中奖品
console.log('中奖')
}
}
setTimeout(() => {
this.isDraw = false
}, 1000)
};
render() {
const { prizeInfo } = store;
return (
<div className="rotaryDraw">
<div className="rotaryTableContent">
{/* 圆形抽奖转盘 */}
{
prizeInfo?.options?.length > 0 &&
<CircleTurntable
className="turntable"
ref={(e) => { this.ttRef = e }}
options={prizeInfo?.options}
angleOffset={20}
radian={90}
launchDuration={1000}
brakingDuration={1000}
maxSpeed={20}
didStop={this.onEnd}
renderBackground={<span className="bg" />}
renderStartButton={
// 「抽奖」按钮
<div className="start-button" onClick={this.onClickStartButton}></div>
}
renderOption={(optionData, index) => {
return (
<div className="option" key={index + "a"}>
<span className="option-name">{optionData.optionName}</span>
<img className="option-icon" src={optionData.optionImg} />
</div>
);
}}
></CircleTurntable>
}
</div>
</div>
);
}
}
export default CircleTurntableDemo;
@import "../../res.less";
.rotaryDraw {
width: 750px;
height: 996px;
left: 0px;
top: 80px;
position: absolute;
background-color: rgba(0, 0, 0, 0.5);
overflow: hidden;
.rotaryTableContent {
width: 750px;
height: 734px;
left: 0px;
top: 152px;
position: absolute;
.atEndLight {
width: 750px;
height: 1624px;
left: 0px;
top: -500px;
position: absolute;
}
.turntable {
width: 713px;
height: 713px;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
position: absolute;
z-index: 99;
.bg {
width: 713px;
height: 713px;
left: 0px;
top: 0px;
position: absolute;
transform: rotate(29deg);
.sparkBg("转盘抽奖/抽奖背景.png");
}
.start-button {
width: 107px;
height: 130px;
.sparkBg("转盘抽奖/抽奖.png");
top: -10px !important;
}
.option {
width: 1.3rem;
height: 1.1rem;
transform: rotate(8deg);
.option-icon {
position: absolute;
left: 50%;
transform: translateX(-50%);
width: 60px;
height: 60px;
}
.option-name {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 60px;
display: block;
width: 100%;
// height: 0.2rem;
text-align: center;
font-size: 24px;
}
}
}
}
.text {
width: 667px;
height: 930px;
left: 42px;
top: 0px;
position: absolute;
.mainTitle {
width: 667px;
height: 202px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("转盘抽奖/主标题.png");
}
.remainingTimes {
width: 100%;
height: 74px;
left: 0;
top: 830px;
text-align: center;
position: absolute;
font-size: 32px;
color: rgba(255, 255, 255, 1);
}
.tips {
width: 100%;
height: 74px;
left: 26px;
top: 885px;
position: absolute;
font-size: 32px;
color: rgba(255, 255, 255, 1);
}
.primaryText {
font-size: 41px;
color: #fff;
}
}
.shutDown {
width: 43px;
height: 43px;
left: 353px;
top: 955px;
position: absolute;
.sparkBg("转盘抽奖/关闭.png");
z-index: 99;
}
.disk-turntable .tt_wrapper .tt_start-button {
position: absolute;
left: 50%;
top: 280px!important;
transform: translateX(-50%)!important;
}
.disk-turntable .tt_wrapper .tt_fan .tt_options .tt_option .tt_option-wrapper {
left: 20px!important;
position: absolute;
bottom: 0;
}
.disk-turntable .tt_wrapper .tt_fan .tt_options .tt_option:first-child .tt_option-wrapper {
left: 20px!important;
}
}
/**
* 活动首页demo 开发自行修改或替换
*/
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './homeDemo.less';
import store from '@src/store';
import { all_page } from '@src/utils/constants';
@observer
class HomeDemo extends React.Component {
constructor(props) {
super(props);
}
toPage = (page) => {
store.changePage(page)
}
render() {
return (
<div className="homeDemo">
{all_page.map(({name, page}) => {
console.log(name,page)
return <div key={name + page}>
<div onClick={() => this.toPage(page)}>{name}</div>
</div>
})}
</div>
);
}
}
export default HomeDemo;
@import "../../res.less";
.homeDemo {}
/**
* 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';
@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(()=>{
this.props.changePage && store.changePage(this.props.changePage); // 跳转页面
}, 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 && this.props.changePage && store.changePage(this.props.changePage); // 跳转页面
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 API from "@src/api";
export default {
/** 奖品数据 */
prizeInfo: {},
/** 设置奖品数据 */
setPrizeInfo(data) {
this.prizeInfo = data;
},
/** 抽奖组件-查询首页接口 */
async drawPrizeHome() {
const { data, success } = await API.drawPrizeHome();
if (data && success) {
this.setPrizeInfo(data);
}
},
/** 抽奖数据 */
drawPrizeInfo: {},
/** 设置抽奖数据 */
setDrawPrizeInfo(data) {
this.drawPrizeInfo = data;
},
}
\ No newline at end of file
import { makeAutoObservable } from 'mobx';
import API from '../api/index';
import CircleTurntableDemo from './CircleTurntableDemo';
const store = makeAutoObservable({
/** 活动规则 */
ruleInfo: '',
/** 前端开发配置 */
frontVariable: {},
/** 当前页面 */
curPage: 'homePage',
/** 设置活动规则 */
setRule(ruleInfo) {
this.ruleInfo = ruleInfo
},
/** 场景切换 */
changePage(page, callback) {
this.curPage = page;
callback && callback();
},
/** 获取活动规则 */
async initRule() {
// 模拟获取远程的数据
const { data } = await API.getRule();
this.setRule(data)
},
/** 获取前端配置项 */
async getFrontVariable() {
// 获取前端开发配置
const { data } = await API.getFrontVariable();
this.frontVariable = data || {};
console.log('前端开发配置', data)
},
...CircleTurntableDemo
})
export default store;
\ No newline at end of file
import { makeAutoObservable } from 'mobx';
// 此处配置页面的优先级,越大优先级越高
// PopIndex:11
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: 2022-11-01 11:25:33
* @LastEditors: Please set LastEditors
* @Description:
*/
/**
* 弹窗优先级 可以是负数, 不写默认是10, 数值越小,层级越高
*/
export const MODAL_INDEX = {
// rank: 1,
};
/**
* 接口错误消息配置
*/
export function ERROR_MESSAGE(errorCode) {
let message = "";
switch (errorCode) {
// case 999101:
// message = "登录已过期";
// break;
case 999002:
message = "活动已结束";
break;
case 500021:
message = "没有昨日线下步数";
break;
case 500022:
message = "已经领取过了";
break;
default:
message = "";
break;
}
return message;
}
export const all_page = [
{ name: '原型转盘', page: 'CircleTurntableDemo' }
]
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 { Toast } from "@spark/ui";
import { ERROR_MESSAGE } from "./constants"
import { useRef, useEffect, useCallback } from "react";
export {
showToast, // 展示Toast
_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
}
/**
* @description: 展示Toast
* @param { string } errCode 错误码
* @param { message } message 错误消息
*/
/** */
function showToast(errCode, message) {
Toast(ERROR_MESSAGE[errCode] || message || "网络异常,请稍后再试~", 2000, {hideOthers:true});
}
/**
* @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, ref) {
// 如果不包括此参数
if (url.indexOf(ref) == -1) return url;
const arr_url = url.split('?');
const base = arr_url[0];
const arr_param = arr_url[1].split('&');
let index = -1;
for (let i = 0; i < arr_param.length; i++) {
const paired = arr_param[i].split('=');
if (paired[0] == ref) {
index = i;
break;
}
}
if (index == -1) {
return url;
} else {
arr_param.splice(index, 1);
return base + '?' + arr_param.join('&');
}
}
/**
* 日期格式化
* @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 });
}
}
\ No newline at end of file
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