Compare commits
No commits in common. 'master' and 'zry_code' have entirely different histories.
-
30code/vue-base1/.gitignore
-
29code/vue-base1/README.md
-
13code/vue-base1/index.html
-
8code/vue-base1/jsconfig.json
-
3323code/vue-base1/package-lock.json
-
31code/vue-base1/package.json
-
BINcode/vue-base1/public/favicon.ico
-
35code/vue-base1/src/App.vue
-
86code/vue-base1/src/assets/base.css
-
1code/vue-base1/src/assets/logo.svg
-
35code/vue-base1/src/assets/main.css
-
299code/vue-base1/src/components/Sidebar.vue
-
100code/vue-base1/src/cs.vue
-
BINcode/vue-base1/src/images/cat.gif
-
BINcode/vue-base1/src/images/dx.gif
-
BINcode/vue-base1/src/images/star.gif
-
BINcode/vue-base1/src/images/tech.gif
-
BINcode/vue-base1/src/images/tech2.gif
-
BINcode/vue-base1/src/images/water.jpg
-
12code/vue-base1/src/main.js
-
76code/vue-base1/src/router/index.js
-
22code/vue-base1/src/views/HomeView.vue
-
241code/vue-base1/src/views/Recharge.vue
-
320code/vue-base1/src/views/RechargeDetails.vue
-
237code/vue-base1/src/views/Refund.vue
-
358code/vue-base1/src/views/UserDetails.vue
-
331code/vue-base1/src/views/audit/RechargeAudit.vue
-
321code/vue-base1/src/views/audit/RefundAudit.vue
-
379code/vue-base1/src/views/consumption/GoodsConsumption.vue
-
206code/vue-base1/src/views/consumption/TransactionStatistics.vue
-
30code/vue-base1/vite.config.js
-
19code/vue-base1/vue.config.js
-
0m.txt
@ -0,0 +1,30 @@ |
|||||
|
# Logs |
||||
|
logs |
||||
|
*.log |
||||
|
npm-debug.log* |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
pnpm-debug.log* |
||||
|
lerna-debug.log* |
||||
|
|
||||
|
node_modules |
||||
|
.DS_Store |
||||
|
dist |
||||
|
dist-ssr |
||||
|
coverage |
||||
|
*.local |
||||
|
|
||||
|
/cypress/videos/ |
||||
|
/cypress/screenshots/ |
||||
|
|
||||
|
# Editor directories and files |
||||
|
.vscode/* |
||||
|
!.vscode/extensions.json |
||||
|
.idea |
||||
|
*.suo |
||||
|
*.ntvs* |
||||
|
*.njsproj |
||||
|
*.sln |
||||
|
*.sw? |
||||
|
|
||||
|
*.tsbuildinfo |
@ -0,0 +1,29 @@ |
|||||
|
# vue-base1 |
||||
|
|
||||
|
This template should help get you started developing with Vue 3 in Vite. |
||||
|
|
||||
|
## Recommended IDE Setup |
||||
|
|
||||
|
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur). |
||||
|
|
||||
|
## Customize configuration |
||||
|
|
||||
|
See [Vite Configuration Reference](https://vite.dev/config/). |
||||
|
|
||||
|
## Project Setup |
||||
|
|
||||
|
```sh |
||||
|
npm install |
||||
|
``` |
||||
|
|
||||
|
### Compile and Hot-Reload for Development |
||||
|
|
||||
|
```sh |
||||
|
npm run dev |
||||
|
``` |
||||
|
|
||||
|
### Compile and Minify for Production |
||||
|
|
||||
|
```sh |
||||
|
npm run build |
||||
|
``` |
@ -0,0 +1,13 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang=""> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<link rel="icon" href="/favicon.ico"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
<title>Vite Newbi</title> |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="app"></div> |
||||
|
<script type="module" src="/src/main.js"></script> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
"paths": { |
||||
|
"@/*": ["./src/*"] |
||||
|
} |
||||
|
}, |
||||
|
"exclude": ["node_modules", "dist"] |
||||
|
} |
3323
code/vue-base1/package-lock.json
File diff suppressed because it is too large
View File
@ -0,0 +1,31 @@ |
|||||
|
{ |
||||
|
"name": "vue-base1", |
||||
|
"version": "0.0.0", |
||||
|
"private": true, |
||||
|
"type": "module", |
||||
|
"scripts": { |
||||
|
"dev": "vite --host 0.0.0.0", |
||||
|
"build": "vite build", |
||||
|
"preview": "vite preview" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@element-plus/icons-vue": "^2.3.1", |
||||
|
"animejs": "^4.0.1", |
||||
|
"axios": "^1.8.4", |
||||
|
"chart.js": "^4.4.9", |
||||
|
"echarts": "^5.6.0", |
||||
|
"element": "^0.1.4", |
||||
|
"element-plus": "^2.9.8", |
||||
|
"gsap": "^3.12.7", |
||||
|
"particles.js": "^2.0.0", |
||||
|
"plus": "^0.1.0", |
||||
|
"vue": "^3.5.13", |
||||
|
"vue-router": "^4.5.0", |
||||
|
"xlsx": "^0.18.5" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@vitejs/plugin-vue": "^5.2.3", |
||||
|
"vite": "^6.2.4", |
||||
|
"vite-plugin-vue-devtools": "^7.7.2" |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
<template> |
||||
|
<div id="app"> |
||||
|
<!-- 动态背景 --> |
||||
|
<div class="dynamic-background"></div> |
||||
|
<!-- 导航栏组件 --> |
||||
|
<Sidebar /> |
||||
|
<!-- 路由视图 --> |
||||
|
<router-view></router-view> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import Sidebar from './components/Sidebar.vue'; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
#app { |
||||
|
position: relative; |
||||
|
min-height: 100vh; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.dynamic-background { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
z-index: -1; |
||||
|
/* 替换为你的动效图片路径 */ |
||||
|
background-image: url('../src/images/water.jpg'); |
||||
|
background-size: cover; |
||||
|
background-position: center; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,86 @@ |
|||||
|
/* color palette from <https://github.com/vuejs/theme> */ |
||||
|
:root { |
||||
|
--vt-c-white: #ffffff; |
||||
|
--vt-c-white-soft: #f8f8f8; |
||||
|
--vt-c-white-mute: #f2f2f2; |
||||
|
|
||||
|
--vt-c-black: #181818; |
||||
|
--vt-c-black-soft: #222222; |
||||
|
--vt-c-black-mute: #282828; |
||||
|
|
||||
|
--vt-c-indigo: #2c3e50; |
||||
|
|
||||
|
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29); |
||||
|
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12); |
||||
|
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); |
||||
|
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); |
||||
|
|
||||
|
--vt-c-text-light-1: var(--vt-c-indigo); |
||||
|
--vt-c-text-light-2: rgba(60, 60, 60, 0.66); |
||||
|
--vt-c-text-dark-1: var(--vt-c-white); |
||||
|
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64); |
||||
|
} |
||||
|
|
||||
|
/* semantic color variables for this project */ |
||||
|
:root { |
||||
|
--color-background: var(--vt-c-white); |
||||
|
--color-background-soft: var(--vt-c-white-soft); |
||||
|
--color-background-mute: var(--vt-c-white-mute); |
||||
|
|
||||
|
--color-border: var(--vt-c-divider-light-2); |
||||
|
--color-border-hover: var(--vt-c-divider-light-1); |
||||
|
|
||||
|
--color-heading: var(--vt-c-text-light-1); |
||||
|
--color-text: var(--vt-c-text-light-1); |
||||
|
|
||||
|
--section-gap: 160px; |
||||
|
} |
||||
|
|
||||
|
@media (prefers-color-scheme: dark) { |
||||
|
:root { |
||||
|
--color-background: var(--vt-c-black); |
||||
|
--color-background-soft: var(--vt-c-black-soft); |
||||
|
--color-background-mute: var(--vt-c-black-mute); |
||||
|
|
||||
|
--color-border: var(--vt-c-divider-dark-2); |
||||
|
--color-border-hover: var(--vt-c-divider-dark-1); |
||||
|
|
||||
|
--color-heading: var(--vt-c-text-dark-1); |
||||
|
--color-text: var(--vt-c-text-dark-2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
*, |
||||
|
*::before, |
||||
|
*::after { |
||||
|
box-sizing: border-box; |
||||
|
margin: 0; |
||||
|
font-weight: normal; |
||||
|
} |
||||
|
|
||||
|
body { |
||||
|
min-height: 100vh; |
||||
|
color: var(--color-text); |
||||
|
background: var(--color-background); |
||||
|
transition: |
||||
|
color 0.5s, |
||||
|
background-color 0.5s; |
||||
|
line-height: 1.6; |
||||
|
font-family: |
||||
|
Inter, |
||||
|
-apple-system, |
||||
|
BlinkMacSystemFont, |
||||
|
'Segoe UI', |
||||
|
Roboto, |
||||
|
Oxygen, |
||||
|
Ubuntu, |
||||
|
Cantarell, |
||||
|
'Fira Sans', |
||||
|
'Droid Sans', |
||||
|
'Helvetica Neue', |
||||
|
sans-serif; |
||||
|
font-size: 15px; |
||||
|
text-rendering: optimizeLegibility; |
||||
|
-webkit-font-smoothing: antialiased; |
||||
|
-moz-osx-font-smoothing: grayscale; |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg> |
@ -0,0 +1,35 @@ |
|||||
|
@import './base.css'; |
||||
|
|
||||
|
#app { |
||||
|
max-width: 1280px; |
||||
|
margin: 0 auto; |
||||
|
padding: 2rem; |
||||
|
font-weight: normal; |
||||
|
} |
||||
|
|
||||
|
a, |
||||
|
.green { |
||||
|
text-decoration: none; |
||||
|
color: hsla(160, 100%, 37%, 1); |
||||
|
transition: 0.4s; |
||||
|
padding: 3px; |
||||
|
} |
||||
|
|
||||
|
@media (hover: hover) { |
||||
|
a:hover { |
||||
|
background-color: hsla(160, 100%, 37%, 0.2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media (min-width: 1024px) { |
||||
|
body { |
||||
|
display: flex; |
||||
|
place-items: center; |
||||
|
} |
||||
|
|
||||
|
#app { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
padding: 0 2rem; |
||||
|
} |
||||
|
} |
@ -0,0 +1,299 @@ |
|||||
|
<template> |
||||
|
<!-- 使用 el-menu 直接包裹菜单项 --> |
||||
|
<el-menu |
||||
|
default-active="1" |
||||
|
class="el-menu-vertical-demo custom-sidebar-menu fixed-sidebar" |
||||
|
@open="handleOpen" |
||||
|
@close="handleClose" |
||||
|
> |
||||
|
<!-- 添加一个带有滚动功能的容器 --> |
||||
|
<div class="menu-container scrollable-menu"> |
||||
|
<!-- 为菜单项添加更明显的悬停效果 --> |
||||
|
<el-menu-item |
||||
|
index="1" |
||||
|
@click="navigateTo('recharge')" |
||||
|
class="fancy-menu-item" |
||||
|
> |
||||
|
<el-icon><Wallet /></el-icon> |
||||
|
<span>充值</span> |
||||
|
</el-menu-item> |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
<el-menu-item |
||||
|
index="2" |
||||
|
@click="navigateTo('refund')" |
||||
|
class="fancy-menu-item" |
||||
|
> |
||||
|
<el-icon><ArrowDown /></el-icon> |
||||
|
<span>退款</span> |
||||
|
</el-menu-item> |
||||
|
<!-- <el-menu-item |
||||
|
index="3" |
||||
|
@click="navigateTo('recharge-audit')" |
||||
|
class="fancy-menu-item" |
||||
|
> |
||||
|
<el-icon><Check /></el-icon> |
||||
|
<span>充值审核</span> |
||||
|
</el-menu-item> --> |
||||
|
<!-- <el-menu-item |
||||
|
index="4" |
||||
|
@click="navigateTo('refund-audit')" |
||||
|
class="fancy-menu-item" |
||||
|
> |
||||
|
<el-icon><Close /></el-icon> |
||||
|
<span>退款审核</span> |
||||
|
</el-menu-item> --> |
||||
|
|
||||
|
<el-sub-menu index="3" class="fancy-menu-item consumption-menu"> |
||||
|
<template #title> |
||||
|
<span>审核</span> |
||||
|
</template> |
||||
|
<div class="sub-menu-items"> |
||||
|
<el-menu-item |
||||
|
index="/audit/recharge-audit" |
||||
|
@click="navigateTo('RechargeAudit')" |
||||
|
class="fancy-menu-item2 sub-item" |
||||
|
> |
||||
|
<span>充值审核</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item |
||||
|
index="/audit/refund-audit" |
||||
|
@click="navigateTo('RefundAudit')" |
||||
|
class="fancy-menu-item2 sub-item" |
||||
|
> |
||||
|
<span>退款审核</span> |
||||
|
</el-menu-item> |
||||
|
</div> |
||||
|
</el-sub-menu> |
||||
|
|
||||
|
<el-menu-item |
||||
|
index="4" |
||||
|
@click="navigateTo('recharge-details')" |
||||
|
class="fancy-menu-item" |
||||
|
> |
||||
|
<el-icon><Document /></el-icon> |
||||
|
<span>充值明细</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item |
||||
|
index="5" |
||||
|
@click="navigateTo('user-details')" |
||||
|
class="fancy-menu-item" |
||||
|
> |
||||
|
<el-icon><User /></el-icon> |
||||
|
<span>用户明细</span> |
||||
|
</el-menu-item> |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
<!-- 修改为子菜单,添加新类名 --> |
||||
|
<el-sub-menu index="6" class="fancy-menu-item consumption-menu"> |
||||
|
<template #title> |
||||
|
<span>消费和统计</span> |
||||
|
</template> |
||||
|
<div class="sub-menu-items"> |
||||
|
<el-menu-item |
||||
|
index="/consumption/goods-consumption" |
||||
|
@click="navigateTo('GoodsConsumption')" |
||||
|
class="fancy-menu-item2 sub-item" |
||||
|
> |
||||
|
<span>产品消费</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item |
||||
|
index="/consumption/transaction-statistics" |
||||
|
@click="navigateTo('TransactionStatistics')" |
||||
|
class="fancy-menu-item2 sub-item" |
||||
|
> |
||||
|
<span>流水统计</span> |
||||
|
</el-menu-item> |
||||
|
</div> |
||||
|
</el-sub-menu> |
||||
|
</div> |
||||
|
</el-menu> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { useRouter } from 'vue-router'; |
||||
|
import { Wallet, ArrowDown, Check, Close, Document, User } from '@element-plus/icons-vue'; |
||||
|
|
||||
|
const router = useRouter(); |
||||
|
|
||||
|
const navigateTo = (path) => { |
||||
|
console.log('尝试跳转到路由:', path); |
||||
|
router.push({ name: path }); |
||||
|
}; |
||||
|
|
||||
|
const handleOpen = (key, keyPath) => { |
||||
|
console.log(key, keyPath); |
||||
|
}; |
||||
|
|
||||
|
const handleClose = (key, keyPath) => { |
||||
|
console.log(key, keyPath); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
/* 菜单项容器样式 */ |
||||
|
.menu-container { |
||||
|
padding: 10px; |
||||
|
margin-top: 30px; |
||||
|
text-align: center; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 美化后的菜单项样式 */ |
||||
|
.fancy-menu-item { |
||||
|
transition: all 0.3s ease; |
||||
|
cursor: pointer; |
||||
|
padding: 15px 20px; |
||||
|
font-size: 16px; |
||||
|
margin: 20px 10px; |
||||
|
border-radius: 4px; |
||||
|
width: calc(100% - 20px); |
||||
|
background-color: rgba(255, 255, 255, 0.7); |
||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
color: #000; |
||||
|
} |
||||
|
.fancy-menu-item2 { |
||||
|
transition: all 0.3s ease; |
||||
|
cursor: pointer; |
||||
|
padding: 15px 20px; |
||||
|
font-size: 16px; |
||||
|
margin: 20px 10px; |
||||
|
border-radius: 4px; |
||||
|
width: calc(100% - 20px); |
||||
|
background-color: rgba(255, 255, 255, 0.7); |
||||
|
box-shadow: 0 4px 6px #9157571a; |
||||
|
position: relative; |
||||
|
} |
||||
|
.fancy-menu-item2.is-active { |
||||
|
background-color: rgba(248, 219, 225, 0.8) !important; |
||||
|
transform: scale(1.05); |
||||
|
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); |
||||
|
color: #000; |
||||
|
} |
||||
|
.fancy-menu-item2:hover { |
||||
|
background-color: rgba(248, 219, 225, 0.8) !important; |
||||
|
transform: scale(1.05); |
||||
|
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 添加发光效果 */ |
||||
|
.fancy-menu-item::before { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: -100%; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.4), transparent); |
||||
|
transition: all 0.6s ease; |
||||
|
} |
||||
|
|
||||
|
/* 悬停效果 */ |
||||
|
.fancy-menu-item:hover { |
||||
|
background-color: rgba(236, 236, 252, 0.8) !important; |
||||
|
transform: scale(1.05); |
||||
|
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
.fancy-menu-item:hover::before { |
||||
|
left: 100%; |
||||
|
} |
||||
|
|
||||
|
/* 激活状态样式 */ |
||||
|
.fancy-menu-item.is-active { |
||||
|
background-color: rgba(228, 219, 248, 0.8) !important; |
||||
|
transform: scale(1.05); |
||||
|
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 子菜单样式调整 */ |
||||
|
.sub-menu-items { |
||||
|
margin-top: 10px; |
||||
|
} |
||||
|
|
||||
|
.sub-item { |
||||
|
margin: 10px 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
/* 导航栏白底样式 */ |
||||
|
.fixed-sidebar { |
||||
|
position: fixed; |
||||
|
top: 30px; |
||||
|
left: 20px; |
||||
|
|
||||
|
width: calc(20% - 40px); |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
/* 保持原有基础样式 */ |
||||
|
.el-menu-vertical-demo:not(.el-menu--collapse) { |
||||
|
width: 180px; |
||||
|
} |
||||
|
|
||||
|
.custom-sidebar-menu { |
||||
|
border-right: none; |
||||
|
box-shadow: 2px 0 5px rgba(228, 182, 182, 0.1); |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
color: #000; |
||||
|
min-height: 400px; |
||||
|
width: 40%; |
||||
|
/* 添加圆角 */ |
||||
|
border-radius: 10px; |
||||
|
} |
||||
|
|
||||
|
/* 单独调整消费和统计、审核按钮的样式 */ |
||||
|
.consumption-menu { |
||||
|
padding: 6px 8px; /* 减小内边距 */ |
||||
|
font-size: 12px; /* 减小字体大小 */ |
||||
|
margin: 10px 4px; /* 调整外边距 */ |
||||
|
} |
||||
|
|
||||
|
/* 添加滚动样式 */ |
||||
|
.scrollable-menu { |
||||
|
max-height: 80vh; /* 设置最大高度,可根据需要调整 */ |
||||
|
overflow-y: auto; /* 当内容超出最大高度时显示垂直滚动条 */ |
||||
|
overflow-x: hidden; /* 隐藏横向滚动条 */ |
||||
|
} |
||||
|
|
||||
|
/* 自定义滚动条样式 */ |
||||
|
.scrollable-menu::-webkit-scrollbar { |
||||
|
width: 4px; /* 滚动条宽度 */ |
||||
|
} |
||||
|
|
||||
|
/* 滚动条轨道 */ |
||||
|
.scrollable-menu::-webkit-scrollbar-track { |
||||
|
background: #f1f1f1; /* 轨道背景颜色 */ |
||||
|
border-radius: 4px; /* 轨道圆角 */ |
||||
|
} |
||||
|
|
||||
|
/* 滚动条滑块 */ |
||||
|
.scrollable-menu::-webkit-scrollbar-thumb { |
||||
|
background: #888; /* 滑块背景颜色 */ |
||||
|
border-radius: 4px; /* 滑块圆角 */ |
||||
|
} |
||||
|
|
||||
|
/* 滚动条滑块悬停状态 */ |
||||
|
.scrollable-menu::-webkit-scrollbar-thumb:hover { |
||||
|
background: #555; /* 悬停时滑块背景颜色 */ |
||||
|
} |
||||
|
|
||||
|
/* 调整固定侧边栏的宽度和位置 */ |
||||
|
.fixed-sidebar { |
||||
|
position: fixed; |
||||
|
top: 30px; |
||||
|
left: 20px; |
||||
|
width: calc(20% - 40px); |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,100 @@ |
|||||
|
<template> |
||||
|
<el-row class="tac"> |
||||
|
<el-col :span="12"> |
||||
|
<h5 class="mb-2">Default colors</h5> |
||||
|
<el-menu |
||||
|
default-active="2" |
||||
|
class="el-menu-vertical-demo" |
||||
|
@open="handleOpen" |
||||
|
@close="handleClose" |
||||
|
> |
||||
|
<el-sub-menu index="1"> |
||||
|
<template #title> |
||||
|
<el-icon><location /></el-icon> |
||||
|
<span>Navigator One</span> |
||||
|
</template> |
||||
|
<el-menu-item-group title="Group One"> |
||||
|
<el-menu-item index="1-1">item one</el-menu-item> |
||||
|
<el-menu-item index="1-2">item two</el-menu-item> |
||||
|
</el-menu-item-group> |
||||
|
<el-menu-item-group title="Group Two"> |
||||
|
<el-menu-item index="1-3">item three</el-menu-item> |
||||
|
</el-menu-item-group> |
||||
|
<el-sub-menu index="1-4"> |
||||
|
<template #title>item four</template> |
||||
|
<el-menu-item index="1-4-1">item one</el-menu-item> |
||||
|
</el-sub-menu> |
||||
|
</el-sub-menu> |
||||
|
<el-menu-item index="2"> |
||||
|
<el-icon><icon-menu /></el-icon> |
||||
|
<span>Navigator Two</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item index="3" disabled> |
||||
|
<el-icon><document /></el-icon> |
||||
|
<span>Navigator Three</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item index="4"> |
||||
|
<el-icon><setting /></el-icon> |
||||
|
<span>Navigator Four</span> |
||||
|
</el-menu-item> |
||||
|
</el-menu> |
||||
|
</el-col> |
||||
|
<el-col :span="12"> |
||||
|
<h5 class="mb-2">Custom colors</h5> |
||||
|
<el-menu |
||||
|
active-text-color="#ffd04b" |
||||
|
background-color="#545c64" |
||||
|
class="el-menu-vertical-demo" |
||||
|
default-active="2" |
||||
|
text-color="#fff" |
||||
|
@open="handleOpen" |
||||
|
@close="handleClose" |
||||
|
> |
||||
|
<el-sub-menu index="1"> |
||||
|
<template #title> |
||||
|
<el-icon><location /></el-icon> |
||||
|
<span>Navigator One</span> |
||||
|
</template> |
||||
|
<el-menu-item-group title="Group One"> |
||||
|
<el-menu-item index="1-1">item one</el-menu-item> |
||||
|
<el-menu-item index="1-2">item two</el-menu-item> |
||||
|
</el-menu-item-group> |
||||
|
<el-menu-item-group title="Group Two"> |
||||
|
<el-menu-item index="1-3">item three</el-menu-item> |
||||
|
</el-menu-item-group> |
||||
|
<el-sub-menu index="1-4"> |
||||
|
<template #title>item four</template> |
||||
|
<el-menu-item index="1-4-1">item one</el-menu-item> |
||||
|
</el-sub-menu> |
||||
|
</el-sub-menu> |
||||
|
<el-menu-item index="2"> |
||||
|
<el-icon><icon-menu /></el-icon> |
||||
|
<span>Navigator Two</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item index="3" disabled> |
||||
|
<el-icon><document /></el-icon> |
||||
|
<span>Navigator Three</span> |
||||
|
</el-menu-item> |
||||
|
<el-menu-item index="4"> |
||||
|
<el-icon><setting /></el-icon> |
||||
|
<span>Navigator Four</span> |
||||
|
</el-menu-item> |
||||
|
</el-menu> |
||||
|
</el-col> |
||||
|
</el-row> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { |
||||
|
Document, |
||||
|
Menu as IconMenu, |
||||
|
Location, |
||||
|
Setting, |
||||
|
} from '@element-plus/icons-vue' |
||||
|
const handleOpen = (key: string, keyPath: string[]) => { |
||||
|
console.log(key, keyPath) |
||||
|
} |
||||
|
const handleClose = (key: string, keyPath: string[]) => { |
||||
|
console.log(key, keyPath) |
||||
|
} |
||||
|
</script> |
After Width: 680 | Height: 400 | Size: 1.2 MiB |
After Width: 1024 | Height: 680 | Size: 2.4 MiB |
After Width: 500 | Height: 240 | Size: 847 KiB |
After Width: 334 | Height: 188 | Size: 1.7 MiB |
After Width: 533 | Height: 300 | Size: 3.7 MiB |
After Width: 800 | Height: 500 | Size: 53 KiB |
@ -0,0 +1,12 @@ |
|||||
|
import { createApp } from 'vue' |
||||
|
import ElementPlus from 'element-plus' |
||||
|
import 'element-plus/dist/index.css' |
||||
|
import Newbi from './App.vue' |
||||
|
import router from './router' // 引入路由
|
||||
|
import axios from 'axios'; |
||||
|
|
||||
|
const app = createApp(Newbi) |
||||
|
app.use(ElementPlus) |
||||
|
app.use(router) // 挂载路由
|
||||
|
app.mount('#app') |
||||
|
|
@ -0,0 +1,76 @@ |
|||||
|
import { createRouter, createWebHistory } from 'vue-router' |
||||
|
import Recharge from '../views/Recharge.vue' |
||||
|
import Refund from '../views/Refund.vue' |
||||
|
import RechargeAudit from '../views/audit/RechargeAudit.vue' |
||||
|
import RefundAudit from '../views/audit/RefundAudit.vue' |
||||
|
import RechargeDetails from '../views/RechargeDetails.vue' |
||||
|
import GoodsConsumption from '../views/consumption/GoodsConsumption.vue'; |
||||
|
import TransactionStatistics from '../views/consumption/TransactionStatistics.vue'; |
||||
|
import UserDetails from '@/views/UserDetails.vue' |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
const routes = [ |
||||
|
{ |
||||
|
path: '/recharge', |
||||
|
name: 'recharge', |
||||
|
component: Recharge |
||||
|
}, |
||||
|
{ |
||||
|
path: '/refund', |
||||
|
name: 'refund', |
||||
|
component: Refund |
||||
|
}, |
||||
|
{ |
||||
|
path:'/audit', |
||||
|
name: 'audit', |
||||
|
meta: { title: '审核' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
path: 'recharge-audit', |
||||
|
name: 'RechargeAudit', |
||||
|
component: RechargeAudit |
||||
|
}, |
||||
|
{ |
||||
|
path: 'refund-audit', |
||||
|
name: 'RefundAudit', |
||||
|
component: RefundAudit |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
{ |
||||
|
path: '/recharge-details', |
||||
|
name: 'recharge-details', |
||||
|
component: RechargeDetails |
||||
|
}, |
||||
|
{ |
||||
|
path: '/user-details', |
||||
|
name: 'user-details', |
||||
|
component: UserDetails |
||||
|
}, |
||||
|
{ |
||||
|
path: '/consumption', |
||||
|
name: 'consumption', |
||||
|
meta: { title: '消费和统计' }, |
||||
|
children: [ |
||||
|
{ |
||||
|
path: 'goods-consumption', |
||||
|
name: 'GoodsConsumption', |
||||
|
component: GoodsConsumption |
||||
|
}, |
||||
|
{ |
||||
|
path: 'transaction-statistics', |
||||
|
name: 'TransactionStatistics', |
||||
|
component: TransactionStatistics |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
const router = createRouter({ |
||||
|
history: createWebHistory(), |
||||
|
routes |
||||
|
}); |
||||
|
|
||||
|
export default router; |
@ -0,0 +1,22 @@ |
|||||
|
<script setup> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<template> |
||||
|
<!-- 使用一个根元素包裹所有内容 --> |
||||
|
<div> |
||||
|
<div>HomeView</div> |
||||
|
<div> |
||||
|
<el-button>Default</el-button> |
||||
|
<el-button type="primary">Primary</el-button> |
||||
|
<el-button type="success">Success</el-button> |
||||
|
<el-button type="info">Info</el-button> |
||||
|
<el-button type="warning">Warning</el-button> |
||||
|
<el-button type="danger">Danger</el-button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<style scoped> |
||||
|
|
||||
|
</style> |
@ -0,0 +1,241 @@ |
|||||
|
<template> |
||||
|
<div class="recharge-form"> |
||||
|
<h2 class="form-title">后台充值</h2> |
||||
|
|
||||
|
<form @submit.prevent="submitForm"> |
||||
|
<!-- 精网号输入组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="jwcode">精网号</label> |
||||
|
<!-- 添加 @blur 事件监听器 --> |
||||
|
<input |
||||
|
type="text" |
||||
|
id="jwcode" |
||||
|
v-model="formData.jwcode" |
||||
|
placeholder="请输入精网号" |
||||
|
required |
||||
|
@blur="checkJwcodeExists" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 充值金额输入组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="amount">充值金额</label> |
||||
|
<!-- 充值金额输入框,使用 v-model.number 绑定到 formData.amount,确保输入为数字 --> |
||||
|
<input |
||||
|
type="number" |
||||
|
id="amount" |
||||
|
v-model.number="formData.amount" |
||||
|
placeholder="请输入充值金额" |
||||
|
required |
||||
|
min="0" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 支付方式选择组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="payment-method">支付方式</label> |
||||
|
<!-- 支付方式选择框,使用 v-model 绑定到 formData.paymentMethod --> |
||||
|
<select id="payment-method" v-model="formData.paymentMethod" required> |
||||
|
<!-- 默认选项 --> |
||||
|
<option value="">请选择支付方式</option> |
||||
|
<!-- 微信支付选项 --> |
||||
|
<option value="WECHAT">微信</option> |
||||
|
<!-- 支付宝支付选项 --> |
||||
|
<option value="ALIPAY">支付宝</option> |
||||
|
<!-- 银行卡支付选项 --> |
||||
|
<option value="BANK">银行卡</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 备注输入组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="notes">备注:</label> |
||||
|
<!-- 修正为绑定到 formData.notes --> |
||||
|
<textarea |
||||
|
id="notes" |
||||
|
v-model="formData.notes" |
||||
|
rows="4" |
||||
|
placeholder="请输入备注(非必填)" |
||||
|
></textarea> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 提交按钮组 --> |
||||
|
<div class="form-group"> |
||||
|
<!-- 修改按钮,根据 isLoading 状态显示不同文字和禁用状态 --> |
||||
|
<button type="submit" :disabled="isLoading"> |
||||
|
{{ isLoading ? '提交中...' : '提交' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
|
||||
|
import axios from 'axios'; |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
|
||||
|
formData: { |
||||
|
|
||||
|
jwcode: '', |
||||
|
|
||||
|
amount: null, |
||||
|
|
||||
|
paymentMethod: '', |
||||
|
|
||||
|
notes: '' |
||||
|
}, |
||||
|
isLoading: false, |
||||
|
jwcodeExists: true // 新增变量,用于标记精网号是否存在 |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
/** |
||||
|
* 检查精网号是否存在于数据库 |
||||
|
*/ |
||||
|
async checkJwcodeExists() { |
||||
|
if (!this.formData.jwcode) { |
||||
|
this.jwcodeExists = true; |
||||
|
return; |
||||
|
} |
||||
|
try { |
||||
|
// 修改为 post 请求,将精网号作为请求体传递 |
||||
|
const response = await axios.post('http://192.168.8.94:5173/recharges/checkJwcode', { |
||||
|
jwcode: this.formData.jwcode |
||||
|
}); |
||||
|
if (response.data && typeof response.data.exists === 'boolean') { |
||||
|
this.jwcodeExists = response.data.exists; |
||||
|
} else { |
||||
|
console.error('后端返回的数据格式不正确,缺少 exists 属性或属性类型错误'); |
||||
|
// 可以根据实际情况处理错误,比如设置默认值 |
||||
|
this.jwcodeExists = true; |
||||
|
} |
||||
|
if (!this.jwcodeExists) { |
||||
|
alert('用户不存在1,请检查精网号。'); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('检查精网号失败:', error); |
||||
|
alert('检查精网号时出错,请稍后再试。'); |
||||
|
// 清空精网号记录 |
||||
|
this.formData.jwcode = ''; |
||||
|
} |
||||
|
}, |
||||
|
/** |
||||
|
* 提交表单的方法 |
||||
|
* 发送表单数据到后端,并处理响应 |
||||
|
*/ |
||||
|
async submitForm() { |
||||
|
// 提交前检查精网号是否为空 |
||||
|
if (!this.formData.jwcode) { |
||||
|
alert("精网号不能为空!"); |
||||
|
return; |
||||
|
} |
||||
|
// 检查精网号是否存在 |
||||
|
if (!this.jwcodeExists) { |
||||
|
alert('用户不存在2,请检查精网号。'); |
||||
|
return; |
||||
|
} |
||||
|
try { |
||||
|
|
||||
|
// 开始提交,设置加载状态为 true |
||||
|
this.isLoading = true; |
||||
|
const response = await axios.post('http://192.168.8.94:5173/recharges/add/addRecharges', this.formData); |
||||
|
|
||||
|
if (response.data.code === "200") { |
||||
|
console.log('后端响应:', response.data.code === "200"); |
||||
|
alert('提交成功!'); |
||||
|
} else { |
||||
|
alert('提交失败:' + (response.data.message || '未知错误')); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('提交表单失败:', error); |
||||
|
alert('提交失败,请稍后再试。'); |
||||
|
} finally { |
||||
|
// 无论提交成功与否,都清空表单数据 |
||||
|
this.formData = { |
||||
|
jwcode: '', |
||||
|
amount: null, |
||||
|
paymentMethod: '', |
||||
|
notes: '' |
||||
|
}; |
||||
|
this.isLoading = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 充值表单样式 */ |
||||
|
.recharge-form { |
||||
|
/* 假设导航栏宽度为 200px,根据实际情况修改 */ |
||||
|
margin-left: 200px; |
||||
|
/* 调整表单宽度,使用百分比适应剩余空间 */ |
||||
|
width: calc(100% - 300px); |
||||
|
min-width: auto; |
||||
|
min-height: 890px; |
||||
|
margin-top: 10px; |
||||
|
padding: 20px; |
||||
|
border-radius: 8px; |
||||
|
/* 设置表单完全透明 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
/* 表单标题样式 */ |
||||
|
.form-title { |
||||
|
text-align: center; |
||||
|
margin-bottom: 30px; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 表单组样式 */ |
||||
|
.form-group { |
||||
|
margin-bottom: 30px; /* 增加表单组之间的间距 */ |
||||
|
} |
||||
|
|
||||
|
/* 表单组标签样式,修改文字颜色为深黑色 */ |
||||
|
.form-group label { |
||||
|
display: block; |
||||
|
margin-bottom: 10px; /* 增加标签与输入框的间距 */ |
||||
|
font-weight: bold; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 表单组输入框、选择框和文本框样式 */ |
||||
|
.form-group input[type="text"], |
||||
|
.form-group input[type="number"], |
||||
|
.form-group select, |
||||
|
.form-group textarea { |
||||
|
width: 98%; |
||||
|
padding: 8px; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 4px; |
||||
|
/* 设置文本框、选择框和文本域一般透明 */ |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
/* 表单组文本框样式 */ |
||||
|
.form-group textarea { |
||||
|
resize: vertical; |
||||
|
} |
||||
|
|
||||
|
/* 表单组按钮样式 */ |
||||
|
.form-group button { |
||||
|
width: 100%; |
||||
|
padding: 10px; |
||||
|
background-color: #007bff; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
border-radius: 4px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
/* 表单组按钮悬停样式 */ |
||||
|
.form-group button:hover { |
||||
|
background-color: #0056b3; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,320 @@ |
|||||
|
<template> |
||||
|
<!-- 整个页面容器 --> |
||||
|
<div class="recharge-details-page"> |
||||
|
<!-- 整个页面的卡片容器 --> |
||||
|
<el-card class="recharge-card"> |
||||
|
<!-- 搜索表单部分,使用卡片布局 --> |
||||
|
<el-card class="search-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<span>搜索条件</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<!-- 第一行搜索表单,内联布局 --> |
||||
|
<el-form :inline="true" :model="searchForm" class="search-form"> |
||||
|
<el-form-item label="精网号"> |
||||
|
<!-- 绑定精网号输入值到 searchForm.jwcode --> |
||||
|
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="地区"> |
||||
|
<!-- 使用 el-select 组件,支持筛选和创建新选项 --> |
||||
|
<el-select |
||||
|
v-model="searchForm.region" |
||||
|
placeholder="请选择或输入地区" |
||||
|
filterable |
||||
|
allow-create |
||||
|
default-first-option |
||||
|
> |
||||
|
<!-- 动态生成地区选项 --> |
||||
|
<el-option |
||||
|
v-for="region in regionList" |
||||
|
:key="region" |
||||
|
:label="region" |
||||
|
:value="region" |
||||
|
></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="订单号"> |
||||
|
<!-- 绑定订单号输入值到 searchForm.orderId --> |
||||
|
<el-input v-model="searchForm.orderId" placeholder="请输入订单号" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="充值方式"> |
||||
|
<el-select |
||||
|
v-model="searchForm.paymentMethod" |
||||
|
placeholder="请选择充值方式" |
||||
|
filterable |
||||
|
allow-create |
||||
|
default-first-option |
||||
|
> |
||||
|
<el-option label="微信" value="WECHAT"></el-option> |
||||
|
<el-option label="支付宝" value="ALIPAY"></el-option> |
||||
|
<el-option label="银行" value="BANK"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<!-- 第二行搜索表单,内联布局 --> |
||||
|
<el-form :inline="true" :model="searchForm" class="search-form"> |
||||
|
<!-- <el-form-item label="充值时间"> --> |
||||
|
<!-- 绑定充值时间范围选择值到 searchForm.rechargeTime --> |
||||
|
<!-- <el-date-picker |
||||
|
v-model="searchForm.rechargeTime" |
||||
|
type="daterange" |
||||
|
range-separator="至" |
||||
|
start-placeholder="开始日期" |
||||
|
end-placeholder="结束日期" |
||||
|
/> |
||||
|
</el-form-item> --> |
||||
|
<!-- 新增一个 div 包裹按钮组 --> |
||||
|
<div class="button-group-wrapper"> |
||||
|
<el-form-item> |
||||
|
<el-button type="primary" @click="handleSearch">查询</el-button> |
||||
|
<el-button @click="resetSearch">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</div> |
||||
|
</el-form> |
||||
|
</el-card> |
||||
|
|
||||
|
<!-- 搜索表单和明细表之间的分隔线 --> |
||||
|
<el-divider /> |
||||
|
|
||||
|
<!-- 明细表 --> |
||||
|
<el-card class="detail-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<span>充值明细</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<!-- 表格组件,绑定表格数据和加载状态 --> |
||||
|
<el-table |
||||
|
:data="rechargeDetailsList" |
||||
|
style="width: 100%" |
||||
|
:loading="isLoading" |
||||
|
stripe |
||||
|
border |
||||
|
> |
||||
|
<el-table-column prop="序号" label="序号" width="80" /> |
||||
|
<el-table-column prop="精网号" label="精网号" width="120" /> |
||||
|
<el-table-column prop="姓名" label="姓名" width="120" /> |
||||
|
<el-table-column prop="订单号" label="订单号" width="180" /> |
||||
|
<el-table-column prop="充值方式" label="充值方式" width="120" /> |
||||
|
<el-table-column prop="所属地区" label="所属地区" width="120" /> |
||||
|
<el-table-column prop="金币数量" label="金币数量" width="120" /> |
||||
|
<el-table-column prop="金额" label="金额" width="120" /> |
||||
|
<el-table-column prop="备注" label="备注" min-width="150"/> |
||||
|
<el-table-column prop="充值时间" label="充值时间" width="180" /> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 空状态提示 --> |
||||
|
<template #empty> |
||||
|
<el-empty description="暂无充值明细数据" /> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 分页组件,绑定当前页码、每页显示数量和总数据量 --> |
||||
|
<el-pagination |
||||
|
class="pagination" |
||||
|
:current-page="currentPage" |
||||
|
:page-size="pageSize" |
||||
|
:total="total" |
||||
|
layout="prev, pager, next, jumper" |
||||
|
@current-change="handlePageChange" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue' |
||||
|
import axios from 'axios' |
||||
|
import { Search } from '@element-plus/icons-vue' |
||||
|
|
||||
|
// 定义充值明细数据,初始为空数组 |
||||
|
const rechargeDetailsList = ref([]) |
||||
|
// 加载状态 |
||||
|
const isLoading = ref(false) |
||||
|
// 当前页码 |
||||
|
const currentPage = ref(1) |
||||
|
// 每页显示数量 |
||||
|
const pageSize = ref(8) |
||||
|
// 定义总数据量,初始为 0 |
||||
|
const total = ref(0) |
||||
|
|
||||
|
// 搜索表单数据 |
||||
|
const searchForm = ref({ |
||||
|
jwcode: '', // 精网号,初始为空字符串 |
||||
|
region: '', // 地区,初始为空字符串 |
||||
|
orderId: '', // 订单号,初始为空字符串 |
||||
|
paymentMethod: '', // 充值方式,初始为空字符串 |
||||
|
rechargeTime: [] // 充值时间范围,初始为空数组 |
||||
|
}) |
||||
|
|
||||
|
// 地区列表 |
||||
|
const regionList = ref([]) |
||||
|
|
||||
|
/** |
||||
|
* 获取地区数据的方法 |
||||
|
* 向服务器发送请求获取地区数据,并更新地区列表 |
||||
|
*/ |
||||
|
const fetchRegionList = async () => { |
||||
|
try { |
||||
|
// 请替换为实际的地区数据接口地址 |
||||
|
const response = await axios.post('http://192.168.8.94:5173/users/getRegion', {}); |
||||
|
if (response.data && Array.isArray(response.data)) { |
||||
|
regionList.value = response.data; |
||||
|
} else { |
||||
|
throw new Error('后端返回的地区数据格式不正确'); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取地区数据失败', error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 获取充值明细数据的方法 |
||||
|
* 向服务器发送请求获取充值明细数据,并更新页面显示 |
||||
|
*/ |
||||
|
const fetchRechargeDetails = async () => { |
||||
|
isLoading.value = true; |
||||
|
try { |
||||
|
const params = { |
||||
|
page: currentPage.value, |
||||
|
size: pageSize.value, |
||||
|
jwcode: searchForm.value.jwcode, |
||||
|
region: searchForm.value.region, // 修改为 region |
||||
|
orderId: searchForm.value.orderId, |
||||
|
paymentMethod: searchForm.value.paymentMethod, |
||||
|
}; |
||||
|
console.log('发送的请求参数:', params); |
||||
|
const response = await axios.post('http://192.168.8.94:5173/recharges/query', params, { |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json' |
||||
|
} |
||||
|
}); |
||||
|
console.log('后端返回的数据:', response.data); |
||||
|
const items = response.data.items || []; |
||||
|
rechargeDetailsList.value = items.map((item, index) => ({ |
||||
|
...item, |
||||
|
序号: (currentPage.value - 1) * pageSize.value + index + 1 |
||||
|
})); |
||||
|
total.value = response.data.total || 0; |
||||
|
} catch (error) { |
||||
|
console.error('获取充值明细数据失败', error); |
||||
|
rechargeDetailsList.value = []; |
||||
|
total.value = 0; |
||||
|
} finally { |
||||
|
isLoading.value = false; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 处理搜索 |
||||
|
const handleSearch = () => { |
||||
|
// 清除搜索表单中字符串类型字段的所有空格 |
||||
|
searchForm.value.jwcode = searchForm.value.jwcode.replace(/\s/g, ''); |
||||
|
searchForm.value.region = searchForm.value.region.replace(/\s/g, ''); |
||||
|
searchForm.value.orderId = searchForm.value.orderId.replace(/\s/g, ''); |
||||
|
console.log('搜索表单数据:', searchForm.value); // 打印搜索表单数据 |
||||
|
currentPage.value = 1; |
||||
|
fetchRechargeDetails(); |
||||
|
}; |
||||
|
|
||||
|
// 重置搜索 |
||||
|
const resetSearch = () => { |
||||
|
searchForm.value = { |
||||
|
jwcode: '', |
||||
|
region: '', |
||||
|
orderId: '', |
||||
|
paymentMethod: '', |
||||
|
rechargeTime: [] |
||||
|
}; |
||||
|
currentPage.value = 1; |
||||
|
fetchRechargeDetails(); |
||||
|
}; |
||||
|
|
||||
|
// 处理分页变化 |
||||
|
const handlePageChange = (page) => { |
||||
|
currentPage.value = page |
||||
|
fetchRechargeDetails() |
||||
|
} |
||||
|
|
||||
|
// 组件挂载完成后,获取充值明细数据和地区数据 |
||||
|
onMounted(() => { |
||||
|
fetchRechargeDetails() |
||||
|
fetchRegionList() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 充值明细页面样式 */ |
||||
|
.recharge-details-page { |
||||
|
/* 假设导航栏宽度为 180px,根据实际情况修改 */ |
||||
|
margin-left: 180px; |
||||
|
/* 调整页面宽度,使用百分比适应剩余空间 */ |
||||
|
width: calc(100% - 250px); |
||||
|
min-width: auto; |
||||
|
margin-top: 2px; |
||||
|
padding: 20px; |
||||
|
/* 设置页面完全透明 */ |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.recharge-card { |
||||
|
margin: 1px; |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
min-height: 890px; |
||||
|
/* 设置卡片半透明背景 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
.search-card, |
||||
|
.detail-card { |
||||
|
margin-bottom: 20px; |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
/* 设置卡片半透明背景 */ |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
color: #000; /* 假设背景较暗,设置文字颜色为白色以便查看 */ |
||||
|
} |
||||
|
|
||||
|
.search-form { |
||||
|
margin-bottom: 20px; |
||||
|
display: flex; |
||||
|
flex-wrap: wrap; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
/* 输入框和选择框样式 */ |
||||
|
.search-form .el-input__inner, |
||||
|
.search-form .el-select .el-input__inner { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
border: 1px solid #ccc; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
.button-group-wrapper { |
||||
|
/* 让这个 div 占据剩余空间 */ |
||||
|
flex-grow: 1; |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式 */ |
||||
|
.el-table { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
|
||||
|
.el-table th, |
||||
|
.el-table td { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.pagination { |
||||
|
margin-top: 20px; |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,237 @@ |
|||||
|
<template> |
||||
|
<div class="refund-form"> |
||||
|
<h2 class="form-title">退款操作</h2> |
||||
|
|
||||
|
<form @submit.prevent="refund"> |
||||
|
<!-- 精网号输入组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="jwcode">精网号</label> |
||||
|
<input |
||||
|
type="text" |
||||
|
id="jwcode" |
||||
|
v-model="jwcode" |
||||
|
placeholder="请输入精网号" |
||||
|
required |
||||
|
@blur="checkJwcodeExists" |
||||
|
/> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 订单选择组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="order-select">订单</label> |
||||
|
<select id="order-select" v-model="selectedOrder" required> |
||||
|
<option value="">请选择订单</option> |
||||
|
<option v-for="record in rechargeRecords" :key="record.id" :value="record.id"> |
||||
|
{{ record.id }} |
||||
|
</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 备注输入组 --> |
||||
|
<div class="form-group"> |
||||
|
<label for="refundnotes">备注</label> |
||||
|
<textarea |
||||
|
id="refundnotes" |
||||
|
v-model="refundnotes" |
||||
|
rows="4" |
||||
|
placeholder="请输入备注(非必填)" |
||||
|
></textarea> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 提交按钮组 --> |
||||
|
<div class="form-group"> |
||||
|
<button type="submit" :disabled="isLoading"> |
||||
|
{{ isLoading ? '退款中...' : '退款' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</form> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import axios from 'axios'; |
||||
|
|
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
jwcode: '', |
||||
|
selectedOrder: '', |
||||
|
refundnotes: '', |
||||
|
rechargeRecords: [], |
||||
|
isLoading: false, |
||||
|
jwcodeExists: true |
||||
|
}; |
||||
|
}, |
||||
|
methods: { |
||||
|
/** |
||||
|
* 检查精网号是否存在于数据库 |
||||
|
*/ |
||||
|
async checkJwcodeExists() { |
||||
|
if (!this.jwcode) { |
||||
|
this.jwcodeExists = true; |
||||
|
return; |
||||
|
} |
||||
|
try { |
||||
|
const response = await axios.post('http://192.168.8.94:5173/refunds/checkJwcode', { |
||||
|
jwcode: this.jwcode |
||||
|
}); |
||||
|
if (response.data && typeof response.data.exists === 'boolean') { |
||||
|
this.jwcodeExists = response.data.exists; |
||||
|
} else { |
||||
|
console.error('后端返回的数据格式不正确,缺少 exists 属性或属性类型错误'); |
||||
|
this.jwcodeExists = true; |
||||
|
} |
||||
|
if (!this.jwcodeExists) { |
||||
|
alert('用户不存在,请检查精网号。'); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('检查精网号失败:', error); |
||||
|
alert('检查精网号时出错,请稍后再试。'); |
||||
|
this.jwcode = ''; |
||||
|
} |
||||
|
}, |
||||
|
async fetchRechargeRecords() { |
||||
|
if (this.jwcode.length !== 6) { |
||||
|
this.rechargeRecords = []; |
||||
|
return; |
||||
|
} |
||||
|
this.isLoading = true; |
||||
|
const REFUND_API_URL = 'http://192.168.8.94:5173/refunds/add/getOrderIds'; |
||||
|
try { |
||||
|
const response = await fetch(`${REFUND_API_URL}?jwcode=${this.jwcode}`); |
||||
|
if (!response.ok) { |
||||
|
throw new Error(`HTTP error! status: ${response.status}`); |
||||
|
} |
||||
|
const data = await response.json(); |
||||
|
this.rechargeRecords = data.map(record => ({ |
||||
|
id: record |
||||
|
})); |
||||
|
} catch (error) { |
||||
|
console.error('查询充值记录失败:', error); |
||||
|
alert(`查询充值记录失败:${error.message}`); |
||||
|
} finally { |
||||
|
this.isLoading = false; |
||||
|
} |
||||
|
}, |
||||
|
async refund() { |
||||
|
if (!this.selectedOrder) { |
||||
|
alert('请选择一个订单!'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!confirm('确定要退款吗?')) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
this.isLoading = true; |
||||
|
try { |
||||
|
const response = await fetch('http://192.168.8.94:5173/refunds/add/addRefunds', { |
||||
|
method: 'POST', |
||||
|
headers: { 'Content-Type': 'application/json' }, |
||||
|
body: JSON.stringify({ |
||||
|
jwcode: this.jwcode, |
||||
|
orderId: this.selectedOrder, |
||||
|
notes: this.refundnotes |
||||
|
}) |
||||
|
}); |
||||
|
const result = await response.json(); |
||||
|
console.log('后端返回结果:', result); |
||||
|
|
||||
|
if (result.code === '200') { |
||||
|
alert('退款提交成功!'); |
||||
|
this.selectedOrder = ''; |
||||
|
this.refundnotes = ''; |
||||
|
this.fetchRechargeRecords(); |
||||
|
} else { |
||||
|
alert('退款失败:' + ('该订单已存在退款待审核或退款成功的记录,无法重复添加退款申请' || '未知错误')); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('退款失败:', error); |
||||
|
alert('退款失败,请稍后再试。出现异常'); |
||||
|
} finally { |
||||
|
this.isLoading = false; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
jwcode(newValue) { |
||||
|
if (newValue.length === 6) { |
||||
|
this.fetchRechargeRecords(); |
||||
|
} else { |
||||
|
this.rechargeRecords = []; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 退款表单样式 */ |
||||
|
.refund-form { |
||||
|
/* 假设导航栏宽度为 200px,根据实际情况修改 */ |
||||
|
margin-left: 200px; |
||||
|
/* 调整表单宽度,使用百分比适应剩余空间 */ |
||||
|
width: calc(100% - 300px); |
||||
|
min-width: auto; |
||||
|
min-height: 890px; |
||||
|
margin-top: 10px; |
||||
|
padding: 20px; |
||||
|
border-radius: 8px; |
||||
|
/* 设置表单完全透明 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
/* 表单标题样式 */ |
||||
|
.form-title { |
||||
|
text-align: center; |
||||
|
margin-bottom: 30px; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 表单组样式 */ |
||||
|
.form-group { |
||||
|
margin-bottom: 30px; /* 增加表单组之间的间距 */ |
||||
|
} |
||||
|
|
||||
|
/* 表单组标签样式 */ |
||||
|
.form-group label { |
||||
|
display: block; |
||||
|
margin-bottom: 10px; /* 增加标签与输入框的间距 */ |
||||
|
font-weight: bold; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 表单组输入框、选择框和文本框样式 */ |
||||
|
.form-group input[type="text"], |
||||
|
.form-group select, |
||||
|
.form-group textarea { |
||||
|
width: 98%; |
||||
|
padding: 8px; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 4px; |
||||
|
/* 设置文本框、选择框和文本域一般透明 */ |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
/* 表单组文本框样式 */ |
||||
|
.form-group textarea { |
||||
|
resize: vertical; |
||||
|
} |
||||
|
|
||||
|
/* 表单组按钮样式 */ |
||||
|
.form-group button { |
||||
|
width: 100%; |
||||
|
padding: 10px; |
||||
|
background-color: #007bff; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
border-radius: 4px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
/* 表单组按钮悬停样式 */ |
||||
|
.form-group button:hover { |
||||
|
background-color: #0056b3; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,358 @@ |
|||||
|
<template> |
||||
|
<div class="user-detail-page"> |
||||
|
<!-- 卡片容器 --> |
||||
|
<el-card class="main-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<h1>用户明细页面</h1> |
||||
|
<!-- 添加导出 Excel 按钮 --> |
||||
|
<el-button type="success" @click="exportToExcel">导出 Excel</el-button> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 搜索表单 --> |
||||
|
<el-form :inline="true" :model="searchForm" class="search-form"> |
||||
|
<el-form-item label="用户名"> |
||||
|
<el-input v-model="searchForm.username" placeholder="请输入用户名" clearable></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="精网号"> |
||||
|
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号" clearable></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="地区"> |
||||
|
<!-- 使用 el-select 组件,支持筛选和创建新选项 --> |
||||
|
<el-select |
||||
|
v-model="searchForm.region" |
||||
|
placeholder="请选择或输入地区" |
||||
|
filterable |
||||
|
allow-create |
||||
|
default-first-option |
||||
|
> |
||||
|
<!-- 遍历地区列表生成选项 --> |
||||
|
<el-option |
||||
|
v-for="region in regionList" |
||||
|
:key="region" |
||||
|
:label="region" |
||||
|
:value="region" |
||||
|
></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item> |
||||
|
<!-- 添加自定义类名 --> |
||||
|
<el-button type="primary" @click="handleSearch" class="custom-search-btn">查询</el-button> |
||||
|
<el-button @click="resetSearch" class="custom-reset-btn">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 表格 --> |
||||
|
<el-table |
||||
|
:data="userDetailList" |
||||
|
style="width: 100%" |
||||
|
stripe |
||||
|
border |
||||
|
:empty-text="isLoading? '加载中...' : '暂无数据'" |
||||
|
fit |
||||
|
> |
||||
|
<el-table-column label="序号" width="80"> |
||||
|
<template #default="scope"> |
||||
|
<!-- 修复序号计算逻辑 --> |
||||
|
<!-- {{ (currentPage - 1) * pageSize + (scope.$rowIndex || 0) + 1 }} --> |
||||
|
<div>{{ (currentPage - 1) * pageSize + (scope.$rowIndex !== undefined ? scope.$rowIndex : scope.$index || 0) + 1 }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="username" label="用户名" min-width="120"></el-table-column> |
||||
|
<el-table-column prop="jwcode" label="精网号" min-width="120"></el-table-column> |
||||
|
<el-table-column prop="total_gold" label="余额" min-width="120"> |
||||
|
<template #default="scope"> |
||||
|
<!-- 格式化余额显示 --> |
||||
|
{{ formatCurrency(scope.row.total_gold) }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="region" label="地区" min-width="120"></el-table-column> |
||||
|
<el-table-column prop="created_at" label="创建时间" min-width="180"> |
||||
|
<template #default="scope"> |
||||
|
{{ formatDate(scope.row.created_at) }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="updated_at" label="更新时间" min-width="180"> |
||||
|
<template #default="scope"> |
||||
|
{{ formatDate(scope.row.updated_at) }} |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页组件 --> |
||||
|
<el-pagination |
||||
|
class="pagination" |
||||
|
:current-page="currentPage" |
||||
|
:page-size="pageSize" |
||||
|
:total="total" |
||||
|
layout="prev, pager, next, jumper" |
||||
|
@current-change="handlePageChange" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue' |
||||
|
import axios from 'axios' |
||||
|
import { ElMessage } from 'element-plus' |
||||
|
// 引入 xlsx 库 |
||||
|
import * as XLSX from 'xlsx' |
||||
|
|
||||
|
// 用户明细数据 |
||||
|
const userDetailList = ref([]) |
||||
|
const isLoading = ref(false) |
||||
|
const currentPage = ref(1) |
||||
|
const pageSize = ref(12) |
||||
|
const total = ref(0) |
||||
|
|
||||
|
// 搜索表单数据 |
||||
|
const searchForm = ref({ |
||||
|
username: '', |
||||
|
jwcode: '', |
||||
|
region: '', |
||||
|
page: currentPage.value, |
||||
|
size: pageSize.value |
||||
|
}) |
||||
|
|
||||
|
// 地区列表 |
||||
|
const regionList = ref([]) |
||||
|
|
||||
|
/** |
||||
|
* 格式化货币显示 |
||||
|
* @param {number} amount - 金额 |
||||
|
* @returns {string} - 格式化后的金额字符串 |
||||
|
*/ |
||||
|
const formatCurrency = (amount) => { |
||||
|
return amount ? `¥${amount.toFixed(2)}` : '¥0.00' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取用户明细数据 |
||||
|
*/ |
||||
|
const fetchUserDetailList = async () => { |
||||
|
isLoading.value = true |
||||
|
try { |
||||
|
searchForm.value.page = currentPage.value |
||||
|
searchForm.value.size = pageSize.value |
||||
|
const response = await axios.post('http://192.168.8.94:5173/users/getInfo', searchForm.value) |
||||
|
// 数据校验 |
||||
|
if (response.data && Array.isArray(response.data.items) && typeof response.data.total === 'number') { |
||||
|
userDetailList.value = response.data.items |
||||
|
total.value = response.data.total |
||||
|
} else { |
||||
|
throw new Error('后端返回的数据格式不正确!') |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取用户明细数据失败', error) |
||||
|
ElMessage.error('获取用户明细数据失败,请稍后重试') |
||||
|
} finally { |
||||
|
isLoading.value = false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取地区数据 |
||||
|
*/ |
||||
|
const fetchRegionList = async () => { |
||||
|
try { |
||||
|
// 修改为 post 请求,若后端需要请求体,可按需修改 {} |
||||
|
const response = await axios.post('http://192.168.8.94:5173/users/getRegion', {}); |
||||
|
if (response.data && Array.isArray(response.data)) { |
||||
|
regionList.value = response.data; |
||||
|
} else { |
||||
|
throw new Error('后端返回的地区数据格式不正确'); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('获取地区数据失败', error); |
||||
|
ElMessage.error('获取地区数据失败,请稍后重试'); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 处理搜索 |
||||
|
const handleSearch = () => { |
||||
|
currentPage.value = 1 |
||||
|
fetchUserDetailList() |
||||
|
} |
||||
|
|
||||
|
// 重置搜索 |
||||
|
const resetSearch = () => { |
||||
|
searchForm.value = { |
||||
|
username: '', |
||||
|
jwcode: '', |
||||
|
region: '', |
||||
|
page: 1, |
||||
|
size: pageSize.value |
||||
|
} |
||||
|
currentPage.value = 1 |
||||
|
fetchUserDetailList() |
||||
|
} |
||||
|
|
||||
|
// 处理分页变化 |
||||
|
const handlePageChange = (page) => { |
||||
|
currentPage.value = page |
||||
|
fetchUserDetailList() |
||||
|
} |
||||
|
|
||||
|
// 组件挂载完成后,获取用户明细数据和地区数据 |
||||
|
onMounted(() => { |
||||
|
fetchUserDetailList() |
||||
|
fetchRegionList() |
||||
|
}) |
||||
|
|
||||
|
/** |
||||
|
* 格式化日期时间 |
||||
|
* @param {string} dateString - ISO 8601 格式的日期时间字符串 |
||||
|
* @returns {string} - 格式化后的日期时间字符串 |
||||
|
*/ |
||||
|
const formatDate = (dateString) => { |
||||
|
if (!dateString) return '' |
||||
|
const date = new Date(dateString) |
||||
|
return date.toLocaleString() |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 导出用户明细数据到 Excel 文件 |
||||
|
*/ |
||||
|
const exportToExcel = () => { |
||||
|
if (userDetailList.value.length === 0) { |
||||
|
ElMessage.warning('暂无数据可导出') |
||||
|
return |
||||
|
} |
||||
|
// 定义表头 |
||||
|
const headers = [ |
||||
|
{ header: '序号', key: 'index' }, |
||||
|
{ header: '用户名', key: 'username' }, |
||||
|
{ header: '精网号', key: 'jwcode' }, |
||||
|
{ header: '余额', key: 'total_gold' }, |
||||
|
{ header: '地区', key: 'region' }, |
||||
|
{ header: '创建时间', key: 'created_at' }, |
||||
|
{ header: '更新时间', key: 'updated_at' } |
||||
|
]; |
||||
|
// 处理数据 |
||||
|
const data = userDetailList.value.map((item, index) => ({ |
||||
|
index: (currentPage.value - 1) * pageSize.value + index + 1, |
||||
|
username: item.username, |
||||
|
jwcode: item.jwcode, |
||||
|
total_gold: formatCurrency(item.total_gold), |
||||
|
region: item.region, |
||||
|
created_at: formatDate(item.created_at), |
||||
|
updated_at: formatDate(item.updated_at) |
||||
|
})); |
||||
|
// 创建工作表 |
||||
|
const worksheet = XLSX.utils.json_to_sheet(data, { header: headers.map(h => h.key) }); |
||||
|
// 设置表头 |
||||
|
XLSX.utils.sheet_add_aoa(worksheet, [headers.map(h => h.header)], { origin: 'A1' }); |
||||
|
|
||||
|
// 直接设置每列的宽度,单位是字符宽度 |
||||
|
// 这里的数值可以根据你的需求调整 |
||||
|
worksheet['!cols'] = [ |
||||
|
{ wch: 5 }, // 序号列宽度 |
||||
|
{ wch: 10 }, // 用户名列宽度 |
||||
|
{ wch: 10 }, // 精网号列宽度 |
||||
|
{ wch: 10 }, // 余额列宽度 |
||||
|
{ wch: 10 }, // 地区列宽度 |
||||
|
{ wch: 20 }, // 创建时间列宽度 |
||||
|
{ wch: 20 } // 更新时间列宽度 |
||||
|
]; |
||||
|
|
||||
|
// 创建工作簿 |
||||
|
const workbook = XLSX.utils.book_new(); |
||||
|
XLSX.utils.book_append_sheet(workbook, worksheet, '用户明细'); |
||||
|
// 导出文件 |
||||
|
XLSX.writeFile(workbook, '用户明细表.xlsx'); |
||||
|
} |
||||
|
|
||||
|
// 组件挂载完成后,获取用户明细数据和地区数据 |
||||
|
onMounted(() => { |
||||
|
fetchUserDetailList() |
||||
|
fetchRegionList() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 用户明细页面样式 */ |
||||
|
.user-detail-page { |
||||
|
/* 假设导航栏宽度为 180px,根据实际情况修改 */ |
||||
|
margin-left: 180px; |
||||
|
/* 调整页面宽度,使用百分比适应剩余空间 */ |
||||
|
width: calc(100% - 250px); |
||||
|
min-width: auto; |
||||
|
margin-top: 2px; |
||||
|
padding: 20px; |
||||
|
/* 设置页面完全透明 */ |
||||
|
background-color: transparent; |
||||
|
min-height: 80vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.main-card { |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
min-height: 890px; |
||||
|
flex: 1; /* 让卡片自适应填充剩余空间 */ |
||||
|
/* 设置卡片半透明背景 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
color: #000; /* 假设背景较暗,设置文字颜色为白色以便查看 */ |
||||
|
} |
||||
|
|
||||
|
.search-form { |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
/* 输入框和选择框样式 */ |
||||
|
.search-form .el-input__inner, |
||||
|
.search-form .el-select .el-input__inner { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
border: 1px solid #ccc; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 调整表格样式 */ |
||||
|
.el-table { |
||||
|
flex: 1; |
||||
|
min-width: 0; /* 防止表格溢出 */ |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
|
||||
|
.el-table th, |
||||
|
.el-table td { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
/* 调整分页样式 */ |
||||
|
.pagination { |
||||
|
margin-top: 10px; |
||||
|
} |
||||
|
|
||||
|
/* 查询按钮样式 */ |
||||
|
.custom-search-btn { |
||||
|
background-color: #ff6b6b; /* 高饱和度的红色 */ |
||||
|
border-color: #ff6b6b; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.custom-search-btn:hover { |
||||
|
background-color: #e55039; /* 鼠标悬停时颜色加深 */ |
||||
|
border-color: #e55039; |
||||
|
} |
||||
|
|
||||
|
/* 重置按钮样式 */ |
||||
|
.custom-reset-btn { |
||||
|
background-color: #48dbfb; /* 高饱和度的蓝色 */ |
||||
|
border-color: #48dbfb; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.custom-reset-btn:hover { |
||||
|
background-color: #0abde3; /* 鼠标悬停时颜色加深 */ |
||||
|
border-color: #0abde3; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,331 @@ |
|||||
|
<template> |
||||
|
<div class="recharge-audit-page"> |
||||
|
<!-- 卡片容器 --> |
||||
|
<el-card class="main-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<h1>充值审核页面</h1> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 搜索表单 --> |
||||
|
<el-form :inline="true" :model="searchForm" class="search-form"> |
||||
|
<el-form-item label="精网号"> |
||||
|
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="状态"> |
||||
|
<el-select v-model="searchForm.status" |
||||
|
placeholder="请选择状态" |
||||
|
filterable |
||||
|
allow-create |
||||
|
default-first-option |
||||
|
> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option label="待审核" value="0"></el-option> |
||||
|
<el-option label="已通过" value="1"></el-option> |
||||
|
<el-option label="已驳回" value="2"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item> |
||||
|
<el-button type="primary" @click="handleSearch">搜索</el-button> |
||||
|
<el-button @click="resetSearch">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 表格 --> |
||||
|
<el-table |
||||
|
:data="rechargeAuditList" |
||||
|
style="width: 100%" |
||||
|
stripe |
||||
|
border |
||||
|
:empty-text="isLoading? '加载中...' : '暂无数据'" |
||||
|
> |
||||
|
<el-table-column label="序号" width="80"> |
||||
|
<template #default="scope"> |
||||
|
<!-- 根据当前页码和每页显示数量计算序号 --> |
||||
|
<div>{{ (currentPage - 1) * pageSize + (scope.$rowIndex !== undefined ? scope.$rowIndex : scope.$index || 0) + 1 }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="jwcode" label="精网号" width="120"></el-table-column> |
||||
|
<el-table-column prop="order_id" label="订单号" width="180"></el-table-column> |
||||
|
<el-table-column prop="姓名" label="用户名" width="120"></el-table-column> |
||||
|
<el-table-column prop="所属地区" label="所属地区" width="120"></el-table-column> |
||||
|
<el-table-column prop="amount" label="充值金额" width="120"></el-table-column> |
||||
|
<el-table-column prop="payment_method" label="支付方式" width="120"></el-table-column> |
||||
|
<el-table-column prop="notes" label="备注" min-width="120"></el-table-column> |
||||
|
<el-table-column label="状态" width="120"> |
||||
|
<template #default="scope"> |
||||
|
<el-tag |
||||
|
:type="getStatusTagType(scope.row.status)" |
||||
|
>{{ getStatusText(scope.row.status) }}</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column label="操作" width="200"> |
||||
|
<template #default="scope"> |
||||
|
<el-button |
||||
|
v-if="scope.row.status === 0" |
||||
|
type="success" |
||||
|
size="small" |
||||
|
@click="handlePass(scope.row)" |
||||
|
>通过</el-button> |
||||
|
<el-button |
||||
|
v-if="scope.row.status === 0" |
||||
|
type="danger" |
||||
|
size="small" |
||||
|
@click="handleReject(scope.row)" |
||||
|
>驳回</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页组件 --> |
||||
|
<el-pagination |
||||
|
class="pagination" |
||||
|
:current-page="currentPage" |
||||
|
:page-size="pageSize" |
||||
|
:total="total" |
||||
|
layout="prev, pager, next, jumper" |
||||
|
@current-change="handlePageChange" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue' |
||||
|
import axios from 'axios' |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus' |
||||
|
|
||||
|
// 充值审核数据 |
||||
|
const rechargeAuditList = ref([]) |
||||
|
const isLoading = ref(false) |
||||
|
const currentPage = ref(1) |
||||
|
const pageSize = ref(12) |
||||
|
const total = ref(0) |
||||
|
|
||||
|
// 搜索表单数据 |
||||
|
const searchForm = ref({ |
||||
|
jwcode: '', |
||||
|
statuses: '', |
||||
|
page: currentPage.value, |
||||
|
size: pageSize.value |
||||
|
}) |
||||
|
|
||||
|
/** |
||||
|
* 根据状态码获取状态文本 |
||||
|
* @param {number} status - 订单状态码 |
||||
|
* @returns {string} - 状态文本 |
||||
|
*/ |
||||
|
const getStatusText = (status) => { |
||||
|
const statusMap = { |
||||
|
0: '充值待审核', |
||||
|
1: '充值通过', |
||||
|
2: '充值驳回', |
||||
|
3: '退款待审核', |
||||
|
4: '退款通过', |
||||
|
5: '退款驳回' |
||||
|
} |
||||
|
return statusMap[status] || '未知状态' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据状态码获取标签类型 |
||||
|
* @param {number} status - 订单状态码 |
||||
|
* @returns {string} - 标签类型 |
||||
|
*/ |
||||
|
const getStatusTagType = (status) => { |
||||
|
const tagTypeMap = { |
||||
|
0: 'info', |
||||
|
1: 'success', |
||||
|
2: 'danger', |
||||
|
3: 'info', |
||||
|
4: 'success', |
||||
|
5: 'danger' |
||||
|
} |
||||
|
return tagTypeMap[status] || 'info' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取充值订单数据 |
||||
|
*/ |
||||
|
const fetchRechargeAuditList = async () => { |
||||
|
isLoading.value = true |
||||
|
try { |
||||
|
searchForm.value.page = currentPage.value |
||||
|
searchForm.value.size = pageSize.value |
||||
|
const response = await axios.post('http://192.168.8.94:5173/recharges/getall', searchForm.value) |
||||
|
console.log('后端返回的数据:', response.data) |
||||
|
rechargeAuditList.value = response.data.items || [] |
||||
|
total.value = response.data.total || 0 |
||||
|
} catch (error) { |
||||
|
console.error('获取充值审核数据失败', error) |
||||
|
} finally { |
||||
|
isLoading.value = false |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 处理搜索 |
||||
|
const handleSearch = () => { |
||||
|
// 清除精网号中的所有空格 |
||||
|
searchForm.value.jwcode = searchForm.value.jwcode.replace(/\s/g, ''); |
||||
|
currentPage.value = 1; |
||||
|
fetchRechargeAuditList(); |
||||
|
} |
||||
|
|
||||
|
// 重置搜索 |
||||
|
const resetSearch = () => { |
||||
|
searchForm.value = { |
||||
|
jwcode: '', |
||||
|
statuses: '', |
||||
|
page: currentPage.value, |
||||
|
size: pageSize.value |
||||
|
}; |
||||
|
currentPage.value = 1; |
||||
|
console.log('重置后 currentPage:', currentPage.value); // 打印当前页码 |
||||
|
fetchRechargeAuditList(); |
||||
|
} |
||||
|
|
||||
|
// 处理分页变化 |
||||
|
const handlePageChange = (page) => { |
||||
|
currentPage.value = Number(page) // 确保赋值为数字类型 |
||||
|
console.log('分页变化后 currentPage:', currentPage.value) // 打印当前页码 |
||||
|
fetchRechargeAuditList() |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 审核充值订单 - 通过 |
||||
|
* @param {Object} record - 当前订单记录 |
||||
|
*/ |
||||
|
const handlePass = async (record) => { |
||||
|
try { |
||||
|
// 弹出确认对话框 |
||||
|
await ElMessageBox.confirm( |
||||
|
'确定通过该订单审核吗?', |
||||
|
'提示', |
||||
|
{ |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning', |
||||
|
} |
||||
|
) |
||||
|
// 请求地址 |
||||
|
await axios.post(`http://192.168.8.94:5173/recharges/approve/${record.order_id}`) |
||||
|
record.status = 1 |
||||
|
ElMessage.success('审核通过成功') |
||||
|
fetchRechargeAuditList() |
||||
|
} catch (error) { |
||||
|
if (error !== 'cancel') { |
||||
|
console.error('审核通过失败', error) |
||||
|
ElMessage.error('审核通过失败') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 审核充值订单 - 驳回 |
||||
|
* @param {Object} record - 当前订单记录 |
||||
|
*/ |
||||
|
const handleReject = async (record) => { |
||||
|
try { |
||||
|
// 弹出确认对话框 |
||||
|
await ElMessageBox.confirm( |
||||
|
'确定驳回该订单审核吗?', |
||||
|
'提示', |
||||
|
{ |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning', |
||||
|
} |
||||
|
) |
||||
|
// 修改请求地址 |
||||
|
await axios.post(`http://192.168.8.94:5173/recharges/reject/${record.order_id}`) |
||||
|
record.status = 2 |
||||
|
ElMessage.success('审核驳回成功') |
||||
|
fetchRechargeAuditList() |
||||
|
} catch (error) { |
||||
|
if (error !== 'cancel') { |
||||
|
console.error('审核驳回失败', error) |
||||
|
ElMessage.error('审核驳回失败') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 组件挂载完成后,获取充值订单数据 |
||||
|
onMounted(() => { |
||||
|
fetchRechargeAuditList() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 充值审核页面样式 */ |
||||
|
.recharge-audit-page { |
||||
|
/* 假设导航栏宽度为 200px,根据实际情况修改 */ |
||||
|
margin-left: 200px; |
||||
|
/* 调整页面宽度,使用百分比适应剩余空间 */ |
||||
|
width: calc(100% - 300px); |
||||
|
min-width: auto; |
||||
|
min-height: 890px; |
||||
|
margin-top: 10px; |
||||
|
padding: 20px; |
||||
|
border-radius: 8px; |
||||
|
/* 设置页面半透明 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
/* 卡片容器样式 */ |
||||
|
.main-card { |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
/* 设置卡片半透明背景 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
/* 卡片标题样式,修改文字颜色为深黑色 */ |
||||
|
.card-header h1 { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
color: #000; /* 假设背景较暗,设置文字颜色为白色以便查看 */ |
||||
|
} |
||||
|
|
||||
|
/* 表单标签样式,修改文字颜色为深黑色 */ |
||||
|
.search-form .el-form-item__label { |
||||
|
font-weight: bold; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
.search-form { |
||||
|
margin-bottom: 30px; /* 增加搜索表单与表格的间距 */ |
||||
|
} |
||||
|
|
||||
|
/* 输入框和选择框样式 */ |
||||
|
.search-form .el-input__inner, |
||||
|
.search-form .el-select .el-input__inner { |
||||
|
width: 98%; |
||||
|
padding: 8px; |
||||
|
border: 1px solid #ccc; |
||||
|
border-radius: 4px; |
||||
|
/* 设置文本框、选择框和文本域一般透明 */ |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式 */ |
||||
|
.el-table { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
.el-table th, |
||||
|
.el-table td { |
||||
|
background-color: transparent; |
||||
|
color: black; |
||||
|
} |
||||
|
|
||||
|
/* 分页组件样式 */ |
||||
|
.pagination { |
||||
|
margin-top: 30px; |
||||
|
text-align: right; |
||||
|
color: black; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,321 @@ |
|||||
|
<template> |
||||
|
<div class="refund-audit-page"> |
||||
|
<!-- 卡片容器 --> |
||||
|
<el-card class="main-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<h1>退款审核页面</h1> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<!-- 搜索表单 --> |
||||
|
<el-form :inline="true" :model="searchForm" class="search-form"> |
||||
|
<el-form-item label="精网号"> |
||||
|
<el-input v-model="searchForm.jwcode" placeholder="请输入精网号"></el-input> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="状态"> |
||||
|
<el-select v-model="searchForm.status" |
||||
|
placeholder="请选择状态" |
||||
|
filterable |
||||
|
allow-create |
||||
|
default-first-option |
||||
|
> |
||||
|
<el-option label="全部" value=""></el-option> |
||||
|
<el-option label="待审核" value="3"></el-option> |
||||
|
<el-option label="已通过" value="4"></el-option> |
||||
|
<el-option label="已驳回" value="5"></el-option> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item> |
||||
|
<el-button type="primary" @click="handleSearch">搜索</el-button> |
||||
|
<el-button @click="resetSearch">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
|
||||
|
<!-- 表格 --> |
||||
|
<el-table |
||||
|
:data="refundAuditList" |
||||
|
style="width: 100%" |
||||
|
stripe |
||||
|
border |
||||
|
:empty-text="isLoading? '加载中...' : '暂无数据'" |
||||
|
> |
||||
|
<el-table-column label="序号" width="80"> |
||||
|
<template #default="scope"> |
||||
|
<!-- 根据当前页码和每页显示数量计算序号 --> |
||||
|
<div>{{ (currentPage - 1) * pageSize + (scope.$rowIndex !== undefined ? scope.$rowIndex : scope.$index || 0) + 1 }}</div> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column prop="jwcode" label="精网号" width="120"></el-table-column> |
||||
|
<el-table-column prop="order_id" label="订单号" width="180"></el-table-column> |
||||
|
<el-table-column prop="姓名" label="用户名" width="120"></el-table-column> |
||||
|
<el-table-column prop="所属地区" label="所属地区" width="120"></el-table-column> |
||||
|
<el-table-column prop="refund_amount" label="退款金额" width="120"></el-table-column> |
||||
|
<el-table-column prop="notes" label="备注" min-width="200"></el-table-column> |
||||
|
<el-table-column label="状态" width="120"> |
||||
|
<template #default="scope"> |
||||
|
<el-tag |
||||
|
:type="getStatusTagType(scope.row.status)" |
||||
|
>{{ getStatusText(scope.row.status) }}</el-tag> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
<el-table-column label="操作" width="200"> |
||||
|
<template #default="scope"> |
||||
|
<el-button |
||||
|
v-if="scope.row.status === 3" |
||||
|
type="success" |
||||
|
size="small" |
||||
|
@click="handlePass(scope.row)" |
||||
|
>通过</el-button> |
||||
|
<el-button |
||||
|
v-if="scope.row.status === 3" |
||||
|
type="danger" |
||||
|
size="small" |
||||
|
@click="handleReject(scope.row)" |
||||
|
>驳回</el-button> |
||||
|
</template> |
||||
|
</el-table-column> |
||||
|
</el-table> |
||||
|
|
||||
|
<!-- 分页组件 --> |
||||
|
<el-pagination |
||||
|
class="pagination" |
||||
|
:current-page="currentPage" |
||||
|
:page-size="pageSize" |
||||
|
:total="total" |
||||
|
layout="prev, pager, next, jumper" |
||||
|
@current-change="handlePageChange" |
||||
|
/> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue' |
||||
|
import axios from 'axios' |
||||
|
import { ElMessage, ElMessageBox } from 'element-plus' |
||||
|
|
||||
|
// 退款审核数据 |
||||
|
const refundAuditList = ref([]) |
||||
|
const isLoading = ref(false) |
||||
|
const currentPage = ref(1) |
||||
|
const pageSize = ref(12) |
||||
|
const total = ref(0) |
||||
|
|
||||
|
// 搜索表单数据 |
||||
|
const searchForm = ref({ |
||||
|
jwcode: '', |
||||
|
statuses: '', |
||||
|
page: currentPage.value, |
||||
|
size: pageSize.value |
||||
|
}) |
||||
|
|
||||
|
/** |
||||
|
* 根据状态码获取状态文本 |
||||
|
* @param {number} status - 订单状态码 |
||||
|
* @returns {string} - 状态文本 |
||||
|
*/ |
||||
|
const getStatusText = (status) => { |
||||
|
const statusMap = { |
||||
|
3: '退款待审核', |
||||
|
4: '退款通过', |
||||
|
5: '退款驳回' |
||||
|
} |
||||
|
return statusMap[status] || '未知状态' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据状态码获取标签类型 |
||||
|
* @param {number} status - 订单状态码 |
||||
|
* @returns {string} - 标签类型 |
||||
|
*/ |
||||
|
const getStatusTagType = (status) => { |
||||
|
const tagTypeMap = { |
||||
|
3: 'info', |
||||
|
4: 'success', |
||||
|
5: 'danger' |
||||
|
} |
||||
|
return tagTypeMap[status] || 'info' |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取退款订单数据 |
||||
|
*/ |
||||
|
const fetchRefundAuditList = async () => { |
||||
|
isLoading.value = true; |
||||
|
try { |
||||
|
searchForm.value.page = currentPage.value; |
||||
|
searchForm.value.size = pageSize.value; |
||||
|
const response = await axios.post('http://192.168.8.94:5173/refunds/getall', searchForm.value); |
||||
|
console.log('后端返回的数据:', response.data); |
||||
|
refundAuditList.value = response.data.items || []; |
||||
|
total.value = response.data.total || 0; |
||||
|
} catch (error) { |
||||
|
console.error('获取退款审核数据失败:', error); |
||||
|
if (error.response) { |
||||
|
console.error('响应状态码:', error.response.status); |
||||
|
console.error('响应数据:', error.response.data); |
||||
|
} else if (error.request) { |
||||
|
console.error('请求已发送,但没有收到响应'); |
||||
|
} else { |
||||
|
console.error('请求设置时出错:', error.message); |
||||
|
} |
||||
|
} finally { |
||||
|
isLoading.value = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 处理搜索 |
||||
|
const handleSearch = async () => { |
||||
|
try { |
||||
|
// 清除精网号中的所有空格 |
||||
|
searchForm.value.jwcode = searchForm.value.jwcode.replace(/\s/g, ''); |
||||
|
console.log('搜索表单数据:', searchForm.value); // 打印搜索表单数据 |
||||
|
currentPage.value = 1; |
||||
|
await fetchRefundAuditList(); |
||||
|
console.log('搜索操作完成'); |
||||
|
} catch (error) { |
||||
|
console.error('搜索操作出错:', error); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 重置搜索 |
||||
|
const resetSearch = () => { |
||||
|
searchForm.value = { |
||||
|
jwcode: '', |
||||
|
statuses: '', |
||||
|
page: currentPage.value, |
||||
|
size: pageSize.value |
||||
|
} |
||||
|
currentPage.value = 1 |
||||
|
fetchRefundAuditList() |
||||
|
} |
||||
|
|
||||
|
// 处理分页变化 |
||||
|
const handlePageChange = (page) => { |
||||
|
currentPage.value = page |
||||
|
fetchRefundAuditList() |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 审核退款订单 - 通过 |
||||
|
* @param {Object} record - 当前订单记录 |
||||
|
*/ |
||||
|
const handlePass = async (record) => { |
||||
|
try { |
||||
|
// 弹出确认对话框 |
||||
|
await ElMessageBox.confirm( |
||||
|
'确定通过该订单退款审核吗?', |
||||
|
'提示', |
||||
|
{ |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning', |
||||
|
} |
||||
|
) |
||||
|
// 请求地址 |
||||
|
await axios.post(`http://192.168.8.94:5173/refunds/approve/${record.order_id}`) |
||||
|
record.status = 4 |
||||
|
ElMessage.success('退款审核通过成功') |
||||
|
fetchRefundAuditList() |
||||
|
} catch (error) { |
||||
|
if (error !== 'cancel') { |
||||
|
console.error('退款审核通过失败', error) |
||||
|
ElMessage.error('退款审核通过失败') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 审核退款订单 - 驳回 |
||||
|
* @param {Object} record - 当前订单记录 |
||||
|
*/ |
||||
|
const handleReject = async (record) => { |
||||
|
try { |
||||
|
// 弹出确认对话框 |
||||
|
await ElMessageBox.confirm( |
||||
|
'确定驳回该订单退款审核吗?', |
||||
|
'提示', |
||||
|
{ |
||||
|
confirmButtonText: '确定', |
||||
|
cancelButtonText: '取消', |
||||
|
type: 'warning', |
||||
|
} |
||||
|
) |
||||
|
// 修改请求地址 |
||||
|
await axios.post(`http://192.168.8.94:5173/refunds/reject/${record.order_id}`) |
||||
|
record.status = 5 |
||||
|
ElMessage.success('退款审核驳回成功') |
||||
|
fetchRefundAuditList() |
||||
|
} catch (error) { |
||||
|
if (error !== 'cancel') { |
||||
|
console.error('退款审核驳回失败', error) |
||||
|
ElMessage.error('退款审核驳回失败') |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 组件挂载完成后,获取退款订单数据 |
||||
|
onMounted(() => { |
||||
|
fetchRefundAuditList() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 退款审核页面样式 */ |
||||
|
.refund-audit-page { |
||||
|
/* 假设导航栏宽度为 180px,根据实际情况修改 */ |
||||
|
margin-left: 180px; |
||||
|
/* 调整页面宽度,使用百分比适应剩余空间 */ |
||||
|
width: calc(100% - 250px); |
||||
|
min-width: auto; |
||||
|
margin-top: 2px; |
||||
|
padding: 20px; |
||||
|
/* 设置页面完全透明 */ |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
/* 卡片容器样式 */ |
||||
|
.main-card { |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
min-height: 890px; |
||||
|
/* 设置卡片半透明背景 */ |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
color: #000; /* 假设背景较暗,设置文字颜色为白色以便查看 */ |
||||
|
} |
||||
|
|
||||
|
.search-form { |
||||
|
margin-bottom: 30px; /* 增加搜索表单与表格的间距 */ |
||||
|
} |
||||
|
|
||||
|
/* 输入框和选择框样式 */ |
||||
|
.search-form .el-input__inner, |
||||
|
.search-form .el-select .el-input__inner { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
border: 1px solid #ccc; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 表格样式 */ |
||||
|
.el-table { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
} |
||||
|
|
||||
|
.el-table th, |
||||
|
.el-table td { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
/* 分页组件样式 */ |
||||
|
.pagination { |
||||
|
margin-top: 30px; |
||||
|
text-align: right; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,379 @@ |
|||||
|
<template> |
||||
|
<div class="goods-consumption-page"> |
||||
|
<el-card class="main-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<h1>产品消费</h1> |
||||
|
</div> |
||||
|
</template> |
||||
|
<div class="inner-form-container"> |
||||
|
<el-form :model="form" label-width="120px" style="max-width: 830px" class="search-form"> |
||||
|
<el-form-item label="精网号"> |
||||
|
<el-input v-model="form.jwcode" @blur="handleJwcodeBlur" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="商品信息"> |
||||
|
<el-select v-model="form.productName" placeholder="请选择商品" @change="updateCoinAmount"> |
||||
|
<el-option v-for="item in goodsOptions" :key="item" :label="item" :value="item" /> |
||||
|
</el-select> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="商品金额"> |
||||
|
<el-input v-model="form.price" :disabled="true" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="备注"> |
||||
|
<el-input v-model="form.notes" type="textarea" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="提交人"> |
||||
|
<el-input v-model="form.submitter" :disabled="true" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item label="当前时间"> |
||||
|
<el-input v-model="form.date1" :disabled="true" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item :label-width="0"> |
||||
|
<el-button type="primary" @click="onSubmit" class="custom-submit-btn">提交</el-button> |
||||
|
<el-button @click="onReset" class="custom-reset-btn">重置</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
<el-card class="embedded-info-panel"> |
||||
|
<template #header> |
||||
|
<div class="panel-header"> |
||||
|
<h3>用户信息</h3> |
||||
|
</div> |
||||
|
</template> |
||||
|
<div class="info-item"> |
||||
|
<span class="info-label">精网号:</span> |
||||
|
<span class="info-value">{{ userInfo.jwcode }}</span> |
||||
|
</div> |
||||
|
<div class="info-item"> |
||||
|
<span class="info-label">用户名:</span> |
||||
|
<span class="info-value">{{ userInfo.username }}</span> |
||||
|
</div> |
||||
|
<div class="info-item"> |
||||
|
<span class="info-label">用户余额:</span> |
||||
|
<span class="info-value">{{ userInfo.total_gold }}</span> |
||||
|
</div> |
||||
|
<div class="info-item"> |
||||
|
<span class="info-label">地区:</span> |
||||
|
<span class="info-value">{{ userInfo.region }}</span> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { reactive, onMounted, onUnmounted, ref } from 'vue' |
||||
|
import { ElMessage } from 'element-plus' |
||||
|
import axios from 'axios' |
||||
|
|
||||
|
const form = reactive({ |
||||
|
jwcode: '', |
||||
|
productName: '', |
||||
|
date1: '', |
||||
|
price: '', |
||||
|
notes: '', |
||||
|
submitter: '张三', |
||||
|
}) |
||||
|
|
||||
|
const goodsOptions = ref([]) |
||||
|
const userInfo = reactive({ |
||||
|
jwcode: '', |
||||
|
username: '', |
||||
|
total_gold: '', |
||||
|
region: '' |
||||
|
}) |
||||
|
|
||||
|
let timer: number; |
||||
|
|
||||
|
const updateCoinAmount = async () => { |
||||
|
if (form.productName) { |
||||
|
try { |
||||
|
const response = await axios.post('http://192.168.8.94:5173/cost/findPrice', { |
||||
|
productName: form.productName |
||||
|
}) |
||||
|
form.price = response.data.toString() |
||||
|
|
||||
|
// // 余额判断逻辑 |
||||
|
// const balance = parseFloat(userInfo.total_gold); |
||||
|
// const productPrice = parseFloat(form.price); |
||||
|
|
||||
|
// if (isNaN(balance) || isNaN(productPrice)) { |
||||
|
// console.error('余额或商品价格转换为数字失败'); |
||||
|
// return; |
||||
|
// } |
||||
|
|
||||
|
// if (balance < productPrice) { |
||||
|
// ElMessage.warning('余额不足,请充值后再购买'); |
||||
|
// } |
||||
|
// else { |
||||
|
// ElMessage.success('余额充足,可以购买'); |
||||
|
// console.log('余额充足,可以购买'); |
||||
|
// } |
||||
|
} catch (error) { |
||||
|
console.error('获取商品价格失败:', error) |
||||
|
ElMessage.error('获取商品价格失败,请重试') |
||||
|
} |
||||
|
} else { |
||||
|
form.price = '' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
const updateTime = () => { |
||||
|
form.date1 = new Date().toLocaleString(); |
||||
|
}; |
||||
|
updateTime(); |
||||
|
timer = window.setInterval(updateTime, 1000); |
||||
|
}); |
||||
|
|
||||
|
onUnmounted(() => { |
||||
|
window.clearInterval(timer); |
||||
|
}); |
||||
|
|
||||
|
const fetchGoods = async () => { |
||||
|
try { |
||||
|
const response = await axios.post('http://192.168.8.94:5173/cost/findProduct') |
||||
|
goodsOptions.value = response.data |
||||
|
} catch (error) { |
||||
|
console.error('获取商品信息失败:', error) |
||||
|
ElMessage.error('获取商品信息失败,请重试') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const onSubmit = async () => { |
||||
|
// 验证精网号和商品信息是否为空 |
||||
|
if (!form.jwcode) { |
||||
|
ElMessage.warning('请输入精网号'); |
||||
|
return; |
||||
|
} |
||||
|
if (!form.productName) { |
||||
|
ElMessage.warning('请选择商品信息'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 余额检查逻辑 |
||||
|
const balance = parseFloat(userInfo.total_gold); |
||||
|
const productPrice = parseFloat(form.price); |
||||
|
|
||||
|
if (isNaN(balance) || isNaN(productPrice)) { |
||||
|
console.error('余额或商品价格转换为数字失败'); |
||||
|
ElMessage.error('余额或商品价格异常,请检查'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (balance < productPrice) { |
||||
|
ElMessage.warning('余额不足,请充值后再购买'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
const { date1, ...submitData } = form |
||||
|
const response = await axios.post('http://192.168.8.94:5173/cost/addCost', submitData) |
||||
|
if (response.data.code === '200') { |
||||
|
console.log('提交成功:', response.data) |
||||
|
ElMessage.success('表单提交成功') |
||||
|
// 提交成功后刷新用户信息 |
||||
|
await refreshUserInfo(); |
||||
|
} else { |
||||
|
ElMessage.error('表单提交失败: ' + response.data.msg) |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('提交失败:', error) |
||||
|
ElMessage.error('表单提交失败,请重试') |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 新增刷新用户信息的方法 |
||||
|
const refreshUserInfo = async () => { |
||||
|
const jwcode = form.jwcode.trim(); |
||||
|
if (jwcode) { |
||||
|
try { |
||||
|
const userResponse = await axios.post('http://192.168.8.94:5173/users/findUserInfoByJwcode', { |
||||
|
jwcode: jwcode |
||||
|
}); |
||||
|
if (userResponse.data.length > 0) { |
||||
|
const userData = userResponse.data[0]; |
||||
|
userInfo.jwcode = userData.jwcode; |
||||
|
userInfo.username = userData.username; |
||||
|
userInfo.total_gold = userData.total_gold.toString(); |
||||
|
userInfo.region = userData.region; |
||||
|
} else { |
||||
|
ElMessage.warning('未找到该精网号对应的用户信息'); |
||||
|
Object.keys(userInfo).forEach(key => userInfo[key] = ''); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('刷新用户信息失败:', error); |
||||
|
ElMessage.error('刷新用户信息失败,请重试'); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const onReset = () => { |
||||
|
Object.keys(form).forEach(key => { |
||||
|
if (key === 'submitter') { |
||||
|
form[key] = '张三' |
||||
|
} else { |
||||
|
form[key] = '' |
||||
|
} |
||||
|
}); |
||||
|
goodsOptions.value = [] |
||||
|
form.price = '' |
||||
|
ElMessage.info('表单已重置') |
||||
|
} |
||||
|
|
||||
|
const handleJwcodeBlur = async () => { |
||||
|
const jwcode = form.jwcode; |
||||
|
const trimmedJwcode = jwcode.trim(); |
||||
|
|
||||
|
if (trimmedJwcode === '') { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (isNaN(Number(trimmedJwcode))) { |
||||
|
ElMessage.warning('精网号必须为数字,请重新输入'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
const checkResponse = await axios.post('http://192.168.8.94:5173/recharges/checkJwcode', { |
||||
|
jwcode: trimmedJwcode |
||||
|
}); |
||||
|
if (checkResponse.data.exists) { |
||||
|
ElMessage.success('精网号存在'); |
||||
|
await fetchGoods(); |
||||
|
|
||||
|
// 调用新接口获取用户信息 |
||||
|
const userResponse = await axios.post('http://192.168.8.94:5173/users/findUserInfoByJwcode', { |
||||
|
jwcode: trimmedJwcode |
||||
|
}); |
||||
|
|
||||
|
if (userResponse.data.length > 0) { |
||||
|
const userData = userResponse.data[0]; |
||||
|
userInfo.jwcode = userData.jwcode; |
||||
|
userInfo.username = userData.username; |
||||
|
userInfo.total_gold = userData.total_gold.toString(); |
||||
|
userInfo.region = userData.region; |
||||
|
} else { |
||||
|
ElMessage.warning('未找到该精网号对应的用户信息'); |
||||
|
Object.keys(userInfo).forEach(key => userInfo[key] = ''); |
||||
|
} |
||||
|
} else { |
||||
|
ElMessage.error('精网号不存在,请检查'); |
||||
|
goodsOptions.value = []; |
||||
|
form.productName = ''; |
||||
|
form.price = ''; |
||||
|
Object.keys(userInfo).forEach(key => userInfo[key] = ''); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error('检查精网号存在性或获取用户信息失败:', error); |
||||
|
ElMessage.error('检查精网号存在性或获取用户信息失败,请重试'); |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 样式保持不变 */ |
||||
|
.goods-consumption-page { |
||||
|
margin-left: 180px; |
||||
|
width: calc(100% - 250px); |
||||
|
min-width: auto; |
||||
|
margin-top: 2px; |
||||
|
padding: 20px; |
||||
|
background-color: transparent; |
||||
|
min-height: 80vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.main-card { |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
min-height: 890px; |
||||
|
flex: 1; |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
animation: fadeIn 0.5s ease-out; |
||||
|
} |
||||
|
|
||||
|
.inner-form-container { |
||||
|
display: flex; |
||||
|
gap: 50px; |
||||
|
align-items: flex-start; |
||||
|
} |
||||
|
|
||||
|
.embedded-info-panel { |
||||
|
background-color: white; |
||||
|
flex: 0 400px; |
||||
|
|
||||
|
|
||||
|
margin-top: 40px; |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.info-item { |
||||
|
margin-bottom: 15px; |
||||
|
} |
||||
|
|
||||
|
.info-label { |
||||
|
font-weight: bold; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
|
||||
|
@keyframes fadeIn { |
||||
|
from { opacity: 0; transform: translateY(-10px); } |
||||
|
to { opacity: 1; transform: translateY(0); } |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
.search-form { |
||||
|
width: 100%; |
||||
|
max-width: 600px; |
||||
|
padding: 40px; |
||||
|
background-color: rgba(255, 255, 255, 0.8); |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.search-form .el-input__inner, |
||||
|
.search-form .el-select .el-input__inner { |
||||
|
background-color: rgba(255, 255, 255, 0.5); |
||||
|
border: 1px solid #ccc; |
||||
|
color: #000; |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.search-form .el-input__inner:hover, |
||||
|
.search-form .el-select .el-input__inner:hover { |
||||
|
border-color: #409EFF; |
||||
|
} |
||||
|
|
||||
|
.custom-submit-btn { |
||||
|
background-color: #ff6b6b; |
||||
|
border-color: #ff6b6b; |
||||
|
color: white; |
||||
|
margin-left: 120px; |
||||
|
} |
||||
|
|
||||
|
.custom-submit-btn:hover { |
||||
|
background-color: #e55039; |
||||
|
border-color: #e55039; |
||||
|
} |
||||
|
|
||||
|
.custom-reset-btn { |
||||
|
background-color: #48dbfb; |
||||
|
border-color: #48dbfb; |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.custom-reset-btn:hover { |
||||
|
background-color: #0abde3; |
||||
|
border-color: #0abde3; |
||||
|
} |
||||
|
|
||||
|
.el-form-item { |
||||
|
margin-bottom: 40px; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,206 @@ |
|||||
|
<template> |
||||
|
<div class="goods-consumption-page"> |
||||
|
<!-- 卡片容器 --> |
||||
|
<el-card class="main-card"> |
||||
|
<template #header> |
||||
|
<div class="card-header"> |
||||
|
<h1>流水统计</h1> |
||||
|
<!-- 可添加类似导出功能的按钮,这里先保留示例 --> |
||||
|
<!-- <el-button type="success" @click="exportToExcel">导出 Excel</el-button> --> |
||||
|
</div> |
||||
|
</template> |
||||
|
<!-- 田字格布局容器 --> |
||||
|
<div class="grid-container"> |
||||
|
<div class="grid-item"> |
||||
|
<canvas ref="barChart"></canvas> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<canvas ref="pieChart"></canvas> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<canvas ref="lineChart"></canvas> |
||||
|
</div> |
||||
|
<div class="grid-item"> |
||||
|
<canvas ref="radarChart"></canvas> |
||||
|
</div> |
||||
|
</div> |
||||
|
</el-card> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts" setup> |
||||
|
import { reactive, onMounted, ref } from 'vue'; |
||||
|
import { ElMessage } from 'element-plus'; |
||||
|
// 完整引入 Chart.js |
||||
|
import Chart from 'chart.js/auto'; |
||||
|
|
||||
|
const barChart = ref(null); |
||||
|
const pieChart = ref(null); |
||||
|
// 新增折线图引用 |
||||
|
const lineChart = ref(null); |
||||
|
// 新增雷达图引用 |
||||
|
const radarChart = ref(null); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
if (barChart.value) { |
||||
|
// 初始化条形图 |
||||
|
new Chart(barChart.value, { |
||||
|
type: 'bar', |
||||
|
data: { |
||||
|
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], |
||||
|
datasets: [ |
||||
|
{ |
||||
|
label: 'Sales', |
||||
|
data: [120, 200, 150, 80, 70, 110, 130], |
||||
|
backgroundColor: 'rgba(75, 192, 192, 0.2)', |
||||
|
borderColor: 'rgba(75, 92, 192, 1)', |
||||
|
borderWidth: 1 |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
options: { |
||||
|
scales: { |
||||
|
y: { |
||||
|
beginAtZero: true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (pieChart.value) { |
||||
|
// 初始化饼图 |
||||
|
new Chart(pieChart.value, { |
||||
|
type: 'pie', |
||||
|
data: { |
||||
|
labels: ['Search Engine', 'Direct', 'Email', 'Union Ads', 'Video Ads'], |
||||
|
datasets: [ |
||||
|
{ |
||||
|
data: [1048, 735, 580, 484, 300], |
||||
|
backgroundColor: [ |
||||
|
'rgba(255, 99, 132, 0.2)', |
||||
|
'rgba(54, 162, 235, 0.2)', |
||||
|
'rgba(255, 206, 86, 0.2)', |
||||
|
'rgba(75, 192, 192, 0.2)', |
||||
|
'rgba(153, 102, 255, 0.2)' |
||||
|
], |
||||
|
borderColor: [ |
||||
|
'rgba(255, 99, 132, 1)', |
||||
|
'rgba(54, 162, 235, 1)', |
||||
|
'rgba(255, 206, 86, 1)', |
||||
|
'rgba(75, 192, 192, 1)', |
||||
|
'rgba(153, 102, 255, 1)' |
||||
|
], |
||||
|
borderWidth: 1 |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
options: { |
||||
|
responsive: true |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 初始化折线图 |
||||
|
if (lineChart.value) { |
||||
|
new Chart(lineChart.value, { |
||||
|
type: 'line', |
||||
|
data: { |
||||
|
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'], |
||||
|
datasets: [ |
||||
|
{ |
||||
|
label: 'Profit', |
||||
|
data: [150, 220, 180, 250, 200, 230, 280], |
||||
|
borderColor: 'rgba(255, 99, 132, 1)', |
||||
|
fill: false |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
options: { |
||||
|
scales: { |
||||
|
y: { |
||||
|
beginAtZero: true |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 初始化雷达图 |
||||
|
if (radarChart.value) { |
||||
|
new Chart(radarChart.value, { |
||||
|
type: 'radar', |
||||
|
data: { |
||||
|
labels: ['Q1', 'Q2', 'Q3', 'Q4'], |
||||
|
datasets: [ |
||||
|
{ |
||||
|
label: 'Performance', |
||||
|
data: [70, 80, 65, 90], |
||||
|
backgroundColor: 'rgba(54, 162, 235, 0.2)', |
||||
|
borderColor: 'rgba(54, 162, 235, 1)', |
||||
|
borderWidth: 1 |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
options: { |
||||
|
responsive: true |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 移除表单提交和重置方法 |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 商品消费页面样式 */ |
||||
|
.goods-consumption-page { |
||||
|
margin-left: 180px; |
||||
|
width: calc(100% - 250px); |
||||
|
min-width: auto; |
||||
|
margin-top: 2px; |
||||
|
padding: 20px; |
||||
|
background-color: transparent; |
||||
|
min-height: 80vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.main-card { |
||||
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); |
||||
|
min-height: 890px; |
||||
|
flex: 1; |
||||
|
background-color: rgba(255, 255, 255, 0.4); |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
color: #000; |
||||
|
} |
||||
|
|
||||
|
/* 田字格布局容器 */ |
||||
|
.grid-container { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
grid-template-rows: 1fr 1fr; |
||||
|
gap: 20px; /* 背景板之间的间距 */ |
||||
|
height: 100%; |
||||
|
padding: 20px; |
||||
|
} |
||||
|
|
||||
|
/* 背景板样式 */ |
||||
|
.grid-item { |
||||
|
background-color: rgba(255, 255, 255, 0.8); /* 背景板颜色 */ |
||||
|
border-radius: 8px; |
||||
|
padding: 20px; |
||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
/* 图表样式 */ |
||||
|
canvas { |
||||
|
width: 80% !important; |
||||
|
height: 100% !important; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,30 @@ |
|||||
|
import { fileURLToPath, URL } from 'node:url' |
||||
|
|
||||
|
import { defineConfig } from 'vite' |
||||
|
import vue from '@vitejs/plugin-vue' |
||||
|
import vueDevTools from 'vite-plugin-vue-devtools' |
||||
|
|
||||
|
// https://vite.dev/config/
|
||||
|
export default defineConfig({ |
||||
|
plugins: [ |
||||
|
vue(), |
||||
|
vueDevTools(), |
||||
|
], |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
'@': fileURLToPath(new URL('./src', import.meta.url)) |
||||
|
}, |
||||
|
}, |
||||
|
server: { |
||||
|
hmr: { |
||||
|
overlay: false |
||||
|
}, |
||||
|
proxy: { |
||||
|
'/recharges': { |
||||
|
target: 'http://192.168.8.94:5173', |
||||
|
changeOrigin: true, |
||||
|
}, |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
}) |
@ -0,0 +1,19 @@ |
|||||
|
module.exports = { |
||||
|
devServer: { |
||||
|
host: '0.0.0.0', // 允许外部访问
|
||||
|
proxy: { |
||||
|
'/recharges': { |
||||
|
target: 'http://192.168.8.94:5173', |
||||
|
changeOrigin: true, |
||||
|
pathRewrite: { '^/recharges': '' }, |
||||
|
}, |
||||
|
'/refunds': { |
||||
|
target: 'http://192.168.8.94:5173', |
||||
|
changeOrigin: true, |
||||
|
pathRewrite: { |
||||
|
'^/refunds': '/refunds' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |