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' |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |