优化: 修复安全隐患、路由权限、环境变量、CSS问题,新增组件,清理无用文件

This commit is contained in:
lihanqi
2026-02-13 18:15:46 +08:00
parent 492d839e9b
commit ca0e2f9370
38 changed files with 1992 additions and 1293 deletions

8
.env Normal file
View File

@@ -0,0 +1,8 @@
# API 基础地址
VITE_API_BASE_URL=https://www.yicaishuzhi.com/api
# Coze AI 聊天助手配置
VITE_COZE_BOT_ID=7524958762676305971
VITE_COZE_SDK_URL=https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.10/libs/cn/index.js
VITE_COZE_ICON_URL=https://wanlvzhisong-1387432270.cos.ap-guangzhou.myqcloud.com/2026/01/14/fb2fb9fb-7b71-4fc0-81f9-9eb7215d0fc3.jpeg
VITE_COZE_AVATAR_URL=https://wanlvzhisong-1387432270.cos.ap-guangzhou.myqcloud.com/2026/01/14/99595bd6-455b-43d9-857c-c5e6a485d94a.png

8
.env.development Normal file
View File

@@ -0,0 +1,8 @@
# 开发环境配置
VITE_API_BASE_URL=http://localhost:8123/api
# Coze AI 聊天助手配置
VITE_COZE_BOT_ID=7524958762676305971
VITE_COZE_SDK_URL=https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.10/libs/cn/index.js
VITE_COZE_ICON_URL=https://wanlvzhisong-1387432270.cos.ap-guangzhou.myqcloud.com/2026/01/14/fb2fb9fb-7b71-4fc0-81f9-9eb7215d0fc3.jpeg
VITE_COZE_AVATAR_URL=https://wanlvzhisong-1387432270.cos.ap-guangzhou.myqcloud.com/2026/01/14/99595bd6-455b-43d9-857c-c5e6a485d94a.png

195
DEPLOY.md
View File

@@ -1,195 +0,0 @@
# 部署说明文档
## 🚀 打包部署流程
### 1. 打包前准备
确保已安装依赖:
```bash
npm install
```
### 2. 执行打包
```bash
npm run build
```
打包完成后,会在项目根目录生成 `dist` 文件夹。
### 3. 缓存控制策略
本项目已配置完善的缓存控制策略,避免浏览器缓存导致更新不生效:
#### ✅ 已配置的缓存方案
1. **文件名 Hash 化**`vite.config.js` 已配置)
- 所有 JS、CSS、图片等资源文件名都会带上 hash 值
- 例如:`index.a1b2c3d4.js``style.e5f6g7h8.css`
- 每次构建后,修改过的文件 hash 会变化,自动避免缓存
2. **HTML 文件不缓存**
- `index.html` 设置为不缓存,每次都会获取最新版本
- 通过服务器配置实现(见下方)
#### 📝 服务器配置
##### Apache 服务器(使用 .htaccess
项目已包含 `public/.htaccess` 文件,打包后会自动复制到 `dist` 目录。
##### Nginx 服务器
参考项目根目录的 `nginx.conf.example` 文件,配置说明:
```nginx
# HTML 文件不缓存
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
# 静态资源长期缓存
location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
```
### 4. 部署步骤
#### 方法一:手动部署
1.`dist` 目录下的所有文件上传到服务器
2. 配置服务器Apache 或 Nginx
3. 重启服务器
#### 方法二:使用 FTP/SFTP
```bash
# 上传 dist 目录到服务器
scp -r dist/* user@your-server:/var/www/html/
```
#### 方法三:使用 Docker可选
创建 `Dockerfile`
```dockerfile
FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf.example /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
### 5. 更新部署注意事项
#### ⚠️ 每次更新部署时:
1. **清理旧文件**
```bash
# 删除服务器上的旧文件
rm -rf /var/www/html/*
```
2. **上传新文件**
```bash
# 上传新的 dist 文件
scp -r dist/* user@your-server:/var/www/html/
```
3. **清理服务器缓存**(如果使用了缓存服务器)
```bash
# Nginx
nginx -s reload
# Apache
systemctl reload apache2
```
4. **通知用户清理浏览器缓存**(可选)
- 可以在系统中添加版本提示
- 或者使用 Service Worker 强制更新
### 6. 验证部署
部署完成后,验证步骤:
1. **清除浏览器缓存**
- Chrome: `Ctrl + Shift + Delete` 或 `Cmd + Shift + Delete`
- 或使用隐身模式访问
2. **检查文件版本**
- 打开浏览器开发者工具F12
- 查看 Network 标签
- 确认 JS/CSS 文件名带有新的 hash 值
3. **检查 HTML 缓存**
- 查看 `index.html` 的响应头
- 确认 `Cache-Control: no-cache`
### 7. 常见问题
#### Q1: 用户反馈看到的还是旧版本?
**解决方案:**
1. 确认服务器配置正确(.htaccess 或 nginx 配置)
2. 清理 CDN 缓存(如果使用了 CDN
3. 通知用户强制刷新Ctrl + F5 或 Cmd + Shift + R
#### Q2: 静态资源 404 错误?
**解决方案:**
1. 检查资源路径配置
2. 确认 `vite.config.js` 中的 `base` 配置正确
3. 检查服务器的静态文件路径
#### Q3: SPA 路由刷新 404
**解决方案:**
- Apache: 确保 `.htaccess` 中的 rewrite 规则生效
- Nginx: 确保配置了 `try_files $uri $uri/ /index.html;`
### 8. 自动化部署(可选)
#### 使用 GitHub Actions
创建 `.github/workflows/deploy.yml`
```yaml
name: Deploy
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install dependencies
run: npm install
- name: Build
run: npm run build
- name: Deploy to server
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{ secrets.REMOTE_USER }}
TARGET: /var/www/html/
```
### 9. 性能优化建议
1. **启用 Gzip/Brotli 压缩**
2. **使用 CDN 加速静态资源**
3. **配置 HTTP/2**
4. **启用 HTTPS**
## 📞 技术支持
如有部署问题,请联系技术团队。

View File

@@ -1,155 +0,0 @@
现在大乐透也开放了预测流程由双色球的5步推测准备、推测首球、推测随球、推测蓝球、确认结果变为6步推测准备、推测前区首球、推测前区随球、推测后区首球、推测前区随球、确认结果。推测准备第一步这一页和双色球的流程一样只不过是上期红球号码变为了上期前区号码上期蓝球号码变成了上次后区号码仅10期开奖号码是通过GET http://localhost:8123/api/dlt-draw/recent-10-draw-ids这个接口获取根据期号获取中奖号码的接口路径是GET http://localhost:8123/api/dlt-draw/draw/{{drawId}}/numbers响应示例为
{
"code": 0,
"success": true,
"message": "操作成功",
"data": [
1,
7,
9,
16,
30,
2,
5
]
}
然后点击继续调用这个接口POST http://localhost:8123/api/dlt/ball-analysis/predict-first-ball入参为
```
public class FirstBallPredictionRequest {
private String level; high/middle/low:高位/中位/低位
private List<Integer> redBalls; 5个前区号码
private List<Integer> blueBalls; 2个后期号码
}
```
响应示例为
```
{
"code": 0,
"success": true,
"message": "操作成功",
"data": [
29,
20,
22,
33,
35,
1,
2,
3,
4,
5,
11,
30
]
}
```
然后进入第二步解析出12个球号作为推荐的12个前区球号
第二步的布局和双色球的的一样先选择1个球号在选择2个球号。然后拿上一步选择的位high/middle/low+ 3个号码第二步选择的 + 5个号码第一步的5个上期前区 + 2个号码第一步的2个上期后区去调用接口POST http://localhost:8123/api/dlt/ball-analysis/predict-follower-ball
入参为:
```
@Data
public class FollowerBallPredictionRequest {
private String level;
private List<Integer> wellRegardedBalls;
private List<Integer> previousFrontBalls;
private List<Integer> previousBackBalls;
}
```
响应示例为
```
{
"code": 0,
"success": true,
"message": "操作成功",
"data": [
1,
2,
3,
4,
5,
6,
35,
29
]
}
```
然后进入第三步解析出8个球号作为推荐的8个前区球号
先选择4个前区球号再选择2个后区球号。然后拿第一步选择的位high/middle/low+ 5个号码第二步选择的首球球号+这一步选择的4个球号+ 5个号码第一步选择5个上期前区球号 + 2个号码第一步选择的2个上期后区球号+ 2个号码这一步选择的2个下期后区。去调用接口POST http://localhost:8123/api/dlt/ball-analysis/predict-back-ball
入参为
```
{
"level": "high",
"nextFrontBalls": [1,2,3,4,5],
"previousFrontBalls": [1,2,3,4,5],
"previousBackBalls": [6,7],
"nextBackBalls": [8,9]
}
```
响应为
```
{
"code": 0,
"success": true,
"message": "操作成功",
"data": [
1,
2,
9,
7
]
}
```
然后进入第四步解析出4个球号作为推荐的4个后区球号
选择一个推荐的后区首球。然后拿第一步选择的位high/middle/low+ 1个号码这一步选择的一个后区首球+ 5个号码第二步选择的前区首球球号+第三步选择的4个前区随球+ 5个号码第一步选择5个上期前区球号 + 2个号码第一步选择的2个上期后区球号。去调用接口POST http://localhost:8123/api/dlt/ball-analysis/predict-follow-back-ball
入参
```
{
"level": "high",
"backFirstBall": 6,
"nextFrontBalls": [1,2,3,4,5],
"previousFrontBalls": [1,2,3,4,5],
"previousBackBalls": [6,7]
}
```
响应示例为
```
{
"code": 0,
"success": true,
"message": "操作成功",
"data": [
1,
3,
5
]
}
```
然后进入第五步解析出3个球号作为推荐的3个后区球号
选择一个推荐的后区随球,点击继续,进入下一步。
第六步:确认推测,这一步跟双色球的第五步一样。

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="">
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -2,8 +2,6 @@
import { onMounted, computed } from 'vue'
import { useRoute } from 'vue-router'
import { userStore } from './store/user'
import HelloWorld from './components/HelloWorld.vue'
import TheWelcome from './components/TheWelcome.vue'
import BottomNavigation from './components/BottomNavigation.vue'
import CozeChat from './components/CozeChat.vue'
@@ -132,7 +130,6 @@ body {
top: 0;
z-index: 999;
box-shadow: 0 2px 12px rgba(248, 85, 85, 0.3);
position: relative;
overflow: hidden;
}

View File

@@ -2,8 +2,7 @@ import axios from 'axios'
// 创建大乐透专用的axios实例
const dltApi = axios.create({
// baseURL: 'http://localhost:8123/api',
baseURL: 'https://www.yicaishuzhi.com/api',
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 300000, // 5分钟超时时间
withCredentials: true, // 关键支持跨域携带cookie和session
headers: {
@@ -19,10 +18,28 @@ dltApi.interceptors.response.use(
// 检查是否是session过期的响应
if (data && data.success === false) {
const message = data.message || ''
// 专门处理账号在其他设备登录的情况
if (message.includes('其他设备登录') || message.includes('当前会话已失效')) {
console.log('检测到账号在其他设备登录,正在踢出当前会话...')
import('element-plus').then(({ ElMessage }) => {
ElMessage.warning({
message: '您的账号已在其他设备登录,请重新登录',
duration: 3000,
showClose: true
})
})
import('../../store/user.js').then(({ userStore }) => {
userStore.logout(true)
})
return data
}
if (message.includes('未登录') || message.includes('登录过期') || message.includes('无权限') || message.includes('Invalid session')) {
console.log('检测到session过期清除本地登录状态')
// 动态导入userStore避免循环依赖
import('../../store/user.js').then(({ userStore }) => {
userStore.logout()
})

View File

@@ -2,8 +2,7 @@ import axios from 'axios'
// 创建axios实例
const api = axios.create({
// baseURL: 'http://localhost:8123/api',
baseURL: 'https://www.yicaishuzhi.com/api',
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 300000, // 5分钟超时时间300秒
withCredentials: true, // 关键支持跨域携带cookie和session
headers: {
@@ -142,6 +141,32 @@ export const lotteryApi = {
return api.get('/user/get/login')
},
// 获取用户信息(用于编辑)
getUserInfo() {
return api.get('/user/get/login')
},
// 更新用户信息
updateUserInfo(userInfo) {
console.log('调用更新用户信息接口:', userInfo)
return api.post('/user/update', userInfo, {
headers: {
'Content-Type': 'application/json'
}
})
},
// 上传文件
uploadFile(file) {
const formData = new FormData()
formData.append('file', file)
return api.post('/file/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
},
// 获取用户统计信息总用户数和VIP用户数
getUserCount() {
return api.get('/user/count')
@@ -489,33 +514,11 @@ export const lotteryApi = {
return api.get(`/operation-history/user/${userId}/module/${operationModule}`)
},
// 管理员登录
// 管理员登录(复用用户登录接口,后台会根据角色判断权限)
adminLogin(username, password) {
// 模拟API请求实际项目中应该调用真实的后端API
return new Promise((resolve) => {
setTimeout(() => {
// 模拟验证管理员账号密码
if (username === 'admin' && password === '123456') {
resolve({
success: true,
data: {
id: 1,
userName: '系统管理员',
userAccount: 'admin',
userRole: 'admin',
avatar: null,
createTime: new Date().toISOString()
},
message: '登录成功'
})
} else {
resolve({
success: false,
data: null,
message: '账号或密码错误'
})
}
}, 1000) // 模拟网络延迟
return api.post('/user/login', {
userAccount: username,
userPassword: password
})
},

View File

@@ -8,17 +8,15 @@
background-color: rgb(240, 242, 245);
}
a,
.green {
a {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
color: #e53e3e;
transition: 0.3s;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
color: #ff6b6b;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -62,10 +62,10 @@ export default {
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08);
position: fixed;
bottom: 0;
left: 0;
right: 0;
left: 50%;
transform: translateX(-50%);
max-width: 850px;
margin: 0 auto;
width: 100%;
}
.nav-item {
@@ -132,9 +132,6 @@ export default {
.bottom-nav {
height: 65px;
max-width: 100%;
left: 15px;
right: 15px;
border-radius: 0 0 8px 8px;
}
.nav-item {
@@ -162,9 +159,6 @@ export default {
.bottom-nav {
height: 60px;
max-width: 100%;
left: 0;
right: 0;
border-radius: 0;
}
.nav-item {

View File

@@ -48,7 +48,7 @@ export default {
// 创建script标签
const script = document.createElement('script')
script.src = 'https://lf-cdn.coze.cn/obj/unpkg/flow-platform/chat-app-sdk/1.2.0-beta.10/libs/cn/index.js'
script.src = import.meta.env.VITE_COZE_SDK_URL
script.async = true
script.onload = () => {
console.log('Coze SDK 加载完成')
@@ -94,7 +94,7 @@ export default {
// 保存Coze SDK配置
const config = {
config: {
bot_id: '7524958762676305971',
bot_id: import.meta.env.VITE_COZE_BOT_ID,
},
componentProps: {
title: '精彩猪手',
@@ -120,12 +120,12 @@ export default {
},
userInfo: {
id: userStore.user?.id?.toString() || 'guest',
url: 'https://wanlvzhisong-1387432270.cos.ap-guangzhou.myqcloud.com/2026/01/14/99595bd6-455b-43d9-857c-c5e6a485d94a.png',
url: import.meta.env.VITE_COZE_AVATAR_URL,
nickname: userStore.user?.username || '游客',
},
ui: {
base: {
icon: 'https://wanlvzhisong-1387432270.cos.ap-guangzhou.myqcloud.com/2026/01/14/fb2fb9fb-7b71-4fc0-81f9-9eb7215d0fc3.jpeg',
icon: import.meta.env.VITE_COZE_ICON_URL,
layout: isMobile ? 'mobile' : 'pc',
zIndex: 1000,
lang:'zh-CN'
@@ -172,15 +172,10 @@ export default {
const jwtExpireSeconds = 3600 // 1小时
const tokenDurationSeconds = 86400 // 24小时
// 根据环境确定API基础URL
let baseUrl = ''
if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
baseUrl = 'http://localhost:8123'
} else {
baseUrl = 'https://www.yicaishuzhi.com'
}
// 使用环境变量获取API基础URL
const apiBaseUrl = import.meta.env.VITE_API_BASE_URL
const url = `${baseUrl}/api/jwt/one-step-token?jwtExpireSeconds=${jwtExpireSeconds}&sessionName=${userId}&tokenDurationSeconds=${tokenDurationSeconds}`
const url = `${apiBaseUrl}/jwt/one-step-token?jwtExpireSeconds=${jwtExpireSeconds}&sessionName=${userId}&tokenDurationSeconds=${tokenDurationSeconds}`
console.log('Fetching Coze token, URL:', url)

View File

@@ -0,0 +1,189 @@
<template>
<div class="custom-select-wrapper">
<div class="custom-select" :class="{ 'open': isOpen }">
<div class="select-trigger" @click="toggleOpen">
<span class="select-value">{{ selectedLabel || placeholder }}</span>
<svg class="select-arrow" viewBox="0 0 24 24">
<path d="M7 10l5 5 5-5z" fill="currentColor"/>
</svg>
</div>
<div v-if="isOpen" class="select-dropdown">
<div
v-for="option in options"
:key="option.value"
class="select-option"
:class="{ 'selected': String(modelValue) === String(option.value) }"
@click="selectOption(option)"
>
{{ option.label }}
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CustomSelect',
props: {
modelValue: {
type: [String, Number],
default: ''
},
options: {
type: Array,
required: true,
validator: (arr) => arr.every(item => 'value' in item && 'label' in item)
},
placeholder: {
type: String,
default: '请选择'
}
},
emits: ['update:modelValue'],
data() {
return {
isOpen: false
}
},
computed: {
selectedLabel() {
const option = this.options.find(opt => String(opt.value) === String(this.modelValue))
return option ? option.label : ''
}
},
methods: {
toggleOpen() {
this.isOpen = !this.isOpen
},
selectOption(option) {
this.$emit('update:modelValue', option.value)
this.isOpen = false
},
closeDropdown() {
this.isOpen = false
},
handleClickOutside(event) {
if (this.$el && !this.$el.contains(event.target)) {
this.isOpen = false
}
}
},
mounted() {
document.addEventListener('click', this.handleClickOutside)
},
beforeUnmount() {
document.removeEventListener('click', this.handleClickOutside)
}
}
</script>
<style scoped>
.custom-select-wrapper {
width: 100%;
}
.custom-select {
position: relative;
width: 100%;
}
.select-trigger {
width: 100%;
padding: 10px 14px;
border: 2px solid #e8eef5;
border-radius: 10px;
background: white;
color: #2d3748;
font-size: 13px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
transition: border-color 0.2s ease;
user-select: none;
}
.select-trigger:hover {
border-color: #d0d8e8;
}
.custom-select.open .select-trigger {
border-color: #4a90e2;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
.select-value {
flex: 1;
text-align: left;
}
.select-arrow {
width: 14px;
height: 14px;
color: #999;
transition: transform 0.2s ease;
flex-shrink: 0;
margin-left: 8px;
}
.custom-select.open .select-arrow {
transform: rotate(180deg);
color: #4a90e2;
}
.select-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 2px solid #4a90e2;
border-top: none;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
z-index: 1000;
max-height: 180px;
overflow-y: auto;
}
.select-option {
padding: 10px 14px;
color: #2d3748;
cursor: pointer;
transition: background-color 0.15s ease;
font-size: 13px;
line-height: 1.4;
}
.select-option:hover {
background: #f5f7fa;
}
.select-option.selected {
background: #f0f4f8;
color: #4a90e2;
font-weight: 500;
}
/* 滚动条美化 */
.select-dropdown::-webkit-scrollbar {
width: 5px;
}
.select-dropdown::-webkit-scrollbar-track {
background: transparent;
}
.select-dropdown::-webkit-scrollbar-thumb {
background: #d0d8e8;
border-radius: 2px;
}
.select-dropdown::-webkit-scrollbar-thumb:hover {
background: #b0b8d8;
}
</style>

View File

@@ -1,63 +1,63 @@
import { createRouter, createWebHistory } from 'vue-router'
import { ElMessage } from 'element-plus'
import LotterySelection from '../views/LotterySelection.vue'
import LotteryPremium from '../views/LotteryPremium.vue'
import Home from '../views/ssq/Home.vue'
import DltHome from '../views/dlt/Home.vue'
import LotteryInfo from '../views/LotteryInfo.vue'
import Profile from '../views/Profile.vue'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
import ResetPassword from '../views/ResetPassword.vue'
import PredictRecords from '../views/PredictRecords.vue'
import DltPredictRecords from '../views/dlt/PredictRecords.vue'
import ExcelImportManagement from '../views/ExcelImportManagement.vue'
import ExchangeRecords from '../views/ExchangeRecords.vue'
import TrendAnalysis from '../views/ssq/TrendAnalysis.vue'
import SurfaceAnalysis from '../views/ssq/SurfaceAnalysis.vue'
import LineAnalysis from '../views/ssq/LineAnalysis.vue'
import SsqTableAnalysis from '../views/ssq/SsqTableAnalysis.vue'
import DataAnalysis from '../views/DataAnalysis.vue'
import HelpCenter from '../views/HelpCenter.vue'
import AboutUs from '../views/AboutUs.vue'
import UserAgreement from '../views/UserAgreement.vue'
import UserGuide from '../views/UserGuide.vue'
import MemberAgreement from '../views/MemberAgreement.vue'
import HitAnalysis from '../views/ssq/HitAnalysis.vue'
import DltHitAnalysis from '../views/dlt/HitAnalysis.vue'
import UsageStats from '../views/ssq/UsageStats.vue'
import DltUsageStats from '../views/dlt/UsageStats.vue'
import PrizeStatistics from '../views/ssq/PrizeStatistics.vue'
import DltPrizeStatistics from '../views/dlt/PrizeStatistics.vue'
// 前台页面懒加载
const LotterySelection = () => import('../views/LotterySelection.vue')
const LotteryPremium = () => import('../views/LotteryPremium.vue')
const Home = () => import('../views/ssq/Home.vue')
const DltHome = () => import('../views/dlt/Home.vue')
const LotteryInfo = () => import('../views/LotteryInfo.vue')
const Profile = () => import('../views/Profile.vue')
const Login = () => import('../views/Login.vue')
const Register = () => import('../views/Register.vue')
const ResetPassword = () => import('../views/ResetPassword.vue')
const PredictRecords = () => import('../views/PredictRecords.vue')
const DltPredictRecords = () => import('../views/dlt/PredictRecords.vue')
const ExcelImportManagement = () => import('../views/ExcelImportManagement.vue')
const ExchangeRecords = () => import('../views/ExchangeRecords.vue')
const DataAnalysis = () => import('../views/DataAnalysis.vue')
const HelpCenter = () => import('../views/HelpCenter.vue')
const AboutUs = () => import('../views/AboutUs.vue')
const UserAgreement = () => import('../views/UserAgreement.vue')
const UserGuide = () => import('../views/UserGuide.vue')
const MemberAgreement = () => import('../views/MemberAgreement.vue')
const PrivacyPolicy = () => import('../views/PrivacyPolicy.vue')
// 双色球相关页面
import SsqLottery from '../views/ssq/Lottery.vue'
const SsqLottery = () => import('../views/ssq/Lottery.vue')
const TrendAnalysis = () => import('../views/ssq/TrendAnalysis.vue')
const SurfaceAnalysis = () => import('../views/ssq/SurfaceAnalysis.vue')
const LineAnalysis = () => import('../views/ssq/LineAnalysis.vue')
const SsqTableAnalysis = () => import('../views/ssq/SsqTableAnalysis.vue')
const HitAnalysis = () => import('../views/ssq/HitAnalysis.vue')
const UsageStats = () => import('../views/ssq/UsageStats.vue')
const PrizeStatistics = () => import('../views/ssq/PrizeStatistics.vue')
// 大乐透相关页面
import DltLottery from '../views/dlt/Lottery.vue'
import DltTableAnalysis from '../views/dlt/DltTableAnalysis.vue'
import DltSurfaceAnalysis from '../views/dlt/SurfaceAnalysis.vue'
import DltLineAnalysis from '../views/dlt/LineAnalysis.vue'
import DltTrendAnalysis from '../views/dlt/TrendAnalysis.vue'
const DltLottery = () => import('../views/dlt/Lottery.vue')
const DltTableAnalysis = () => import('../views/dlt/DltTableAnalysis.vue')
const DltSurfaceAnalysis = () => import('../views/dlt/SurfaceAnalysis.vue')
const DltLineAnalysis = () => import('../views/dlt/LineAnalysis.vue')
const DltTrendAnalysis = () => import('../views/dlt/TrendAnalysis.vue')
const DltHitAnalysis = () => import('../views/dlt/HitAnalysis.vue')
const DltUsageStats = () => import('../views/dlt/UsageStats.vue')
const DltPrizeStatistics = () => import('../views/dlt/PrizeStatistics.vue')
// 精推版页面
import JtSsqHome from '../views/jt/SsqHome.vue'
import JtDltHome from '../views/jt/DltHome.vue'
const JtSsqHome = () => import('../views/jt/SsqHome.vue')
const JtDltHome = () => import('../views/jt/DltHome.vue')
// 后台管理相关组件
import AdminLogin from '../views/admin/AdminLogin.vue'
import AdminLayout from '../views/admin/layout/AdminLayout.vue'
import AdminVipCodeManagement from '../views/admin/VipCodeManagement.vue'
import AdminExcelImportManagement from '../views/admin/ExcelImportManagement.vue'
import AdminDltExcelImportManagement from '../views/admin/DltExcelImportManagement.vue'
import AdminPredictionManagement from '../views/admin/PredictionManagement.vue'
import AdminDltPredictionManagement from '../views/admin/DltPredictionManagement.vue'
import AdminPrizeStatistics from '../views/admin/PrizeStatistics.vue'
import AdminDltPrizeStatistics from '../views/admin/DltPrizeStatistics.vue'
import AdminUsageStats from '../views/ssq/UsageStats.vue'
import AdminDltUsageStats from '../views/dlt/UsageStats.vue'
const AdminLogin = () => import('../views/admin/AdminLogin.vue')
const AdminLayout = () => import('../views/admin/layout/AdminLayout.vue')
const AdminVipCodeManagement = () => import('../views/admin/VipCodeManagement.vue')
const AdminExcelImportManagement = () => import('../views/admin/ExcelImportManagement.vue')
const AdminDltExcelImportManagement = () => import('../views/admin/DltExcelImportManagement.vue')
const AdminPredictionManagement = () => import('../views/admin/PredictionManagement.vue')
const AdminDltPredictionManagement = () => import('../views/admin/DltPredictionManagement.vue')
const AdminPrizeStatistics = () => import('../views/admin/PrizeStatistics.vue')
const AdminDltPrizeStatistics = () => import('../views/admin/DltPrizeStatistics.vue')
const AdminUsageStats = () => import('../views/ssq/UsageStats.vue')
const AdminDltUsageStats = () => import('../views/dlt/UsageStats.vue')
const routes = [
// 前台用户路由
@@ -258,7 +258,13 @@ const routes = [
path: '/member-agreement',
name: 'MemberAgreement',
component: MemberAgreement,
meta: { requiresAuth: true }
meta: { requiresAuth: false }
},
{
path: '/privacy-policy',
name: 'PrivacyPolicy',
component: PrivacyPolicy,
meta: { requiresAuth: false }
},
{
path: '/table-analysis',
@@ -483,7 +489,16 @@ const routes = [
const router = createRouter({
history: createWebHistory(),
routes
routes,
scrollBehavior(to, from, savedPosition) {
// 如果有保存的位置(比如浏览器后退),使用保存的位置
if (savedPosition) {
return savedPosition
} else {
// 否则滚动到页面顶部
return { top: 0, behavior: 'smooth' }
}
}
})
// 路由守卫 - 权限控制
@@ -523,6 +538,18 @@ router.beforeEach((to, from, next) => {
console.error('加载用户状态出错:', error)
next('/cpzsadmin/login')
})
} else if (to.meta.requiresAuth) {
// 前台需要登录的页面权限检查
import('../store/user.js').then(({ userStore }) => {
if (!userStore.isLoggedIn) {
ElMessage.warning('请先登录后再访问该页面')
next('/login')
} else {
next()
}
}).catch(() => {
next('/login')
})
} else {
next()
}

View File

@@ -47,79 +47,7 @@ body.admin-body {
overflow: hidden;
}
/* 页面头部样式 */
.page-header {
text-align: center;
padding: 60px 20px 30px;
background: linear-gradient(135deg, #e53e3e 0%, #ff6b6b 100%);
color: white;
margin-bottom: 0;
position: relative;
overflow: hidden;
}
.page-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><defs><pattern id="pattern" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse"><circle cx="20" cy="20" r="1.5" fill="rgba(255,255,255,0.1)"/></pattern></defs><rect width="100" height="100" fill="url(%23pattern)"/></svg>');
pointer-events: none;
}
.page-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
position: relative;
z-index: 1;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.page-subtitle {
font-size: 16px;
opacity: 0.9;
position: relative;
z-index: 1;
}
/* 通用按钮样式 */
.btn {
display: inline-block;
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
font-weight: bold;
text-align: center;
transition: all 0.3s;
border: none;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background: linear-gradient(135deg, #e53e3e, #ff6b6b);
color: white;
}
.btn-primary:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(229, 62, 62, 0.3);
}
.btn-secondary {
background: #f8f9fa;
color: #666;
border: 1px solid #e0e0e0;
}
.btn-secondary:hover:not(:disabled) {
background: #e9ecef;
border-color: #ccc;
}
/* 通用按钮样式补充(基础样式在 App.vue 中定义) */
.btn:disabled {
opacity: 0.7;
cursor: not-allowed;
@@ -215,20 +143,7 @@ body.admin-body {
}
}
/* 加载动画 */
.loading {
display: inline-block;
width: 16px;
height: 16px;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: white;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* 加载动画在 App.vue 中已定义,此处不再重复 */
/* 错误提示样式 */
.error-message {
@@ -255,4 +170,15 @@ body.admin-body {
/* 隐藏子页面顶部背景图的浮动元素 */
.page-header-modern::before {
display: none !important;
}
/* 移除移动端 tap 高亮效果(保留键盘导航的 outline 以确保无障碍访问) */
a, button {
-webkit-tap-highlight-color: transparent;
}
/* 协议链接去除高亮背景 */
.terms-link {
-webkit-tap-highlight-color: transparent;
background: transparent !important;
}

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
<!-- 顶部Banner区域 -->
<div class="banner-section">
<div class="banner-content">
<img src="https://jingcaishuju.oss-cn-beijing.aliyuncs.com/banner1.png" alt="精彩猪手" class="banner-image" />
<img src="https://yicaishuzhi-1326058838.cos.ap-beijing.myqcloud.com/4d1d2e40-140f-45e9-9f4d-6dafb716cdb2.png" alt="精彩猪手" class="banner-image" />
</div>
</div>

View File

@@ -3,7 +3,7 @@
<!-- 顶部Banner区域 -->
<div class="banner-section">
<div class="banner-content">
<img src="https://jingcaishuju.oss-cn-beijing.aliyuncs.com/banner.png" alt="精彩猪手" class="banner-image" />
<img src="https://yicaishuzhi-1326058838.cos.ap-beijing.myqcloud.com/d7c7f147-45f8-4999-8595-d97f37276a71.png" alt="精彩猪手" class="banner-image" />
</div>
</div>

View File

@@ -21,16 +21,6 @@
<div class="agreement-content">
<!-- 标题 -->
<div class="agreement-title-section">
<h2 class="main-title">精彩猪手会员服务协议</h2>
<p class="intro-text">
欢迎使用精彩猪手数据服务<br>
精彩猪手是一款彩票数据姿态逻辑分析推测工具是各位会员朋友的超级数据助理
</p>
<div class="notice-box">
在开始使用我们的服务之前请会员仔细阅读并充分理解本会员服务协议的全部内容
本协议是会员与我们西安溢彩数智科技有限公司之间关于使用本服务的法律协议
一旦会员使用本服务即表示会员已同意接受本协议的约束
</div>
</div>
<!-- 第一章 -->
@@ -208,6 +198,13 @@
</div>
</div>
<!-- 重要提示 -->
<div class="notice-box">
在开始使用我们的服务之前请会员仔细阅读并充分理解本会员服务协议的全部内容
本协议是会员与我们西安溢彩数智科技有限公司之间关于使用本服务的法律协议
一旦会员使用本服务即表示会员已同意接受本协议的约束
</div>
<!-- 底部信息 -->
<div class="footer-info">
<p class="company-name">西安溢彩数智科技有限公司</p>
@@ -307,7 +304,7 @@ export default {
/* 标题区域 */
.agreement-title-section {
text-align: center;
margin-bottom: 30px;
margin-bottom: 10px;
}
.main-title {
@@ -333,6 +330,7 @@ export default {
font-size: 14px;
color: #666;
line-height: 1.6;
margin-bottom: 20px;
}
/* 章节样式 */

411
src/views/PrivacyPolicy.vue Normal file
View File

@@ -0,0 +1,411 @@
<template>
<div class="privacy-policy-page">
<!-- 页面头部 -->
<div class="page-header-modern">
<div class="header-content">
<button class="back-btn" @click="goBack">
<svg viewBox="0 0 24 24" class="back-icon">
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path>
</svg>
</button>
<div class="header-info">
<div class="header-text">
<h1 class="header-title">个人信息收集与处理规则</h1>
<p class="header-subtitle">精彩猪手网站个人信息保护政策</p>
</div>
</div>
</div>
</div>
<!-- 主要内容 -->
<div class="policy-content">
<!-- 标题区域 -->
<div class="policy-title-section">
</div>
<!-- 引言 -->
<div class="policy-section">
<div class="section-content">
<p>尊敬的会员欢迎来到精彩猪手网站以下简称"本网站"为了更好地保障您的个人权益我们依据国家法律法规监管要求和标准规范结合业务开展的实际情况制定了精彩猪手网站个人信息保护政策旨在明确本网站收集存储使用传输删除等个人信息处理活动的具体规则保障您的个人信息权益与隐私安全</p>
<p>本规则适用于所有访问注册使用本网站服务的会员以下简称"您"以及本网站运营团队参与网站运营的相关人员和第三方服务提供商对个人信息的处理行为当您访问注册或使用本网站服务时即表示您已充分阅读理解并同意本政策的全部内容</p>
</div>
</div>
<!-- 第一章 -->
<div class="policy-section">
<h3 class="section-title">1核心处理原则</h3>
<div class="section-content">
<p><strong>1合法正当诚信原则</strong>本网站仅在法律法规允许的范围内出于合法正当的目的处理您的个人信息不通过误导欺诈胁迫等方式收集或使用个人信息</p>
<p><strong>2最小必要原则</strong>仅收集与本网站服务功能直接相关的个人信息限于实现处理目的的最小范围不过度收集信息</p>
<p><strong>3公开透明原则</strong>以显著方式清晰易懂的语言公开个人信息处理规则明确告知您处理目的方式和范围</p>
<p><strong>4安全保障原则</strong>采取必要的技术和管理措施保障您的个人信息在收集存储使用等全生命周期的安全防止未经授权的访问泄露篡改或丢失</p>
<p><strong>5会员权利保障原则</strong>充分保障您对个人信息享有的知情权访问权更正权删除权撤回同意权等合法权益并提供便捷的行使渠道</p>
</div>
</div>
<!-- 第二章 -->
<div class="policy-section">
<h3 class="section-title">2个人信息收集范围与方式</h3>
<div class="section-content">
<p>本网站仅在实现特定服务功能的必要范围内通过以下方式收集您的个人信息具体收集类型将根据您使用的服务场景动态调整</p>
<h4 class="sub-title">1必要收集的个人信息</h4>
<p><strong>账户注册信息</strong>当您注册本网站账户时需提供自设账号自设昵称手机号码经验证以及自设密码将采用强加密算法存储不保留明文此类信息用于身份验证账户登录密码找回及重要通知推送是保障账户安全和正常使用的基础</p>
<h4 class="sub-title">2可选收集的个人信息</h4>
<p>为提升服务质量本网站可能在特定服务场景下如个性化推荐活动参与客户咨询收集以下信息您可自主选择是否提供</p>
<ul>
<li>1个人资料信息如姓名性别头像等用于完善会员信息标记</li>
<li>2彩票偏好信息用于提供个性化内容和服务推荐</li>
<li>3联系方式信息如微信通讯地址等仅用于日常必要时的沟通交流或活动奖品寄送线上支付线下服务对接等特定目的</li>
<li>4获客渠道信息用于掌握触达潜在用户完成用户转化的核心通路</li>
<li>5所属省市级地域信息用于一般性的会员群属统计</li>
</ul>
<h4 class="sub-title">3敏感个人信息的特殊收集</h4>
<p>本网站当前服务业务无需对敏感个人信息的特殊收集故不会主动收集此类信息</p>
<h4 class="sub-title">4收集方式说明</h4>
<ul>
<li>1主动提交您通过注册表单留言表单咨询窗口等主动填写并提交的信息</li>
<li>2系统自动采集通过网站服务器日志Cookie等技术手段自动收集的访问数据您可通过浏览器设置管理Cookie权限</li>
<li>3第三方获取若您通过社交账号等第三方平台登录本网站将仅获取您授权公开的昵称头像等信息具体以第三方平台的授权协议为准</li>
<li>4问卷调查本网站客服可能会向您发出有关网站服务的问卷调查其中所有问题您均有权选答或据答</li>
</ul>
</div>
</div>
<!-- 第三章 -->
<div class="policy-section">
<h3 class="section-title">3个人信息使用规则</h3>
<div class="section-content">
<p>1本网站仅在以下合法目的范围内使用收集的个人信息</p>
<ul>
<li> 保障网站服务的正常运行如账户管理身份验证安全防护</li>
<li> 履行服务承诺如提供内容浏览个性化推荐会员支持</li>
<li> 优化服务质量如分析会员行为改进功能设计修复系统漏洞</li>
<li> 开展合法营销活动如推送活动通知优惠信息您可随时拒绝此类推送</li>
<li> 遵守法律法规要求如配合监管检查履行信息留存义务</li>
</ul>
<p>2若需将个人信息用于本规则未明确的其他目的本网站将提前向您告知并重新取得您的同意</p>
<p>3本网站利用个人信息进行自动化决策如个性化推荐将保证决策的透明度和结果公平公正不实行不合理的差别待遇</p>
<p>4未经您的单独同意本网站不会将个人信息用于广告推送或与其他第三方共享</p>
</div>
</div>
<!-- 第四章 -->
<div class="policy-section">
<h3 class="section-title">4个人信息存储与安全保障</h3>
<div class="section-content">
<h4 class="sub-title">1存储规范</h4>
<ul>
<li> 您的个人信息将存储于中国大陆境内的服务器符合数据本地化存储要求</li>
<li> 存储期限遵循"必要最短时间"原则超出实现服务目的所需的存储期限后将自动删除或进行匿名化处理</li>
<li> 您注销账户后本网站将在30日内完成个人信息的删除或匿名化处理法律法规另有规定的除外</li>
</ul>
<h4 class="sub-title">2安全措施</h4>
<ul>
<li> 技术防护采用HTTPS传输加密AES256存储加密访问权限分级管理等技术防止数据泄露篡改</li>
<li> 运维保障定期开展安全审计漏洞扫描建立数据备份机制保留最近90天历史数据及时修复安全隐患</li>
<li> 人员管理对接触个人信息的运营人员进行严格资质审核和保密培训签订保密协议明确岗位职责</li>
</ul>
<p>3若发生个人信息泄露篡改丢失等安全事件本网站将立即启动应急响应机制采取补救措施并在事件发生后72小时内告知您事件相关情况及处理措施法律法规另有规定的除外</p>
</div>
</div>
<!-- 第五章 -->
<div class="policy-section">
<h3 class="section-title">5个人信息共享与第三方合作</h3>
<div class="section-content">
<p>1本网站原则上不向第三方共享您的个人信息仅在以下特殊情形下经您单独同意或法律法规允许时共享</p>
<ul>
<li> 为履行服务必需如委托第三方服务商提供云存储CDN加速支付结算等服务将与第三方签订保密协议明确数据处理范围和安全义务</li>
<li> 为遵守法律规定如配合司法机关监管部门的合法调查取证要求</li>
<li> 为保护合法权益如为保护您或他人的人身财产安全或维护本网站的合法权益</li>
</ul>
<p>2向第三方共享个人信息时将严格限制共享范围仅提供实现服务目的所必需的信息且要求第三方采取与本网站同等的安全保护措施第三方不得超出约定范围处理个人信息</p>
<p>3若第三方服务提供商发生合并分立解散等情形本网站将要求其及时返还或删除所持有您的个人信息并向您告知接收方信息如涉及信息转移</p>
</div>
</div>
<!-- 第六章 -->
<div class="policy-section">
<h3 class="section-title">6您的权利及行使方式</h3>
<div class="section-content">
<p>您可依法行使以下个人信息相关权利本网站将提供便捷的行使渠道不设置不合理条件限制您的权利行使</p>
<ul>
<li><strong>1知情权</strong>您可通过查阅本规则了解个人信息的收集使用共享等具体情况</li>
<li><strong>2访问权与复制权</strong>您可登录个人账户"我的"页面查看您的个人信息副本</li>
<li><strong>3更正权</strong>若您发现个人信息不准确或不完整可通过直接修改或联系客服协助更正</li>
<li><strong>4删除权</strong>在符合法律法规规定的情形下如信息收集目的已实现您撤回同意您可要求删除个人信息联系客服后将及时处理</li>
<li><strong>5撤回同意权</strong>您可通过联系客服撤回对个人信息处理的同意撤回同意后本网站将停止相关信息处理活动但不影响撤回前基于同意已进行的合法处理行为的效力</li>
<li><strong>6投诉举报权</strong>若您认为本网站的个人信息处理行为侵害了您的权益可通过客服联系方式投诉举报我们将在15个工作日内予以答复</li>
</ul>
<p><strong>权利行使方式</strong>通过微信客服渠道申请时需完成身份验证如短信验证码以保障信息安全</p>
</div>
</div>
<!-- 第七章 -->
<div class="policy-section">
<h3 class="section-title">7规则更新与通知</h3>
<div class="section-content">
<p>1本规则将根据法律法规更新服务功能调整等情况适时修订修订后将在网站显著位置公示更新内容及生效时间公示期不少于30日</p>
<p>2若修订内容涉及您的核心权益如收集范围扩大使用目的变更将通过微信等方式向您单独通知您继续使用本网站服务即表示同意修订后的规则</p>
<p>3您可随时访问网站查看本规则的最新版本</p>
</div>
</div>
<!-- 第八章 -->
<div class="policy-section">
<h3 class="section-title">8附则</h3>
<div class="section-content">
<p>1本规则的解释权归本网站运营主体所有</p>
<p>2若您与本网站就个人信息处理产生争议可先通过客服渠道协商解决协商不成的可向有管辖权的人民法院提起诉讼</p>
<p>3本政策自2026年1月26日起生效</p>
</div>
</div>
<!-- 第九章 -->
<div class="policy-section">
<h3 class="section-title">9联系方式</h3>
<div class="section-content">
<p><strong>运营主体</strong>西安溢彩数智科技有限公司</p>
<p><strong>注册地址</strong>陕西省西安市高新区</p>
<p><strong>微信客服二维码</strong></p>
<div class="qrcode-placeholder">
<img src="/assets/home/erweima.png" alt="客服二维码" class="qrcode-img" />
</div>
</div>
</div>
<!-- 底部信息 -->
<div class="footer-info">
<p class="company-name">西安溢彩数智科技有限公司</p>
<p class="date">2026年2月1日</p>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'PrivacyPolicy',
methods: {
goBack() {
this.$router.go(-1)
}
}
}
</script>
<style scoped>
.privacy-policy-page {
min-height: 100vh;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
padding: 0;
}
/* 页面头部 */
.page-header-modern {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
color: white;
padding: 20px 0;
position: relative;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
align-items: center;
padding: 0 20px;
position: relative;
z-index: 1;
}
.back-btn {
background: rgba(255, 255, 255, 0.2);
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 12px;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
color: white;
margin-right: 16px;
}
.back-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: scale(1.05);
}
.back-icon {
width: 24px;
height: 24px;
fill: currentColor;
}
.header-info {
flex: 1;
}
.header-title {
font-size: 24px;
font-weight: 700;
margin: 0 0 4px 0;
color: white;
}
.header-subtitle {
font-size: 14px;
opacity: 0.8;
margin: 0;
}
/* 主要内容 */
.policy-content {
max-width: 850px;
margin: 0 auto;
padding: 24px 20px;
}
/* 标题区域 */
.policy-title-section {
text-align: center;
margin-bottom: 10px;
}
/* 章节样式 */
.policy-section {
background: white;
border-radius: 16px;
padding: 24px;
margin-bottom: 20px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.section-title {
font-size: 18px;
font-weight: 700;
color: #333;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 2px solid #4facfe;
}
.section-content {
font-size: 14px;
color: #555;
line-height: 1.8;
}
.section-content p {
margin-bottom: 12px;
}
.section-content p:last-child {
margin-bottom: 0;
}
.sub-title {
font-size: 15px;
font-weight: 600;
color: #333;
margin: 16px 0 12px 0;
}
/* 列表样式 */
.section-content ul {
padding-left: 20px;
margin: 12px 0;
}
.section-content ul li {
margin-bottom: 8px;
line-height: 1.6;
}
/* 二维码 */
.qrcode-placeholder {
text-align: center;
margin-top: 16px;
}
.qrcode-img {
max-width: 200px;
height: auto;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* 提示框 */
.notice-box {
background: linear-gradient(135deg, rgba(255, 193, 7, 0.1) 0%, rgba(255, 152, 0, 0.1) 100%);
padding: 16px 20px;
border-radius: 12px;
border-left: 4px solid #ffc107;
text-align: left;
font-size: 14px;
color: #666;
line-height: 1.6;
margin-bottom: 20px;
}
/* 底部信息 */
.footer-info {
text-align: center;
color: #666;
}
.company-name {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 8px;
}
.date {
font-size: 14px;
color: #999;
}
/* 响应式设计 */
@media (max-width: 768px) {
.page-header-modern {
padding: 16px 0;
}
.header-content {
padding: 0 16px;
}
.back-btn {
width: 40px;
height: 40px;
margin-right: 12px;
}
.header-title {
font-size: 18px;
}
.policy-content {
padding: 16px;
}
.section-title {
font-size: 16px;
}
.qrcode-img {
max-width: 160px;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -118,7 +118,8 @@
<div class="form-group">
<el-checkbox v-model="formData.agreeTerms" class="checkbox-custom">
我已阅读并同意
<a href="#" class="terms-link" @click.prevent="showTerms">用户服务协议</a>
<span class="terms-link" @click.stop="showTerms">会员服务协议</span>
<span class="terms-link" @click.stop="showPrivacyPolicy">个人信息收集与处理规则</span>
</el-checkbox>
<div v-if="errors.agreeTerms" class="error-text">{{ errors.agreeTerms }}</div>
</div>
@@ -383,7 +384,7 @@ export default {
// 服务条款验证
if (!this.formData.agreeTerms) {
this.errors.agreeTerms = '请同意用户服务协议';
this.errors.agreeTerms = '请同意会员服务协议';
}
return Object.keys(this.errors).length === 0;
@@ -485,7 +486,14 @@ export default {
showTerms() {
// 跳转前立即保存当前表单数据
this.saveFormData(true);
this.$router.push('/user-agreement');
this.$router.push('/member-agreement');
},
// 显示隐私政策
showPrivacyPolicy() {
// 跳转前立即保存当前表单数据
this.saveFormData(true);
this.$router.push('/privacy-policy');
}
},
// 监听表单数据变化,自动保存
@@ -806,12 +814,29 @@ input::-webkit-credentials-auto-fill-button {
color: #e53e3e;
text-decoration: none;
margin-left: 4px;
cursor: pointer;
-webkit-tap-highlight-color: transparent !important;
-webkit-touch-callout: none !important;
-webkit-user-select: none !important;
-khtml-user-select: none !important;
-moz-user-select: none !important;
-ms-user-select: none !important;
user-select: none !important;
outline: none !important;
background-color: transparent !important;
display: inline;
}
.terms-link:hover {
text-decoration: underline;
}
.terms-link:active {
background-color: transparent !important;
outline: none !important;
-webkit-tap-highlight-color: transparent !important;
}
/* 注册按钮 */
.register-btn {
width: 100%;
@@ -887,9 +912,36 @@ input::-webkit-credentials-auto-fill-button {
}
/* 处理用户协议链接 */
.checkbox-custom {
align-items: flex-start !important;
}
.checkbox-custom :deep(.el-checkbox__input) {
margin-top: 4px;
}
.checkbox-custom :deep(.el-checkbox__label) {
display: flex;
align-items: center;
display: block;
line-height: 1.8;
white-space: normal;
word-break: break-word;
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
padding-top: 0;
}
.checkbox-custom :deep(.el-checkbox__label) * {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-webkit-tap-highlight-color: transparent !important;
-webkit-touch-callout: none;
background-color: transparent !important;
}
/* 登录链接 */
@@ -976,6 +1028,15 @@ input::-webkit-credentials-auto-fill-button {
.phone-code-row {
gap: 8px;
}
.checkbox-custom :deep(.el-checkbox__label) {
font-size: 13px;
}
.terms-link {
display: inline;
margin: 0 2px;
}
}
/* 桌面端样式 - 这部分已在上面定义,这里移除重复定义 */

View File

@@ -19,14 +19,6 @@
<!-- 主要内容 -->
<div class="guide-content">
<!-- 标题 -->
<div class="guide-title-section">
<h2 class="main-title">精彩猪手使用指南</h2>
<p class="intro-text">
精彩猪手是一款彩票数据姿态逻辑专业分析工具是广大彩民用数据指向凭理性娱彩的良师益友
</p>
</div>
<!-- 目录导航 -->
<div class="toc-section">
<h3 class="toc-title">目录</h3>
@@ -337,15 +329,42 @@
<p class="date">2025年12月31日</p>
</div>
</div>
<!-- 回到顶部按钮 -->
<div class="back-to-top" @click="scrollToTop" v-show="showBackToTop">
<svg viewBox="0 0 24 24" class="top-icon">
<path d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path>
</svg>
</div>
</div>
</template>
<script>
export default {
name: 'UserGuide',
data() {
return {
showBackToTop: false
}
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
},
beforeUnmount() {
window.removeEventListener('scroll', this.handleScroll)
},
methods: {
goBack() {
this.$router.go(-1)
},
handleScroll() {
this.showBackToTop = window.scrollY > 300
},
scrollToTop() {
window.scrollTo({
top: 0,
behavior: 'smooth'
})
}
}
}
@@ -724,4 +743,51 @@ export default {
margin-right: 0;
}
}
/* 回到顶部按钮 */
.back-to-top {
position: fixed;
bottom: 80px;
right: 20px;
width: 48px;
height: 48px;
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 12px rgba(79, 172, 254, 0.4);
transition: all 0.3s ease;
z-index: 999;
}
.back-to-top:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(79, 172, 254, 0.6);
}
.back-to-top:active {
transform: translateY(-1px);
}
.top-icon {
width: 24px;
height: 24px;
fill: white;
}
@media (max-width: 768px) {
.back-to-top {
bottom: 70px;
right: 15px;
width: 44px;
height: 44px;
}
.top-icon {
width: 22px;
height: 22px;
}
}
</style>

View File

@@ -108,7 +108,7 @@
</div>
<div class="form-footer">
<p>© 2024 彩票推测系统 - 后台管理</p>
<p>© 2025-2026 彩票推测系统 - 后台管理</p>
</div>
</div>
</div>

View File

@@ -33,7 +33,7 @@
<el-card class="table-card">
<el-table :data="tableData" style="width: 100%" v-loading="loading" border stripe>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="userId" label="用户ID" width="100" />
<el-table-column prop="userId" label="用户ID" width="180" />
<el-table-column prop="drawId" label="期号" width="100" />
<el-table-column label="前区号码" min-width="180">
<template #default="scope">

View File

@@ -31,7 +31,7 @@
<el-card class="table-card">
<el-table :data="tableData" style="width: 100%" v-loading="loading" border stripe>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="userId" label="用户ID" width="100" />
<el-table-column prop="userId" label="用户ID" width="180" />
<el-table-column prop="drawId" label="期号" width="100" />
<el-table-column label="前区号码" min-width="180">
<template #default="scope">

View File

@@ -30,7 +30,7 @@
<el-card class="table-card">
<el-table :data="tableData" style="width: 100%" v-loading="loading" border stripe>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="userId" label="用户ID" width="100" />
<el-table-column prop="userId" label="用户ID" width="180" />
<el-table-column prop="drawId" label="期号" width="100" />
<el-table-column label="红球" min-width="200">
<template #default="scope">

View File

@@ -28,7 +28,7 @@
<el-card class="table-card">
<el-table :data="tableData" style="width: 100%" v-loading="loading" border stripe>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="userId" label="用户ID" width="100" />
<el-table-column prop="userId" label="用户ID" width="180" />
<el-table-column prop="drawId" label="期号" width="100" />
<el-table-column label="红球" min-width="200">
<template #default="scope">

View File

@@ -320,9 +320,23 @@
{{ currentUserDetail.isVip === 1 ? '是' : '否' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="套餐类别">
<el-tag :type="getVipTypeTag(currentUserDetail.vipType)">
{{ currentUserDetail.vipType || '体验会员' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="VIP到期时间">
{{ currentUserDetail.vipExpire ? formatDate(currentUserDetail.vipExpire) : '-' }}
</el-descriptions-item>
<el-descriptions-item label="所在省市">
{{ currentUserDetail.location || '-' }}
</el-descriptions-item>
<el-descriptions-item label="彩票偏好">
{{ currentUserDetail.preference || '-' }}
</el-descriptions-item>
<el-descriptions-item label="获客渠道">
{{ currentUserDetail.channel || '-' }}
</el-descriptions-item>
<el-descriptions-item label="注册时间" :span="2">
{{ formatDate(currentUserDetail.createTime) }}
</el-descriptions-item>
@@ -958,6 +972,16 @@ export default {
return (rate * 100).toFixed(2) + '%'
}
// 获取会员类型标签颜色
const getVipTypeTag = (vipType) => {
const typeMap = {
'年度会员': 'warning',
'月度会员': 'primary',
'体验会员': 'info'
}
return typeMap[vipType] || 'info'
}
return {
searchForm,
userList,
@@ -999,7 +1023,8 @@ export default {
usageStatsDialogVisible,
usageStatsLoading,
usageStats,
formatHitRate
formatHitRate,
getVipTypeTag
}
}
}

View File

@@ -178,6 +178,10 @@
<el-icon><List /></el-icon>
<span>会员码列表</span>
<div class="header-actions">
<el-button type="success" @click="exportToExcel">
<el-icon><Download /></el-icon>
导出
</el-button>
<el-button type="primary" @click="refreshList">
<el-icon><Refresh /></el-icon>
刷新
@@ -755,6 +759,70 @@ export default {
return date.toLocaleString('zh-CN')
}
// 导出到Excel
const exportToExcel = async () => {
try {
tableLoading.value = true
// 获取所有数据(不分页)
const params = {
page: 1,
size: 10000, // 获取所有数据
...searchForm
}
const response = await lotteryApi.getVipCodeList(params)
if (response && response.success) {
const allData = response.data.records || []
if (allData.length === 0) {
ElMessage.warning('没有数据可以导出')
return
}
// 准备CSV数据
const headers = ['VIP编号', '会员码', '有效期', '状态', '创建人', '创建时间', '使用人ID', '使用时间']
const csvContent = [
headers.join(','),
...allData.map(row => [
row.vipNumber || 1,
row.code,
`${row.vipExpireTime}个月`,
row.isUse === 0 ? '可用' : '已使用',
row.createdUserName || '-',
formatDate(row.createTime),
row.usedUserName || '-',
row.updateTime ? formatDate(row.updateTime) : '-'
].join(','))
].join('\n')
// 添加BOM以支持中文
const BOM = '\uFEFF'
const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' })
// 创建下载链接
const link = document.createElement('a')
const url = URL.createObjectURL(blob)
link.setAttribute('href', url)
link.setAttribute('download', `会员码列表_${new Date().toLocaleDateString('zh-CN').replace(/\//g, '-')}.csv`)
link.style.visibility = 'hidden'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
ElMessage.success(`成功导出 ${allData.length} 条数据`)
} else {
ElMessage.error(response?.message || '导出失败')
}
} catch (error) {
console.error('导出失败:', error)
ElMessage.error('导出失败,请重试')
} finally {
tableLoading.value = false
}
}
// 验证表单
const validateGenerateForm = () => {
generateFormRef.value?.validateField('vipExpireTime')
@@ -800,7 +868,8 @@ export default {
confirmActionLoading,
confirmGenerateVipCodes,
confirmGetAvailableCode,
cancelDialog
cancelDialog,
exportToExcel
}
}
}

View File

@@ -9,7 +9,7 @@
</div>
<div class="page-title">
<h1 class="main-title">大乐透智能推测</h1>
<p class="subtitle">专业算法智能推荐提升体验</p>
<p class="subtitle">姿态逻辑分析数据分段推测逻辑组号</p>
</div>
</div>
@@ -1313,11 +1313,6 @@ export default {
// 初始化桌面端检测
this.initResponsiveDesign()
// 启动定时检查登录状态每5秒
this.loginCheckTimer = setInterval(() => {
this.verifyUserLogin(true) // 传入true表示是定时检查
}, 5000)
// 启动定时清除缓存每15分钟
this.cacheCleanupTimer = setInterval(() => {
this.clearCache()
@@ -1329,11 +1324,6 @@ export default {
if (this.resizeHandler && typeof window !== 'undefined') {
window.removeEventListener('resize', this.resizeHandler)
}
// 清除登录状态检查定时器
if (this.loginCheckTimer) {
clearInterval(this.loginCheckTimer)
this.loginCheckTimer = null
}
// 清除缓存清除定时器
if (this.cacheCleanupTimer) {
clearInterval(this.cacheCleanupTimer)

View File

@@ -53,7 +53,7 @@
<div class="prize-table-container">
<div class="table-header">
<div class="header-cell award-type">奖项</div>
<div class="header-cell win-count">中奖次数</div>
<div class="header-cell win-count">次数</div>
<div class="header-cell prize-amount">累计奖金()</div>
</div>

View File

@@ -9,7 +9,7 @@
</div>
<div class="page-title">
<h1 class="main-title">大乐透精细推测</h1>
<p class="subtitle">专业算法智能推荐提升体验</p>
<p class="subtitle">姿态逻辑分析数据分段推测逻辑组号</p>
</div>
</div>
@@ -1110,11 +1110,6 @@
// 初始化桌面端检测
this.initResponsiveDesign()
// 启动定时检查登录状态每5秒
this.loginCheckTimer = setInterval(() => {
this.verifyUserLogin(true) // 传入true表示是定时检查
}, 5000)
// 启动定时清除缓存每15分钟
this.cacheCleanupTimer = setInterval(() => {
this.clearCache()
@@ -1126,11 +1121,6 @@
if (this.resizeHandler && typeof window !== 'undefined') {
window.removeEventListener('resize', this.resizeHandler)
}
// 清除登录状态检查定时器
if (this.loginCheckTimer) {
clearInterval(this.loginCheckTimer)
this.loginCheckTimer = null
}
// 清除缓存清除定时器
if (this.cacheCleanupTimer) {
clearInterval(this.cacheCleanupTimer)

View File

@@ -9,7 +9,7 @@
</div>
<div class="page-title">
<h1 class="main-title">双色球精细推测</h1>
<p class="subtitle">专业算法智能推荐提升体验</p>
<p class="subtitle">姿态逻辑分析数据分段推测逻辑组号</p>
</div>
</div>
@@ -980,11 +980,6 @@
// 初始化桌面端检测
this.initResponsiveDesign()
// 启动定时检查登录状态每5秒
this.loginCheckTimer = setInterval(() => {
this.verifyUserLogin(true) // 传入true表示是定时检查
}, 5000)
// 启动定时清除缓存每15分钟
this.cacheCleanupTimer = setInterval(() => {
this.clearCache()
@@ -996,11 +991,6 @@
if (this.resizeHandler && typeof window !== 'undefined') {
window.removeEventListener('resize', this.resizeHandler)
}
// 清除登录状态检查定时器
if (this.loginCheckTimer) {
clearInterval(this.loginCheckTimer)
this.loginCheckTimer = null
}
// 清除缓存清除定时器
if (this.cacheCleanupTimer) {
clearInterval(this.cacheCleanupTimer)

View File

@@ -9,7 +9,7 @@
</div>
<div class="page-title">
<h1 class="main-title">双色球智能推测</h1>
<p class="subtitle">专业算法智能推荐提升体验</p>
<p class="subtitle">姿态逻辑分析数据分段推测逻辑组号</p>
</div>
</div>
@@ -1243,11 +1243,6 @@ export default {
// 初始化桌面端检测
this.initResponsiveDesign()
// 启动定时检查登录状态每5秒
this.loginCheckTimer = setInterval(() => {
this.verifyUserLogin(true) // 传入true表示是定时检查
}, 5000)
// 启动定时清除缓存每15分钟
this.cacheCleanupTimer = setInterval(() => {
this.clearCache()
@@ -1259,11 +1254,6 @@ export default {
if (this.resizeHandler && typeof window !== 'undefined') {
window.removeEventListener('resize', this.resizeHandler)
}
// 清除登录状态检查定时器
if (this.loginCheckTimer) {
clearInterval(this.loginCheckTimer)
this.loginCheckTimer = null
}
// 清除缓存清除定时器
if (this.cacheCleanupTimer) {
clearInterval(this.cacheCleanupTimer)

View File

@@ -53,7 +53,7 @@
<div class="prize-table-container">
<div class="table-header">
<div class="header-cell award-type">奖项</div>
<div class="header-cell win-count">中奖次数</div>
<div class="header-cell win-count">次数</div>
<div class="header-cell prize-amount">累计奖金()</div>
</div>