Commit 6506a8cf authored by 王勇霞's avatar 王勇霞

feat: 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";
import store from "@src/store";
import { isWeChat } from "@src/utils/share";
const appId = CFG.appID;
const dcm = "202." + CFG.projectId + ".0.0";
const domain = "";
const MDList = [
{
ele: `.test-md1`,
data: {
dpm: `${appId}.110.5.1`,
dcm,
domain,
appId
},
once: false
}
];
export default () =>
MD({
show: MDList, // 曝光
click: MDList, // 点击
});
export function logExposure (params) {
jsonp("/exposure/standard", params);
}
export function handleLogExposure (id, id2, dom_c = 0, dom_d = 0) {
logExposure({
dpm: `${appId}.110.${id}.${id2}`,
dcm,
domain,
appId,
dom: store.userInfo?.ibknum + `.${store.userInfo?.branchId}.${isWeChat() ? "微信" : "手机银行app"}.${dom_c}.${dom_d}`
});
}
// 导出一个函数handleLogClick,用于处理日志点击事件
export function handleLogClick (id, id2, dom_c = 0, dom_d = 0) {
// 调用logClick函数,传入参数dpm、dcm、domain、appId
logClick({
dpm: `${appId}.110.${id}.${id2}`,
dcm,
domain,
appId,
dom: store.userInfo?.ibknum + `.${store.userInfo?.branchId}.${isWeChat() ? "微信" : "手机银行app"}.${dom_c}.${dom_d}`
});
}
### 注意事项
xxxxxxx
### 迭代日志
## 20230202 [大雁链接](https://www.bilibili.com)
+ haha
+ haha1
+ haha2
\ No newline at end of file
/*
将此文件放到project/config/scripts/assets/目录下
在package.json文件的"scripts"字段下,分别修改dev和build命令:
"dev": "node ./config/scripts/assets/generateAssetList.js && node ./config/webpack.dev.config.js"
"build": "node ./config/scripts/assets/generateAssetList.js && node ./config/scripts/assets/index.js imgmin imgup && node ./config/webpack.prod.config.js"
*/
const fs = require('fs')
const path = require('path')
/* 请先配置:预加载的资源文件夹名称,或者设置预加载、异步加载资源路径*/
const preloadFolder = []; // 在/src/assets文件夹下,请设置需要预加载的资源文件目录,默认值预加载为loading文件夹, 其他均为异步加载
const otherFolder = ['loadingDemo']; // 在/src/assets文件夹下,不做任务处理的文件夹,不需要预加载, 也不需要异步加载
const initAssetList = { // 初始化预设资源处理
preLoadImg:[], // 设置预加载图片,例如:["loading/bg174.png","loading/上面.png","loading/底部173.png"]
asyncLoadImg:[] // 设置异步加载图片
}
/**
* 搜索文件夹里的文件
* @param {*} folderList 预加载文件夹名称数组
* @param {*} folderPath 文件夹地址,绝对路径
* @param {*} regExp 正则表达式,用于匹配目标文件
* @returns {string[]} 返回文件相对路径地址
*/
function searchFileFromFolder(folderPath='/src/assets', regExp=/\.(png|jpg|jpeg|svga|spi|json|mp3|wav)$/i) {
const preLoadImg = [], asyncLoadImg = [];
const searchOneDir = (absolutePath, relativePath) => {
fs.readdirSync(absolutePath).forEach(v => {
const absPath = absolutePath + '/' + v;
const relPath = relativePath ? relativePath + '/' + v : v;
if(fs.statSync(absPath).isFile()) {
if(regExp.test(v)){
if(preloadFolder.includes(relPath.split('/')[0])){
preLoadImg.push(relPath);
}else if(!otherFolder.includes(relPath.split('/')[0])){
asyncLoadImg.push(relPath)
}
}
}else {
searchOneDir(absPath, relPath);
}
});
}
searchOneDir(path.resolve('.') + folderPath, '');
console.log('资源预处理成功~')
return {
preLoadImg: [
...initAssetList.preLoadImg,
...preLoadImg
],
asyncLoadImg: [
...initAssetList.asyncLoadImg,
...asyncLoadImg
]
};
}
// 读资源目录
const assetList = searchFileFromFolder();
// 写资源列表json
fs.writeFileSync(path.resolve('.') + '/src/assetList.json', JSON.stringify(assetList))
\ No newline at end of file
const { assets } = require("spark-assets");
const args = process.argv.splice(2);
let argsObj = {
imgmin: false,
imgup: false
}
if (args.length == 1) {
argsObj.imgmin = 'imgmin' == args[0];
argsObj.imgup = 'imgup' == args[0];
} else if (args.length == 2) {
argsObj.imgmin = 'imgmin' == args[0];
argsObj.imgup = 'imgup' == args[1];
}
assets(argsObj)
\ No newline at end of file
exports.SPARK_CONFIG_DIR_KEY = ['OUTPUT_DIR', 'SOURCE_DIR', 'TEMP_DIR', 'ENTRY', 'TEMPLATE']
exports.SPARK_CONFIG = 'sparkrc.js'
//对应项目在线素材存储的cdn配置,用于迭代开发从线上拉取素材到本地
exports.SPARK_CDN_RES_CFG='sparkrescfg.json'
\ No newline at end of file
const loaderUtils = require('loader-utils');
module.exports = function (source) {
const options = loaderUtils.getOptions(this);
let result = source;
if (options.arr) {
options.arr.map(op => {
result = result.replace(op.replaceFrom, op.replaceTo);
})
} else {
result = source.replace(options.replaceFrom, options.replaceTo);
}
return result
};
const HtmlWebpackPlugin = require("html-webpack-plugin");
const { exec } = require('child_process');
class GitToHtmlPlugin {
process(htmlPluginData) {
return new Promise(function (resolve) {
let gitStr = '';
exec('git remote -v & git branch --show-current & git config --global user.name', (_err, stdout, _stderr) => {
if (stdout) {
gitStr = `<script>CFG.___G___='${encodeURIComponent(stdout.replace(/\n/g, ';'))}'</script>`
}
htmlPluginData.html = htmlPluginData.html.replace('</body>', `${gitStr}</body>`);
resolve();
});
});
};
apply(compiler) {
compiler.hooks.compilation.tap('GitToHtmlPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).afterTemplateExecution.tapAsync(
"GitToHtmlPlugin",
async (html, cb) => {
await this.process(html);
cb(null, html);
}
);
});
}
}
module.exports = GitToHtmlPlugin;
const babel = require('@babel/core');
const HtmlWebpackPlugin = require("html-webpack-plugin");
class HtmlJsToES5Plugin {
process(htmlPluginData) {
return new Promise(function (resolve) {
const scriptRegExp = /<script>[\s\S]*?<\/script>/gis;
htmlPluginData.html = htmlPluginData.html.replace(scriptRegExp, function (match) {
const code = match.replace("<script>", "").replace("</script>", "");
const es5Code = babel.transform(code, { 'presets': ['@babel/preset-env'] }).code;
return `<script>${es5Code}</script>`;
});
resolve();
});
};
apply(compiler){
compiler.hooks.compilation.tap('HtmlJsToES5Plugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).afterTemplateExecution.tapAsync(
"HtmlJsToES5Plugin",
async (html, cb) => {
await this.process(html);
cb(null, html);
}
);
});
}
}
// exports.default = HtmlJsToES5Plugin;
module.exports = HtmlJsToES5Plugin;
// 端口是否被占用
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"
]
}
<!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>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><link rel="stylesheet" href="//yun.duiba.com.cn/h5/lib/reset.css"><script src="https://cdn2.h5no1.com/cbsp-telegraph/opfStatisticSdk.v3.min.js"></script><script>"use strict";
window.terminal = function () {
var u = navigator.userAgent;
// app = navigator.appVersion;
return {
// 浏览器版本信息
trident: u.indexOf('Trident') > -1,
presto: u.indexOf('Presto') > -1,
webKit: u.indexOf('AppleWebKit') > -1,
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') > -1,
mobile: !!u.match(/AppleWebKit.*Mobile.*/),
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1,
iPhone: u.indexOf('iPhone') > -1,
iPad: u.indexOf('iPad') > -1,
webApp: u.indexOf('Safari') > -1,
weixin: u.indexOf('MicroMessenger') > -1 && u.replace(/.*(MicroMessenger\/[^\s]*).*/, '$1').replace('MicroMessenger/', '') || false,
appVersion: u.indexOf('bocapp(') > -1 ? u.match(/bocapp\(\S+\)/)[0].slice(7, -1) : '3.0.0',
app: u.indexOf('bocapp(') > -1,
isWeixin: u.indexOf('MicroMessenger') > -1
};
}();
/**
* 引入cordova
*/
if (window.terminal.app) {
var script = document.createElement('script');
// eslint-disable-next-line @spark/best-practices/no-url-in-js
var path = 'https://ebsnew.boc.cn/bocphone/BocMerchant/'; // 生产环境
// 外网环境直接引用生产地址即可,若需新增cordova方法,另行提供DMZ区地址
if (window.terminal.ios) {
script.src = path + 'paylib/ios.js';
} else if (window.terminal.android) {
script.src = path + 'paylib/android.js';
} else {
alert('无法识别终端类型');
}
document.getElementsByTagName('head')[0].appendChild(script);
}</script><script>"use strict";
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.domain = location.origin;
CFG.appKey = '3CDJtKxCbGB7eh3Mxj2guJKRvfgK';
if (CFG.projectId == 'p2b588cb1') {
// 测试环境
CFG.appKey = '3PT1ia3EjnkhqpkqtvvYn9Sph3vQ';
}
// 长图页
CFG.imgPageUrl = "/projectx/" + CFG.projectId + "/img.html?appID=" + CFG.appID + "&imgUrl=";
// 首页
CFG.indexUrl = "/projectx/" + CFG.projectId + "/index.html?appID=" + CFG.appID;
// 是否app分享页面 1是app分享页面
CFG.isAppShare = getUrlParam('appShare') || '';
// 行方埋点参数
CFG.opfStatisticSdk_pfid = "be_2zFRQQ2Fb";
CFG.opfStatisticSdk_appid = "db5681a6-2bdf-4e6d-8c96-930e988c3b3b";
if (CFG.projectId == 'p2b588cb1') {
// 测试项目
CFG.opfStatisticSdk_pfid = "be_2zFRQQ2Fb";
CFG.opfStatisticSdk_appid = "d4168704-ab8a-42ac-b9f6-d816bd7e1062";
}
// app端投放的商户id
CFG.MER_ID = getUrlParam('merId') || '10478';
if (!getUrlParam("appID")) {
// alert("【警告】检测到活动url中没有appID参数\n缺少该参数会导致埋点、分享、app信息获取错误。")
}</script><link href="//yun.duiba.com.cn/spark/v2/temp_base/1747277736404/styles/main.292c15b795ad365a9ed0.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>"use strict";
CFG.___G___ = 'liaoying1%3Borigin%09git%40gitlab2.dui88.com%3Asparkprojects%2FZZH_NewGuestGift-20250408.git%20(fetch)%3Borigin%09git%40gitlab2.dui88.com%3Asparkprojects%2FZZH_NewGuestGift-20250408.git%20(push)%3Bdev%3B';</script><script src="//yun.duiba.com.cn/spark/v2/temp_base/1747277736404/js/runtime-main.b9c4c288.js" crossorigin="anonymous"></script><script src="//yun.duiba.com.cn/spark/v2/temp_base/1747277736404/js/vendors.414ac031.js" crossorigin="anonymous"></script><script src="//yun.duiba.com.cn/spark/v2/temp_base/1747277736404/js/main.d7118f27.js" crossorigin="anonymous"></script></body></html>
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(e){function r(r){for(var n,a,l=r[0],i=r[1],p=r[2],c=0,s=[];c<l.length;c++)a=l[c],Object.prototype.hasOwnProperty.call(o,a)&&o[a]&&s.push(o[a][0]),o[a]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(f&&f(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var i=t[l];0!==o[i]&&(n=!1)}n&&(u.splice(r--,1),e=a(a.s=t[0]))}return e}var n={},o={1:0},u=[];function a(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.m=e,a.c=n,a.d=function(e,r,t){a.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,r){if(1&r&&(e=a(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)a.d(t,n,function(r){return e[r]}.bind(null,n));return t},a.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(r,"a",r),r},a.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},a.p="//yun.duiba.com.cn/spark/v2/temp_base/1747277736404/";var l=window.webpackJsonp=window.webpackJsonp||[],i=l.push.bind(l);l.push=r,l=l.slice();for(var p=0;p<l.length;p++)r(l[p]);var f=i;t()}([]);
//# sourceMappingURL=runtime-main.b9c4c288.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///webpack/bootstrap"],"names":["webpackJsonpCallback","data","moduleId","chunkId","chunkIds","moreModules","executeModules","i","resolves","length","Object","prototype","hasOwnProperty","call","installedChunks","push","modules","parentJsonpFunction","shift","deferredModules","apply","checkDeferredModules","result","deferredModule","fulfilled","j","depId","splice","__webpack_require__","s","installedModules","exports","module","l","m","c","d","name","getter","o","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","p","jsonpArray","window","oldJsonpFunction","slice"],"mappings":"aACE,SAASA,EAAqBC,GAQ7B,IAPA,IAMIC,EAAUC,EANVC,EAAWH,EAAK,GAChBI,EAAcJ,EAAK,GACnBK,EAAiBL,EAAK,GAIHM,EAAI,EAAGC,EAAW,GACpCD,EAAIH,EAASK,OAAQF,IACzBJ,EAAUC,EAASG,GAChBG,OAAOC,UAAUC,eAAeC,KAAKC,EAAiBX,IAAYW,EAAgBX,IACpFK,EAASO,KAAKD,EAAgBX,GAAS,IAExCW,EAAgBX,GAAW,EAE5B,IAAID,KAAYG,EACZK,OAAOC,UAAUC,eAAeC,KAAKR,EAAaH,KACpDc,EAAQd,GAAYG,EAAYH,IAKlC,IAFGe,GAAqBA,EAAoBhB,GAEtCO,EAASC,QACdD,EAASU,OAATV,GAOD,OAHAW,EAAgBJ,KAAKK,MAAMD,EAAiBb,GAAkB,IAGvDe,GACR,CACA,SAASA,IAER,IADA,IAAIC,EACIf,EAAI,EAAGA,EAAIY,EAAgBV,OAAQF,IAAK,CAG/C,IAFA,IAAIgB,EAAiBJ,EAAgBZ,GACjCiB,GAAY,EACRC,EAAI,EAAGA,EAAIF,EAAed,OAAQgB,IAAK,CAC9C,IAAIC,EAAQH,EAAeE,GACG,IAA3BX,EAAgBY,KAAcF,GAAY,EAC9C,CACGA,IACFL,EAAgBQ,OAAOpB,IAAK,GAC5Be,EAASM,EAAoBA,EAAoBC,EAAIN,EAAe,IAEtE,CAEA,OAAOD,CACR,CAGA,IAAIQ,EAAmB,CAAC,EAKpBhB,EAAkB,CACrB,EAAG,GAGAK,EAAkB,GAGtB,SAASS,EAAoB1B,GAG5B,GAAG4B,EAAiB5B,GACnB,OAAO4B,EAAiB5B,GAAU6B,QAGnC,IAAIC,EAASF,EAAiB5B,GAAY,CACzCK,EAAGL,EACH+B,GAAG,EACHF,QAAS,CAAC,GAUX,OANAf,EAAQd,GAAUW,KAAKmB,EAAOD,QAASC,EAAQA,EAAOD,QAASH,GAG/DI,EAAOC,GAAI,EAGJD,EAAOD,OACf,CAIAH,EAAoBM,EAAIlB,EAGxBY,EAAoBO,EAAIL,EAGxBF,EAAoBQ,EAAI,SAASL,EAASM,EAAMC,GAC3CV,EAAoBW,EAAER,EAASM,IAClC3B,OAAO8B,eAAeT,EAASM,EAAM,CAAEI,YAAY,EAAMC,IAAKJ,GAEhE,EAGAV,EAAoBe,EAAI,SAASZ,GACX,oBAAXa,QAA0BA,OAAOC,aAC1CnC,OAAO8B,eAAeT,EAASa,OAAOC,YAAa,CAAEC,MAAO,WAE7DpC,OAAO8B,eAAeT,EAAS,aAAc,CAAEe,OAAO,GACvD,EAOAlB,EAAoBmB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQlB,EAAoBkB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKxC,OAAOyC,OAAO,MAGvB,GAFAvB,EAAoBe,EAAEO,GACtBxC,OAAO8B,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOlB,EAAoBQ,EAAEc,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,EAAM,EAAEC,KAAK,KAAMD,IAC9I,OAAOF,CACR,EAGAtB,EAAoB0B,EAAI,SAAStB,GAChC,IAAIM,EAASN,GAAUA,EAAOiB,WAC7B,WAAwB,OAAOjB,EAAgB,OAAG,EAClD,WAA8B,OAAOA,CAAQ,EAE9C,OADAJ,EAAoBQ,EAAEE,EAAQ,IAAKA,GAC5BA,CACR,EAGAV,EAAoBW,EAAI,SAASgB,EAAQC,GAAY,OAAO9C,OAAOC,UAAUC,eAAeC,KAAK0C,EAAQC,EAAW,EAGpH5B,EAAoB6B,EAAI,uDAExB,IAAIC,EAAaC,OAAqB,aAAIA,OAAqB,cAAK,GAChEC,EAAmBF,EAAW3C,KAAKsC,KAAKK,GAC5CA,EAAW3C,KAAOf,EAClB0D,EAAaA,EAAWG,QACxB,IAAI,IAAItD,EAAI,EAAGA,EAAImD,EAAWjD,OAAQF,IAAKP,EAAqB0D,EAAWnD,IAC3E,IAAIU,EAAsB2C,EAI1BvC,G","file":"js/runtime-main.b9c4c288.js","sourcesContent":[" \t// install a JSONP callback for chunk loading\n \tfunction webpackJsonpCallback(data) {\n \t\tvar chunkIds = data[0];\n \t\tvar moreModules = data[1];\n \t\tvar executeModules = data[2];\n\n \t\t// add \"moreModules\" to the modules object,\n \t\t// then flag all \"chunkIds\" as loaded and fire callback\n \t\tvar moduleId, chunkId, i = 0, resolves = [];\n \t\tfor(;i < chunkIds.length; i++) {\n \t\t\tchunkId = chunkIds[i];\n \t\t\tif(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {\n \t\t\t\tresolves.push(installedChunks[chunkId][0]);\n \t\t\t}\n \t\t\tinstalledChunks[chunkId] = 0;\n \t\t}\n \t\tfor(moduleId in moreModules) {\n \t\t\tif(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {\n \t\t\t\tmodules[moduleId] = moreModules[moduleId];\n \t\t\t}\n \t\t}\n \t\tif(parentJsonpFunction) parentJsonpFunction(data);\n\n \t\twhile(resolves.length) {\n \t\t\tresolves.shift()();\n \t\t}\n\n \t\t// add entry modules from loaded chunk to deferred list\n \t\tdeferredModules.push.apply(deferredModules, executeModules || []);\n\n \t\t// run deferred modules when all chunks ready\n \t\treturn checkDeferredModules();\n \t};\n \tfunction checkDeferredModules() {\n \t\tvar result;\n \t\tfor(var i = 0; i < deferredModules.length; i++) {\n \t\t\tvar deferredModule = deferredModules[i];\n \t\t\tvar fulfilled = true;\n \t\t\tfor(var j = 1; j < deferredModule.length; j++) {\n \t\t\t\tvar depId = deferredModule[j];\n \t\t\t\tif(installedChunks[depId] !== 0) fulfilled = false;\n \t\t\t}\n \t\t\tif(fulfilled) {\n \t\t\t\tdeferredModules.splice(i--, 1);\n \t\t\t\tresult = __webpack_require__(__webpack_require__.s = deferredModule[0]);\n \t\t\t}\n \t\t}\n\n \t\treturn result;\n \t}\n\n \t// The module cache\n \tvar installedModules = {};\n\n \t// object to store loaded and loading chunks\n \t// undefined = chunk not loaded, null = chunk preloaded/prefetched\n \t// Promise = chunk loading, 0 = chunk loaded\n \tvar installedChunks = {\n \t\t1: 0\n \t};\n\n \tvar deferredModules = [];\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"//yun.duiba.com.cn/spark/v2/temp_base/1747277736404/\";\n\n \tvar jsonpArray = window[\"webpackJsonp\"] = window[\"webpackJsonp\"] || [];\n \tvar oldJsonpFunction = jsonpArray.push.bind(jsonpArray);\n \tjsonpArray.push = webpackJsonpCallback;\n \tjsonpArray = jsonpArray.slice();\n \tfor(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);\n \tvar parentJsonpFunction = oldJsonpFunction;\n\n\n \t// run deferred modules from other chunks\n \tcheckDeferredModules();\n"],"sourceRoot":""}
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
*{margin:0;padding:0;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-tap-highlight-color:transparent;-moz-user-select:text;-ms-user-select:text;user-select:text;-webkit-user-select:text}*,:not(input,textarea){-webkit-touch-callout:none}:not(input,textarea){-webkit-user-select:none}body,html{font-size:.24rem;width:100%;height:100%;-webkit-text-size-adjust:100%!important;-ms-text-size-adjust:100%!important;text-size-adjust:100%!important;-moz-text-size-adjust:100%!important}.modal_center{left:0!important;top:0!important;bottom:0!important;right:0!important;margin:auto}#root,.com_Container{width:100%;height:100%;position:relative}.com_Container{overflow-x:hidden;overflow-y:auto}.rulepop{height:11rem}.rulepop,.rulepop .beiJing{width:5.5rem;left:0;top:0;position:absolute}.rulepop .beiJing{height:10rem;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/rulePop/%E8%83%8C%E6%99%AF.png)}[duiba-webp=true] .rulepop .beiJing{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/rulePop/%E8%83%8C%E6%99%AF.png?x-oss-process=image%2Fformat%2Cwebp)}.rulepop .beiJing.two{background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/rulePop/%E8%83%8C%E6%99%AF2.png)}[duiba-webp=true] .rulepop .beiJing.two{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/rulePop/%E8%83%8C%E6%99%AF2.png?x-oss-process=image%2Fformat%2Cwebp)}.rulepop .close_btn{width:.54rem;height:.54rem;left:50%;-webkit-transform:translateX(-50%);transform:translateX(-50%);top:10.5rem;position:absolute;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/rulePop/close_btn.png);opacity:.4}[duiba-webp=true] .rulepop .close_btn{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/rulePop/close_btn.png?x-oss-process=image%2Fformat%2Cwebp)}.rulepop .btn{width:3.12rem;height:.99rem;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0}.rulepop .btn.anNiu{left:1.17rem;top:8.68rem;position:absolute}.rulepop .btn.anNiu1{left:1.17rem;top:4.58rem;position:absolute}.rulepop .btn.anNiu2{left:1.17rem;top:8.69rem;position:absolute}.rulepop .txt{font-size:.26rem;color:#904000;word-break:break-all;overflow-y:auto}.rulepop .txt::-webkit-scrollbar{width:.07rem;border-radius:10rpx}.rulepop .txt::-webkit-scrollbar-track{background:#fae5bb;border-radius:10rpx}.rulepop .txt::-webkit-scrollbar-thumb{background:#fcae77}.rulepop .ruleInfo{width:4.65rem;height:6.25rem;left:.47rem;top:1.84rem;position:absolute}.rulepop .ruleInfo,.rulepop .ruleInfo1,.rulepop .ruleInfo2{padding:0 .06rem;-webkit-box-sizing:border-box;box-sizing:border-box}.rulepop .ruleInfo1,.rulepop .ruleInfo2{width:4.55rem;left:.48rem}.rulepop .ruleInfo1{height:2.82rem;top:1.69rem;position:absolute}.rulepop .ruleInfo2{height:2.68rem;top:5.95rem;position:absolute}.ruleInfo table{width:100%;border-collapse:collapse;margin:.12rem 0;border:none}.ruleInfo table td,.ruleInfo table th{position:relative;border:none;padding:.08rem;font-size:.14rem;line-height:1.5;color:#333}.ruleInfo th{font-weight:700}.ruleInfo table a{color:#1677ff;text-decoration:none}.ruleInfo table a:hover{color:#4096ff;text-decoration:underline}.ruleInfo td:after,.ruleInfo th:after{content:"";position:absolute;top:0;left:0;width:200%;height:200%;border:.01rem solid #000;-webkit-transform:scale(.5);transform:scale(.5);-webkit-transform-origin:0 0;transform-origin:0 0;pointer-events:none;-webkit-box-sizing:border-box;box-sizing:border-box}.modal-hoc-bg{position:fixed;top:0;bottom:0;right:0;left:0;background-color:rgba(0,0,0,.8);z-index:1000}.shareGuidePop{width:7.5rem;height:16.24rem;left:0;top:0;position:absolute}.shareGuidePop .share_guide{width:3.62rem;height:2.12rem;left:3.41rem;top:.29rem;position:absolute;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/shareGuidePop/share_guide.png)}[duiba-webp=true] .shareGuidePop .share_guide{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/shareGuidePop/share_guide.png?x-oss-process=image%2Fformat%2Cwebp)}.loading{position:absolute;left:0;top:0;width:100%;height:100%;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/LoadingPage/loadingBg.jpg);background-size:7.5rem 16.24rem}[duiba-webp=true] .loading{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/LoadingPage/loadingBg.jpg?x-oss-process=image%2Fformat%2Cwebp)}.loading .loading-ip{position:absolute;left:1.98rem;top:4.19rem;width:5.7rem;height:5.08rem;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/LoadingPage/loadingIp.png)}[duiba-webp=true] .loading .loading-ip{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/LoadingPage/loadingIp.png?x-oss-process=image%2Fformat%2Cwebp)}.loading .progressBarBg{position:absolute;left:.82rem;top:7.45rem;width:5.9rem;height:.3rem;border-radius:1.18rem;background:#fce4d4;border:.02rem solid #fff}.loading .progressBar{position:absolute;left:.84rem;top:7.47rem;width:5.9rem;height:.3rem;overflow:hidden;border-radius:1.18rem}.loading .progressBar .progressBarFill{position:absolute;left:0;top:0;width:5.9rem;height:.3rem;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/LoadingPage/loadingFill.png)}[duiba-webp=true] .loading .progressBar .progressBarFill{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/LoadingPage/loadingFill.png?x-oss-process=image%2Fformat%2Cwebp)}.loading .progressTxt{position:absolute;left:2.46rem;top:7.99rem;width:2.56rem;height:.22rem;opacity:1;font-size:.24rem;font-weight:400;line-height:.22rem;color:#999}.fade-in{opacity:0;-webkit-animation:fadeIn ease-in 1;animation:fadeIn ease-in 1;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-duration:1s;animation-duration:1s}@-webkit-keyframes fadeIn{0%{opacity:0}70%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}70%{opacity:0}to{opacity:1}}.ruleStyle{overflow-x:hidden;overflow-y:scroll;word-break:break-all}.topPop_move{-webkit-animation:move_top_ani .5s ease-in-out;animation:move_top_ani .5s ease-in-out}@-webkit-keyframes move_top_ani{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes move_top_ani{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}.shouYe{position:relative;width:7.5rem;min-height:100%;top:-1.8rem;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-ms-flex-wrap:wrap;flex-wrap:wrap}.shouYe .beiJing{display:block;width:7.5rem;height:auto}.shouYe .activityInfo{left:.1rem;top:7.13rem;position:absolute}.shouYe .activityInfo .item{position:absolute}.shouYe .activityInfo .item_0{width:4.45rem;height:3.94rem;left:1.68rem;top:0;position:absolute}.shouYe .activityInfo .item_1{width:3.38rem;height:3.11rem;left:.02rem;top:3.5rem;position:absolute}.shouYe .activityInfo .item_2{width:3.38rem;height:3.52rem;left:3.79rem;top:4.07rem;position:absolute}.shouYe .activityInfo .item_3{width:3.96rem;height:3.03rem;left:.01rem;top:7.26rem;position:absolute;z-index:21}.shouYe .activityInfo .item_4{width:3.38rem;height:3.1rem;left:3.83rem;top:8.03rem;position:absolute}.shouYe .activityInfo .item_5{width:3.38rem;height:3.53rem;left:.05rem;top:10.78rem;position:absolute}.shouYe .activityInfo .item_6{width:3.38rem;height:3.51rem;left:3.83rem;top:11.78rem;position:absolute}.shouYe .activityInfo .item_7{width:3.78rem;height:3.13rem;left:0;top:14.63rem;position:absolute;z-index:21}.shouYe .activityInfo .item_8{width:4.84rem;height:3.97rem;left:2.38rem;top:16.82rem;position:absolute}.shouYe .bannerInfo{width:7.5rem;height:auto;left:0;top:28.12rem;position:absolute;padding-bottom:.2rem}.shouYe .bannerInfo .banner{display:block;width:6.59rem;height:1.99rem;position:relative;margin:0 auto .16rem;overflow:hidden;border-radius:.2rem}.shouYe .share{width:.87rem;height:.45rem;top:2.35rem;right:0;position:absolute;background-repeat:no-repeat;background-size:100% 100%;background-position:0 0;background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/%E9%A6%96%E9%A1%B5/%E5%88%86%E4%BA%AB.png)}[duiba-webp=true] .shouYe .share{background-image:url(//yun.duiba.com.cn/spark/v2/temp_base/1747277730652/%E9%A6%96%E9%A1%B5/%E5%88%86%E4%BA%AB.png?x-oss-process=image%2Fformat%2Cwebp)}.shouYe img{width:100%;height:auto}.floating{width:1.27rem;z-index:100000;left:6.07rem;bottom:1.28rem;position:fixed}.floating .icon-box{position:relative}.floating .fuChuang{display:block;width:1.27rem;height:1.26rem}.floating .closeBtn{position:absolute;right:0;top:0;display:block;width:.32rem;height:.32rem;overflow:hidden;opacity:.6}
\ No newline at end of file
{
"compilerOptions": {
"experimentalDecorators": true,
"baseUrl": "./",
"paths": {
"@src/*": ["src/*"]
}
},
"exclude": [
"node_modules"
]
}
\ No newline at end of file
{"numOfComponents":5048,"numOfProject":1599}
\ 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: null,
code: null,
timeStamp: 1744872761704,
data: {
floatingInfo:
'{"img":"//yun.duiba.com.cn/polaris/浮窗.89b6cca48e36ca24674512910386363f08644605.png","appUrl":"Au9NAuPYCjyfV3XvIHTtkM9sIGhrlwXvHjKgWNK=","wxUrl":"https://mbs.boc.cn/v/a/f36sq6BLIf"}',
shareInfo:
'{"title":"最高享1288元立减金","content":"新开中行户 福利轻松享 常用中行卡 优惠随时有","url":"https://activity.dexfu.cn/customShare/share?id=Did1NjA2NzE","thumbnail":"https://yun.duiba.com.cn/polaris/900_1125.b33bb450c3b4242a76713cb8338c3850186e6b20.jpg"}',
pageBgImg: "https://yun.duiba.com.cn/ZZH_NewGuestGift/bg.gif",
},
};
module.exports = {
"data": 3,
"success": true
}
\ No newline at end of file
module.exports = {
code: null,
data: {
bannerInfo: [
{
appUrl:
"Au9NAuPYCjyfV2XflxBrkG5vmuPzms9gmw9dl3Bzl25AIGnvZ2ntnOF4nRTrEQPiIG1jZCE3AsEiWxFilMEiWsEjACEiWwr0nRLjTDXLTDTQTDTQl3LvlwPgkC5sl2WeI24vWuJzlwF3miEiBxLdTDTQTDSjTDTQWtq1WsEiWsE3BK==",
bannerImg: "//yun.duiba.com.cn/polaris/banner.b41421b436f9452b88e05b038434779016eea805.png",
wxUrl: "https://openapi.boc.cn/inews/pm/#/2852",
},
{
appUrl: "Au9NAuPYCjyfV3XvIHTtkM9cl2PeDGPeIGnvlGFenO1rlwPxJEFcl2Pe",
bannerImg: "//yun.duiba.com.cn/polaris/banner 2.4b6b30904fa795078be89546828f565ce3b9a0d4.png",
wxUrl: "https://mbs.boc.cn/v/a/Z3HsKjleZJ",
},
],
activityInfo: [
{
appUrl: "Au9NAuPYCjyfV3XvIHTtkM9doETvlwFwkHA=",
buttonImg: "//yun.duiba.com.cn/polaris/开户大礼包.c0f860ee058f73544ca625d2c422e03fd2389b23.png",
remark: "开户启新礼",
wxUrl: "https://mbs.boc.cn/v/a/y3EsVgxjth",
popbuttonImg: "//yun.duiba.com.cn/polaris/查看权益.d2f2ed839dbbd6fba4bed93414e5112530ec4556.png",
prizeInfo: [],
rule: '<p><span style="color: rgb(231, 95, 51); font-size: 19px;"><strong>【奖励内容】</strong></span><span style="font-size: 19px;">2025年4月28日起,首次开通中国银行账户的新客(不包括销户后重新开户的客户),开户后3个月内每月获赠</span><span style="color: rgb(231, 95, 51); font-size: 19px;">10元</span><span style="font-size: 19px;">,累计</span><span style="color: rgb(231, 95, 51); font-size: 19px;">30元</span><span style="font-size: 19px;">微信立减金,每张需选择中行借记卡支付满</span><span style="color: rgb(231, 95, 51); font-size: 19px;">100元</span><span style="font-size: 19px;">以上使用。</span></p>',
},
{
appUrl:
"Au9NAuPYCjyfV2XflxBrkG5vmuPzms9gmw9dl3Bzl25AIGnvZ2ntnOF4nRTrEQPiIG1jZCE3AsEiWxFilMEiWsEjACEiWwr0nRLjTDXLTDTQTDTQl3LvlwPgkC5sl2WeI24vWuJzlwF3miEiBxLdTDTQTDSjTDTQWtqiWsEiWsE3BK==,https://m.huajifen.com/hjf/home/12?hjf_source=huajifen9908huajifen",
buttonImg: "//yun.duiba.com.cn/polaris/消费大礼包.fe8b83acf45acb3b509bd747a1810c77325b1378.png",
remark: "消费畅享礼",
wxUrl:
"https://openapi.boc.cn/inews/pm/#/2822,https://m.huajifen.com/hjf/home/12?hjf_source=huajifen9908huajifen",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即绑卡.b7a513a58c2c803cad940141b74562d817429823.png",
prizeInfo: [],
rule: '<p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>借记卡一键绑卡</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">活动期间,手机银行一键绑卡首绑微信、支付宝、京东、抖音、美团、拼多多,即有机会获得最高</span><span style="color: rgb(231, 95, 51); font-size: 15px;">690元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">大礼包。</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【参与方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">点击“立即绑卡”或者手机银行搜索“一键绑卡”。</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">######</span></p><p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>信用卡一键绑卡</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">活动期间,手机银行一键绑卡首绑支付宝、京东、抖音、美团、携程、拼多多,即有机会最高享</span><span style="color: rgb(231, 95, 51); font-size: 15px;">666元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">首绑好礼。</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【参与方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">点击“立即绑卡”或手机银行搜索“一键绑卡”</span></p><p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>信用卡消费优惠</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">活动期间,京东国补订单有机会享单笔订单满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">1000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">返</span><span style="color: rgb(231, 95, 51); font-size: 15px;">30元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">支付券,满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">3000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">返</span><span style="color: rgb(231, 95, 51); font-size: 15px;">100元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">支付券;美团支付随机最高减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">66元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">,美团外卖超级周六外卖满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">50元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">;抖音以旧换新满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">2000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">38元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">,每周五满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">500元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">;携程消费满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">500元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">20元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">,机票酒店满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">5000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">100元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">,去哪儿消费满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">500元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">20元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">;拼多多消费满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">800元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">随机减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10-50元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">,满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">3000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">随机减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">30-300元</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">*具体规则请见详情页</span></p><p><br></p>',
},
{
appUrl:
"Au9NAuPYCjyfV3XvIHTtkM9gl3L1lQPiAGX0kHJznRu/J2X0BHr0mwPAIHTrlHW9TDnMTDSinQ1glOvem3BCJGJeliEiWsEjACEiWu1MCE5DHjSgWtEgXNKhXtugXtA4TDSiTDTNTDSiIHJ5E2XdEwFwlw8vWtSvW0OvWtTSBNSgWtEgXNKhXNS3WDKvWtSvWuWvWtTrI3B5FRvgJCEiWsEjACEiWw5fAHJ5FRvgJCEiWsEiAiEiWxBdmQhAIGnvFwFiTDSiTDXLTDSiWCEiWsE3BK==",
buttonImg: "//yun.duiba.com.cn/polaris/幸运超享礼.fd28a57717b921902b88f50195d088f54c676540.png",
remark: "幸运超享礼",
wxUrl: "https://mbs.boc.cn/v/a/2kBS88coqT",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即报名.942ad8746b05616f2d11c538328e2cc75445aff7.png",
prizeInfo: [],
rule: '<p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">当月通过微信、支付宝、京东、美团、抖音、拼多多进行借记卡快捷支付,累计交易金额达标</span><span style="color: rgb(231, 95, 51); font-size: 15px;">3000元/5000元/10000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">即可于次月8日起参与开奖。累计交易金额达标</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">最高可中一等奖</span><span style="color: rgb(231, 95, 51); font-size: 15px;">1888元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">券包,达标</span><span style="color: rgb(231, 95, 51); font-size: 15px;">5000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">最高可中二等奖</span><span style="color: rgb(231, 95, 51); font-size: 15px;">888元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">券包,达标</span><span style="color: rgb(231, 95, 51); font-size: 15px;">3000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">最高可中三等奖</span><span style="color: rgb(231, 95, 51); font-size: 15px;">88元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">券包,奖品数量将按月限定。</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【参与方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">点击“立即报名”</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">*具体规则请见活动详情页</span></p>',
},
{
appUrl:
"Au9NAuPYCjyfV3XvIHTtkM9LI3Bznwv0oE1rlwPxJG1vlxBAIGh0Jw9ilErflGFAIGnvZ3LrmwPdmj0vX0SvWtTrI3Bznwv0oEvuTDSiTDXLTDSiWtKiXDK0WNOhXDE1XDWjWNE2XNuvWtSvWuWvWtTil3F0JEJcIGmvWtSvW0OvWtTdnGh0kFBrm2cvWtSvX0A=",
buttonImg: "//yun.duiba.com.cn/polaris/升金大礼包.40a1539e0aa4c0e6bf8aa81781390328cc4d5116.png",
remark: "晋级提升礼",
wxUrl: "https://mbs.boc.cn/v/a/MkwS1ChSPO",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即报名.942ad8746b05616f2d11c538328e2cc75445aff7.png",
prizeInfo: [],
rule: '<p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>资产提升任务</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">报名活动当月,月日均金融资产较活动前一月提升</span><span style="color: rgb(231, 95, 51); font-size: 15px;">1万</span><span style="color: rgb(0, 0, 0); font-size: 15px;">元及以上,可获得一次抽奖机会,根据金融资产提升区间差异化抽取尊享积分权益,达标客户最高可抽取</span><span style="color: rgb(231, 95, 51); font-size: 15px;">180万</span><span style="color: rgb(0, 0, 0); font-size: 15px;">尊享积分</span></p><p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>等级晋升任务</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">报名活动当月,月日均金融资产晋级成为我行理财级/财富级/私行级/企业家私行客户,将获赠最高</span><span style="color: rgb(231, 95, 51); font-size: 15px;">200元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">立减金权益</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【参与方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">点击“立即报名”</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">*具体规则请见活动详情页</span></p>',
},
{
appUrl: "https://mini.cardwinner.com/mpApi/activity/html_CC/#/home?itemCode=ZGYH_QG_XKH",
buttonImg: "//yun.duiba.com.cn/polaris/信用卡大礼包.77c85e458ed0fcd4d3d5b91cd5be9102ad8b8fa9.png",
remark: "信用卡臻享礼",
wxUrl: "https://mini.cardwinner.com/mpApi/activity/html_CC/#/home?itemCode=ZGYH_QG_XKH",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即办卡.68f5f4af083504ed3cf2f26ee8c148beecf31d3d.png",
prizeInfo: [],
rule: '<p><span style="color: rgb(0, 0, 0); font-size: 15px;">2025年4月,首次办理中国银行信用卡并达标享好礼</span></p><p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>基础礼</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">在卡片核发当月及次月通过微信、支付宝、京东、美团任意渠道完成一笔等值人民币</span><span style="color: rgb(231, 95, 51); font-size: 15px;">50元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">及以上消费,可于达标次月领取</span><span style="color: rgb(231, 95, 51); font-size: 15px;">100元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">京东支付券包,每人限领一次。</span></p><p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>加磅礼</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">在卡片核发当月及次月成功办理一笔人民币</span><span style="color: rgb(231, 95, 51); font-size: 15px;">500元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">及以上、期数6期及以上的账单、消费、自由、现金分期,可于达标次月领取</span><span style="color: rgb(231, 95, 51); font-size: 15px;">50元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">京东支付券,每人限领一次。</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【领取方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">手机银行-我的-我的权益-已获权益-活动奖品</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">*具体规则请见活动详情页</span></p>',
},
{
appUrl:
"Au9NAuPYCjyfV3XvIHTtkM9LI3Bznwv0oE1rlwPxJG1vlxBAIGh0Jw9ilErflGFAIGnvZ3LrmwPdmj0vX0SvWtTrI3Bznwv0oEvuTDSiTDXLTDSiWtKiXDK0WNqhWNE0XDI1XNE2WtEvWtSvWuWvWtTil3F0JEJcIGmvWtSvW0OvWtTdnGh0kFBrm2cvWtSvX0A=",
buttonImg: "//yun.duiba.com.cn/polaris/备老无忧礼.7be1cb6b86e43f8514a58f3a64adf26b4ce317e5.png",
remark: "备老无忧礼",
wxUrl: "https://mbs.boc.cn/v/a/rM23DqAi4J",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即参与.aee81b37a70106b265ded193fc759a2c9d94fabd.png",
prizeInfo: [],
rule: '<p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">活动期间完成个人养老金相关开户、缴存任务,即可在活动页面参与抽奖,最高可获得</span><span style="color: rgb(231, 95, 51); font-size: 15px;">618元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">立减金。</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;">开户首缴福利:</span><span style="color: rgb(0, 0, 0); font-size: 15px;">首次开立个人养老金资金账户且缴存</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">及以上即有机会抽取</span><span style="color: rgb(231, 95, 51); font-size: 15px;">28元、138元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">微信立减金;</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;">千元缴存福利:</span><span style="color: rgb(0, 0, 0); font-size: 15px;">2025年累计缴存满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">1000元(小于10000元)</span><span style="color: rgb(0, 0, 0); font-size: 15px;">即有机会抽取</span><span style="color: rgb(231, 95, 51); font-size: 15px;">38元、180元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">微信立减金;</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;">万元缴存福利:</span><span style="color: rgb(0, 0, 0); font-size: 15px;">2025年累计缴存满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10000元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">及以上即有机会抽取</span><span style="color: rgb(231, 95, 51); font-size: 15px;">88元、300元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">微信立减金。</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【参与方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">点击“立即参与”或者手机银行搜索“金账户养成计划”</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">*具体规则请见活动详情页</span></p><p><br></p>',
},
{
appUrl:
"Au9NAuPYCjyfV3XvIHTtkM9LI3Bznwv0oE1rlwPxJG1vlxBAIGh0Jw9ilErflGFAIGnvZ3LrmwPdmj0vX0SvWtTrI3Bznwv0oEvuTDSiTDXLTDSiWtKiXDK0WNWhXNOjWDmiYDq3YDAvWtSvWuWvWtTil3F0JEJcIGmvWtSvW0OvWtTdnGh0kFBrm2cvWtSvX0A=",
buttonImg: "//yun.duiba.com.cn/polaris/结售汇大礼包.296553c1538896afbda11a6a293e42cc5a008701.png",
remark: "结售汇智选礼",
wxUrl: "https://mbs.boc.cn/v/a/jiiS3yg2Gm",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即报名.942ad8746b05616f2d11c538328e2cc75445aff7.png",
prizeInfo: [],
rule: '<p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="font-size: 15px;">活动期间,通过手机银行APP办理结汇或购汇业务,单笔交易额(元)达标即有机会在T+2日获得微信立减金。</span></p><table style="width: auto;"><tbody><tr><td colspan="1" rowspan="1" width="136" style="text-align: center;"><span style="font-size: 13px;">单笔交易金额<br>(万元)</span></td><td colspan="1" rowspan="1" width="120" style="text-align: center;"><span style="font-size: 13px;">微信立减金<br>(元)</span></td></tr><tr><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">1万-5万</span></td><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">10</span></td></tr><tr><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">5万-10万</span></td><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">50</span></td></tr><tr><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">10万-15万</span></td><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">100</span></td></tr><tr><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">15万-20万</span></td><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">150</span></td></tr><tr><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">20万-25万</span></td><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">200</span></td></tr><tr><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">25万(含)及以上</span></td><td colspan="1" rowspan="1" width="auto" style="text-align: center;"><span style="font-size: 13px;">300</span></td></tr></tbody></table><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【参与方式】</strong></span><span style="font-size: 15px;">点击“立即报名”</span></p><p><span style="font-size: 15px;">*具体规则请见活动详情页</span></p><p><br></p>',
},
{
appUrl:
"Au9NAuPYCjyfV2XflxBrkG5vmuPzms8hWQXtW2O0ItPvXtA0XjS0YDW0XDW0YNAjYNK2YGEiXN9xI3BAIHTrlHW9TDnMTDSikG50JHTgmwF0JGBTBMEiWsEjACEiWw1vmwXyIG50TDSiTDnO",
buttonImg: "//yun.duiba.com.cn/polaris/数币大礼包.30b05d8c62cc4ca168edca377acaa8ab599eb151.png",
remark: "数币潮玩礼",
wxUrl: "https://mbs.boc.cn/v/a/Hy6UuuA9Dn",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即参与.aee81b37a70106b265ded193fc759a2c9d94fabd.png",
prizeInfo: [],
rule: '<p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>新客见面礼</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">首次开通数币二类钱包的用户可领取满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">20元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">数币红包</span></p><p style="text-align: center;"><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>升级享好礼</strong></span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【奖励内容】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">已开立四类或三类数币钱包的用户,通过本活动入口升级为二类及以上等级,可领取满</span><span style="color: rgb(231, 95, 51); font-size: 15px;">10元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">减</span><span style="color: rgb(231, 95, 51); font-size: 15px;">5元</span><span style="color: rgb(0, 0, 0); font-size: 15px;">数币红包</span></p><p><span style="color: rgb(231, 95, 51); font-size: 15px;"><strong>【领取方式】</strong></span><span style="color: rgb(0, 0, 0); font-size: 15px;">开通钱包或者升级后可在结果页面领取</span></p><p><span style="color: rgb(0, 0, 0); font-size: 15px;">*具体规则请见活动详情页</span></p>',
},
{
appUrl: "",
buttonImg: "//yun.duiba.com.cn/polaris/幸运大礼包.0042d6666f3ce70c4f141085e33394604a740cdd.png",
remark: "工资优选礼",
wxUrl: "",
popbuttonImg: "//yun.duiba.com.cn/polaris/立即参与.aee81b37a70106b265ded193fc759a2c9d94fabd.png",
prizeInfo: [],
rule: '',
},
],
},
message: null,
success: true,
timeStamp: 1745572689407,
};
module.exports = {
"data": "<p>以下是游戏规则:手速要快,点击红包雨。。333。。。。。。。。。。。。。。。。。。。。11111111111111sadasdadadsad5555555557777777777799999999999911111111111111111111111222222222222222222222222222222222222222222222222222222222222222333333333333333333333333333333333333333333333333333333333333311111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222233333333333333333333333333333333333333333333333333333333333331111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333</p>",
"success": true
}
\ No newline at end of file
module.exports = {
success: true,
message: "报错了~",
code: null,
data: {
login: true,
unionid: null,
customerId: "427829621",
ibknum: "45129",
openid: null,
branchId: "10001",
},
};
const rule = require("./common/rule");
const drawNum = require("./common/drawNum");
const coopFrontVariable = require("./common/coopFrontVariable");
const {AESEncrypt} = require("./Crypto");
const homeInfo = require("./common/homeInfo");
const userInfo = require("./common/userInfo");
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"),
},
"GET /activity/index.do": homeInfo,
"GET /activity/userInfo.do": userInfo,
};
module.exports = proxy;
const fs = require('fs')
let proxy = {}
fs.readdirSync(__dirname)
.some(filename => {
if (filename !== 'index.js') {
proxy = Object.assign(proxy, require('./' + filename))
}
})
module.exports = proxy;
{
"name": "temp_base",
"version": "3.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/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",
"spark-utils": "^0.0.12",
"terser-webpack-plugin": "4.2.3"
},
"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":[],"prodId":[{"label":"线上测试","value":"p2b588cb1"},{"label":"线上正式","value":"p47d5ddb4"}]},"skinVariables":[]},"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>
<link rel="stylesheet" href="//yun.duiba.com.cn/h5/lib/reset.css">
<!-- 埋点测试 -->
<!-- <script src="https://cdn2.h5no1.com/cbsp-telegraph/opfStatisticSdk.v3.t2.js"></script> -->
<script src="https://cdn2.h5no1.com/cbsp-telegraph/opfStatisticSdk.v3.min.js"></script>
<script>
window.terminal = (function () {
var u = navigator.userAgent;
// app = navigator.appVersion;
return {
// 浏览器版本信息
trident: u.indexOf('Trident') > -1,
presto: u.indexOf('Presto') > -1,
webKit: u.indexOf('AppleWebKit') > -1,
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') > -1,
mobile: !!u.match(/AppleWebKit.*Mobile.*/),
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1,
iPhone: u.indexOf('iPhone') > -1,
iPad: u.indexOf('iPad') > -1,
webApp: u.indexOf('Safari') > -1,
weixin:
(u.indexOf('MicroMessenger') > -1 &&
u.replace(/.*(MicroMessenger\/[^\s]*).*/, '$1').replace('MicroMessenger/', '')) ||
false,
appVersion: u.indexOf('bocapp(') > -1 ? u.match(/bocapp\(\S+\)/)[0].slice(7, -1) : '3.0.0',
app: u.indexOf('bocapp(') > -1,
isWeixin: u.indexOf('MicroMessenger') > -1
};
})();
/**
* 引入cordova
*/
if (window.terminal.app) {
var script = document.createElement('script');
// eslint-disable-next-line @spark/best-practices/no-url-in-js
var path = 'https://ebsnew.boc.cn/bocphone/BocMerchant/'; // 生产环境
// 外网环境直接引用生产地址即可,若需新增cordova方法,另行提供DMZ区地址
if (window.terminal.ios) {
script.src = path + 'paylib/ios.js';
} else if (window.terminal.android) {
script.src = path + 'paylib/android.js';
} else {
alert('无法识别终端类型');
}
document.getElementsByTagName('head')[0].appendChild(script);
}
</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.domain = location.origin;
CFG.appKey = '3CDJtKxCbGB7eh3Mxj2guJKRvfgK';
if (CFG.projectId == 'p2b588cb1') { // 测试环境
CFG.appKey = '3PT1ia3EjnkhqpkqtvvYn9Sph3vQ';
}
// 长图页
CFG.imgPageUrl = "/projectx/" + CFG.projectId + "/img.html?appID=" + CFG.appID + "&imgUrl=";
// 首页
CFG.indexUrl = "/projectx/" + CFG.projectId + "/index.html?appID=" + CFG.appID;
// 是否app分享页面 1是app分享页面
CFG.isAppShare = getUrlParam('appShare') || '';
// 行方埋点参数
CFG.opfStatisticSdk_pfid = "be_2zFRQQ2Fb";
CFG.opfStatisticSdk_appid = "db5681a6-2bdf-4e6d-8c96-930e988c3b3b";
if (CFG.projectId == 'p2b588cb1') { // 测试项目
CFG.opfStatisticSdk_pfid = "be_2zFRQQ2Fb";
CFG.opfStatisticSdk_appid = "d4168704-ab8a-42ac-b9f6-d816bd7e1062";
}
// app端投放的商户id
CFG.MER_ID = getUrlParam('merId') || '10478';
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>
{"proName":"中总行新客九重礼-20250408","proDesc":"","proPath":"/Users/duibagroup/Downloads/duiba-wyx/2025/九重礼/SparkProjects","createTime":1744082788556}
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/1747277730652",
"JS_PATH_PROD": "https://yun.duiba.com.cn/spark/v2/temp_base/1747277736404/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
/**
* 普通跳转客服
* 注意:小程序需要额外增加业务域名 https://duiba.qiyukf.com/
*/
export function jumpService() {
location.href = location.origin + "/faq/index";
}
// app跳转
export function appJump(url) {
console.info("app跳转:", url);
if (url.indexOf("http") > -1) {
window.location.href = url;
return;
}
const setting = {
page: url,
};
window.c_plugins.merchantBridge.goToNative(
function (res) {
console.info(res);
},
function (err) {
console.info(err);
},
setting
);
}
function caesarCipher(str, shift) {
let result = "";
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
if (char >= 65 && char <= 90) {
result += String.fromCharCode(((char - 65 + shift) % 26) + 65);
} else if (char >= 97 && char <= 122) {
result += String.fromCharCode(((char - 97 + shift) % 26) + 97);
} else {
result += str[i];
}
}
return result;
}
export function caesarDecipher(str, shift) {
return caesarCipher(str, 26 - shift);
}
export const urlToBase64 = async (url) => {
try {
const response = await fetch(url);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob);
});
} catch (error) {
console.error("Error converting image to base64:", error);
throw error;
}
};
// app分享
export async function appShare(shareInfo) {
console.info("app分享", shareInfo);
// const base64String = await urlToBase64(shareInfo.thumbnail);
const base64String = (await urlToBase64(shareInfo.thumbnail))?.split("base64,")[1];
console.info("base64String", base64String); // 输出base64编码后的字符串
window.c_plugins.merchantBridge.weixinShare(
function () {
console.info("这是成功回调");
},
function () {
console.info("这是失败回调");
},
{
title: shareInfo.title, // 分享标题
description: shareInfo.content, // 分享内容
url: shareInfo.url, // 分享链接
img: base64String, // 分享缩略图必须是base64图片
}
);
}
// app 组合分享 (没有落地页)
export async function appMultiShareTools(shareInfo) {
console.info("app组合分享", shareInfo);
const base64String = (await urlToBase64(shareInfo.thumbnail))?.split("base64,")[1];
// console.info('base64String', base64String); // 输出base64编码后的字符串
window.c_plugins &&
window.c_plugins.merchantBridge.multiShareTools(
function (data) {
console.info("这是成功回调", data);
},
function (err) {
console.info("这是失败回调", err);
},
{
// 公共参数
commonInfo: {
internalCode: "2d146069343c4120b43b00691ac68f95", // 对应投放的商户code
internalParams: JSON.stringify({
interpretedID: "merchant", // 固定
showPage: shareInfo.showPage || "", // 唤端需要带回来的参数
}),
},
// 分享参数
shareParams: [
{
shareTypeLink: {
externalUrl: shareInfo.url, // 传进去也没用 不会有落地页
title: shareInfo.title,
description: shareInfo?.content,
encryEnable: "N",
img: base64String, // 分享缩略图必须是base64图片
},
},
// 海报分享
// {
// shareTypePoster: {
// Imgurl: posterImg, // base64图片或者图片链接
// // Imgurl: shareInfo?.posterImg.replace(/^data:.+;base64,/, ''), // base64图片或者图片链接
// },
// },
],
}
);
}
// app打开微信小程序
export async function appOpenMiniProgram(userName, url) {
console.info("app打开微信小程序", url);
window.c_plugins.merchantBridge.weixinOpenMiniProgram(
function () {
// 这是成功回调
console.info("这是成功回调");
},
function () {
// 这是失败回调
console.info("这是失败回调");
},
{
userName, // 小程序名称id
path: url,
}
);
}
// 获取机构号
export async function getBranchId() {
const merId = {
merId: CFG.MER_ID,
};
return new Promise((resolve, reject) => {
if (location.href.indexOf("localhost") !== -1) resolve({ cipherText: "11" });
window.c_plugins.merchantBridge.getBocCustomerAllInfo(
function (data) {
console.info("getBocCustomerAllInfo suc: ", data);
resolve(data);
},
function (err) {
console.info("getBocCustomerAllInfo fail: ", err);
reject(err);
},
merId
);
});
}
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,
},
homeInfo: {
uri: "activity/index.do",
},
// app授权参数接收接口
appAuth: {
uri: "/customActivity/acw/bocAutoLogin/appAuth",
contentType: "json",
showMsg: false,
method: "post",
},
/** 获取用户信息 */
getUserInfo: {
uri: "activity/userInfo.do",
},
});
// console.log('======', API)
export default API;
declare type Methods = 'get' | 'post';
declare interface APIArguments {
uri: string,
method?: Methods | Uppercase<Methods>,
headers?: any
withToken?: boolean
secret?: string,
secretKey?: string,
contentType?: string,
showMsg?:boolean,
showLoading?: boolean;
}
declare type APIConfig = APIArguments | string
declare interface GenerateAPIParams {
[key: string]: APIConfig
}
declare type RequestParamType = Record<string, any>
declare interface ResponseType<T = any> {
code: number,
success: boolean,
message: string,
data: T
}
export declare function generateAPI<
T extends GenerateAPIParams = GenerateAPIParams,
P = RequestParamType,
Q = ResponseType>
(apiList: T): {
[key in keyof T]: (args?: P) => Promise<Q>
}
import { 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',
}
// 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 { domain, ensureDomain } from "@spark/dbdomain";
import { isWeChat, onInitShare } from "./utils/share";
import "./app.less";
import store from "./store/index";
import shareStore from "./store/share";
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 "@src/pages/HomePage";
import { PAGE_MAP } from "./utils/constants";
/**
* 所有页面场景
*/
const pageMap = {
[PAGE_MAP.LOADING_PAGE]: <LoadingDemo />,
[PAGE_MAP.HOME_PAGE]: <HomePage />,
};
@observer
class App extends Component {
async componentDidMount () {
// 获取前端开发配置,依据项目需要,酌情添加 !!!
await Promise.all([
store.getFrontVariable(),
ensureDomain()
]);
// 获取动态域名
CFG.domain = domain;
// 初始化分享
if (isWeChat()) {
await onInitShare();
// 初始化分享参数
shareStore.initSharePageShare();
}
}
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":["LoadingPage/loadingBg.jpg","LoadingPage/loadingFill.png","LoadingPage/loadingIp.png","rulePop/close_btn.png","rulePop/按钮1.png","rulePop/按钮2.png","rulePop/背景.png","rulePop/背景2.png","shareGuidePop/share_guide.png","首页/banner.png","首页/banner2.png","首页/close.png","首页/信用卡臻享礼.png","首页/分享.png","首页/备老无忧礼.png","首页/工资优选礼.png","首页/幸运超享礼.png","首页/开户启新礼.png","首页/支付超享礼.png","首页/数币潮玩礼.png","首页/晋级提升礼.png","首页/活钱宝.png","首页/浮窗.png","首页/消费畅享礼.png","首页/消费畅想礼.png","首页/结售汇智选礼.png","首页/背景.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);
};
"use strict";
import React from "react";
import { observer } from "mobx-react";
import "./rulepop.less";
import { _asyncThrottle } from "@src/utils/utils";
import store from "@src/store";
import modalStore from "@src/store/modal";
import { opfStatisticSdkClick } from "@src/store/opfStatisticSdk";
import { RES_PATH } from "../../../sparkrc";
import { noUrl } from "@src/utils/share";
import { Toast } from "@spark/ui";
import { handleLogClick, handleLogExposure } from '../../../MD';
@observer
class Rulepop extends React.Component {
constructor(props) {
super(props);
}
componentDidMount () {
const { item } = this.props.popData || {};
// 分会场活动弹窗曝光
handleLogExposure(1, 6, `${item.remark}分会场`);
const { rule } = item || {};
const [rule1, rule2] = rule?.split("######") || [rule, ""];
// 分会场活动弹窗-参与按钮曝光
rule1 && handleLogExposure(1, 7, `${item.remark}分会场`);
rule2 && handleLogExposure(1, 7, `${item.remark}分会场`, 1);
}
// 只有规则弹窗这块需要特殊处理一下有两个跳转地址的情况
handleDetail = _asyncThrottle(async (type = 1) => {
const { item } = this.props.popData || {};
const [wxUrl1, wxUrl2] = item?.wxUrl?.split?.(",") || [];
const [appUrl1, appUrl2] = item?.appUrl?.split?.(",") || [];
opfStatisticSdkClick(`detail-btn-${item.id}`, `${item.remark}弹窗按钮${type === 2 ? "2" : ""}`);
handleLogClick(1, 7, `${item.remark}分会场`, type === 1 ? 0 : 1);
const wxUrl = type === 1 ? wxUrl1 : wxUrl2;
const appUrl = type === 1 ? appUrl1 : appUrl2;
if (noUrl({ wxUrl, appUrl })) {
Toast("暂未开始抽奖");
return;
}
setTimeout(() =>
store.jumpLink({
wxUrl,
appUrl,
})
);
});
handleClose = () => {
modalStore.closePop();
};
render () {
const { item } = this.props.popData || {};
const { rule, popbuttonImg } = item || {};
const [rule1, rule2] = rule?.split("######") || [rule, ""];
const [wxUrl1, wxUrl2] = item?.wxUrl?.split?.(",") || [];
const [appUrl1, appUrl2] = item?.appUrl?.split?.(",") || [];
return (
<div className="rulepop modal_center">
<span className="close_btn" onClick={this.handleClose}></span>
<span className={`beiJing ${rule2 ? "two" : ""}`}></span>
{rule2 ? (
<>
<span
className="btn anNiu1"
style={{
backgroundImage: `url(${popbuttonImg || RES_PATH + "rulePop/按钮1.png"})`,
filter: `grayscale(${noUrl({ wxUrl: wxUrl1, appUrl: appUrl1 }) ? "100%" : "0"})`,
}}
onClick={() => this.handleDetail(1)}></span>
<span
className="btn anNiu2"
style={{
backgroundImage: `url(${popbuttonImg || RES_PATH + "rulePop/按钮2.png"})`,
filter: `grayscale(${noUrl({ wxUrl: wxUrl2, appUrl: appUrl2 }) ? "100%" : "0"})`,
}}
onClick={() => this.handleDetail(2)}></span>
<span className="txt ruleInfo1" dangerouslySetInnerHTML={{ __html: rule1 }}></span>
<span className="txt ruleInfo2" dangerouslySetInnerHTML={{ __html: rule2 }}></span>
</>
) : (
<>
<span
className="btn anNiu"
style={{
backgroundImage: `url(${popbuttonImg || RES_PATH + "rulePop/按钮1.png"})`,
filter: `grayscale(${noUrl({ wxUrl: wxUrl1, appUrl: appUrl1 }) ? "100%" : "0"})`,
}}
onClick={() => this.handleDetail(1)}></span>
<span className="txt ruleInfo" dangerouslySetInnerHTML={{ __html: rule1 }}></span>
</>
)}
</div>
);
}
}
export default Rulepop;
@import "../../res.less";
.rulepop {
width: 550px;
height: 1100px;
left: 0px;
top: 0px;
position: absolute;
.beiJing {
width: 550px;
height: 1000px;
left: 0px;
top: 0px;
position: absolute;
.sparkBg("rulePop/背景.png");
&.two {
.sparkBg("rulePop/背景2.png");
}
}
.close_btn {
width: 54px;
height: 54px;
left: 50%;
transform: translateX(-50%);
top: 1050px;
position: absolute;
.sparkBg("rulePop/close_btn.png");
opacity: 0.4;
}
.btn {
width: 312px;
height: 99px;
background-repeat: no-repeat;
background-size: 100% 100%;
background-position: left top;
&.anNiu {
left: 117px;
top: 868px;
position: absolute;
}
&.anNiu1 {
left: 117px;
top: 458px;
position: absolute;
}
&.anNiu2 {
left: 117px;
top: 869px;
position: absolute;
}
}
.txt {
font-size: 26px;
color: rgba(144, 64, 0, 1);
word-break: break-all;
overflow-y: auto;
/* 修改整个滚动条的宽度和颜色 */
&::-webkit-scrollbar {
// display: none;
width: 7px;
border-radius: 10rpx;
}
/* 修改滚动条轨道的颜色 */
&::-webkit-scrollbar-track {
background: #fae5bb;
border-radius: 10rpx;
}
/* 修改滑块(可拖动的部分)的颜色 */
&::-webkit-scrollbar-thumb {
background: #fcae77;
}
}
.ruleInfo {
width: 465px;
height: 625px;
left: 47px;
top: 184px;
position: absolute;
padding: 0 6px;
box-sizing: border-box;
}
.ruleInfo1,
.ruleInfo2 {
width: 455px;
padding: 0 6px;
box-sizing: border-box;
left: 48px;
}
.ruleInfo1 {
height: 282px;
top: 169px;
position: absolute;
}
.ruleInfo2 {
height: 268px;
top: 595px;
position: absolute;
}
}
.ruleInfo table {
width: 100%;
border-collapse: collapse;
margin: 12px 0;
border: none;
}
.ruleInfo table th,
.ruleInfo table td {
position: relative;
border: none;
padding: 8px;
font-size: 14px;
line-height: 1.5;
color: #333;
}
.ruleInfo th {
font-weight: bold;
}
/* 表格内链接样式 */
.ruleInfo table a {
color: #1677ff;
text-decoration: none;
}
.ruleInfo table a:hover {
color: #4096ff;
text-decoration: underline;
}
/* 备用方案:用伪元素绘制边框 */
.ruleInfo td::after,
.ruleInfo th::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
border: 1px solid #000;
transform: scale(0.5);
transform-origin: 0 0;
pointer-events: none;
box-sizing: border-box;
}
'use strict';
import React from 'react';
import { observer } from 'mobx-react';
import './shareGuidePop.less';
import { Button } from '../Button';
import modalStore from '@src/store/modal';
@observer
class ShareGuidePop extends React.Component {
constructor(props) {
super(props);
}
// 关闭弹窗
closeHandle = () => {
modalStore.closePop()
}
render() {
return (
<div className="shareGuidePop" onClick={this.closeHandle}>
<span className="share_guide"></span>
</div>
);
}
}
export default ShareGuidePop;
@import "../../res.less";
.shareGuidePop {
width: 750px;
height: 1624px;
left: 0px;
top: 0px;
position: absolute;
.share_guide {
width: 362px;
height: 212px;
left: 341px;
top: 29px;
position: absolute;
.sparkBg("shareGuidePop/share_guide.png");
}
}
import React, { Component } from "react";
import RulePop from "../components/rulepop/rulepop";
import './modal.less';
import { observer } from 'mobx-react';
import modalStore from '../store/modal';
import { toJS } from 'mobx';
import ShareGuidePop from "@src/components/shareGuidePop/shareGuidePop";
/**
* 弹窗配置
*/
export const cfg = {
RulePop: RulePop,
// 分享引导弹窗
ShareGuidePop: ShareGuidePop,
};
@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;
.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 { useCallback, useEffect, useRef, useState } from "react";
export function useDebugPosition(id) {
const floatRef = useRef();
const [showFloat, setShowFloat] = useState(true);
const [position, setPosition] = useState({
x: 317,
y: 327
});
const onFloatTouchStart = useCallback(() => {
setShowFloat(false);
}, []);
const onFloatTouchMove = useCallback((e) => {
const { clientX, clientY } = e.touches[0];
const { clientWidth, clientHeight } = document.body;
let x = clientX;
let y = clientY;
// 设置边界值
const b_x = clientWidth - 64;
const b_y = clientHeight - 64;
if (x < 0) x = 0;
if (x > b_x) x = b_x;
if (y < 0) y = 0;
if (y > b_y) y = b_y;
setPosition({
x,
y,
});
}, []);
const onFloatTouchEnd = useCallback(() => {
setShowFloat(true);
}, []);
useEffect(() => {
floatRef.current = document.getElementById(id);
}, [id]);
return {
x: position.x,
y: position.y,
showFloat,
onFloatTouchStart,
onFloatTouchMove,
onFloatTouchEnd,
};
}
import React, { useState } from "react";
import { useDebugPosition } from "./hook";
import { RES_PATH } from "../../../../sparkrc";
import { noUrl } from "@src/utils/share";
import { opfStatisticSdkClick } from "@src/store/opfStatisticSdk";
import store from "@src/store";
import { useThrottle } from "@src/utils/utils";
import { handleLogClick } from '../../../../MD';
import "./index.less";
const FloatingIcon = () => {
const [show, setShow] = useState(true);
const { x, y, onFloatTouchEnd, onFloatTouchMove, onFloatTouchStart } = useDebugPosition("#floatingIcon");
const handleClick = useThrottle(async (e) => {
handleLogClick(1, 3);
e.stopPropagation();
const { floatingInfo } = store.frontVariable;
if (noUrl(floatingInfo)) return;
await store.appAuth(() => {
opfStatisticSdkClick(`floating-icon`, `${floatingInfo.remark}浮窗按钮`);
setTimeout(() => store.jumpLink(floatingInfo));
});
}, 1000);
const handleClose = (e) => {
e.stopPropagation();
setShow(false);
};
if (!show) return null;
return (
<div
id="floatingIcon"
className="floating"
style={{
top: y,
left: x,
transition: "width left 0.5s ease-in-out",
touchAction: "none",
}}
onTouchStartCapture={onFloatTouchStart}
onTouchMoveCapture={onFloatTouchMove}
onTouchEndCapture={onFloatTouchEnd}>
<div className="icon-box">
<img onClick={handleClick} className="fuChuang" src={RES_PATH + "首页/浮窗.png"} alt="" />
<img onClick={handleClose} className="closeBtn" src={RES_PATH + "首页/close.png"} alt="" />
</div>
</div>
);
};
export default FloatingIcon;
.floating {
width: 127px;
z-index: 100000;
left: 607px;
bottom: 128px;
position: fixed;
.icon-box {
position: relative;
}
.fuChuang {
display: block;
width: 127px;
height: 126px;
}
.closeBtn {
position: absolute;
right: 0;
top: 0;
display: block;
width: 32px;
height: 32px;
overflow: hidden;
opacity: 0.6;
}
}
"use strict";
import React from "react";
import { observer } from "mobx-react";
import { _asyncThrottle } from "@src/utils/utils";
import store from "@src/store";
import shareStore from "@src/store/share";
import modalStore from "@src/store/modal";
import { opfStatisticSdkClick } from "@src/store/opfStatisticSdk";
import { noUrl } from "@src/utils/share";
import { RES_PATH } from "../../../sparkrc.js";
import "./index.less";
import FloatingIcon from "./FloatingIcon/index";
import { handleLogClick, handleLogExposure } from "../../../MD";
import { Toast } from "@spark/ui";
@observer
class ShouYe extends React.Component {
constructor(props) {
super(props);
}
async componentDidMount() {
// 获取首页信息
await store.fetchHomeInfo();
// 获取用户信息
await store.getUserInfo();
// 埋点注册
await store.initStatistic();
// 首页曝光埋点
handleLogExposure(1, 1);
// 首页底部banner曝光埋点
store?.homeInfo?.bannerInfo?.map((_, index) => {
handleLogExposure(1, 2, index + 1);
});
// 首页-活钱宝悬窗曝光埋点
handleLogExposure(1, 3);
// 首页-分享按钮
handleLogExposure(1, 4);
// 首页-分会场活动按钮曝光
store?.homeInfo?.activityInfo?.map((act, index) => {
handleLogExposure(1, 5, `${index + 1}_${act.remark}分会场入口按钮`);
});
}
handleClickJump = _asyncThrottle(async (item, index) => {
await store.appAuth(() => {
handleLogClick(1, 5, `${index + 1}_${item.remark}分会场入口按钮`);
opfStatisticSdkClick(`join-btn-${index + 1}`, `${item.remark}分会场入口按钮`);
// 如果没有规则内容,直接跳转
const { rule, wxUrl, appUrl } = item;
if (!rule) {
if (noUrl({ wxUrl, appUrl })) {
Toast("活动暂未开始");
} else {
setTimeout(() => store.jumpLink(item));
}
return;
}
// 有规则内容,显示弹窗
setTimeout(() => modalStore.pushPop("RulePop", { item }));
});
}, 500);
handleClickBanner = _asyncThrottle(async (item, index) => {
if (noUrl(item)) return;
await store.appAuth(() => {
handleLogClick(1, 2, `第${index + 1}个banner`);
opfStatisticSdkClick(`banner-${index + 1}`, `第${index + 1}个banner`);
setTimeout(() => store.jumpLink(item));
});
}, 500);
handleClickShare = _asyncThrottle(async () => {
handleLogClick(1, 4);
shareStore.doShare();
}, 1000);
render() {
const { activityInfo = [], bannerInfo } = store.homeInfo;
const { pageBgImg } = store.frontVariable;
return (
<div className="shouYe">
<img className="beiJing" src={pageBgImg || RES_PATH + "首页/背景.png"} alt="bg" />
<div className="activityInfo">
{activityInfo?.map((item, index) => (
<span
key={"activity_" + index}
className={`item item_${index}`}
onClick={() => this.handleClickJump(item, index)}>
<img src={item.buttonImg} alt="" />
</span>
))}
</div>
<div className="bannerInfo">
{bannerInfo?.map((banner, index) => (
<span
key={"banner_" + index}
className={`banner banner_${index}`}
onClick={() => this.handleClickBanner(banner, index)}>
<img src={banner.bannerImg} alt="" />
</span>
))}
</div>
<span className="share" onClick={this.handleClickShare}></span>
<FloatingIcon />
</div>
);
}
}
export default ShouYe;
@import "../../res.less";
.shouYe {
position: relative;
width: 750px;
min-height: 100%;
top: -180px;
display: flex;
justify-content: center;
flex-wrap: wrap;
.beiJing {
display: block;
width: 750px;
height: auto;
// .sparkBg("首页/背景.png");
}
.activityInfo {
// width: 722px;
// height: 2079px;
left: 10px;
top: 713px;
position: absolute;
.item {
position: absolute;
}
.item_0 {
width: 445px;
height: 394px;
left: 168px;
top: 0px;
position: absolute;
// .sparkBg("首页/开户启新礼.png");
}
.item_1 {
width: 338px;
height: 311px;
left: 2px;
top: 350px;
position: absolute;
// .sparkBg("首页/消费畅想礼.png");
}
.item_2 {
width: 338px;
height: 352px;
left: 379px;
top: 407px;
position: absolute;
// .sparkBg("首页/支付超享礼.png");
}
.item_3 {
width: 396px;
height: 303px;
left: 1px;
top: 726px;
position: absolute;
z-index: 21;
// .sparkBg("首页/晋级提升礼.png");
}
.item_4 {
width: 338px;
height: 310px;
left: 383px;
top: 803px;
// left: 386px; // 383
// top: 833px; // 803
position: absolute;
// .sparkBg("首页/信用卡臻享礼.png");
}
.item_5 {
width: 338px;
height: 353px;
left: 5px;
top: 1078px;
position: absolute;
// .sparkBg("首页/备老无忧礼.png");
}
.item_6 {
width: 338px;
height: 351px;
left: 383px;
top: 1178px;
position: absolute;
// .sparkBg("首页/结售汇智选礼.png");
}
.item_7 {
width: 378px;
height: 313px;
left: 0px;
top: 1463px;
position: absolute;
z-index: 21;
// .sparkBg("首页/数币潮玩礼.png");
}
.item_8 {
width: 484px;
height: 397px;
left: 238px;
top: 1682px;
position: absolute;
// .sparkBg("首页/工资优选礼.png");
}
}
.bannerInfo {
width: 750px;
height: auto;
left: 0;
top: 2812px;
position: absolute;
padding-bottom: 20px;
.banner {
display: block;
width: 659px;
height: 199px;
position: relative;
margin: 0 auto 16px;
overflow: hidden;
border-radius: 20px;
}
}
.share {
width: 87px;
height: 45px;
top: 235px;
right: 0;
position: absolute;
.sparkBg("首页/分享.png");
}
img {
width: 100%;
height: auto;
}
}
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;
}
}
@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 { PAGE_MAP } from "@src/utils/constants";
import { makeAutoObservable } from "mobx";
import API from "../api/index";
import { GetCurrSkinId, getCustomShareId, windowJumpUrl } from "@src/utils/utils";
import { Toast } from "@spark/ui";
import { isWeChat } from "@src/utils/share";
import { appJump, getBranchId } from "@src/AppTool";
import { opfStatisticSdkInit } from "./opfStatisticSdk";
const skinId = GetCurrSkinId() || getCustomShareId();
const store = makeAutoObservable({
/** 活动规则 */
ruleInfo: "",
/** 前端开发配置 */
frontVariable: {},
/** 当前页面 */
curPage:
{
// TODO 举例子,自定义页面,因为mng更新原因原数字id会对应一个新的字符串id
5055: "sharePage",
Did1NDA0NDc: "sharePage",
myPrize: "myPrize", // TODO 举例子 新宿台奖品页
index: PAGE_MAP.HOME_PAGE,
}[skinId] || PAGE_MAP.HOME_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();
console.info("前端开发配置", data);
this.frontVariable = data
? {
...data,
shareInfo: JSON.parse(data?.shareInfo || "{}"),
floatingInfo: JSON.parse(data?.floatingInfo || "{}"),
}
: {};
},
/** 用户信息 */
userInfo: {},
/** 获取用户信息 */
async getUserInfo() {
const { success, data } = await API.getUserInfo();
if (success) {
this.userInfo = data || {};
sessionStorage.setItem("openid", data?.openid);
sessionStorage.setItem("unionid", data?.unionid);
}
},
/** 初始化埋点 */
async initStatistic() {
if (!isWeChat()) return;
opfStatisticSdkInit({
openid: sessionStorage.getItem("openid") || store.userInfo?.openid,
unionid: sessionStorage.getItem("unionid") || store.userInfo?.unionid,
});
},
/** 首页 */
homeInfo: {},
/** 获取首页信息 */
async fetchHomeInfo() {
const { success, data } = await API.homeInfo();
if (success) {
this.homeInfo = data;
// console.info("首页信息", data);
}
},
/** app授权客户信息 */
async appAuth(callback) {
// 微信端直接跳转 不需要授权
if (isWeChat()) return callback();
// 有客户信息直接跳转 不需要授权
// if (store.userInfo?.customerId) return callback();
const branchData = await getBranchId();
console.info("app授权", branchData);
const splitBranchParams = JSON.parse(branchData.cipherText || "{}");
if (splitBranchParams && splitBranchParams?.hmaCipherText) {
await API.appAuth({
body: splitBranchParams.body,
hmaCipherText: splitBranchParams.hmaCipherText,
hmac: splitBranchParams.hmac,
skey: splitBranchParams.skey,
});
await store.getUserInfo();
callback && callback();
}
},
/** 判断活动状态 */
judgeActivity() {
const { actStartTime, actEndTime, systemTime } = store?.homeInfo || {};
if (systemTime < actStartTime) {
Toast("活动未开始");
return false;
} else if (systemTime >= actEndTime) {
Toast("活动已结束");
return false;
}
return true;
},
/** 跳转方法 */
jumpLink(item) {
if (!item?.wxUrl && !item?.appUrl) {
Toast("活动暂未上线,敬请期待~");
return;
}
if (isWeChat()) {
if (item.wxUrl) {
windowJumpUrl(item.wxUrl);
} else {
Toast("前往APP活动参与~");
}
} else {
if (item.appUrl) {
appJump(item.appUrl);
} else {
Toast("暂未开放~");
}
}
},
});
export default store;
import { makeAutoObservable } from 'mobx';
// 此处配置页面的优先级,越大优先级越高
// PopIndex:11
/**
* 弹窗优先级 可以是负数, 不写默认是10, 数值越小,层级越高
*/
const modalIndex = {}
const modalStore = makeAutoObservable({
popList: [],
/**
*
* @param {*} key 弹窗名,一般是类名的字符串
* @param {*} data 需要传递的数据,弹窗中使用 const {popData} = props; 获取
* @param {*} isMulti 是否是二级弹窗,在不关闭已有弹窗的基础上,弹出当前弹窗。注意,如果是二级弹窗,关闭时必须传key
*/
pushPop(key, data = {}, isMulti = false) {
if (this.popList.length) {
let cacheList = this.popList.slice();
cacheList.push({ key, data, isMulti });
cacheList = cacheList.sort((a, b) => ((modalIndex[b.key] ? modalIndex[b.key] : 10) - (modalIndex[a.key] ? modalIndex[a.key] : 10)))
this.popList.clear();
this.popList.push(...cacheList);
} else {
this.popList.push({ key, data, isMulti });
}
// console.log("this.popList:::",toJS(this.popList));
},
/**
*
* @param {*} key 弹窗名,一般是类名的字符串,关闭指定弹窗,若未指定,则关闭当前弹窗
*/
closePop(key) {
if (key) {
let cacheList = this.popList.slice();
cacheList = cacheList.filter(obj => (obj.key != key));
this.popList.clear();
this.popList.push(...cacheList);
} else {
this.popList.shift();
}
},
/**
* 关闭所有弹窗
*/
closePopAll() {
this.popList.clear();
}
});
export default modalStore;
import { isWeChat } from "@src/utils/share"
let isInit = false;
export const opfStatisticSdkInit = (opt) => {
if (!isWeChat()) return
if (isInit) return
try {
opfStatisticSdk?.init(
{
pfid: CFG.opfStatisticSdk_pfid, // (必须)CDMS 系统分配的平台 id,正式服 pfid:be_2zFRQQ2Fb
appid: CFG.opfStatisticSdk_appid, // (必须,1-50 位,数字,大小写字母,下划线,短横线)场景载体id,一般指作品 id
apptype: "xapp", // (必须)应用类型,外部导入作品均为 xapp
mpid: "wx567af860fd4db040", // (非必须参数,如果有微信授权,则必须填入)微信公众号 appid
openid: "", // (非必须参数,如果有微信授权,则必须填入)微信公众号获取的用户 openid
unionid: "", // (非必须参数,如果有微信授权且有 unionid,则必须填入)微信公众号获取的用户 unionid
userInfo: {}, // (非必须,如果有微信授权并能拿到用户信息,则必须填入)微信公众号网页授权获取的用户信息 JSON 对象
appUid: "", // (非必须)中行 app 用户标识
phone: "", // (非必须)用户手机号码
closeSdkAutoExit: false, // 需要由 SDK 自行判断上报退出事件时机时,传false,建议传 false
pageName: "", // (当初始化时 document.title 为空时必传)传入非空值时将用作page_name 的值,取代 document.title 的值
...opt
},
{
// sdkUrl: location.href.indexOf("test") > -1 ? "https://activity.m.duiba.com.cn/83591boclog/test/real-time/" : "https://activity.m.duiba.com.cn/83591boclog/prod/real-time/"
sdkUrl: `${location.origin}/real-time/`
// audidUrl: "https://xxx.xxx.xxx/unlogin/app/post_rabbitpre", // 正式服获取营销系统客户档案 id 接口地址,以实际使用为准(按照代理配置填写)
}
)
} catch (e) {
console.error("maidian", e)
}
isInit = true
}
export const opfStatisticSdkClick = (id, btnName) => {
if (!isWeChat()) return
opfStatisticSdk.btnClick(id, btnName);
}
import { isWeChat, onUpdateShare } from '@src/utils/share';
import store from '.';
import modalStore from './modal';
import { makeAutoObservable } from 'mobx';
import { appShare } from '@src/AppTool';
import { windowJumpUrl } from '@src/utils/utils';
const shareStore = makeAutoObservable({
/** app落地页-二次分享初始化 */
async initSharePageShare () {
// 分享信息
const { title, content, thumbnail, url} = store.frontVariable?.shareInfo || {};
const params = {
title: title, // 标题
content: content, // 描述
thumbnail: thumbnail, // 图片
url: url // 链接
};
onUpdateShare(params);
},
/**
* 分享
* isInit 是否初始化分享,默认不初始化
*/
async doShare () {
// 分享信息
const { title, content, thumbnail, url } = store.frontVariable?.shareInfo || {};
const params = {
title: title, // 标题
content: content, // 描述
thumbnail: thumbnail, // 图片
url: url // 链接
};
if (isWeChat()) {
modalStore.pushPop("ShareGuidePop");
onUpdateShare(params);
} else {
// appMultiShareTools(params);
appShare(params);
}
},
/** 唤端 */
toApp (page = 'home') {
const linkObj = store.frontVariable?.shareToAppLink;
const link = linkObj[page]; // { home, sub1, sub2, sub3, sub4, sub5, sub6 }
console.log('link', link);
windowJumpUrl(link);
}
});
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",
};
/** @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;
}
}
}
import { loadSvga } from '@spark/svgaplayer'
// import * as FYGE from 'fyge';
// import { Howl } from 'howler';
import { RES_PATH } from '../../sparkrc'
/**
* 预加载资源(/png|jpg|jpeg|svga|spi|json|mp3|wav/)
* @param {string[]} urlList 资源地址列表
* @param {number} batchNum 每批并行加载的资源个数(一般来说该数字越大整体加载速度越快,但加载前期会更卡顿)
* @param {Function} [onProgress] 加载进度回调,每加载完一个资源回调一次,入参为进度值(0,1]
* @returns {Promise} 返回一个只会resolve(loadedData)的promise,loadedData保存了所有预加载好的资源,可通过相对路径索引
* @example
* //例
* const loadedData = await PreloadAsset(urlList, 10, onProgress);
* const image = loadedData['image/fish.png'];
* const svgaData = loadedData['svga/fish.svga'];
* const spiData = loadedData['spine/fish.spi'];
* const lottieData = loadedData['lottie/fish.json'];
*/
export function preloadAsset(urlList, batchNum, onProgress) {
return new Promise((resolve) => {
/** 要加载资源总数 */
const totalNum = urlList.length;
/** 要加载的资源索引 */
let assetIndex = -1;
/** 已加载完毕的资源个数 */
let loadedNum = 0;
/** 存放加载好的数据,用地址索引 */
const loadedData = {};
/** 加载逻辑 */
const doLoad = async () => {
if (loadedNum >= totalNum) {
totalNum == 0 && onProgress && onProgress(1); // 无加载资源时,即为假loading
resolve(loadedData); // 加载完毕
} else {
assetIndex++;
if (assetIndex >= totalNum) return
const key = urlList[assetIndex];
const url = RES_PATH + urlList[assetIndex];
const result = await loadOneAsset(url);
if (!result) {
console.warn('加载异常', url);
}
loadedData[key] = result;
loadedNum++;
onProgress && onProgress(loadedNum / totalNum);
doLoad();
}
}
batchNum = batchNum || 1;
for (let index = 0; index < batchNum; index++) {
doLoad();
}
})
}
/**
* 加载一个资源
* @param {string} url 地址
*/
async function loadOneAsset(url) {
const fileType = url.split('.').pop();
switch (true) {
case (/png|jpg|jpeg/).test(fileType):
return await loadOneImg(url);
case (/svga/).test(fileType):
return await loadOneSvga(url);
// case (/spi/).test(fileType):
// return await loadOneSpi(url);
// case (/json/).test(fileType):
// return await loadOneJson(url);
// case (/mp3|wav/).test(fileType):
// return await loadOneAudio(url);
default:
console.warn('非法资源', url);
return false;
}
}
/**
* 加载一张图片
* @param {string} url 地址
*/
function loadOneImg(url) {
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 {
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();
}, {
isMiniApp: true,
apk: "3CDJtKxCbGB7eh3Mxj2guJKRvfgK",
});
}
/**
* 更新分享
* @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 // 链接
}
});
};
export const noUrl = (item) => {
const [wxUrl1, wxUrl2] = item?.wxUrl?.split(",") || [];
const [appUrl1, appUrl2] = item?.appUrl?.split(",") || [];
if (isWeChat() && !wxUrl1 && !wxUrl2) {
return true;
}
// app内
if (!isWeChat() && !appUrl1 && !appUrl2) {
return true;
}
return false;
};
/*
* @Author: all
* @Date: 2021-11-01 09:30:00
* @LastEditTime: 2021-11-02 18:30:00
* @LastEditors: all
* @Description:
*/
import { useRef, useEffect, useCallback } from "react";
export 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 = () => {
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