优化: 修复安全隐患、路由权限、环境变量、CSS问题,新增组件,清理无用文件
This commit is contained in:
8
.env
Normal file
8
.env
Normal 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
8
.env.development
Normal 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
195
DEPLOY.md
@@ -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**
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有部署问题,请联系技术团队。
|
||||
|
||||
|
||||
155
Home步骤 - 副本.md
155
Home步骤 - 副本.md
@@ -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个后区球号,
|
||||
|
||||
选择一个推荐的后区随球,点击继续,进入下一步。
|
||||
|
||||
第六步:确认推测,这一步跟双色球的第五步一样。
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
|
||||
BIN
public/assets/home/erweima.png
Normal file
BIN
public/assets/home/erweima.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB |
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
|
||||
@@ -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
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
@@ -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 |
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
189
src/components/CustomSelect.vue
Normal file
189
src/components/CustomSelect.vue
Normal 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>
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
411
src/views/PrivacyPolicy.vue
Normal 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传输加密、AES-256存储加密、访问权限分级管理等技术,防止数据泄露、篡改;</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
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/* 桌面端样式 - 这部分已在上面定义,这里移除重复定义 */
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
</div>
|
||||
|
||||
<div class="form-footer">
|
||||
<p>© 2024 彩票推测系统 - 后台管理</p>
|
||||
<p>© 2025-2026 彩票推测系统 - 后台管理</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user