Compare commits

...

No commits in common. 'master' and 'zry_code' have entirely different histories.

  1. 30
      code/vue-base1/.gitignore
  2. 29
      code/vue-base1/README.md
  3. 13
      code/vue-base1/index.html
  4. 8
      code/vue-base1/jsconfig.json
  5. 3323
      code/vue-base1/package-lock.json
  6. 31
      code/vue-base1/package.json
  7. BIN
      code/vue-base1/public/favicon.ico
  8. 35
      code/vue-base1/src/App.vue
  9. 86
      code/vue-base1/src/assets/base.css
  10. 1
      code/vue-base1/src/assets/logo.svg
  11. 35
      code/vue-base1/src/assets/main.css
  12. 299
      code/vue-base1/src/components/Sidebar.vue
  13. 100
      code/vue-base1/src/cs.vue
  14. BIN
      code/vue-base1/src/images/cat.gif
  15. BIN
      code/vue-base1/src/images/dx.gif
  16. BIN
      code/vue-base1/src/images/star.gif
  17. BIN
      code/vue-base1/src/images/tech.gif
  18. BIN
      code/vue-base1/src/images/tech2.gif
  19. BIN
      code/vue-base1/src/images/water.jpg
  20. 12
      code/vue-base1/src/main.js
  21. 76
      code/vue-base1/src/router/index.js
  22. 22
      code/vue-base1/src/views/HomeView.vue
  23. 241
      code/vue-base1/src/views/Recharge.vue
  24. 320
      code/vue-base1/src/views/RechargeDetails.vue
  25. 237
      code/vue-base1/src/views/Refund.vue
  26. 358
      code/vue-base1/src/views/UserDetails.vue
  27. 331
      code/vue-base1/src/views/audit/RechargeAudit.vue
  28. 321
      code/vue-base1/src/views/audit/RefundAudit.vue
  29. 379
      code/vue-base1/src/views/consumption/GoodsConsumption.vue
  30. 206
      code/vue-base1/src/views/consumption/TransactionStatistics.vue
  31. 30
      code/vue-base1/vite.config.js
  32. 19
      code/vue-base1/vue.config.js
  33. 0
      m.txt

30
code/vue-base1/.gitignore

@ -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

29
code/vue-base1/README.md

@ -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
```

13
code/vue-base1/index.html

@ -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>

8
code/vue-base1/jsconfig.json

@ -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

31
code/vue-base1/package.json

@ -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"
}
}

BIN
code/vue-base1/public/favicon.ico

35
code/vue-base1/src/App.vue

@ -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>

86
code/vue-base1/src/assets/base.css

@ -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;
}

1
code/vue-base1/src/assets/logo.svg

@ -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>

35
code/vue-base1/src/assets/main.css

@ -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;
}
}

299
code/vue-base1/src/components/Sidebar.vue

@ -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>

100
code/vue-base1/src/cs.vue

@ -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>

BIN
code/vue-base1/src/images/cat.gif

After

Width: 680  |  Height: 400  |  Size: 1.2 MiB

BIN
code/vue-base1/src/images/dx.gif

After

Width: 1024  |  Height: 680  |  Size: 2.4 MiB

BIN
code/vue-base1/src/images/star.gif

After

Width: 500  |  Height: 240  |  Size: 847 KiB

BIN
code/vue-base1/src/images/tech.gif

After

Width: 334  |  Height: 188  |  Size: 1.7 MiB

BIN
code/vue-base1/src/images/tech2.gif

After

Width: 533  |  Height: 300  |  Size: 3.7 MiB

BIN
code/vue-base1/src/images/water.jpg

After

Width: 800  |  Height: 500  |  Size: 53 KiB

12
code/vue-base1/src/main.js

@ -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')

76
code/vue-base1/src/router/index.js

@ -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;

22
code/vue-base1/src/views/HomeView.vue

@ -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>

241
code/vue-base1/src/views/Recharge.vue

@ -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>

320
code/vue-base1/src/views/RechargeDetails.vue

@ -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>

237
code/vue-base1/src/views/Refund.vue

@ -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>

358
code/vue-base1/src/views/UserDetails.vue

@ -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>

331
code/vue-base1/src/views/audit/RechargeAudit.vue

@ -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>

321
code/vue-base1/src/views/audit/RefundAudit.vue

@ -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>

379
code/vue-base1/src/views/consumption/GoodsConsumption.vue

@ -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>

206
code/vue-base1/src/views/consumption/TransactionStatistics.vue

@ -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>

30
code/vue-base1/vite.config.js

@ -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,
},
}
},
})

19
code/vue-base1/vue.config.js

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

0
m.txt

Loading…
Cancel
Save