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