release(custom): i
4
.env
@@ -2,8 +2,4 @@ VITE_TITLE = '捷兑通 - 平台端'
|
||||
|
||||
VITE_PORT = 4000
|
||||
|
||||
VITE_WS_URL = 'www.wanzhuanyongcheng.cn/admin/data'
|
||||
|
||||
VITE_WS1_URL = 'www.jdt168.com/dice/home'
|
||||
|
||||
VITE_GAME_API = 'https://www.jdt168.com'
|
||||
@@ -9,3 +9,9 @@ VITE_USE_PROXY = true
|
||||
|
||||
# base api
|
||||
VITE_BASE_API = '/admin'
|
||||
|
||||
|
||||
VITE_WS1_URL = 'game.wanzhuanyongcheng.cn/dice/home'
|
||||
VITE_WS_URL = 'test.wanzhuanyongcheng.cn/admin/data'
|
||||
|
||||
VITE_MER_LOGIN_URL = '//localhost:3100/#/login'
|
||||
@@ -13,4 +13,11 @@ VITE_BASE_API = 'https://www.wanzhuanyongcheng.cn/admin'
|
||||
VITE_USE_COMPRESS = true
|
||||
|
||||
# 压缩类型
|
||||
VITE_COMPRESS_TYPE = gzip
|
||||
VITE_COMPRESS_TYPE = gzip
|
||||
|
||||
VITE_WS1_URL = 'www.jdt168.com/dice/home'
|
||||
|
||||
VITE_WS_URL = 'www.wanzhuanyongcheng.cn/admin/data'
|
||||
|
||||
|
||||
VITE_MER_LOGIN_URL = '//www.wanzhuanyongcheng.cn/static/mer/#/login'
|
||||
17
.env.test
@@ -1,7 +1,18 @@
|
||||
VITE_PUBLIC_PATH = '/'
|
||||
# 资源公共路径,需要以 /开头和结尾
|
||||
VITE_PUBLIC_PATH = '/static/admin'
|
||||
|
||||
# 是否启用MOCK
|
||||
VITE_USE_MOCK = true
|
||||
VITE_USE_MOCK = false
|
||||
|
||||
# 是否启用代理
|
||||
VITE_USE_PROXY = false
|
||||
|
||||
# base api
|
||||
VITE_BASE_API = '/api'
|
||||
VITE_BASE_API = '/admin'
|
||||
|
||||
|
||||
VITE_WS1_URL = 'game.wanzhuanyongcheng.cn/dice/home'
|
||||
VITE_WS_URL = 'test.wanzhuanyongcheng.cn/admin/data'
|
||||
VITE_GAME_API = 'https://game.wanzhuanyongcheng.cn'
|
||||
|
||||
VITE_MER_LOGIN_URL = '//test.wanzhuanyongcheng.cn/static/mer/#/login'
|
||||
5
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
12
.idea/admin.iml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
65
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,65 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="HTML_QUOTE_STYLE" value="Single" />
|
||||
<option name="HTML_ENFORCE_QUOTES" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="100" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
||||
6
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
||||
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/admin.iml" filepath="$PROJECT_DIR$/.idea/admin.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
217
README.md
@@ -1,217 +0,0 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/zclzone/vue-naive-admin">
|
||||
<img alt="Vue Naive Admin Logo" width="200" src="./src/assets/images/logo.png">
|
||||
</a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/zclzone/vue-naive-admin"><img alt="stars" src="https://badgen.net/github/stars/zclzone/vue-naive-admin"/></a>
|
||||
<a href="https://github.com/zclzone/vue-naive-admin"><img alt="forks" src="https://badgen.net/github/forks/zclzone/vue-naive-admin"/></a>
|
||||
<a href="./LICENSE"><img alt="MIT License" src="https://badgen.net/github/license/zclzone/vue-naive-admin"/></a>
|
||||
</p>
|
||||
|
||||
<p align='center'>
|
||||
<b>中文</b> |
|
||||
<a href="https://github.com/zclzone/vue-naive-admin/blob/main/README.EN.md">English</a>
|
||||
</p>
|
||||
|
||||
### 简介
|
||||
|
||||
[Vue Naive Admin](https://github.com/zclzone/vue-naive-admin) 是一个 **完全开源免费且允许商用** 的后台管理模板,基于 `Vue3、Vite3、Pinia、Unocss 和 Naive UI` 等前端最新技术栈。相较于其他比较流行的后台管理模板,此项目更加简洁、轻量,风格清新,学习成本非常低,非常适合中小型项目或者个人项目。
|
||||
|
||||
### 功能
|
||||
|
||||
- 🍒 集成 [Naive UI](https://www.naiveui.com)
|
||||
- 🍑 集成登陆、注销及权限验证
|
||||
- 🍐 集成多环境配置,dev、测试、生产和github pages环境
|
||||
- 🍎 集成 `eslint + prettier`,代码约束和格式化统一
|
||||
- 🍌 集成 `husky + commitlint`,代码提交规范化
|
||||
- 🍉 集成 `mock` 接口服务,dev 环境和发布环境都支持,可动态配置是否启用 mock 服务,不启用时不会加载 mock 包,减少打包体积
|
||||
- 🍍 集成 `pinia`,vuex 的替代方案,轻量、简单、易用
|
||||
- 📦 集成 `unplugin` 插件,自动导入,解放双手,开发效率直接起飞
|
||||
- 🤹 集成 `iconify` 图标,支持自定义 svg 图标, 优雅使用icon
|
||||
- 🍇 集成 `unocss`,antfu 开源的原子 css 解决方案,非常轻量
|
||||
|
||||
### 预览
|
||||
|
||||
[https://template.qszone.com](https://template.qszone.com)
|
||||
|
||||
[https://base.isme.top](https://base.isme.top)
|
||||
|
||||
### 文档
|
||||
|
||||
[Vue Naive Admin Docs](https://zclzone.github.io/vue-naive-admin-docs)
|
||||
|
||||
[语雀文档:Vue Naive Admin](https://www.yuque.com/qszone/vue-naive-admin)
|
||||
|
||||
### 快速开始
|
||||
|
||||
```shell
|
||||
# 推荐配置git autocrlf 为 false(本项目规范使用lf换行符,此配置是为防止git自动将源文件转换为crlf)
|
||||
# 不清楚为什么要这样做的请参考这篇文章:https://www.freesion.com/article/4532642129
|
||||
git config --global core.autocrlf false
|
||||
|
||||
# 克隆项目
|
||||
git clone https://github.com/zclzone/vue-naive-admin.git
|
||||
|
||||
# 进入项目目录
|
||||
cd vue-naive-admin
|
||||
|
||||
# 安装依赖(建议使用pnpm: https://pnpm.io/zh/installation)
|
||||
npm i -g pnpm # 装了可忽略
|
||||
pnpm i # 或者 npm i
|
||||
|
||||
# 启动
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### 构建发布
|
||||
|
||||
```shell
|
||||
# 构建测试环境
|
||||
pnpm build:test
|
||||
|
||||
# 构建github pages环境
|
||||
pnpm build:github
|
||||
|
||||
# 构建生产环境
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### 其他指令
|
||||
|
||||
```shell
|
||||
# eslint代码格式检查
|
||||
pnpm lint
|
||||
|
||||
# 代码检查并修复
|
||||
pnpm lint:fix
|
||||
|
||||
# 预览发布包效果(需先执行构建指令)
|
||||
pnpm preview
|
||||
|
||||
# 提交代码(husky+commitlint)
|
||||
pnpm cz
|
||||
```
|
||||
|
||||
|
||||
### 目录说明
|
||||
|
||||
```
|
||||
Vue Naive Admin
|
||||
|-- .github // github相关,如推送github仓库后自动部署gh pages
|
||||
|-- .husky // git commit钩子
|
||||
|-- .vscode // vscode编辑器相关
|
||||
| |-- extensions.json // 插件推荐
|
||||
| |-- settings.json // 项目级别的vscode配置,优先级大于全局vscode配置
|
||||
|-- build // 构建相关配置
|
||||
| |-- constant.js // 构建相关的常量
|
||||
| |-- utils.js // 构建相关的工具方法
|
||||
| |-- config
|
||||
| | |-- define.js // 注入全局常量,启动或打包后将添加到window中
|
||||
| | |-- proxy.js // 代理配置
|
||||
| |-- plugin
|
||||
| | |-- html.js // vite-plugin-html插件,用于注入变量或者html标签
|
||||
| | |-- mock.js // vite-plugin-mock插件,处理mock
|
||||
| | |-- unplugin.js // unplugin相关插件,包含DefineOptions和自动导入
|
||||
| |-- script // 打包完成后执行的一些node脚本(不重要)
|
||||
| |-- build-cname.js // 自动生成cname
|
||||
|-- mock // mock
|
||||
| |-- utils.js // mock请求需要用到的工具方法
|
||||
| |-- api // mock接口
|
||||
|-- public // 公共资源,文件夹下的文件会在打包后会直接加到dist根目录下
|
||||
|-- settings // 项目配置
|
||||
| |-- proxy-config.js // 代理配置文件
|
||||
| |-- theme.json // 主题配置项,主题色等
|
||||
|-- src
|
||||
| |-- api // 公共api
|
||||
| |-- assets // 静态资源
|
||||
| | |-- images // 图片
|
||||
| | |-- svg // svg图标
|
||||
| |-- components // 全局组件
|
||||
| | |-- common // 公共组件
|
||||
| | |-- icon // icon相关组件
|
||||
| | |-- page // 页面组件
|
||||
| | |-- query-bar // 查询选项
|
||||
| | |-- table // 封装的表格组件
|
||||
| |-- composables // 封装的组合式函数
|
||||
| |-- layout // 布局相关组件
|
||||
| | |-- components
|
||||
| | |-- AppMain.vue // 主体内容
|
||||
| | |-- header // 头部
|
||||
| | |-- sidebar // 侧边菜单栏
|
||||
| | |-- tags // 多页签栏
|
||||
| |-- router // 路由
|
||||
| | |-- guard // 路由守卫
|
||||
| | |-- routes // 路由列表
|
||||
| |-- store // 状态管理(pinia)
|
||||
| | |-- modules // 模块
|
||||
| | |-- app // 管理页面重新加载、折叠菜单栏和keepAlive等
|
||||
| | |-- permission // 权限相关,管理权限菜单
|
||||
| | |-- tags // 管理多页签
|
||||
| | |-- user // 用户模块,管理用户信息、登录登出
|
||||
| |-- styles // 样式
|
||||
| |-- utils // 封装的工具方法
|
||||
| | |-- auth // 权限相关,如token、跳转登录页等
|
||||
| | |-- common // 通用
|
||||
| | |-- http // 封装axios
|
||||
| | |-- storage // 封装localStorage和sessionStorage
|
||||
| |-- views // 页面
|
||||
| | |-- demo // 示例
|
||||
| | |-- error-page // 错误页
|
||||
| | |-- login // 登录页
|
||||
| | |-- workbench // 首页
|
||||
| |-- App.vue
|
||||
| |-- main.js
|
||||
|-- .cz-config.js // git提交配置
|
||||
|-- .editorconfig // 编辑器配置
|
||||
|-- .env // 环境文件,所有环境都会载入
|
||||
|-- .env.development // 开发环境文件
|
||||
|-- .env.production // 生产环境文件
|
||||
|-- .env.test // 测试环境文件
|
||||
|-- .eslintignore // eslint忽略
|
||||
|-- .eslintrc.js // eslint配置
|
||||
|-- .gitignore // git忽略
|
||||
|-- .prettierignore // prettier格式化忽略
|
||||
|-- commitlint.config.js // commitlint规范配置
|
||||
|-- index.html
|
||||
|-- jsconfig.json // js配置
|
||||
|-- LICENSE // 协议
|
||||
|-- package.json // 依赖描述文件
|
||||
|-- pnpm-lock.yaml // 依赖锁定文件
|
||||
|-- prettier.config.js // prettier格式化配置
|
||||
|-- README.md // 项目描述文档(英文)
|
||||
|-- README.zh-CN.md // 项目描述文档(中文)
|
||||
|-- unocss.config.js // unocss配置
|
||||
|-- vite.config.js // vite配置
|
||||
```
|
||||
|
||||
### TS 版本: Qs Admin
|
||||
|
||||
#### 源码
|
||||
|
||||
- github: [https://github.com/zclzone/qs-admin](https://github.com/zclzone/qs-admin)
|
||||
- gitee: [https://gitee.com/zclzone/qs-admin-ts](https://gitee.com/zclzone/qs-admin-ts)
|
||||
|
||||
#### 预览
|
||||
|
||||
- [https://admin.qszone.com](https://admin.qszone.com)
|
||||
- [https://zclzone.github.io/qs-admin](https://zclzone.github.io/qs-admin)
|
||||
|
||||
### 使用该项目的开源项目
|
||||
|
||||
- [gin-vue-blog](https://github.com/szluyu99/gin-vue-blog): Golang 全栈博客项目, 博客后台的前端基于 vue-naive-admin,对接真实后端服务,实现了后端控制路由等特性。
|
||||
|
||||
|
||||
### 入群交流 & 关于作者
|
||||
|
||||
<a href="https://blog.qszone.com/about/">
|
||||
<img src="https://assets.qszone.com/images/about.png" style="max-width: 400px" />
|
||||
</a>
|
||||
|
||||
### ☕ 赞助我
|
||||
|
||||
> 开源不易,请作者喝杯咖啡吧
|
||||
<p>
|
||||
<img src="https://assets.qszone.com/images/zhifu_weixin.jpg" style="width: 220px" />
|
||||
<img src="https://assets.qszone.com/images/zhifu_zhifubao.jpg" style="width: 220px" />
|
||||
</p>
|
||||
@@ -7,7 +7,7 @@ export const PROXY_CONFIG = {
|
||||
* @转发路径 http://localhost:8080/user
|
||||
*/
|
||||
'/admin': {
|
||||
target: 'https://www.wanzhuanyongcheng.cn',
|
||||
target: 'https://test.wanzhuanyongcheng.cn',
|
||||
changeOrigin: true,
|
||||
// rewrite: (path) => path.replace(new RegExp('^/api'), ''),
|
||||
},
|
||||
|
||||
36
package.json
@@ -31,28 +31,30 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@unocss/eslint-config": "^0.55.0",
|
||||
"@vueuse/core": "^10.3.0",
|
||||
"@unocss/eslint-config": "^0.55.7",
|
||||
"@vueuse/core": "^10.5.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.4.0",
|
||||
"dayjs": "^1.11.9",
|
||||
"axios": "^1.5.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.4.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"md-editor-v3": "^4.2.2",
|
||||
"md-editor-v3": "^4.7.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.6",
|
||||
"vite": "^4.4.9",
|
||||
"vite": "^4.4.11",
|
||||
"vue": "3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
"vue-echarts": "^6.6.1",
|
||||
"vue-router": "^4.2.5",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.7.1",
|
||||
"@commitlint/cli": "^17.7.2",
|
||||
"@commitlint/config-conventional": "^17.7.0",
|
||||
"@iconify/json": "^2.2.100",
|
||||
"@iconify/json": "^2.2.126",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@unocss/preset-rem-to-px": "^0.55.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@unocss/preset-rem-to-px": "^0.55.7",
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@zclzone/eslint-config": "^0.0.4",
|
||||
"chalk": "^5.3.0",
|
||||
@@ -63,17 +65,17 @@
|
||||
"esno": "^0.17.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^13.2.3",
|
||||
"naive-ui": "^2.34.4",
|
||||
"lint-staged": "^13.3.0",
|
||||
"naive-ui": "^2.35.0",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.65.1",
|
||||
"sass": "^1.69.0",
|
||||
"unocss": "0.55.0",
|
||||
"unplugin-auto-import": "^0.16.6",
|
||||
"unplugin-icons": "^0.16.5",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"unplugin-icons": "^0.16.6",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-html": "^3.2.0",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-mock": "^2.9.8",
|
||||
"vite-plugin-svg-icons": "^2.0.1"
|
||||
}
|
||||
}
|
||||
|
||||
9555
pnpm-lock.yaml
generated
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 20 KiB |
2233
public/favicon.svg
|
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 20 KiB |
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"naiveThemeOverrides": {
|
||||
"common": {
|
||||
"primaryColor": "#409EFFE3",
|
||||
"primaryColor": "#316C72FF",
|
||||
"primaryColorHover": "#316C72E3",
|
||||
"primaryColorPressed": "#2B4C59FF",
|
||||
"primaryColorSuppl": "#316C72E3",
|
||||
|
||||
|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 20 KiB |
51
src/components/Echarts.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<template>
|
||||
<v-chart :loading="loading" class="chart" :option="option" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { use } from 'echarts/core'
|
||||
import { BarChart, PieChart } from 'echarts/charts'
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent,
|
||||
DataZoomComponent,
|
||||
} from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
|
||||
use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent,
|
||||
GridComponent,
|
||||
BarChart,
|
||||
CanvasRenderer,
|
||||
PieChart,
|
||||
DataZoomComponent,
|
||||
])
|
||||
|
||||
import VChart, { THEME_KEY } from 'vue-echarts'
|
||||
|
||||
import { provide } from 'vue'
|
||||
|
||||
provide(THEME_KEY, 'white')
|
||||
|
||||
defineProps({
|
||||
option: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<KeepAlive :include="keepAliveNames">
|
||||
<component :is="Component" v-if="!tagStore.reloading" :key="route.fullPath" />
|
||||
<div h-full w-full>
|
||||
<component :is="Component" v-if="!tagStore.reloading" :key="route.fullPath" />
|
||||
</div>
|
||||
</KeepAlive>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
@@ -89,11 +89,11 @@ function getMenuItem(route, basePath = '') {
|
||||
|
||||
function getIcon(meta) {
|
||||
if (meta?.customIcon) return renderCustomIcon(meta.customIcon, { size: 18 })
|
||||
if (meta?.icon) return renderIcon(meta.icon, { size: 18 })
|
||||
if (meta?.icon !== '无' && meta?.icon) return renderIcon(meta.icon, { size: 18 })
|
||||
return null
|
||||
}
|
||||
|
||||
function handleMenuSelect(key, item) {
|
||||
function handleMenuSelect(_, item) {
|
||||
if (isExternal(item.path)) {
|
||||
window.open(item.path)
|
||||
} else {
|
||||
|
||||
21
src/main.js
@@ -18,6 +18,27 @@ async function setupApp() {
|
||||
|
||||
await setupRouter(app)
|
||||
|
||||
app.directive('perms', {
|
||||
mounted: (el, binding) => {
|
||||
const { value } = binding
|
||||
const permissions = JSON.parse(localStorage.getItem('roles'))
|
||||
const all_permission = '*'
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length > 0) {
|
||||
const hasPermission = permissions.some((key) => {
|
||||
return all_permission == key || value.includes(key)
|
||||
})
|
||||
|
||||
if (!hasPermission) {
|
||||
el.parentNode && el.parentNode.removeChild(el)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error('like v-perms="[\'auth/menu/edit\']"')
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
app.mount('#app')
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router
|
||||
import { setupRouterGuard } from './guard'
|
||||
import { basicRoutes, EMPTY_ROUTE, NOT_FOUND_ROUTE } from './routes'
|
||||
import { getToken, isNullOrWhitespace } from '@/utils'
|
||||
import { useUserStore, usePermissionStore } from '@/store'
|
||||
import { usePermissionStore } from '@/store'
|
||||
|
||||
const isHash = true
|
||||
export const router = createRouter({
|
||||
@@ -17,16 +17,6 @@ export async function setupRouter(app) {
|
||||
app.use(router)
|
||||
}
|
||||
|
||||
export async function resetRouter() {
|
||||
const basicRouteNames = getRouteNames(basicRoutes)
|
||||
router.getRoutes().forEach((route) => {
|
||||
const name = route.name
|
||||
if (!basicRouteNames.includes(name)) {
|
||||
router.removeRoute(name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function addDynamicRoutes() {
|
||||
const token = getToken()
|
||||
|
||||
@@ -38,10 +28,8 @@ export async function addDynamicRoutes() {
|
||||
|
||||
// 有token的情况
|
||||
try {
|
||||
const userStore = useUserStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
!userStore.userId && (await userStore.getUserInfo())
|
||||
const accessRoutes = permissionStore.generateRoutes(userStore.role)
|
||||
const accessRoutes = permissionStore.generateRoutes()
|
||||
accessRoutes.forEach((route) => {
|
||||
!router.hasRoute(route.name) && router.addRoute(route)
|
||||
})
|
||||
@@ -49,6 +37,7 @@ export async function addDynamicRoutes() {
|
||||
router.addRoute(NOT_FOUND_ROUTE)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +47,8 @@ export function getRouteNames(routes) {
|
||||
|
||||
function getRouteName(route) {
|
||||
const names = [route.name]
|
||||
if (route.children && route.children.length) {
|
||||
names.push(...route.children.map((item) => getRouteName(item)).flat(1))
|
||||
if (route.subMenu && route.subMenu.length) {
|
||||
names.push(...route.subMenu.map((item) => getRouteName(item)).flat(1))
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export const basicRoutes = [
|
||||
{
|
||||
name: '404',
|
||||
@@ -17,43 +15,6 @@ export const basicRoutes = [
|
||||
title: '登录页',
|
||||
},
|
||||
},
|
||||
|
||||
// {
|
||||
// name: 'ExternalLink',
|
||||
// path: '/external-link',
|
||||
// component: Layout,
|
||||
// meta: {
|
||||
// title: '外部链接',
|
||||
// icon: 'mdi:link-variant',
|
||||
// order: 4,
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// name: 'LinkGithubSrc',
|
||||
// path: 'https://github.com/zclzone/vue-naive-admin',
|
||||
// meta: {
|
||||
// title: '源码 - github',
|
||||
// icon: 'mdi:github',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'LinkGiteeSrc',
|
||||
// path: 'https://gitee.com/zclzone/vue-naive-admin',
|
||||
// meta: {
|
||||
// title: '源码 - gitee',
|
||||
// icon: 'simple-icons:gitee',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// name: 'LinkDocs',
|
||||
// path: 'https://zclzone.github.io/vue-naive-admin-docs',
|
||||
// meta: {
|
||||
// title: '文档 - vuepress',
|
||||
// icon: 'mdi:vuejs',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
]
|
||||
|
||||
export const NOT_FOUND_ROUTE = {
|
||||
@@ -68,11 +29,3 @@ export const EMPTY_ROUTE = {
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: null,
|
||||
}
|
||||
|
||||
const modules = import.meta.glob('@/views/**/route.js', { eager: true })
|
||||
const asyncRoutes = []
|
||||
Object.keys(modules).forEach((key) => {
|
||||
asyncRoutes.push(modules[key].default)
|
||||
})
|
||||
|
||||
export { asyncRoutes }
|
||||
|
||||
@@ -1,33 +1,105 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { asyncRoutes, basicRoutes } from '@/router/routes'
|
||||
import { basicRoutes } from '@/router/routes'
|
||||
import { RouterView } from 'vue-router'
|
||||
|
||||
function hasPermission(route, role) {
|
||||
// * 不需要权限直接返回true
|
||||
if (!route.meta?.requireAuth) return true
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
const routeRole = route.meta?.role ? route.meta.role : []
|
||||
// 匹配views里面所有的.vue文件,动态引入
|
||||
const modules = import.meta.glob('/src/views/**/*.vue')
|
||||
|
||||
// * 登录用户没有角色或者路由没有设置角色判定为没有权限
|
||||
if (!role.length || !routeRole.length) return false
|
||||
|
||||
// * 路由指定的角色包含任一登录用户角色则判定有权限
|
||||
return role.some((item) => routeRole.includes(item))
|
||||
//
|
||||
export function getModulesKey() {
|
||||
return Object.keys(modules).map((item) => item.replace('/src/views/', '').replace('.vue', ''))
|
||||
}
|
||||
|
||||
function filterAsyncRoutes(routes = [], role) {
|
||||
// 动态加载组件
|
||||
export function loadRouteView(component) {
|
||||
try {
|
||||
const key = Object.keys(modules).find((key) => {
|
||||
return key.includes(`${component}.vue`)
|
||||
})
|
||||
if (key) {
|
||||
return modules[key]
|
||||
}
|
||||
throw Error(`找不到组件${component},请确保组件路径正确`)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
return RouterView
|
||||
}
|
||||
}
|
||||
|
||||
// function hasPermission(route, role) {
|
||||
// // * 不需要权限直接返回true
|
||||
// if (!route.meta?.requireAuth) return true
|
||||
|
||||
// const routeRole = route.meta?.role ? route.meta.role : []
|
||||
|
||||
// // * 登录用户没有角色或者路由没有设置角色判定为没有权限
|
||||
// if (!role.length || !routeRole.length) return false
|
||||
|
||||
// // * 路由指定的角色包含任一登录用户角色则判定有权限
|
||||
// return role.some((item) => routeRole.includes(item))
|
||||
// }
|
||||
|
||||
// 过滤异步路由
|
||||
function filterAsyncRoutes(routes = [], firstRoute = true) {
|
||||
const ret = []
|
||||
routes.forEach((route) => {
|
||||
if (hasPermission(route, role)) {
|
||||
const curRoute = {
|
||||
...route,
|
||||
children: [],
|
||||
}
|
||||
if (route.children && route.children.length) {
|
||||
curRoute.children = filterAsyncRoutes(route.children, role)
|
||||
} else {
|
||||
Reflect.deleteProperty(curRoute, 'children')
|
||||
}
|
||||
ret.push(curRoute)
|
||||
// 过滤掉type为3的路由
|
||||
if (route.type === 3) return
|
||||
const isHidden = route.is_show === 1 ? false : true
|
||||
|
||||
const meta = {
|
||||
requireAuth: true,
|
||||
title: route.name,
|
||||
icon: route.icon,
|
||||
order: route.sort,
|
||||
}
|
||||
|
||||
let redirect = ''
|
||||
|
||||
if (route.route === '/' && firstRoute) {
|
||||
// 重定向
|
||||
redirect = route.subMenu[0].route
|
||||
}
|
||||
|
||||
const curRoute = {
|
||||
path: route.route,
|
||||
name: route.name,
|
||||
isHidden,
|
||||
meta,
|
||||
redirect,
|
||||
children: [],
|
||||
}
|
||||
if (route.subMenu && route.subMenu.length) {
|
||||
curRoute.children = filterAsyncRoutes(route.subMenu, false)
|
||||
} else {
|
||||
Reflect.deleteProperty(curRoute, 'children')
|
||||
}
|
||||
|
||||
switch (route.type) {
|
||||
case 1:
|
||||
curRoute.component = firstRoute ? Layout : RouterView
|
||||
break
|
||||
case 2:
|
||||
curRoute.component = loadRouteView(route.components)
|
||||
break
|
||||
}
|
||||
|
||||
ret.push(curRoute)
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
// 递归寻找type为3的路由
|
||||
function findType3Routes(routes = []) {
|
||||
const ret = []
|
||||
routes.forEach((route) => {
|
||||
if (route.type === 3) {
|
||||
ret.push(route.api_route)
|
||||
}
|
||||
if (route.subMenu && route.subMenu.length) {
|
||||
ret.push(...findType3Routes(route.subMenu))
|
||||
}
|
||||
})
|
||||
return ret
|
||||
@@ -48,8 +120,10 @@ export const usePermissionStore = defineStore('permission', {
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
generateRoutes(role = []) {
|
||||
const accessRoutes = filterAsyncRoutes(asyncRoutes, role)
|
||||
generateRoutes() {
|
||||
const menus = JSON.parse(localStorage.getItem('menu'))
|
||||
const accessRoutes = filterAsyncRoutes(menus)
|
||||
window.localStorage.setItem('roles', JSON.stringify(findType3Routes(menus)))
|
||||
this.accessRoutes = accessRoutes
|
||||
return accessRoutes
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { resetRouter } from '@/router'
|
||||
// import { resetRouter } from '@/router'
|
||||
import { useTagsStore, usePermissionStore } from '@/store'
|
||||
import { removeToken, toLogin } from '@/utils'
|
||||
// import api from '@/api'
|
||||
@@ -18,7 +18,10 @@ export const useUserStore = defineStore('user', {
|
||||
return this.userInfo?.name
|
||||
},
|
||||
avatar() {
|
||||
return this.userInfo?.avatar
|
||||
return (
|
||||
this.userInfo?.avatar ||
|
||||
'https://pic3.58cdn.com.cn/nowater/webim/big/n_v21bc7874294754e63a22b80febac9cf51.jpg'
|
||||
)
|
||||
},
|
||||
role() {
|
||||
return this.userInfo?.role || []
|
||||
@@ -41,7 +44,7 @@ export const useUserStore = defineStore('user', {
|
||||
removeToken()
|
||||
resetTags()
|
||||
resetPermission()
|
||||
resetRouter()
|
||||
// resetRouter()
|
||||
this.$reset()
|
||||
toLogin()
|
||||
},
|
||||
|
||||
@@ -24,6 +24,9 @@ export function resolveResError(code, message) {
|
||||
case 500:
|
||||
message = message ?? '服务器异常'
|
||||
break
|
||||
case 402:
|
||||
message = message ?? '无权限访问'
|
||||
break
|
||||
default:
|
||||
message = message ?? `【${code}】: 未知异常!`
|
||||
break
|
||||
|
||||
@@ -14,4 +14,6 @@ export function createAxios(options = {}) {
|
||||
return service
|
||||
}
|
||||
|
||||
export const request = createAxios({})
|
||||
export const request = createAxios({
|
||||
baseURL: import.meta.env.VITE_BASE_API,
|
||||
})
|
||||
|
||||
@@ -2,11 +2,11 @@ import { getToken } from '@/utils'
|
||||
import { resolveResError } from './helpers'
|
||||
|
||||
export function reqResolve(config) {
|
||||
if (config.url.includes('/dice')) {
|
||||
config.baseURL = import.meta.env.VITE_GAME_API
|
||||
} else {
|
||||
config.baseURL = import.meta.env.VITE_BASE_API
|
||||
}
|
||||
// if (config.url.includes('/dice')) {
|
||||
// config.baseURL = import.meta.env.VITE_GAME_API
|
||||
// } else {
|
||||
// config.baseURL = import.meta.env.VITE_BASE_API
|
||||
// }
|
||||
// 处理不需要token的请求
|
||||
if (config.noNeedToken) {
|
||||
return config
|
||||
@@ -37,7 +37,7 @@ export function resResolve(response) {
|
||||
const code = data?.code ?? status
|
||||
|
||||
/** 根据code处理对应的操作,并返回处理后的message */
|
||||
const message = resolveResError(code, data?.message ?? statusText)
|
||||
const message = resolveResError(code, data?.msg ?? statusText)
|
||||
|
||||
/** 需要错误提醒 */
|
||||
!config.noNeedTip && window.$message?.error(message)
|
||||
|
||||
6
src/views/business/mer_class/api.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getList: (data) => request.post('/store/classify', data),
|
||||
addClass: (data) => request.post('/store/classify/edit', data),
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button type="primary" @click="handleAdd(1)">新增商品分类</n-button>
|
||||
<n-button v-perms="['/admin/store/classify/edit']" type="primary" @click="handleAdd(1)">
|
||||
新增商户分类
|
||||
</n-button>
|
||||
<!-- {{ formValue }} -->
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
@@ -8,6 +10,7 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
@@ -45,9 +48,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, h } from 'vue'
|
||||
import { onMounted, h, withDirectives, resolveDirective } from 'vue'
|
||||
import api from './api'
|
||||
import { NButton } from 'naive-ui'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -76,17 +80,20 @@ const columns = ref([
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
},
|
||||
},
|
||||
},
|
||||
() => '编辑'
|
||||
() => '编辑'
|
||||
),
|
||||
[[vPerms, ['/admin/store/classify/edit']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
@@ -105,6 +112,7 @@ const rules = {
|
||||
}
|
||||
|
||||
const formValue = ref({
|
||||
ID: 0,
|
||||
name: '',
|
||||
status: 1,
|
||||
})
|
||||
@@ -112,9 +120,9 @@ const formValue = ref({
|
||||
const showModal = ref(false)
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -133,12 +141,12 @@ onMounted(() => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getMerClass({
|
||||
pageNum: pagination.value.current,
|
||||
const res = await api.getList({
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
@@ -148,12 +156,13 @@ const getList = async () => {
|
||||
const modelTitle = ref('')
|
||||
|
||||
const handleAdd = (e) => {
|
||||
modelTitle.value = e === 1 ? '新增商品分类' : '编辑商品分类'
|
||||
modelTitle.value = e === 1 ? '新增商户分类' : '编辑商户分类'
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
formValue.value = {
|
||||
ID: 0,
|
||||
name: '',
|
||||
status: 1,
|
||||
}
|
||||
@@ -165,7 +174,7 @@ const handleValidateClick = async (e) => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
await api.addMerClass(formValue.value)
|
||||
await api.addClass(formValue.value)
|
||||
$message.success('成功')
|
||||
clear()
|
||||
getList()
|
||||
@@ -4,4 +4,6 @@ export default {
|
||||
getList: (data) => request.post('/store', data),
|
||||
addMer: (data) => request.post('/store/edit', data),
|
||||
getMerType: () => request.post('/store/getOther'),
|
||||
// 一键登录
|
||||
login: (data) => request.post('/store/easy/login', data),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<!-- {{ formValue }} -->
|
||||
<n-button type="primary" @click="handleAdd(1)">新增商户</n-button>
|
||||
<n-button v-perms="['/store/edit']" type="primary" @click="handleAdd(1)">新增商户</n-button>
|
||||
<n-grid class="mb-10" x-gap="12" cols="6" collapsed>
|
||||
<n-gi>
|
||||
<div class="flex items-center">
|
||||
@@ -41,6 +41,7 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
|
||||
<n-drawer v-model:show="showModal" :width="502" placement="right">
|
||||
@@ -54,23 +55,26 @@
|
||||
:rules="rules"
|
||||
size="medium"
|
||||
>
|
||||
<!-- <n-form-item label="商户头像:" path="img">
|
||||
<n-upload
|
||||
v-model:file-list="formValue.img"
|
||||
action="https://www.mocky.io/v2/5e4bafc63100007100d8b70f"
|
||||
list-type="image-card"
|
||||
>
|
||||
点击上传
|
||||
</n-upload>
|
||||
</n-form-item> -->
|
||||
<n-form-item label="商户名称:" path="name">
|
||||
<n-input v-model:value="formValue.name" placeholder="请输入商户名称" />
|
||||
<n-input
|
||||
v-model:value="formValue.name"
|
||||
:disabled="isEdit"
|
||||
placeholder="请输入商户名称"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="负责人姓名:" path="username">
|
||||
<n-input v-model:value="formValue.username" placeholder="请输入负责人姓名" />
|
||||
<n-input
|
||||
v-model:value="formValue.username"
|
||||
:disabled="isEdit"
|
||||
placeholder="请输入负责人姓名"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="商户手机号:" path="phone">
|
||||
<n-input v-model:value="formValue.phone" placeholder="请输入商户手机号" />
|
||||
<n-input
|
||||
v-model:value="formValue.phone"
|
||||
:disabled="isEdit"
|
||||
placeholder="请输入商户手机号"
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item label="商户座机:" path="mobile">
|
||||
<n-input v-model:value="formValue.mobile" placeholder="请输入商户座机" />
|
||||
@@ -88,13 +92,12 @@
|
||||
:options="classOptions"
|
||||
/>
|
||||
</n-form-item>
|
||||
<!-- <n-form-item label="商户经纬度:" path="local">
|
||||
<n-input v-model:value="formValue.local" placeholder="请输入商户地址" />
|
||||
<div ref="wrapRef" class="h-300 w-300"></div>
|
||||
</n-form-item> -->
|
||||
<n-form-item label="商户密码:" path="password">
|
||||
<n-form-item v-if="!isEdit" label="商户密码:" path="password">
|
||||
<n-input v-model:value="formValue.password" placeholder="请输入商户密码" />
|
||||
</n-form-item>
|
||||
<n-form-item v-else label="修改密码:" path="password">
|
||||
<n-input v-model:value="formValue.password" placeholder="不修改密码请留空" />
|
||||
</n-form-item>
|
||||
<n-form-item label="商户类型:" path="bType">
|
||||
<n-select
|
||||
v-model:value="formValue.bType"
|
||||
@@ -137,7 +140,7 @@
|
||||
>
|
||||
提交
|
||||
</n-button>
|
||||
<n-button class="m-auto w-200" @click="handleClearValidateClick">重置</n-button>
|
||||
<!-- <n-button class="m-auto w-200" @click="handleClearValidateClick">重置</n-button> -->
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</n-drawer-content>
|
||||
@@ -146,10 +149,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, h, unref, nextTick } from 'vue'
|
||||
import { onMounted, ref, h, withDirectives, resolveDirective } from 'vue'
|
||||
import { NButton } from 'naive-ui'
|
||||
import api from './api'
|
||||
import { useScript } from '@/hooks/useScript'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const isEdit = computed(() => (drawerTitle.value === '编辑商户' ? true : false))
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
@@ -177,19 +182,47 @@ const columns = ref([
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = { ...row }
|
||||
Reflect.deleteProperty(formValue.value, 'password')
|
||||
handleAdd(2)
|
||||
},
|
||||
},
|
||||
},
|
||||
() => '编辑'
|
||||
() => '编辑'
|
||||
),
|
||||
[[vPerms, ['/admin/store/edit']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
class: 'ml-10',
|
||||
type: 'primary',
|
||||
text: true,
|
||||
size: 'small',
|
||||
onClick: async () => {
|
||||
const res = await api.login({
|
||||
bid: row.bid,
|
||||
})
|
||||
window.open(
|
||||
`${import.meta.env.VITE_MER_LOGIN_URL}?redirect=/workbench&type=${
|
||||
res.data.type
|
||||
}&tk=${res.data.token}`
|
||||
)
|
||||
},
|
||||
},
|
||||
() => '一键登录'
|
||||
),
|
||||
[[vPerms, ['/admin/store/login']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
@@ -204,12 +237,12 @@ const showModal = ref(false)
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const drawerTitle = ref('')
|
||||
const drawerTitle = ref('新增商户')
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -226,21 +259,6 @@ const QuryVal = ref({
|
||||
Status: null,
|
||||
})
|
||||
|
||||
// const defaultValueRef = () => ({
|
||||
// name: '',
|
||||
// username: '',
|
||||
// phone: '',
|
||||
// mobile: '',
|
||||
// address: '',
|
||||
// classId: null,
|
||||
// local: '',
|
||||
// password: '',
|
||||
// bType: null,
|
||||
// scaleType: null,
|
||||
// scale: null,
|
||||
// status: 2,
|
||||
// })
|
||||
|
||||
let formValue = ref({
|
||||
name: '',
|
||||
username: '',
|
||||
@@ -323,42 +341,20 @@ const rules = {
|
||||
},
|
||||
}
|
||||
|
||||
const wrapRef = ref(null)
|
||||
const MapUrl =
|
||||
'https://map.qq.com/api/gljs?v=1.exp&key=S3GBZ-WR26O-IXNW2-SXBOD-LZXV6-WAFNO&callback=initMap'
|
||||
|
||||
const { toPromise } = useScript({ src: MapUrl })
|
||||
|
||||
onMounted(() => {
|
||||
initMap()
|
||||
getList()
|
||||
getMertype()
|
||||
})
|
||||
|
||||
const initMap = async () => {
|
||||
await toPromise()
|
||||
await nextTick()
|
||||
const wrapEl = unref(wrapRef.value)
|
||||
if (!wrapEl) return
|
||||
const TMap = window?.TMap
|
||||
const center = new TMap.Map.LatLng(39.984104, 116.307503)
|
||||
const map = new TMap.Map.Map(wrapEl, {
|
||||
rotation: 20, //设置地图旋转角度
|
||||
pitch: 30, //设置俯仰角度(0~45)
|
||||
zoom: 12, //设置地图缩放级别
|
||||
center: center, //设置地图中心点坐标
|
||||
})
|
||||
console.log(map)
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
const res = await api.getList({
|
||||
...QuryVal.value,
|
||||
PageNum: pagination.value.current,
|
||||
PageNum: pagination.value.page,
|
||||
PageSize: pagination.value.pageSize,
|
||||
})
|
||||
data.value = res.data.data
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
@@ -374,7 +370,7 @@ const getMertype = async () => {
|
||||
const clearQuryVal = () => {
|
||||
QuryVal.value = {
|
||||
StoreName: '',
|
||||
Status: '',
|
||||
Status: null,
|
||||
}
|
||||
getList()
|
||||
}
|
||||
@@ -384,14 +380,6 @@ const handleAdd = (e) => {
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
// const onPositiveClick = () => {
|
||||
// showModal.value = false
|
||||
// }
|
||||
|
||||
// const onNegativeClick = () => {
|
||||
// showModal.value = false
|
||||
// }
|
||||
|
||||
const handleValidateClick = (e) => {
|
||||
e.preventDefault()
|
||||
formRef.value?.validate(async (errors) => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button type="primary" @click="handleAdd(1)">新增商户类型</n-button>
|
||||
<n-button v-perms="['/admin/typesof/edit']" type="primary" @click="handleAdd(1)">
|
||||
新增商户类型
|
||||
</n-button>
|
||||
<!-- {{ formValue }} -->
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
@@ -8,6 +10,7 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
@@ -45,9 +48,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, h } from 'vue'
|
||||
import { onMounted, h, withDirectives, resolveDirective } from 'vue'
|
||||
import api from './api'
|
||||
import { NButton } from 'naive-ui'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -76,17 +80,20 @@ const columns = ref([
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
},
|
||||
},
|
||||
},
|
||||
() => '编辑'
|
||||
() => '编辑'
|
||||
),
|
||||
[[vPerms, ['/admin/typesof/edit']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
@@ -112,9 +119,9 @@ const formValue = ref({
|
||||
const showModal = ref(false)
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -134,11 +141,11 @@ const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getMerType({
|
||||
pageNum: pagination.value.current,
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
// 获取入驻审核列表
|
||||
getAuditList: (data) => request.post('/process/store', data),
|
||||
// 通过审核/不通过
|
||||
passAudit: (data) => request.post('/process/store/edit', data),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,209 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title"></CommonPage>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<!-- 详情 -->
|
||||
<n-drawer v-model:show="active" :width="502" placement="right">
|
||||
<n-drawer-content title="商户入驻详情">
|
||||
<div>
|
||||
<div>商户名称:{{ nowRow.name }}</div>
|
||||
<div mt-10>用户姓名:{{ nowRow.username }}</div>
|
||||
<div mt-10>联系电话:{{ nowRow.phone }}</div>
|
||||
<div mt-10>开户行:{{ nowRow.bank }}</div>
|
||||
<div mt-10>银行卡号:{{ nowRow.bank_card }}</div>
|
||||
<div mt-10>商户类型:{{ atype.name }}</div>
|
||||
<div mt-10>经营类目:{{ btype.name }}</div>
|
||||
<div mt-10>
|
||||
<div>营业执照:</div>
|
||||
<n-image width="100" :src="nowRow.license" />
|
||||
</div>
|
||||
<div mt-10>
|
||||
<div>法人身份证(正面):</div>
|
||||
<n-image width="100" :src="nowRow.front" />
|
||||
</div>
|
||||
<div mt-10>
|
||||
<div>法人身份证(反面):</div>
|
||||
<n-image width="100" :src="nowRow.back" />
|
||||
</div>
|
||||
<div mt-10>
|
||||
<div>门头照:</div>
|
||||
<n-image-group>
|
||||
<n-image
|
||||
v-for="(item, index) in nowRow.img"
|
||||
:key="index"
|
||||
mr-10
|
||||
width="100"
|
||||
:src="item"
|
||||
/>
|
||||
</n-image-group>
|
||||
</div>
|
||||
</div>
|
||||
<div m-auto w-full flex justify-center>
|
||||
<n-button mr-20 type="primary" @click="ok">通过</n-button>
|
||||
<n-button mr-20 type="warning" @click="noOk">不通过</n-button>
|
||||
<n-button @click="active = false">关闭</n-button>
|
||||
</div>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
<script setup>
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
import api from './api'
|
||||
import api1 from '../mer_list/api'
|
||||
import { NButton } from 'naive-ui'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const nowRow = ref({})
|
||||
|
||||
const active = ref(false)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '商户名称',
|
||||
align: 'center',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '用户姓名',
|
||||
align: 'center',
|
||||
key: 'username',
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
align: 'center',
|
||||
key: 'phone',
|
||||
},
|
||||
{
|
||||
title: '开户银行',
|
||||
align: 'center',
|
||||
key: 'bank',
|
||||
},
|
||||
{
|
||||
title: '银行卡号',
|
||||
align: 'center',
|
||||
key: 'bank_card',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'detail',
|
||||
render: (row) => {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowRow.value = {
|
||||
...row,
|
||||
img: row.img.split(','),
|
||||
}
|
||||
active.value = true
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '详情',
|
||||
}
|
||||
),
|
||||
[[vPerms, ['/admin/process/store/edit']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
const data = ref([])
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getData()
|
||||
getMertype()
|
||||
})
|
||||
|
||||
const getData = async () => {
|
||||
loading.value = true
|
||||
const res = await api.getAuditList({
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const classOptions = ref([])
|
||||
const typeOptions = ref([])
|
||||
|
||||
const getMertype = async () => {
|
||||
const res = await api1.getMerType()
|
||||
classOptions.value = res.data.class
|
||||
typeOptions.value = res.data.type
|
||||
}
|
||||
|
||||
const atype = computed(() => {
|
||||
return typeOptions.value.find((item) => {
|
||||
if (item.ID === nowRow.value.bType) return item
|
||||
})
|
||||
})
|
||||
|
||||
const btype = computed(() => {
|
||||
return classOptions.value.find((item) => {
|
||||
if (item.ID === nowRow.value.classId) return item
|
||||
})
|
||||
})
|
||||
|
||||
const ok = async () => {
|
||||
$dialog.warning({
|
||||
title: '提示',
|
||||
content: '同意后无法撤销,确认同意吗?',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
const res = await api.passAudit({
|
||||
bid: nowRow.value.bid,
|
||||
status: 1,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
},
|
||||
onNegativeClick: () => {
|
||||
$message.warning('已取消操作')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const noOk = async () => {
|
||||
const res = await api.passAudit({
|
||||
bid: nowRow.value.bid,
|
||||
status: 2,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
nowRow.value = {}
|
||||
active.value = false
|
||||
getData()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -5,6 +5,11 @@ export default {
|
||||
path: '/merchant',
|
||||
component: Layout,
|
||||
redirect: '/mer_list',
|
||||
meta: {
|
||||
title: '商户管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Merlist',
|
||||
@@ -16,6 +21,16 @@ export default {
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Classlist',
|
||||
path: 'mer_class',
|
||||
component: () => import('./mer_class/index.vue'),
|
||||
meta: {
|
||||
title: '商户分类',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Mertype',
|
||||
path: 'mer_type',
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getMerClass: (data) => request.post('/classify', data),
|
||||
addMerClass: (data) => request.post('/classify/edit', data),
|
||||
}
|
||||
@@ -8,33 +8,214 @@
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<!-- 拒绝 -->
|
||||
<n-modal v-model:show="isNoteModel">
|
||||
<n-card
|
||||
style="width: 500px"
|
||||
title="拒绝信息"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-input v-model:value="notesVal" type="textarea" placeholder="请输入拒绝理由...." />
|
||||
<div m-auto p-10>
|
||||
<n-button type="primary" @click="veeify">确定</n-button>
|
||||
<n-button ml-10 @click="clear">取消</n-button>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<!-- 豆子设置 -->
|
||||
<n-modal v-model:show="isDzModel">
|
||||
<n-card
|
||||
style="width: 500px"
|
||||
title="赠送/比例"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-form ref="formRef" :model="nowRow" :rules="rules" label-placement="left">
|
||||
<n-grid :cols="24">
|
||||
<n-form-item-gi :span="20" label="赠送豆子" path="pulse_number">
|
||||
<n-input-number
|
||||
v-model:value="nowRow.pulse_number"
|
||||
clearable
|
||||
:precision="2"
|
||||
placeholder="请输入赠送豆子数量...."
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="20" label="赠送积分" path="integral">
|
||||
<n-input-number
|
||||
v-model:value="nowRow.integral"
|
||||
clearable
|
||||
:precision="2"
|
||||
placeholder="请输入赠送积分数量...."
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="18" label="分佣类型" path="commission_type">
|
||||
<n-select
|
||||
v-model:value="nowRow.commission_type"
|
||||
placeholder="请选择分佣类型"
|
||||
clearable
|
||||
:options="[
|
||||
{
|
||||
label: '百分比',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '数值',
|
||||
value: 2,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="20" label="分佣比例" path="commission">
|
||||
<n-input-number
|
||||
v-model:value="nowRow.commission"
|
||||
clearable
|
||||
placeholder="请输入分佣比例...."
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12">
|
||||
<div m-auto p-10>
|
||||
<n-button type="primary" @click="veeify">确定</n-button>
|
||||
<n-button ml-10 @click="clear">取消</n-button>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<!-- 商品详情 -->
|
||||
<n-drawer v-model:show="showDrawer" :width="502">
|
||||
<n-drawer-content title="商品详情" closable>
|
||||
<n-space vertical>
|
||||
<div>
|
||||
<span>商品分类:</span>
|
||||
<span>{{ goodInfo.class_name }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品名称:</span>
|
||||
<span>{{ goodInfo.name }}</span>
|
||||
</div>
|
||||
<div flex items-center>
|
||||
<span>封面:</span>
|
||||
<n-image width="100" :src="goodInfo.cover" />
|
||||
</div>
|
||||
<div flex items-center>
|
||||
<span>轮播图:</span>
|
||||
<div w-400 overflow-auto>
|
||||
<n-image
|
||||
v-for="(url, index) in goodInfo.rotation?.split(',')"
|
||||
:key="index"
|
||||
width="100"
|
||||
:src="url"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品价格:</span>
|
||||
<span>{{ goodInfo.number }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>市场价格:</span>
|
||||
<span>{{ goodInfo.market_num }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品库存:</span>
|
||||
<span>{{ goodInfo.stock }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品简介:</span>
|
||||
<span>{{ goodInfo.profile }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品详情:</span>
|
||||
<div v-html="goodInfo.details"></div>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import { NDropdown, NButton } from 'naive-ui'
|
||||
import { h } from 'vue'
|
||||
import { NButton, NImage, NSpace, NEllipsis } from 'naive-ui'
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const isNoteModel = ref(false)
|
||||
|
||||
const isDzModel = ref(false)
|
||||
|
||||
const goodInfo = ref({})
|
||||
|
||||
const showDrawer = ref(false)
|
||||
|
||||
const notesVal = ref('')
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const rules = {
|
||||
pulse_number: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入赠送豆子数量',
|
||||
trigger: 'blur',
|
||||
},
|
||||
integral: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入赠送积分数量',
|
||||
trigger: 'blur',
|
||||
},
|
||||
commission_type: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择分佣类型',
|
||||
trigger: 'change',
|
||||
},
|
||||
commission: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请输入分佣比例',
|
||||
trigger: 'blur',
|
||||
},
|
||||
}
|
||||
|
||||
const nowRow = ref({})
|
||||
const nowKey = ref(null)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '商品名称',
|
||||
key: 'name',
|
||||
slot: 'name',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: 'max-width: 200px',
|
||||
},
|
||||
{
|
||||
default: () => row.name,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品封面',
|
||||
slot: 'cover',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h('img', {
|
||||
return h(NImage, {
|
||||
src: row.cover,
|
||||
style: {
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
},
|
||||
width: '30',
|
||||
})
|
||||
},
|
||||
},
|
||||
@@ -44,7 +225,7 @@ const columns = ref([
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '商品价格',
|
||||
title: '商品价格(元)',
|
||||
key: 'number',
|
||||
align: 'center',
|
||||
},
|
||||
@@ -53,52 +234,130 @@ const columns = ref([
|
||||
key: 'stock',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '赠送豆子',
|
||||
key: 'pulse_number',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '赠送积分',
|
||||
key: 'integral',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '分佣类型',
|
||||
slot: 'commission_type',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return row.commission_type === 1 ? '百分比' : '数值'
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '分佣比例',
|
||||
key: 'commission',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '商品状态',
|
||||
slot: 'status',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return row.status === 0 ? '待审核' : row.status === 1 ? '已审核' : '已拒绝'
|
||||
return row.status === 3 ? '待审核' : row.status === 1 ? '已审核' : '已拒绝'
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'notes',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
const el = []
|
||||
|
||||
if (row.status === 0) {
|
||||
el.push(
|
||||
let el = []
|
||||
if (row.status === 3) {
|
||||
el = [
|
||||
h(
|
||||
NDropdown,
|
||||
NSpace,
|
||||
{
|
||||
trigger: 'click',
|
||||
options: [
|
||||
{
|
||||
label: '审核',
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
label: '拒绝',
|
||||
key: 2,
|
||||
},
|
||||
],
|
||||
onSelect: (key) => {
|
||||
veeify(key, row)
|
||||
},
|
||||
justify: 'center',
|
||||
},
|
||||
() =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
{
|
||||
default: () => [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowKey.value = 1
|
||||
nowRow.value = { ...row }
|
||||
veeify()
|
||||
},
|
||||
},
|
||||
() => '审核通过'
|
||||
),
|
||||
[[vPerms, ['/admin/goods/process']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'error',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowKey.value = 2
|
||||
nowRow.value = { ...row }
|
||||
isNoteModel.value = true
|
||||
},
|
||||
},
|
||||
() => '审核拒绝'
|
||||
),
|
||||
[[vPerms, ['/admin/goods/process']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'warning',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowKey.value = 3
|
||||
goodInfo.value = { ...row }
|
||||
showDrawer.value = true
|
||||
},
|
||||
},
|
||||
() => '商品详情'
|
||||
),
|
||||
[[vPerms, ['/admin/goods/process']]]
|
||||
),
|
||||
],
|
||||
}
|
||||
),
|
||||
]
|
||||
} else {
|
||||
el = [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'info',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowRow.value = { ...row }
|
||||
nowKey.value = 3
|
||||
isDzModel.value = true
|
||||
},
|
||||
() => '审核'
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
{
|
||||
default: () => '赠送/比例',
|
||||
}
|
||||
),
|
||||
[[vPerms, ['/admin/goods/process']]]
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
return el
|
||||
@@ -109,9 +368,9 @@ const columns = ref([
|
||||
const data = ref([])
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itamCount: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -131,24 +390,47 @@ const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getHotlist({
|
||||
pageNum: pagination.value.current,
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const veeify = async (key, row) => {
|
||||
const res = await api.getHotStatus({
|
||||
gid: row.gid,
|
||||
status: key,
|
||||
})
|
||||
console.log(res)
|
||||
getList()
|
||||
const clear = () => {
|
||||
isNoteModel.value = false
|
||||
isDzModel.value = false
|
||||
notesVal.value = ''
|
||||
nowRow.value = {}
|
||||
}
|
||||
|
||||
const veeify = async () => {
|
||||
let data = {}
|
||||
if (nowKey.value === 1 || nowKey.value === 2) {
|
||||
data = {
|
||||
gid: nowRow.value.gid,
|
||||
status: nowKey.value,
|
||||
notes: notesVal.value,
|
||||
}
|
||||
await api.getHotStatus(data)
|
||||
await getList()
|
||||
clear()
|
||||
} else {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
data = {
|
||||
...nowRow.value,
|
||||
}
|
||||
await api.getHotStatus(data)
|
||||
await getList()
|
||||
clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,5 +2,5 @@ import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getPointlist: (data) => request.post('/point/goods', data),
|
||||
getPointStatus: (data) => request.post('/point/goods/process', data),
|
||||
setPointStatus: (data) => request.post('/point/goods/process', data),
|
||||
}
|
||||
|
||||
@@ -6,34 +6,122 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<!-- 拒绝 -->
|
||||
<n-modal v-model:show="isNoteModel">
|
||||
<n-card
|
||||
style="width: 500px"
|
||||
title="拒绝信息"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-input v-model:value="notesVal" type="textarea" placeholder="请输入拒绝理由...." />
|
||||
<div m-auto p-10>
|
||||
<n-button type="primary" @click="veeify">确定</n-button>
|
||||
<n-button ml-10 @click="clear">取消</n-button>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<!-- 商品详情 -->
|
||||
<n-drawer v-model:show="showDrawer" :width="502">
|
||||
<n-drawer-content title="商品详情" closable>
|
||||
<n-space vertical>
|
||||
<div>
|
||||
<span>商品分类:</span>
|
||||
<span>{{ goodInfo.class_name }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品名称:</span>
|
||||
<span>{{ goodInfo.name }}</span>
|
||||
</div>
|
||||
<div flex items-center>
|
||||
<span>封面:</span>
|
||||
<n-image width="100" :src="goodInfo.cover" />
|
||||
</div>
|
||||
<div flex items-center>
|
||||
<span w-90>轮播图:</span>
|
||||
<div flex flex-wrap>
|
||||
<n-image
|
||||
v-for="(url, index) in goodInfo.rotation?.split(',')"
|
||||
:key="index"
|
||||
width="100"
|
||||
:src="url"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品价格:</span>
|
||||
<span>{{ goodInfo.number }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>市场价格:</span>
|
||||
<span>{{ goodInfo.market_num }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品库存:</span>
|
||||
<span>{{ goodInfo.stock }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品简介:</span>
|
||||
<span>{{ goodInfo.profile }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>商品详情:</span>
|
||||
<div v-html="goodInfo.details"></div>
|
||||
</div>
|
||||
</n-space>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import { NDropdown, NButton } from 'naive-ui'
|
||||
import { h } from 'vue'
|
||||
import { NEllipsis, NButton, NImage, NSpace } from 'naive-ui'
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const isNoteModel = ref(false)
|
||||
|
||||
const goodInfo = ref({})
|
||||
|
||||
const showDrawer = ref(false)
|
||||
|
||||
const notesVal = ref('')
|
||||
|
||||
const nowRow = ref({})
|
||||
const nowKey = ref(null)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '商品名称',
|
||||
key: 'name',
|
||||
slot: 'name',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: 'max-width: 200px',
|
||||
},
|
||||
{
|
||||
default: () => row.name,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品封面',
|
||||
slot: 'cover',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h('img', {
|
||||
return h(NImage, {
|
||||
src: row.cover,
|
||||
style: {
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
},
|
||||
width: '30',
|
||||
})
|
||||
},
|
||||
},
|
||||
@@ -57,50 +145,81 @@ const columns = ref([
|
||||
slot: 'status',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return row.status === 0 ? '待审核' : row.status === 1 ? '已审核' : '已拒绝'
|
||||
return row.status === 3 ? '待审核' : row.status === 1 ? '已审核' : '已拒绝'
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'notes',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
const el = []
|
||||
|
||||
if (row.status === 0) {
|
||||
el.push(
|
||||
if (row.status === 3) {
|
||||
return [
|
||||
h(
|
||||
NDropdown,
|
||||
NSpace,
|
||||
{
|
||||
trigger: 'click',
|
||||
options: [
|
||||
{
|
||||
label: '审核',
|
||||
key: 1,
|
||||
},
|
||||
{
|
||||
label: '拒绝',
|
||||
key: 2,
|
||||
},
|
||||
],
|
||||
onSelect: (key) => {
|
||||
veeify(key, row)
|
||||
},
|
||||
justify: 'center',
|
||||
},
|
||||
() =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
},
|
||||
() => '审核'
|
||||
)
|
||||
)
|
||||
)
|
||||
{
|
||||
default: () => [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowKey.value = 1
|
||||
nowRow.value = { ...row }
|
||||
veeify()
|
||||
},
|
||||
},
|
||||
() => '审核通过'
|
||||
),
|
||||
[[vPerms, ['/admin/point/goods/process']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'error',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowKey.value = 2
|
||||
nowRow.value = { ...row }
|
||||
isNoteModel.value = true
|
||||
},
|
||||
},
|
||||
() => '审核拒绝'
|
||||
),
|
||||
[[vPerms, ['/admin/point/goods/process']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'info',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
nowKey.value = 3
|
||||
goodInfo.value = { ...row }
|
||||
showDrawer.value = true
|
||||
},
|
||||
},
|
||||
() => '商品详情'
|
||||
),
|
||||
[[vPerms, ['/admin/point/goods/process']]]
|
||||
),
|
||||
],
|
||||
}
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
return el
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -108,9 +227,9 @@ const columns = ref([
|
||||
const data = ref([])
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -130,25 +249,30 @@ const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getPointlist({
|
||||
pageNum: pagination.value.current,
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const veeify = async (key, row) => {
|
||||
console.log(typeof key)
|
||||
const res = await api.getPointStatus({
|
||||
gid: row.gid,
|
||||
status: key,
|
||||
const clear = () => {
|
||||
isNoteModel.value = false
|
||||
notesVal.value = ''
|
||||
}
|
||||
|
||||
const veeify = async () => {
|
||||
await api.setPointStatus({
|
||||
gid: nowRow.value.gid,
|
||||
status: nowKey.value,
|
||||
notes: notesVal.value,
|
||||
})
|
||||
console.log(res)
|
||||
getList()
|
||||
clear()
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -5,17 +5,12 @@ export default {
|
||||
path: '/commodity',
|
||||
component: Layout,
|
||||
redirect: '/commodity_class',
|
||||
meta: {
|
||||
title: '商品管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'CommodityClass',
|
||||
path: 'commodity_class',
|
||||
component: () => import('./commodity_class/index.vue'),
|
||||
meta: {
|
||||
title: '商品分类',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'HotList',
|
||||
path: 'hot_list',
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<template>
|
||||
<CommonPage show-footer>
|
||||
<n-space size="large">
|
||||
<n-card title="按钮 Button">
|
||||
<n-space>
|
||||
<n-button>Default</n-button>
|
||||
<n-button type="tertiary">Tertiary</n-button>
|
||||
<n-button type="primary">Primary</n-button>
|
||||
<n-button type="info">Info</n-button>
|
||||
<n-button type="success">Success</n-button>
|
||||
<n-button type="warning">Warning</n-button>
|
||||
<n-button type="error">Error</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card title="带 Icon 的按钮">
|
||||
<n-space>
|
||||
<n-button type="info">
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />
|
||||
新增
|
||||
</n-button>
|
||||
<n-button type="error">
|
||||
<TheIcon icon="material-symbols:delete-outline" :size="18" class="mr-5" />
|
||||
删除
|
||||
</n-button>
|
||||
<n-button type="warning">
|
||||
<TheIcon icon="material-symbols:edit-outline" :size="18" class="mr-5" />
|
||||
编辑
|
||||
</n-button>
|
||||
<n-button type="primary">
|
||||
<TheIcon icon="majesticons:eye-line" :size="18" class="mr-5" />
|
||||
查看
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
</n-space>
|
||||
|
||||
<n-space size="large" mt-30>
|
||||
<n-card min-w-340 title="通知 Notification">
|
||||
<n-space>
|
||||
<n-button @click="notify('info')">信息</n-button>
|
||||
<n-button @click="notify('success')">成功</n-button>
|
||||
<n-button @click="notify('warning')">警告</n-button>
|
||||
<n-button @click="notify('error')">错误</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
|
||||
<n-card min-w-340 title="确认弹窗 Dialog">
|
||||
<n-button type="error" @click="handleDelete">
|
||||
<icon-mi:delete mr-5 />
|
||||
删除
|
||||
</n-button>
|
||||
</n-card>
|
||||
|
||||
<n-card min-w-340 title="消息提醒 Message">
|
||||
<n-button :loading="loading" type="primary" @click="handleLogin">
|
||||
<icon-mdi:login v-show="!loading" mr-5 />
|
||||
登陆
|
||||
</n-button>
|
||||
</n-card>
|
||||
</n-space>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const handleDelete = function () {
|
||||
$dialog.confirm({
|
||||
content: '确认删除?',
|
||||
confirm() {
|
||||
$message.success('删除成功')
|
||||
},
|
||||
cancel() {
|
||||
$message.warning('已取消')
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
function handleLogin() {
|
||||
loading.value = true
|
||||
$message.loading('登陆中...')
|
||||
setTimeout(() => {
|
||||
$message.error('登陆失败')
|
||||
$message.loading('正在尝试重新登陆...')
|
||||
setTimeout(() => {
|
||||
$message.success('登陆成功')
|
||||
loading.value = false
|
||||
}, 2000)
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
function notify(type) {
|
||||
$notification[type]({
|
||||
content: '说点啥呢',
|
||||
meta: '想不出来',
|
||||
duration: 2500,
|
||||
keepAliveOnHover: true,
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -1,31 +0,0 @@
|
||||
<template>
|
||||
<CommonPage show-footer>
|
||||
<div w-350>
|
||||
<n-input v-model:value="inputVal" />
|
||||
<n-input-number v-model:value="number" mt-30 />
|
||||
<p mt-20 text-center text-14 color-gray>注:右击标签重新加载可重置keep-alive</p>
|
||||
</div>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineOptions({ name: 'KeepAlive' })
|
||||
|
||||
const inputVal = ref('')
|
||||
const number = ref(0)
|
||||
|
||||
onMounted(() => {
|
||||
$message.success('onMounted')
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
$message.error('onUnmounted')
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
$message.info('onActivated')
|
||||
})
|
||||
onDeactivated(() => {
|
||||
$message.warning('onDeactivated')
|
||||
})
|
||||
</script>
|
||||
@@ -1,44 +0,0 @@
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export default {
|
||||
name: 'Test',
|
||||
path: '/base',
|
||||
component: Layout,
|
||||
redirect: '/base/index',
|
||||
isHidden: true,
|
||||
meta: {
|
||||
title: '基础功能',
|
||||
icon: 'majesticons:compass-line',
|
||||
order: 1,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'BaseComponents',
|
||||
path: 'index',
|
||||
component: () => import('./index.vue'),
|
||||
meta: {
|
||||
title: '基础组件',
|
||||
icon: 'material-symbols:auto-awesome-outline-rounded',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Unocss',
|
||||
path: 'unocss',
|
||||
component: () => import('./unocss/index.vue'),
|
||||
meta: {
|
||||
title: 'Unocss',
|
||||
icon: 'material-symbols:auto-awesome-outline-rounded',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'KeepAlive',
|
||||
path: 'keep-alive',
|
||||
component: () => import('./keep-alive/index.vue'),
|
||||
meta: {
|
||||
title: 'KeepAlive',
|
||||
icon: 'material-symbols:auto-awesome-outline-rounded',
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<CommonPage show-footer>
|
||||
<p>
|
||||
文档:
|
||||
<a c-blue hover-decoration-underline href="https://uno.antfu.me/" target="_blank">
|
||||
https://uno.antfu.me/
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
playground:
|
||||
<a c-blue hover-decoration-underline href="https://unocss.antfu.me/play/" target="_blank">
|
||||
https://unocss.antfu.me/play/
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div mt-20 w-350 f-c-c flex-col>
|
||||
<div flex flex-wrap justify-around rounded-10 p-10 border="1 solid #ccc">
|
||||
<div m-20 h-50 w-50 f-c-c rounded-5 p-10 border="1 solid">
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div m-20 h-50 w-50 flex justify-between rounded-5 p-10 border="1 solid">
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 self-end rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div m-20 h-50 w-50 flex justify-between rounded-5 p-10 border="1 solid">
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 self-center rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 self-end rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div m-20 h-50 w-50 flex justify-between rounded-5 p-10 border="1 solid">
|
||||
<div flex-col justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div flex-col justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
</div>
|
||||
<div m-20 h-50 w-50 flex-col items-center justify-between rounded-5 p-10 border="1 solid">
|
||||
<div w-full flex justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<div w-full flex justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
</div>
|
||||
<div m-20 h-50 w-50 flex-col justify-between rounded-5 p-10 border="1 solid">
|
||||
<div w-full flex justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div w-full flex justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
<div w-full flex justify-between>
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
<span h-6 w-6 rounded-3 bg-black dark:bg-white />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 mt-10 text-14 font-normal color-gray>Flex 骰子</h2>
|
||||
</div>
|
||||
</CommonPage>
|
||||
</template>
|
||||
@@ -1,47 +0,0 @@
|
||||
<template>
|
||||
<CommonPage>
|
||||
<div h-60 flex items-center bg-white pl-20 pr-20 dark:bg-dark>
|
||||
<input
|
||||
v-model="post.title"
|
||||
class="mr-20 flex-1 pb-15 pt-15 text-20 font-bold color-primary"
|
||||
dark:bg-dark
|
||||
type="text"
|
||||
placeholder="输入文章标题..."
|
||||
/>
|
||||
<n-button type="primary" style="width: 80px" :loading="btnLoading" @click="handleSavePost">
|
||||
<TheIcon v-if="!btnLoading" icon="line-md:confirm-circle" class="mr-5" :size="18" />
|
||||
保存
|
||||
</n-button>
|
||||
</div>
|
||||
<MdEditor v-model="post.content" style="height: calc(100vh - 305px)" dark:bg-dark />
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { MdEditor } from 'md-editor-v3'
|
||||
import 'md-editor-v3/lib/style.css'
|
||||
|
||||
defineOptions({ name: 'MDEditor' })
|
||||
|
||||
// refs
|
||||
let post = ref({})
|
||||
let btnLoading = ref(false)
|
||||
|
||||
function handleSavePost() {
|
||||
btnLoading.value = true
|
||||
$message.loading('正在保存...')
|
||||
setTimeout(() => {
|
||||
$message.success('保存成功')
|
||||
btnLoading.value = false
|
||||
}, 2000)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.md-preview {
|
||||
ul,
|
||||
ol {
|
||||
list-style: revert;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<AppPage>
|
||||
<div class="h-full flex-col" border="1 solid #ccc" dark:bg-dark>
|
||||
<WangToolbar
|
||||
border-b="1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:default-config="toolbarConfig"
|
||||
mode="default"
|
||||
/>
|
||||
<WangEditor
|
||||
v-model="valueHtml"
|
||||
style="flex: 1; overflow-y: hidden"
|
||||
:default-config="editorConfig"
|
||||
mode="default"
|
||||
@on-created="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</AppPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { Editor as WangEditor, Toolbar as WangToolbar } from '@wangeditor/editor-for-vue'
|
||||
|
||||
defineOptions({ name: 'RichTextEditor' })
|
||||
const editorRef = shallowRef()
|
||||
const toolbarConfig = { excludeKeys: 'fullScreen' }
|
||||
const editorConfig = { placeholder: '请输入内容...', MENU_CONF: {} }
|
||||
const valueHtml = ref('')
|
||||
|
||||
const handleCreated = (editor) => {
|
||||
editorRef.value = editor
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
html.dark {
|
||||
--w-e-textarea-bg-color: #333;
|
||||
--w-e-textarea-color: #fff;
|
||||
--w-e-toolbar-bg-color: #333;
|
||||
--w-e-toolbar-color: #fff;
|
||||
--w-e-toolbar-active-bg-color: #666;
|
||||
--w-e-toolbar-active-color: #fff;
|
||||
/* ...其他... */
|
||||
}
|
||||
</style>
|
||||
@@ -1,65 +0,0 @@
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export default {
|
||||
name: 'Demo',
|
||||
path: '/demo',
|
||||
component: Layout,
|
||||
redirect: '/demo/crud',
|
||||
meta: {
|
||||
title: '示例页面',
|
||||
customIcon: 'logo',
|
||||
role: ['admin'],
|
||||
requireAuth: true,
|
||||
order: 3,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Crud',
|
||||
path: 'crud',
|
||||
component: () => import('./table/index.vue'),
|
||||
meta: {
|
||||
title: 'CRUD表格',
|
||||
icon: 'ic:baseline-table-view',
|
||||
role: ['admin'],
|
||||
requireAuth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'MDEditor',
|
||||
path: 'md-editor',
|
||||
component: () => import('./editor/md-editor.vue'),
|
||||
meta: {
|
||||
title: 'MD编辑器',
|
||||
icon: 'ri:markdown-line',
|
||||
role: ['admin'],
|
||||
requireAuth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'RichTextEditor',
|
||||
path: 'rich-text',
|
||||
component: () => import('./editor/rich-text.vue'),
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
icon: 'ic:sharp-text-rotation-none',
|
||||
role: ['admin'],
|
||||
requireAuth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'Upload',
|
||||
path: 'upload',
|
||||
component: () => import('./upload/index.vue'),
|
||||
meta: {
|
||||
title: '图片上传',
|
||||
icon: 'mdi:upload',
|
||||
role: ['admin'],
|
||||
requireAuth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getPosts: (params = {}) => request.get('posts', { params }),
|
||||
getPostById: (id) => request.get(`/post/${id}`),
|
||||
addPost: (data) => request.post('/post', data),
|
||||
updatePost: (data) => request.put(`/post/${data.id}`, data),
|
||||
deletePost: (id) => request.delete(`/post/${id}`),
|
||||
}
|
||||
@@ -1,233 +0,0 @@
|
||||
<template>
|
||||
<CommonPage show-footer title="文章">
|
||||
<template #action>
|
||||
<div>
|
||||
<n-button type="primary" secondary @click="$table?.handleExport()">
|
||||
<TheIcon icon="mdi:download" :size="18" class="mr-5" />
|
||||
导出
|
||||
</n-button>
|
||||
<n-button type="primary" class="ml-16" @click="handleAdd">
|
||||
<TheIcon icon="material-symbols:add" :size="18" class="mr-5" />
|
||||
新建文章
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<CrudTable
|
||||
ref="$table"
|
||||
v-model:query-items="queryItems"
|
||||
:extra-params="extraParams"
|
||||
:scroll-x="1200"
|
||||
:columns="columns"
|
||||
:get-data="api.getPosts"
|
||||
@on-checked="onChecked"
|
||||
@on-data-change="(data) => (tableData = data)"
|
||||
>
|
||||
<template #queryBar>
|
||||
<QueryBarItem label="标题" :label-width="50">
|
||||
<n-input
|
||||
v-model:value="queryItems.title"
|
||||
type="text"
|
||||
placeholder="请输入标题"
|
||||
@keypress.enter="$table?.handleSearch"
|
||||
/>
|
||||
</QueryBarItem>
|
||||
</template>
|
||||
</CrudTable>
|
||||
<!-- 新增/编辑/查看 -->
|
||||
<CrudModal
|
||||
v-model:visible="modalVisible"
|
||||
:title="modalTitle"
|
||||
:loading="modalLoading"
|
||||
:show-footer="modalAction !== 'view'"
|
||||
@on-save="handleSave"
|
||||
>
|
||||
<n-form
|
||||
ref="modalFormRef"
|
||||
label-placement="left"
|
||||
label-align="left"
|
||||
:label-width="80"
|
||||
:model="modalForm"
|
||||
:disabled="modalAction === 'view'"
|
||||
>
|
||||
<n-form-item label="作者" path="author">
|
||||
<n-input v-model:value="modalForm.author" disabled />
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
label="文章标题"
|
||||
path="title"
|
||||
:rule="{
|
||||
required: true,
|
||||
message: '请输入文章标题',
|
||||
trigger: ['input', 'blur'],
|
||||
}"
|
||||
>
|
||||
<n-input v-model:value="modalForm.title" placeholder="请输入文章标题" />
|
||||
</n-form-item>
|
||||
<n-form-item
|
||||
label="文章内容"
|
||||
path="content"
|
||||
:rule="{
|
||||
required: true,
|
||||
message: '请输入文章内容',
|
||||
trigger: ['input', 'blur'],
|
||||
}"
|
||||
>
|
||||
<n-input
|
||||
v-model:value="modalForm.content"
|
||||
placeholder="请输入文章内容"
|
||||
type="textarea"
|
||||
:autosize="{
|
||||
minRows: 3,
|
||||
maxRows: 5,
|
||||
}"
|
||||
/>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</CrudModal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NButton, NSwitch } from 'naive-ui'
|
||||
import { formatDateTime, renderIcon, isNullOrUndef } from '@/utils'
|
||||
import { useCRUD } from '@/composables'
|
||||
import api from './api'
|
||||
|
||||
defineOptions({ name: 'Crud' })
|
||||
|
||||
const $table = ref(null)
|
||||
/** 表格数据,触发搜索的时候会更新这个值 */
|
||||
const tableData = ref([])
|
||||
/** QueryBar筛选参数(可选) */
|
||||
const queryItems = ref({})
|
||||
/** 补充参数(可选) */
|
||||
const extraParams = ref({})
|
||||
|
||||
onActivated(() => {
|
||||
$table.value?.handleSearch()
|
||||
})
|
||||
|
||||
const columns = [
|
||||
{ type: 'selection', fixed: 'left' },
|
||||
{
|
||||
title: '发布',
|
||||
key: 'isPublish',
|
||||
width: 60,
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
render(row) {
|
||||
return h(NSwitch, {
|
||||
size: 'small',
|
||||
rubberBand: false,
|
||||
value: row['isPublish'],
|
||||
loading: !!row.publishing,
|
||||
onUpdateValue: () => handlePublish(row),
|
||||
})
|
||||
},
|
||||
},
|
||||
{ title: '标题', key: 'title', width: 150, ellipsis: { tooltip: true } },
|
||||
{ title: '分类', key: 'category', width: 80, ellipsis: { tooltip: true } },
|
||||
{ title: '创建人', key: 'author', width: 80 },
|
||||
{
|
||||
title: '创建时间',
|
||||
key: 'createDate',
|
||||
width: 150,
|
||||
render(row) {
|
||||
return h('span', formatDateTime(row['createDate']))
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '最后更新时间',
|
||||
key: 'updateDate',
|
||||
width: 150,
|
||||
render(row) {
|
||||
return h('span', formatDateTime(row['updateDate']))
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 240,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
hideInExcel: true,
|
||||
render(row) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
secondary: true,
|
||||
onClick: () => handleView(row),
|
||||
},
|
||||
{ default: () => '查看', icon: renderIcon('majesticons:eye-line', { size: 14 }) }
|
||||
),
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'primary',
|
||||
style: 'margin-left: 15px;',
|
||||
onClick: () => handleEdit(row),
|
||||
},
|
||||
{ default: () => '编辑', icon: renderIcon('material-symbols:edit-outline', { size: 14 }) }
|
||||
),
|
||||
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
size: 'small',
|
||||
type: 'error',
|
||||
style: 'margin-left: 15px;',
|
||||
onClick: () => handleDelete(row.id),
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
icon: renderIcon('material-symbols:delete-outline', { size: 14 }),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 选中事件
|
||||
function onChecked(rowKeys) {
|
||||
if (rowKeys.length) $message.info(`选中${rowKeys.join(' ')}`)
|
||||
}
|
||||
|
||||
// 发布
|
||||
function handlePublish(row) {
|
||||
if (isNullOrUndef(row.id)) return
|
||||
|
||||
row.publishing = true
|
||||
setTimeout(() => {
|
||||
row.isPublish = !row.isPublish
|
||||
row.publishing = false
|
||||
$message?.success(row.isPublish ? '已发布' : '已取消发布')
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const {
|
||||
modalVisible,
|
||||
modalAction,
|
||||
modalTitle,
|
||||
modalLoading,
|
||||
handleAdd,
|
||||
handleDelete,
|
||||
handleEdit,
|
||||
handleView,
|
||||
handleSave,
|
||||
modalForm,
|
||||
modalFormRef,
|
||||
} = useCRUD({
|
||||
name: '文章',
|
||||
initForm: { author: '大脸怪' },
|
||||
doCreate: api.addPost,
|
||||
doDelete: api.deletePost,
|
||||
doUpdate: api.updatePost,
|
||||
refresh: () => $table.value?.handleSearch(),
|
||||
})
|
||||
</script>
|
||||
@@ -1,84 +0,0 @@
|
||||
<template>
|
||||
<CommonPage>
|
||||
<n-upload
|
||||
class="mx-auto w-[75%] p-20 text-center"
|
||||
:custom-request="handleUpload"
|
||||
:show-file-list="false"
|
||||
accept=".png,.jpg,.jpeg"
|
||||
@before-upload="onBeforeUpload"
|
||||
>
|
||||
<n-upload-dragger>
|
||||
<div class="h-150 f-c-c flex-col">
|
||||
<TheIcon icon="mdi:upload" :size="68" class="mb-12 c-primary" />
|
||||
<n-text class="text-14 c-gray">点击或者拖动文件到该区域来上传</n-text>
|
||||
</div>
|
||||
</n-upload-dragger>
|
||||
</n-upload>
|
||||
|
||||
<n-card v-if="imgList && imgList.length" class="mt-16 items-center">
|
||||
<n-image-group>
|
||||
<n-space justify="space-between" align="center">
|
||||
<n-card v-for="(item, index) in imgList" :key="index" class="w-280 hover:card-shadow">
|
||||
<div class="h-160 f-c-c">
|
||||
<n-image width="200" :src="item.url" />
|
||||
</div>
|
||||
<n-space class="mt-16" justify="space-evenly">
|
||||
<n-button dashed type="primary" @click="copy(item.url)">url</n-button>
|
||||
<n-button dashed type="primary" @click="copy(``)">
|
||||
MD
|
||||
</n-button>
|
||||
<n-button
|
||||
dashed
|
||||
type="primary"
|
||||
@click="copy(`<img src="${item.url}" />`)"
|
||||
>
|
||||
img
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-card>
|
||||
<div v-for="i in 4" :key="i" class="w-280" />
|
||||
</n-space>
|
||||
</n-image-group>
|
||||
</n-card>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useClipboard } from '@vueuse/core'
|
||||
defineOptions({ name: 'Upload' })
|
||||
|
||||
const { copy, copied } = useClipboard()
|
||||
|
||||
const imgList = reactive([
|
||||
{ url: 'https://cdn.qszone.com/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||
{ url: 'https://cdn.qszone.com/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||
{ url: 'https://cdn.qszone.com/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||
{ url: 'https://cdn.qszone.com/images/5c23d52f880511ebb6edd017c2d2eca2.jpg' },
|
||||
])
|
||||
|
||||
watch(copied, (val) => {
|
||||
val && $message.success('已复制到剪切板')
|
||||
})
|
||||
|
||||
function onBeforeUpload({ file }) {
|
||||
if (!file.file?.type.startsWith('image/')) {
|
||||
$message.error('只能上传图片')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function handleUpload({ file, onFinish }) {
|
||||
if (!file || !file.type) {
|
||||
$message.error('请选择文件')
|
||||
}
|
||||
|
||||
// 模拟上传
|
||||
$message.loading('上传中...')
|
||||
setTimeout(() => {
|
||||
$message.success('上传成功')
|
||||
imgList.push({ fileName: file.name, url: URL.createObjectURL(file.file) })
|
||||
onFinish()
|
||||
}, 1500)
|
||||
}
|
||||
</script>
|
||||
7
src/views/finance/api.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getData: (data) => request.post('/store/withdraw', data),
|
||||
// 审核提现
|
||||
passAudit: (data) => request.post('/store/withdraw/edit', data),
|
||||
}
|
||||
358
src/views/finance/index.vue
Normal file
@@ -0,0 +1,358 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-row gutter="12">
|
||||
<n-col :span="24">
|
||||
<div flex>
|
||||
<n-card w-400>
|
||||
<n-statistic label="总提现金额(含已驳回)" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.total / 100"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-400>
|
||||
<n-statistic label="待处理金额" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.service / 100"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-400>
|
||||
<n-statistic label="已审核金额" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.count / 100"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10>
|
||||
<span w-80>提现状态:</span>
|
||||
<n-radio-group v-model:value="queryData.status" class="ml-10">
|
||||
<n-radio-button
|
||||
v-for="song in songs"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
:label="song.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-80>号码搜索:</span>
|
||||
<n-input v-model:value="queryData.word" style="width: 25%" placeholder="请输入手机号码" />
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="10">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-80>申请时间:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryData.time"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10>
|
||||
<n-button type="primary" @click="getList">搜索</n-button>
|
||||
<n-button ml-10 @click="clearQueryData">重置</n-button>
|
||||
</div>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-data-table
|
||||
class="mt-10"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<!-- 打款记录 -->
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 500px"
|
||||
title="打款记录"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
|
||||
<n-grid :cols="1" :x-gap="24">
|
||||
<n-form-item-gi :span="12" label="打款截图" path="img">
|
||||
<Upload v-model:list="model.img" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi>
|
||||
<div w-full flex justify-center>
|
||||
<n-button type="primary" @click="ok">确定</n-button>
|
||||
<n-button class="ml-10" @click="clear">关闭</n-button>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import { NButton, NImage, NTag } from 'naive-ui'
|
||||
import Upload from '@/components/Upload.vue'
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
const vPerms = resolveDirective('perms')
|
||||
const vRole = [[vPerms, ['/admin/store/withdraw/edit']]]
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const queryData = ref({
|
||||
status: '',
|
||||
word: '',
|
||||
time: '',
|
||||
})
|
||||
|
||||
const cardData = ref({
|
||||
total: 0,
|
||||
service: 0,
|
||||
count: 0,
|
||||
})
|
||||
|
||||
const songs = ref([
|
||||
{
|
||||
value: 1,
|
||||
label: '已审核',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '已驳回',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '待审核',
|
||||
},
|
||||
])
|
||||
|
||||
const nowRow = ref({})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const model = ref({
|
||||
img: [],
|
||||
})
|
||||
|
||||
const rules = {
|
||||
img: { required: true, type: 'array', message: '请上传打款截图' },
|
||||
}
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '名字',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '电话',
|
||||
key: 'phone',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '银行名称',
|
||||
key: 'bank',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '银行卡号',
|
||||
key: 'bank_card',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '账户名称',
|
||||
key: 'bank_name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '法人',
|
||||
key: 'bank_user',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '提现金额',
|
||||
slot: 'integral',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h('span', {}, row.integral / 100)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '审核状态',
|
||||
align: 'center',
|
||||
slot: 'status',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status === 1 ? 'success' : row.status === 2 ? 'error' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.status === 1 ? '已审核' : row.status === 2 ? '已驳回' : '待审核'),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '申请时间',
|
||||
key: 'add_time',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '打款截图',
|
||||
slot: 'img',
|
||||
render: (row) => {
|
||||
return h(NImage, {
|
||||
src: row.status_img,
|
||||
width: '50',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render: (row) => {
|
||||
if (row.status === 3) {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'primary',
|
||||
onClick: () => {
|
||||
nowRow.value = row
|
||||
showModal.value = true
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '审核',
|
||||
}
|
||||
),
|
||||
vRole
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
class: 'ml-10',
|
||||
text: true,
|
||||
type: 'error',
|
||||
onClick: () => {
|
||||
nowRow.value = row
|
||||
refuse()
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '拒绝',
|
||||
}
|
||||
),
|
||||
vRole
|
||||
),
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
])
|
||||
const loading = ref(false)
|
||||
const data = ref([])
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
const query_data = {
|
||||
Status: queryData.value.status || '',
|
||||
Phone: queryData.value.word || '',
|
||||
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
|
||||
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
|
||||
}
|
||||
const res = await api.getData({
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
...query_data,
|
||||
})
|
||||
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
cardData.value.total = res.data.all
|
||||
cardData.value.service = res.data.audit_integral
|
||||
cardData.value.count = res.data.success_integral
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const clear = async () => {
|
||||
model.value = {
|
||||
img: [],
|
||||
}
|
||||
showModal.value = false
|
||||
formRef.value?.restoreValidation()
|
||||
await getList()
|
||||
}
|
||||
|
||||
const ok = () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
const res = await api.passAudit({
|
||||
bid: nowRow.value.bid,
|
||||
wid: nowRow.value.wid,
|
||||
img: model.value.img[0].url,
|
||||
status: 1,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const refuse = async () => {
|
||||
const res = await api.passAudit({
|
||||
bid: nowRow.value.bid,
|
||||
wid: nowRow.value.wid,
|
||||
img: model.value.img[0]?.url || '',
|
||||
status: 2,
|
||||
})
|
||||
clear()
|
||||
$message.success(res.msg)
|
||||
}
|
||||
|
||||
const clearQueryData = () => {
|
||||
queryData.value = {
|
||||
status: '',
|
||||
time: null,
|
||||
word: '',
|
||||
}
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
25
src/views/finance/route.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const Layout = () => import('@/layout/index.vue')
|
||||
|
||||
export default {
|
||||
name: '财务管理',
|
||||
path: '/finance',
|
||||
component: Layout,
|
||||
redirect: '/finance_list',
|
||||
meta: {
|
||||
title: '财务管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'financelist',
|
||||
path: 'finance_list',
|
||||
component: () => import('./index.vue'),
|
||||
meta: {
|
||||
title: '商户提现',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -1,8 +1,17 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getData: () => request.post('/dice/getisStart'),
|
||||
setStatus: (data) => request.post('/dice/isStart', data),
|
||||
getDS: () => request.post('/dice/getBetting'),
|
||||
setDS: (data) => request.post('/dice/setBetting', data),
|
||||
getData: () => request.post('/getisStart'),
|
||||
setStatus: (data) => request.post('/isStart', data),
|
||||
getDS: () => request.post('/getBetting'),
|
||||
setDS: (data) => request.post('/setBetting', data),
|
||||
getKJList: () => request.post('/draw'),
|
||||
// 获取统计
|
||||
getStatistics: (data) => request.post('/user/betting/list', data),
|
||||
|
||||
// log
|
||||
getLog: () => request.post('/log'),
|
||||
|
||||
// 宙斯详情
|
||||
getDetail: (data) => request.post('/log/betting/list', data),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<div flex>
|
||||
<div flex>
|
||||
<div flex items-center>
|
||||
<div mr-20 flex>
|
||||
<div>游戏状态:</div>
|
||||
<n-switch
|
||||
v-model:value="val1"
|
||||
@@ -10,7 +10,11 @@
|
||||
@update:value="handleUpdateValue1"
|
||||
/>
|
||||
</div>
|
||||
<div ml-20 flex>
|
||||
<div flex items-center>
|
||||
<div>开奖记录:</div>
|
||||
<n-button type="primary" @click="openData">预览</n-button>
|
||||
</div>
|
||||
<!-- <div ml-20 flex>
|
||||
<div>点杀状态:</div>
|
||||
<n-switch
|
||||
v-model:value="val"
|
||||
@@ -18,55 +22,119 @@
|
||||
:unchecked-value="2"
|
||||
@update:value="handleUpdateValue"
|
||||
/>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
|
||||
<div flex>
|
||||
<div>
|
||||
预开期数:
|
||||
<span text-25>{{ list[0]?.periods || 0 }}</span>
|
||||
<span text-25>{{ list[0]?.Periods || 0 }}</span>
|
||||
</div>
|
||||
<div ml-20>
|
||||
剩余开奖时间:
|
||||
<span text-25>{{ time || 0 }}</span>
|
||||
</div>
|
||||
<div ml-20>
|
||||
本局总下注:
|
||||
<span text-25>{{ totalA || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div flex flex-wrap justify-between>
|
||||
<n-spin size="large" :show="show">
|
||||
<div flex flex-wrap justify-between>
|
||||
<n-card
|
||||
v-for="item in list"
|
||||
:key="item.ID"
|
||||
class="mb-10 mt-10 h-120 w-250 flex-shrink-0 cursor-pointer"
|
||||
:title="item.name"
|
||||
>
|
||||
<p text-25 op-60 :style="{ color: item.count === 0 ? 'green' : 'red' }">
|
||||
{{ item.count }}
|
||||
</p>
|
||||
</n-card>
|
||||
<div h-0 w-250></div>
|
||||
<div h-0 w-250></div>
|
||||
</div>
|
||||
</n-spin>
|
||||
<n-spin size="large" :show="show1">
|
||||
<div flex flex-wrap justify-between>
|
||||
<n-card
|
||||
v-for="item in list1"
|
||||
:key="item.ID"
|
||||
class="mb-10 mt-10 h-150 w-250 flex-shrink-0 cursor-pointer"
|
||||
:title="`${item.NumName}(${item.Name})`"
|
||||
>
|
||||
<p text-25 op-60 :style="{ color: item.Total === 0 ? 'green' : 'red' }">
|
||||
{{ item.Total }}
|
||||
</p>
|
||||
<n-popconfirm @positive-click="handleUpdateValue(item.ID)">
|
||||
<template #trigger>
|
||||
<n-button>你猜</n-button>
|
||||
</template>
|
||||
一切都将一去杳然,任何人都无法将其捕获。
|
||||
</n-popconfirm>
|
||||
</n-card>
|
||||
<div h-0 w-250></div>
|
||||
<div h-0 w-250></div>
|
||||
<div h-0 w-250></div>
|
||||
<div h-0 w-250></div>
|
||||
</div>
|
||||
</n-spin>
|
||||
<!-- 开奖记录 -->
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
v-for="item in list"
|
||||
:key="item.ID"
|
||||
class="mb-10 mt-10 w-300 flex-shrink-0 cursor-pointer"
|
||||
:title="item.name"
|
||||
style="width: 900px"
|
||||
title="开奖记录"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<p text-25 op-60 :style="{ color: item.count === 0 ? 'green' : 'red' }">{{ item.count }}</p>
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:max-height="600"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
/>
|
||||
</n-card>
|
||||
<div h-0 w-300></div>
|
||||
<div h-0 w-300></div>
|
||||
</div>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h } from 'vue'
|
||||
import api from '../api'
|
||||
import { getToken } from '@/utils'
|
||||
|
||||
const ws = new WebSocket(`wss://${import.meta.env.VITE_WS_URL}`)
|
||||
|
||||
const ws1 = new WebSocket(`wss://${import.meta.env.VITE_WS1_URL}`)
|
||||
|
||||
const list = ref([])
|
||||
const list1 = ref([])
|
||||
|
||||
const val1 = ref(null)
|
||||
const val = ref(null)
|
||||
// const val = ref(null)
|
||||
|
||||
const time = ref(null)
|
||||
|
||||
const show = ref(true)
|
||||
const show1 = ref(true)
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('1连接成功')
|
||||
}
|
||||
|
||||
const totalA = ref(null)
|
||||
|
||||
ws.onmessage = (msg) => {
|
||||
const res = JSON.parse(msg.data)
|
||||
list.value = res
|
||||
list.value = res.betting.sort((a, b) => b.Total - a.Total)
|
||||
show.value = false
|
||||
list1.value = res.list.sort((a, b) => b.Total - a.Total)
|
||||
show1.value = false
|
||||
totalA.value = res.total
|
||||
}
|
||||
|
||||
ws1.onopen = () => {
|
||||
@@ -87,14 +155,14 @@ ws1.onmessage = (msg) => {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
get_data()
|
||||
// get_data()
|
||||
get_data1()
|
||||
})
|
||||
|
||||
const get_data = async () => {
|
||||
const res = await api.getDS()
|
||||
val.value = res.data.diceStatus
|
||||
}
|
||||
// const get_data = async () => {
|
||||
// const res = await api.getDS()
|
||||
// val.value = res.data.diceStatus
|
||||
// }
|
||||
const get_data1 = async () => {
|
||||
const res = await api.getData()
|
||||
val1.value = res.data.diceStart
|
||||
@@ -106,10 +174,13 @@ onBeforeUnmount(() => {
|
||||
|
||||
const handleUpdateValue = async (e) => {
|
||||
const res = await api.setDS({
|
||||
status: e,
|
||||
status: 1,
|
||||
id: e,
|
||||
user_id: getToken(),
|
||||
Periods: list.value[0]?.Periods,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
get_data()
|
||||
// get_data()
|
||||
}
|
||||
|
||||
const handleUpdateValue1 = async (e) => {
|
||||
@@ -119,6 +190,48 @@ const handleUpdateValue1 = async (e) => {
|
||||
$message.success(res.msg)
|
||||
get_data1()
|
||||
}
|
||||
|
||||
const showModal = ref(false)
|
||||
const loading = ref(false)
|
||||
const columns = ref([
|
||||
{
|
||||
title: '期数',
|
||||
key: 'Periods',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖号码',
|
||||
key: 'Name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖时间',
|
||||
key: 'DrawTime',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖号码',
|
||||
slot: 'Num',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h('p', `${row.Start}-${row.End}`)
|
||||
},
|
||||
},
|
||||
])
|
||||
const data = ref([])
|
||||
const openData = async () => {
|
||||
try {
|
||||
showModal.value = true
|
||||
loading.value = true
|
||||
const res = await api.getKJList()
|
||||
console.log(res)
|
||||
data.value = res.data.data
|
||||
loading.value = false
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -5,6 +5,11 @@ export default {
|
||||
path: '/game',
|
||||
component: Layout,
|
||||
redirect: '/game_data',
|
||||
meta: {
|
||||
title: '游戏管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Gamelist',
|
||||
@@ -12,6 +17,27 @@ export default {
|
||||
component: () => import('./data/index.vue'),
|
||||
meta: {
|
||||
title: '实时数据',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'statistics',
|
||||
path: 'game_statistics',
|
||||
component: () => import('./statistics/index.vue'),
|
||||
meta: {
|
||||
title: '数据统计',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'zs',
|
||||
path: 'game_zs',
|
||||
component: () => import('./zs/index.vue'),
|
||||
meta: {
|
||||
title: '宙斯统计',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
|
||||
250
src/views/game/statistics/index.vue
Normal file
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-grid class="mb-10" x-gap="12" :cols="4">
|
||||
<n-gi>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="range"
|
||||
value-format="yyyy-MM-dd"
|
||||
type="daterange"
|
||||
clearable
|
||||
/>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-button type="primary" @click="getList">搜索</n-button>
|
||||
<n-button ml-10 @click="clear">重置</n-button>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<!-- <n-grid class="mb-10" x-gap="12" :cols="3">
|
||||
<n-gi>
|
||||
<n-card>
|
||||
<n-statistic label="总下注">
|
||||
<n-number-animation :from="0" :to="TYVal.total" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-card>
|
||||
<n-statistic label="总赔付">
|
||||
<n-number-animation :from="0" :to="TYVal.totalNum" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
<n-gi>
|
||||
<n-card>
|
||||
<n-statistic label="总盈利">
|
||||
<n-number-animation :from="0" :to="TYVal.total / 10 - TYVal.totalNum / 100" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</n-gi>
|
||||
</n-grid> -->
|
||||
<div w-full flex items-center>
|
||||
<Echarts :loading="loading" :option="option" />
|
||||
<Echarts :loading="loading" :option="option1" />
|
||||
</div>
|
||||
<div w-full flex items-center justify-between>
|
||||
<n-card title="开奖记录" :bordered="false" content-style="padding: 0;">
|
||||
<n-data-table
|
||||
:max-height="500"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:bordered="true"
|
||||
:virtual-scroll="true"
|
||||
remote
|
||||
/>
|
||||
</n-card>
|
||||
</div>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import api from '../api.js'
|
||||
import Echarts from '@/components/Echarts.vue'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const range = ref(null)
|
||||
|
||||
const option = ref({
|
||||
title: {
|
||||
text: '单期下注(豆子)/赔付(积分) 统计',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
// legend: {
|
||||
// data: ['下注(豆子)', '赔付(积分)'],
|
||||
// left: 'left',
|
||||
// type: 'scroll',
|
||||
// },
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '下注(豆子)',
|
||||
data: [],
|
||||
type: 'bar',
|
||||
},
|
||||
{
|
||||
name: '赔付(积分)',
|
||||
data: [],
|
||||
type: 'bar',
|
||||
},
|
||||
],
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
},
|
||||
{
|
||||
type: 'slider',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const option1 = ref({
|
||||
title: {
|
||||
text: '总下注(豆子)/总赔付(积分)',
|
||||
left: 'center',
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'right',
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: [],
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const data = ref([])
|
||||
|
||||
const TYVal = ref({
|
||||
total: 0,
|
||||
totalNum: 0,
|
||||
})
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '期数',
|
||||
key: 'Periods',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖号码',
|
||||
key: 'Name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '下注',
|
||||
key: 'NumberSum',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '赔付',
|
||||
key: 'TotalCount',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'Date',
|
||||
align: 'center',
|
||||
},
|
||||
])
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const clear = () => {
|
||||
range.value = null
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
const dataObj = {
|
||||
StartTime: dayjs().format('YYYY-MM-DD'),
|
||||
EndTime: dayjs().format('YYYY-MM-DD'),
|
||||
}
|
||||
if (range.value) {
|
||||
dataObj.StartTime = range.value[0]
|
||||
dataObj.EndTime = range.value[1]
|
||||
}
|
||||
const res = await api.getStatistics(dataObj)
|
||||
const newData = res.data.data || []
|
||||
data.value = newData
|
||||
TYVal.value = {
|
||||
total: res.data.total,
|
||||
totalNum: res.data.totalDices,
|
||||
}
|
||||
option.value.xAxis.data = []
|
||||
option.value.series[0].data = []
|
||||
option.value.series[1].data = []
|
||||
option1.value.series[0].data = []
|
||||
if (newData.length > 0) {
|
||||
res.data.data.forEach((item) => {
|
||||
const a = (
|
||||
((res.data.total * 10) / (res.data.total * 10 + res.data.totalDices)) *
|
||||
100
|
||||
).toFixed(2)
|
||||
const b = ((res.data.totalDices / (res.data.total * 10 + res.data.totalDices)) * 100).toFixed(
|
||||
2
|
||||
)
|
||||
option.value.xAxis.data.push(`第${item.Periods}期-${item.Name}`)
|
||||
option.value.series[0].name = `下注(豆子): ${a}%`
|
||||
option.value.series[0].data.push(item.NumberSum)
|
||||
option.value.series[1].name = `赔付(积分): ${b}%`
|
||||
option.value.series[1].data.push(item.TotalCount)
|
||||
})
|
||||
|
||||
const a = (((res.data.total * 10) / (res.data.total * 10 + res.data.totalDices)) * 100).toFixed(
|
||||
2
|
||||
)
|
||||
const b = ((res.data.totalDices / (res.data.total * 10 + res.data.totalDices)) * 100).toFixed(2)
|
||||
|
||||
option1.value.series[0].data.push({ value: res.data.total, name: `总下注: ${a}%` })
|
||||
option1.value.series[0].data.push({
|
||||
value: res.data.totalDices,
|
||||
name: `总赔付: ${b}%`,
|
||||
})
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chart {
|
||||
width: 50%;
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
189
src/views/game/zs/index.vue
Normal file
@@ -0,0 +1,189 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-data-table
|
||||
:max-height="500"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:bordered="true"
|
||||
:virtual-scroll="true"
|
||||
:pagination="pagination"
|
||||
remote
|
||||
/>
|
||||
<!-- -->
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 800px"
|
||||
title="宙斯的眷顾"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-data-table
|
||||
:loading="zsLoading"
|
||||
:columns="zsColumns"
|
||||
:data="zsData"
|
||||
:pagination="zsPagination"
|
||||
:bordered="false"
|
||||
/>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from '../api.js'
|
||||
import { NButton } from 'naive-ui'
|
||||
import { h, ref, onMounted } from 'vue'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '期数',
|
||||
key: 'periods',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖号码',
|
||||
key: 'betting_name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖数字',
|
||||
key: 'betting_number',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作人',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '开奖时间',
|
||||
key: 'draw_time',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
key: 'add_time',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作IP',
|
||||
key: 'ip',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
strong: true,
|
||||
secondary: true,
|
||||
onClick: () => {
|
||||
openModal(row)
|
||||
},
|
||||
},
|
||||
{ default: () => '查看' }
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
getData()
|
||||
})
|
||||
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getData()
|
||||
},
|
||||
})
|
||||
|
||||
const getData = async () => {
|
||||
const res = await api.getLog({
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
}
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const zsLoading = ref(false)
|
||||
|
||||
const zsColumns = ref([
|
||||
{
|
||||
title: '期数',
|
||||
key: 'Periods',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '数字',
|
||||
key: 'PeriodsNum',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '总投注(豆子)',
|
||||
key: 'NumberSum',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '赔付(积分)',
|
||||
key: 'TotalCount',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '选中用户',
|
||||
key: 'User',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '电话号码',
|
||||
key: 'Phone',
|
||||
align: 'center',
|
||||
},
|
||||
])
|
||||
|
||||
const zsData = ref([])
|
||||
|
||||
const zsPagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
zsPagination.value.page = page
|
||||
getData()
|
||||
},
|
||||
})
|
||||
|
||||
const openModal = async (row) => {
|
||||
showModal.value = true
|
||||
zsLoading.value = true
|
||||
const res = await api.getDetail({
|
||||
periods: row.periods,
|
||||
draw_time: row.draw_time,
|
||||
pageNum: zsPagination.value.page,
|
||||
pageSize: zsPagination.value.pageSize,
|
||||
})
|
||||
zsData.value = res.data.data || []
|
||||
zsPagination.value.itemCount = res.data.total
|
||||
zsLoading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -100,8 +100,8 @@ async function handleLogin() {
|
||||
loading.value = true
|
||||
$message.loading('正在验证...')
|
||||
const res = await api.login({ phone: name, password: password.toString() })
|
||||
console.log(res.data.token)
|
||||
$message.success('登录成功')
|
||||
window.localStorage.setItem('menu', JSON.stringify(res.data.auth))
|
||||
setToken(res.data.token)
|
||||
if (isRemember.value) {
|
||||
lStorage.set('loginInfo', { name, password })
|
||||
@@ -117,7 +117,6 @@ async function handleLogin() {
|
||||
router.push('/')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
$message.removeMessage()
|
||||
}
|
||||
loading.value = false
|
||||
|
||||
@@ -1,25 +1,155 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-row gutter="12">
|
||||
<n-col :span="24">
|
||||
<div flex>
|
||||
<n-card w-500>
|
||||
<n-statistic label="订单流水(元)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.total" :precision="2" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<!-- <n-card ml-10 w-500>
|
||||
<n-statistic label="订单佣金(元)" tabular-nums>
|
||||
<n-number-animation :from="0" :to="Number(cardData.service)" :precision="2" />
|
||||
</n-statistic>
|
||||
</n-card> -->
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="订单数量" tabular-nums>
|
||||
<n-number-animation :from="0" :to="cardData.count" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24" mt-10>
|
||||
<div>
|
||||
<span>订单状态:</span>
|
||||
<n-radio-group v-model:value="queryData.status">
|
||||
<n-radio-button
|
||||
v-for="song in songs"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
:label="song.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10 flex items-center>
|
||||
<div w-100>关键字搜索:</div>
|
||||
<n-input-group>
|
||||
<n-select
|
||||
v-model:value="queryData.selectKey"
|
||||
:style="{ width: '15%' }"
|
||||
:options="selectOptions"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<n-input v-model:value="queryData.word" :style="{ width: '20%' }" />
|
||||
</n-input-group>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="10">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-100>订单时间:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryData.time"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10>
|
||||
<n-button type="primary" @click="getList">搜索</n-button>
|
||||
<n-button ml-10 @click="clear">重置</n-button>
|
||||
</div>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-data-table
|
||||
class="mt-5"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
:scroll-x="1800"
|
||||
remote
|
||||
/>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import { NEllipsis } from 'naive-ui'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const queryData = ref({
|
||||
status: '',
|
||||
time: null,
|
||||
word: '',
|
||||
selectKey: null,
|
||||
})
|
||||
|
||||
const cardData = ref({
|
||||
total: 0,
|
||||
service: 0,
|
||||
count: 0,
|
||||
})
|
||||
|
||||
const songs = ref([
|
||||
{
|
||||
value: 1,
|
||||
label: '待付款',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '待核销',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '已核销',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '已过期',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '已销售',
|
||||
},
|
||||
])
|
||||
|
||||
const selectOptions = ref([
|
||||
{
|
||||
value: 0,
|
||||
label: '商品名称',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '用户昵称',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '手机号',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '订单号',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '商家名称',
|
||||
},
|
||||
])
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
width: 200,
|
||||
fixed: 'left',
|
||||
},
|
||||
{
|
||||
title: '用户',
|
||||
@@ -27,15 +157,46 @@ const columns = ref([
|
||||
key: 'user_name',
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
title: '用户电话',
|
||||
align: 'center',
|
||||
key: 'goods_name',
|
||||
key: 'phone',
|
||||
},
|
||||
{
|
||||
title: '商品价格',
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
slot: 'goods_name',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: 'max-width: 240px',
|
||||
},
|
||||
{
|
||||
default: () => row.goods_name,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品数量',
|
||||
align: 'center',
|
||||
key: 'count',
|
||||
},
|
||||
{
|
||||
title: '订单总价',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '商家名称',
|
||||
align: 'center',
|
||||
key: 'store_name',
|
||||
},
|
||||
// {
|
||||
// title: '订单佣金(元)',
|
||||
// align: 'center',
|
||||
// key: 'commission_number',
|
||||
// },
|
||||
{
|
||||
title: '订单状态',
|
||||
align: 'center',
|
||||
@@ -45,20 +206,30 @@ const columns = ref([
|
||||
case 0:
|
||||
return h('span', '待付款')
|
||||
case 1:
|
||||
return h('span', '待使用')
|
||||
return h('span', '待核销')
|
||||
case 2:
|
||||
return h('span', '已完成')
|
||||
return h('span', '已核销')
|
||||
case 3:
|
||||
return h('span', '已过期')
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '下单时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
{
|
||||
title: '核销时间',
|
||||
align: 'center',
|
||||
key: 'cancel_time',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
console.log(row)
|
||||
render() {
|
||||
// console.log(row)
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -66,9 +237,9 @@ const columns = ref([
|
||||
const data = ref([])
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -87,17 +258,52 @@ onMounted(() => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const query_data = {
|
||||
Status: queryData.value.status || '',
|
||||
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
|
||||
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
|
||||
}
|
||||
switch (queryData.value.selectKey) {
|
||||
case 0:
|
||||
query_data['GoodsName'] = queryData.value.word
|
||||
break
|
||||
case 1:
|
||||
query_data['UserName'] = queryData.value.word
|
||||
break
|
||||
case 2:
|
||||
query_data['Phone'] = queryData.value.word
|
||||
break
|
||||
case 3:
|
||||
query_data['Oid'] = queryData.value.word
|
||||
break
|
||||
case 4:
|
||||
query_data['StoreName'] = queryData.value.word
|
||||
}
|
||||
const res = await api.getOrder({
|
||||
pageNum: pagination.value.current,
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
...query_data,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
cardData.value.total = res.data.number
|
||||
cardData.value.service = res.data.commission
|
||||
cardData.value.count = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
queryData.value = {
|
||||
status: '',
|
||||
time: null,
|
||||
word: '',
|
||||
selectKey: null,
|
||||
}
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,41 +1,200 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<!-- {{ queryData }} -->
|
||||
<n-row gutter="12">
|
||||
<n-col :span="24">
|
||||
<div flex>
|
||||
<n-card w-500>
|
||||
<n-statistic label="订单流水(积分)" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.total" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="订单佣金(积分)" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.service" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="订单数量" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.count" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10>
|
||||
<span>订单状态:</span>
|
||||
<n-radio-group v-model:value="queryData.status">
|
||||
<n-radio-button
|
||||
v-for="song in songs"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
:label="song.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10 flex items-center>
|
||||
<div w-100>订单搜索:</div>
|
||||
<n-input-group>
|
||||
<n-select
|
||||
v-model:value="queryData.selectKey"
|
||||
:style="{ width: '15%' }"
|
||||
:options="selectOptions"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<n-input v-model:value="queryData.word" :style="{ width: '20%' }" />
|
||||
</n-input-group>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="10">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-100>订单时间:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryData.time"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="24">
|
||||
<div mt-10>
|
||||
<n-button type="primary" @click="getList">搜索</n-button>
|
||||
<n-button ml-10 @click="clear">重置</n-button>
|
||||
</div>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-data-table
|
||||
class="mt-5"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from './api'
|
||||
import { NEllipsis } from 'naive-ui'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const cardData = ref({
|
||||
total: 0,
|
||||
service: 0,
|
||||
count: 0,
|
||||
})
|
||||
|
||||
const queryData = ref({
|
||||
status: '',
|
||||
time: null,
|
||||
word: '',
|
||||
selectKey: null,
|
||||
})
|
||||
|
||||
const songs = ref([
|
||||
{
|
||||
value: 1,
|
||||
label: '待付款',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '待核销',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '已核销',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '已过期',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '已销售',
|
||||
},
|
||||
])
|
||||
|
||||
const selectOptions = ref([
|
||||
{
|
||||
value: 0,
|
||||
label: '商品名称',
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
label: '用户昵称',
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '手机号',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '订单号',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '商家名称',
|
||||
},
|
||||
])
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '订单归属商户',
|
||||
align: 'center',
|
||||
key: 'store_name',
|
||||
},
|
||||
{
|
||||
title: '用户',
|
||||
align: 'center',
|
||||
key: 'user_name',
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
title: '用户电话',
|
||||
align: 'center',
|
||||
key: 'goods_name',
|
||||
key: 'phone',
|
||||
},
|
||||
{
|
||||
title: '商品价格',
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
slot: 'goods_name',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: 'max-width: 240px',
|
||||
},
|
||||
{
|
||||
default: () => row.goods_name,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品数量',
|
||||
align: 'center',
|
||||
key: 'count',
|
||||
},
|
||||
{
|
||||
title: '订单总价',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '订单佣金',
|
||||
align: 'center',
|
||||
key: 'commission',
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
align: 'center',
|
||||
@@ -45,30 +204,40 @@ const columns = ref([
|
||||
case 0:
|
||||
return h('span', '待付款')
|
||||
case 1:
|
||||
return h('span', '待使用')
|
||||
return h('span', '待核销')
|
||||
case 2:
|
||||
return h('span', '已完成')
|
||||
return h('span', '已核销')
|
||||
case 3:
|
||||
return h('span', '已过期')
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
title: '下单时间',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
console.log(row)
|
||||
},
|
||||
key: 'add_time',
|
||||
},
|
||||
{
|
||||
title: '核销时间',
|
||||
align: 'center',
|
||||
key: 'cancel_time',
|
||||
},
|
||||
// {
|
||||
// title: '操作',
|
||||
// align: 'center',
|
||||
// slot: 'action',
|
||||
// render(row) {
|
||||
// console.log(row)
|
||||
// },
|
||||
// },
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -87,17 +256,53 @@ onMounted(() => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const query_data = {
|
||||
Status: queryData.value.status || '',
|
||||
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
|
||||
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
|
||||
}
|
||||
switch (queryData.value.selectKey) {
|
||||
case 0:
|
||||
query_data['GoodsName'] = queryData.value.word
|
||||
break
|
||||
case 1:
|
||||
query_data['UserName'] = queryData.value.word
|
||||
break
|
||||
case 2:
|
||||
query_data['Phone'] = queryData.value.word
|
||||
break
|
||||
case 3:
|
||||
query_data['Oid'] = queryData.value.word
|
||||
break
|
||||
case 4:
|
||||
query_data['StoreName'] = queryData.value.word
|
||||
break
|
||||
}
|
||||
const res = await api.getPoint({
|
||||
pageNum: pagination.value.current,
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
...query_data,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
cardData.value.total = res.data.number
|
||||
cardData.value.service = res.data.commission
|
||||
cardData.value.count = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
queryData.value = {
|
||||
status: '',
|
||||
time: null,
|
||||
word: '',
|
||||
selectKey: null,
|
||||
}
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -5,6 +5,11 @@ export default {
|
||||
path: '/order',
|
||||
component: Layout,
|
||||
redirect: '/order_list',
|
||||
meta: {
|
||||
title: '订单管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Orderlist',
|
||||
|
||||
805
src/views/system/acc/acc_menu/ant-design-meta.json
Normal file
@@ -0,0 +1,805 @@
|
||||
{
|
||||
"name": "Ant Design Icons",
|
||||
"total": 789,
|
||||
"version": "4.3.1",
|
||||
"license": {
|
||||
"title": "MIT",
|
||||
"spdx": "MIT"
|
||||
},
|
||||
"samples": ["pushpin-filled", "pie-chart-outlined", "shopping-twotone"],
|
||||
"height": 16,
|
||||
"category": "General",
|
||||
"palette": false,
|
||||
"id": "ant-design",
|
||||
"icons": [
|
||||
"account-book-filled",
|
||||
"account-book-outlined",
|
||||
"account-book-twotone",
|
||||
"aim-outlined",
|
||||
"alert-filled",
|
||||
"alert-outlined",
|
||||
"alert-twotone",
|
||||
"alibaba-outlined",
|
||||
"align-center-outlined",
|
||||
"align-left-outlined",
|
||||
"align-right-outlined",
|
||||
"alipay-circle-filled",
|
||||
"alipay-circle-outlined",
|
||||
"alipay-outlined",
|
||||
"alipay-square-filled",
|
||||
"aliwangwang-filled",
|
||||
"aliwangwang-outlined",
|
||||
"aliyun-outlined",
|
||||
"amazon-circle-filled",
|
||||
"amazon-outlined",
|
||||
"amazon-square-filled",
|
||||
"android-filled",
|
||||
"android-outlined",
|
||||
"ant-cloud-outlined",
|
||||
"ant-design-outlined",
|
||||
"apartment-outlined",
|
||||
"api-filled",
|
||||
"api-outlined",
|
||||
"api-twotone",
|
||||
"apple-filled",
|
||||
"apple-outlined",
|
||||
"appstore-add-outlined",
|
||||
"appstore-filled",
|
||||
"appstore-outlined",
|
||||
"appstore-twotone",
|
||||
"area-chart-outlined",
|
||||
"arrow-down-outlined",
|
||||
"arrow-left-outlined",
|
||||
"arrow-right-outlined",
|
||||
"arrow-up-outlined",
|
||||
"arrows-alt-outlined",
|
||||
"audio-filled",
|
||||
"audio-muted-outlined",
|
||||
"audio-outlined",
|
||||
"audio-twotone",
|
||||
"audit-outlined",
|
||||
"backward-filled",
|
||||
"backward-outlined",
|
||||
"bank-filled",
|
||||
"bank-outlined",
|
||||
"bank-twotone",
|
||||
"bar-chart-outlined",
|
||||
"barcode-outlined",
|
||||
"bars-outlined",
|
||||
"behance-circle-filled",
|
||||
"behance-outlined",
|
||||
"behance-square-filled",
|
||||
"behance-square-outlined",
|
||||
"bell-filled",
|
||||
"bell-outlined",
|
||||
"bell-twotone",
|
||||
"bg-colors-outlined",
|
||||
"block-outlined",
|
||||
"bold-outlined",
|
||||
"book-filled",
|
||||
"book-outlined",
|
||||
"book-twotone",
|
||||
"border-bottom-outlined",
|
||||
"border-horizontal-outlined",
|
||||
"border-inner-outlined",
|
||||
"border-left-outlined",
|
||||
"border-outer-outlined",
|
||||
"border-outlined",
|
||||
"border-right-outlined",
|
||||
"border-top-outlined",
|
||||
"border-verticle-outlined",
|
||||
"borderless-table-outlined",
|
||||
"box-plot-filled",
|
||||
"box-plot-outlined",
|
||||
"box-plot-twotone",
|
||||
"branches-outlined",
|
||||
"bug-filled",
|
||||
"bug-outlined",
|
||||
"bug-twotone",
|
||||
"build-filled",
|
||||
"build-outlined",
|
||||
"build-twotone",
|
||||
"bulb-filled",
|
||||
"bulb-outlined",
|
||||
"bulb-twotone",
|
||||
"calculator-filled",
|
||||
"calculator-outlined",
|
||||
"calculator-twotone",
|
||||
"calendar-filled",
|
||||
"calendar-outlined",
|
||||
"calendar-twotone",
|
||||
"camera-filled",
|
||||
"camera-outlined",
|
||||
"camera-twotone",
|
||||
"car-filled",
|
||||
"car-outlined",
|
||||
"car-twotone",
|
||||
"caret-down-filled",
|
||||
"caret-down-outlined",
|
||||
"caret-left-filled",
|
||||
"caret-left-outlined",
|
||||
"caret-right-filled",
|
||||
"caret-right-outlined",
|
||||
"caret-up-filled",
|
||||
"caret-up-outlined",
|
||||
"carry-out-filled",
|
||||
"carry-out-outlined",
|
||||
"carry-out-twotone",
|
||||
"check-circle-filled",
|
||||
"check-circle-outlined",
|
||||
"check-circle-twotone",
|
||||
"check-outlined",
|
||||
"check-square-filled",
|
||||
"check-square-outlined",
|
||||
"check-square-twotone",
|
||||
"chrome-filled",
|
||||
"chrome-outlined",
|
||||
"ci-circle-filled",
|
||||
"ci-circle-outlined",
|
||||
"ci-circle-twotone",
|
||||
"ci-outlined",
|
||||
"ci-twotone",
|
||||
"clear-outlined",
|
||||
"clock-circle-filled",
|
||||
"clock-circle-outlined",
|
||||
"clock-circle-twotone",
|
||||
"close-circle-filled",
|
||||
"close-circle-outlined",
|
||||
"close-circle-twotone",
|
||||
"close-outlined",
|
||||
"close-square-filled",
|
||||
"close-square-outlined",
|
||||
"close-square-twotone",
|
||||
"cloud-download-outlined",
|
||||
"cloud-filled",
|
||||
"cloud-outlined",
|
||||
"cloud-server-outlined",
|
||||
"cloud-sync-outlined",
|
||||
"cloud-twotone",
|
||||
"cloud-upload-outlined",
|
||||
"cluster-outlined",
|
||||
"code-filled",
|
||||
"code-outlined",
|
||||
"code-sandbox-circle-filled",
|
||||
"code-sandbox-outlined",
|
||||
"code-sandbox-square-filled",
|
||||
"code-twotone",
|
||||
"codepen-circle-filled",
|
||||
"codepen-circle-outlined",
|
||||
"codepen-outlined",
|
||||
"codepen-square-filled",
|
||||
"coffee-outlined",
|
||||
"column-height-outlined",
|
||||
"column-width-outlined",
|
||||
"comment-outlined",
|
||||
"compass-filled",
|
||||
"compass-outlined",
|
||||
"compass-twotone",
|
||||
"compress-outlined",
|
||||
"console-sql-outlined",
|
||||
"contacts-filled",
|
||||
"contacts-outlined",
|
||||
"contacts-twotone",
|
||||
"container-filled",
|
||||
"container-outlined",
|
||||
"container-twotone",
|
||||
"control-filled",
|
||||
"control-outlined",
|
||||
"control-twotone",
|
||||
"copy-filled",
|
||||
"copy-outlined",
|
||||
"copy-twotone",
|
||||
"copyright-circle-filled",
|
||||
"copyright-circle-outlined",
|
||||
"copyright-circle-twotone",
|
||||
"copyright-outlined",
|
||||
"copyright-twotone",
|
||||
"credit-card-filled",
|
||||
"credit-card-outlined",
|
||||
"credit-card-twotone",
|
||||
"crown-filled",
|
||||
"crown-outlined",
|
||||
"crown-twotone",
|
||||
"customer-service-filled",
|
||||
"customer-service-outlined",
|
||||
"customer-service-twotone",
|
||||
"dash-outlined",
|
||||
"dashboard-filled",
|
||||
"dashboard-outlined",
|
||||
"dashboard-twotone",
|
||||
"database-filled",
|
||||
"database-outlined",
|
||||
"database-twotone",
|
||||
"delete-column-outlined",
|
||||
"delete-filled",
|
||||
"delete-outlined",
|
||||
"delete-row-outlined",
|
||||
"delete-twotone",
|
||||
"delivered-procedure-outlined",
|
||||
"deployment-unit-outlined",
|
||||
"desktop-outlined",
|
||||
"diff-filled",
|
||||
"diff-outlined",
|
||||
"diff-twotone",
|
||||
"dingding-outlined",
|
||||
"dingtalk-circle-filled",
|
||||
"dingtalk-outlined",
|
||||
"dingtalk-square-filled",
|
||||
"disconnect-outlined",
|
||||
"dislike-filled",
|
||||
"dislike-outlined",
|
||||
"dislike-twotone",
|
||||
"dollar-circle-filled",
|
||||
"dollar-circle-outlined",
|
||||
"dollar-circle-twotone",
|
||||
"dollar-outlined",
|
||||
"dollar-twotone",
|
||||
"dot-chart-outlined",
|
||||
"double-left-outlined",
|
||||
"double-right-outlined",
|
||||
"down-circle-filled",
|
||||
"down-circle-outlined",
|
||||
"down-circle-twotone",
|
||||
"down-outlined",
|
||||
"down-square-filled",
|
||||
"down-square-outlined",
|
||||
"down-square-twotone",
|
||||
"download-outlined",
|
||||
"drag-outlined",
|
||||
"dribbble-circle-filled",
|
||||
"dribbble-outlined",
|
||||
"dribbble-square-filled",
|
||||
"dribbble-square-outlined",
|
||||
"dropbox-circle-filled",
|
||||
"dropbox-outlined",
|
||||
"dropbox-square-filled",
|
||||
"edit-filled",
|
||||
"edit-outlined",
|
||||
"edit-twotone",
|
||||
"ellipsis-outlined",
|
||||
"enter-outlined",
|
||||
"environment-filled",
|
||||
"environment-outlined",
|
||||
"environment-twotone",
|
||||
"euro-circle-filled",
|
||||
"euro-circle-outlined",
|
||||
"euro-circle-twotone",
|
||||
"euro-outlined",
|
||||
"euro-twotone",
|
||||
"exception-outlined",
|
||||
"exclamation-circle-filled",
|
||||
"exclamation-circle-outlined",
|
||||
"exclamation-circle-twotone",
|
||||
"exclamation-outlined",
|
||||
"expand-alt-outlined",
|
||||
"expand-outlined",
|
||||
"experiment-filled",
|
||||
"experiment-outlined",
|
||||
"experiment-twotone",
|
||||
"export-outlined",
|
||||
"eye-filled",
|
||||
"eye-invisible-filled",
|
||||
"eye-invisible-outlined",
|
||||
"eye-invisible-twotone",
|
||||
"eye-outlined",
|
||||
"eye-twotone",
|
||||
"facebook-filled",
|
||||
"facebook-outlined",
|
||||
"fall-outlined",
|
||||
"fast-backward-filled",
|
||||
"fast-backward-outlined",
|
||||
"fast-forward-filled",
|
||||
"fast-forward-outlined",
|
||||
"field-binary-outlined",
|
||||
"field-number-outlined",
|
||||
"field-string-outlined",
|
||||
"field-time-outlined",
|
||||
"file-add-filled",
|
||||
"file-add-outlined",
|
||||
"file-add-twotone",
|
||||
"file-done-outlined",
|
||||
"file-excel-filled",
|
||||
"file-excel-outlined",
|
||||
"file-excel-twotone",
|
||||
"file-exclamation-filled",
|
||||
"file-exclamation-outlined",
|
||||
"file-exclamation-twotone",
|
||||
"file-filled",
|
||||
"file-gif-outlined",
|
||||
"file-image-filled",
|
||||
"file-image-outlined",
|
||||
"file-image-twotone",
|
||||
"file-jpg-outlined",
|
||||
"file-markdown-filled",
|
||||
"file-markdown-outlined",
|
||||
"file-markdown-twotone",
|
||||
"file-outlined",
|
||||
"file-pdf-filled",
|
||||
"file-pdf-outlined",
|
||||
"file-pdf-twotone",
|
||||
"file-ppt-filled",
|
||||
"file-ppt-outlined",
|
||||
"file-ppt-twotone",
|
||||
"file-protect-outlined",
|
||||
"file-search-outlined",
|
||||
"file-sync-outlined",
|
||||
"file-text-filled",
|
||||
"file-text-outlined",
|
||||
"file-text-twotone",
|
||||
"file-twotone",
|
||||
"file-unknown-filled",
|
||||
"file-unknown-outlined",
|
||||
"file-unknown-twotone",
|
||||
"file-word-filled",
|
||||
"file-word-outlined",
|
||||
"file-word-twotone",
|
||||
"file-zip-filled",
|
||||
"file-zip-outlined",
|
||||
"file-zip-twotone",
|
||||
"filter-filled",
|
||||
"filter-outlined",
|
||||
"filter-twotone",
|
||||
"fire-filled",
|
||||
"fire-outlined",
|
||||
"fire-twotone",
|
||||
"flag-filled",
|
||||
"flag-outlined",
|
||||
"flag-twotone",
|
||||
"folder-add-filled",
|
||||
"folder-add-outlined",
|
||||
"folder-add-twotone",
|
||||
"folder-filled",
|
||||
"folder-open-filled",
|
||||
"folder-open-outlined",
|
||||
"folder-open-twotone",
|
||||
"folder-outlined",
|
||||
"folder-twotone",
|
||||
"folder-view-outlined",
|
||||
"font-colors-outlined",
|
||||
"font-size-outlined",
|
||||
"fork-outlined",
|
||||
"form-outlined",
|
||||
"format-painter-filled",
|
||||
"format-painter-outlined",
|
||||
"forward-filled",
|
||||
"forward-outlined",
|
||||
"frown-filled",
|
||||
"frown-outlined",
|
||||
"frown-twotone",
|
||||
"fullscreen-exit-outlined",
|
||||
"fullscreen-outlined",
|
||||
"function-outlined",
|
||||
"fund-filled",
|
||||
"fund-outlined",
|
||||
"fund-projection-screen-outlined",
|
||||
"fund-twotone",
|
||||
"fund-view-outlined",
|
||||
"funnel-plot-filled",
|
||||
"funnel-plot-outlined",
|
||||
"funnel-plot-twotone",
|
||||
"gateway-outlined",
|
||||
"gif-outlined",
|
||||
"gift-filled",
|
||||
"gift-outlined",
|
||||
"gift-twotone",
|
||||
"github-filled",
|
||||
"github-outlined",
|
||||
"gitlab-filled",
|
||||
"gitlab-outlined",
|
||||
"global-outlined",
|
||||
"gold-filled",
|
||||
"gold-outlined",
|
||||
"gold-twotone",
|
||||
"golden-filled",
|
||||
"google-circle-filled",
|
||||
"google-outlined",
|
||||
"google-plus-circle-filled",
|
||||
"google-plus-outlined",
|
||||
"google-plus-square-filled",
|
||||
"google-square-filled",
|
||||
"group-outlined",
|
||||
"hdd-filled",
|
||||
"hdd-outlined",
|
||||
"hdd-twotone",
|
||||
"heart-filled",
|
||||
"heart-outlined",
|
||||
"heart-twotone",
|
||||
"heat-map-outlined",
|
||||
"highlight-filled",
|
||||
"highlight-outlined",
|
||||
"highlight-twotone",
|
||||
"history-outlined",
|
||||
"holder-outlined",
|
||||
"home-filled",
|
||||
"home-outlined",
|
||||
"home-twotone",
|
||||
"hourglass-filled",
|
||||
"hourglass-outlined",
|
||||
"hourglass-twotone",
|
||||
"html5-filled",
|
||||
"html5-outlined",
|
||||
"html5-twotone",
|
||||
"idcard-filled",
|
||||
"idcard-outlined",
|
||||
"idcard-twotone",
|
||||
"ie-circle-filled",
|
||||
"ie-outlined",
|
||||
"ie-square-filled",
|
||||
"import-outlined",
|
||||
"inbox-outlined",
|
||||
"info-circle-filled",
|
||||
"info-circle-outlined",
|
||||
"info-circle-twotone",
|
||||
"info-outlined",
|
||||
"insert-row-above-outlined",
|
||||
"insert-row-below-outlined",
|
||||
"insert-row-left-outlined",
|
||||
"insert-row-right-outlined",
|
||||
"instagram-filled",
|
||||
"instagram-outlined",
|
||||
"insurance-filled",
|
||||
"insurance-outlined",
|
||||
"insurance-twotone",
|
||||
"interaction-filled",
|
||||
"interaction-outlined",
|
||||
"interaction-twotone",
|
||||
"issues-close-outlined",
|
||||
"italic-outlined",
|
||||
"key-outlined",
|
||||
"laptop-outlined",
|
||||
"layout-filled",
|
||||
"layout-outlined",
|
||||
"layout-twotone",
|
||||
"left-circle-filled",
|
||||
"left-circle-outlined",
|
||||
"left-circle-twotone",
|
||||
"left-outlined",
|
||||
"left-square-filled",
|
||||
"left-square-outlined",
|
||||
"left-square-twotone",
|
||||
"like-filled",
|
||||
"like-outlined",
|
||||
"like-twotone",
|
||||
"line-chart-outlined",
|
||||
"line-height-outlined",
|
||||
"line-outlined",
|
||||
"link-outlined",
|
||||
"linkedin-filled",
|
||||
"linkedin-outlined",
|
||||
"loading-3-quarters-outlined",
|
||||
"loading-outlined",
|
||||
"lock-filled",
|
||||
"lock-outlined",
|
||||
"lock-twotone",
|
||||
"login-outlined",
|
||||
"logout-outlined",
|
||||
"mac-command-filled",
|
||||
"mac-command-outlined",
|
||||
"mail-filled",
|
||||
"mail-outlined",
|
||||
"mail-twotone",
|
||||
"man-outlined",
|
||||
"medicine-box-filled",
|
||||
"medicine-box-outlined",
|
||||
"medicine-box-twotone",
|
||||
"medium-circle-filled",
|
||||
"medium-outlined",
|
||||
"medium-square-filled",
|
||||
"medium-workmark-outlined",
|
||||
"meh-filled",
|
||||
"meh-outlined",
|
||||
"meh-twotone",
|
||||
"menu-fold-outlined",
|
||||
"menu-outlined",
|
||||
"menu-unfold-outlined",
|
||||
"merge-cells-outlined",
|
||||
"message-filled",
|
||||
"message-outlined",
|
||||
"message-twotone",
|
||||
"minus-circle-filled",
|
||||
"minus-circle-outlined",
|
||||
"minus-circle-twotone",
|
||||
"minus-outlined",
|
||||
"minus-square-filled",
|
||||
"minus-square-outlined",
|
||||
"minus-square-twotone",
|
||||
"mobile-filled",
|
||||
"mobile-outlined",
|
||||
"mobile-twotone",
|
||||
"money-collect-filled",
|
||||
"money-collect-outlined",
|
||||
"money-collect-twotone",
|
||||
"monitor-outlined",
|
||||
"more-outlined",
|
||||
"node-collapse-outlined",
|
||||
"node-expand-outlined",
|
||||
"node-index-outlined",
|
||||
"notification-filled",
|
||||
"notification-outlined",
|
||||
"notification-twotone",
|
||||
"number-outlined",
|
||||
"one-to-one-outlined",
|
||||
"ordered-list-outlined",
|
||||
"paper-clip-outlined",
|
||||
"partition-outlined",
|
||||
"pause-circle-filled",
|
||||
"pause-circle-outlined",
|
||||
"pause-circle-twotone",
|
||||
"pause-outlined",
|
||||
"pay-circle-filled",
|
||||
"pay-circle-outlined",
|
||||
"percentage-outlined",
|
||||
"phone-filled",
|
||||
"phone-outlined",
|
||||
"phone-twotone",
|
||||
"pic-center-outlined",
|
||||
"pic-left-outlined",
|
||||
"pic-right-outlined",
|
||||
"picture-filled",
|
||||
"picture-outlined",
|
||||
"picture-twotone",
|
||||
"pie-chart-filled",
|
||||
"pie-chart-outlined",
|
||||
"pie-chart-twotone",
|
||||
"play-circle-filled",
|
||||
"play-circle-outlined",
|
||||
"play-circle-twotone",
|
||||
"play-square-filled",
|
||||
"play-square-outlined",
|
||||
"play-square-twotone",
|
||||
"plus-circle-filled",
|
||||
"plus-circle-outlined",
|
||||
"plus-circle-twotone",
|
||||
"plus-outlined",
|
||||
"plus-square-filled",
|
||||
"plus-square-outlined",
|
||||
"plus-square-twotone",
|
||||
"pound-circle-filled",
|
||||
"pound-circle-outlined",
|
||||
"pound-circle-twotone",
|
||||
"pound-outlined",
|
||||
"poweroff-outlined",
|
||||
"printer-filled",
|
||||
"printer-outlined",
|
||||
"printer-twotone",
|
||||
"profile-filled",
|
||||
"profile-outlined",
|
||||
"profile-twotone",
|
||||
"project-filled",
|
||||
"project-outlined",
|
||||
"project-twotone",
|
||||
"property-safety-filled",
|
||||
"property-safety-outlined",
|
||||
"property-safety-twotone",
|
||||
"pull-request-outlined",
|
||||
"pushpin-filled",
|
||||
"pushpin-outlined",
|
||||
"pushpin-twotone",
|
||||
"qq-circle-filled",
|
||||
"qq-outlined",
|
||||
"qq-square-filled",
|
||||
"qrcode-outlined",
|
||||
"question-circle-filled",
|
||||
"question-circle-outlined",
|
||||
"question-circle-twotone",
|
||||
"question-outlined",
|
||||
"radar-chart-outlined",
|
||||
"radius-bottomleft-outlined",
|
||||
"radius-bottomright-outlined",
|
||||
"radius-setting-outlined",
|
||||
"radius-upleft-outlined",
|
||||
"radius-upright-outlined",
|
||||
"read-filled",
|
||||
"read-outlined",
|
||||
"reconciliation-filled",
|
||||
"reconciliation-outlined",
|
||||
"reconciliation-twotone",
|
||||
"red-envelope-filled",
|
||||
"red-envelope-outlined",
|
||||
"red-envelope-twotone",
|
||||
"reddit-circle-filled",
|
||||
"reddit-outlined",
|
||||
"reddit-square-filled",
|
||||
"redo-outlined",
|
||||
"reload-outlined",
|
||||
"rest-filled",
|
||||
"rest-outlined",
|
||||
"rest-twotone",
|
||||
"retweet-outlined",
|
||||
"right-circle-filled",
|
||||
"right-circle-outlined",
|
||||
"right-circle-twotone",
|
||||
"right-outlined",
|
||||
"right-square-filled",
|
||||
"right-square-outlined",
|
||||
"right-square-twotone",
|
||||
"rise-outlined",
|
||||
"robot-filled",
|
||||
"robot-outlined",
|
||||
"rocket-filled",
|
||||
"rocket-outlined",
|
||||
"rocket-twotone",
|
||||
"rollback-outlined",
|
||||
"rotate-left-outlined",
|
||||
"rotate-right-outlined",
|
||||
"safety-certificate-filled",
|
||||
"safety-certificate-outlined",
|
||||
"safety-certificate-twotone",
|
||||
"safety-outlined",
|
||||
"save-filled",
|
||||
"save-outlined",
|
||||
"save-twotone",
|
||||
"scan-outlined",
|
||||
"schedule-filled",
|
||||
"schedule-outlined",
|
||||
"schedule-twotone",
|
||||
"scissor-outlined",
|
||||
"search-outlined",
|
||||
"security-scan-filled",
|
||||
"security-scan-outlined",
|
||||
"security-scan-twotone",
|
||||
"select-outlined",
|
||||
"send-outlined",
|
||||
"setting-filled",
|
||||
"setting-outlined",
|
||||
"setting-twotone",
|
||||
"shake-outlined",
|
||||
"share-alt-outlined",
|
||||
"shop-filled",
|
||||
"shop-outlined",
|
||||
"shop-twotone",
|
||||
"shopping-cart-outlined",
|
||||
"shopping-filled",
|
||||
"shopping-outlined",
|
||||
"shopping-twotone",
|
||||
"shrink-outlined",
|
||||
"signal-filled",
|
||||
"sisternode-outlined",
|
||||
"sketch-circle-filled",
|
||||
"sketch-outlined",
|
||||
"sketch-square-filled",
|
||||
"skin-filled",
|
||||
"skin-outlined",
|
||||
"skin-twotone",
|
||||
"skype-filled",
|
||||
"skype-outlined",
|
||||
"slack-circle-filled",
|
||||
"slack-outlined",
|
||||
"slack-square-filled",
|
||||
"slack-square-outlined",
|
||||
"sliders-filled",
|
||||
"sliders-outlined",
|
||||
"sliders-twotone",
|
||||
"small-dash-outlined",
|
||||
"smile-filled",
|
||||
"smile-outlined",
|
||||
"smile-twotone",
|
||||
"snippets-filled",
|
||||
"snippets-outlined",
|
||||
"snippets-twotone",
|
||||
"solution-outlined",
|
||||
"sort-ascending-outlined",
|
||||
"sort-descending-outlined",
|
||||
"sound-filled",
|
||||
"sound-outlined",
|
||||
"sound-twotone",
|
||||
"split-cells-outlined",
|
||||
"star-filled",
|
||||
"star-outlined",
|
||||
"star-twotone",
|
||||
"step-backward-filled",
|
||||
"step-backward-outlined",
|
||||
"step-forward-filled",
|
||||
"step-forward-outlined",
|
||||
"stock-outlined",
|
||||
"stop-filled",
|
||||
"stop-outlined",
|
||||
"stop-twotone",
|
||||
"strikethrough-outlined",
|
||||
"subnode-outlined",
|
||||
"swap-left-outlined",
|
||||
"swap-outlined",
|
||||
"swap-right-outlined",
|
||||
"switcher-filled",
|
||||
"switcher-outlined",
|
||||
"switcher-twotone",
|
||||
"sync-outlined",
|
||||
"table-outlined",
|
||||
"tablet-filled",
|
||||
"tablet-outlined",
|
||||
"tablet-twotone",
|
||||
"tag-filled",
|
||||
"tag-outlined",
|
||||
"tag-twotone",
|
||||
"tags-filled",
|
||||
"tags-outlined",
|
||||
"tags-twotone",
|
||||
"taobao-circle-filled",
|
||||
"taobao-circle-outlined",
|
||||
"taobao-outlined",
|
||||
"taobao-square-filled",
|
||||
"team-outlined",
|
||||
"thunderbolt-filled",
|
||||
"thunderbolt-outlined",
|
||||
"thunderbolt-twotone",
|
||||
"to-top-outlined",
|
||||
"tool-filled",
|
||||
"tool-outlined",
|
||||
"tool-twotone",
|
||||
"trademark-circle-filled",
|
||||
"trademark-circle-outlined",
|
||||
"trademark-circle-twotone",
|
||||
"trademark-outlined",
|
||||
"transaction-outlined",
|
||||
"translation-outlined",
|
||||
"trophy-filled",
|
||||
"trophy-outlined",
|
||||
"trophy-twotone",
|
||||
"twitter-circle-filled",
|
||||
"twitter-outlined",
|
||||
"twitter-square-filled",
|
||||
"underline-outlined",
|
||||
"undo-outlined",
|
||||
"ungroup-outlined",
|
||||
"unlock-filled",
|
||||
"unlock-outlined",
|
||||
"unlock-twotone",
|
||||
"unordered-list-outlined",
|
||||
"up-circle-filled",
|
||||
"up-circle-outlined",
|
||||
"up-circle-twotone",
|
||||
"up-outlined",
|
||||
"up-square-filled",
|
||||
"up-square-outlined",
|
||||
"up-square-twotone",
|
||||
"upload-outlined",
|
||||
"usb-filled",
|
||||
"usb-outlined",
|
||||
"usb-twotone",
|
||||
"user-add-outlined",
|
||||
"user-delete-outlined",
|
||||
"user-outlined",
|
||||
"user-switch-outlined",
|
||||
"usergroup-add-outlined",
|
||||
"usergroup-delete-outlined",
|
||||
"verified-outlined",
|
||||
"vertical-align-bottom-outlined",
|
||||
"vertical-align-middle-outlined",
|
||||
"vertical-align-top-outlined",
|
||||
"vertical-left-outlined",
|
||||
"vertical-right-outlined",
|
||||
"video-camera-add-outlined",
|
||||
"video-camera-filled",
|
||||
"video-camera-outlined",
|
||||
"video-camera-twotone",
|
||||
"wallet-filled",
|
||||
"wallet-outlined",
|
||||
"wallet-twotone",
|
||||
"warning-filled",
|
||||
"warning-outlined",
|
||||
"warning-twotone",
|
||||
"wechat-filled",
|
||||
"wechat-outlined",
|
||||
"weibo-circle-filled",
|
||||
"weibo-circle-outlined",
|
||||
"weibo-outlined",
|
||||
"weibo-square-filled",
|
||||
"weibo-square-outlined",
|
||||
"whats-app-outlined",
|
||||
"wifi-outlined",
|
||||
"windows-filled",
|
||||
"windows-outlined",
|
||||
"woman-outlined",
|
||||
"yahoo-filled",
|
||||
"yahoo-outlined",
|
||||
"youtube-filled",
|
||||
"youtube-outlined",
|
||||
"yuque-filled",
|
||||
"yuque-outlined",
|
||||
"zhihu-circle-filled",
|
||||
"zhihu-outlined",
|
||||
"zhihu-square-filled",
|
||||
"zoom-in-outlined",
|
||||
"zoom-out-outlined"
|
||||
]
|
||||
}
|
||||
431
src/views/system/acc/acc_menu/index.vue
Normal file
@@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="'(非技术人员勿动)'">
|
||||
<n-button v-perms="['/admin/auth/menu/set']" type="primary" @click="openModal(1)">
|
||||
添加菜单
|
||||
</n-button>
|
||||
<n-data-table
|
||||
class="mt-5"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
remote
|
||||
:row-key="rowKey"
|
||||
children-key="subMenu"
|
||||
/>
|
||||
<!-- 添加菜单 -->
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 500px"
|
||||
title="添加/编辑菜单"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<!-- {{ model }} -->
|
||||
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
|
||||
<n-grid :cols="24" :x-gap="24">
|
||||
<n-form-item-gi :span="16" label="父级分类:" path="pid">
|
||||
<n-cascader
|
||||
v-model:value="model.pid"
|
||||
placeholder="请选择菜单目录"
|
||||
expand-trigger="click"
|
||||
:options="[
|
||||
{
|
||||
ID: 0,
|
||||
name: '顶层菜单',
|
||||
pid: 0,
|
||||
},
|
||||
...data,
|
||||
]"
|
||||
check-strategy="all"
|
||||
show-path
|
||||
label-field="name"
|
||||
value-field="ID"
|
||||
children-field="subMenu"
|
||||
separator="->"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="权限类型:" path="type">
|
||||
<n-select
|
||||
v-model:value="model.type"
|
||||
placeholder="请选择菜单类型"
|
||||
:options="[
|
||||
{
|
||||
label: '目录',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '菜单',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '按钮',
|
||||
value: 3,
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="model.type !== 3" :span="24" label="菜单图标:" path="icon">
|
||||
<div>
|
||||
<n-input-group flex items-center>
|
||||
<TheIcon :icon="model.icon" :size="30" />
|
||||
<n-input
|
||||
v-model:value="model.icon"
|
||||
placeholder="请选择菜单图标"
|
||||
:style="{ width: '100%' }"
|
||||
/>
|
||||
<n-button type="primary" ghost @click="iconModal = true">+</n-button>
|
||||
</n-input-group>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="model.type !== 3" :span="10" label="是否开启:" path="status">
|
||||
<n-switch v-model:value="model.status" :checked-value="1" :unchecked-value="2" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="model.type !== 3" :span="10" label="是否显示:" path="isShow">
|
||||
<n-switch v-model:value="model.isShow" :checked-value="1" :unchecked-value="2" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="菜单名称:" path="name">
|
||||
<n-input v-model:value="model.name" placeholder="请填写菜单名称" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi
|
||||
v-if="model.type === 2 || model.type === 1"
|
||||
:span="16"
|
||||
label="菜单路径:"
|
||||
path="route"
|
||||
>
|
||||
<n-input v-model:value="model.route" placeholder="请填写菜单路径" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi
|
||||
v-if="model.type === 3 || model.type === 2"
|
||||
:span="16"
|
||||
label="权限标识:"
|
||||
path="api_route"
|
||||
>
|
||||
<n-input v-model:value="model.api_route" placeholder="请填写权限标识" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="model.type === 2" :span="16" label="组件路径:" path="components">
|
||||
<n-input v-model:value="model.components" placeholder="请填写组件路径" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="model.type === 2" :span="16" label="菜单参数:" path="params">
|
||||
<n-input v-model:value="model.params" placeholder="请填写菜单参数" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="菜单排序:" path="sort">
|
||||
<n-input-number v-model:value="model.sort" :min="0" placeholder="越低越靠前" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="18">
|
||||
<div m-auto>
|
||||
<n-button type="primary" @click="handleValidateClick">保存</n-button>
|
||||
<n-button ml-10 @click="clear">取消</n-button>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<!-- 图标 -->
|
||||
<n-modal v-model:show="iconModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="选择菜单图标"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div h-500 w-full flex flex-wrap overflow-auto>
|
||||
<div
|
||||
v-for="(item, index) in antIconList.icons"
|
||||
:key="index"
|
||||
@click="clickIcon(`ant-design:${item}`)"
|
||||
>
|
||||
<TheIcon :icon="`ant-design:${item}`" :size="40" />
|
||||
</div>
|
||||
</div>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
import api from '../api'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
import { NTag, NButton } from 'naive-ui'
|
||||
import antIconList from './ant-design-meta.json'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const showModal = ref(false)
|
||||
const iconModal = ref(false)
|
||||
|
||||
const rowKey = (row) => {
|
||||
return row.subMenu || []
|
||||
}
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '菜单名称',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '菜单图标',
|
||||
slot: 'icon',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(TheIcon, {
|
||||
icon: row.icon,
|
||||
size: 20,
|
||||
}),
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
slot: 'type',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
'span',
|
||||
{},
|
||||
{
|
||||
default: () => (row.type === 1 ? '目录' : row.type === 2 ? '菜单' : '按钮'),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '路径',
|
||||
key: 'route',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '权限标识',
|
||||
key: 'api_route',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '路由参数',
|
||||
key: 'params',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '组件路径',
|
||||
key: 'components',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '排序',
|
||||
key: 'sort',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '是否启用',
|
||||
solt: 'status',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status === 1 ? 'success' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.status === 1 ? '启用' : '禁用'),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '是否显示',
|
||||
solt: 'status',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
NTag,
|
||||
{
|
||||
type: row.is_show === 1 ? 'success' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.is_show === 1 ? '显示' : '隐藏'),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
onClick: () => {
|
||||
openModal(2, row)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '编辑',
|
||||
}
|
||||
),
|
||||
[[vPerms, ['/admin/auth/menu/set']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
const res = await api.getMenuList()
|
||||
data.value = res.data.data || []
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const model = ref({
|
||||
id: null,
|
||||
pid: null,
|
||||
type: null,
|
||||
icon: '',
|
||||
status: 1,
|
||||
isShow: 1,
|
||||
name: '',
|
||||
route: '',
|
||||
params: '',
|
||||
sort: 0,
|
||||
components: '',
|
||||
api_route: '',
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const rules = {
|
||||
pid: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择菜单目录',
|
||||
trigger: 'change',
|
||||
},
|
||||
type: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择菜单类型',
|
||||
trigger: 'change',
|
||||
},
|
||||
name: {
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
route: {
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请填写菜单路径',
|
||||
trigger: 'blur',
|
||||
},
|
||||
components: {
|
||||
required: true,
|
||||
message: '请填写组件路径',
|
||||
trigger: 'blur',
|
||||
},
|
||||
}
|
||||
|
||||
const openModal = (type, row = {}) => {
|
||||
// get_dir_list()
|
||||
|
||||
if (type === 2) {
|
||||
model.value = {
|
||||
id: row.ID,
|
||||
type: row.type,
|
||||
icon: row.icon,
|
||||
status: row.status,
|
||||
isShow: row.isShow,
|
||||
name: row.name,
|
||||
route: row.route,
|
||||
params: row.params,
|
||||
sort: row.sort,
|
||||
pid: row.pid,
|
||||
components: row.components,
|
||||
api_route: row.api_route,
|
||||
}
|
||||
}
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const clickIcon = (icon) => {
|
||||
model.value.icon = icon
|
||||
iconModal.value = false
|
||||
}
|
||||
|
||||
const handleValidateClick = () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
const dataObj = {
|
||||
ID: model.value.id || '',
|
||||
Icon: model.value.icon,
|
||||
Type: model.value.type,
|
||||
Status: model.value.status,
|
||||
IsShow: model.value.isShow,
|
||||
Name: model.value.name,
|
||||
Route: model.value.route,
|
||||
Params: model.value.params,
|
||||
Sort: model.value.sort,
|
||||
Pid: model.value.pid || '',
|
||||
Components: model.value.components || '',
|
||||
ApiRoute: model.value.api_route || '',
|
||||
}
|
||||
const res = await api.addMenu(dataObj)
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
formRef.value?.restoreValidation()
|
||||
|
||||
model.value = {
|
||||
pid: null,
|
||||
type: null,
|
||||
icon: '',
|
||||
status: 1,
|
||||
isShow: 1,
|
||||
name: '',
|
||||
route: '',
|
||||
params: '',
|
||||
components: '',
|
||||
sort: 0,
|
||||
}
|
||||
|
||||
showModal.value = false
|
||||
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
221
src/views/system/acc/acc_role/index.vue
Normal file
@@ -0,0 +1,221 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button v-perms="['/admin/auth/set']" type="primary" @click="openModal(1)">添加角色</n-button>
|
||||
<n-data-table
|
||||
class="mt-5"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="添加/编辑角色"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<!-- {{ model.AuthId }} -->
|
||||
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
|
||||
<n-grid :cols="24" :x-gap="24">
|
||||
<n-form-item-gi :span="16" label="角色名称:" path="Name">
|
||||
<n-input v-model:value="model.Name" placeholder="请输入角色名称" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="角色权限:" path="AuthId">
|
||||
<n-tree
|
||||
:data="menus"
|
||||
:default-checked-keys="model.AuthId"
|
||||
label-field="name"
|
||||
value-field="ID"
|
||||
key-field="ID"
|
||||
children-field="subMenu"
|
||||
checkable
|
||||
cascade
|
||||
virtual-scroll
|
||||
@update:checked-keys="updateCheckedKeys"
|
||||
@update:indeterminate-keys="updateIndeterminateKeys"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="角色状态:" path="Status">
|
||||
<n-switch v-model:value="model.Status" :unchecked-value="2" :checked-value="1" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="18">
|
||||
<div m-auto>
|
||||
<n-button type="primary" @click="handleValidateClick">保存</n-button>
|
||||
<n-button ml-10 @click="clear">取消</n-button>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from '../api'
|
||||
import { NTag, NButton } from 'naive-ui'
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '角色名称',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '角色状态',
|
||||
slot: 'status',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status === 1 ? 'success' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.status === 1 ? '启用' : '禁用'),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
onClick: () => {
|
||||
openModal(2, row)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '编辑',
|
||||
}
|
||||
),
|
||||
[[vPerms, ['/admin/auth/set']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const model = ref({
|
||||
Name: '',
|
||||
AuthId: [],
|
||||
Status: 2,
|
||||
})
|
||||
|
||||
const rules = {
|
||||
Name: {
|
||||
required: true,
|
||||
message: '请输入角色名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
AuthId: {
|
||||
required: true,
|
||||
type: 'array',
|
||||
message: '请选择角色权限',
|
||||
trigger: ['blur', 'input'],
|
||||
},
|
||||
}
|
||||
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
get_menu_list()
|
||||
})
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getRoleList({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
throw error
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const menus = ref([])
|
||||
|
||||
const get_menu_list = async () => {
|
||||
const res = await api.getMenuList()
|
||||
menus.value = res.data.data
|
||||
}
|
||||
|
||||
const openModal = (type, row = {}) => {
|
||||
if (type === 2) {
|
||||
model.value = {
|
||||
ID: row.ID,
|
||||
Name: row.name,
|
||||
AuthId: JSON.parse(row.auth_id),
|
||||
Status: row.status,
|
||||
}
|
||||
}
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
formRef.value?.restoreValidation()
|
||||
model.value = {
|
||||
Name: '',
|
||||
AuthId: [],
|
||||
Status: 2,
|
||||
}
|
||||
showModal.value = false
|
||||
getList()
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const handleValidateClick = () => {
|
||||
formRef.value?.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const res = await api.addRole(model.value)
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const updateCheckedKeys = (value) => {
|
||||
model.value.AuthId = value
|
||||
}
|
||||
|
||||
const updateIndeterminateKeys = (value) => {
|
||||
model.value.AuthId = [...model.value.AuthId, ...value]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
297
src/views/system/acc/acc_user/index.vue
Normal file
@@ -0,0 +1,297 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button v-perms="['/admin/manage/set']" type="primary" @click="openModal(1)">
|
||||
添加管理员
|
||||
</n-button>
|
||||
<n-data-table
|
||||
class="mt-5"
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="添加/编辑角色"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-form ref="formRef" :model="model" :rules="rules" label-placement="left">
|
||||
<n-grid :cols="24" :x-gap="24">
|
||||
<n-form-item-gi :span="16" label="名称:" path="Name">
|
||||
<n-input v-model:value="model.Name" placeholder="请输入管理员名称" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="账号:" path="Phone">
|
||||
<n-input v-model:value="model.Phone" placeholder="请输入管理员账号(手机号码)" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-if="nowType === 1" :span="16" label="密码:" path="Password">
|
||||
<n-input v-model:value="model.Password" placeholder="请输入管理员密码" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi v-else :span="16" label="新密码:">
|
||||
<n-input v-model:value="model.Password" placeholder="不修改请留空" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="角色:" path="AuthId">
|
||||
<n-select
|
||||
v-model:value="model.AuthId"
|
||||
multiple
|
||||
:options="roles"
|
||||
label-field="name"
|
||||
value-field="ID"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="16" label="角色状态:" path="Status">
|
||||
<n-switch v-model:value="model.Status" :unchecked-value="2" :checked-value="1" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="18">
|
||||
<div m-auto>
|
||||
<n-button type="primary" @click="handleValidateClick">保存</n-button>
|
||||
<n-button ml-10 @click="clear">取消</n-button>
|
||||
</div>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h, withDirectives, resolveDirective } from 'vue'
|
||||
import api from '../api'
|
||||
import { NTag, NButton } from 'naive-ui'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '管理员名称',
|
||||
key: 'name',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
key: 'phone',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '角色',
|
||||
slot: 'auth_id',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
const nowRow = { ...row, auth_id: JSON.parse(row.auth_id) }
|
||||
const roleName = []
|
||||
roles.value.forEach((item) => {
|
||||
nowRow.auth_id.forEach((itm) => {
|
||||
if (item.ID === itm) {
|
||||
roleName.push(item.name)
|
||||
}
|
||||
})
|
||||
})
|
||||
return h(
|
||||
'span',
|
||||
{},
|
||||
{
|
||||
default: () => roleName.join(' | '),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '角色状态',
|
||||
slot: 'status',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status === 1 ? 'success' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.status === 1 ? '启用' : '禁用'),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
slot: 'action',
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
onClick: () => {
|
||||
openModal(2, row)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '编辑',
|
||||
}
|
||||
),
|
||||
[[vPerms, ['/admin/manage/set']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
class: 'ml-10',
|
||||
type: 'error',
|
||||
text: true,
|
||||
onClick: () => {
|
||||
delVerifyUser(row)
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => '删除',
|
||||
}
|
||||
),
|
||||
[[vPerms, ['/admin/manage/delete']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const model = ref({
|
||||
Name: '',
|
||||
AuthId: null,
|
||||
Phone: '',
|
||||
Password: '',
|
||||
Status: 2,
|
||||
})
|
||||
|
||||
const rules = {
|
||||
Name: {
|
||||
required: true,
|
||||
message: '请输入角色名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
AuthId: {
|
||||
required: true,
|
||||
type: 'array',
|
||||
message: '请选择分配权限',
|
||||
trigger: ['blur'],
|
||||
},
|
||||
Phone: {
|
||||
required: true,
|
||||
message: '请输入管理员账号(手机号码)',
|
||||
trigger: 'blur',
|
||||
},
|
||||
Password: {
|
||||
required: true,
|
||||
message: '请输入管理员密码',
|
||||
trigger: 'blur',
|
||||
},
|
||||
}
|
||||
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
get_role_list()
|
||||
})
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await api.getAdminList({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
throw error
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const roles = ref([])
|
||||
|
||||
const get_role_list = async () => {
|
||||
const res = await api.getRoleList()
|
||||
roles.value = res.data.data || []
|
||||
}
|
||||
|
||||
const nowType = ref(1)
|
||||
|
||||
const openModal = (type, row = {}) => {
|
||||
nowType.value = type
|
||||
get_role_list()
|
||||
if (type === 2) {
|
||||
model.value = {
|
||||
ID: row.ID,
|
||||
Name: row.name,
|
||||
AuthId: JSON.parse(row.auth_id),
|
||||
Status: row.status,
|
||||
Phone: row.phone,
|
||||
}
|
||||
}
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
formRef.value?.restoreValidation()
|
||||
model.value = {
|
||||
Name: '',
|
||||
AuthId: null,
|
||||
Phone: '',
|
||||
Password: '',
|
||||
Status: 2,
|
||||
}
|
||||
showModal.value = false
|
||||
getList()
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const handleValidateClick = () => {
|
||||
formRef.value?.validate(async (valid) => {
|
||||
if (!valid) {
|
||||
const res = await api.addAdmin(model.value)
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const delVerifyUser = async (row) => {
|
||||
$dialog.error({
|
||||
title: '提示',
|
||||
content: '删除无法撤销,请谨慎!',
|
||||
positiveText: '确定',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
const res = await api.delVerifyUser({
|
||||
uid: row.uid,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
getList()
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
24
src/views/system/acc/api.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
// 获取权限列表
|
||||
getMenuList: () => request.post('/auth/menu'),
|
||||
|
||||
// 获取菜单目录
|
||||
getDirList: (data) => request.post('/auth/menu/parent', data),
|
||||
|
||||
// 添加/修改菜单
|
||||
addMenu: (data) => request.post('/auth/menu/set', data),
|
||||
|
||||
// 获取角色列表
|
||||
getRoleList: (data) => request.post('/auth', data),
|
||||
|
||||
// 添加/修改角色
|
||||
addRole: (data) => request.post('/auth/set', data),
|
||||
// 获取管理员列表
|
||||
getAdminList: (data) => request.post('/manage', data),
|
||||
// 添加/修改管理员
|
||||
addAdmin: (data) => request.post('/manage/set', data),
|
||||
// 删除核销人员
|
||||
delVerifyUser: (data) => request.post('/manage/delete', data),
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button type="primary" @click="handleAdd(1)">新增幻灯片</n-button>
|
||||
<n-button v-perms="['/admin/rotation/edit']" type="primary" @click="handleAdd(1)">
|
||||
新增幻灯片
|
||||
</n-button>
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
@@ -44,10 +47,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, h } from 'vue'
|
||||
import { onMounted, h, withDirectives, resolveDirective } from 'vue'
|
||||
import api from './api'
|
||||
import { NButton } from 'naive-ui'
|
||||
import { NButton, NImage } from 'naive-ui'
|
||||
import Upload from '@/components/Upload.vue'
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
@@ -62,12 +66,9 @@ const columns = ref([
|
||||
align: 'center',
|
||||
slot: 'url',
|
||||
render(row) {
|
||||
return h('img', {
|
||||
return h(NImage, {
|
||||
width: '50',
|
||||
src: row.url[0]?.url || '',
|
||||
style: {
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
@@ -85,17 +86,20 @@ const columns = ref([
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
return [
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
formValue.value = row
|
||||
handleAdd(2)
|
||||
},
|
||||
},
|
||||
},
|
||||
() => '编辑'
|
||||
() => '编辑'
|
||||
),
|
||||
[[vPerms, ['/admin/rotation/edit']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
|
||||
@@ -5,6 +5,11 @@ export default {
|
||||
path: '/sys',
|
||||
component: Layout,
|
||||
redirect: '/sys_banner',
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Sysbanner',
|
||||
@@ -26,5 +31,47 @@ export default {
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'SysAcc',
|
||||
path: 'sys_acc',
|
||||
redirect: '/sys_acc_user',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'SysAccUser',
|
||||
path: 'sys_acc_user',
|
||||
component: () => import('./acc/acc_user/index.vue'),
|
||||
meta: {
|
||||
title: '账号管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'SysAccRole',
|
||||
path: 'sys_acc_role',
|
||||
component: () => import('./acc/acc_role/index.vue'),
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'SysAccMenu',
|
||||
path: 'sys_acc_menu',
|
||||
component: () => import('./acc/acc_menu/index.vue'),
|
||||
meta: {
|
||||
title: '菜单权限',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -15,7 +15,14 @@
|
||||
/>
|
||||
</n-form-item>
|
||||
<n-form-item>
|
||||
<n-button attr-type="button" @click="handleValidateClick">保存</n-button>
|
||||
<n-button
|
||||
v-perms="['/admin/userConfig/edit']"
|
||||
attr-type="button"
|
||||
type="primary"
|
||||
@click="handleValidateClick"
|
||||
>
|
||||
保存
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
</n-form>
|
||||
</CommonPage>
|
||||
|
||||
@@ -2,4 +2,14 @@ import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getUser: (data) => request.post('/user', data),
|
||||
// 获取活动订单
|
||||
gethdlist: (data) => request.post('/user/order', data),
|
||||
// 获取积分订单
|
||||
getjflist: (data) => request.post('/user/point/order', data),
|
||||
// 获取豆子记录
|
||||
getdzJllist: (data) => request.post('/user/pluse', data),
|
||||
// 获取积分记录
|
||||
getjfJllist: (data) => request.post('/user/point', data),
|
||||
// 获取推广记录
|
||||
gettgJllist: (data) => request.post('/user/referee/point', data),
|
||||
}
|
||||
|
||||
@@ -1,33 +1,225 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-grid class="mb-10" x-gap="12">
|
||||
<n-gi :span="24">
|
||||
<div flex>
|
||||
<n-card w-500>
|
||||
<n-statistic label="用户积分(留存)" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.integral" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="用户豆子(留存)" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.pulse" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="今日新增用户" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.today_user"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="用户总流水(元)" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.total_number"
|
||||
:precision="2"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="总佣金(积分)" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.referee"
|
||||
:precision="2"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-500>
|
||||
<n-statistic label="平台总用户" tabular-nums>
|
||||
<n-number-animation
|
||||
ref="numberAnimationInstRef"
|
||||
:from="0"
|
||||
:to="cardData.total_user"
|
||||
/>
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi span="12" mt-10 flex items-center>
|
||||
<span w-100>筛选条件:</span>
|
||||
<n-input-group>
|
||||
<n-select
|
||||
v-model:value="queryParams.selectKey"
|
||||
:style="{ width: '20%' }"
|
||||
:options="selectOptions"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
<n-input v-model:value="queryParams.word" :style="{ width: '30%' }" />
|
||||
</n-input-group>
|
||||
</n-gi>
|
||||
<n-gi span="24" mt-10 flex items-center>
|
||||
<n-button type="primary" @click="getList">查询</n-button>
|
||||
<n-button ml-10 @click="clear">重置</n-button>
|
||||
</n-gi>
|
||||
</n-grid>
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
@update:sorter="handleSorterChange"
|
||||
/>
|
||||
<!-- 用户详情 -->
|
||||
<n-drawer v-model:show="isDrawer" :width="1000" placement="right" :mask-closable="false">
|
||||
<n-drawer-content title="用户详情" closable>
|
||||
<div flex items-center>
|
||||
<img rounded-full :src="nowRow.avatarUrl" width="70" />
|
||||
<div ml-10>
|
||||
<div>昵称:{{ nowRow.nickName }}</div>
|
||||
<div>电话:{{ nowRow.phone }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div mt-10 w-200 flex items-center justify-between text-center>
|
||||
<div>
|
||||
<div>用户积分</div>
|
||||
<div text-red>{{ nowRow.integral }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>用户豆子</div>
|
||||
<div text-red>{{ nowRow.pulse }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<n-tabs v-model:value="tabVal" type="line" animated @update-value="tabsChange">
|
||||
<n-tab name="1" tab="活动订单"></n-tab>
|
||||
<n-tab name="2" tab="积分订单"></n-tab>
|
||||
<n-tab name="3" tab="豆子记录"></n-tab>
|
||||
<n-tab name="4" tab="积分记录"></n-tab>
|
||||
<n-tab name="5" tab="推广记录"></n-tab>
|
||||
</n-tabs>
|
||||
<n-row gutter="12">
|
||||
<n-col :span="12">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-100>时间筛选:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryData.time"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="10" v-if="tabVal === '4'">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-100>条件筛选:</span>
|
||||
<n-select
|
||||
v-model:value="queryData.selectKey"
|
||||
:style="{ width: '30%' }"
|
||||
:options="[
|
||||
{
|
||||
label: '取消订单',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '支付订单',
|
||||
value: 2,
|
||||
},
|
||||
{
|
||||
label: '商品赠送',
|
||||
value: 3,
|
||||
},
|
||||
]"
|
||||
placeholder="请选择类型"
|
||||
/>
|
||||
</div>
|
||||
</n-col>
|
||||
<n-col :span="4">
|
||||
<div mt-10>
|
||||
<n-button type="primary" @click="getTabsList">搜索</n-button>
|
||||
<n-button ml-10 @click="tabsClear">重置</n-button>
|
||||
</div>
|
||||
</n-col>
|
||||
</n-row>
|
||||
<n-data-table
|
||||
class="mt-5"
|
||||
:columns="tabsColumns"
|
||||
:loading="tabsLoading"
|
||||
:data="tabsData"
|
||||
:pagination="tabsPagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
</n-drawer-content>
|
||||
</n-drawer>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h } from 'vue'
|
||||
import api from './api'
|
||||
import { NDropdown, NButton, NEllipsis } from 'naive-ui'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const queryParams = ref({
|
||||
word: '',
|
||||
selectKey: null,
|
||||
})
|
||||
|
||||
const queryData = ref({
|
||||
time: null,
|
||||
selectKey: 2,
|
||||
})
|
||||
|
||||
const cardData = ref({
|
||||
integral: 0,
|
||||
pulse: 0,
|
||||
today_user: 0,
|
||||
total_number: 0,
|
||||
total_user: 0,
|
||||
})
|
||||
|
||||
const selectOptions = [
|
||||
{
|
||||
label: '用户昵称',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '用户电话',
|
||||
value: 1,
|
||||
},
|
||||
]
|
||||
|
||||
const isDrawer = ref(false)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'ID',
|
||||
align: 'center',
|
||||
key: 'ID',
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
align: 'center',
|
||||
key: 'nickName',
|
||||
},
|
||||
{
|
||||
title: '头像',
|
||||
align: 'center',
|
||||
slot: 'avatar',
|
||||
render(row) {
|
||||
return h('img', {
|
||||
src: row.avatar,
|
||||
src: row.avatarUrl,
|
||||
style: {
|
||||
width: '30px',
|
||||
height: '30px',
|
||||
@@ -45,18 +237,59 @@ const columns = ref([
|
||||
title: '用户积分',
|
||||
align: 'center',
|
||||
key: 'integral',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '用户豆子',
|
||||
align: 'center',
|
||||
key: 'pulse',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render(row) {
|
||||
console.log(row)
|
||||
return [
|
||||
h(
|
||||
NDropdown,
|
||||
{
|
||||
trigger: 'click',
|
||||
options: [
|
||||
{
|
||||
label: '用户详情',
|
||||
key: 1,
|
||||
},
|
||||
],
|
||||
onSelect: (key) => {
|
||||
switch (key) {
|
||||
case 1:
|
||||
openDrawer(row)
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
iconPlacement: 'right',
|
||||
},
|
||||
{
|
||||
default: () => '更多',
|
||||
icon: () =>
|
||||
h(TheIcon, {
|
||||
icon: 'ant-design:down-outlined',
|
||||
}),
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -64,9 +297,9 @@ const columns = ref([
|
||||
const data = ref([])
|
||||
|
||||
const pagination = ref({
|
||||
current: 1,
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
@@ -82,20 +315,290 @@ onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const clear = () => {
|
||||
queryParams.value = {
|
||||
word: '',
|
||||
selectKey: null,
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const query_data = {}
|
||||
switch (queryParams.value.selectKey) {
|
||||
case 0:
|
||||
query_data['UserName'] = queryParams.value.word
|
||||
break
|
||||
case 1:
|
||||
query_data['Phone'] = queryParams.value.word
|
||||
break
|
||||
}
|
||||
const res = await api.getUser({
|
||||
pageNum: pagination.value.current,
|
||||
pageNum: pagination.value.page,
|
||||
pageSize: pagination.value.pageSize,
|
||||
...query_data,
|
||||
})
|
||||
console.log(res)
|
||||
data.value = res.data.data
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total_user
|
||||
cardData.value.today_user = res.data.today_user
|
||||
cardData.value.total_user = res.data.total_user
|
||||
cardData.value.total_number = res.data.total_number
|
||||
cardData.value.integral = res.data.integral
|
||||
cardData.value.pulse = res.data.pulse
|
||||
cardData.value.referee = res.data.referee
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
const nowRow = ref({})
|
||||
|
||||
const tabVal = ref('1')
|
||||
|
||||
const tabsLoading = ref(false)
|
||||
const tabsColumns = ref([])
|
||||
const tabsData = ref([])
|
||||
const tabsPagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
tabsPagination.value.page = page
|
||||
getTabsList()
|
||||
},
|
||||
})
|
||||
const openDrawer = (row) => {
|
||||
nowRow.value = row
|
||||
isDrawer.value = true
|
||||
// getTabsList()
|
||||
tabsChange()
|
||||
}
|
||||
|
||||
const tabsChange = async (e = '1') => {
|
||||
tabVal.value = e
|
||||
tabsData.value = []
|
||||
tabsColumns.value = []
|
||||
if (tabVal.value === '1' || tabVal.value === '2') {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '商品封面',
|
||||
align: 'center',
|
||||
slot: 'cover',
|
||||
render: (row) => {
|
||||
return h('img', {
|
||||
src: row.cover,
|
||||
style: {
|
||||
width: '50px',
|
||||
height: '50px',
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
slot: 'goods_name',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NEllipsis,
|
||||
{
|
||||
style: 'max-width: 240px',
|
||||
},
|
||||
{
|
||||
default: () => row.goods_name,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品价格',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '订单状态',
|
||||
align: 'center',
|
||||
slot: 'status',
|
||||
render: (row) => {
|
||||
let nameStr = ''
|
||||
switch (row.status) {
|
||||
case 0:
|
||||
nameStr = '待付款'
|
||||
break
|
||||
case 1:
|
||||
nameStr = '待使用'
|
||||
break
|
||||
case 2:
|
||||
nameStr = '已使用'
|
||||
break
|
||||
case 3:
|
||||
nameStr = '已过期'
|
||||
}
|
||||
return h(
|
||||
'span',
|
||||
{},
|
||||
{
|
||||
default: () => nameStr,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '下单时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
} else if (tabVal.value === '3') {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
key: 'goods_name',
|
||||
},
|
||||
{
|
||||
title: '消费金额',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '下单时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
} else if (tabVal.value === '4') {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
key: 'goods_name',
|
||||
},
|
||||
{
|
||||
title: '积分',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
} else {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '用户昵称',
|
||||
align: 'center',
|
||||
key: 'nick_name',
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
key: 'goods_name',
|
||||
},
|
||||
{
|
||||
title: '获得积分',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
}
|
||||
tabsPagination.value.page = 1
|
||||
getTabsList()
|
||||
}
|
||||
|
||||
const getTabsList = async () => {
|
||||
tabsLoading.value = true
|
||||
let res
|
||||
const data = {
|
||||
uid: nowRow.value.uid,
|
||||
pageNum: tabsPagination.value.page,
|
||||
pageSize: tabsPagination.value.pageSize,
|
||||
StartTime: queryData.value.time === null ? '' : queryData.value.time[0] || '',
|
||||
EndTime: queryData.value.time === null ? '' : queryData.value.time[1] || '',
|
||||
}
|
||||
switch (tabVal.value) {
|
||||
case '1':
|
||||
res = await api.gethdlist(data)
|
||||
break
|
||||
case '2':
|
||||
res = await api.getjflist(data)
|
||||
break
|
||||
case '3':
|
||||
res = await api.getdzJllist(data)
|
||||
break
|
||||
case '4':
|
||||
data['Type'] = queryData.value.selectKey
|
||||
res = await api.getjfJllist(data)
|
||||
break
|
||||
case '5':
|
||||
res = await api.gettgJllist(data)
|
||||
break
|
||||
}
|
||||
tabsData.value = res.data.data || []
|
||||
tabsPagination.value.itemCount = res.data.total
|
||||
tabsLoading.value = false
|
||||
}
|
||||
|
||||
const handleSorterChange = (sorter) => {
|
||||
if (!loading.value) {
|
||||
loading.value = true
|
||||
switch (sorter.columnKey) {
|
||||
case 'pulse':
|
||||
columns.value[5].sortOrder = !sorter ? false : sorter.order
|
||||
data.value = data.value.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.pulse - a.pulse
|
||||
return a.pulse - b.pulse
|
||||
})
|
||||
break
|
||||
case 'integral':
|
||||
columns.value[4].sortOrder = !sorter ? false : sorter.order
|
||||
data.value = data.value.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.integral - a.integral
|
||||
return a.integral - b.integral
|
||||
})
|
||||
break
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const tabsClear = async () => {
|
||||
queryData.value = {
|
||||
time: null,
|
||||
selectKey: null,
|
||||
}
|
||||
await getTabsList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -5,6 +5,11 @@ export default {
|
||||
path: '/user',
|
||||
component: Layout,
|
||||
redirect: '/user_list',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'Userlist',
|
||||
@@ -12,6 +17,7 @@ export default {
|
||||
component: () => import('./index/index.vue'),
|
||||
meta: {
|
||||
title: '用户列表',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
|
||||