初始化
1
.env.development
Normal file
@ -0,0 +1 @@
|
|||||||
|
VITE_MODE = dev
|
||||||
6
.env.production
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
VITE_MODE = production
|
||||||
|
VITE_HOST = https://hd.xglpa.com
|
||||||
|
VITE_CDN = https://cdn.xglpa.com
|
||||||
|
VITE_FOLDER = /palc-lantern-cash/
|
||||||
|
VITE_API = /pingan-exchange-yx-2025
|
||||||
|
VITE_ACTIVITY_URL = https://w.pingan.com.cn/lckj/pawm-mmo-activity-xgl-frontend/index.html
|
||||||
6
.env.test
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
VITE_MODE = test
|
||||||
|
VITE_HOST = https://test.szxgl.cn
|
||||||
|
VITE_CDN = https://test.szxgl.cn
|
||||||
|
VITE_FOLDER = /palc-lantern-cash/
|
||||||
|
VITE_API = /pingan-exchange-yx-2025
|
||||||
|
VITE_ACTIVITY_URL = https://w-stg.pingan.com.cn/lckj/pawm-mmo-activity-xgl-frontend/index.html
|
||||||
26
.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
*.zip
|
||||||
|
*.mp4
|
||||||
3
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
|
||||||
|
}
|
||||||
25
config/config.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// px转viewport配置
|
||||||
|
export default {
|
||||||
|
unitToConvert: 'px',
|
||||||
|
viewportWidth: file => {
|
||||||
|
let num = 750;
|
||||||
|
//van是375
|
||||||
|
if (file.indexOf('van') > 0) {
|
||||||
|
num = 375;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
},
|
||||||
|
unitPrecision: 5, // 单位转换后保留的精度
|
||||||
|
propList: ['*'], // 能转化为vw的属性列表
|
||||||
|
viewportUnit: 'vw', // 希望使用的视口单位
|
||||||
|
fontViewportUnit: 'vw', // 字体使用的视口单位
|
||||||
|
selectorBlackList: ['ignore-'], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
|
||||||
|
minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
|
||||||
|
mediaQuery: true, // 媒体查询里的单位是否需要转换单位
|
||||||
|
replace: true, // 是否直接更换属性值,而不添加备用属性
|
||||||
|
exclude: [], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
|
||||||
|
include: [], // 如果设置了include,那将只有匹配到的文件才会被转换
|
||||||
|
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
|
||||||
|
landscapeUnit: 'vw', // 横屏时使用的单位
|
||||||
|
landscapeWidth: 1628, // 横屏时使用的视口宽度
|
||||||
|
}
|
||||||
24
deploy.config.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
export default {
|
||||||
|
"test": {
|
||||||
|
host: '106.75.145.24',//服务器IP
|
||||||
|
port: 22,//服务器端口
|
||||||
|
username: 'root',//服务器ssh登录用户名
|
||||||
|
password: 'wehjdfh&4)2%2@kg',//服务器ssh登录密码
|
||||||
|
serverpath: '/mnt/services/tomcat-8090-test/webapps/palc-lantern-cash',//服务器web目录 切记不要加/ 当前目录不存在会创建目录并且当前目录所有文件会被清空重新部署前端项目
|
||||||
|
},
|
||||||
|
"prod": {
|
||||||
|
host: '120.77.32.131',//8.135.39.87
|
||||||
|
port: 22,//服务器端口
|
||||||
|
username: 'root',//服务器ssh登录用户名
|
||||||
|
password: 'JDhweh*63*03%3267',//服务器ssh登录密码
|
||||||
|
serverpath: '/mnt/apps/palc-lantern-cash',//服务器web目录 切记不要加/ 当前目录不存在会创建目录并且当前目录所有文件会被清空重新部署前端项目
|
||||||
|
},
|
||||||
|
"cdn": {
|
||||||
|
host: '120.77.32.131',//8.135.39.87
|
||||||
|
port: 22,//服务器端口
|
||||||
|
username: 'root',//服务器ssh登录用户名
|
||||||
|
password: 'JDhweh*63*03%3267',//服务器ssh登录密码
|
||||||
|
serverpath: '/mnt/cdn/palc-lantern-cash',//服务器web目录 切记不要加/ 当前目录不存在会创建目录并且当前目录所有文件会被清空重新部署前端项目
|
||||||
|
},
|
||||||
|
//...其他自定义环境
|
||||||
|
}
|
||||||
15
index.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
<title><%- title%></title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
11
jsconfig.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": "./",
|
||||||
|
"allowJs": true,
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
package.json
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "vite-4",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"test": "vite --mode test",
|
||||||
|
"build:test": "vite build --mode test",
|
||||||
|
"build:pro": "vite build --mode production",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@vueuse/core": "^10.5.0",
|
||||||
|
"alloytouch": "^0.3.0",
|
||||||
|
"amfe-flexible": "^2.2.1",
|
||||||
|
"axios": "^1.5.0",
|
||||||
|
"fabric": "^5.3.0",
|
||||||
|
"gsap": "^3.12.2",
|
||||||
|
"howler": "^2.2.4",
|
||||||
|
"pinia": "^2.1.6",
|
||||||
|
"pixi.js": "7.2.4",
|
||||||
|
"qs": "^6.11.2",
|
||||||
|
"three": "0.148.0",
|
||||||
|
"three-orbit-controls": "^82.1.0",
|
||||||
|
"vant": "^4.7.0",
|
||||||
|
"vconsole": "^3.15.1",
|
||||||
|
"vue": "^3.3.4",
|
||||||
|
"vue-router": "^4.2.5",
|
||||||
|
"weixin-js-sdk": "^1.6.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@vant/auto-import-resolver": "^1.0.1",
|
||||||
|
"@vitejs/plugin-vue": "^4.2.3",
|
||||||
|
"autoprefixer": "^10.4.16",
|
||||||
|
"postcss-px-to-viewport-8-plugin": "^1.2.3",
|
||||||
|
"sass": "^1.68.0",
|
||||||
|
"sass-loader": "^13.3.2",
|
||||||
|
"unplugin-auto-import": "^0.16.6",
|
||||||
|
"unplugin-vue-components": "^0.25.2",
|
||||||
|
"vite": "^4.4.5",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-html": "^3.2.0",
|
||||||
|
"vite-plugin-lvdeploy": "^1.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/api/Axios.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import QS from 'qs';
|
||||||
|
|
||||||
|
|
||||||
|
let url = import.meta.env.VITE_HOST + import.meta.env.VITE_API
|
||||||
|
|
||||||
|
// 创建axios
|
||||||
|
const service = axios.create({
|
||||||
|
baseURL: url,
|
||||||
|
timeout: 50000,
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 添加请求拦截器
|
||||||
|
service.interceptors.request.use((config) => {
|
||||||
|
// 在发送请求之前做些什么
|
||||||
|
config
|
||||||
|
return config;
|
||||||
|
}, function (error) {
|
||||||
|
// 对请求错误做些什么
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加响应拦截器
|
||||||
|
service.interceptors.response.use(
|
||||||
|
(response) => {
|
||||||
|
return response.data || {};
|
||||||
|
}, (error) => {
|
||||||
|
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default service;
|
||||||
73
src/api/http.js
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import service from './Axios'
|
||||||
|
import qs from "qs";
|
||||||
|
|
||||||
|
|
||||||
|
// json格式请求头
|
||||||
|
const headerJSON = {
|
||||||
|
"Content-Type": "application/json;charset=UTF-8",
|
||||||
|
};
|
||||||
|
// FormData格式请求头
|
||||||
|
const headerFormData = {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const http = {
|
||||||
|
/**
|
||||||
|
* methods: 请求
|
||||||
|
* @param url 请求地址
|
||||||
|
* @param params 请求参数
|
||||||
|
* @param json 判断数据发送是否是json格式: true-为是 false-为否
|
||||||
|
*/
|
||||||
|
get(url, params, json, authorization) {
|
||||||
|
if (authorization) {
|
||||||
|
headerJSON['authorization'] = authorization
|
||||||
|
}
|
||||||
|
const config = {
|
||||||
|
method: "get",
|
||||||
|
url: url,
|
||||||
|
headers: json ? headerJSON : headerFormData
|
||||||
|
};
|
||||||
|
if (params) config.params = params;
|
||||||
|
return service(config);
|
||||||
|
},
|
||||||
|
post(url, params, json, authorization) {
|
||||||
|
|
||||||
|
if (authorization) {
|
||||||
|
headerJSON['authorization'] = authorization
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
method: "post",
|
||||||
|
url: url,
|
||||||
|
headers: json ? headerJSON : headerFormData
|
||||||
|
};
|
||||||
|
|
||||||
|
if (params) config.data = json ? params : qs.stringify(params);
|
||||||
|
|
||||||
|
return service(config);
|
||||||
|
},
|
||||||
|
put(url, params, json) {
|
||||||
|
const config = {
|
||||||
|
method: "put",
|
||||||
|
url: url,
|
||||||
|
headers: headerFormData
|
||||||
|
};
|
||||||
|
if (params) config.params = params;
|
||||||
|
return service(config);
|
||||||
|
},
|
||||||
|
delete(url, params, json) {
|
||||||
|
const config = {
|
||||||
|
method: "delete",
|
||||||
|
url: url,
|
||||||
|
headers: headerFormData
|
||||||
|
};
|
||||||
|
if (params) config.params = params;
|
||||||
|
return service(config);
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//导出
|
||||||
|
export default http;
|
||||||
27
src/api/index.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import http from './http'
|
||||||
|
|
||||||
|
// 获取code
|
||||||
|
export function authorize(data, authorization) {
|
||||||
|
return http.get("/wechatApi/oauth2/wxLogin",
|
||||||
|
data,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
export function getUserInfo(data, authorization) {
|
||||||
|
return http.get("/wechatApi/oauth2/getUserInfo",
|
||||||
|
data,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兑奖
|
||||||
|
export function exchangePrize(data, authorization) {
|
||||||
|
return http.post("/pa/rewards",
|
||||||
|
data,
|
||||||
|
true,
|
||||||
|
authorization
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BIN
src/assets/images/index/arrow-icon.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/assets/images/index/bg.jpg
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
src/assets/images/index/boat-2.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/images/index/boat.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
src/assets/images/index/bottom-bird-1.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
src/assets/images/index/cloud-bottom.png
Normal file
|
After Width: | Height: | Size: 138 KiB |
BIN
src/assets/images/index/crane-1.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
src/assets/images/index/crane-2.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
src/assets/images/index/crane-3.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
src/assets/images/index/line.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
src/assets/images/index/location-bj.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
src/assets/images/index/location-gz.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
src/assets/images/index/location-sh.png
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
src/assets/images/index/location-wh.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
src/assets/images/index/location-xa.png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
src/assets/images/index/name-bj.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/images/index/name-gz.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/images/index/name-sh.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/images/index/name-wh.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
src/assets/images/index/name-xa.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
src/assets/images/index/sign-icon.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
src/assets/images/index/tips.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/assets/images/index/title.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
src/assets/images/index/top-bird-1.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/images/index/top-bird-2.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
src/assets/images/share.jpg
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
src/assets/media/bgm.mp3
Normal file
BIN
src/assets/media/click.mp3
Normal file
BIN
src/assets/media/entry.mp3
Normal file
23
src/components/Index.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="IndexPage">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.IndexPage {
|
||||||
|
@include fixed();
|
||||||
|
@include flexCen();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
||||||
44
src/components/Loading.vue
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<div class="LoadPage">
|
||||||
|
<Loading size="24px" type="spinner" color="#0094ff" vertical>loading...</Loading>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import gsap from 'gsap'
|
||||||
|
import { Loading, Icon } from 'vant'
|
||||||
|
import { loadImg } from '@/data/imgList'
|
||||||
|
import Preloader from '@/plugins/Preloader'
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
const emit = defineEmits(["LoadPage"]); // 声明触发事件,对应父组件上面的方法
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
Preloader(
|
||||||
|
{
|
||||||
|
name: '加载页资源',
|
||||||
|
imgs: loadImg,
|
||||||
|
callback: (progress) => console.log('进度:', progress)
|
||||||
|
}
|
||||||
|
).then(
|
||||||
|
res => {
|
||||||
|
console.log('加载完成');
|
||||||
|
gsap.to('.LoadPage', { duration: .5, autoAlpha: 0, onComplete: () => { emit("LoadPage", { action: 'hide' }) } })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.LoadPage {
|
||||||
|
@include fixed();
|
||||||
|
@include flexCen();
|
||||||
|
|
||||||
|
.brrage {
|
||||||
|
width: 750px;
|
||||||
|
height: 750px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
31
src/data/data.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
export default {
|
||||||
|
|
||||||
|
//manifest 加载到纹理缓存使用的资源
|
||||||
|
manifest: [
|
||||||
|
{
|
||||||
|
name: "test0",
|
||||||
|
url: new URL(`@/assets/images/m_0.png`, import.meta.url).href,
|
||||||
|
position: { x: 110, y: 210, w: 80, h: 80 },
|
||||||
|
zIndex: 4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test1",
|
||||||
|
url: new URL(`@/assets/images/m_1.png`, import.meta.url).href,
|
||||||
|
position: { x: 110, y: 200, w: 100, h: 100 },
|
||||||
|
zIndex: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
url: new URL(`@/assets/images/m_2.png`, import.meta.url).href,
|
||||||
|
position: { x: 110, y: 300, w: 100, h: 100 },
|
||||||
|
zIndex: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test3",
|
||||||
|
url: new URL(`@/assets/images/share.jpg`, import.meta.url).href,
|
||||||
|
position: { x: 110, y: 400, w: 300, h: 300 },
|
||||||
|
zIndex: 1,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
}
|
||||||
20
src/data/imgList.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
const load = [
|
||||||
|
'load/1.png',
|
||||||
|
]
|
||||||
|
|
||||||
|
// 处理为vite引入图片格式
|
||||||
|
function imgCreate(url, img) {
|
||||||
|
let i = new URL(`../assets/images/${url}`, import.meta.url).href
|
||||||
|
img.push(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const loadImg = []
|
||||||
|
const pageImg = []
|
||||||
|
|
||||||
|
load.forEach(element => {
|
||||||
|
imgCreate(element, loadImg)
|
||||||
|
});
|
||||||
|
|
||||||
|
export { loadImg, pageImg }
|
||||||
25
src/page/Home/App.vue
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<script setup>
|
||||||
|
import Loading from '@/components/Loading.vue';
|
||||||
|
import Index from '@/components/Index.vue';
|
||||||
|
|
||||||
|
const showLoading = ref(true);
|
||||||
|
const showIndex = ref(false);
|
||||||
|
|
||||||
|
const LoadingFn = (item) =>{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const IndexFn = (item) =>{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Index v-if="showIndex" />
|
||||||
|
<Loading v-if="showLoading" @LoadPage="LoadingFn" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
</style>
|
||||||
26
src/page/Home/main.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
import { wxShare } from '@/plugins/wxshare';
|
||||||
|
import {isMobile} from '@/plugins'
|
||||||
|
import VConsole from 'vconsole';
|
||||||
|
import 'vant/lib/index.css';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const app = createApp(App)
|
||||||
|
|
||||||
|
|
||||||
|
// 微信分享配置
|
||||||
|
wxShare({
|
||||||
|
title: '今夜宜赏灯,来测测你的专属元宵花灯',
|
||||||
|
desc: '赏花灯,赢惊喜,欢天喜地闹元宵!',
|
||||||
|
link: '/index.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试环境开vconsole
|
||||||
|
if (isMobile()) {
|
||||||
|
// const vConsole = new VConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.mount('#app')
|
||||||
|
console.log('Mode:', import.meta.env.VITE_MODE);
|
||||||
31
src/plugins/Preloader.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// 图片资源预加载器
|
||||||
|
const imgPreloader = url => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let image = new Image();
|
||||||
|
image.src = url;
|
||||||
|
image.onload = () => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
image.onerror = () => {
|
||||||
|
reject();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const Preloader = ({ name, imgs, callback }) => {
|
||||||
|
let promiseArr = [];
|
||||||
|
let num = 0
|
||||||
|
imgs.forEach(element => {
|
||||||
|
promiseArr.push(imgPreloader(element).then((res => {
|
||||||
|
num++
|
||||||
|
let progress = parseInt((num / imgs.length) * 100)
|
||||||
|
callback(progress)
|
||||||
|
})));
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(name, promiseArr.length, '张');
|
||||||
|
return Promise.all(promiseArr);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default Preloader
|
||||||
13
src/plugins/algorithm.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// 洗牌算法
|
||||||
|
export function FYShuffle(arr) {
|
||||||
|
let len = arr.length;
|
||||||
|
|
||||||
|
while (len > 1) {
|
||||||
|
let rand = Math.floor(Math.random() * len);
|
||||||
|
len--;
|
||||||
|
[arr[len], arr[rand]] = [arr[rand], arr[len]] // 采用的数组的结构赋值
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
49
src/plugins/hooks.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
// 倒计时
|
||||||
|
export function useCountDown() {
|
||||||
|
const countNum = ref(0)
|
||||||
|
const countInterval = ref(null)
|
||||||
|
|
||||||
|
const startCountDown = num => {
|
||||||
|
countNum.value = Number(num)
|
||||||
|
clearCountDown()
|
||||||
|
countInterval.value = setInterval(() => {
|
||||||
|
if (countNum.value === 0) {
|
||||||
|
clearInterval(countInterval.value)
|
||||||
|
countInterval.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
countNum.value--
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearCountDown = () => {
|
||||||
|
if (countInterval.value) {
|
||||||
|
clearInterval(countInterval.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { countNum, startCountDown, clearCountDown }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 防抖
|
||||||
|
export function useDebounce(cb, delay = 150) {
|
||||||
|
const timer = ref(null)
|
||||||
|
|
||||||
|
const handler = () => {
|
||||||
|
if (timer.value) {
|
||||||
|
clearTimeout(timer.value)
|
||||||
|
timer.value = setTimeout(() => {
|
||||||
|
cb()
|
||||||
|
}, delay)
|
||||||
|
} else {
|
||||||
|
timer.value = setTimeout(() => {
|
||||||
|
cb()
|
||||||
|
}, delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { handler }
|
||||||
|
}
|
||||||
521
src/plugins/index.js
Normal file
@ -0,0 +1,521 @@
|
|||||||
|
// 常用的方法
|
||||||
|
import gsap from 'gsap'
|
||||||
|
import { Howl, Howler } from 'howler';
|
||||||
|
import { Toast } from 'vant';
|
||||||
|
import { createVNode, render } from 'vue'
|
||||||
|
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
const btnEnable = ref(true) //按钮可点击状态
|
||||||
|
|
||||||
|
// 音效初始化
|
||||||
|
const sound = new Howl({
|
||||||
|
src: [new URL(`@/assets/media/click.mp3`, import.meta.url).href]
|
||||||
|
});
|
||||||
|
|
||||||
|
// 背景音乐
|
||||||
|
export function createBGM() {
|
||||||
|
const musicNode = createVNode(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
class: 'music-icon',
|
||||||
|
id: 'musicBtn',
|
||||||
|
onClick: () => {
|
||||||
|
let auduoEle = document.querySelector("#audio")
|
||||||
|
let musicBox = document.querySelector("#musicBtn")
|
||||||
|
if (auduoEle.paused) {
|
||||||
|
auduoEle.play()
|
||||||
|
audioAni.play()
|
||||||
|
musicBox.classList.add('music-on')
|
||||||
|
musicBox.classList.remove('music-off')
|
||||||
|
} else {
|
||||||
|
gsap.set('#musicBtn', { rotation: '0deg' })
|
||||||
|
auduoEle.pause()
|
||||||
|
audioAni.pause()
|
||||||
|
musicBox.classList.add('music-off')
|
||||||
|
musicBox.classList.remove('music-on')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
)
|
||||||
|
const audioNode = createVNode(
|
||||||
|
'audio',
|
||||||
|
{
|
||||||
|
class: 'audio-icon',
|
||||||
|
id: 'audio',
|
||||||
|
src: new URL(`@/assets/media/bgm.mp3`, import.meta.url).href,
|
||||||
|
autoPlay: true,
|
||||||
|
loop: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
render(musicNode, document.querySelector('.home'));
|
||||||
|
render(audioNode, document.querySelector("#musicBtn"));
|
||||||
|
document.querySelector("#musicBtn").classList.add('music-on')
|
||||||
|
let audioAni = gsap.timeline({ paused: true })
|
||||||
|
audioAni.to('#musicBtn', { duration: 10, rotation: "+=360", repeat: -1, ease: 'none' })
|
||||||
|
setTimeout(() => {
|
||||||
|
audioAni.play()
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//是否在微信环境
|
||||||
|
export function isWX() {
|
||||||
|
var ua = window.navigator.userAgent.toLowerCase();
|
||||||
|
if (ua.match(/MicroMessenger/i) == 'micromessenger') {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否移动端
|
||||||
|
export function isMobile() {
|
||||||
|
let userAgentInfo = navigator.userAgent;
|
||||||
|
let Agents = ['Android', 'iPhone', 'SymbianOS', 'Windows Phone', 'iPad', 'iPod'];
|
||||||
|
let getArr = Agents.filter(i => userAgentInfo.includes(i));
|
||||||
|
return getArr.length ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 判断微博环境
|
||||||
|
export function isWeibo() {
|
||||||
|
var ua = window.navigator.userAgent.toLowerCase();
|
||||||
|
if (ua.match(/WeiBo/i) == "weibo") {
|
||||||
|
console.log("微博环境");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log("非微博环境");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断Safari环境
|
||||||
|
export function isSafari() {
|
||||||
|
var ua = window.navigator.userAgent.toLowerCase();
|
||||||
|
if ((/Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent))) {
|
||||||
|
console.log("Safari环境");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log("非Safari环境");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//是否为安卓
|
||||||
|
export function isAndriod() {
|
||||||
|
var naviga = navigator.userAgent;
|
||||||
|
if (naviga.indexOf('Android') > -1 || naviga.indexOf('Adr') > -1) {
|
||||||
|
console.log("Andriod环境");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log("非Andriod环境");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//路径获取关键字
|
||||||
|
export function getQueryString(name) {
|
||||||
|
//截取页面传递字符串
|
||||||
|
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
|
||||||
|
var r = window.location.search.substr(1).match(reg);
|
||||||
|
if (r != null) return unescape(r[2]);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断全面屏和非全面屏,true为全面屏
|
||||||
|
export function judgeBigScreen() {
|
||||||
|
let result = false;
|
||||||
|
const rate = window.screen.height / window.screen.width;
|
||||||
|
let limit = window.screen.height == window.screen.availHeight ? 1.8 : 1.65; // 临界判断值
|
||||||
|
// window.screen.height为屏幕高度
|
||||||
|
// window.screen.availHeight 为浏览器 可用高度
|
||||||
|
if (rate > limit) {
|
||||||
|
// console.log("全面屏");
|
||||||
|
|
||||||
|
result = true;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
// console.log("非面屏");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取url html名称
|
||||||
|
export function getUrlHtml() {
|
||||||
|
//获取url地址
|
||||||
|
var ts_href = window.location.href;
|
||||||
|
var ts_mainText = "";
|
||||||
|
//获取地址最后一个“/”的下标
|
||||||
|
var ts_indexof = ts_href.lastIndexOf("/");
|
||||||
|
//获取地址“/”之后的的内容
|
||||||
|
var ts_indexText = ts_href.substring(ts_indexof + 1);
|
||||||
|
//获取地址“.html”的下标
|
||||||
|
var ts_htmlBeforeText = ts_indexText.indexOf(".html");
|
||||||
|
//获取 “/”到".html"之间的内容
|
||||||
|
ts_mainText = ts_indexText.substring(0, ts_htmlBeforeText);
|
||||||
|
|
||||||
|
console.log("当前入口:", ts_mainText);
|
||||||
|
return ts_mainText;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取cookie中的值
|
||||||
|
export function getSetCookie(name, value, options) {
|
||||||
|
if (typeof value != 'undefined') {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (value === null) {
|
||||||
|
value = '';
|
||||||
|
options = this._extend({}, options, true);
|
||||||
|
options.expires = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var expires = '';
|
||||||
|
|
||||||
|
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
|
||||||
|
var date;
|
||||||
|
|
||||||
|
if (typeof options.expires == 'number') {
|
||||||
|
date = new Date();
|
||||||
|
date.setTime(date.getTime() + options.expires * 24 * 60 * 60 * 1000);
|
||||||
|
} else {
|
||||||
|
date = options.expires;
|
||||||
|
}
|
||||||
|
|
||||||
|
expires = '; expires=' + date.toUTCString();
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = options.path ? '; path=' + options.path : '';
|
||||||
|
var domain = options.domain ? '; domain=' + options.domain : '';
|
||||||
|
var secure = options.secure ? '; secure' : '';
|
||||||
|
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
|
||||||
|
} else {
|
||||||
|
var cookieValue = null;
|
||||||
|
|
||||||
|
if (document.cookie && document.cookie != '') {
|
||||||
|
var cookies = document.cookie.split(';');
|
||||||
|
|
||||||
|
for (var i = 0; i < cookies.length; i++) {
|
||||||
|
// var cookie = $.trim(cookies[i]);
|
||||||
|
var cookie = cookies[i].trim();
|
||||||
|
|
||||||
|
if (cookie.substring(0, name.length + 1) == name + '=') {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 安卓机型自动播放音乐
|
||||||
|
export function BGMAutoPlayMgr(url) {
|
||||||
|
this.audioContext = new (window.AudioContext || window.webkitAudioContext || window.mozAudioContext)();
|
||||||
|
this.sourceNode = null;
|
||||||
|
this.buffer = null;
|
||||||
|
this.isPlayingBGM = false;
|
||||||
|
this.toggleBGM = function () {
|
||||||
|
if (typeof this.sourceNode == 'object') {
|
||||||
|
if (this.isPlayingBGM) {
|
||||||
|
this.sourceNode.stop();
|
||||||
|
this.isPlayingBGM = false;
|
||||||
|
} else this._playSourceNode();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._playSourceNode = function () {
|
||||||
|
const audioContext = this.audioContext;
|
||||||
|
audioContext.resume();
|
||||||
|
const _sourceNode = audioContext.createBufferSource();
|
||||||
|
_sourceNode.buffer = this.buffer;
|
||||||
|
_sourceNode.loop = true;
|
||||||
|
_sourceNode.connect(audioContext.destination);
|
||||||
|
_sourceNode.start(0);
|
||||||
|
this.sourceNode = _sourceNode;
|
||||||
|
this.isPlayingBGM = true;
|
||||||
|
}
|
||||||
|
let loadAndAutoPlay = (audioUrl) => {
|
||||||
|
const audioContext = this.audioContext;
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', audioUrl, true);
|
||||||
|
xhr.responseType = 'arraybuffer';
|
||||||
|
xhr.onreadystatechange = () => {
|
||||||
|
if (xhr.status < 400 && xhr.status >= 200 && xhr.readyState === 4) {
|
||||||
|
audioContext.decodeAudioData(xhr.response, buffer => {
|
||||||
|
this.buffer = buffer;
|
||||||
|
WeixinJSBridge.invoke("getNetworkType", {}, () => this._playSourceNode());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xhr.send();
|
||||||
|
}
|
||||||
|
loadAndAutoPlay(url);
|
||||||
|
loadAndAutoPlay = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 金管家获取openid及openToken
|
||||||
|
export function getJGJId() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
// 获取openid
|
||||||
|
PALifeOpen.getOpenId(
|
||||||
|
{
|
||||||
|
appId: '1aeedce2a31340e591a5d64080d6105b' // 商户编号,区分⽣产和测试环境商户编号
|
||||||
|
},
|
||||||
|
rsp => {
|
||||||
|
// 调⽤接⼝成功后的回调函数
|
||||||
|
console.log('success', rsp.data);
|
||||||
|
store.state.openId = rsp.data.openId
|
||||||
|
store.state.openToken = rsp.data.openToken
|
||||||
|
if (rsp.ret == 0) {
|
||||||
|
store.state.openId = rsp.data.openId
|
||||||
|
store.state.openToken = rsp.data.openToken
|
||||||
|
const data = {
|
||||||
|
id: store.state.openId,
|
||||||
|
token: store.state.openToken
|
||||||
|
};
|
||||||
|
resolve(data);
|
||||||
|
// console.log(`获取到金管家openid时间:${Math.abs(s_t - new Date().getTime())}`);
|
||||||
|
}
|
||||||
|
if (rsp.ret == -1) {
|
||||||
|
console.log(JSON.stringify(rsp));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
e => {
|
||||||
|
// 调⽤接⼝异常的回调函数
|
||||||
|
console.log('failed', e);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 金管家内部埋点
|
||||||
|
export function addPoint(page, spage, des = {}) {
|
||||||
|
const activityId = process.env.VUE_APP_ACTIVITYID;
|
||||||
|
PALifeOpen.invoke(
|
||||||
|
"device",
|
||||||
|
"addRecord",
|
||||||
|
{
|
||||||
|
eventId: `499${page}-${activityId}`, //必填,根据需求
|
||||||
|
labelId: `499${page}${spage}-${activityId}`, //必填,根据需求
|
||||||
|
// 扩展参数
|
||||||
|
parameters: {
|
||||||
|
ext: JSON.stringify(des)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 调用接口成功后的回调函数 // alert(JSON.stringify(rsp))
|
||||||
|
rsp => console.debug("success ", rsp),
|
||||||
|
// 调用接口异常的回调函数
|
||||||
|
e => console.debug("failed ", e),
|
||||||
|
{
|
||||||
|
timeout: 86400000
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 百度统计埋点
|
||||||
|
export function addPointByBd(des) {
|
||||||
|
_hmt && _hmt.push(["_trackEvent", `${des}`, `${des} success`]);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防抖函数
|
||||||
|
export function debounceTap(target, callbacks, timeScale = 1) {
|
||||||
|
if (!btnEnable.value) return false
|
||||||
|
btnEnable.value = false;
|
||||||
|
let createAni = () => {
|
||||||
|
let timeline = new gsap.timeline({
|
||||||
|
onStart: () => {
|
||||||
|
sound.play()
|
||||||
|
},
|
||||||
|
onComplete: () => {
|
||||||
|
callbacks && callbacks();
|
||||||
|
btnEnable.value = true;
|
||||||
|
timeline.kill();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timeline
|
||||||
|
.to(target, { duration: timeScale * 0.2, scale: 0.8, })
|
||||||
|
.to(target, { duration: timeScale * 0.5, scale: 1, })
|
||||||
|
}
|
||||||
|
createAni();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 节流函数
|
||||||
|
export function throttle(fn, wait) {
|
||||||
|
let inThrottle = false
|
||||||
|
return (...args) => {
|
||||||
|
// @ts-ignore
|
||||||
|
const context = this
|
||||||
|
if (!inThrottle) {
|
||||||
|
inThrottle = true
|
||||||
|
fn.apply(context, args)
|
||||||
|
setTimeout(() => {
|
||||||
|
inThrottle = false
|
||||||
|
}, wait)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// vite环境引用图片
|
||||||
|
export function imgUrl(url) {
|
||||||
|
return new URL(`../assets/images/${url}`, import.meta.url).href
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算两个日期之间的天数(开始日期,结束日期)
|
||||||
|
export function getDaysBetween(date1, date2) {
|
||||||
|
let day1 = new Date(date1.join('-'))
|
||||||
|
let day2 = new Date(date2.join('-'))
|
||||||
|
let oneDay = 24 * 60 * 60 * 1000;
|
||||||
|
let diffDays = Math.round(Math.abs((day2 - day1) / oneDay));
|
||||||
|
return diffDays;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算星座
|
||||||
|
export function getAstro(month, day) {
|
||||||
|
var s = "魔羯水瓶双鱼牡羊金牛双子巨蟹狮子处女天秤天蝎射手魔羯";
|
||||||
|
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22];
|
||||||
|
return s.substr(month * 2 - (day < arr[month - 1] ? 2 : 0), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 洗牌算法:打乱数组顺序
|
||||||
|
export function FYShuffle(arr) {
|
||||||
|
let len = arr.length;
|
||||||
|
|
||||||
|
while (len > 1) {
|
||||||
|
let rand = Math.floor(Math.random() * len);
|
||||||
|
len--;
|
||||||
|
[arr[len], arr[rand]] = [arr[rand], arr[len]] // 采用的数组的结构赋值
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 选出数组中出现次数最多的值
|
||||||
|
export function mostValue(arr) {
|
||||||
|
// 创建一个空对象用于存储值及其出现的次数
|
||||||
|
let counter = {};
|
||||||
|
|
||||||
|
// 遍历数组中的每个元素
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
// 如果值已经在counter对象中,增加其计数
|
||||||
|
// 如果值不在counter对象中,设置计数为1
|
||||||
|
if (counter[arr[i]]) {
|
||||||
|
counter[arr[i]]++;
|
||||||
|
} else {
|
||||||
|
counter[arr[i]] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出出现次数最多的值及其出现的次数
|
||||||
|
let mostFrequentValue = null;
|
||||||
|
let maxCount = 0;
|
||||||
|
for (let key in counter) {
|
||||||
|
if (counter[key] > maxCount) {
|
||||||
|
mostFrequentValue = key;
|
||||||
|
maxCount = counter[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回出现次数最多的值和其出现的次数
|
||||||
|
return { value: mostFrequentValue, count: maxCount };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 海报生成
|
||||||
|
export function posterCreate(option, imageArr, textArr, theme,eqcode) {
|
||||||
|
let posterUrl = ''
|
||||||
|
const { width, height } = option
|
||||||
|
Toast.loading({
|
||||||
|
message: '海报生成中',
|
||||||
|
duration: 0,
|
||||||
|
forbidClick: true
|
||||||
|
})
|
||||||
|
console.log('theme', theme);
|
||||||
|
|
||||||
|
let mycanvas = document.createElement('canvas') // 创建一个canvas画布元素
|
||||||
|
let ctx = mycanvas.getContext('2d')
|
||||||
|
mycanvas.style.width = '750px'; //设置canvas的宽
|
||||||
|
mycanvas.style.height = '1500px'; //设置canvas的高
|
||||||
|
mycanvas.width = width
|
||||||
|
mycanvas.height = height
|
||||||
|
console.log('imgs:', imageArr);
|
||||||
|
//Promise对象加载资源
|
||||||
|
let loader_p = [];
|
||||||
|
imageArr.map((item) => {
|
||||||
|
const _p = new Promise(resolve => {
|
||||||
|
const img = new Image();
|
||||||
|
img.crossOrigin = 'Anonymous'
|
||||||
|
img.onload = () => {
|
||||||
|
resolve(img)
|
||||||
|
};
|
||||||
|
img.src = item.src;
|
||||||
|
});
|
||||||
|
loader_p.push(_p);
|
||||||
|
})
|
||||||
|
//Promise的.all方法,当所有预加载的图像加载好的回调函数
|
||||||
|
Promise.all(loader_p)
|
||||||
|
.then(imgList => {
|
||||||
|
|
||||||
|
imgList.map((item, index) => {
|
||||||
|
// console.log('item',imageArr[index]);
|
||||||
|
if (imageArr[index].name != 'eqcode') {
|
||||||
|
ctx.drawImage(item, imageArr[index].pos.x, imageArr[index].pos.y, imageArr[index].pos.w, imageArr[index].pos.h); //原生canvas的绘制图片方法,直接百度搜索 `js drawImage`查看方法的参数
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx.rotate(-4.3 * (Math.PI / 180));
|
||||||
|
|
||||||
|
// console.log('imgList[2]',imgList[2]);
|
||||||
|
ctx.drawImage(imgList[2], imageArr[2].pos.x, imageArr[2].pos.y, imageArr[2].pos.w, imageArr[2].pos.h)
|
||||||
|
|
||||||
|
// 画签文
|
||||||
|
// ctx.rotate(-4 * (Math.PI / 180));
|
||||||
|
ctx.textAlign = 'start'; //type2
|
||||||
|
ctx.textBaseline = 'top'
|
||||||
|
textArr.map((item, index) => {
|
||||||
|
const { x, y } = textArr[index].style.pos
|
||||||
|
ctx.font = textArr[index].style.font; // normal bold
|
||||||
|
|
||||||
|
// title绘制描边
|
||||||
|
if (index == 0) {
|
||||||
|
ctx.lineWidth = 5
|
||||||
|
ctx.strokeStyle = 'white'
|
||||||
|
ctx.strokeText(textArr[index].content, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ctx.fillStyle = textArr[index].style.color ? textArr[index].style.color : theme; //原生canvas的绘制文字方法,属性类似css,确定好文字颜色、粗细、字号、字体、对齐方式
|
||||||
|
ctx.fillText(textArr[index].content, x, y); //绘制文字
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//海报绘制完 ,转成图片对象
|
||||||
|
return mycanvas.toDataURL('image/jpeg', 1);
|
||||||
|
})
|
||||||
|
.then(baseURL => {
|
||||||
|
//返回的图片地址,就是最后海报的地址,可以放在DOM显示
|
||||||
|
let posterImg = document.querySelector('#poster')
|
||||||
|
posterImg.src = baseURL
|
||||||
|
setTimeout(() => {
|
||||||
|
Toast.success({ message: '生成成功!' })
|
||||||
|
gsap.to('.posterPop', { duration: 0.5, autoAlpha: 1 })
|
||||||
|
|
||||||
|
}, 500)
|
||||||
|
})
|
||||||
|
|
||||||
|
return posterUrl
|
||||||
|
}
|
||||||
60
src/plugins/wxshare.js
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import wx from 'weixin-js-sdk';
|
||||||
|
import axios from "axios";
|
||||||
|
import { showToast } from "vant";
|
||||||
|
|
||||||
|
const imgUrl = new URL(`../assets/images/share.jpg`, import.meta.url).href
|
||||||
|
const linkUrl = import.meta.env.VITE_ACTIVITY_URL
|
||||||
|
|
||||||
|
export function wxShare(option) {
|
||||||
|
let url = location.href.split('#')[0];
|
||||||
|
axios.get('https://wx.xfhd.net/wxapi/api/jsconfig?appid=wx41d80a1bb01f658d', {
|
||||||
|
params: { url }
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
console.log('分享配置:', {
|
||||||
|
'title': option.title,
|
||||||
|
'desc': option.desc,
|
||||||
|
'link': linkUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
let data = res.data;
|
||||||
|
wx.config({
|
||||||
|
debug: false, // 开启调试模式
|
||||||
|
appId: data.appId, // 必填,公众号的唯一标识
|
||||||
|
timestamp: data.timestamp, // 必填,生成签名的时间戳
|
||||||
|
nonceStr: data.nonceStr, // 必填,生成签名的随机串
|
||||||
|
signature: data.signature, // 必填,签名,见附录1
|
||||||
|
jsApiList: [
|
||||||
|
'checkJsApi',
|
||||||
|
'showMenuItems',
|
||||||
|
'onMenuShareAppMessage',
|
||||||
|
'onMenuShareTimeline',
|
||||||
|
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
|
||||||
|
})
|
||||||
|
wx.ready(function () {
|
||||||
|
wx.onMenuShareTimeline({
|
||||||
|
link: linkUrl, // 分享链接
|
||||||
|
title: option.title, // 分享标题
|
||||||
|
desc: option.desc, // 分享描述
|
||||||
|
imgUrl: imgUrl, // 分享图标
|
||||||
|
success() {// 用户成功分享后执行的回调函数
|
||||||
|
showToast('分享成功')
|
||||||
|
},
|
||||||
|
});
|
||||||
|
wx.onMenuShareAppMessage({
|
||||||
|
link: linkUrl, // 分享链接
|
||||||
|
title: option.title, // 分享标题
|
||||||
|
desc: option.desc, // 分享描述
|
||||||
|
imgUrl: imgUrl, // 分享图标
|
||||||
|
success() {// 用户成功分享后执行的回调函数
|
||||||
|
showToast('分享成功')
|
||||||
|
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
});
|
||||||
|
}
|
||||||
15
src/store/index.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// pinia仓库
|
||||||
|
import { defineStore } from "pinia"
|
||||||
|
|
||||||
|
|
||||||
|
export const useMainStore = defineStore("counter", {
|
||||||
|
state: () => {
|
||||||
|
return {
|
||||||
|
name: '超级管理员'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 相当于computed属性,对state进行二次加工
|
||||||
|
getters: {},
|
||||||
|
// 异步处理方法
|
||||||
|
actions: {},
|
||||||
|
})
|
||||||
52
src/styles/global.scss
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
@charset "utf-8";
|
||||||
|
$red: red;
|
||||||
|
$green: green;
|
||||||
|
|
||||||
|
// DIV宽高
|
||||||
|
@mixin box($width, $height) {
|
||||||
|
width: $width;
|
||||||
|
height: $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DIV宽高定位
|
||||||
|
@mixin pos($width, $height, $left, $top) {
|
||||||
|
width: $width;
|
||||||
|
height: $height;
|
||||||
|
position: absolute;
|
||||||
|
left: $left;
|
||||||
|
top: $top;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DIV背景
|
||||||
|
@mixin bg_pos($src) {
|
||||||
|
// new URL(`../assets/images/${url}`, import.meta.url).href
|
||||||
|
background-image: url("../assets/images/" + $src);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 100% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
// flex居中定位
|
||||||
|
@mixin flex() {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 全屏样式
|
||||||
|
@mixin fixed() {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// flex垂直水平居中
|
||||||
|
@mixin flexCen() {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
141
vite.config.js
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
import { defineConfig, loadEnv } from 'vite'
|
||||||
|
import vue from '@vitejs/plugin-vue'
|
||||||
|
import { resolve } from 'path'
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite';
|
||||||
|
import { VantResolver } from '@vant/auto-import-resolver';
|
||||||
|
import vitePluginLvdeploy from 'vite-plugin-lvdeploy' //自动化部署
|
||||||
|
import deployConfig from './deploy.config'; //自动化部署配置文件
|
||||||
|
import viteCompression from 'vite-plugin-compression';
|
||||||
|
import { createHtmlPlugin } from 'vite-plugin-html'
|
||||||
|
import postcsspxtoviewport8plugin from 'postcss-px-to-viewport-8-plugin';
|
||||||
|
import pxtoviewportCofig from './config/config'
|
||||||
|
|
||||||
|
const ENV_DIR = resolve(__dirname, './config')
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig(({ command, mode }) => {
|
||||||
|
console.log('mode:', mode);
|
||||||
|
const env = loadEnv(mode, process.cwd(), '')
|
||||||
|
return {
|
||||||
|
// envDir: ENV_DIR, //更改环境变量目录
|
||||||
|
plugins: [
|
||||||
|
vue(),
|
||||||
|
// 多页面配置插件
|
||||||
|
createHtmlPlugin({
|
||||||
|
minify: true,
|
||||||
|
pages: [
|
||||||
|
{
|
||||||
|
filename: 'index',
|
||||||
|
entry: '/src/page/Home/main.js',
|
||||||
|
template: 'index.html',
|
||||||
|
injectOptions: {
|
||||||
|
data: {
|
||||||
|
title: '猜灯谜闹元宵',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
// 按需引入vant组件
|
||||||
|
Components({
|
||||||
|
resolvers: [VantResolver()],
|
||||||
|
}),
|
||||||
|
// 自动化部署
|
||||||
|
vitePluginLvdeploy(deployConfig),
|
||||||
|
AutoImport({
|
||||||
|
imports: ['vue', 'vue-router',]
|
||||||
|
}),
|
||||||
|
// 开启gzip压缩
|
||||||
|
viteCompression({
|
||||||
|
verbose: true, // 默认即可
|
||||||
|
disable: false, //开启压缩(不禁用),默认即可
|
||||||
|
deleteOriginFile: false, //删除源文件
|
||||||
|
threshold: 10240, //压缩前最小文件大小
|
||||||
|
algorithm: 'gzip', //压缩算法
|
||||||
|
ext: '.gz', //文件类型
|
||||||
|
})
|
||||||
|
],
|
||||||
|
server: { // ← ← ← ← ← ←
|
||||||
|
host: '0.0.0.0' // ← 新增内容 ←
|
||||||
|
},
|
||||||
|
// 别名配置
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': resolve(__dirname, './src'),
|
||||||
|
},
|
||||||
|
extensions: ['.js', '.vue', '.json'] // 引入对应的文件时可以忽略其后缀
|
||||||
|
},
|
||||||
|
// sass全局配置
|
||||||
|
css: {
|
||||||
|
postcss: {
|
||||||
|
plugins: [
|
||||||
|
postcsspxtoviewport8plugin({
|
||||||
|
unitToConvert: 'px',
|
||||||
|
viewportWidth: file => {
|
||||||
|
let num = 750;
|
||||||
|
//van是375
|
||||||
|
if (file.indexOf('van') > 0) {
|
||||||
|
num = 375;
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
},
|
||||||
|
unitPrecision: 5, // 单位转换后保留的精度
|
||||||
|
propList: ['*'], // 能转化为vw的属性列表
|
||||||
|
viewportUnit: 'vw', // 希望使用的视口单位
|
||||||
|
fontViewportUnit: 'vw', // 字体使用的视口单位
|
||||||
|
selectorBlackList: ['ignore-'], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
|
||||||
|
minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
|
||||||
|
mediaQuery: true, // 媒体查询里的单位是否需要转换单位
|
||||||
|
replace: true, // 是否直接更换属性值,而不添加备用属性
|
||||||
|
exclude: [], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
|
||||||
|
include: [], // 如果设置了include,那将只有匹配到的文件才会被转换
|
||||||
|
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
|
||||||
|
landscapeUnit: 'vw', // 横屏时使用的单位
|
||||||
|
landscapeWidth: 1628, // 横屏时使用的视口宽度
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
preprocessorOptions: {
|
||||||
|
scss: {
|
||||||
|
javascriptEnabled: true,
|
||||||
|
additionalData: '@import "./src/styles/global.scss";',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// 公共基础路径:构建生产环境时自动载入cdn路径
|
||||||
|
base: mode == 'production' ? env.VITE_CDN + env.VITE_FOLDER + '/' : './',
|
||||||
|
// 打包配置
|
||||||
|
build: {
|
||||||
|
assetsPublicPath: './',
|
||||||
|
assetsDir: 'static',
|
||||||
|
minify: 'terser',
|
||||||
|
chunkSizeWarningLimit: 1500,
|
||||||
|
terserOptions: {
|
||||||
|
compress: {
|
||||||
|
//生产环境时移除console.log()
|
||||||
|
drop_console: false,
|
||||||
|
drop_debugger: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
rollupOptions: {
|
||||||
|
input: {},
|
||||||
|
output: {
|
||||||
|
// node_modules下引用的插件采用分包策略:名称不改变应对浏览器缓存策略
|
||||||
|
"manualChunks": (id) => {
|
||||||
|
if (id.includes("node_modules")) {
|
||||||
|
return "vendor"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 为输出文件加Hash值
|
||||||
|
chunkFileNames: 'static/js/[name]-[hash].js',
|
||||||
|
entryFileNames: 'static/js/[name]-[hash].js',
|
||||||
|
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||