init
All checks were successful
continuous-integration/drone Build is passing
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-11-18 19:51:56 +08:00
commit f3d4feb872
41 changed files with 21550 additions and 0 deletions

154
.drone.yml Normal file
View File

@@ -0,0 +1,154 @@
kind: pipeline
type: docker
name: default
steps:
- name: 测试服-依赖安装&&编译打包
pull: if-not-exists
image: node:20
when:
branch:
- test
commands:
- npm config set registry https://registry.npmmirror.com/
- npm install -g pnpm
- pnpm install
- pnpm build:h5:test
- rm -rf dist.tar
- rm -rf node_modules
- tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: 正式服-依赖安装&&编译打包
pull: if-not-exists
image: node:20
when:
branch:
- master
commands:
- npm config set registry https://registry.npmmirror.com/
- npm install -g pnpm
- pnpm install
- pnpm build:h5
- rm -rf dist.tar
- rm -rf node_modules
- tar -zcvf dist.tar ./dist ./default.conf ./Dockerfile
- name: 测试服-产物上传
pull: if-not-exists
image: appleboy/drone-scp
when:
branch:
- test
settings:
host:
from_secret: HOST_DEV
username:
from_secret: USER_DEV
password:
from_secret: PWD_DEV
port: 22
strip_components: 1
target: /www/builder
source:
- ./dist.tar
- name: 测试服-部署
pull: if-not-exists
image: appleboy/drone-ssh
when:
branch:
- test
settings:
host:
from_secret: HOST_DEV
username:
from_secret: USER_DEV
password:
from_secret: PWD_DEV
port: 22
script:
- cd /www/builder
- mkdir jdt-tz-dev
- tar -xzvf dist.tar -C /www/builder/jdt-tz-dev
- rm -rf dist.tar
- cd jdt-tz-dev
- docker build -t jdt-tz-dev .
- docker stop jdt-tz-dev
- docker rm jdt-tz-dev
- docker run -d -p 8283:80 --restart=always --name jdt-tz-dev jdt-tz-dev
- cd ..
- rm -rf jdt-tz-dev
- name: 正式服-产物上传
pull: if-not-exists
image: appleboy/drone-scp
when:
branch:
- master
settings:
host:
from_secret: HOST_PROD
username:
from_secret: USER_PROD
password:
from_secret: PWD_PROD
port: 22
strip_components: 1
target: /www/builder
source:
- ./dist.tar
- name: 正式服-部署
pull: if-not-exists
image: appleboy/drone-ssh
when:
branch:
- master
settings:
host:
from_secret: HOST_PROD
username:
from_secret: USER_PROD
password:
from_secret: PWD_PROD
port: 22
script:
- cd /www/builder
- mkdir jdt-tz-prod
- tar -xzvf dist.tar -C /www/builder/jdt-tz-prod
- rm -rf dist.tar
- cd jdt-tz-prod
- docker build -t jdt-tz-prod .
- docker stop jdt-tz-prod
- docker rm jdt-tz-prod
- docker run -d -p 8283:80 --restart=always --name jdt-tz-prod jdt-tz-prod
- cd ..
- rm -rf jdt-tz-prod
- name: 企业微信通知
pull: if-not-exists
image: plugins/webhook
when:
branch:
- test
- master
status:
- success
- failure
settings:
urls: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=a2065e21-4f92-4f5b-a432-2c0cd1d965b5
content_type: application/json
template: |
{
"msgtype": "markdown",
"markdown": {
"content": "{{#success build.status}}✅{{else}}❌{{/success}}**{{ repo.owner }}/{{ repo.name }}** (Build #{{build.number}})\n
>**构建结果**: {{ build.status }}
>**构建详情**: [点击查看]({{ build.link }})
>**代码分支**: {{ build.branch }}
>**提交标识**: {{ build.commit }}
>**提交发起**: {{ build.author }}
>**提交信息**: {{ build.message }}
"
}
}

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

6
.env.development Normal file
View File

@@ -0,0 +1,6 @@
# 配置文档参考 https://taro-docs.jd.com/docs/next/env-mode-config
# TARO_APP_ID="开发环境下的小程序appid"
TARO_APP_API = 'https://game.wanzhuanyongcheng.cn'
TARO_APP_WS = 'wss://game.wanzhuanyongcheng.cn/dice/home'

5
.env.production Normal file
View File

@@ -0,0 +1,5 @@
# TARO_APP_ID="生产环境下的小程序appid"
TARO_APP_API = 'https://www.jdt168.com'
TARO_APP_WS = 'wss://www.jdt168.com/dice/home'

5
.env.test Normal file
View File

@@ -0,0 +1,5 @@
# TARO_APP_ID="测试环境下的小程序appid"
TARO_APP_API = 'https://game.wanzhuanyongcheng.cn'
TARO_APP_WS = 'wss://game.wanzhuanyongcheng.cn/dice/home'

8
.eslintrc Normal file
View File

@@ -0,0 +1,8 @@
// ESLint 检查 .vue 文件需要单独配置编辑器:
// https://eslint.vuejs.org/user-guide/#editor-integrations
{
"extends": ["taro/vue3"],
"rules": {
"vue/multi-word-component-names": "off"
}
}

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
dist/
deploy_versions/
.temp/
.rn_temp/
node_modules/
.DS_Store
.swc

5
Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM nginx:alpine
COPY dist/ /usr/share/nginx/html
COPY default.conf /etc/nginx/conf.d/default.conf

10
__tests__/index.test.js Normal file
View File

@@ -0,0 +1,10 @@
import TestUtils from "@tarojs/test-utils-vue3";
describe("Testing", () => {
test("Test", async () => {
const testUtils = new TestUtils();
await testUtils.createApp();
await testUtils.PageLifecycle.onShow("pages/index/index");
expect(testUtils.html()).toMatchSnapshot();
});
});

13
babel.config.js Normal file
View File

@@ -0,0 +1,13 @@
// babel-preset-taro 更多选项和默认值:
// https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
module.exports = {
presets: [
[
"taro",
{
framework: "vue3",
ts: false,
},
],
],
};

8
config/dev.js Normal file
View File

@@ -0,0 +1,8 @@
export default {
logger: {
quiet: false,
stats: true,
},
mini: {},
h5: {},
};

97
config/index.js Normal file
View File

@@ -0,0 +1,97 @@
import { defineConfig } from "@tarojs/cli";
import { join } from "node:path";
import devConfig from "./dev";
import prodConfig from "./prod";
// https://taro-docs.jd.com/docs/next/config#defineconfig-辅助函数
export default defineConfig(async (merge, { command, mode }) => {
const baseConfig = {
projectName: "jdt-yaotouzi",
date: "2024-11-18",
designWidth: 750,
deviceRatio: {
640: 2.34 / 2,
750: 1,
375: 2,
828: 1.81 / 2,
},
sourceRoot: "src",
outputRoot: "dist",
plugins: [],
defineConstants: {},
copy: {
patterns: [],
options: {},
},
framework: "vue3",
compiler: "webpack5",
cache: {
enable: false, // Webpack 持久化缓存配置建议开启。默认配置请参考https://docs.taro.zone/docs/config-detail#cache
},
alias: {
"@": join(__dirname, "..", "src"),
},
mini: {
postcss: {
pxtransform: {
enable: true,
config: {},
},
url: {
enable: true,
config: {
limit: 1024, // 设定转换尺寸上限
},
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: "module", // 转换模式,取值为 global/module
generateScopedName: "[name]__[local]___[hash:base64:5]",
},
},
},
},
h5: {
publicPath: "/",
staticDirectory: "static",
output: {
filename: "js/[name].[hash:8].js",
chunkFilename: "js/[name].[chunkhash:8].js",
},
miniCssExtractPluginOption: {
ignoreOrder: true,
filename: "css/[name].[hash].css",
chunkFilename: "css/[name].[chunkhash].css",
},
postcss: {
autoprefixer: {
enable: true,
config: {},
},
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
config: {
namingPattern: "module", // 转换模式,取值为 global/module
generateScopedName: "[name]__[local]___[hash:base64:5]",
},
},
},
},
rn: {
appName: "taroDemo",
postcss: {
cssModules: {
enable: false, // 默认为 false如需使用 css modules 功能,则设为 true
},
},
},
};
if (process.env.NODE_ENV === "development") {
// 本地开发构建配置(不混淆压缩)
return merge({}, baseConfig, devConfig);
}
// 生产构建配置(默认开启压缩混淆等)
return merge({}, baseConfig, prodConfig);
});

31
config/prod.js Normal file
View File

@@ -0,0 +1,31 @@
export default {
mini: {},
h5: {
/**
* WebpackChain 插件配置
* @docs https://github.com/neutrinojs/webpack-chain
*/
// webpackChain (chain) {
// /**
// * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
// * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
// */
// chain.plugin('analyzer')
// .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
// /**
// * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
// * @docs https://github.com/chrisvfritz/prerender-spa-plugin
// */
// const path = require('path')
// const Prerender = require('prerender-spa-plugin')
// const staticDir = path.join(__dirname, '..', 'dist')
// chain
// .plugin('prerender')
// .use(new Prerender({
// staticDir,
// routes: [ '/pages/index/index' ],
// postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
// }))
// }
},
};

18
default.conf Normal file
View File

@@ -0,0 +1,18 @@
server {
# 监听ipv4
listen 80;
# 监听ipv6
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

7
jest.config.js Normal file
View File

@@ -0,0 +1,7 @@
const defineJestConfig =
require("@tarojs/test-utils-vue3/dist/jest.js").default;
module.exports = defineJestConfig({
testEnvironment: "jsdom",
testMatch: ["<rootDir>/__tests__/**/*.(spec|test).[jt]s?(x)"],
});

86
package.json Normal file
View File

@@ -0,0 +1,86 @@
{
"name": "jdt-yaotouzi",
"version": "1.0.0",
"private": true,
"description": "",
"templateInfo": {
"name": "default",
"typescript": false,
"css": "Sass",
"framework": "Vue3"
},
"scripts": {
"build:weapp": "taro build --type weapp",
"build:swan": "taro build --type swan",
"build:alipay": "taro build --type alipay",
"build:tt": "taro build --type tt",
"build:h5": "taro build --type h5",
"build:h5:test": "taro build --type h5 --mode test",
"build:rn": "taro build --type rn",
"build:qq": "taro build --type qq",
"build:jd": "taro build --type jd",
"build:quickapp": "taro build --type quickapp",
"build:harmony-hybrid": "taro build --type harmony-hybrid",
"dev:weapp": "npm run build:weapp -- --watch",
"dev:swan": "npm run build:swan -- --watch",
"dev:alipay": "npm run build:alipay -- --watch",
"dev:tt": "npm run build:tt -- --watch",
"dev:h5": "npm run build:h5 -- --watch",
"dev:rn": "npm run build:rn -- --watch",
"dev:qq": "npm run build:qq -- --watch",
"dev:jd": "npm run build:jd -- --watch",
"dev:quickapp": "npm run build:quickapp -- --watch",
"dev:harmony-hybrid": "npm run build:harmony-hybrid -- --watch",
"test": "jest",
"lf": "npx prettier --write --end-of-line lf ."
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"author": "",
"dependencies": {
"@babel/runtime": "^7.21.5",
"@tarojs/components": "3.6.35",
"@tarojs/helper": "3.6.35",
"@tarojs/plugin-platform-weapp": "3.6.35",
"@tarojs/plugin-platform-alipay": "3.6.35",
"@tarojs/plugin-platform-tt": "3.6.35",
"@tarojs/plugin-platform-swan": "3.6.35",
"@tarojs/plugin-platform-jd": "3.6.35",
"@tarojs/plugin-platform-qq": "3.6.35",
"@tarojs/plugin-platform-h5": "3.6.35",
"@tarojs/plugin-platform-harmony-hybrid": "3.6.35",
"@tarojs/runtime": "3.6.35",
"@tarojs/shared": "3.6.35",
"@tarojs/taro": "3.6.35",
"@tarojs/plugin-framework-vue3": "3.6.35",
"vue": "^3.0.0"
},
"devDependencies": {
"@babel/core": "^7.8.0",
"@tarojs/cli": "3.6.35",
"@types/webpack-env": "^1.13.6",
"webpack": "5.78.0",
"@tarojs/taro-loader": "3.6.35",
"@tarojs/webpack5-runner": "3.6.35",
"babel-preset-taro": "3.6.35",
"css-loader": "3.4.2",
"style-loader": "1.3.0",
"@tarojs/test-utils-vue3": "^0.1.1",
"@vue/babel-plugin-jsx": "^1.0.6",
"@vue/compiler-sfc": "^3.0.0",
"vue-loader": "^17.1.0",
"eslint-plugin-vue": "^8.0.0",
"eslint-config-taro": "3.6.35",
"eslint": "^8.12.0",
"stylelint": "^14.4.0",
"postcss": "^8.4.18",
"ts-node": "^10.9.1",
"@types/node": "^18.15.11",
"@types/jest": "^29.3.1",
"jest": "^29.3.1",
"jest-environment-jsdom": "^29.5.0"
}
}

19674
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

15
project.config.json Normal file
View File

@@ -0,0 +1,15 @@
{
"miniprogramRoot": "./dist",
"projectname": "jdt-yaotouzi",
"description": "",
"appid": "touristappid",
"setting": {
"urlCheck": true,
"es6": false,
"enhance": false,
"compileHotReLoad": false,
"postcss": false,
"minified": false
},
"compileType": "miniprogram"
}

9
project.tt.json Normal file
View File

@@ -0,0 +1,9 @@
{
"miniprogramRoot": "./",
"projectname": "jdt-yaotouzi",
"appid": "testAppId",
"setting": {
"es6": false,
"minified": false
}
}

20
src/api/index.js Normal file
View File

@@ -0,0 +1,20 @@
import request from "@/utils/request";
// 获取投注项
export const getGameOption = () =>
request({
url: "/dice/betting",
method: "GET",
});
// 获取开奖列表
export const getKaiJiangList = () =>
request({ url: "/dice/draw", method: "GET" });
// 获取用户信息
export const getJfDz = (uid) =>
request({ url: `/dice/userBettingInfo?uid=${uid}`, method: "GET" });
// 获取用户投注记录
export const getTzJl = (uid) =>
request({ url: `/dice/userBettingRecord?uid=${uid}`, method: "GET" });

9
src/app.config.js Normal file
View File

@@ -0,0 +1,9 @@
export default defineAppConfig({
pages: ["pages/index/index", "pages/records/index"],
window: {
backgroundTextStyle: "light",
navigationBarBackgroundColor: "#fff",
navigationBarTitleText: "摇骰子",
navigationBarTextStyle: "black",
},
});

13
src/app.js Normal file
View File

@@ -0,0 +1,13 @@
import { createApp } from "vue";
import Taro from "@tarojs/taro";
import "./app.scss";
const App = createApp({
onLaunch({ query }) {
Taro.setStorageSync("uid", query.uid);
Taro.setStorageSync("id", query.id);
},
onShow(options) {},
});
export default App;

0
src/app.scss Normal file
View File

10
src/config/index.js Normal file
View File

@@ -0,0 +1,10 @@
// 用于配置项目的一些常量如接口地址、websocket地址等
import Taro from "@tarojs/taro";
export const app = {
API_URL: () => `${process.env.TARO_APP_API}`,
API_WS: () =>
`${process.env.TARO_APP_WS}?uid=${Taro.getStorageSync(
"uid"
)}&game_id=${Taro.getStorageSync("id")}`,
};

22
src/index.html Normal file
View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
<meta
content="width=device-width,initial-scale=1,user-scalable=no"
name="viewport"
/>
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-touch-fullscreen" content="yes" />
<meta name="format-detection" content="telephone=no,address=no" />
<meta name="apple-mobile-web-app-status-bar-style" content="white" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>jdt-yaotouzi</title>
<script>
<%= htmlWebpackPlugin.options.script %>
</script>
</head>
<body>
<div id="app"></div>
</body>
</html>

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: "摇骰子",
});

444
src/pages/index/index.scss Normal file
View File

@@ -0,0 +1,444 @@
.index {
background-color: #23684b;
width: 100%;
height: 100%;
//.tips {
// border-radius: 0;
// width: 100%;
// height: 60px;
//}
.userInfo {
width: 92%;
margin: 10px auto;
//margin: auto;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.left {
display: flex;
align-items: center;
image {
width: 100px;
height: 100px;
border-radius: 50%;
border: 2px solid #ffffff;
}
.userText {
margin-left: 10px;
width: 250px;
// 文字超出显示省略号
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.userName {
font-size: 35px;
}
.userScore {
font-size: 25px;
}
}
}
.right {
background-color: #429c78;
border-radius: 50px;
flex: 1;
display: flex;
justify-content: flex-start;
align-items: center;
padding: 10px;
.icon {
background-image: url("../../static/dz.png");
background-size: 100% 100%;
width: 50px;
height: 50px;
display: inline-block;
}
.bean {
margin-left: 20px;
}
}
}
.gameInfo {
width: 92%;
margin: auto;
padding: 20px;
display: flex;
justify-content: space-between;
align-items: center;
.left {
display: flex;
image {
width: 110px;
height: 110px;
}
.game_box {
margin-left: 10px;
width: 200px;
// 文字超出显示省略号
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.gameName {
font-size: 35px;
}
.lottery {
font-size: 25px;
display: flex;
justify-content: flex-start;
.text {
background-color: #e9422f;
width: 55px;
height: 55px;
text-align: center;
line-height: 55px;
font-size: 40px;
margin-right: 30px;
border-radius: 10px;
}
}
}
}
.center {
font-size: 30px;
}
.right {
.btn {
width: 137px;
background: linear-gradient(-180deg, #fbe039, #fdc413);
font-size: 30px;
text-align: center;
border: 1px solid #ffedc5;
border-radius: 26px;
}
}
}
.opt {
display: flex;
justify-content: space-between;
margin: 0 10px;
flex-wrap: wrap;
.item {
width: 19%;
text-align: center;
//margin: auto 3px;
margin-top: 10px;
border: 1px dashed #228960;
position: relative;
.name {
//font-size: 30px;
font-weight: bold;
}
.odd {
font-size: 25px;
}
.mask {
position: absolute;
//background: #7950f2;
width: 100%;
height: 100%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 10px;
text-align: center;
line-height: 100px;
background-color: rgba(0, 0, 0, 0.4);
.text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
.item1 {
width: 24%;
}
}
.opt1 {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
text-align: center;
box-sizing: border-box;
height: 400px;
padding: 0 10px;
margin-top: 10px;
.optt {
width: 27%;
height: 100%;
}
.item {
width: 100%;
margin-bottom: 10px;
text-align: center;
border: 1px dashed #228960;
position: relative;
height: 48%;
.name {
font-size: 55px;
font-weight: bold;
position: absolute;
top: 40%;
left: 50%;
transform: translate(-50%, -50%);
}
.odd {
font-size: 25px;
position: absolute;
width: 100%;
top: 60%;
left: 50%;
transform: translate(-50%, -10%);
}
.mask {
position: absolute;
//background: #7950f2;
width: 100%;
height: 100%;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.4);
.text {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
}
}
.base {
background-image: url("../../static/cais.png");
background-size: 100% 100%;
//flex: 1;
width: 43%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-end;
align-items: center;
position: relative;
overflow: hidden;
.qz {
background-image: url("../../static/qz.png");
background-size: 100% 100%;
width: 90%;
height: 25%;
margin-bottom: 20px;
line-height: 100px;
color: #fbe039;
font-weight: bold;
}
}
}
.bottomBar {
width: 100%;
box-sizing: border-box;
height: 130px;
position: fixed;
bottom: 0;
border-top-left-radius: 40px;
border-top-right-radius: 40px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 50px;
.btn1 {
background-image: url("../../static/cz.png");
background-size: 100% 100%;
width: 150px;
height: 100px;
text-align: center;
line-height: 95px;
}
.btn2 {
background-image: url("../../static/ssd.png");
background-size: 100% 100%;
width: 150px;
height: 100px;
text-align: center;
line-height: 100px;
}
.btn3 {
background-image: url("../../static/tz.png");
background-size: 100% 100%;
width: 150px;
height: 100px;
text-align: center;
line-height: 95px;
}
}
}
.subColor {
background-color: #228960;
color: white;
border-radius: 10px;
}
.btn1,
.btn2,
.btn3 {
&:active {
animation: btn ease 0.3s;
}
}
@keyframes btn {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
// 虚线边框且闪烁3秒
.flicker {
animation: flicker 1s infinite;
}
@keyframes flicker {
0% {
border: 1px dashed #228960;
background-color: #228960;
}
50% {
border: 1px dashed white;
background: rgba(225, 225, 225, 0.5);
}
100% {
border: 1px dashed #228960;
background-color: #228960;
}
}
.expanding-div {
width: 10px;
height: 90px;
background-image: url("../../static/qzzz.png");
background-size: 100% 100%;
position: absolute;
left: 50%;
top: 55%;
transform: translate(-50%, -50%);
animation-name: rotate, expand;
animation-duration: 2s, 3s;
animation-delay: 0s, 2s;
animation-timing-function: linear, ease-in-out;
animation-fill-mode: forwards, forwards;
overflow: hidden;
text-align: center;
line-height: 90px;
color: red;
}
@keyframes rotate {
0% {
top: -100%;
transform: translate(-50%, -50%) rotate(0deg);
}
100% {
top: 50%;
transform: translate(-50%, -50%) rotate(360deg);
}
}
@keyframes expand {
from {
width: 10px;
}
to {
width: 200px;
}
}
// 倒计时动画
.activeQz {
animation: change-size 1s ease-in-out infinite;
font-weight: bold;
}
@keyframes change-size {
0% {
opacity: 1;
}
90% {
font-size: 300px;
opacity: 0;
}
100% {
opacity: 1;
}
}
.card {
width: 92%;
padding: 20px;
margin: 10px auto;
border-radius: 10px;
.sub {
margin-top: 10px;
color: #555555;
}
}
.close {
width: 35px;
height: 35px;
border-radius: 50%;
background-color: rgba(255, 0, 0, 0.4);
position: absolute;
top: -10px;
right: -10px;
text-align: center;
line-height: 30px;
font-size: 25px;
z-index: 1;
}

678
src/pages/index/index.vue Normal file
View File

@@ -0,0 +1,678 @@
<template>
<view class="index">
<!-- 公告 -->
<view class="subColor tips"></view>
<scroll-view style="height: 100vh" scroll-y>
<!-- 用户信息 -->
<view class="subColor userInfo">
<view class="left">
<image :src="userInfo.avatarUrl"></image>
<view class="userText">
<view class="userName">{{ userInfo.nickName || "用户" }}</view>
<view class="userScore">积分: {{ userInfo.integral || 0 }}</view>
</view>
</view>
<view class="right">
<view class="icon"></view>
<view class="bean">{{ userInfo.pulse || 0 }}</view>
</view>
</view>
<!-- 游戏信息 -->
<view class="subColor gameInfo">
<view class="left">
<image src="../../static/tx.png"></image>
<view class="game_box">
<view class="gameName">{{ kJData[0]?.Periods || 0 }}期开奖</view>
<view class="lottery">
<text class="text">{{ kJData[0]?.Start || 0 }}</text>
<text class="text">{{ kJData[0]?.End || 0 }}</text>
</view>
</view>
</view>
<view class="center">
<view>剩余{{ time }}s开奖</view>
</view>
<view class="right">
<view class="btn" @click="utils.vibrateShort(() => toPage(1))"
>开奖记录</view
>
<view class="btn mt-15" @click="utils.vibrateShort(() => toPage(2))">
投注记录
</view>
</view>
</view>
<!-- 选项区 -->
<view class="opt">
<view
class="subColor item"
:class="{ flicker: item.flicker }"
v-for="item in odds"
:key="item.id"
@click.stop="utils.vibrateShort(() => addNum(item))"
>
<view class="name">{{ item.name }}</view>
<view class="odd">{{ item.odds }}</view>
<view class="mask" v-if="item.status">
<view class="text">{{ item.numStr }}</view>
</view>
<view
class="close"
v-if="item.status"
@click.stop="utils.vibrateShort(() => clearOneBet(item))"
>
x
</view>
</view>
</view>
<view class="opt1" v-if="odds1.length > 0">
<view class="optt">
<view
class="subColor item"
:class="{ flicker: odds1[0].flicker }"
@click.stop="utils.vibrateShort(() => addNum(odds1[0]))"
>
<view class="name">{{ odds1[0].name }}</view>
<view class="odd">{{ odds1[0].odds }}</view>
<view class="mask" v-if="odds1[0].status">
<view class="text">{{ odds1[0].numStr }}</view>
</view>
<view
class="close"
v-if="odds1[0].status"
@click.stop="utils.vibrateShort(() => clearOneBet(odds1[0]))"
>
x
</view>
</view>
<view
class="subColor item"
:class="{ flicker: odds1[1].flicker }"
@click.stop="utils.vibrateShort(() => addNum(odds1[1]))"
>
<view class="name">{{ odds1[1].name }}</view>
<view class="odd">{{ odds1[1].odds }}</view>
<view class="mask" v-if="odds1[1].status">
<view class="text">{{ odds1[1].numStr }}</view>
</view>
<view
class="close"
v-if="odds1[1].status"
@click.stop="utils.vibrateShort(() => clearOneBet(odds1[1]))"
>
x
</view>
</view>
</view>
<view class="base">
<view class="qz">
<view :class="{ activeQz: isExpanding }">
{{ qzTitle }}
</view>
</view>
<view class="expanding-div" v-if="isShow">
{{ nowKJInfo.Start }} - {{ nowKJInfo.End }}
</view>
</view>
<view class="optt">
<view
class="subColor item"
style="height: 100%"
:class="{ flicker: odds1[2].flicker }"
@click.stop="utils.vibrateShort(() => addNum(odds1[2]))"
>
<view class="name">{{ odds1[2].name }}</view>
<view class="odd">{{ odds1[2].odds }}</view>
<view class="mask" v-if="odds1[2].status">
<!-- <view class="text">{{ odds1[2].markNum }}</view> -->
<view class="text">{{ odds1[2].numStr }}</view>
</view>
<view
class="close"
v-if="odds1[2].status"
@click.stop="utils.vibrateShort(() => clearOneBet(odds1[2]))"
>
x
</view>
</view>
</view>
</view>
<view class="opt">
<view
class="subColor item item1"
:class="{ flicker: item.flicker }"
v-for="item in odds2"
:key="item.id"
@click.stop="utils.vibrateShort(() => addNum(item))"
>
<view class="name">{{ item.name }}</view>
<view class="odd">{{ item.odds }}</view>
<view class="mask" v-if="item.status">
<!-- <view class="text">{{ item.markNum }}</view> -->
<view class="text">{{ item.numStr }}</view>
</view>
<view
class="close"
v-if="item.status"
@click.stop="utils.vibrateShort(() => clearOneBet(item))"
>
x
</view>
</view>
</view>
<scroll-view style="height: 25%" scroll-y>
<view
class="card subColor"
v-for="(item, index) in kjList"
:key="index"
>
<view>
<view>
{{ item.Periods }}期开奖:
<text style="color: red">{{ item.Start }} , {{ item.End }}</text>
<text
v-for="(num, i) in item.Name"
:key="i"
:style="{
color: numColor(num),
}"
>
, {{ num }}
</text>
</view>
</view>
</view>
</scroll-view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="subColor bottomBar">
<view class="btn1" @click="utils.vibrateShort(() => clearBet(true))"
>重置</view
>
<view class="btn2" @click="utils.vibrateShort(() => changeOdd())"
>X{{ oddVal }}</view
>
<view class="btn3" @click="utils.vibrateShort(() => verifyBet())"
>投注</view
>
</view>
</view>
</template>
<script setup>
import { ref, onBeforeUnmount } from "vue";
import Taro from "@tarojs/taro";
import { app } from "@/config";
import { getGameOption, getKaiJiangList, getJfDz } from "@/api";
import utils from "@/utils";
const ws = ref(null);
// 是否显示开奖动画
const isShow = ref(false);
// 是否开启文字放大动画
const isExpanding = ref(false);
const odds = ref([]);
const odds1 = ref([]);
const odds2 = ref([]);
const qzTitle = ref("请投注");
const time = ref(0);
const nowKJInfo = ref({});
const userInfo = ref({});
const zjObj = ref([]);
const iMsgNum = ref(0);
const numColor = (num) => {
switch (num) {
case "大":
return "red";
case "小":
return "green";
// case '单':
// return 'orange'
// case '双':
// return 'blue'
case "和":
return "purple";
default:
return "black";
}
};
Taro.useDidShow(() => {
startMusic(
"https://files.wanzhuanyongcheng.com/file/music/yaotouzi/bg.MP3",
true
);
initWs();
initData();
});
Taro.useDidHide(() => {
ws.value?.close();
});
onBeforeUnmount(() => {
ws.value?.close();
});
const initData = () => {
qzTitle.value = "请投注";
getOddList();
getKJList();
getUserInfo();
};
const arr = ["2+2", "3+3", "4+4", "5+5", "6+6"];
// const arr1 = ["大", "小", "单", "双"];
// const arr2 = ["和", "3", "4", "5", "6", "8", "9", "10", "11"];
const arr1 = ["大", "小", "和"];
const arr2 = ["3", "4", "5", "6", "8", "9", "10", "11"];
// 投注列表
const getOddList = async () => {
const res = await getGameOption();
odds.value = [];
odds1.value = [];
odds2.value = [];
res.data.data.forEach((item) => {
const { name, ID, odds: odd, max } = item;
const newItem = {
id: ID,
name: name,
odds: odd,
max: max,
markNum: 0,
numStr: 0,
status: false,
flicker: false,
};
if (Taro.getStorageSync("odds")) {
odds.value = Taro.getStorageSync("odds");
odds1.value = Taro.getStorageSync("odds1");
odds2.value = Taro.getStorageSync("odds2");
} else {
if (arr1.includes(name)) {
odds1.value.push(newItem);
} else if (arr.includes(name)) {
odds.value.push(newItem);
} else if (arr2.includes(name)) {
odds2.value.push(newItem);
}
}
});
// const el = odds2.value.splice(0, 1)[0];
// odds2.value.splice(4, 0, el);
// console.log(odds1.value)
};
// 获取个人信息
const getUserInfo = async () => {
const res = await getJfDz(Taro.getStorageSync("uid"));
userInfo.value = res.data.data;
};
const kJData = ref([]);
const kjList = ref([]);
// 开奖列表
const getKJList = async () => {
const res = await getKaiJiangList();
kJData.value = res.data.data;
kjList.value = res.data.data.map((item) => {
return {
...item,
Name: item.Name.split("-"),
};
});
};
// 初始化游戏
const initWs = () => {
ws.value = new WebSocket(`${app.API_WS()}`);
ws.value.onopen = async () => {
Taro.showToast({
title: "连接游戏服务器成功",
mask: true,
icon: "none",
});
};
ws.value.onmessage = async (e) => {
const res = JSON.parse(e.data);
switch (res.code) {
case 200:
let num = Number(res.data);
time.value = num;
if (num === 5) {
openDraw();
}
break;
case 401:
Taro.showToast({
title: "未登录",
mask: true,
icon: "none",
});
break;
case 403:
Taro.showToast({
title: "未能操作",
mask: true,
icon: "none",
});
break;
case 400:
Taro.showToast({
title: "豆子不足",
mask: true,
icon: "none",
});
break;
case 5:
Taro.showToast({
title: "投注成功",
mask: true,
icon: "none",
});
break;
case 100:
Taro.showToast({
title: res.msg,
icon: "none",
});
break;
case 101:
Taro.showToast({
title: res.msg,
icon: "none",
});
break;
case 301:
Taro.showToast({
title: res.msg,
icon: "none",
});
setTimeout(() => {
Taro.navigateBack({
delta: 1,
});
}, 2000);
default:
iMsgNum.value++;
zjObj.value = res;
break;
}
};
ws.value.onerror = () => {
console.log("连接游戏服务器失败");
Taro.showToast({
title: "连接游戏服务器失败",
mask: true,
icon: "none",
});
};
};
const isKj = ref(false);
// 开奖倒计时
const openDraw = () => {
isExpanding.value = true;
isKj.value = true;
startMusic(
"https://files.wanzhuanyongcheng.com/file/music/yaotouzi/djs.MP3",
false
);
for (let i = 5; i > 0; i--) {
setTimeout(() => {
// i <= 3 && (fontColor.value = "red");
qzTitle.value = i.toString();
}, 1000 * (5 - i));
}
setTimeout(async () => {
isExpanding.value = false;
const res = await getKaiJiangList();
nowKJInfo.value = res.data.data[0];
qzTitle.value = `${nowKJInfo.value?.Periods || 0}期开奖`;
playAminExpand();
setTimeout(() => {
startFlicker();
}, 6000);
isKj.value = false;
}, 5000);
};
const oddVal = ref(100);
const changeOdd = () => {
switch (oddVal.value) {
case 100:
oddVal.value = 1000;
break;
// case 10:
// oddVal.value = 100
// break
case 1000:
oddVal.value = 100;
break;
}
};
const clearBet = (val) => {
clearItems(odds.value, val);
clearItems(odds1.value, val);
clearItems(odds2.value, val);
savsData();
};
const clearItems = (items, isClear = false) => {
items.forEach((item) => {
item.markNum = 0;
if (isClear) {
item.numStr = 0;
item.status = false;
}
});
};
const addNum = (item) => {
if (isKj.value)
return Taro.showToast({
title: "正在开奖,不能下注",
icon: "none",
});
if (item.name === "单" || item.name === "双")
return Taro.showToast({
title: `${item.name}不能下注`,
icon: "none",
});
item.status = true;
item.markNum += oddVal.value;
item.numStr += oddVal.value;
if (item.markNum >= item.max && item.numStr >= item.max) {
item.markNum = item.max;
item.numStr = item.max;
Taro.showToast({
title: `单注不能超过${item.max}`,
icon: "none",
});
}
savsData();
};
// 投注
const verifyBet = () => {
if (isKj.value)
return Taro.showToast({
title: "正在开奖,不能下注",
icon: "none",
});
let numCount = 0;
odds.value.forEach((item) => {
if (item.markNum > 0) {
numCount += item.markNum;
}
});
odds1.value.forEach((item) => {
if (item.markNum > 0) {
numCount += item.markNum;
}
});
odds2.value.forEach((item) => {
if (item.markNum > 0) {
numCount += item.markNum;
}
});
if (numCount === 0)
return Taro.showToast({
title: "请选择选项,再投注",
icon: "none",
});
if (numCount > userInfo.value?.pulse)
return Taro.showToast({
title: "投注豆子不足",
icon: "none",
});
const data = [];
odds.value.forEach((item) => {
if (item.markNum > 0) {
data.push({
bid: item.id,
number: item.markNum,
name: item.name,
});
}
});
odds1.value.forEach((item) => {
if (item.markNum > 0) {
data.push({
bid: item.id,
number: item.markNum,
name: item.name,
});
}
});
odds2.value.forEach((item) => {
if (item.markNum > 0) {
data.push({
bid: item.id,
number: item.markNum,
name: item.name,
});
}
});
ws.value?.send(
JSON.stringify({
type: 1,
data: data,
})
);
setTimeout(() => {
getUserInfo();
}, 1000);
qzTitle.value = "已投注";
clearBet(false);
savsData();
};
// 前往开奖和投注记录
const toPage = (type) => {
Taro.navigateTo({
url: `/pages/records/index?type=${type}`,
});
};
// 播放展开动画
const playAminExpand = () => {
isShow.value = true;
startMusic(
"https://files.wanzhuanyongcheng.com/file/music/yaotouzi/kj.MP3",
false
);
setTimeout(() => {
isShow.value = false;
initData();
}, 10000);
};
// 开始闪烁
const startFlicker = () => {
flickerItems(odds.value);
flickerItems(odds1.value);
flickerItems(odds2.value);
zjObj.value = [];
};
const flickerItems = (items) => {
delData();
items.forEach((item) => {
zjObj.value.forEach((item2) => {
if (item.name === item2.name) {
item.flicker = true;
setTimeout(() => {
item.flicker = false;
}, 3000);
}
});
});
startMusic(
"https://files.wanzhuanyongcheng.com/file/music/yaotouzi/bg.MP3",
true
);
};
const innerAudioContext = Taro.createInnerAudioContext();
const startMusic = (path, loop = false) => {
// const innerAudioContext = Taro.createInnerAudioContext()
innerAudioContext.autoplay = true;
innerAudioContext.src = path;
innerAudioContext.loop = loop;
innerAudioContext.onPlay();
innerAudioContext.onError();
};
const clearOneBet = (item) => {
item.markNum = 0;
item.numStr = 0;
item.status = false;
savsData();
};
const savsData = () => {
Taro.setStorageSync("odds", odds.value);
Taro.setStorageSync("odds1", odds1.value);
Taro.setStorageSync("odds2", odds2.value);
};
const delData = () => {
Taro.removeStorageSync("odds");
Taro.removeStorageSync("odds1");
Taro.removeStorageSync("odds2");
};
</script>
<style lang="scss" scoped>
@import "./index.scss";
</style>

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarBackgroundColor: "#23684B",
enablePullDownRefresh: true,
});

141
src/pages/records/index.vue Normal file
View File

@@ -0,0 +1,141 @@
<template>
<view class="app">
<template v-if="typeNum === 1">
<view class="card" v-for="(item, index) in list" :key="index">
<view>
<view>
{{ item.Periods }}期开奖:
<text
v-for="(num, i) in item.Name"
:key="i"
:style="{
color: numColor(num),
}"
>
{{ num }},
</text>
</view>
</view>
</view>
</template>
<template v-else>
<view v-if="list.length > 0">
<view class="card desc" v-for="(item, index) in list" :key="index">
<view>
<view>{{ item.Periods }}期投注:</view>
<view>
点数:
<text style="color: red">{{ item.Name }}</text>
</view>
<view class="sub">
投注时间:
<text>{{ item.DrawTime }}</text>
</view>
</view>
<view style="text-align: right">
<view style="color: red" v-if="item.State === 1"
>+{{ item.DrawNum }}积分</view
>
<view style="color: green">-{{ item.Number }}豆子</view>
</view>
</view>
</view>
<view v-else>
<view style="margin-top: 100px">暂无记录.....</view>
</view>
</template>
</view>
</template>
<script setup>
import { ref } from "vue";
import Taro from "@tarojs/taro";
import { getKaiJiangList, getTzJl } from "@/api";
const list = ref([]);
const typeNum = ref(0);
Taro.useLoad((options) => {
typeNum.value = Number(options.type);
Taro.setNavigationBarTitle({
title: options.type === "1" ? "开奖记录" : "投注记录",
});
getData();
});
Taro.usePullDownRefresh(() => {
getData();
});
const numColor = (num) => {
switch (num) {
case "大":
return "red";
case "小":
return "green";
case "单":
return "orange";
case "双":
return "blue";
case "和":
return "purple";
default:
return "black";
}
};
const getData = async () => {
let res;
if (typeNum.value === 1) {
res = await getKaiJiangList();
list.value = res.data.data.map((item) => {
return {
...item,
Name: item.Name.split("-"),
};
});
} else {
res = await getTzJl(Taro.getStorageSync("uid"));
list.value = res.data.data || [];
}
Taro.stopPullDownRefresh();
};
</script>
<style lang="scss">
.taro_page {
background-color: #f5f5f5 !important;
}
.app {
display: flex;
flex-direction: column;
align-items: center;
//justify-content: center;
font-size: 35px;
width: 100%;
padding: 10px;
box-sizing: border-box;
}
.card {
width: 95%;
padding: 20px;
background-color: #fff;
margin-top: 10px;
border-radius: 10px;
.sub {
margin-top: 10px;
color: #555555;
}
}
.desc {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
}
</style>

BIN
src/static/cais.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

BIN
src/static/cz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/static/dz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
src/static/qz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/static/qzzz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
src/static/ssd.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

BIN
src/static/tx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
src/static/tz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

11
src/utils/index.js Normal file
View File

@@ -0,0 +1,11 @@
import Taro from "@tarojs/taro";
export default {
vibrateShort: (Func) =>
Taro.vibrateShort({
type: "medium",
complete: () => {
Func();
},
}),
};

25
src/utils/request.js Normal file
View File

@@ -0,0 +1,25 @@
import { app } from "@/config";
import Taro from "@tarojs/taro";
const request = (request) => {
return new Promise((resolve, reject) => {
Taro.request({
url: `${app.API_URL()}${request.url}`,
method: request.method,
timeout: 5000,
dataType: "json",
header: request.header || {},
data: request.data || {},
success: (res) => {
resolve(res.data);
// Taro.hideLoading()
},
fail: (err) => {
reject(err);
Taro.hideLoading();
},
});
});
};
export default request;