Commit 4a61a299 authored by haiyoucuv's avatar haiyoucuv

init

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-resnder-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';
import { logClick } from "@spark/utils/src-js/md";
import { jsonp } from "@spark/api-base";
let appId = CFG.appID;
const dcm = '202.' + CFG.projectId + '.0.0';
const domain = '';
const dom = `${CFG.channel}.0.0.0`;
const MDList = Array.from({ length: 30 }).map((_, index) => {
return {
ele: `.md${index + 1}`,
data: {
dpm: `${appId}.110.${index + 1}.1`,
dcm,
domain,
appId,
dom
},
once: false
};
})
export default () =>
MD({
show: MDList, // 曝光
click: MDList // 点击
});
export function logExposure(params) {
jsonp("/exposure/standard", params);
}
export function handleLogExposure(id, id2 = 1) {
logExposure({
dpm: `${appId}.110.${id}.${id2}`,
dcm,
domain,
appId,
dom
});
}
export function handleLogClick(id, id2 = 1) {
logClick({
dpm: `${appId}.110.${id}.${id2}`,
dcm,
domain,
appId,
dom
});
}
### 注意事项
xxxxxxx
### 迭代日志
## 20230202 [大雁链接](https://www.bilibili.com)
+ haha
+ haha1
+ haha2
### 端午节-线上测试:
官微菜单栏:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=guanwei
官微权益中心:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=quanyizhongxin
寿险小程序:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=shouxian
官微推文:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=tuiwen
太好钉:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=taihaoding
海报:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=haibao
太好店小程序:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=taihaodian
企微:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=qiwei
服务大厅:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fp8d9bf93b%2Findex.html%3FappID%3D93842&channel=fuwu
### 端午节-线上正式:
官微菜单栏:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=guanwei
官微权益中心:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=quanyizhongxin
寿险小程序:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=shouxian
官微推文:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=tuiwen
太好钉:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=taihaoding
海报:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=haibao
太好店小程序:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=taihaodian
企微:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=qiwei
服务大厅:
https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&actUrl=https%3A%2F%2F93842-activity.dexfu.cn%2Fprojectx%2Fpeb5290b6%2Findex.html%3FappID%3D93842&channel=fuwu
/*
将此文件放到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;
// 端口是否被占用
const net = require("net");
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;
}
}
exports.isPortAvailable = function (port, host = "localhost") {
return new Promise((resolve) => {
if (isNaN(port) || port != parseInt(port) || port < 0 || port > 65536) {
resolve(false);
}
try {
const tester = net.createServer()
// catch errors, and resolve false
.once('error', err => {
resolve(false);
})
// return true if succed
.once('listening', (err) => {
tester
.once('close', () => {
resolve(true);
})
.close()
})
.listen(port, host);
} catch (e) {
resolve(false);
}
})
}
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;
}
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: /noRem/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 {isPortAvailable} = 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(async (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);
let i = 0;
for (; i < 100; i++) {
const canUse = await isPortAvailable(port + i, "0.0.0.0");
console.log(port + i, canUse)
if (canUse) {
break;
}
}
server.listen(
port + i || 8088,
"0.0.0.0",
() => {
console.log(`Starting server on http://localhost:${port}`);
opn(`http://localhost:${port + i || 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 JavaScriptObfuscator = require("javascript-obfuscator");
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 files = fs.readdirSync(config.output.path + "/js");
let fileName = "";
for (let i = 0; i < files.length; i++) {
if (files[i].endsWith('.js') && files[i].indexOf("main") == 0) {
fileName = files[i];
}
}
const js = fs.readFileSync(path.join(config.output.path, "js/" + fileName), "utf-8");
const resJs = JavaScriptObfuscator.obfuscate(
js,
{
debugProtectionInterval: 4000,
debugProtection: true,
// 单行输出
compact: true,
selfDefending: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.3,
// 注入死代码
deadCodeInjection: true,
deadCodeInjectionThreshold: 0.2,
// 标识符名称生成器
// hexadecimal 16进制 包体增大较多
// mangled 短名称
// mangled-shuffled 与mangled相同,但带有洗牌字母表
// "identifier-names-generator": 'mangled-shuffled',
// 数字转表达式 如:
// const foo = 1234;
// const foo=-0xd93+-0x10b4+0x41*0x67+0x84e*0x3+-0xff8;
// numbersToExpressions: true,
log: true,
// 拆分字面字符串
splitStrings: true,
stringArray: true,
stringArrayRotate: true,
stringArrayCallsTransform: true,
stringArrayCallsTransformThreshold: 1,
stringArrayWrappersParametersMaxCount: 5,
stringArrayThreshold: 1,
// transformObjectKeys: true,
target: "browser-no-eval",
}
);
fs.writeFileSync(path.join(config.output.path, "js/" + fileName), resJs.getObfuscatedCode(), "utf-8");
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":5048,"numOfProject":2884}
\ No newline at end of file
const {AES, enc, mode, pad} = require("crypto-js");
const getOptions = (iv) => {
return {
iv: enc.Utf8.parse(iv),
mode: mode.CBC,
padding: pad.ZeroPadding,
};
}
/** 加密 */
exports.AESEncrypt = (str, key, iv) => {
const options = getOptions(iv);
return AES.encrypt(str, enc.Utf8.parse(key), options).toString();
};
/** 解密 */
exports.AESDecrypt = (cipherText, key, iv) => {
const options = getOptions(iv);
return AES.decrypt(cipherText, enc.Utf8.parse(key), options)
.toString(enc.Utf8)
.trim()
.replace(//g, '')
.replace(//g, '')
.replace(/\v/g, '')
.replace(/\x00/g, '');
};
module.exports = {
"success": true,
"message": "报错了~",
"code": null,
"data": {
"shareInfo": JSON.stringify({
"title": "分享标题",
"content": "分享描述",
// 缩略图-非小程序端
"thumbnail": "https://yun.duiba.com.cn/polaris/thumb.68900332d31453cda9bdf783b9ef7e7042c3c6ac.png",
// 缩略图-小程序端
"thumbnail_mini": "https://yun.duiba.com.cn/polaris/share_mini.0129b9ad510447874f454dfb8f98992a9527aa62.png"
}),
// 分享中间页-线上测试
"shareMiddle": "https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NjM&channel=channelName&isFromShare=1&empno=empnoParam&empname=empnameParam&param=changHangParam&actUrl=activityUrl&src=sxwx2db",
// 分享中间页-线上正式
// "shareMiddle": "https://93842-activity.dexfu.cn/customShare/share?id=Did1NjA4NzE&channel=channelName&isFromShare=1&empno=empnoParam&empname=empnameParam&param=changHangParam&actUrl=activityUrl&src=sxwx2db",
// 小程序分享落地页前缀
"miniProgramUrl": {
"qujiankang": "/packageHealthy/pages/webviewguanwei/webviewguanwei",
"xiaochengxu": "pages/webview/webview",
"taihaodian": "pages/newWeb/index",
"gongzhong": "/customShare/share?id=Did1NTc3OTk"
},
"posterQrCode": "https://yun.duiba.com.cn/polaris/%E6%AD%A3%E5%BC%8F.3e6da171761686ed529d75eaf31237d40d0fd334.png"
}
}
\ 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 {AESEncrypt} = require("./Crypto");
const proxy = {
...require("./project"),
"GET /projectRule.query": rule,
"GET /drawNum.query": drawNum,
"GET /coop_frontVariable.query": coopFrontVariable,
"GET /spring/start.do": {
"code": "code",
"success": true,
"message": "message",
"timeStamp": Date.now(),
"data": AESEncrypt(JSON.stringify({
"startId": "officia",
"countDown": 30
}), "1696BD3E5BB915A0", "cDOiBC1n2QrkAY2P"),
},
};
module.exports = proxy;
module.exports = {
"GET /home/index.do": {
code: null,
data: {
startTime: +new Date() - 34234234, /// 活动开始时间戳
endTime: +new Date() + 34234234, /// 活动结束时间戳
remainAnswerTimes: 324, /// 剩余答题机会
nickname: null,
avatar: null,
salesman: true, // 是否代理人
shareCode: '326534', // 分享code
},
message: null,
success: true,
timeStamp: +new Date(),
},
"GET /home/startAnswer.do": {
code: null,
data: {
startId: 1,
},
message: null,
success: true,
timeStamp: 1723602734176,
},
"GET /home/submitAnswer.do": {
code: null,
data: {
awardTimes: 18,
},
message: null,
success: true,
timeStamp: 1723602734176,
}
};
const fs = require('fs')
let proxy = {}
fs.readdirSync(__dirname)
.some(filename => {
if (filename !== 'index.js') {
proxy = Object.assign(proxy, require('./' + filename))
}
})
module.exports = proxy;
module.exports = {
"/common/queryUserInfo.do": {
"code": null,
"data": {
openId: "openId45342", // 静默授权 openid
unionId: "unionId2342", // unionId
nonSilentOpenid: "nonSilentOpenid65432", // 非静默授权
newEmpNo: "newEmpNo11", // 自己的业务员工号 不是业务员就没有
friendEmpNo: "friendEmpNo11", // 邀请人的业务员工号
friendOpenId: "friendOpenId543", // 邀请人的OpenId
},
"message": null,
"success": true
},
}
\ No newline at end of file
module.exports = {
"GET /common/prizeRecord.do": {
code: null,
data: {
activityCode: "1",
list: [
{
gmtCreate: 1741598452000,
gwObjectStatus: 1,
id: "2271436954",
prizeId: "sp_virtual_gw_1",
prizeImg: "//yun.duiba.com.cn/polaris/bkimg.cdn.bcebos.6ef8023b6501b44c5dc1f49a9fa478a72a9689f5.png",
prizeName: "太保虚拟商品",
userRecordId: 2271437739,
},
{
gmtCreate: 1741598341000,
gwObjectStatus: 0,
id: "2271435639",
prizeId: "sp_virtual_gw_1",
prizeImg: "//yun.duiba.com.cn/polaris/bkimg.cdn.bcebos.6ef8023b6501b44c5dc1f49a9fa478a72a9689f5.png",
prizeName: "太保虚拟商品",
userRecordId: 2271437739,
},
{
gmtCreate: 1741597050000,
gwObjectStatus: 0,
id: "2271419006",
prizeId: "sp_virtual_gw_1",
prizeImg: "//yun.duiba.com.cn/polaris/bkimg.cdn.bcebos.6ef8023b6501b44c5dc1f49a9fa478a72a9689f5.png",
prizeName: "太保虚拟商品",
userRecordId: 2271437739,
},
{
gmtCreate: 1741326305000,
gwObjectStatus: 1,
id: "2267883678",
prizeId: "sp_object_gw_1",
prizeImg: "//yun.duiba.com.cn/polaris/yihetang.45e0a9fd9a8e59e5b6b4ea774cd56f43bf81458c.png",
prizeName: "官微实物奖品官微实物奖品官微实物奖品",
userRecordId: null,
},
...new Array(10).fill({}).map((_, i) => {
return {
gmtCreate: 1723602636000,
gwObjectStatus: null,
id: "500750476" + i,
userRecordId: "500750476" + i,
prizeId: "sp_virtual_gw",
prizeImg:
i == 0
? "https://yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg"
: "//yun.dui88.com/projectxh5/phonebill-250-250.png",
prizeName: "10元话费" + i,
};
}),
],
focusStatus: true,
bindStatus: false,
},
message: null,
success: true,
timeStamp: 1723602734176,
},
};
module.exports = {
"GET /customActivity/qrcode/getQrcode": {
success: true,
code: "0000000000",
desc: "OK",
timestamp: 1730699350735,
data: "data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAAPcAAAD3CAIAAABO7lUEAAAFqklEQVR42u3cQY7cMAwEwMn/H+08IJdgYLJbmhL26PXIcsmHBsXP5/Mn+Pf8M7L3yT7pd0/xfDW+u89b/7W88h/KKaeccsopp5xyyimnnHLKKU8of8bGW+/+RA2be2Nz5efW8MXfopxyyimnnHLKKaeccsopp5zyEuX92dmcqranyO75uf0zaoxyyimnnHLKKaeccsopp5xyyn9Q+ZyYzTtnd8tb82n7ulFOOeWUU0455ZRTTjnllFNOOeX7lU/ZuqJNDf1VcZRTTjnllFNOOeWUU0455ZRTTvm7o63O6cTTbnfcedkY5ZRTTjnllFNOOeWUU0455ZRHlJ/Y79g1Z12jf7lrKKfcNZRT7hrKKXcN5ZS75j7lT9nY7LyT3c931KUdMSinnHLKKaeccsopp5xyyimnvER5W0q4aXpzhps6N/dP/CtAOeWUU0455ZRTTjnllFNOOeW1yttWrc3Z5rm+E78mc6tKOeWUU0455ZRTTjnllFNOOeUp5XdUGp1YidWm86138fQNyimnnHLKKaeccsopp5xyyik/SPnkFJOp3Oa77+9hnX2DX8+Zcsopp5xyyimnnHLKKaeccsoPUp6tRpoT3FbVtLmq1+xnyimnnHLKKaeccsopp5xyyimPKJ97i0/9aHuu/q7W2V///kQc5ZRTTjnllFNOOeWUU0455ZS/rXwz98meN3vrueb2xuZvvbWGowngWzOknHLKKaeccsopp5xyyimnnPKI8s05ndiDOJsbZrsFHdrdiXLKKaeccsopp5xyyimnnHLKI8rnsqE7TsRlK5/mVmNz/ywnv5RTTjnllFNOOeWUU0455ZRTXqK8LZlqc7+ZY7Z1ieo/R0c55ZRTTjnllFNOOeWUU0455Snlbanc5h7rr46aW/lNCcszpJxyyimnnHLKKaeccsopp5zyBeVtzrJ7zN64786UU0455ZRTTjnllFNOOeWUU96sPGs62zv4xB7N/bnhaBJNOeWUU0455ZRTTjnllFNOOeWXKd9MEvv7SsfztSFVmyf9KKeccsopp5xyyimnnHLKKae8R/lcwtWW92W787T9V/9ueXMClFNOOeWUU0455ZRTTjnllFOeUD6XKG2qaktI+/O+zS/Fcs5LOeWUU0455ZRTTjnllFNOOeULym9do6zFE580mzaOJpKUU0455ZRTTjnllFNOOeWUUx5R3p9VZZPE7GnAE/sixf1QTjnllFNOOeWUU0455ZRTTvmC8v4qov499ssp4RHdlCinnHLKKaeccsopp5xyyimnfEF5dpZ3dIiey+DmnvSnnotyGiinnHLKKaeccsopp5xyykuU35HuZdO0zeqozRm2JdHfJ4mUU0455ZRTTjnllFNOOeWUU/628mxP5M0k8cTTgHesxnKVHuWUU0455ZRTTjnllFNOOeWUR5RvVtLMGWqrPcrO8MT88cXvJuWUU0455ZRTTjnllFNOOeWU1yrPJoBz2dmt59barlmunKOccsopp5xyyimnnHLKKaec8lrl2dNu/eey2vo4Z/dzvL6Ncsopp5xyyimnnHLKKaeccsoXlD9lY7P6547fyiaAR9SlUU455ZRTTjnllFNOOeWUU075gvJ4R5iqvXFiJ+UTs+DNrxvllFNOOeWUU0455ZRTTjnllKeUZ1PC7BvK7sO538qufDx7pZxyyimnnHLKKaeccsopp5zyEuWb/YX7M8Fs2uhE3FSSSDnllFNOOeWUU0455ZRTTjnltys/sa90NiHd1Jn9lv3nf1FOOeWUU0455ZRTTjnllFNOOeXxFGxT1ebu7c8xf6tai3LKKaeccsopp5xyyimnnPLfUD63+nP3aUvKsqfLNt9p9ttBOeWUU0455ZRTTjnllFNOOeU9yts6O8fPUwVTws2ePptruPwVoJxyyimnnHLKKaeccsopp5zyBeWPYdw+/gJfOheLC3d1xwAAAABJRU5ErkJggg==",
},
};
module.exports = {
"/task_1/queryTasks.do": {
"code": null,
"message": null,
"success": true,
"data": {
"endTimestamp": 1622356189000,
"item": [
{
"title": "浏览短剧",
"subTitle": "食物食物食物食物食物食物食物衣蛾三四获得50枚",
"taskStatus": 0, // 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限
"buttonText": "1",
"code": "browse_view_mini",
"completedSize": 0,
"desc": "1",
"extra": null,
"icon": "//yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg",
"id": "task1",
"index": null,
"intervalLimitSize": 4,
"intervalType": 1,
// "jumpUrl": "/packageVariety/pages/videoVariety/videoVariety?source_channel=duanwu",
"jumpUrl": "https://www.baidu.com/",
"options": [
{
"degree": null,
"icon": null,
"icon2": null,
"id": "o79a0b9d2",
"index": 1,
"name": "抽奖奖品",
"prizeId": "sp_1",
"prizeType": 1,
"refId": null,
"refType": null,
"sendCount": 1
}
],
"prizePendingCode": null,
"prizePendingCodeList": [
{
"index": 1,
"code": "awefrgdrtyhgr566y4e564235"
}
],
"ruleId": "ru_1",
"sendPrize": null,
},
{
"title": "浏览视频任务名称试试太长的名称繁琐的就看见佛教给哦肉丝哦IE家人腹肌是能打",
"subTitle": "食物食物食物食物食物食物食物衣蛾三四获得50枚",
"taskStatus": 0, // 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限
"buttonText": "1",
"code": "browse_view",
"completedSize": 0,
"desc": "1",
"extra": null,
"icon": "//yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg",
"id": "task1",
"index": null,
"intervalLimitSize": 4,
"intervalType": 1,
"jumpUrl": "//yun.duiba.com.cn/polaris/3928%E9%A2%84%E5%91%8A__92pct_smaller.a97b770fba99798654d52d33c3e7cd9e85d42f63.mp4",
"options": [
{
"degree": null,
"icon": null,
"icon2": null,
"id": "o79a0b9d2",
"index": 1,
"name": "抽奖奖品",
"prizeId": "sp_1",
"prizeType": 1,
"refId": null,
"refType": null,
"sendCount": 1
}
],
"prizePendingCode": null,
"prizePendingCodeList": [
{
"index": 1,
"code": "awefrgdrtyhgr566y4e564235"
}
],
"ruleId": "ru_1",
"sendPrize": null,
},
{
"title": "浏览任务名称试试太长的名称繁琐的就看见佛教给哦肉丝哦IE家人腹肌是能打",
"subTitle": "食物食物食物食物食物食物食物衣蛾三四获得50枚",
"taskStatus": 0, // 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限
"buttonText": "1",
"code": "browse_product",
"completedSize": 0,
"desc": "1",
"extra": null,
"icon": "//yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg",
"id": "task1",
"index": null,
"intervalLimitSize": 4,
"intervalType": 1,
"jumpUrl": "//yun.duiba.com.cn/polaris/1.7daf1cf068b7ed6f80c9bff91236be64624d5ac6.png",
"options": [
{
"degree": null,
"icon": null,
"icon2": null,
"id": "o79a0b9d2",
"index": 1,
"name": "抽奖奖品",
"prizeId": "sp_1",
"prizeType": 1,
"refId": null,
"refType": null,
"sendCount": 1
}
],
"prizePendingCode": null,
"prizePendingCodeList": [
{
"index": 1,
"code": "awefrgdrtyhgr566y4e564235"
}
],
"ruleId": "ru_1",
"sendPrize": null,
},
{
"title": "每日签到",
"subTitle": "游戏次数+1",
"taskStatus": 2, // 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限
"buttonText": "1",
"code": "common_sign",
"completedSize": 0,
"desc": "1",
"extra": null,
"icon": "//yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg",
"id": "task1",
"index": null,
"intervalLimitSize": 4,
"intervalType": 1,
"jumpUrl": "https://projectx-console.duiba.com.cn/#/development",
"options": [
{
"degree": null,
"icon": null,
"icon2": null,
"id": "o79a0b9d2",
"index": 1,
"name": "抽奖奖品",
"prizeId": "sp_1",
"prizeType": 1,
"refId": null,
"refType": null,
"sendCount": 1
}
],
"prizePendingCode": null,
"prizePendingCodeList": [
{
"index": 1,
"code": "awefrgdrtyhgr566y4e564235"
}
],
"ruleId": "ru_1",
"sendPrize": null,
},
{
"title": "1v1留资查询",
"subTitle": "游戏次数+65",
"taskStatus": 0, // 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限
"buttonText": "1",
"code": "third_retain",
"completedSize": 2,
"desc": "1",
"extra": null,
"icon": "//yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg",
"id": "task3",
"index": null,
"intervalLimitSize": 3,
"intervalType": 1,
"jumpUrl": "1",
"options": [
{
"degree": null,
"icon": null,
"icon2": null,
"id": "o79a0b9d2",
"index": 1,
"name": "抽奖奖品",
"prizeId": "sp_1",
"prizeType": 1,
"refId": null,
"refType": null,
"sendCount": 1
}
],
"prizePendingCode": null,
"prizePendingCodeList": [
{
"index": 1,
"code": "awefrgdrtyhgr566y4e564235"
}
],
"ruleId": "ru_1",
"sendPrize": null,
},
{
"title": "啦啦啦啦啦",
"subTitle": "游戏次数+1111",
"taskStatus": 1, // 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限
"buttonText": "1",
"code": "followAndBind",
"completedSize": 10,
"desc": "1",
"extra": null,
"icon": "//yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg",
"id": "task2",
"index": null,
"intervalLimitSize": 10,
"intervalType": 1,
"jumpUrl": "https://www.baidu.com",
"options": [
{
"degree": null,
"icon": null,
"icon2": null,
"id": "o79a0b9d2",
"index": 1,
"name": "抽奖奖品",
"prizeId": "sp_1",
"prizeType": 1,
"refId": null,
"refType": null,
"sendCount": 1
}
],
"prizePendingCode": null,
"prizePendingCodeList": [
{
"index": 1,
"code": "awefrgdrtyhgr566y4e564235"
}
],
"ruleId": "ru_1",
"sendPrize": null,
},
],
"startTimestamp": 1619418589000,
"timestamp": 1619590450914
}
},
"POST /task_1/doCompleted.do": {
"code": null,
"data": {
"buttonText": "1",
"code": "1",
"desc": "1",
"extra": null,
"icon": null,
"id": "3b8mxhjo",
"options": [
{
"extra": null,
"optionId": "o693fc73e",
"optionImg": null,
"optionName": "游戏x1",
"position": null,
"prizeId": "sp_1",
"prizeType": 1,
"ruleId": "ru_1",
"sendCount": 55,
"url": "null24155",
"userRecordId": 24155
}
],
"prizePendingCode": null,
"sendPrize": true,
"timestamp": 1619146587178,
"title": "1"
},
"message": null,
"success": true
},
"POST /task_1/sendPrize.do": {
"code": null,
"data": {
"extra": null,
"options": [
{
"optionId": "o693fc73e",
"optionImg": null,
"optionName": "游戏x1",
"position": null,
"prizeId": "sp_1",
"sendCount": 15345,
"prizeType": 1,
"ruleId": "ru_1",
"url": "null18519"
}
]
},
"message": null,
"success": true
}
}
module.exports = {
"GET /home/drawIndex.do": {
"success": true,
"message": "报错了~",
"code": null,
timeStamp: Date.now(),
"data": {
"startTime": +new Date() - 123, //活动开始时间戳
"endTime": +new Date() + 123, //活动结束时间戳
"remainDrawTimes": 1, // 抽奖次数
"actPrizeList": [
{
prizeName: `奖品名称奖品名称奖品名称奖品名称1`,
prizeImg: `//yun.duiba.com.cn/polaris/jinmoqiang.9e5bbc004f69ab6a3f34d93daeda028616a9f00d.png`,
prizeId: `thanks1`,
},
{
prizeName: `奖品名称奖品名称`,
prizeImg: `//yun.duiba.com.cn/polaris/kouzhao.dc55dac6db1ff94c4666c2602d6cb0ac84139982.png`,
prizeId: `thanks2`,
},
{
prizeName: `奖品名称奖品`,
prizeImg: `//yun.duiba.com.cn/polaris/mac.fe55aa43e5a231d568973dcdd0b91e0263d5a12c.png`,
prizeId: `thanks3`,
},
{
prizeName: `奖品名称4`,
prizeImg: `//yun.duiba.com.cn/polaris/mgtv.e5f0709a2a4083bc57ce52ff200a50a2e56b2658.png`,
prizeId: `thanks4`,
},
{
prizeName: `奖品名称5`,
prizeImg: `//yun.duiba.com.cn/polaris/qq.73ad13cd0c6fd705327d12957c12e9d5b2e77baa.png`,
prizeId: `thanks5`,
},
{
prizeName: `奖品名称6`,
prizeImg: `//yun.duiba.com.cn/polaris/xx.e2f6a49a2954673d16d956d7b3bba25c2b6c667e.png`,
prizeId: `thanks6`,
},
{
prizeName: `奖品名称7`,
prizeImg: `//yun.duiba.com.cn/polaris/xx.e2f6a49a2954673d16d956d7b3bba25c2b6c667e.png`,
prizeId: `thanks7`,
},
{
prizeName: `谢谢参与lll`,
prizeImg: `https://yun.duiba.com.cn/spark/assets/f77861647e7b55e9c95e9c49d891a21526157a76.jpg`,
prizeId: `thanks`,
},
]
}
},
"GET /home/doDraw.do": {
"success": true,
"message": "报错了~",
"code": null,
"data": {
prizeId: `thanks1`, // 奖品id thanks
prizeName: `奖品名称6gggggggggggggggggggvvvbbbbhsgxhsghshxbhsxbhs`,
prizeImg: `//yun.duiba.com.cn/polaris/xx.e2f6a49a2954673d16d956d7b3bba25c2b6c667e.png`,
},
},
}
\ No newline at end of file
{
"name": "temp_base",
"version": "3.6.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/circle-turntable": "^1.0.3",
"@spark/dbdomain": "^1.0.25",
"@spark/share": "^2.0.340",
"@spark/svgaplayer": "^2.0.5",
"@spark/ui": "^2.0.8",
"@spark/utils": "^2.0.17",
"classnames": "^2.5.1",
"crypto-js": "^4.2.0",
"duiba-utils": "^1.0.2",
"history": "^4.10.1",
"javascript-obfuscator": "^4.1.0",
"mobx": "^6.2.0",
"mobx-react": "^7.1.0",
"qs": "^6.9.4",
"react": "^16.4.1",
"react-dom": "^16.4.1",
"sa-sdk-javascript": "^1.27.4",
"spark-utils": "^0.0.12",
"terser-webpack-plugin": "4.2.3",
"video.js": "^8.22.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.11",
"@types/crypto-js": "^4.2.2",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.2.2",
"chalk": "^4.1.0",
"clean-webpack-plugin": "^3.0.0",
"commander": "^11.1.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",
"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.6.2",
"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"
}
}
{"proSetting":{"projectxIDs":{"testId":[{"label":"测试","value":"pa1ce2110"}],"prodId":[{"label":"线上测试","value":"p8d9bf93b"},{"label":"线上正式","value":"peb5290b6"}]},"skinVariables":[],"mockSetting":{"projectId":"","pageId":""}},"envSetting":{},"psdSetting":{"psdFSSetting":true,"psdCenterSetting":true}}
<!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 src="//res.wx.qq.com/open/js/jweixin-1.6.0.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}';
CFG.activityName = "官微-25端午节活动";
CFG.channel = getUrlParam("channel") || sessionStorage.getItem("channel")
CFG.empno = getUrlParam('empno') || sessionStorage.getItem("empno")
CFG.empname = getUrlParam('empname') || sessionStorage.getItem("empname")
CFG.empno && sessionStorage.setItem("empno", CFG.empno)
CFG.empname && sessionStorage.setItem("empname", CFG.empname)
CFG.channel && sessionStorage.setItem("channel", CFG.channel)
if (location.href.indexOf("customShare") > 0) {
CFG.appID = "93842";
if (location.href.indexOf("Did1NjA4NjM") > 0) { // 线上测试免登中间页
CFG.projectId = "p8d9bf93b"; // 线上测试
} else {
CFG.projectId = "peb5290b6"; // 线上正式
}
}
// 动态域名
CFG.domain = window.location.origin;
// 首页 app.jsx中会拼动态域名
CFG.index = '/projectx/' + CFG.projectId + '/index.html?appID=' + CFG.appID;
// 我的奖品页
CFG.prize = 'myPrize.html?appID=' + CFG.appID + '&channel=' + getUrlParam("channel") + '&fromPage=home';
// 免登的地址
CFG.middleRequest = "https://93842-activity.dexfu.cn//customActivity/taibao/login?";
// 正式要改
CFG.middleTHRequest = "https://93842-activity.dexfu.cn//customActivity/taibao/thd/login?";
// 正式 奖品页面跳转链接
CFG.prizeJumpUrl = "https://wx.cpic.com.cn/cpiccustomerclub/userInformation/index.html";
// 生产
CFG.sensorUrl = 'https://xnjkfx.cpic.com.cn:8006/sa?project=SXGW';
// 环境,test测试,prod生产 TODO
CFG.env = 'prod';
CFG.environment = '';
//关注公众号二维码-正式
CFG.codeUrl = 'https://yun.duiba.com.cn/polaris/guanzhuCode.a398f4a2e47acb9265b0022adf36c9355921d975.png'
// 官微绑定页面地址 生产环境
CFG.bindUrl = 'https://wx.cpic.com.cn/sxwxclub/wx/menuByState?state=21039&source_channel=24NEMT'
if (CFG.env == 'test') {
// 测试
CFG.environment = 'test';
CFG.miniEnv = "trial";
CFG.sensorUrl = 'https://xnjkfxsit.cpic.com.cn/sa?project=SXGW';
//关注公众号二维码-测试
CFG.codeUrl = "https://yun.duiba.com.cn/polaris/moon-test.8a5c54742e74780a76e7809255016cf522441232.png";
// 测试 奖品页面跳转链接
CFG.prizeJumpUrl = "https://wxtest.cpic.com.cn/cpiccustomerclub/userInformation/index.html";
// 官微绑定页面地址 测试环境
CFG.bindUrl = "https://wxtest.cpic.com.cn/sxwxclub/wx/menuByState?state=21039&source_channel=24NEMT";
}
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":"太保-端午节-20250429","proDesc":"","proPath":"/Users/yujiating/Documents/programme/太保-端午节-20250429","createTime":1745912031535}
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/1747634221363",
"JS_PATH_PROD": "https://yun.duiba.com.cn/spark/v2/temp_base/1748589458226/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 API from "@src/api";
import {
setCookieId
} from "@src/api/utils";
/**
* 普通跳转客服
* 注意:小程序需要额外增加业务域名 https://duiba.qiyukf.com/
*/
export function jumpService() {
location.href = location.origin + '/faq/index'
}
export function appJump(url, hideNavbar = false, data = {}) {
API.tempSaveCookie().then((res) => {
if (res?.data) {
localStorage.setItem("db_temp_cookie", res.data);
// setCookieId(res.data);
}
}).finally(() => {
// app对应跳转方法
window.MarsJSBridge.invoke("pushPage", {
uri: url,
data,
pageParams: {
hideNavbar,
}
});
});
}
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
},
// cookie丢失-临时保存cookie
tempSaveCookie: {
uri: "/autoLogin/tempSaveCookie",
showMsg: false,
},
// cookie丢失-重新设置cookie
resetCookie: "/autoLogin/resetCookie",
userLogin: {
uri: "userLogin.check",
showMsg: false,
},
buriedPoint: {
uri: "home/buriedPoint.do",
showMsg: false,
},
home: 'home/index.do',
startAnswer: {
uri: 'home/startAnswer.do',
withToken: true, // 携带token
},
submitAnswer: {
uri: 'home/submitAnswer.do',
withToken: true, // 携带token
},
/** 抽奖首页 */
drawIndex: "home/drawIndex.do",
/** 抽奖 */
drawJoin: {
uri: "home/doDraw.do",
withToken: true,
},
/** 查询用户信息(神策埋点用) */
queryUserInfo: "common/queryUserInfo.do",
/** 查询任务列表 */
queryTasks: "task_1/queryTasks.do",
/** 完成任务 */
doCompleted: {
method: "post",
uri: "task_1/doCompleted.do",
withToken: true,
},
/** 任务发奖 */
sendPrize: {
method: "post",
uri: "task_1/sendPrize.do",
withToken: true,
},
/** 奖品页信息 */
getPrizePageInfo: "common/prizeRecord.do",
createCode: "/customActivity/taibao/custom/createCode",
thdLoginNew: {
uri: '/customActivity/taibao/thd/authLogin'
},
tbLoginNew: {
uri: 'https://customer-link.duiba.com.cn/customer/93842/taibao/login'
},
})
// 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 { callApi } from '@spark/api-base'
import { Loading, Toast } from '@spark/ui'
import { isFromShare, newUser } from 'duiba-utils';
import { errorHandler } from "@src/utils/errorHandler";
import API from "@src/api/index";
import { getPxToken } from "@src/built-in/getPxToken";
const mergeData = {
user_type: newUser ? '0' : '1',
is_from_share: isFromShare ? '0' : '1',
channel: CFG.channel || '',
from: CFG.channel || ''
}
// let tempCookieId = "";
//
// export function setCookieId(cookieId) {
// tempCookieId = cookieId;
// }
export function resetBackCookie(duibaTempCookieId) {
return new Promise((resolve) => {
callApi("/autoLogin/resetCookie", {
duibaTempCookieId
}).then((resp) => {
return resolve('success');
}, (e) => {
return resolve(e);
});
});
}
/**
* 请求方法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) => {
// cookie丢失的问题
// 如遇跳转Cookie丢失,打开如下代码
// const duibaTempCookieId = localStorage.getItem("db_temp_cookie");
// // const duibaTempCookieId = tempCookieId;
//
// if (duibaTempCookieId) {
// localStorage.removeItem("db_temp_cookie");
// // tempCookieId = "";
//
// const res = await API.userLogin()
// .catch(async () => {
// await resetBackCookie(duibaTempCookieId);
// });
//
// if (!res || !res.success) {
// await resetBackCookie(duibaTempCookieId);
// }
// }
// 根据接口配置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 Homepage from "./pages/homepage/homepage";
import { PAGE_MAP } from "./utils/constants";
import { ensureDomain, domain } from '@spark/dbdomain';
import Answerpage from "./pages/answerpage/answerpage";
import Prizepage from "./pages/prizepage/prizepage";
import Drawpage from "./pages/drawpage/drawpage";
import Resultpage from "./pages/resultpage/resultpage";
import MiddlePageNew from "@src/pages/middlePageNew/middlePageNew";
import { loadOneFont } from "./utils/preload1.3";
/**
* 所有页面场景
*/
const pageMap = {
[PAGE_MAP.LOADING_PAGE]: <LoadingDemo />,
[PAGE_MAP.HOME_PAGE]: <Homepage />,
[PAGE_MAP.QUES_PAGE]: <Answerpage />,
[PAGE_MAP.PRIZE_PAGE]: <Prizepage />,
[PAGE_MAP.DRAW_PAGE]: <Drawpage />,
[PAGE_MAP.RESU_PAGE]: <Resultpage />,
[PAGE_MAP.MID_PAGE]: <MiddlePageNew />,
};
@observer
class App extends Component {
async componentDidMount() {
// 获取前端开发配置,依据项目需要,酌情添加 !!!
if (store.curPage !== PAGE_MAP.MID_PAGE) {
await store.getFrontVariable();
// 获取动态域名
await ensureDomain();
CFG.domain = domain || window.location.origin;
CFG.index = CFG.domain + CFG.index;
}
if (store.curPage === PAGE_MAP.HOME_PAGE) {
this.loadFonts();
}
}
/**
* 加载特殊字体包
*/
loadFonts = async() => {
await loadOneFont(
"//yun.duiba.com.cn/polaris/Alimama_DongFangDaKai_Regular.48e78d785f0db9f0e1de9defb6a5994b76bfc188.ttf",
"Alimama_DongFangDaKai_Regular"
);
};
render() {
const { curPage, pageData } = store;
return (
<>
{{ ...pageMap[curPage], props: { ...pageData } }}
<Modal />
</>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
* {
margin: 0;
padding: 0;
-webkit-touch-callout: none; /*系统默认菜单被禁用*/
-khtml-user-select: none; /*早期浏览器*/
-moz-user-select: none; /*火狐*/
-ms-user-select: none; /*IE10*/
-webkit-tap-highlight-color: transparent;
user-select: text;
-webkit-user-select: text;
}
*:not(input,textarea) {
-webkit-touch-callout: none;
-webkit-user-select: none;
}
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;
overflow: hidden;
}
.modal_center {
left: 0 !important;
top: 0 !important;
bottom: 0 !important;
right: 0 !important;
margin: auto;
}
#root {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.com_Container {
width: 100%;
height: 100%;
position: relative;
overflow-x: hidden;
overflow-y: auto;
}
{"preLoadImg":[],"asyncLoadImg":["answerPage/bg1.png","answerPage/bg2.png","answerPage/bg3.png","answerPage/bg4.png","answerPage/bg5.png","answerPage/content_bg.png","answerPage/option_bg.png","answerPage/option_bg_select.png","answerPage/progress1.png","answerPage/progress2.png","answerPage/progress3.png","answerPage/progress4.png","answerPage/progress5.png","answerPage/progress_bg.png","answerPage/progress_title.png","answerPage/queslast.png","answerPage/quesnext.png","answerPage/submitbtn.png","bindPop/bg.png","bindPop/bind_btn.png","common/back.png","common/close.png","drawChancePop/bg.png","drawChancePop/btn.png","drawFailPop/bg.png","drawFailPop/btn.png","drawPage/bg.png","drawPage/cloud.png","drawPage/draw_btn.png","drawPage/pointer.png","drawPage/prize_btn.png","drawPage/rule_btn.png","drawPage/subtitle_bg.png","drawPage/task_btn.png","drawPage/title.png","drawPage/turntable_bg.png","drawPage/turntable_box.png","drawSucPop/bg.png","drawSucPop/happy_btn.png","drawSucPop/leaves.png","drawSucPop/light.png","drawSucPop/prize_img.png","exitPop/bg.png","exitPop/cancel_btn.png","exitPop/confirm_btn.png","focusPop/bg.png","focusPop/qrcode_bg.png","homePage/bg.png","homePage/content.png","homePage/draw_btn.png","homePage/main_btn.png","homePage/ptize_btn.png","homePage/rule_btn.png","homePage/share_btn.png","homePage/subtitle_bg.png","homePage/task_btn.png","homePage/title.png","LoadingPage/loadingBg.jpg","LoadingPage/loadingFill.png","LoadingPage/loadingIp.png","mid/bg.jpg","prizePage/bg.png","prizePage/prize_btn.png","prizePage/prize_img.png","prizePage/prize_item_bg.png","prizePage/title.png","resultPage/bg.png","resultPage/code_bg.png","resultPage/code_img.png","resultPage/draw_btn.png","resultPage/product_btn.png","resultPage/share_btn.png","resultPage/传统守护者.png","resultPage/佛系随性派.png","resultPage/元气美食家.png","resultPage/狂热尝新党.png","resultPage/社交组局王.png","rulePop/bg.png","svga/choose.svga","svga/home.svga","svga/pop.svga","taskPop/bg.png","taskPop/grey_bg.png","taskPop/red_bg.png","taskPop/task_icon.png","taskPop/task_item_bg.png","taskPop/yellow_bg.png"]}
\ No newline at end of file
export class CodeError {
name = 'CodeError';
code = '';
message = '';
stack;
payload;
constructor(input, message) {
if (typeof input === 'number' || typeof input === 'string') {
this.code = input + '';
} else if (typeof input === 'object') {
if (input['code']) {
this.code = input['code'];
} else {
if (input instanceof Error) {
this.code = input['message'];
} else {
console.warn('input without code field:', JSON.stringify(input));
}
}
if (input['message']) {
this.message = input['message'];
}
if (input['payload']) {
this.payload = input['payload'];
}
}
if (message) {
this.message = message;
}
this.stack = (new Error()).stack;
if (!Error['captureStackTrace'])
this.stack = (new Error()).stack;
else
Error['captureStackTrace'](this, this.constructor);
}
}
export const Errors = {
UNKNOWN: 200001, // 未知异常
NET_ERROR: 210001, // 网络异常
// REQUEST_TIMEOUT, // 请求超时
// POLLING_TIMEOUT, // 轮询超时
// INVALID_RESPONSE, // 无效的响应体
// CALL_LIMITING, // 接口限流
GET_PX_TOKEN_FAILED: 220001, // 获取星速台token失败
};
import { callApi, jsonp } from '@spark/api-base';
import { evalJsScript } from '@spark/utils';
import { CodeError } from "./common-helpers/CodeError";
import { Errors as ERRORS } from "./common-helpers/errors";
const isProd = location.href.indexOf('.com.cn/projectx') >= 0;
let pxTokenKeyValid = false;
let getting = false;
const _queue = [];
/**
* 获取token
* @ctype PROCESS
* @description 获取星速台防刷token
* @returns
* token string token
* @example 一般用法
* const token = await getPxToken().catch(e=>{
* console.log('获取失败,失败原因:', e.message)
* })
* if(token){
* console.log('获取成功,token:', token)
* }
*/
export async function getPxToken() {
if (!isProd) {
console.log('[Mock] getPxToken');
return 'test_token';
}
return new Promise((resolve, reject) => {
_queue.push({resolve, reject});
setTimeout(_getPxToken, 10);
});
}
async function _getPxToken() {
if (getting) {
return;
}
if (_queue.length > 0) {
const p = _queue.shift();
try {
getting = true;
const token = await _tryGetPxToken();
p.resolve(token);
getting = false;
setTimeout(_getPxToken, 10);
} catch (e) {
getting = false;
p.reject(e);
}
}
}
async function _tryGetPxToken() {
let token, err;
for (let i = 0; i < 2; i++) {
try {
token = await _doGetPxToken();
break;
} catch (e) {
err = e;
invalidPxTokenKey();
}
}
if (token) {
return token;
} else {
throw err;
}
}
async function _doGetPxToken() {
if (!pxTokenKeyValid) {
await refreshPxTokenKey();
}
return getToken();
}
/**
* 让tokenKey失效
* @ctype PROCESS
* @description 比如活动页跳转到其他星速台页面,回来的时候就需要监听history然后让tokenKey失效
* @example 一般用法
* invalidPxTokenKey()
*/
export function invalidPxTokenKey() {
pxTokenKeyValid = false;
}
async function refreshPxTokenKey() {
if (!isProd) {
pxTokenKeyValid = true;
console.log('[Mock] refreshPxTokenKey');
return;
}
pxTokenKeyValid = false;
await jsonp('getTokenKey?_t=' + Date.now());
pxTokenKeyValid = true;
}
async function getToken() {
const resp = await callApi('getToken', undefined, undefined, undefined, false);
if (resp.success) {
evalJsScript(resp.data);
const token = window['ohjaiohdf']();
if (token) {
return token;
}
throw new CodeError(ERRORS.GET_PX_TOKEN_FAILED, '获取token失败,请查明key是否被覆盖');
} else {
throw new CodeError(resp);
}
}
/**
* index.jsx
* Created by 还有醋v on 2022/10/10 下午3:03.
* Copyright © 2022 haiyoucuv. All rights reserved.
*/
import React, { useState } from "react";
/**
* @param children
* @param className
* @param onClick
* @param style
* @return {React.DetailedReactHTMLElement<{onTouchStart: onTouchStart, onClick: (function(): any), onTouchCancel: onTouchEnd, className, style: {transform: string, transitionDuration: number}, onTouchEnd: onTouchEnd}, HTMLElement>}
* @constructor
*/
export const Button = ({ children, className, onClick = () => void 0, style = {} }) => {
const [scale, setScale] = useState("unset");
const onTouchStart = () => {
setScale("scale(0.9,0.9)");
};
const onTouchEnd = () => {
setScale("unset");
};
const onTouchCancel = onTouchEnd;
return React.createElement("div", {
className, onTouchStart, onTouchEnd, onTouchCancel, onClick, style: {
transitionDuration: 0.5, transform: scale, ...style
}
}, children);
};
import React from 'react';
import videojs from 'video.js';
import './VideoPlayer.less'
export default class VideoPlayer extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.player = videojs(this.videoNode, this.props, () => {
videojs.log('onPlayerReady', this);
this.props.videoRefCb(this.player)
});
}
// Dispose the player when the component will unmount
componentWillUnmount() {
if (this.player) {
this.player.dispose();
}
}
render() {
return (
<div data-vjs-player style={this.props.style}>
<video
ref={node => this.videoNode = node}
className="video-js"
></video>
</div>
);
}
}
@import "~video.js/dist/video-js.css";
@import "../../res.less";
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './bindpop.less';
import { _throttle, windowJumpUrl } from '@src/utils/utils';
import modalStore from '@src/store/modal';
import { Button } from '../Button';
@observer
class Bindpop extends React.Component {
constructor(props) {
super(props);
}
/** 关闭弹窗 */
closeDialog = _throttle(() => {
modalStore.closePop();
})
/** 去绑定 */
toBind = _throttle(() => {
modalStore.closePop();
windowJumpUrl(CFG.bindUrl);
})
render() {
return (
<div className="bindpop modal_center">
<span className="bg"></span>
<Button className="bind_btn" onClick={this.toBind}></Button>
<Button className="close" onClick={this.closeDialog}></Button>
</div>
);
}
}
export default Bindpop;
@import "../../res.less";
.bindpop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
position: absolute;
left: 27px;
top: 518px;
width: 671px;
height: 486px;
.sparkBg("bindPop/bg.png");
}
.bind_btn {
position: absolute;
left: 212px;
top: 855px;
width: 328px;
height: 93px;
.sparkBg("bindPop/bind_btn.png");
}
.close {
position: absolute;
left: 345px;
top: 1053px;
width: 59px;
height: 61px;
.sparkBg("common/close.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './drawchancepop.less';
import modalStore from '@src/store/modal';
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { Button } from '../Button';
@observer
class Drawchancepop extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
sensorMdExpouse("PopupExpouse", { pageName: "提示获取抽奖机会弹窗" });
}
handleClick = () => {
sensorMdClick("PopupClick", { pageName: "提示获取抽奖机会弹窗点击", buttonName: "去做任务" });
modalStore.closePop("Drawchancepop");
modalStore.pushPop("Taskpop", { refresh: this.props.popData.refresh })
}
render() {
return (
<div className="drawchancepop modal_center">
<span className="bg"></span>
<span className="text">完成任务可获得抽奖机会哦~</span>
<Button className="btn md12" onClick={this.handleClick}></Button>
</div>
);
}
}
export default Drawchancepop;
@import "../../res.less";
.drawchancepop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 671px;
height: 486px;
left: 27px;
top: 568px;
position: absolute;
.sparkBg("drawChancePop/bg.png");
}
.text {
width: 543px;
height: 31px;
left: 110px;
top: 796px;
position: absolute;
font-size: 32px;
line-height: 31px;
color: rgba(33, 55, 9, 1);
text-align: center;
}
.btn {
width: 328px;
height: 93px;
left: 212px;
top: 890px;
position: absolute;
.sparkBg("drawChancePop/btn.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './drawfailpop.less';
import { _throttle } from '@src/utils/utils';
import modalStore from '@src/store/modal';
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { Button } from '../Button';
@observer
class Drawfailpop extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
sensorMdExpouse("PopupExpouse", { pageName: "未中奖弹窗" });
}
knowHandle = _throttle(() => {
sensorMdClick("PopupClick", { pageName: "未中奖弹窗点击", buttonName: "我知道啦" });
modalStore.closePop();
});
render() {
return (
<div className="drawfailpop modal_center">
<span className="bg"></span>
<span className="text">别灰心,明天再试试吧~</span>
<Button className="btn md13" onClick={this.knowHandle}></Button>
</div>
);
}
}
export default Drawfailpop;
@import "../../res.less";
.drawfailpop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 671px;
height: 486px;
left: 27px;
top: 568px;
position: absolute;
.sparkBg("drawFailPop/bg.png");
}
.text {
width: 436px;
height: 31px;
left: 163px;
top: 797px;
position: absolute;
font-size: 32px;
line-height: 31px;
color: rgba(33, 55, 9, 1);
text-align: center;
}
.btn {
width: 328px;
height: 93px;
left: 212px;
top: 890px;
position: absolute;
.sparkBg("drawFailPop/btn.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './drawsucpop.less';
import store from '@src/store';
import { PAGE_MAP } from '@src/utils/constants';
import { Button } from '../Button';
import { _throttle } from '@src/utils/utils';
import modalStore from '@src/store/modal';
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { SvgaPlayer } from '@spark/svgaplayer';
import { RES_PATH } from '../../../sparkrc';
@observer
class Drawsucpop extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
sensorMdExpouse("PopupExpouse", { pageName: "中奖弹窗", prize: this.props?.popData?.prizeName });
}
/** 进入【奖品】页 */
jumpPrize = _throttle(() => {
sensorMdClick("PopupClick", { pageName: "中奖弹窗点击", buttonName: "开心收下", prize: this.props?.popData?.prizeName });
modalStore.closePop();
location.href = CFG.prize;
})
render() {
const { prizeName, prizeImg } = this.props.popData || {}
return (
<div className="drawsucpop modal_center">
<SvgaPlayer className="light" src={RES_PATH + "svga/pop.svga"} />
<span className="leaves"></span>
<span className="bg"></span>
<img className="prize_img" src={prizeImg} />
<span className="prize_name">{prizeName}</span>
<Button className="happy_btn md11" onClick={this.jumpPrize}></Button>
<span className="tips">奖品可在首页 我的奖品 中查看</span>
</div>
);
}
}
export default Drawsucpop;
@import "../../res.less";
.drawsucpop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.light {
width: 750px;
height: 1085px;
left: 0px;
top: 300px;
position: absolute;
// .sparkBg("drawSucPop/light.png");
}
.leaves {
width: 671px;
height: 604px;
left: 27px;
top: 496px;
position: absolute;
.sparkBg("drawSucPop/leaves.png");
}
.bg {
width: 514px;
height: 659px;
left: 118px;
top: 478px;
position: absolute;
.sparkBg("drawSucPop/bg.png");
}
.prize_img {
width: 212px;
height: 212px;
left: 270px;
top: 674px;
position: absolute;
// .sparkBg("drawSucPop/prize_img.png");
border-radius: 20px;
}
.prize_name {
width: 426px;
height: 29px;
left: 168px;
top: 902px;
position: absolute;
font-size: 28px;
line-height: 29px;
color: rgba(33, 55, 9, 1);
text-align: center;
.lineClamp1();
}
.happy_btn {
width: 327px;
height: 93px;
left: 212px;
top: 962px;
position: absolute;
.sparkBg("drawSucPop/happy_btn.png");
}
.tips {
width: 342px;
height: 20px;
left: 209px;
top: 1064px;
position: absolute;
font-size: 20px;
line-height: 20px;
color: rgba(118, 138, 104, 1);
text-align: center;
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './exitPop.less';
import modalStore from '@src/store/modal';
import { _throttle } from '@src/utils/utils';
import { Button } from '../Button';
import store from '@src/store';
import { PAGE_MAP } from '@src/utils/constants';
@observer
class ExitPop extends React.Component {
constructor(props) {
super(props);
}
/** 确认退出 */
exitHandle = () => {
modalStore.closePop();
store.changePage(PAGE_MAP.HOME_PAGE)
}
/** 关闭 */
closeHandle = () => {
modalStore.closePop();
}
render() {
return (
<div className="exitPop modal_center">
<span className="bg"></span>
<span className="text">退出后将结束本次测试,<br />参与机会不作返还</span>
<Button className="cancel_btn" onClick={this.closeHandle}></Button>
<Button className="confirm_btn" onClick={this.exitHandle}></Button>
{/* <Button className="close" onClick={this.closeHandle}></Button> */}
</div>
);
}
}
export default ExitPop;
@import "../../res.less";
.exitPop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
position: absolute;
left: 27px;
top: 568px;
width: 671px;
height: 486px;
.sparkBg("exitPop/bg.png");
}
.text {
font-size: 32px;
font-family: "SourceHanSansCN";
color: rgb(33, 55, 9);
text-align: center;
position: absolute;
width: 590px;
left: 85px;
top: 773px;
}
.cancel_btn {
position: absolute;
left: 158px;
top: 908px;
width: 203px;
height: 58px;
.sparkBg("exitPop/cancel_btn.png");
}
.confirm_btn {
position: absolute;
left: 381px;
top: 908px;
width: 203px;
height: 58px;
.sparkBg("exitPop/confirm_btn.png");
}
.close {
position: absolute;
left: 345px;
top: 935px;
width: 59px;
height: 61px;
.sparkBg("common/close.png");
}
}
import React from 'react';
import { observer } from 'mobx-react';
import './focuspop.less';
import { _throttle } from '@src/utils/utils';
import modalStore from '@src/store/modal';
import { Button } from '../Button';
@observer
class Focuspop extends React.Component {
constructor(props) {
super(props);
}
/** 关闭弹窗 */
closeDialog = _throttle(() => {
modalStore.closePop();
})
render() {
return (
<div className="focuspop modal_center">
<span className="bg"></span>
<img className="qrcode_bg" src={CFG.codeUrl} />
<Button className="close" onClick={this.closeDialog}></Button>
</div>
);
}
}
export default Focuspop;
@import "../../res.less";
.focuspop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
position: absolute;
left: 27px;
top: 428px;
width: 671px;
height: 638px;
.sparkBg("focusPop/bg.png");
}
.qrcode_bg {
border-radius: 26px;
background-color: rgb(74, 115, 31);
position: absolute;
left: 246px;
top: 615px;
width: 258px;
height: 258px;
box-sizing: border-box;
}
.close {
position: absolute;
left: 345px;
top: 1113px;
width: 59px;
height: 61px;
.sparkBg("common/close.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './longImgPop.less';
import modalStore from '@src/store/modal';
import { _throttle } from '@src/utils/utils';
import { Button } from '../Button';
@observer
class LongImgPop extends React.Component {
constructor(props) {
super(props);
this.state = {
isFinish: false,
countdownTime: 5, // 单位s
}
this.timer = ''
}
componentDidMount() {
this.startInterval()
}
componentWillUnmount() {
if (this.timer) clearInterval(this.timer)
}
/** 开始倒计时 */
startInterval = () => {
if (this.timer) clearInterval(this.timer)
this.timer = setInterval(async () => {
if (this.state.countdownTime === 0) {
this.setState({
isFinish: true
})
clearInterval(this.timer)
this.timer = ''
// 完成任务
const { taskSucCb } = this.props?.popData || {}
taskSucCb && taskSucCb()
return
}
this.setState({
countdownTime: this.state.countdownTime - 1,
})
}, 1000);
}
/** 关闭当前页面 */
closeHandle = () => {
modalStore.closePop();
}
render() {
const { imgUrl } = this.props.popData || {}
const { isFinish, countdownTime } = this.state
return (
<div className="longImgPop">
<img src={imgUrl} alt="" className="mainImg" />
<Button className="back_home" onClick={this.closeHandle}></Button>
{!isFinish && <span className='countdown'>{`${countdownTime}s`}</span>}
</div>
);
}
}
export default LongImgPop;
@import "../../res.less";
.longImgPop {
position: absolute;
width: 100vw;
height: 100vh;
left: 0px;
top: 0px;
overflow-y: auto;
overflow-x: hidden;
background-color: #fff;
.mainImg {
position: relative;
width: 100%;
height: auto;
display: block;
}
.back_home {
position: fixed;
left: 12px;
top: 109px;
width: 64px;
height: 64px;
.sparkBg("common/back.png");
}
.countdown {
width: 100px;
height: 63px;
text-align: center;
position: fixed;
left: 0;
top: 244px;
color: #fff;
border: 2px solid rgb(255, 255, 255);
border-left: none;
border-radius: 0 21px 21px 0;
background-color: rgba(70, 0, 0, 0.302);
box-shadow: inset 0.707px 0.707px 2px 0px rgba(225, 253, 255, 0.83);
font-size: 37px;
text-align: center;
line-height: 63px;
box-sizing: border-box;
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './longImgPop2.less';
import modalStore from '@src/store/modal';
import { _throttle } from '@src/utils/utils';
import { Button } from '../Button';
@observer
class LongImgPop2 extends React.Component {
constructor(props) {
super(props);
}
/** 关闭当前页面 */
closeHandle = () => {
modalStore.closePop();
}
render() {
const { imgUrl } = this.props.popData || {}
return (
<div className="longImgPop2">
<img src={imgUrl} alt="" className="mainImg" />
<Button className="back_home" onClick={this.closeHandle}></Button>
</div>
);
}
}
export default LongImgPop2;
@import "../../res.less";
.longImgPop2 {
position: absolute;
width: 100vw;
height: 100vh;
left: 0px;
top: 0px;
overflow-y: auto;
overflow-x: hidden;
background-color: #fff;
.mainImg {
position: relative;
width: 100%;
height: auto;
display: block;
}
.back_home {
position: fixed;
left: 12px;
top: 109px;
width: 64px;
height: 64px;
.sparkBg("common/back.png");
}
.countdown {
width: 100px;
height: 63px;
text-align: center;
position: fixed;
left: 0;
top: 244px;
color: #fff;
border: 2px solid rgb(255, 255, 255);
border-left: none;
border-radius: 0 21px 21px 0;
background-color: rgba(70, 0, 0, 0.302);
box-shadow: inset 0.707px 0.707px 2px 0px rgba(225, 253, 255, 0.83);
font-size: 37px;
text-align: center;
line-height: 63px;
box-sizing: border-box;
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './rulepop.less';
import store from '@src/store';
import { Button } from '../Button';
import modalStore from '@src/store/modal';
import { _throttle } from '@src/utils/utils';
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
@observer
class Rulepop extends React.Component {
constructor(props) {
super(props);
}
async componentDidMount() {
sensorMdExpouse("PopupExpouse", { pageName: "活动规则弹窗" });
await store.initRule();
}
closeHandle = _throttle(() => {
sensorMdClick("PopupClick", { pageName: "活动规则弹窗点击", buttonName: "关闭" });
modalStore.closePop();
});
render() {
return (
<div className="rulepop modal_center">
<span className="bg"></span>
<div className="content" dangerouslySetInnerHTML={{ __html: store?.ruleInfo }}></div>
<Button className="close" onClick={this.closeHandle}></Button>
</div>
);
}
}
export default Rulepop;
@import "../../res.less";
.rulepop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 671px;
height: 860px;
left: 27px;
top: 306px;
position: absolute;
.sparkBg("rulePop/bg.png");
}
.content {
width: 417px;
height: 600px;
left: 174px;
top: 496px;
position: absolute;
font-size: 26px;
color: rgba(33, 55, 9, 1);
word-wrap: break-word;
overflow-y: scroll;
padding-right: 12px;
}
.close {
width: 59px;
height: 61px;
left: 345px;
top: 1201px;
position: absolute;
.sparkBg("common/close.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './taskpop.less';
import API from '@src/api';
import { _asyncThrottle, _throttle, randomNum, windowJumpUrl } from '@src/utils/utils';
import store from '@src/store';
import { unwatchPageVisibility, watchPageVisibility } from '@spark/utils';
import { Toast } from '@spark/ui';
import { TASK_CODE } from '@src/utils/constants';
import modalStore from '@src/store/modal';
import shareStore from '@src/store/share';
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { Button } from '../Button';
import { miniGoUrl } from '@src/utils/share';
@observer
class Taskpop extends React.Component {
constructor(props) {
super(props);
this.state = {
taskInfo: [], // 任务列表
}
}
async componentDidMount() {
sensorMdExpouse("PopupExpouse", { pageName: "任务弹窗" })
this.getTaskList()
}
componentWillUnmount() {
unwatchPageVisibility(this.pageVChange)
}
/** 页面显示 */
pageVChange = (visible) => {
console.log('页面', visible ? '可见' : '不可见')
if (visible) {
this.getTaskList()
}
}
/**
* 获取任务列表数据
*/
getTaskList = async () => {
const { success, data } = await API.queryTasks()
if (success && data) {
this.setState({
taskInfo: data || {}
})
}
}
// 按钮点击事件
completeHandle = _throttle(async (item) => {
const { code, jumpUrl } = item || {}
if (code.indexOf(TASK_CODE.THIRD_RETAIN) > -1) {
sensorMdClick("PopupClick", { pageName: "任务弹窗点击", buttonName: "留资咨询" })
setTimeout(() => {
windowJumpUrl(jumpUrl)
}, 300)
} else if (code.indexOf(TASK_CODE.BROWSE_PRODUCT) > -1) {
sensorMdClick("PopupClick", { pageName: "任务弹窗点击", buttonName: "浏览产品" })
modalStore.pushPop("LongImgPop", {
imgUrl: jumpUrl,
taskSucCb: async () => {
const param = { taskCode: code }
await API.doCompleted(param)
}
})
} else if (code.indexOf(TASK_CODE.SIGN) > -1) {
sensorMdClick("PopupClick", { pageName: "任务弹窗点击", buttonName: "每日签到" })
} else if (code.indexOf(TASK_CODE.BROWSE_MINI) > -1) {
await API.doCompleted({ taskCode: code })
watchPageVisibility(this.pageVChange)
if (jumpUrl.indexOf("http") === 0) {
setTimeout(() => {
windowJumpUrl(jumpUrl)
}, 300)
} else {
miniGoUrl(jumpUrl)
}
} else if (code.indexOf(TASK_CODE.BROWSE_VIDEO) > -1) {
sensorMdClick("PopupClick", { pageName: "任务弹窗点击", buttonName: "浏览视频" })
modalStore.pushPop("Videopop", {
url: jumpUrl,
taskSucCb: async () => {
const param = { taskCode: code }
await API.doCompleted(param)
}
})
}
})
/** 任务领奖 */
receiveHandle = _asyncThrottle(async (task) => {
const { success, data } = await API.sendPrize({
taskId: task.id,
taskCode: task.code,
prizePendingCode: task?.prizePendingCode
})
if (success) {
data?.options?.[0]?.sendCount && Toast(`恭喜您领取${data.options[0].sendCount}次抽奖次数`)
this.getTaskList()
}
});
/* 关闭弹窗*/
closeHandle = () => {
sensorMdClick("PopupClick", { pageName: "任务弹窗点击", buttonName: "关闭" })
this.props.popData?.refresh && this.props.popData?.refresh();
modalStore.closePop()
};
render() {
const { taskInfo } = this.state
return (
<div className="taskpop">
<span className="bg"></span>
<div className="task_list">
{
taskInfo?.item?.map((item, index) => (
<div className="task_item" key={index}>
<span className="task_item_bg"></span>
<img className="task_icon" src={item.icon} />
<div className="task_title">{item.title}</div>
<span className="task_subtitle">{item.subTitle}</span>
{/* taskStatus 任务状态 * 0 去完成,任务完成次数未达到上限 * 1 待领奖 * 2 已完成,任务完成次数达到上限 */}
{item.taskStatus === 0 && <Button className="task_btn red_bg" onClick={() => this.completeHandle(item)}>去完成</Button>}
{item.taskStatus === 1 && <Button className="task_btn yellow_bg" onClick={() => this.receiveHandle(item)}>待领取</Button>}
{item.taskStatus === 2 && <Button className="task_btn grey_bg">已完成</Button>}
</div>
))
}
</div>
<Button className="close" onClick={this.closeHandle}></Button>
</div>
);
}
}
export default Taskpop;
@import "../../res.less";
.taskpop {
width: 750px;
height: 1624px;
left: 0px;
bottom: 0px;
position: absolute;
.bg {
width: 750px;
height: 943px;
left: 0px;
top: 681px;
position: absolute;
.sparkBg("taskPop/bg.png");
}
.task_list {
width: 660px;
height: 750px;
left: 45px;
top: 868px;
position: absolute;
overflow-y: auto;
.task_item {
width: 660px;
height: 141px;
position: relative;
margin-bottom: 12px;
.task_item_bg {
width: 660px;
height: 141px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("taskPop/task_item_bg.png");
}
.task_icon {
width: 98px;
height: 97px;
left: 33px;
top: 22px;
position: absolute;
// .sparkBg("taskPop/task_icon.png");
border-radius: 50%;
}
.task_title {
width: 300px;
height: 32px;
left: 165px;
top: 35px;
position: absolute;
font-size: 31px;
line-height: 32px;
color: rgba(255, 255, 255, 1);
font-weight: bold;
.lineClamp1();
}
.task_subtitle {
width: 300px;
height: 24px;
left: 166px;
top: 83px;
position: absolute;
font-size: 23px;
line-height: 24px;
color: rgba(255, 255, 255, 1);
.lineClamp1();
}
.task_btn {
width: 162px;
height: 62px;
left: 469px;
top: 43px;
position: absolute;
font-size: 25px;
line-height: 26px;
color: rgba(255, 255, 255, 1);
.flex_center();
&.red_bg {
.sparkBg("taskPop/red_bg.png");
}
&.yellow_bg {
.sparkBg("taskPop/yellow_bg.png");
}
&.grey_bg {
.sparkBg("taskPop/grey_bg.png");
}
}
}
}
.close {
position: absolute;
left: 660px;
top: 700px;
width: 59px;
height: 61px;
.sparkBg("common/close.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './videopop.less';
import VideoPlayer from '../VideoPlayer/VideoPlayer';
import modalStore from '@src/store/modal';
import { Button } from '../Button';
@observer
class Videopop extends React.Component {
constructor(props) {
super(props);
this.state = {
isPlay: false,
countdownTime: 10, // 单位s
}
this.videoRef = React.createRef();
this.timer = ''
}
componentDidMount() {
}
componentWillUnmount() {
if (this.timer) clearInterval(this.timer)
}
playVideo = () => {
this.startInterval()
this.setState({
isPlay: true
})
this.player?.play();
}
/** 开始倒计时 */
startInterval = () => {
if (this.timer) clearInterval(this.timer)
this.timer = setInterval(async () => {
if (this.state.countdownTime === 0) {
this.setState({
isFinish: true
})
clearInterval(this.timer)
this.timer = ''
// 完成任务
const { taskSucCb } = this.props?.popData || {}
taskSucCb && await taskSucCb()
return
}
this.setState({
countdownTime: this.state.countdownTime - 1,
})
}, 1000);
}
/** 关闭 */
closeHandle = () => {
this.props.popData?.closeCb?.()
modalStore.closePop();
}
render() {
const { url } = this.props.popData || {}
const { countdownTime, isPlay, isFinish } = this.state
return (
<div className="videopop modal_center">
<div className="video_box">
<VideoPlayer
videoRefCb={r => this.player = r}
controls
playsinline
webkitPlaysinline
// poster={RES_PATH + 'playPopup/cover.png'}
preload="none"
// muted
sources={[{
src: url,
// type: 'video/mp4'
}]}
userActions={{
click: false
}}
style={{
width: '100%',
height: '100%',
}} />
{!isPlay && <span className="play_btn" onClick={this.playVideo}></span>}
</div>
{!isFinish && <div className="countdown">{countdownTime}s</div>}
<Button className="close" onClick={this.closeHandle}></Button>
</div>
);
}
}
export default Videopop;
@import "../../res.less";
.videopop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.video_box {
width: 750px;
height: 422px;
left: 0px;
top: 520px;
position: absolute;
.video {
width: 100%;
height: 100%;
left: 0px;
top: 0px;
position: absolute;
}
.play_btn {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
}
.countdown {
width: 100px;
height: 63px;
text-align: center;
position: absolute;
left: 0;
top: 400px;
color: #fff;
border: 2px solid rgb(255, 255, 255);
border-left: none;
border-radius: 0 21px 21px 0;
background-color: rgba(70, 0, 0, 0.302);
box-shadow: inset 0.707px 0.707px 2px 0px rgba(225, 253, 255, 0.83);
font-size: 37px;
text-align: center;
line-height: 63px;
box-sizing: border-box;
}
.close {
width: 59px;
height: 61px;
left: 346px;
top: 989px;
position: absolute;
.sparkBg("common/close.png");
}
}
"use strict";
import React, { forwardRef } from "react";
import { observer } from "mobx-react";
import "./wxOpenModule.less";
const WxOpenModule = observer(forwardRef((props, ref) => {
console.info("WxOpenModule-props:",props)
return (
<div className="wxOpenModule">
<wx-open-launch-weapp
id = {props.id}
ref={ref}
username={props.username}
appid={props.appid}
path={props.path}
env-version = {props?.envVersion||'release'}
style={{ width: "100%", height: "100%", top: '0', left: '0', display: "block", border: "none", background: "none", opacity: 1, overflow: "hidden" }}
>
<script type="text/wxtag-template">
<button style={{ width: "2.78rem", height: "0.95rem", display: "block", border: "none", background: "none", opacity: 1 }}></button>
</script>
</wx-open-launch-weapp>
</div>
)
}))
export default WxOpenModule;
@import "../../res.less";
.wxOpenModule {
width: 100%;
height: 100%;
// left: 0px;
// top: 0;
// position: absolute;
overflow: hidden;
}
import React, { Component } from "react";
import Rulepop from "../components/rulepop/rulepop";
import Taskpop from "../components/taskpop/taskpop";
import Drawfailpop from "../components/drawfailpop/drawfailpop";
import Drawchancepop from "../components/drawchancepop/drawchancepop";
import Drawsucpop from "../components/drawsucpop/drawsucpop";
import './modal.less';
import { observer } from 'mobx-react';
import modalStore from '../store/modal';
import { toJS } from 'mobx';
import Focuspop from "@src/components/focuspop/focuspop";
import Bindpop from "@src/components/bindpop/bindpop";
import LongImgPop from "@src/components/longImgPop/longImgPop";
import LongImgPop2 from "@src/components/longImgPop2/longImgPop2";
import Videopop from "@src/components/videopop/videopop";
import ExitPop from "@src/components/exitPop/exitPop";
/**
* 弹窗配置
*/
export const cfg = {
Drawsucpop: Drawsucpop,
Drawchancepop: Drawchancepop,
Drawfailpop: Drawfailpop,
Taskpop: Taskpop,
Rulepop: Rulepop,
Focuspop,
Bindpop,
ExitPop,
LongImgPop,
LongImgPop2,
Videopop
};
@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
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 {
state = {
curPercentage: 0
}
curPercentage = 0;
intervalId = 0;
isEvenLoad = true; // 是否匀速加载进度条
componentDidMount() {
this.preloadAssetInit();
}
/**
* 资源预加载
*/
preloadAssetInit = async () => {
const imageList = assetList.preLoadImg;
preloadAsset(
imageList,
3,
this.onLoadingProgress,
).then(() => {
// 预加载资源完成
// 异步加载默认关闭
// setTimeout(() => {
// // 异步加载资源开始
// const asyncImageList = assetList.asyncLoadImg;
// preloadAsset(asyncImageList, 1)
// }, 5000);
});
};
jump = () => {
setTimeout(() => {
store.changePage(PAGE_MAP.HOME_PAGE); // 跳转页面
}, 100);
};
/**
* 资源加载进度回调
* @param {*} progress
*/
onLoadingProgress = (progress) => {
const percentage = Math.floor(progress * 100);
console.log("progress", percentage);
if (this.isEvenLoad) {
this.setEvenProgress(percentage);
} else {
if (percentage == 100) {
this.jump();
}
}
};
/**
* 以1%匀速加载进度
* @param {*} percentage
*/
setEvenProgress = (percentage) => {
this.intervalId && clearInterval(this.intervalId);
let curPercentage = this.curPercentage;
this.intervalId = setInterval(() => {
if (curPercentage >= percentage) {
clearInterval(this.intervalId);
this.jump();
return;
}
curPercentage += 1;
this.curPercentage = curPercentage;
this.setState({
curPercentage,
});
}, 10);
};
render() {
const {curPercentage} = this.state;
return <div className="loading">
<div className="loading-ip"/>
<div className="progressBarBg"/>
<div className="progressBar">
<div className="progressBarFill" style={{
transform: `translateX(${curPercentage - 100}%)`
}}/>
</div>
<span className="progressTxt">金豆正在路上...... {curPercentage}%</span>
</div>;
}
}
export default LoadingDemo;
@import "../../res.less";
.loading {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
//background: linear-gradient(152deg, #AEE7FF 6%, #F1DEFC 43%, #F3E7FB 73%);
.webpBg("LoadingPage/loadingBg.jpg");
background-size: 750px 1624px;
.loading-ip {
position: absolute;
left: 198px;
top: 572-153px;
width: 570px;
height: 508px;
.webpBg("LoadingPage/loadingIp.png");
}
.progressBarBg {
position: absolute;
left: 82px;
top: 898-153px;
width: 590px;
height: 30px;
border-radius: 118px;
background: #FCE4D4;
border: 2px solid #FFFFFF;
}
.progressBar {
position: absolute;
left: 84px;
top: 900-153px;
width: 590px;
height: 30px;
overflow: hidden;
border-radius: 118px;
.progressBarFill {
position: absolute;
left: 0;
top: 0;
width: 590px;
height: 30px;
.webpBg("LoadingPage/loadingFill.png")
}
}
.progressTxt {
position: absolute;
left: 246px;
top: 952-153px;
width: 256px;
height: 22px;
opacity: 1;
font-size: 24px;
font-weight: normal;
line-height: 22px;
color: #999999;
}
}
import {RES_PATH} from "../../../sparkrc";
export const QuestionCfg = [
{
stage: "童年守护(12岁)",
bg: RES_PATH + "answerPage/bg1.jpg",
title: "你在12岁这年攒够了5000压岁钱, 你会为自己选择什么生日礼物?",
titleImg: RES_PATH + "answerPage/q1.png",
answer: [
{
txt: 'A.购买一辆山地自行车',
},
{
txt: 'B.报名参加暑期夏令营',
},
{
txt: 'C.把钱存到储物罐中以后再花',
},
],
},
{
stage: "初入社会(22岁)",
bg: RES_PATH + "answerPage/bg2.jpg",
title: "你在这一年读完了大学,站在了人 生的十字路口,你的选择是?",
titleImg: RES_PATH + "answerPage/q2.png",
answer: [
{
txt: 'A.继续攻读研究生',
},
{
txt: 'B.在公司内拉帮结派,建立小团体孤立他人',
},
{
txt: 'C.在工作之余寻找兼职',
},
],
},
{
stage: "结婚(32岁)",
bg: RES_PATH + "answerPage/bg3.jpg",
title: "一转眼小孩也能打酱油了,你会为 小孩做什么呢?",
titleImg: RES_PATH + "answerPage/q3.png",
answer: [
{
txt: 'A.购买金生无忧(2024)儿童保险计划',
},
{
txt: 'B.请保姆加强照护',
},
{
txt: 'C.周末陪孩子出去玩,平时就放养了',
},
],
},
{
stage: "事业有成(40岁)",
bg: RES_PATH + "answerPage/bg4.jpg",
title: "你在公司呆了多年,现在有一份晋 升机会摆在你面前,你的选择是?",
titleImg: RES_PATH + "answerPage/q4.png",
answer: [
{
txt: 'A.你觉得前途无望,跳槽前往隔壁公司',
},
{
txt: 'B.铤而走险贿赂上级',
},
{
txt: 'C.自费读EMBA提升自己',
},
],
},
{
stage: "资产配置(50岁)",
bg: RES_PATH + "answerPage/bg5.jpg",
title: "你会如何配置投资进行资产提升?",
titleImg: RES_PATH + "answerPage/q5.png",
answer: [
{
txt: 'A.炒股',
},
{
txt: 'B.投资实体产业',
},
{
txt: 'C.购买保险理财,选择太保鑫禧年年年金',
},
],
},
{
stage: "养老规划(60岁)",
bg: RES_PATH + "answerPage/bg6.jpg",
title: "在对家中进行适老化改造的同时,你会如何为自己的养老生活增添保障呢",
titleImg: RES_PATH + "answerPage/q6.png",
answer: [
{
txt: 'A.投保金生无忧(2024)成人保险计划',
},
{
txt: 'B.旅居养老',
},
{
txt: 'C.入住太保家园',
},
],
},
]
import React from "react";
import {observer} from "mobx-react";
import "./answerpage.less";
import {_asyncThrottle, _throttle, waitTime} from "@src/utils/utils";
import {Button} from "@src/components/Button";
import {QuestionCfg} from "@src/pages/answerpage/QuestionCfg";
import classNames from "classnames";
@observer
class Answerpage extends React.Component {
state = {
quesIndex: 0,
chooseIndex: null,
};
componentDidMount() {
}
clickItem(index) {
this.setState({
chooseIndex: index,
});
}
render() {
const {quesIndex, chooseIndex} = this.state;
const {stage, bg, titleImg, answer} = QuestionCfg[quesIndex];
// 当前题目
return <div className="answerpage modal_center">
<div
className="bg"
style={{
backgroundImage: `url(${bg})`,
}}
/>
<div className="stage">{stage}</div>
<div className="quesArea">
<div className="queIndex">
<span>1</span>
<text>/</text>
6
</div>
<div className="questionArea">
<img
src={titleImg}
style={{
transformOrigin: 'top left',
transform: `scale(${window.remScale}) translateY(50%)`
}}
/>
</div>
<div className="answerArea">
{answer.map((item, index) => {
const cls = classNames(
"answerItem",
{
active: index === chooseIndex,
}
);
return <div
className={cls}
onClick={this.clickItem.bind(this, index)}
>{item.txt}</div>
})}
</div>
</div>
<Button className="confirm"/>
</div>;
}
}
export default Answerpage;
@import "../../res.less";
.answerpage {
width: 750px;
height: 1624px;
left: 0;
top: 0;
position: absolute;
.bg {
position: absolute;
left: 0;
top: 0;
width: 750px;
height: 1624px;
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: left top;
}
.stage {
font-size: 34px;
line-height: 0;
color: rgb(0, 0, 0);
position: absolute;
left: 0;
top: 241px;
width: 750px;
height: 59px;
display: flex;
align-items: center;
justify-content: center;
}
.quesArea {
position: absolute;
left: 10px;
top: 941px;
width: 730px;
height: 567px;
.webpBg("answerPage/queBg.png");
.queIndex {
position: absolute;
right: 26px;
font-size: 34.38px;
color: rgb(255, 255, 255);
text-align: left;
top: 30px;
transform: rotate(10deg);
span {
font-size: 42.31px;
}
text {
margin: 0 4px;
font-size: 33.05px;
}
}
}
.questionArea {
position: absolute;
left: 55px;
top: 32px;
width: 540px;
height: 80px;
display: flex;
align-items: center;
justify-content: left;
}
.confirm {
position: absolute;
left: 217px;
top: 1404px;
width: 316px;
height: 97px;
.webpBg("answerPage/按钮.png");
}
.answerArea {
position: absolute;
left: 0;
top: 146px;
width: 100%;
height: 288px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
.answerItem {
width: 632px;
height: 80px;
padding-left: 34px;
.webpBg("answerPage/normalBg.png");
font-size: 28px;
line-height: 28px;
//font-weight: bold;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: start;
color: #353535;
font-weight: 500;
&.active {
color: white;
font-weight: 500;
.webpBg("answerPage/chooseBg.png");
}
}
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './drawpage.less';
import API from '@src/api';
import { _asyncThrottle, _throttle } from '@src/utils/utils';
import modalStore from '@src/store/modal';
import store from '@src/store';
import { PAGE_MAP } from '@src/utils/constants';
import { CircleTurntable } from '@spark/circle-turntable'
import { pageLeave, sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { Button } from '@src/components/Button';
@observer
class Drawpage extends React.Component {
constructor(props) {
super(props);
this.state = {
drawInfo: {},
}
this.btnStarting = false // 转盘是否启动
this.turntableRef = null // 大转盘
this.drawResultInfo = {}
}
async componentDidMount() {
await this.getDrawInfo()
sensorMdExpouse("activityPage", { pageName: "抽奖页", follow_action: this.props?.is_from == 'result' ? 4 : 2 });
pageLeave("抽奖页", 5);
}
/** 获取转盘信息 */
getDrawInfo = async () => {
const { success, data } = await API.drawIndex()
if (success && data) {
this.setState({
drawInfo: data || {},
})
}
}
// 开始抽奖
lottteryHandle = _asyncThrottle(async () => {
const { actPrizeList, remainDrawTimes } = this.state.drawInfo
sensorMdClick("activityClick", { pageName: "抽奖页点击", buttonName: "开始抽奖" });
// 无抽奖次数
if (!remainDrawTimes) {
return modalStore.pushPop("Drawchancepop", {
refresh: () => this.getDrawInfo()
})
}
if (this.btnStarting) return false
this.btnStarting = true
const { success, data } = await API.drawJoin()
if (success && data) {
// 转盘转动开始抽奖
this.turntableRef.launch()
this.drawResultInfo = data || {}
// 转盘停止转动,指针停在index
const index = actPrizeList?.findIndex(item => item.prizeId === this.drawResultInfo.prizeId)
console.info('index', index)
// !important 注意:randomOffset设置为false,这样每次旋转的偏移量就不是随机滴啦
this.turntableRef.braking(index, { immediately: false, randomOffset: false });
} else {
this.btnStarting = false
this.getDrawInfo()
}
})
// 转盘停止处理
stopOkHandle = () => {
this.btnStarting = false
if (this.drawResultInfo?.prizeId == "thanks") {
modalStore.pushPop("Drawfailpop")
} else {
modalStore.pushPop("Drawsucpop", { ...this.drawResultInfo })
}
this.getDrawInfo();
}
/** 返回 */
backHome = _throttle(() => {
if (this.btnStarting) return false
store.changePage(PAGE_MAP.HOME_PAGE)
})
/** 奖品 */
prizeHandle = _throttle(async () => {
if (this.btnStarting) return false
if (!store.checkActStatus(true)) return
sensorMdClick("activityClick", { pageName: "抽奖页点击", buttonName: "奖品" });
location.href = CFG.prize;
})
/** 规则 */
ruleHandle = _throttle(async () => {
if (this.btnStarting) return false
if (!store.checkActStatus(true)) return
sensorMdClick("activityClick", { pageName: "抽奖页点击", buttonName: "规则" });
modalStore.pushPop("Rulepop")
})
/** 任务 */
taskHandle = _throttle(() => {
if (this.btnStarting) return false
if (!store.checkActStatus()) return
sensorMdClick("activityClick", { pageName: "抽奖页点击", buttonName: "做任务" });
modalStore.pushPop("Taskpop", {
refresh: () => this.getDrawInfo()
})
})
render() {
const { actPrizeList, remainDrawTimes } = this.state.drawInfo
return (
<div className="drawpage modal_center">
<span className="beiJing"></span>
<span className="title"></span>
<div className="subtitle">
<span className="subtitle_bg"></span>
<span className="subtitle_text">测一测你的端午专属人设</span>
</div>
<div className="turntable">
<span className="turntable_box_bg"></span>
<CircleTurntable
className="turntable_box"
ref={ref => this.turntableRef = ref}
options={actPrizeList || []}
angleOffset={23} // 角度偏移量
radian={80} // 奖项半径
launchDuration={1000} // 启动时间
brakingDuration={0}
maxSpeed={18}
// 大转盘背景
renderBackground={<div className="turntable_bg"></div>}
// 大转盘指针
renderStartButton={<></>}
// 渲染奖品信息
renderOption={(option) => {
return <div className="prize_item">
<div className="prize_name">{option.prizeName}</div>
<img className="prize_img" src={option.prizeImg} alt="" />
</div>
}}
didStop={this.stopOkHandle}
/>
<span className="pointer"></span>
<span className="cloud"></span>
</div>
<Button className="back" onClick={this.backHome} />
<Button className="rule_btn" onClick={this.ruleHandle}></Button>
<Button className="prize_btn" onClick={this.prizeHandle}></Button>
<Button className="task_btn" onClick={this.taskHandle}></Button>
<div className="draw_btn md10" onClick={this.lottteryHandle}>
<span className="left_num">剩余次数:{remainDrawTimes || 0}</span>
</div>
</div>
);
}
}
export default Drawpage;
@import "../../res.less";
.drawpage {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.beiJing {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("drawPage/bg.png");
}
.back {
width: 64px;
height: 65px;
left: 12px;
top: 159px;
position: absolute;
.sparkBg("common/back.png");
}
.rule_btn {
width: 95*1.2px;
height: 44*1.2px;
left: 0px;
top: 405px;
position: absolute;
.sparkBg("drawPage/rule_btn.png");
transform-origin: 0% 50%;
}
.prize_btn {
width: 92*1.2px;
height: 44*1.2px;
right: 0px;
top: 405px;
position: absolute;
.sparkBg("drawPage/prize_btn.png");
transform-origin: 100% 50%;
}
.task_btn {
width: 160px;
height: 202px;
left: 590px;
top: 1252px;
position: absolute;
.sparkBg("drawPage/task_btn.png");
}
.draw_btn {
width: 428px;
height: 133px;
left: 167px;
top: 1281px;
position: absolute;
.sparkBg("drawPage/draw_btn.png");
.left_num {
width: 420px;
height: 22px;
left: 8px;
top: 96px;
position: absolute;
text-align: center;
font-size: 22px;
line-height: 22px;
color: rgb(249, 252, 206);
}
}
.title {
width: 565px;
height: 152px;
left: 91px;
top: 204px;
position: absolute;
.sparkBg("drawPage/title.png");
}
.subtitle {
width: 545px;
height: 44px;
left: 99px;
top: 363px;
position: absolute;
.subtitle_bg {
width: 545px;
height: 44px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("drawPage/subtitle_bg.png");
}
.subtitle_text {
width: 411px;
height: 26px;
left: 68px;
top: 8px;
position: absolute;
font-size: 26px;
line-height: 26px;
color: rgba(255, 255, 255, 1);
text-align: center;
}
}
.turntable {
width: 704px;
height: 1022px;
left: 14px;
top: 260px;
position: absolute;
.turntable_box_bg {
width: 627px;
height: 1022px;
left: 60px;
top: 0px;
position: absolute;
.sparkBg("drawPage/turntable_box.png");
}
.turntable_box {
width: 519px;
height: 521px;
left: 115px;
top: 261px;
position: absolute;
}
.turntable_bg {
width: 519px;
height: 521px;
left: 0;
top: 0;
position: absolute;
.sparkBg("drawPage/turntable_bg.png");
}
.prize_item {
width: 190px;
height: 150px;
text-align: center;
color: #3e5623;
font-size: 22px;
padding-left: 16px;
box-sizing: border-box;
.prize_name {
width: 100%;
.lineClamp1();
font-weight: bold;
}
.prize_img {
width: 80px;
height: 70px;
object-fit: contain;
margin-top: 10px;
}
}
.pointer {
width: 128px;
height: 139px;
left: 312px;
top: 447px;
position: absolute;
.sparkBg("drawPage/pointer.png");
}
.cloud {
width: 704px;
height: 596px;
left: 0px;
top: 210px;
position: absolute;
.sparkBg("drawPage/cloud.png");
pointer-events: none;
}
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './homepage.less';
import { _throttle } from '@src/utils/utils';
import { Button } from '@src/components/Button';
import modalStore from '@src/store/modal';
import store from '@src/store';
import { Toast } from '@spark/ui';
import { onInitShare } from '@src/utils/share';
import shareStore from '@src/store/share';
import { PAGE_MAP } from '@src/utils/constants';
import { pageLeave, sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { SvgaPlayer } from '@spark/svgaplayer';
import { RES_PATH } from '../../../sparkrc';
@observer
class Homepage extends React.Component {
constructor(props) {
super(props);
}
async componentDidMount() {
await store.getHomeInfo();
// 初始化神策埋点 得在首页接口之后 首页接口会打上业务员绑定标记
await store.initSensorsMd();
await onInitShare();
// 初始化首页分享信息
shareStore.initShareInfo();
// modalStore.pushPop("Bindpop");
sensorMdExpouse("activityPage", { pageName: "端午活动首页", follow_action: 1 });
pageLeave("端午节活动首页点击", 5);
}
// 我的奖品
goPrize = _throttle(() => {
if (!store.checkActStatus(true)) return
sensorMdClick("activityClick", { pageName: "端午节活动首页点击", buttonName: "奖品" });
// store.changePage(PAGE_MAP.PRIZE_PAGE);
location.href = CFG.prize;
})
doShare = _throttle(() => {
if (!store.checkActStatus()) return
sensorMdClick("activityClick", { pageName: "端午节活动首页点击", buttonName: "分享" });
shareStore.doShare();
})
openRule = _throttle(() => {
if (!store.checkActStatus(true)) return
sensorMdClick("activityClick", { pageName: "端午节活动首页点击", buttonName: "规则" });
modalStore.pushPop("Rulepop")
})
goDraw = _throttle(() => {
if (!store.checkActStatus()) return
sensorMdClick("activityClick", { pageName: "端午节活动首页点击", buttonName: "抽奖" });
store.changePage(PAGE_MAP.DRAW_PAGE);
})
openTask = _throttle(() => {
if (!store.checkActStatus()) return
sensorMdClick("activityClick", { pageName: "端午节活动首页点击", buttonName: "做任务" });
modalStore.pushPop("Taskpop", {
refresh: () => store.getHomeInfo()
})
})
goTest = _throttle(() => {
if (!store.checkActStatus()) {
return
}
sensorMdClick("activityClick", { pageName: "端午节活动首页点击", buttonName: "开始测试" });
if (store.homeInfo?.remainAnswerTimes > 0) {
// 进入测试页
store.startTest()
} else {
Toast("今日参与次数不足,明天再来吧~")
}
})
render() {
return (
<div className="homepage modal_center">
<span className="bg"></span>
{/* <span className="content"></span> */}
<Button className="rule_btn md1" onClick={this.openRule}></Button>
<Button className="ptize_btn md2" onClick={this.goPrize}></Button>
<Button className="share_btn md3" onClick={this.doShare}></Button>
<Button className="task_btn md6" onClick={this.openTask}></Button>
<Button className="draw_btn md4" onClick={this.goDraw}></Button>
<div className="main_btn md5" onClick={this.goTest}>
<span className="test_times">剩余次数:{store.homeInfo?.remainAnswerTimes || 0}</span>
</div>
{/* <span className="title"></span> */}
<SvgaPlayer className="home_svga" src={RES_PATH + "svga/home.svga"} />
<div className="subtitle">
<span className="subtitle_bg"></span>
<span className="subtitle_text">测一测你的端午专属人设</span>
</div>
</div>
);
}
}
export default Homepage;
@import "../../res.less";
.homepage {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("homePage/bg.png");
}
.content {
width: 750px;
height: 820px;
left: 0px;
top: 468px;
position: absolute;
.sparkBg("homePage/content.png");
}
.rule_btn {
width: 95*1.2px;
height: 44*1.2px;
left: 0px;
top: 405px;
position: absolute;
.sparkBg("homePage/rule_btn.png");
transform-origin: 0% 50%;
}
.ptize_btn {
width: 92*1.2px;
height: 44*1.2px;
right: 0px;
top: 405px;
position: absolute;
.sparkBg("homePage/ptize_btn.png");
transform-origin: 100% 50%;
}
.share_btn {
width: 92*1.2px;
height: 44*1.2px;
right: 0px;
top: 460px;
position: absolute;
.sparkBg("homePage/share_btn.png");
transform-origin: 100% 50%;
}
.main_btn {
width: 429px;
height: 138px;
left: 167px;
top: 1281px;
position: absolute;
.sparkBg("homePage/main_btn.png");
.breathAnimation();
.test_times {
width: 422px;
height: 22px;
left: 7px;
top: 98px;
position: absolute;
text-align: center;
font-size: 22px;
line-height: 22px;
color: rgb(249, 252, 206);
}
}
.title {
width: 565px;
height: 152px;
left: 91px;
top: 204px;
position: absolute;
.sparkBg("homePage/title.png");
}
.home_svga {
width: 750px;
height: 1083px;
left: 0px;
top: 204px;
position: absolute;
pointer-events: none;
}
.subtitle {
width: 545px;
height: 44px;
left: 99px;
top: 363px;
position: absolute;
.subtitle_bg {
width: 545px;
height: 44px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("homePage/subtitle_bg.png");
}
.subtitle_text {
width: 100%;
height: 26px;
left: 0;
top: 8px;
position: absolute;
font-size: 26px;
line-height: 26px;
color: rgba(255, 255, 255, 1);
text-align: center;
}
}
.task_btn {
width: 160px;
height: 202px;
left: 590px;
top: 1252px;
position: absolute;
.sparkBg("homePage/task_btn.png");
}
.draw_btn {
width: 171px;
height: 202px;
left: 0px;
top: 1252px;
position: absolute;
.sparkBg("homePage/draw_btn.png");
.breathAnimation();
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import { md5 } from '@spark/utils';
import { CHANNEL_PARAMS } from '@src/utils/constants';
import API from '@src/api';
import './middlePageNew.less';
import { _throttle, getUrlParam, isWechatMini } from '@src/utils/utils';
import WxOpenModule from '@src/components/wxOpenModule/wxOpenModule';
import { onInitShare } from '@src/utils/share';
import { ensureDomain, domain } from '@spark/dbdomain';
@observer
class MiddlePage extends React.Component {
constructor(props) {
super(props);
this.state = {
isWeixinMini: false,
};
}
async componentDidMount() {
await onInitShare();
await ensureDomain();
CFG.domain = domain || window.location.origin;
CFG.index = CFG.domain + CFG.index;
this.juageWeixin();
const requestParam = getUrlParam("requestParam");
const param = getUrlParam("param"); // 长航加油站 加密串参数
const channel = getUrlParam("channel");
const timestamp = new Date().getTime();
const sign = md5(requestParam + timestamp + "24fnTNEK8KqURnlZhIJ58mdylVe1rs0BZha");
const isFromShare = getUrlParam("isFromShare");
const empno = getUrlParam("empno");
const empname = getUrlParam("empname");
const wdTK = getUrlParam("wdTK");
const empnoShow = getUrlParam("empnoShow");
let actUrl = getUrlParam("actUrl") && getUrlParam("actUrl") != 'null' ? decodeURIComponent(getUrlParam("actUrl")) : CFG.index;
// console.log(actUrl);
const jumpUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(getUrlParam("redirectUrl")))) || CFG.index;
let directUrl = encodeURIComponent(
encodeURIComponent(
actUrl +
`&channel=${channel}&empno=${empno}&empname=${empname}&wdTK=${wdTK}&isFromShare=${isFromShare}&empnoShow=${empnoShow}`
)
);
// alert(actUrl);
// alert(directUrl);
console.info("这是直接跳转的链接", directUrl);
if (channel == CHANNEL_PARAMS.THD_MINI) {
const thirdParam = JSON.parse(decodeURIComponent(getUrlParam("thdParam")));
let newEmpno = thirdParam.empno;
let newEmpname = thirdParam.empname;
let openId = thirdParam.openid
let newEmpnoShow = thirdParam.empnoShow
let custPhone = thirdParam.custPhone
let token = getUrlParam("token")
let newWdTK = decodeURIComponent(getUrlParam("wdTK"))
// JSON.parse(decodeURIComponent(getUrlParam("wdTK")));
// alert("thirdParam " + thirdParam + "123 " + custPhone)
console.error("kankancanshu", thirdParam, custPhone)
// location.href = `${CFG.middleTHRequest}requestParam=${requestParam}&redirect=${directUrl}&channel=${channel}&sign=${sign}&timestamp=${timestamp}&empno=${newEmpno}&empname=${newEmpname}&openid=${openId}&wdTK=${newWdTK}&empnoShow=${newEmpnoShow}&custPhone=${custPhone}`;
console.info('---sign',getUrlParam("thdParam") + timestamp + "24fnTNEK8KqURnlZhIJ58mdylVe1rs0BZha")
const {success, data} = await API.thdLoginNew({
requestParam:encodeURIComponent(getUrlParam("thdParam")),
redirect:decodeURIComponent(directUrl),
channel:channel,
sign:md5(getUrlParam("thdParam") + timestamp + "24fnTNEK8KqURnlZhIJ58mdylVe1rs0BZha"),
timestamp:timestamp,
empno:newEmpno,
empname:newEmpname,
openid:openId,
wdTK:newWdTK,
empnoShow:newEmpnoShow,
custPhone:custPhone,
token:token,
authToken:'P_PT_PHONE_THD25DragonBoat',
environment:CFG.environment,
// environment:CFG.projectId == 'pd5fd3088' ? 'test' : ''
})
if(success){
if(data?.boolLogin){ // 已登录
location.href = data?.autoLoginUrl; //免登录链接
}else{
location.href = data?.authUrl; //授权地址
}
}
}else{
const { success, data } = await API.tbLoginNew({
token: getUrlParam("token"),
src: "d8",
redirectUrl: directUrl,
});
if (success) {
if (data?.loginFlag) {
location.href = data?.autoLoginUrl;
}
} else {
}
}
// 这个活动投太好店
// let targetUrl = `${CFG.middleRequest}requestParam=${requestParam}&redirect=${directUrl}&channel=${channel}&sign=${sign}&timestamp=${timestamp}&empno=${empno}&empname=${empname}&wdTK=${wdTK}`;
// if (param && param != "changHangParam") targetUrl += `&chparam=${param}`;
// location.href = targetUrl;
}
juageWeixin = async () => {
const env = await isWechatMini();
console.error(env, "这个有问题??");
let isWeixinMini = false;
if (env == "mini") isWeixinMini = true;
this.setState({
isWeixinMini,
});
};
getWeixinMiniPath = () => {
let path = "";
const { isWeixinMini } = this.state;
console.info(
"这是path",
`/packageMember/pages/bindPhone/thirdBindPhone/thirdBindPhone?gwAuthToken=P_PT_PHONE_25DragonBoat`
);
if (isWeixinMini) {
//官微小程序
CFG.miniAppid = "wx68b00bbff4c69cc9"; //生产appid wx68b00bbff4c69cc9 测试appid: wx0fe22fef352fafc1
CFG.miniUserName = "gh_70448c19370c";
path = `/packageMember/pages/bindPhone/thirdBindPhone/thirdBindPhone?gwAuthToken=P_PT_PHONE_25DragonBoat&channel=${CFG.channel}`;
} else {
//H5默认跳转官微小程序
CFG.miniAppid = "wx68b00bbff4c69cc9";
CFG.miniUserName = "gh_70448c19370c";
path = `/packageMember/pages/bindPhone/thirdBindPhone/thirdBindPhone?gwAuthToken=P_PT_PHONE_25DragonBoat&channel=${CFG.channel}`;
}
window.myAlert && window.myAlert(path);
return path;
};
handleToDraw = _throttle(() => {
const { isWeixinMini } = this.state;
console.info("跳转-->", isWeixinMini, this.getWeixinMiniPath());
if (!isWeixinMini) return; //H5走组件的能力,小程序内直接跳转
wx?.miniProgram?.navigateTo({ url: this.getWeixinMiniPath() });
// wx.miniProgram.navigateTo({ url: url });
});
render() {
const { isWeixinMini } = this.state;
const channel = getUrlParam("channel");
return (
<div className="middlePageNew">
<div className={`${channel == CHANNEL_PARAMS.THD_MINI ? 'bg' : 'bg1'}`}>
{/* <div className="btn"></div> */}
<div className="btn" onClick={this.handleToDraw}>
{!isWeixinMini && (
<WxOpenModule
id={"downLoadbtn"}
appid={CFG.miniAppid}
username={CFG.miniUserName}
envVersion={CFG.miniEnv}
path={this.getWeixinMiniPath()}></WxOpenModule>
)}
</div>
</div>
</div>
);
}
}
export default MiddlePage;
@import "../../res.less";
.middlePageNew {
width: 750px;
height: 100vh;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 750px;
height: 1624px;
position: absolute;
top: calc(50% - 1624px/2);
left: 0;
.btn {
width: 750px;
height: 1624px;
position: absolute;
top: calc(50% - 1624px/2);
left: 0;
}
}
.bg1 {
width: 750px;
height: 1624px;
position: absolute;
top: calc(50% - 1624px/2);
left: 0;
.sparkBg("mid/bg.jpg");
.btn {
width: 750px;
height: 1624px;
position: absolute;
top: calc(50% - 1624px/2);
left: 0;
}
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './prizepage.less';
import { Button } from '@src/components/Button';
import store from '@src/store'
import { _asyncThrottle, getUrlParam, windowJumpUrl } from '@src/utils/utils'
import modalStore from '@src/store/modal'
import { unwatchPageVisibility, watchPageVisibility } from '@spark/utils';
import API from '@src/api'
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd'
@observer
class Prizepage extends React.Component {
constructor(props) {
super(props);
this.state = {
prizeInfo: {},
}
}
async componentDidMount() {
// 初始化神策埋点
await store.initSensorsMd()
sensorMdExpouse("activityPage", { pageName: "我的奖品页", activityName: CFG.activityName, follow_action: 2 })
await this.getPrizeInfo()
// 关注回来 刷新首页
watchPageVisibility(this.pageVChange)
}
componentWillUnmount() {
unwatchPageVisibility(this.pageVChange)
}
/** 页面显示 */
pageVChange = (visible) => {
console.log('页面', visible ? '可见' : '不可见')
if (visible) {
this.getPrizeInfo()
}
}
/** 获取奖品信息 */
getPrizeInfo = async () => {
const res = await API.getPrizePageInfo();
if (res?.success) {
this.setState({
prizeInfo: res.data || {},
});
}
};
navigateToPrize = (item) => {
return _asyncThrottle(async () => {
sensorMdClick("activityClick", { pageName: "我的奖品页点击", activityName: CFG.activityName, buttonName: `点击奖品` });
// sensorMdClick("activityClick", { pageName: "我的奖品页", buttonName: `去看看-${item.prizeName}` });
const {
focusStatus, // 是否关注公众号(true-关注,false-未关注)
bindStatus, // 是否绑定(true-已绑定,false-未绑定)
activityCode,
} = this.state.prizeInfo;
if (!focusStatus) {
modalStore.pushPop("Focuspop");
return false;
} else if (!bindStatus) {
modalStore.pushPop("Bindpop");
return false;
}
const { id, prizeId, userRecordId } = item;
if (prizeId?.indexOf("sp_object_gw") == -1 && prizeId?.indexOf("sp_virtual") == -1) {
//非官微奖品
setTimeout(() => {
location.href = `/aaw/projectx/takePrize?projectOrderNo=${id}`;
}, 300)
} else {
if (prizeId?.indexOf("sp_virtual") > -1 && userRecordId) {
location.href = `/aaw/projectx/takePrize?projectOrderNo=${userRecordId}`;
return
}
if (item.gwObjectStatus == 1) return;
const { success, data } = await API.createCode({ recordId: id, newActivityCode: activityCode });
if (!success) return;
const { enParamStr } = data;
setTimeout(() => {
location.href = CFG.prizeJumpUrl + `?enParamStr=${enParamStr}&activityCode=${data.activityCode}`;
}, 300)
}
});
}
backHome() {
sensorMdClick("activityClick", { pageName: "我的奖品页点击", buttonName: "返回", activityName: CFG.activityName })
const fromPage = getUrlParam('fromPage')
if (fromPage == 'home') {
history.go(-1)
} else {
const indexUrl = 'index.html?appID=' + CFG.appID + '&channel=' + getUrlParam("channel")
//留资跳转回来
windowJumpUrl(indexUrl)
}
}
render() {
const { prizeInfo } = this.state
return (
<div className="prizepage modal_center">
<span className="bg"></span>
<span className="title"></span>
<div className="prize_list">
{prizeInfo?.list?.map((item, index) => (
<div className="prize_item" key={index} onClick={this.navigateToPrize(item)}>
<img className="prize_img" src={item.prizeImg} />
<span className="prize_name">{item.prizeName}</span>
<span className="prize_btn"></span>
</div>
))}
</div>
<Button className="back" onClick={this.backHome}></Button>
</div>
);
}
}
export default Prizepage;
@import "../../res.less";
.prizepage {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("prizePage/bg.png");
}
.title {
width: 234px;
height: 58px;
left: 258px;
top: 163px;
position: absolute;
.sparkBg("prizePage/title.png");
}
.prize_list {
width: 690px;
// height: 641px;
height: calc(100vh - 270px);
left: 29px;
top: 267px;
position: absolute;
padding-right: 20px;
overflow-y: auto;
.prize_item {
width: 690px;
height: 149px;
position: relative;
.sparkBg("prizePage/prize_item_bg.png");
margin-bottom: 16px;
.prize_btn {
width: 23px;
height: 34px;
left: 618px;
top: 58px;
position: absolute;
.sparkBg("prizePage/prize_btn.png");
}
.prize_name {
width: 440px;
height: 32px;
left: 176px;
top: 59px;
position: absolute;
font-size: 32px;
line-height: 32px;
color: rgba(70, 101, 64, 1);
font-weight: bold;
.lineClamp1();
}
.prize_img {
width: 114px;
height: 114px;
left: 20px;
top: 18px;
position: absolute;
.sparkBg("prizePage/prize_img.png");
}
}
}
.back {
width: 64px;
height: 65px;
left: 12px;
top: 159px;
position: absolute;
.sparkBg("common/back.png");
}
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './resultpage.less';
import store from '@src/store';
import { Button } from '@src/components/Button';
import { _throttle } from '@src/utils/utils';
import modalStore from '@src/store/modal';
import { PAGE_MAP, ROLE_MAP } from '@src/utils/constants';
import shareStore from '@src/store/share';
import { sensorMdClick, sensorMdExpouse } from '@src/utils/sensorMd';
import { hideLoading, showLoading } from '@spark/ui';
import { htmlShot } from '@spark/utils';
@observer
class Resultpage extends React.Component {
constructor(props) {
super(props);
this.state = {
poster: null,
};
}
async componentDidMount() {
sensorMdExpouse("activityPage", { pageName: "结果页", follow_action: 3 });
showLoading();
await this.doCreatePoster();
this.pageRef?.scrollTo(0, 240 * window.remScale);
}
/** 合成海报 */
doCreatePoster = async () => {
setTimeout(async () => {
const url = await htmlShot(this.shotRef)
this.setState({ poster: url })
hideLoading()
}, 500);
};
doShare = _throttle(() => {
sensorMdClick("activityClick", { pageName: "结果页", buttonName: "分享人设" });
shareStore.doShare();
})
goDraw = _throttle(() => {
sensorMdClick("activityClick", { pageName: "结果页", buttonName: "立即抽奖" });
store.changePage(PAGE_MAP.DRAW_PAGE, { is_from: 'result' });
})
// 查看产品详情
openProduct = _throttle(() => {
sensorMdClick("activityClick", { pageName: "结果页", buttonName: "点击查看" });
modalStore.pushPop("LongImgPop2", { imgUrl: ROLE_MAP[store.roleId].productLink })
})
backHandle = _throttle(() => {
sensorMdClick("activityClick", { pageName: "结果页", buttonName: "返回" });
store.changePage(PAGE_MAP.HOME_PAGE)
})
render() {
const { poster } = this.state;
return (
<div className="com_Container" ref={r => this.pageRef = r}>
<div className="resultpage">
<div className="shot_content" ref={r => this.shotRef = r}>
<span className="bg"></span>
<span className={`role_${store.roleId}`}></span>
<div className="code_boc">
<span className="code_bg"></span>
<img className="code_img" src={store.frontVariable.posterQrCode} alt="" />
</div>
</div>
{poster && <img className="poster_img" src={poster} style={{ opacity: 0 }} alt="" />}
<Button className={`product_btn type_${store.roleId}`} onClick={this.openProduct}></Button>
<div className="bottom_btn">
<Button className="share_btn md9" onClick={this.doShare}></Button>
<Button className="draw_btn md8" onClick={this.goDraw}></Button>
<span className="tips">长按保存海报分享</span>
</div>
<Button className="back_btn" onClick={this.backHandle}></Button>
</div>
</div>
);
}
}
export default Resultpage;
@import "../../res.less";
.resultpage {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.shot_content {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.bg {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("resultPage/bg.png");
}
.role_a {
width: 750px;
height: 1064px;
left: 0px;
top: 219px;
position: absolute;
.sparkBg("resultPage/传统守护者.png");
}
.role_b {
width: 750px;
height: 1057px;
left: 0px;
top: 219px;
position: absolute;
.sparkBg("resultPage/社交组局王.png");
}
.role_c {
width: 750px;
height: 1059px;
left: 0px;
top: 219px;
position: absolute;
.sparkBg("resultPage/狂热尝新党.png");
}
.role_d {
width: 750px;
height: 1055px;
left: 0px;
top: 219px;
position: absolute;
.sparkBg("resultPage/佛系随性派.png");
}
.role_e {
width: 750px;
height: 1065px;
left: 0px;
top: 219px;
position: absolute;
.sparkBg("resultPage/元气美食家.png");
}
.code_boc {
width: 679px;
height: 190px;
left: 0px;
top: 1261px;
position: absolute;
.code_bg {
width: 679px;
height: 190px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("resultPage/code_bg.png");
}
.code_img {
width: 124px;
height: 124px;
left: 546px;
top: 21px;
position: absolute;
.sparkBg("resultPage/code_img.png");
border-radius: 10px;
}
}
}
.product_btn {
width: 129px;
height: 51px;
left: 505px;
top: 1113px;
position: absolute;
.sparkBg("resultPage/product_btn.png");
&.type_d {
left: 525px;
top: 1124px;
}
}
.poster_img {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
}
.bottom_btn {
width: 677px;
height: 138px;
left: 34px;
top: 1437px;
position: absolute;
.share_btn {
width: 323px;
height: 105px;
left: 354px;
top: 0px;
position: absolute;
.sparkBg("resultPage/share_btn.png");
}
.draw_btn {
width: 323px;
height: 105px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("resultPage/draw_btn.png");
}
.tips {
width: 254px;
height: 18px;
left: 216px;
top: 120px;
position: absolute;
font-size: 17px;
line-height: 18px;
color: rgba(53, 95, 49, 1);
text-align: center;
text-decoration: underline;
}
}
.back_btn {
width: 64px;
height: 65px;
left: 12px;
// top: 133px;
// position: absolute;
top: 50px;
position: fixed;
.sparkBg("common/back.png");
}
}
@RES_PATH: '/src/assets/';
@webp: '?x-oss-process=image/format,webp';
.sparkBg(@value) {
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: left top;
background-image: url("@{RES_PATH}@{value}");
[duiba-webp='true'] & {
background-image: url("@{RES_PATH}@{value}@{webp}");
}
}
.webpBg(@value) {
.sparkBg("@{value}");
}
.mask(@value) {
mask-image: url("@{RES_PATH}@{value}");
-webkit-mask-image: url("@{RES_PATH}@{value}");
mask-repeat: no-repeat;
}
// 定位-水平垂直居中
.transform_center() {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
position: absolute;
}
// flex-水平垂直居中
.flex_center() {
display: flex;
justify-content: center;
align-items: center;
}
/* 文本过长隐藏文字并显示省略号 单行*/
.lineClamp1() {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.lineClampN(@num) {
text-overflow: -o-ellipsis-lastline;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: @num;
overflow: hidden;
}
// 按钮呼吸动效
.breathAnimation() {
animation: scale 1s linear infinite alternate;
@keyframes scale {
0% {
transform: scale(1);
}
100% {
transform: scale(1.12);
}
}
}
// 弹窗居中缩放动效
// 注意 加该样式的元素居中方式不能用translate(-50%, -50%)
// 可以用margin-left、margin-top为负的宽度、高度的一半
.popupCenterShow() {
animation: centerShowAni 300ms ease-out;
@keyframes centerShowAni {
0% {
transform: scale(0);
}
66.7% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
}
// 渐入动画
.fade-in {
opacity: 0;
animation: fadeIn ease-in 1;
animation-fill-mode: forwards;
animation-duration: 1s;
}
@keyframes fadeIn {
0% {
opacity: 0;
}
70% {
opacity: 0;
}
100% {
opacity: 1;
}
}
// 隐藏滚动条
.hideScrollbar() {
scrollbar-width: none;
/* firefox */
-ms-overflow-style: none;
/* IE 10+ */
&::-webkit-scrollbar {
display: none;
/* Chrome Safari */
}
}
.ruleStyle {
overflow-x: hidden;
overflow-y: scroll;
word-break: break-all;
}
// 上滑弹窗动画
.topPop_move {
animation: move_top_ani 500ms ease-in-out;
@keyframes move_top_ani {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
}
import { ACT_STATUS, ACT_STATUS_TOAST, PAGE_MAP, TOPIC_LIST } from '@src/utils/constants';
import { makeAutoObservable } from 'mobx';
import API from '../api/index';
import { GetCurrSkinId, getCustomShareId } from "@src/utils/utils";
import { initSensors } from '@src/utils/sensorMd';
import { Toast } from '@spark/ui';
const skinId = GetCurrSkinId() || getCustomShareId();
const store = makeAutoObservable({
/** 活动规则 */
ruleInfo: '',
/** 前端开发配置 */
frontVariable: {},
/** 当前页面 */
curPage: {
index: PAGE_MAP.HOME_PAGE,
myPrize: PAGE_MAP.PRIZE_PAGE,
// 免登中间页-线上测试
Did1NjA4NjM: PAGE_MAP.MID_PAGE,
// 免登中间页-线上正式
Did1NjA4NzE: PAGE_MAP.MID_PAGE,
}[skinId] || PAGE_MAP.QUES_PAGE,
pageData: {},
/** 场景切换 */
changePage(page, data = {}) {
this.pageData = data;
this.curPage = page;
},
/** 获取活动规则 */
async initRule() {
// 模拟获取远程的数据
const { data } = await API.getRule();
this.ruleInfo = data;
},
/** 获取前端配置项 */
async getFrontVariable() {
// 获取前端开发配置
const { data } = await API.getFrontVariable();
const resData = data || {};
if (resData.shareInfo) resData.shareInfo = JSON.parse(resData.shareInfo);
this.frontVariable = resData;
console.log('前端开发配置', data)
},
/** 首页信息 */
homeInfo: {},
async getHomeInfo() {
let params = {}
const shareCode = getUrlParam("shareCode") && getUrlParam("shareCode") !== 'null' ? getUrlParam("shareCode") : '';
if (shareCode) {
params.shareCode = shareCode
}
const { success, data, timeStamp } = await API.home(params);
if (success) {
const resData = data || {}
const { startTime, endTime } = resData
if (timeStamp >= startTime && timeStamp < endTime) {
resData._actStatus = ACT_STATUS.ING;
} else if (timeStamp < startTime) {
resData._actStatus = ACT_STATUS.NOT_START;
} else {
resData._actStatus = ACT_STATUS.ENDED;
}
this.homeInfo = resData;
console.log('首页数据', this.homeInfo);
}
},
/**
* 校验活动状态
*/
checkActStatus(isSpecial) {
if (this.homeInfo._actStatus && this.homeInfo._actStatus !== ACT_STATUS.ING) {
// 活动已结束:【奖品】、【规则】按钮可点
if (isSpecial && this.homeInfo._actStatus === ACT_STATUS.ENDED) return true;
Toast(ACT_STATUS_TOAST[this.homeInfo._actStatus]);
return false;
}
return true;
},
startInfo: {},
roleId: 'a', // 人设id
async startTest() {
this.roleId = 'a'
const { success, data } = await API.startAnswer();
if (success) {
this.startInfo = data || {}
// 从题库随机出一条数据
this.startInfo.topicList = TOPIC_LIST[Math.floor(Math.random() * TOPIC_LIST.length)]
this.changePage(PAGE_MAP.QUES_PAGE)
}
},
async submitTest(answer) {
let roleId = ''
answer.map((item, index) => {
const itemRuleId = store.startInfo.topicList[index]?.options?.[item]?.split("$$$")?.[1]
if (itemRuleId) roleId = itemRuleId
})
this.roleId = roleId
const { success, data } = await API.submitAnswer({
chooses: roleId, // 人设Id
startId: this.startInfo.startId
});
if (success) {
this.changePage(PAGE_MAP.RESU_PAGE, {
awardTimes: data.awardTimes || 0
})
}
},
userInfo: {},
setUserInfo(userInfo) {
this.userInfo = userInfo;
},
/** 初始化神策埋点 */
async initSensorsMd() {
const res = await API.queryUserInfo();
if (res.success) {
store.setUserInfo(res.data || {});
const { unionId, openId, friendEmpNo, friendOpenId, newEmpNo } = res.data || {};
// initSensors(unionId, openId, friendEmpNo, friendOpenId);
initSensors(unionId, openId, newEmpNo);
}
},
})
export default store;
import { makeAutoObservable } from 'mobx';
// 此处配置页面的优先级,越大优先级越高
// PopIndex:11
/**
* 弹窗优先级 可以是负数, 不写默认是10, 数值越小,层级越高
*/
const modalIndex = {
LongImgPop: 11,
Videopop: 11,
}
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;
import { makeAutoObservable } from 'mobx';
import { CHANNEL_PARAMS } from '@src/utils/constants';
import { isWeChat, onUpdateShare } from '@src/utils/share';
import { showShareGuide } from "@spark/share";
import store from '.';
import { isWechatMini } from '@src/utils/utils';
import { accessLog } from 'duiba-utils';
const shareStore = makeAutoObservable({
/**
* 被动分享
**/
async doShare() {
accessLog(503, { from: CFG.channel })
// 首页链接(不带邀请参数)
let shareUrl = `${CFG.index}&shareCode=${store.homeInfo?.shareCode}${!!store.userInfo?.unionId ? `&uid=${store.userInfo.unionId}` : ""}`
this.initShareInfo(shareUrl)
// 分享引导蒙层
if (isWeChat()) {
showShareGuide()
}
},
/**
* 初始化分享
* @param shareTargetUrl 分享链接
**/
async initShareInfo(shareTargetUrl) {
// 首页 (页面初始化分享 用到的当前页面链接 + 长航参数 + 用户uid)
const initUrl = location.href + `&shareCode=${store.homeInfo?.shareCode}` + (!!store.userInfo?.unionId ? `&uid=${store.userInfo.unionId}` : "")
let { miniProgramUrl = {}, shareInfo = {}, shareMiddle = '' } = store.frontVariable || {}
const {
title, // 分享标题
content, // 分享描述
thumbnail, // 缩略图-非小程序端
thumbnail_mini // 缩略图-小程序端
} = shareInfo
// const isMini = await isWechatMini() == 'mini'
// 分享主副标题
const share_title = title
const share_content = content
let link = ''
link = shareMiddle?.replace("channelName", CFG.channel)
if (store.homeInfo?.changHangParam) {
link = link?.replace("changHangParam", store.homeInfo.changHangParam)
}
link = link.replace("activityUrl", encodeURIComponent(shareTargetUrl || initUrl))
link = encodeURIComponent(link);
const shareData = {
sharedesc: share_title,
shareimg: thumbnail_mini,
shareurl: `/pages/webviewByToken/index?url=${link}`
}
console.info('shareData', shareData);
if (CFG.channel == CHANNEL_PARAMS.THD_MINI) {
wx.miniProgram.postMessage({
data: {
title: share_title, // 标题
desc: share_content, // 描述
imageUrl: thumbnail_mini, // 图片
path: `${miniProgramUrl?.['taihaodian']}?shareUrl=${link}&source=crhd`
}
});
return;
} else {
wx.miniProgram.postMessage({
data: shareData
});
}
// else {
// let wxLink = `${CFG.domain}${miniProgramUrl?.['gongzhong']}&redirectUrl=${encodeURIComponent(link)}`;
// onUpdateShare({
// title: share_title,
// content: share_content,
// thumbnail: thumbnail,
// url: wxLink
// })
// }
},
})
export default shareStore;
import {AES, enc, mode, pad} from "crypto-js";
const getOptions = (iv) => {
return {
iv: enc.Utf8.parse(iv),
mode: mode.CBC,
padding: pad.ZeroPadding,
};
}
/** 加密 */
export const AESEncrypt = (str, key, iv) => {
const options = getOptions(iv);
return AES.encrypt(str, enc.Utf8.parse(key), options).toString();
};
/** 解密 */
export const AESDecrypt = (cipherText, key, iv) => {
const options = getOptions(iv);
return AES.decrypt(cipherText, enc.Utf8.parse(key), options)
.toString(enc.Utf8)
.trim()
.replace(//g, '')
.replace(//g, '')
.replace(/\v/g, '')
.replace(/\x00/g, '');
};
/**
* 检查是否支持.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='
}
})()
/*
* @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",
DRAW_PAGE: "drawPage",
PRIZE_PAGE: "prizePage",
MID_PAGE: "midPage",
QUES_PAGE: "quesPage",
RESU_PAGE: "resultPage"
};
/**
* 渠道参数
* 1.官微菜单栏 guanwei
* 2.官微权益中心 quanyizhongxin
* 3.寿险小程序 shouxian
* 4.官微推文 tuiwen
* 5.长航加油站 dingdong
* 6.太好钉 taihaoding
* 7.海报 haibao
* 8.太好店小程序
* 9.官微服务大厅
* 10.集团app
*/
export const CHANNEL_PARAMS = {
/** 官微菜单栏 */
GW_MENU: "guanwei",
/** 官微权益中心 */
GW_EQUITY: "quanyizhongxin",
/** 寿险小程序 */
SHOUXIAN_MINI: "shouxian",
/** 官微推文 */
GW_TWEET: "tuiwen",
/** 长航加油站(公众号) */
CHANG_HANG: "dingdong",
/** 太好钉 */
THD: "taihaoding",
/** 海报 */
POSTER: "haibao",
/** 太好店小程序 */
THD_MINI: "taihaodian",
/** 官微服务大厅 */
FUWU: "fuwu",
/** 集团app */
APP: "app",
/** 心意商城小程序 */
XINYI: "xinyi",
/** 年报活动 */
NIANBAO: "nianbao",
/** 订阅号 */
DINGYUEHAO: "dingyuehao",
}
/** 神策 sysSourceName渠道参数 */
export const SYS_SOURCE_CHANNEL = {
/** 官微菜单栏 */
[CHANNEL_PARAMS.GW_MENU]: "官微",
/** 官微权益中心 */
[CHANNEL_PARAMS.GW_EQUITY]: "官微",
/** 寿险小程序 */
[CHANNEL_PARAMS.SHOUXIAN_MINI]: "官微",
/** 官微推文 */
[CHANNEL_PARAMS.GW_TWEET]: "官微",
/** 长航加油站(公众号) */
[CHANNEL_PARAMS.CHANG_HANG]: "个险",
/** 太好钉 */
[CHANNEL_PARAMS.THD]: "官微",
/** 海报 */
[CHANNEL_PARAMS.POSTER]: "官微",
/** 太好店小程序 */
[CHANNEL_PARAMS.THD_MINI]: "个险",
/** 官微服务大厅 */
[CHANNEL_PARAMS.FUWU]: "官微",
/** 集团app */
[CHANNEL_PARAMS.APP]: "官微",
/** 心意商城小程序 */
[CHANNEL_PARAMS.XINYI]: "官微",
/** 年报活动 */
[CHANNEL_PARAMS.NIANBAO]: "官微",
/** 订阅号 */
[CHANNEL_PARAMS.DINGYUEHAO]: "官微",
}
/** 神策 activitySource渠道参数 */
export const ACTIVITY_SOURCE_CHANNEL = {
/** 官微菜单栏 */
[CHANNEL_PARAMS.GW_MENU]: "官微菜单栏",
/** 官微权益中心 */
[CHANNEL_PARAMS.GW_EQUITY]: "官微权益中心",
/** 寿险小程序 */
[CHANNEL_PARAMS.SHOUXIAN_MINI]: "寿险小程序",
/** 官微推文 */
[CHANNEL_PARAMS.GW_TWEET]: "官微推文",
/** 长航加油站(公众号) */
[CHANNEL_PARAMS.CHANG_HANG]: "长航加油站",
/** 太好钉 */
[CHANNEL_PARAMS.THD]: "太好钉",
/** 海报 */
[CHANNEL_PARAMS.POSTER]: "海报",
/** 太好店小程序 */
[CHANNEL_PARAMS.THD_MINI]: "太好店小程序",
/** 官微服务大厅 */
[CHANNEL_PARAMS.FUWU]: "官微服务大厅",
/** 集团app */
[CHANNEL_PARAMS.APP]: "集团app",
/** 心意商城小程序 */
[CHANNEL_PARAMS.XINYI]: "心意商城小程序",
/** 年报活动 */
[CHANNEL_PARAMS.NIANBAO]: "年报活动",
/** 订阅号 */
[CHANNEL_PARAMS.DINGYUEHAO]: "订阅号",
}
/**
* 活动状态(前端定义)
*/
export const ACT_STATUS = {
/** 未开始 */
NOT_START: '1',
/** 进行中 */
ING: '2',
/** 已结束 */
ENDED: '3',
}
/**
* 活动状态toast
*/
export const ACT_STATUS_TOAST = {
/** 未开始 */
[ACT_STATUS.NOT_START]: '活动尚未开始',
/** 已结束 */
[ACT_STATUS.ENDED]: '活动已经结束',
}
/**
* 任务code
*/
export const TASK_CODE = {
/** 每日签到 */
SIGN: 'common_sign',
/** 浏览产品 */
BROWSE_PRODUCT: 'browse_product',
/** 1v1留资查询 */
THIRD_RETAIN: 'third_retain',
/** 浏览视频 */
BROWSE_VIDEO: 'browse_view',
/** 浏览小程序页面 */
BROWSE_MINI: 'browse_view_mini',
}
/**
* 答题配置 共5组 每组5道题
*/
export const TOPIC_LIST = [
// 第一组
[
{
topic: "端午节必吃粽子,你更偏爱哪种口味?",
options: [
"甜粽派(豆沙、蜜枣)$$$a",
"咸粽派(蛋黄肉粽、火腿)$$$a",
"冰粽等创新粽子$$$c",
]
},
{
topic: "端午节时,你希望收到什么样的祝福?",
options: [
"创意满满的搞笑段子",
"传统又温馨的话语",
"简单一句“端午快乐”"
]
},
{
topic: "如果粽子有性格,你希望它是?",
options: [
"粽子怎么可能有性格",
"热情似火的小太阳",
"古灵精怪的小精灵"
]
},
{
topic: "假如端午假期和朋友约饭,你选?",
options: [
"亲自为朋友做粽子盛情款待$$$e",
"找家网红餐厅尝鲜",
"在家等朋友投喂"
]
},
{
topic: "端午节一些地方会举办爬龙舟比赛,你想?",
options: [
"亲自下场,感受速度激情$$$b",
"岸边助威,当最强气氛组",
"线上围观,不想出门凑热闹$$$d"
]
},
],
// 第二组
[
{
topic: "当听说有的地方端午用雄黄酒在小孩额头写“王”字,你?",
options: [
"觉得很有趣,想给孩子试试$$$c",
"新奇,但不会轻易尝试",
"这操作有点迷,不太理解"
]
},
{
topic: "端午期间,你愿意花时间学编五彩绳吗?",
options: [
"必须的,编好送亲友多有面$$$a",
"可以试试,毕竟是传统文化$$$a",
"太麻烦了,直接买现成的$$$d"
]
},
{
topic: "端午节你会邀请朋友来家里吗?",
options: [
"肯定会,组织一场热闹的聚会$$$b",
"看情况,有时间就邀请$$$a",
"不会,喜欢自己过节"
]
},
{
topic: " 假如粽子会上网,它发的第一条动态会是?",
options: [
"“家人们谁懂啊,被包得好紧”",
"“今天和隔壁月饼battle了一下人气”",
"“谁来尝尝我的新馅料,绝了”"
]
},
{
topic: "对于粽子的搭配,你有研究吗?",
options: [
"会研究各种新奇搭配和茶点$$$e",
"随便搭配,能吃就行",
"没有,就正常吃粽子"
]
},
],
// 第三组
[
{
topic: "你觉得端午节吃粽子,什么最重要?",
options: [
"必须是独特的口味",
"精致好看的外形",
"亲手制作的心意"
]
},
{
topic: "端午节公司搞活动,你希望是?",
options: [
"包粽子大赛,展示自己的手艺$$$e",
"粽子创意摆盘比赛,看谁脑洞大",
"放假一天,在家躺平"
]
},
{
topic: "有些地方端午节有“躲午”的习俗,你怎么看?",
options: [
"很有意思,想了解更多",
"不太理解,感觉没必要",
"尊重习俗,但不参与"
]
},
{
topic: "假如有个粽子博物馆,你觉得里面会有?",
options: [
"从古至今各种传统粽子模具,展示历史$$$a",
"世界各地奇葩粽子的标本,大开眼界$$$c",
"休息区,逛累了可以吃粽子$$$d"
]
},
{
topic: "朋友邀请你参加端午节户外活动,你会?",
options: [
"马上答应,还主动帮忙策划$$$b",
"犹豫一下,看自己的安排",
"找借口拒绝,想在家休息"
]
},
],
// 第四组
[
{
topic: "端午节一些地区有“送时节”的习俗,你知道是指?",
options: [
"送应季礼品走亲访友",
"给亲朋好友送祝福",
"不太清楚,没听说过"
]
},
{
topic: "假如端午节有专属宠物装扮,你会给宠物?",
options: [
"精心打扮,拍照发圈$$$c",
"偶尔试试,图个乐呵",
"算了,感觉很麻烦"
]
},
{
topic: "假如粽子会唱歌,你觉得它会唱?",
options: [
"《最炫民族风》,经典永不褪色",
"《卡路里》,吃了不怕胖",
"《无所谓》,和自己的心态一样"
]
},
{
topic: "端午当天,你会选择什么活动?",
options: [
"观看传统的龙舟比赛,感受古老的竞技氛围$$$a",
"看看电视里的端午节目,轻松度过$$$d",
"参加粽子品鉴会,品尝各地特色粽子$$$e"
]
},
{
topic: "端午节在社交平台,你:",
options: [
"频繁发动态,邀请大家线下玩$$$b",
"偶尔发一条祝福动态",
"基本不看也不发动态"
]
},
],
// 第五组
[
{
topic: "端午节时,你会选择哪种方式放松?",
options: [
"泡个艾草澡,舒缓身心",
"DIY端午美食,招待好友$$$e",
"和朋友逛街喝咖啡"
]
},
{
topic: "你觉得端午节的龙舟竞渡,最吸引人的是?",
options: [
"紧张刺激的比赛氛围",
"独特的龙舟造型和装饰",
"大家齐心协力的团队精神"
]
},
{
topic: "看到商家推出的端午限定产品,你会?",
options: [
"选择具有传统特色的产品,支持传统文化$$$a",
"买一些和朋友分享,增加节日氛围$$$b",
"购买最有创意、最新奇的限定产品$$$c"
]
},
{
topic: "假如粽子拥有超能力,你希望它具备什么神奇技能?",
options: [
"能自己包自己,还能变出各种馅料",
"能瞬间变成各种新奇口味,永远吃不腻",
"不需要什么技能,随遇而安就好$$$d"
]
},
{
topic: "如果你穿越成屈原,你会?",
options: [
"举办第一届粽子品鉴大会",
"把离骚改编成说唱,宣传端午",
"先找个地方好好睡一觉,太操心了"
]
},
],
]
/**
* 人设信息
*/
export const ROLE_MAP = {
// a 传统守护者
// b 社交组局王
// c 狂热尝新党
// d 佛系随性派
// e 元气美食家
a: {
name: "传统守护者",
productName: "太保岁优保(2024版)两全保险",
productLink: "https://yun.duiba.com.cn/polaris/%E5%B2%81%E4%BC%98%E4%BF%9D.ea5556dd06efd9d929ad9d8617fa896d3164eae9.jpg",
},
b: {
name: "社交组局王",
productName: "太保蛮好人生(传世)终身寿险(分红型)",
productLink: "https://yun.duiba.com.cn/polaris/%E8%9B%AE%E5%A5%BD%E4%BA%BA%E7%94%9F%E9%95%BF%E5%9B%BE.867da0febb90ed822ee5180f0d7c216dba306b3b.jpg",
},
c: {
name: "狂热尝新党",
productName: "太保爱心保(尊享2024)重大疾病保险",
productLink: "https://yun.duiba.com.cn/polaris/%E7%88%B1%E5%BF%83%E4%BF%9D.ff0c1419a91b0be23d378eadd24e447ed621ce17.jpg",
},
d: {
name: "佛系随性派",
productName: "太保安享百万2025医疗保险(费率可调)",
productLink: "https://yun.duiba.com.cn/polaris/5%E3%80%81%E5%AE%89%E4%BA%AB2025%E9%95%BF%E5%9B%BE.c644f11434fe71c7bdead8267757240452886f5c.jpg",
},
e: {
name: "元气美食家",
productName: "金生无忧2024(成人版)保险产品计划",
productLink: "https://yun.duiba.com.cn/polaris/%E9%87%91%E7%94%9F%E6%97%A0%E5%BF%A7%EF%BC%88%E6%88%90%E4%BA%BA%EF%BC%89.46c5cd14e1c5e4bfe4cf6950bc407add9e5671ae.jpg",
},
}
\ No newline at end of file
/** @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: "活动太火爆了,奖品已抢完咯~",
100001: "登录过期啦,请重新登录哦~",
};
/**
* 统一错误处理
* @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;
}
}
}
var sdkversion_placeholder="1.26.5";function wrapPluginInitFn(e,t,a){if(t&&(e.plugin_name=t),a&&e.init){var i=e.init;e.init=function(r,n){if(wrapLogFn(r,e,t),r.readyState&&r.readyState.state>=3||!r.on)return s();function s(){i.call(e,r,n)}r.on(a,s)}}return e}function wrapLogFn(e,t,a){function i(t,i){e.logger?e.logger.msg.apply(e.logger,i).module(a+""||"").level(t).log():e.log&&e.log.apply(e,i)}t.log=function(){i("log",arguments)},t.warn=function(){i("warn",arguments)},t.error=function(){i("error",arguments)}}function createPlugin(e,t,a){return wrapPluginInitFn(e,t,a),e.plugin_version=sdkversion_placeholder,e}var page_hidden_status_refresh_time=5e3,MAX_DURATION=432e3;function PageLeave(){this.sd=null,this.start_time=+new Date,this.page_show_status=!0,this.page_hidden_status=!1,this._={},this.timer=null,this.current_page_url=document.referrer,this.url=location.href,this.title=document.title||"",this.option={},this.heartbeat_interval_time=5e3,this.heartbeat_interval_timer=null,this.page_id=null,this.storage_name="sawebjssdkpageleave",this.max_duration=MAX_DURATION}PageLeave.prototype.init=function(e,t){if(e){if(this.sd=e,this._=this.sd._,t){this.option=t;var a=t.heartbeat_interval_time;a&&(this._.isNumber(a)||this._.isNumber(1*a))&&1*a>0&&(this.heartbeat_interval_time=1e3*a);var i=t.max_duration;i&&(this._.isNumber(i)||this._.isNumber(1*i))&&1*i>0&&(this.max_duration=i)}this.page_id=Number(String(this._.getRandom()).slice(2,5)+String(this._.getRandom()).slice(2,4)+String((new Date).getTime()).slice(-4)),this.addEventListener(),!0===document.hidden?this.page_show_status=!1:this.addHeartBeatInterval(),this.log("PageLeave\u521d\u59cb\u5316\u5b8c\u6bd5")}else this.log("\u795e\u7b56JS SDK\u672a\u6210\u529f\u5f15\u5165")},PageLeave.prototype.log=function(e){this.sd&&this.sd.log(e)},PageLeave.prototype.refreshPageEndTimer=function(){var e=this;this.timer&&(clearTimeout(this.timer),this.timer=null),this.timer=setTimeout(function(){e.page_hidden_status=!1},page_hidden_status_refresh_time)},PageLeave.prototype.hiddenStatusHandler=function(){clearTimeout(this.timer),this.timer=null,this.page_hidden_status=!1},PageLeave.prototype.pageStartHandler=function(){this.start_time=+new Date,!0==!document.hidden?this.page_show_status=!0:this.page_show_status=!1,this.url=location.href,this.title=document.title},PageLeave.prototype.pageEndHandler=function(){if(!0!==this.page_hidden_status){var e=this.getPageLeaveProperties();!1===this.page_show_status&&delete e.event_duration,this.page_show_status=!1,this.page_hidden_status=!0,this.isCollectUrl(this.url)&&this.sd.track("$WebPageLeave",e),this.refreshPageEndTimer(),this.delHeartBeatData()}},PageLeave.prototype.addEventListener=function(){this.addPageStartListener(),this.addPageSwitchListener(),this.addSinglePageListener(),this.addPageEndListener()},PageLeave.prototype.addPageStartListener=function(){var e=this;"onpageshow"in window&&this._.addEvent(window,"pageshow",function(){e.pageStartHandler(),e.hiddenStatusHandler()})},PageLeave.prototype.isCollectUrl=function(e){return"function"!=typeof this.option.isCollectUrl||("string"!=typeof e||""===e||this.option.isCollectUrl(e))},PageLeave.prototype.addSinglePageListener=function(){var e=this;this.sd.ee&&this.sd.ee.spa.prepend("switch",function(t){t!==location.href&&(e.url=t,e.pageEndHandler(),e.stopHeartBeatInterval(),e.current_page_url=e.url,e.pageStartHandler(),e.hiddenStatusHandler(),e.addHeartBeatInterval())})},PageLeave.prototype.addPageEndListener=function(){var e=this;this._.each(["pagehide","beforeunload","unload"],function(t){"on"+t in window&&e._.addEvent(window,t,function(){e.pageEndHandler(),e.stopHeartBeatInterval()})})},PageLeave.prototype.addPageSwitchListener=function(){var e=this;this._.listenPageState({visible:function(){e.pageStartHandler(),e.hiddenStatusHandler(),e.addHeartBeatInterval()},hidden:function(){e.url=location.href,e.title=document.title,e.pageEndHandler(),e.stopHeartBeatInterval()}})},PageLeave.prototype.addHeartBeatInterval=function(){this._.localStorage.isSupport()&&this.startHeartBeatInterval()},PageLeave.prototype.startHeartBeatInterval=function(){var e=this;this.heartbeat_interval_timer&&this.stopHeartBeatInterval();var t=!0;this.isCollectUrl(this.url)||(t=!1),this.heartbeat_interval_timer=setInterval(function(){t&&e.saveHeartBeatData()},this.heartbeat_interval_time),t&&this.saveHeartBeatData("is_first_heartbeat"),this.reissueHeartBeatData()},PageLeave.prototype.stopHeartBeatInterval=function(){clearInterval(this.heartbeat_interval_timer),this.heartbeat_interval_timer=null},PageLeave.prototype.saveHeartBeatData=function(e){var t=this.getPageLeaveProperties(),a=new Date;t.$time=a,"is_first_heartbeat"===e&&(t.event_duration=3.14);var i=this.sd.kit.buildData({type:"track",event:"$WebPageLeave",properties:t});i.heartbeat_interval_time=this.heartbeat_interval_time,this.sd.store.saveObjectVal(this.storage_name+"-"+this.page_id,i)},PageLeave.prototype.delHeartBeatData=function(e){this._.localStorage.isSupport()&&this._.localStorage.remove(e||this.storage_name+"-"+this.page_id)},PageLeave.prototype.reissueHeartBeatData=function(){for(var e=window.localStorage.length-1;e>=0;e--){var t=window.localStorage.key(e);if(t&&t!==this.storage_name+"-"+this.page_id&&0===t.indexOf(this.storage_name+"-")){var a=this.sd.store.readObjectVal(t);this._.isObject(a)&&1*new Date-a.time>a.heartbeat_interval_time+5e3&&(delete a.heartbeat_interval_time,a._flush_time=(new Date).getTime(),this.sd.kit.sendData(a),this.delHeartBeatData(t))}}},PageLeave.prototype.getPageLeaveProperties=function(){var e=(+new Date-this.start_time)/1e3;(isNaN(e)||e<0||e>this.max_duration)&&(e=0),e=Number(e.toFixed(3));var t=this._.getReferrer(this.current_page_url),a=document.documentElement&&document.documentElement.scrollTop||window.pageYOffset||document.body&&document.body.scrollTop||0;a=Math.round(a)||0;var i={$title:this.title,$url:this._.getURL(this.url),$url_path:this._.getURLPath(this._.URL(this.url).pathname),$referrer_host:t?this._.getHostname(t):"",$referrer:t,$viewport_position:a};return 0!==e&&(i.event_duration=e),i=this._.extend(i,this.option.custom_props)};var pageLeave=new PageLeave,index=createPlugin(pageLeave,"PageLeave","sdkReady");export default index;
\ No newline at end of file
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;
}
}
/**
* 添加样式到<head>
* @param {string} styleInnerHTML
*/
function AddStyleToHead(styleInnerHTML) {
const style = document.createElement('style');
style.id = Math.random();
style.innerHTML = styleInnerHTML;
document.getElementsByTagName('head')[0].appendChild(style);
}
/**
* 加载一个字体
* @param {string} url 地址
*/
export function loadOneFont(url, fontName) {
return new Promise((resolve) => {
const link = document.createElement("link");
link.rel = "preload";
link.href = url;
link.as = "font";
// link.type = 'font/ttf';
link.crossOrigin = "anonymous";
link.onload = () => {
// const strArr = url.split('/');
// const fontName = strArr[strArr.length - 1];
AddStyleToHead(`
@font-face {
font-family: '${fontName}';
src: url('${url}')
}
`);
resolve(url);
};
link.onerror = () => resolve(false);
document.head.appendChild(link);
});
}
/**
* 加载一张图片
* @param {string} url 地址
*/
function loadOneImg(url) {
const isWebp = localStorage.isWebp;
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 + (isWebp ? '?x-oss-process=image/format,webp' : '');
})
}
/**
* 加载一个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 { objectAssign } from '@spark/utils';
import sensors from 'sa-sdk-javascript';
import { ACTIVITY_SOURCE_CHANNEL, SYS_SOURCE_CHANNEL } from './constants';
import pageleave from './pageleave';
export const initSensors = (uid, openId, newEmpNo, friendOpenId) => {
sensors.init({
server_url: CFG.sensorUrl,
is_track_single_page: true,
show_log: true,
heatmap: {
clickmap: 'default',
scroll_notice_map: 'not_collect',
collect_tags: {
div: true
},
},
});
sensors.login(uid);
const shareTypeMap = {
1: "客户邀请",
2: "业务员邀请"
}
const skinId = getUrlParam("skinId");
// 本次没区分业务员/客户 就null
const shareType = !!skinId ? shareTypeMap[skinId] : null;
const publicObjec = objectAssign({
currentUrl: location.href,
referrer: document.referrer,
activityId: "25DragonBoat",
unionid: uid,
openid: openId,
source: CFG.channel,
systemName: '寿险2013版微信公众平台',
activityName: CFG.activityName,
c_date: "2025/5/19-2025/6/13",
empNp: newEmpNo, // 用户实际绑定业务员工号(与转发无关)
activitySource: ACTIVITY_SOURCE_CHANNEL[CFG.channel] || "",
sysSourceName: SYS_SOURCE_CHANNEL[CFG.channel] || "",
friendDistinctId: getUrlParam("uid") || "", // 转发人unionId
shareMark: !!getUrlParam("uid") ? "是" : "否",
shareType: shareType,
friendEmpNo: getUrlParam("shareNewEmpNo"), // 邀请人的业务员工号(A转发给B,传A的业务员工号)
friendOpenId: getUrlParam("shareOpenId"), // 邀请人的openId
empno: CFG.empno
}, window['__tbSensorsObj__']);
sensors.registerPage(publicObjec);
sensors.quick('autoTrack');
};
export const pageLeave = (pagaName, duration) => {
sensors.use(pageleave, {
custom_props: {
pagaName
},
heartbeat_intervaltime: duration,
max_duration: duration * 24 * 60 * 60,
isCollectUrl: function (url) {
return true
}
})
}
/**
* 页面曝光埋点
* @param {string} eventName
* @param {{pageName:string,activityName:string,prize:string}} option
*/
export const sensorMdExpouse = (eventName, option) => {
sensors.track(eventName, option);
};
/**
* 页面点击埋点
* @param {string} eventName
* @param {{pageName:string,activityName:string,buttonName:string,prize:string}} option
*/
export const sensorMdClick = (eventName, option) => {
sensors.track(eventName, option);
};
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";
/** 判断h5/小程序 */
export const isWechatMini = async () => {
const ua = window.navigator.userAgent.toLowerCase();
let env = '';
if (ua.match(/MicroMessenger/i) == "micromessenger") {
env = await new Promise((r) => {
try {
if (window.navigator.userAgent.toLocaleLowerCase().indexOf('miniprogram') > -1) {
r('mini');
}
wx.miniProgram.getEnv((res) => {
if (res.miniprogram) {
// 在微信小程序中打开
r('mini');
}
r('wechat');
});
} catch (error) {
r('wechat');
}
});
}
return env;
};
export function getQueryString(params){
const paramArr = [];
for (const key in params) {
paramArr.push(key + "=" + params[key]);
}
return paramArr.join("&");
}
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);
}
};
};
/**
* @description: 支持异步函数的节流,防止接口时间太长击穿防连点
* @return {Function}
* @param fun
* @param delay
*/
export const _asyncThrottle = (fun, delay = 2000) => {
let last, deferTimer;
let canClick = true;
return function () {
const now = Date.now();
if (!canClick) return;
if (last && now < last + delay) {
// clearTimeout(deferTimer);
// deferTimer = setTimeout(() => {
// last = now;
// }, delay);
} else {
last = now;
const ps = fun.apply(this, arguments);
if (ps instanceof Promise) {
canClick = false;
ps.finally(() => {
canClick = true;
});
}
}
};
};
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
}
export const getCustomShareId = () => {
return getUrlParam('id')
const matched = location.href.match(/\/share\?id=(.*)/);
const shareId = matched ? matched && matched[1] : '';
console.log('自定义活动页id', shareId);
return shareId;
};
/** 跳转 */
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