Merge branch 'test'
@@ -1,23 +1,26 @@
|
||||
module.exports = {
|
||||
types: [
|
||||
{ value: 'feat', name:'feat: 新增功能' },
|
||||
{ value: 'fix', name:'fix: 修复bug' },
|
||||
{ value: 'docs', name:'docs: 文档变更' },
|
||||
{ value: 'style', name:'style: 代码格式(不影响功能,例如空格、分号等格式修正)' },
|
||||
{ value: 'refactor', name:'refactor: 代码重构(不包括 bug 修复、功能新增)' },
|
||||
{ value: 'perf', name:'perf: 性能优化' },
|
||||
{ value: 'test', name:'test: 添加、修改测试用例' },
|
||||
{ value: 'build', name:'build: 构建流程、外部依赖变更(如升级 npm 包、修改 脚手架 配置等)' },
|
||||
{ value: 'ci', name:'ci: 修改 CI 配置、脚本' },
|
||||
{ value: 'chore', name:'chore: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)' },
|
||||
{ value: 'revert', name:'revert: 回滚 commit' },
|
||||
{ value: 'wip', name:'wip: 开发中' },
|
||||
{ value: 'mod', name:'mod: 不确定分类的修改' },
|
||||
{ value: 'release', name:'release: 发布' },
|
||||
{ value: 'feat', name: 'feat: 新增功能' },
|
||||
{ value: 'fix', name: 'fix: 修复bug' },
|
||||
{ value: 'docs', name: 'docs: 文档变更' },
|
||||
{ value: 'style', name: 'style: 代码格式(不影响功能,例如空格、分号等格式修正)' },
|
||||
{ value: 'refactor', name: 'refactor: 代码重构(不包括 bug 修复、功能新增)' },
|
||||
{ value: 'perf', name: 'perf: 性能优化' },
|
||||
{ value: 'test', name: 'test: 添加、修改测试用例' },
|
||||
{
|
||||
value: 'build',
|
||||
name: 'build: 构建流程、外部依赖变更(如升级 npm 包、修改 脚手架 配置等)',
|
||||
},
|
||||
{ value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
|
||||
{ value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)' },
|
||||
{ value: 'revert', name: 'revert: 回滚 commit' },
|
||||
{ value: 'wip', name: 'wip: 开发中' },
|
||||
{ value: 'mod', name: 'mod: 不确定分类的修改' },
|
||||
{ value: 'release', name: 'release: 发布' },
|
||||
],
|
||||
scopes: [
|
||||
['custom', '自定义'],
|
||||
['projects', '项目搭建'],
|
||||
['projects', '项目搭建'],
|
||||
['components', '组件相关'],
|
||||
['utils', 'utils 相关'],
|
||||
['styles', '样式相关'],
|
||||
@@ -26,7 +29,7 @@ module.exports = {
|
||||
].map(([value, description]) => {
|
||||
return {
|
||||
value,
|
||||
name: `${value.padEnd(30)} (${description})`
|
||||
name: `${value.padEnd(30)} (${description})`,
|
||||
}
|
||||
}),
|
||||
messages: {
|
||||
@@ -37,9 +40,9 @@ module.exports = {
|
||||
body: '填写更加详细的变更描述(可选)。使用 "|" 换行:',
|
||||
breaking: '列举非兼容性重大的变更(可选):',
|
||||
footer: '列举出所有变更的 Issues Closed(可选)。 例如: #31, #34:',
|
||||
confirmCommit: '确认提交?'
|
||||
confirmCommit: '确认提交?',
|
||||
},
|
||||
allowBreakingChanges: ['feat', 'fix'],
|
||||
subjectLimit: 100,
|
||||
breaklineChar: '|'
|
||||
breaklineChar: '|',
|
||||
}
|
||||
|
||||
1
.dockerignore
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
10
.env
@@ -1,9 +1,3 @@
|
||||
VITE_TITLE = '捷兑通 - 平台端'
|
||||
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'
|
||||
VITE_PORT=4000
|
||||
|
||||
@@ -1,11 +1,22 @@
|
||||
# 资源公共路径,需要以 /开头和结尾
|
||||
VITE_PUBLIC_PATH = '/'
|
||||
VITE_PUBLIC_PATH='/'
|
||||
|
||||
# 是否启用MOCK
|
||||
VITE_USE_MOCK = false
|
||||
VITE_USE_MOCK=false
|
||||
|
||||
# 是否启用代理
|
||||
VITE_USE_PROXY = true
|
||||
VITE_USE_PROXY=true
|
||||
|
||||
# base api
|
||||
VITE_BASE_API = '/admin'
|
||||
VITE_BASE_API='https://test.wanzhuanyongcheng.cn/admin'
|
||||
|
||||
VITE_WS1_URL='game.wanzhuanyongcheng.cn/dice/home'
|
||||
|
||||
VITE_WS_URL='test.wanzhuanyongcheng.cn/admin/data'
|
||||
|
||||
VITE_MER_LOGIN_URL='//localhost:3100/login'
|
||||
|
||||
VITE_GAME_API='https://www.jdt168.com'
|
||||
|
||||
# 是否启用监控
|
||||
VITE_SENTRY=false
|
||||
|
||||
13
.env.github
@@ -1,13 +0,0 @@
|
||||
# 自定义域名CNAME
|
||||
# VITE_CNAME = 'template.qszone.com'
|
||||
|
||||
# 资源公共路径,需要以 /开头和结尾
|
||||
VITE_PUBLIC_PATH = '/vue-naive-admin/'
|
||||
|
||||
VITE_USE_HASH = true
|
||||
|
||||
# 是否启用MOCK
|
||||
VITE_USE_MOCK = true
|
||||
|
||||
# base api
|
||||
VITE_BASE_API = '/api'
|
||||
@@ -1,16 +1,24 @@
|
||||
# 资源公共路径,需要以 /开头和结尾
|
||||
VITE_PUBLIC_PATH = '/static/admin'
|
||||
VITE_PUBLIC_PATH='/'
|
||||
|
||||
# 是否启用MOCK
|
||||
VITE_USE_MOCK = false
|
||||
VITE_USE_MOCK=false
|
||||
|
||||
# base api
|
||||
VITE_BASE_API = 'https://www.wanzhuanyongcheng.cn/admin'
|
||||
|
||||
# VITE_GAME_API = 'http://www.wanzhuanyongcheng.cn/admin'
|
||||
VITE_BASE_API='//www.wanzhuanyongcheng.cn/admin'
|
||||
|
||||
# 是否启用压缩
|
||||
VITE_USE_COMPRESS = true
|
||||
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='//jdt-prod-mer.wanzhuanyongcheng.cn/login'
|
||||
|
||||
# 是否启用监控
|
||||
VITE_SENTRY=true
|
||||
|
||||
5
.env.sentry-build-plugin
Normal file
@@ -0,0 +1,5 @@
|
||||
# DO NOT commit this file to your repository!
|
||||
# The SENTRY_AUTH_TOKEN variable is picked up by the Sentry Build Plugin.
|
||||
# It's used for authentication when uploading source maps.
|
||||
# You can also set this env variable in your own `.env` files and remove this file.
|
||||
SENTRY_AUTH_TOKEN="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"
|
||||
27
.env.test
@@ -1,7 +1,28 @@
|
||||
VITE_PUBLIC_PATH = '/'
|
||||
# 资源公共路径,需要以 /开头和结尾
|
||||
VITE_PUBLIC_PATH='/'
|
||||
|
||||
# 是否启用MOCK
|
||||
VITE_USE_MOCK = true
|
||||
VITE_USE_MOCK=false
|
||||
|
||||
# 是否启用代理
|
||||
VITE_USE_PROXY=false
|
||||
|
||||
# 是否启用压缩
|
||||
VITE_USE_COMPRESS=true
|
||||
|
||||
# 压缩类型
|
||||
VITE_COMPRESS_TYPE=gzip
|
||||
|
||||
# base api
|
||||
VITE_BASE_API = '/api'
|
||||
VITE_BASE_API='//test.wanzhuanyongcheng.cn/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='//jdt-test-mer.wanzhuanyongcheng.cn/login'
|
||||
|
||||
# 是否启用监控
|
||||
VITE_SENTRY=false
|
||||
|
||||
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>
|
||||
4
.idea/codeStyles/Project.xml
generated
@@ -36,6 +36,10 @@
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
<option name="SMART_TABS" value="true" />
|
||||
>>>>>>> test
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
|
||||
15
.idea/git_toolbox_prj.xml
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitToolBoxProjectSettings">
|
||||
<option name="commitMessageIssueKeyValidationOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
<option name="commitMessageValidationEnabledOverride">
|
||||
<BoolValueOverride>
|
||||
<option name="enabled" value="true" />
|
||||
</BoolValueOverride>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
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>
|
||||
6
.idea/jsLibraryMappings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/jsLinters/eslint.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="EslintConfiguration">
|
||||
<option name="fix-on-save" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
4
.idea/modules.xml
generated
@@ -2,7 +2,11 @@
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<<<<<<< HEAD
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/jdt-admin.iml" filepath="$PROJECT_DIR$/.idea/jdt-admin.iml" />
|
||||
=======
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/admin.iml" filepath="$PROJECT_DIR$/.idea/admin.iml" />
|
||||
>>>>>>> test
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/prettier.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
</component>
|
||||
</project>
|
||||
2
.vscode/extensions.json
vendored
@@ -4,6 +4,6 @@
|
||||
"antfu.iconify",
|
||||
"mikestead.dotenv",
|
||||
"sdras.vue-vscode-snippets",
|
||||
"cipchk.cssrem",
|
||||
"cipchk.cssrem"
|
||||
]
|
||||
}
|
||||
|
||||
3
.vscode/settings.json
vendored
@@ -30,5 +30,8 @@
|
||||
"files.associations": {
|
||||
"*.env.*": "dotenv",
|
||||
"*.css": "postcss"
|
||||
},
|
||||
"[dockerfile]": {
|
||||
"editor.defaultFormatter": "ms-azuretools.vscode-docker"
|
||||
}
|
||||
}
|
||||
|
||||
5
Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY dist/ /usr/share/nginx/html
|
||||
|
||||
COPY default.conf /etc/nginx/conf.d/default.conf
|
||||
21
LICENSE
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ronnie Zhang
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
209
README.EN.md
@@ -1,209 +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 allt="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.md">中文</a>
|
||||
</p>
|
||||
|
||||
### Introduction
|
||||
|
||||
[Vue Naive Admin](https://github.com/zclzone/vue-naive-admin) is a **completely open source free and commercially allowed ** admin template,Based on the latest technology stack of front-end such as `Vue3、Vite3、Pinia、Unocss and Naive UI`. Compared with other more popular backend management templates, this project is more concise, lightweight, fresh style, very low learning costs, ideal for small and medium-sized projects or personal projects.
|
||||
|
||||
### Features
|
||||
|
||||
- 🍒 Integrated [Naive UI](https://www.naiveui.com),recommended by Evan You.
|
||||
- 🍑 Integrated login, logout and permission verification.
|
||||
- 🍐 Integrated multi-environment configuration, dev, test, production and github pages environments.
|
||||
- 🍎 Integrated `eslint + prettier`.
|
||||
- 🍌 Integrated `husky + commitlint`.
|
||||
- 🍉 Integrated `Mock`.
|
||||
- 🍍 Integrated `pinia`,lightweight, simple and easy to use alternative to vuex.
|
||||
- 📦 Integrated `unplugin` auto import.
|
||||
- 🤹 Integrated `iconify` icon,support custom svg icons.
|
||||
- 🍇 Integrated `unocss`.
|
||||
|
||||
### Preview
|
||||
|
||||
[https://template.qszone.com](https://template.qszone.com)
|
||||
|
||||
[https://base.isme.top](https://base.isme.top)
|
||||
|
||||
### Docs
|
||||
|
||||
[Vue Naive Admin Docs](https://zclzone.github.io/vue-naive-admin-docs)
|
||||
|
||||
|
||||
### Getting Started
|
||||
|
||||
```shell
|
||||
# Recommended setup git autocrlf 为 false
|
||||
git config --global core.autocrlf false
|
||||
|
||||
# Clone Project
|
||||
git clone https://github.com/zclzone/vue-naive-admin.git
|
||||
|
||||
cd vue-naive-admin
|
||||
|
||||
# Install dependencies(Recommended use pnpm: https://pnpm.io/zh/installation)
|
||||
npm i -g pnpm # Installed and can be ignored
|
||||
pnpm i # or npm i
|
||||
|
||||
# Start
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
### Build and Release
|
||||
|
||||
```shell
|
||||
# Test Environment
|
||||
pnpm build:test
|
||||
|
||||
# Github Environment
|
||||
pnpm build:github
|
||||
|
||||
# Prod Environment
|
||||
pnpm build
|
||||
```
|
||||
|
||||
### Other
|
||||
|
||||
```shell
|
||||
# eslint check
|
||||
pnpm lint
|
||||
|
||||
# eslint check and fix
|
||||
pnpm lint:fix
|
||||
|
||||
# Preview(Need to build first)
|
||||
pnpm preview
|
||||
|
||||
# Commit(husky+commitlint)
|
||||
pnpm cz
|
||||
```
|
||||
|
||||
### Directory description
|
||||
|
||||
```
|
||||
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 version: Qs Admin
|
||||
|
||||
#### source code
|
||||
|
||||
- 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)
|
||||
|
||||
#### preview
|
||||
|
||||
- [https://admin.qszone.com](https://admin.qszone.com)
|
||||
- [https://zclzone.github.io/qs-admin](https://zclzone.github.io/qs-admin)
|
||||
|
||||
### Open source projects that use this project:
|
||||
|
||||
- [gin-vue-blog](https://github.com/szluyu99/gin-vue-blog): A full-stack blog project in Golang, the frontend of the blog backend is based on vue-naive-admin and integrates with a real backend service, implementing features such as backend-controlled routing.
|
||||
|
||||
### Communication group & About the author
|
||||
|
||||
<a href="https://blog.qszone.com/about/">
|
||||
<img src="https://assets.qszone.com/images/about.png" style="max-width: 400px" />
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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'), ''),
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import viteCompression from 'vite-plugin-compression'
|
||||
import { configHtmlPlugin } from './html'
|
||||
import { configMockPlugin } from './mock'
|
||||
import unplugin from './unplugin'
|
||||
import { sentryVitePlugin } from '@sentry/vite-plugin'
|
||||
|
||||
export function createVitePlugins(viteEnv, isBuild) {
|
||||
const plugins = [vue(), ...unplugin, configHtmlPlugin(viteEnv, isBuild), Unocss()]
|
||||
@@ -29,12 +30,27 @@ export function createVitePlugins(viteEnv, isBuild) {
|
||||
if (isBuild) {
|
||||
plugins.push(
|
||||
visualizer({
|
||||
open: true,
|
||||
open: false,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (viteEnv.VITE_SENTRY) {
|
||||
plugins.push(
|
||||
sentryVitePlugin({
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
org: 'sentry',
|
||||
project: 'jdt-admin',
|
||||
url: 'https://w.huakk.top',
|
||||
sourcemaps: {
|
||||
ignore: ['node_modules'],
|
||||
filesToDeleteAfterUpload: ['dist/**/*.js.map'],
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
24
default.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
server {
|
||||
# 监听ipv4
|
||||
listen 80;
|
||||
# 监听ipv6
|
||||
listen [::]:80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
add_header Access-Control-Allow-Headers *;
|
||||
add_header Access-Control-Allow-Methods *;
|
||||
add_header Access-Control-Allow-Credentials true;
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
add_header Cache-Control no-cache;
|
||||
add_header Access-Control-Max-Age 1728000;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
推荐阅读作者在掘金的文章:
|
||||
|
||||
[保熟的UnoCSS使用指北,优雅使用antfu大佬的原子化CSS](https://juejin.cn/post/7142466784971456548)
|
||||
40
docs/使用图标.md
@@ -1,40 +0,0 @@
|
||||
## 使用 iconify 图标
|
||||
|
||||
首先去图标库地址:[icones](https://icones.js.org/) 找合适的图标
|
||||
|
||||
### 1. 结合 unocss 使用
|
||||
|
||||
```html
|
||||
<i i-carbon-sun />
|
||||
<i class="i-carbon-sun" />
|
||||
```
|
||||
|
||||
### 2. 结合插件 unplugin-icons 自定义标签使用
|
||||
|
||||
`<icon-[iconify图标名称]`
|
||||
|
||||
```html
|
||||
<icon-ant-design:fullscreen-exit-outlined />
|
||||
<icon-ant-design:fullscreen-outlined />
|
||||
```
|
||||
|
||||
这种方式还支持自定义 svg 图标,本项目自定义 svg 图标固定放在 src/assets/svg 下
|
||||
|
||||
`<icon-custom-[svg图标文件名]`
|
||||
|
||||
```
|
||||
<icon-custom-logo />
|
||||
```
|
||||
|
||||
具体配置参看 build/plugin/unplugin.js
|
||||
|
||||
### 3. 结合 Naive UI 的 NIcon 组件封装使用
|
||||
|
||||
```html
|
||||
<!-- iconify图标 -->
|
||||
<TheIcon icon="material-symbols:delete-outline" />
|
||||
<!-- 自定义svg图标 -->
|
||||
<TheIcon icon="logo" type="custom" />
|
||||
```
|
||||
|
||||
封装组件参看 src/components/icon
|
||||
@@ -1,32 +0,0 @@
|
||||
## 安装pnpm
|
||||
|
||||
### 使用Corepack安装(推荐)
|
||||
|
||||
从 v16.13 开始,Node.js 发布了 Corepack 来管理包管理器。 这是一项实验性功能,需要通过运行如下脚本来启用它:
|
||||
|
||||
```
|
||||
npx corepack enable // 可能需要管理员权限
|
||||
```
|
||||
|
||||
这将自动在您的系统上安装 pnpm。 但是,它可能不是最新版本的 pnpm。 若要升级,请检查[最新的 pnpm 版本](https://github.com/pnpm/pnpm/releases/latest) 并运行,如 7.14.0
|
||||
```
|
||||
corepack prepare pnpm@7.14.0 --activate
|
||||
```
|
||||
|
||||
如果是 Node.js v16.17 或者更新的版本,可以直接安装最新版本的 pnpm
|
||||
```
|
||||
corepack prepare pnpm@latest --activate
|
||||
```
|
||||
|
||||
### 使用npm安装
|
||||
|
||||
```
|
||||
npm i -g pnpm
|
||||
```
|
||||
|
||||
更新,卸了重新装
|
||||
|
||||
```
|
||||
npm uninstall -g pnpm
|
||||
npm i -g pnpm
|
||||
```
|
||||
51
package.json
@@ -3,7 +3,6 @@
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"build:github": "vite build --mode github && esno ./build/script",
|
||||
"build:test": "vite build --mode test",
|
||||
"cz": "cz",
|
||||
"dev": "vite",
|
||||
@@ -11,10 +10,12 @@
|
||||
"lint:fix": "eslint --fix --ext .js,.vue .",
|
||||
"lint:staged": "lint-staged",
|
||||
"prepare": "husky install",
|
||||
"preview": "vite preview"
|
||||
"preview": "vite preview",
|
||||
"lf": "npx prettier --write --end-of-line lf ."
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,vue}": [
|
||||
"npx prettier --write --end-of-line lf .",
|
||||
"eslint --ext .js,.vue ."
|
||||
]
|
||||
},
|
||||
@@ -31,29 +32,33 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@unocss/eslint-config": "^0.55.0",
|
||||
"@vueuse/core": "^10.3.0",
|
||||
"@sentry/vite-plugin": "^2.10.1",
|
||||
"@sentry/vue": "^7.81.0",
|
||||
"@unocss/eslint-config": "^0.55.7",
|
||||
"@vueuse/core": "^10.6.1",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.4.0",
|
||||
"dayjs": "^1.11.9",
|
||||
"axios": "^1.6.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.4.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"md-editor-v3": "^4.2.2",
|
||||
"md-editor-v3": "^4.8.3",
|
||||
"mockjs": "^1.1.0",
|
||||
"pinia": "^2.1.6",
|
||||
"vite": "^4.4.9",
|
||||
"pinia": "^2.1.7",
|
||||
"vite": "^4.5.0",
|
||||
"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/config-conventional": "^17.7.0",
|
||||
"@iconify/json": "^2.2.100",
|
||||
"@commitlint/cli": "^17.8.1",
|
||||
"@commitlint/config-conventional": "^17.8.1",
|
||||
"@iconify/json": "^2.2.144",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@unocss/preset-rem-to-px": "^0.55.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@unocss/preset-rem-to-px": "^0.55.7",
|
||||
"@vitejs/plugin-vue": "^4.5.0",
|
||||
"@vue/compiler-sfc": "^3.3.8",
|
||||
"@zclzone/eslint-config": "^0.0.4",
|
||||
"chalk": "^5.3.0",
|
||||
"commitizen": "^4.3.0",
|
||||
@@ -63,17 +68,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.5",
|
||||
"unocss": "0.55.0",
|
||||
"unplugin-auto-import": "^0.16.6",
|
||||
"unplugin-icons": "^0.16.5",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"unplugin-auto-import": "^0.16.7",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
8249
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",
|
||||
|
||||
@@ -3,4 +3,11 @@ import { request } from '@/utils'
|
||||
export default {
|
||||
getUser: () => request.get('/user'),
|
||||
refreshToken: () => request.post('/auth/refreshToken', null, { noNeedTip: true }),
|
||||
// 上传
|
||||
uploadImg: (data) =>
|
||||
request.post('/upload', data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
|
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>
|
||||
97
src/components/Editor.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div ref="_ref" style="border: 1px solid #ccc">
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:default-config="toolbarConfig"
|
||||
mode="default"
|
||||
/>
|
||||
<Editor
|
||||
v-model="htmlValue"
|
||||
:style="{ height: height + 'px', overflowY: 'hidden' }"
|
||||
:default-config="editorConfig"
|
||||
mode="default"
|
||||
@on-created="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||
|
||||
import { onBeforeUnmount, shallowRef, ref } from 'vue'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import api from '@/api'
|
||||
|
||||
const props = defineProps({
|
||||
valueHtml: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 500,
|
||||
},
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:valueHtml'])
|
||||
|
||||
const editorRef = shallowRef()
|
||||
|
||||
const _ref = ref(null)
|
||||
|
||||
const toolbarConfig = {}
|
||||
|
||||
const editorConfig = { placeholder: '请输入游戏介绍...', MENU_CONF: {} }
|
||||
|
||||
const htmlValue = computed({
|
||||
get() {
|
||||
return props.valueHtml
|
||||
},
|
||||
set(value) {
|
||||
emit('update:valueHtml', value)
|
||||
},
|
||||
})
|
||||
|
||||
editorConfig.MENU_CONF['uploadImage'] = {
|
||||
allowedFileTypes: ['image/*'],
|
||||
base64LimitSize: 5 * 1024, // 5kb
|
||||
// 超时时间,默认为 10 秒
|
||||
timeout: 5 * 1000, // 5 秒
|
||||
|
||||
async customUpload(file, insertFn) {
|
||||
await upFile(file, insertFn)
|
||||
},
|
||||
}
|
||||
|
||||
editorConfig.MENU_CONF['uploadVideo'] = {
|
||||
allowedFileTypes: ['video/*'],
|
||||
|
||||
async customUpload(file, insertFn) {
|
||||
await upFile(file, insertFn)
|
||||
},
|
||||
}
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
|
||||
const handleCreated = (editor) => {
|
||||
editorRef.value = editor // 记录 editor 实例,重要!
|
||||
editor.on('fullScreen', () => {
|
||||
_ref.value.style.zIndex = 10
|
||||
})
|
||||
}
|
||||
|
||||
const upFile = async (file, insertFn) => {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const res = await api.uploadImg(formData)
|
||||
insertFn(res.data.data)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></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,9 +2,10 @@ 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'
|
||||
import * as Sentry from '@sentry/vue'
|
||||
|
||||
const isHash = true
|
||||
const isHash = false
|
||||
export const router = createRouter({
|
||||
history: isHash ? createWebHashHistory('/') : createWebHistory('/'),
|
||||
routes: basicRoutes,
|
||||
@@ -14,19 +15,25 @@ export const router = createRouter({
|
||||
export async function setupRouter(app) {
|
||||
await addDynamicRoutes()
|
||||
setupRouterGuard(router)
|
||||
if (import.meta.env.VITE_SENTRY === 'true') {
|
||||
Sentry.init({
|
||||
app,
|
||||
dsn: 'https://1c158d5f832eef396e69447959d902d2@w.huakk.top/12',
|
||||
integrations: [
|
||||
new Sentry.BrowserTracing({
|
||||
tracePropagationTargets: ['localhost', /^https:\/\/w\.huakk\.top\/api/],
|
||||
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
|
||||
}),
|
||||
new Sentry.Replay(),
|
||||
],
|
||||
tracesSampleRate: 1.0,
|
||||
replaysSessionSampleRate: 0.1,
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
})
|
||||
}
|
||||
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 +45,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 +54,7 @@ export async function addDynamicRoutes() {
|
||||
router.addRoute(NOT_FOUND_ROUTE)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +64,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,104 @@
|
||||
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,
|
||||
}
|
||||
|
||||
const curRoute = {
|
||||
path: route.route,
|
||||
name: route.name,
|
||||
isHidden,
|
||||
meta,
|
||||
children: [],
|
||||
}
|
||||
|
||||
if (route.route === '/' && firstRoute) {
|
||||
curRoute['redirect'] = route.subMenu[0].route
|
||||
} else if (route.subMenu && route.type === 1) {
|
||||
curRoute['redirect'] = `${route.subMenu[0].route}`
|
||||
}
|
||||
|
||||
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 +119,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('/admin')) {
|
||||
// config.baseURL = import.meta.env.VITE_ADMIN_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,9 @@
|
||||
:data="data"
|
||||
:pagination="pagination"
|
||||
:bordered="false"
|
||||
remote
|
||||
:row-key="rowKey"
|
||||
children-key="Classify"
|
||||
/>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
@@ -45,12 +50,17 @@
|
||||
</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)
|
||||
|
||||
const rowKey = (row) => {
|
||||
return row.Classify || []
|
||||
}
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: 'ID',
|
||||
@@ -76,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/store/classify/edit']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
@@ -105,6 +118,7 @@ const rules = {
|
||||
}
|
||||
|
||||
const formValue = ref({
|
||||
ID: 0,
|
||||
name: '',
|
||||
status: 1,
|
||||
})
|
||||
@@ -112,9 +126,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 +147,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 +162,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 +180,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="请输入商户座机" />
|
||||
@@ -78,9 +82,9 @@
|
||||
<n-form-item label="商户地址:" path="address">
|
||||
<n-input v-model:value="formValue.address" placeholder="请输入商户地址" />
|
||||
</n-form-item>
|
||||
<n-form-item label="经营类目:" path="classId">
|
||||
<n-form-item label="经营类目:" path="store_class_id">
|
||||
<n-select
|
||||
v-model:value="formValue.classId"
|
||||
v-model:value="formValue.store_class_id"
|
||||
label-field="name"
|
||||
value-field="ID"
|
||||
clearable
|
||||
@@ -88,23 +92,22 @@
|
||||
: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 label="商户类型:" path="bType">
|
||||
<n-select
|
||||
v-model:value="formValue.bType"
|
||||
label-field="name"
|
||||
value-field="ID"
|
||||
placeholder="请选择商户类型"
|
||||
clearable
|
||||
:options="typeOptions"
|
||||
/>
|
||||
<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"-->
|
||||
<!-- label-field="name"-->
|
||||
<!-- value-field="ID"-->
|
||||
<!-- placeholder="请选择商户类型"-->
|
||||
<!-- clearable-->
|
||||
<!-- :options="typeOptions"-->
|
||||
<!-- />-->
|
||||
<!-- </n-form-item>-->
|
||||
<n-form-item label="手续费收取类型:" path="scaleType">
|
||||
<n-select
|
||||
v-model:value="formValue.scaleType"
|
||||
@@ -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,13 @@
|
||||
</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 === '编辑商户')
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
@@ -157,18 +163,10 @@ const columns = ref([
|
||||
align: 'center',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '商户类型',
|
||||
align: 'center',
|
||||
key: 'type',
|
||||
render(row) {
|
||||
return h('span', row.bType === 1 ? '供应商' : '兑换商')
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
align: 'center',
|
||||
key: 'status',
|
||||
slot: 'status',
|
||||
render(row) {
|
||||
return h('span', row.status === 1 ? '正常' : '禁用')
|
||||
},
|
||||
@@ -177,19 +175,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 +230,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,31 +252,15 @@ 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: '',
|
||||
phone: '',
|
||||
mobile: '',
|
||||
address: '',
|
||||
classId: null,
|
||||
store_class_id: null,
|
||||
local: '',
|
||||
password: '',
|
||||
bType: null,
|
||||
scaleType: null,
|
||||
scale: null,
|
||||
status: 2,
|
||||
@@ -287,7 +297,7 @@ const rules = {
|
||||
message: '请搜索商户经纬度',
|
||||
trigger: 'blur',
|
||||
},
|
||||
classId: {
|
||||
store_class_id: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择经营类目',
|
||||
@@ -298,12 +308,6 @@ const rules = {
|
||||
// message: '请输入商户密码',
|
||||
// trigger: 'blur',
|
||||
// },
|
||||
bType: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
message: '请选择商户类型',
|
||||
trigger: 'change',
|
||||
},
|
||||
scaleType: {
|
||||
required: true,
|
||||
type: 'number',
|
||||
@@ -323,42 +327,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 +356,7 @@ const getMertype = async () => {
|
||||
const clearQuryVal = () => {
|
||||
QuryVal.value = {
|
||||
StoreName: '',
|
||||
Status: '',
|
||||
Status: null,
|
||||
}
|
||||
getList()
|
||||
}
|
||||
@@ -384,14 +366,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) => {
|
||||
@@ -400,8 +374,8 @@ const handleValidateClick = (e) => {
|
||||
await api.addMer(formValue.value)
|
||||
$message.success('成功')
|
||||
handleClearValidateClick()
|
||||
getMertype()
|
||||
getList()
|
||||
await getMertype()
|
||||
await getList()
|
||||
showModal.value = false
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
|
||||
@@ -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,211 @@
|
||||
<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(','),
|
||||
}
|
||||
console.log(nowRow.value)
|
||||
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.store_class_id) 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),
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-data-table
|
||||
@@ -8,43 +9,248 @@
|
||||
: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">
|
||||
<n-input-number
|
||||
v-model:value="nowRow.pulse"
|
||||
clearable
|
||||
placeholder="请输入赠送豆子数量...."
|
||||
:min="0"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="20" label="商品赠送积分" path="integral">
|
||||
<n-input-number
|
||||
v-model:value="nowRow.integral"
|
||||
clearable
|
||||
placeholder="请输入赠送积分数量...."
|
||||
:min="0"
|
||||
/>
|
||||
</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="请输入分佣比例...."
|
||||
:min="0"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="20" label="豆子过期时间" path="expiration">
|
||||
<n-input-number
|
||||
v-model:value="nowRow.expiration"
|
||||
clearable
|
||||
placeholder="请输入豆子过期时间"
|
||||
:min="0"
|
||||
/>
|
||||
</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: {
|
||||
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',
|
||||
},
|
||||
expiration: {
|
||||
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',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品分类',
|
||||
key: 'class_name',
|
||||
slot: 'Classify',
|
||||
align: 'center',
|
||||
render(row) {
|
||||
return h(
|
||||
'div',
|
||||
{},
|
||||
{
|
||||
default: () => row.Classify.name,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品价格',
|
||||
title: '商品价格(元)',
|
||||
key: 'number',
|
||||
align: 'center',
|
||||
},
|
||||
@@ -53,52 +259,130 @@ const columns = ref([
|
||||
key: 'stock',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '赠送豆子',
|
||||
key: 'pulse',
|
||||
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 +393,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 +415,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',
|
||||
@@ -26,15 +21,15 @@ export default {
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'PointList',
|
||||
path: 'point_list',
|
||||
component: () => import('./point/index.vue'),
|
||||
meta: {
|
||||
title: '积分商品',
|
||||
icon: 'mdi:account-multiple',
|
||||
order: 10,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// name: 'PointList',
|
||||
// path: 'point_list',
|
||||
// component: () => import('./point/index.vue'),
|
||||
// meta: {
|
||||
// title: '积分商品',
|
||||
// icon: 'mdi:account-multiple',
|
||||
// order: 10,
|
||||
// },
|
||||
// },
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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,43 @@
|
||||
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),
|
||||
|
||||
// 获取游戏大厅
|
||||
getGameList: (data) => request.post('/game/list', data),
|
||||
|
||||
// 添加修改游戏
|
||||
addGame: (data) => request.post('/game/edit', data),
|
||||
|
||||
// 当前投注用户
|
||||
nowUser: () => request.post('/now/draw/user', {}),
|
||||
|
||||
// 全部投注用户
|
||||
allUser: () => request.post('/all/draw/user', {}),
|
||||
|
||||
// 吹气球相关
|
||||
// 游戏状态
|
||||
getisBalloonStart: () => request.post('/getisBalloonStart'),
|
||||
// 修改游戏状态
|
||||
setisBalloonStart: (data) => request.post('/isBalloonStart', data),
|
||||
// 全部开奖记录
|
||||
getBalloonList: () => request.post('/balloon/draw'),
|
||||
// 本局投注记录
|
||||
getBalloonUser: () => request.post('/now/balloon/draw/user'),
|
||||
// 全部投注记录
|
||||
getAllBalloonUser: () => request.post('/all/balloon/draw/user'),
|
||||
// 统计全部投注和中奖列表
|
||||
getUserList: () => request.post('/user/balloon/list'),
|
||||
}
|
||||
|
||||
206
src/views/game/balloon/data/index.vue
Normal file
@@ -0,0 +1,206 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<div flex items-center>
|
||||
<div mr-20 flex>
|
||||
<div>游戏状态:</div>
|
||||
<n-switch
|
||||
v-model:value="gameStatus"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
@update:value="handleUpdateValue"
|
||||
/>
|
||||
</div>
|
||||
<div ml-20 flex items-center>
|
||||
<div>本局记录:</div>
|
||||
<n-button type="primary" @click="openModal(1)">预览</n-button>
|
||||
</div>
|
||||
<div ml-20 flex items-center>
|
||||
<div>全部记录:</div>
|
||||
<n-button type="primary" @click="openModal(2)">预览</n-button>
|
||||
</div>
|
||||
</div>
|
||||
<n-modal v-model:show="showModal">
|
||||
<n-card
|
||||
style="width: 900px"
|
||||
:title="modalTitle"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template v-if="keyModal === 1">
|
||||
<div>
|
||||
<span>
|
||||
总投注(豆子):
|
||||
<span text-red>{{ nowData.total_number }}</span>
|
||||
</span>
|
||||
<span ml-20>
|
||||
总积分:
|
||||
<span text-red>{{ nowData.total_integral }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<n-data-table
|
||||
:loading="nowData.Loading"
|
||||
:columns="nowData.Columns"
|
||||
:data="nowData.data"
|
||||
:max-height="700"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
remote
|
||||
@update:sorter="nowData.handleSorterChange"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>
|
||||
<span>
|
||||
总投注(豆子):
|
||||
<span text-red>{{ allData.total_number }}</span>
|
||||
</span>
|
||||
<span ml-20>
|
||||
总积分:
|
||||
<span text-red>{{ allData.total_integral }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<n-data-table
|
||||
:loading="allData.Loading"
|
||||
:columns="allData.Columns"
|
||||
:data="allData.data"
|
||||
:max-height="700"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
remote
|
||||
@update:sorter="allData.handleSorterChange"
|
||||
/>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from '../../api'
|
||||
|
||||
const gameStatus = ref(2)
|
||||
|
||||
onMounted(() => {
|
||||
get_status()
|
||||
})
|
||||
|
||||
const get_status = async () => {
|
||||
const res = await api.getisBalloonStart()
|
||||
gameStatus.value = res.data.balloonStart
|
||||
}
|
||||
|
||||
const handleUpdateValue = async (e) => {
|
||||
await api.setisBalloonStart({
|
||||
Start: e,
|
||||
})
|
||||
$message.success('修改成功')
|
||||
get_status()
|
||||
}
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const keyModal = ref(null)
|
||||
|
||||
const modalTitle = ref('')
|
||||
|
||||
const openModal = (type) => {
|
||||
keyModal.value = type
|
||||
showModal.value = true
|
||||
modalTitle.value = type === 1 ? '本局记录' : '全部记录'
|
||||
if (type === 1) return fetchData(nowData)
|
||||
fetchData(allData)
|
||||
}
|
||||
|
||||
const { value: tempCol } = ref([
|
||||
{
|
||||
title: '昵称',
|
||||
key: 'User',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '电话',
|
||||
key: 'Phone',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '期数',
|
||||
key: 'Periods',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '下注豆子',
|
||||
key: 'TotalCount',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '赢积分',
|
||||
key: 'NumberSum',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '购买秒数',
|
||||
key: 'DrawTime',
|
||||
align: 'center',
|
||||
},
|
||||
])
|
||||
|
||||
const { value: nowData } = ref({
|
||||
Loading: false,
|
||||
Columns: [...tempCol],
|
||||
data: [],
|
||||
total_number: 0,
|
||||
total_integral: 0,
|
||||
handleSorterChange: (sorter) => sortData(sorter, nowData),
|
||||
api: api.getBalloonUser,
|
||||
})
|
||||
|
||||
const { value: allData } = ref({
|
||||
Loading: false,
|
||||
Columns: [...tempCol],
|
||||
data: [],
|
||||
total_number: 0,
|
||||
total_integral: 0,
|
||||
handleSorterChange: (sorter) => sortData(sorter, allData),
|
||||
api: api.getAllBalloonUser,
|
||||
})
|
||||
|
||||
const fetchData = async (target) => {
|
||||
target.Loading = true
|
||||
const res = await target.api()
|
||||
target.data = res.data.data || []
|
||||
target.total_integral = res.data.total_integral
|
||||
target.total_number = res.data.total_number
|
||||
target.Loading = false
|
||||
}
|
||||
|
||||
const sortData = (sorter, target) => {
|
||||
if (!target.Loading) {
|
||||
target.Loading = true
|
||||
switch (sorter.columnKey) {
|
||||
case 'TotalCount':
|
||||
target.Columns[3].sortOrder = !sorter ? false : sorter.order
|
||||
target.data = target.data.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.TotalCount - a.TotalCount
|
||||
return a.TotalCount - b.TotalCount
|
||||
})
|
||||
break
|
||||
case 'NumberSum':
|
||||
target.Columns[4].sortOrder = !sorter ? false : sorter.order
|
||||
target.data = target.data.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.NumberSum - a.NumberSum
|
||||
return a.NumberSum - b.NumberSum
|
||||
})
|
||||
break
|
||||
}
|
||||
target.Loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
214
src/views/game/balloon/statistics/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<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>
|
||||
<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',
|
||||
},
|
||||
},
|
||||
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 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.getUserList(dataObj)
|
||||
const newData = res.data.data || []
|
||||
data.value = newData
|
||||
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 * 10)
|
||||
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 * 10, 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>
|
||||
@@ -1,124 +0,0 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<div flex>
|
||||
<div flex>
|
||||
<div>游戏状态:</div>
|
||||
<n-switch
|
||||
v-model:value="val1"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
@update:value="handleUpdateValue1"
|
||||
/>
|
||||
</div>
|
||||
<div ml-20 flex>
|
||||
<div>点杀状态:</div>
|
||||
<n-switch
|
||||
v-model:value="val"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
@update:value="handleUpdateValue"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div flex>
|
||||
<div>
|
||||
预开期数:
|
||||
<span text-25>{{ list[0]?.periods || 0 }}</span>
|
||||
</div>
|
||||
<div ml-20>
|
||||
剩余开奖时间:
|
||||
<span text-25>{{ time || 0 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div flex flex-wrap justify-between>
|
||||
<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"
|
||||
>
|
||||
<p text-25 op-60 :style="{ color: item.count === 0 ? 'green' : 'red' }">{{ item.count }}</p>
|
||||
</n-card>
|
||||
<div h-0 w-300></div>
|
||||
<div h-0 w-300></div>
|
||||
</div>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import api from '../api'
|
||||
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 val1 = ref(null)
|
||||
const val = ref(null)
|
||||
|
||||
const time = ref(null)
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('1连接成功')
|
||||
}
|
||||
|
||||
ws.onmessage = (msg) => {
|
||||
const res = JSON.parse(msg.data)
|
||||
list.value = res
|
||||
}
|
||||
|
||||
ws1.onopen = () => {
|
||||
console.log('2连接成功')
|
||||
}
|
||||
|
||||
ws1.onmessage = (msg) => {
|
||||
const res = JSON.parse(msg.data)
|
||||
switch (res.code) {
|
||||
case 200:
|
||||
// let num = res.data
|
||||
time.value = res.data
|
||||
break
|
||||
case 301:
|
||||
$message.error(res.msg)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
get_data()
|
||||
get_data1()
|
||||
})
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
ws.close()
|
||||
})
|
||||
|
||||
const handleUpdateValue = async (e) => {
|
||||
const res = await api.setDS({
|
||||
status: e,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
get_data()
|
||||
}
|
||||
|
||||
const handleUpdateValue1 = async (e) => {
|
||||
const res = await api.setStatus({
|
||||
start: e,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
get_data1()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
354
src/views/game/dice/data/index.vue
Normal file
@@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<div flex items-center>
|
||||
<div mr-20 flex>
|
||||
<div>游戏状态:</div>
|
||||
<n-switch
|
||||
v-model:value="val1"
|
||||
:checked-value="1"
|
||||
:unchecked-value="2"
|
||||
@update:value="handleUpdateValue1"
|
||||
/>
|
||||
</div>
|
||||
<div flex items-center>
|
||||
<div>开奖记录:</div>
|
||||
<n-button type="primary" @click="openData">预览</n-button>
|
||||
</div>
|
||||
<div ml-20 flex items-center>
|
||||
<div>本局记录:</div>
|
||||
<n-button type="primary" @click="openJl(1)">预览</n-button>
|
||||
</div>
|
||||
<div ml-20 flex items-center>
|
||||
<div>全部记录:</div>
|
||||
<n-button type="primary" @click="openJl(2)">预览</n-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div flex>
|
||||
<div>
|
||||
预开期数:
|
||||
<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>
|
||||
<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
|
||||
style="width: 900px"
|
||||
title="开奖记录"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-data-table
|
||||
:loading="loading"
|
||||
:columns="columns"
|
||||
:data="data"
|
||||
:max-height="600"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
remote
|
||||
/>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
<!-- 游戏记录 -->
|
||||
<n-modal v-model:show="jlModal">
|
||||
<n-card
|
||||
style="width: 900px"
|
||||
:title="jlTitle"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<div>
|
||||
<span>
|
||||
总投注(豆子):
|
||||
<span text-red>{{ jlData.total_number }}</span>
|
||||
</span>
|
||||
<span ml-20>
|
||||
总积分:
|
||||
<span text-red>{{ jlData.total_integral }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<n-data-table
|
||||
:loading="jlLoading"
|
||||
:columns="jlColumns"
|
||||
:data="jlData.data"
|
||||
:max-height="600"
|
||||
:pagination="false"
|
||||
:bordered="false"
|
||||
remote
|
||||
@update:sorter="handleSorterChange"
|
||||
/>
|
||||
</n-card>
|
||||
</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 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.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 = () => {
|
||||
console.log('2连接成功')
|
||||
}
|
||||
|
||||
ws1.onmessage = (msg) => {
|
||||
const res = JSON.parse(msg.data)
|
||||
switch (res.code) {
|
||||
case 200:
|
||||
// let num = res.data
|
||||
time.value = res.data
|
||||
break
|
||||
case 301:
|
||||
$message.error(res.msg)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// get_data()
|
||||
get_data1()
|
||||
})
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
ws.close()
|
||||
})
|
||||
|
||||
const handleUpdateValue = async (e) => {
|
||||
const res = await api.setDS({
|
||||
status: 1,
|
||||
id: e,
|
||||
user_id: getToken(),
|
||||
Periods: list.value[0]?.Periods,
|
||||
})
|
||||
$message.success(res.msg)
|
||||
// get_data()
|
||||
}
|
||||
|
||||
const handleUpdateValue1 = async (e) => {
|
||||
const res = await api.setStatus({
|
||||
start: 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
|
||||
}
|
||||
}
|
||||
|
||||
const jlModal = ref(false)
|
||||
const jlTitle = ref('')
|
||||
|
||||
const jlData = ref({})
|
||||
|
||||
const jlColumns = ref([
|
||||
{
|
||||
title: '昵称',
|
||||
key: 'User',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '电话',
|
||||
key: 'Phone',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '期数',
|
||||
key: 'Periods',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '下注豆子',
|
||||
key: 'TotalCount',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '赢积分',
|
||||
key: 'NumberSum',
|
||||
align: 'center',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '购买号码',
|
||||
key: 'PeriodsNum',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'DrawTime',
|
||||
align: 'center',
|
||||
},
|
||||
])
|
||||
|
||||
const jlLoading = ref(false)
|
||||
|
||||
const openJl = async (e) => {
|
||||
try {
|
||||
jlModal.value = true
|
||||
jlTitle.value = e === 1 ? '本局游戏记录' : '全部游戏记录'
|
||||
jlLoading.value = true
|
||||
const res = e === 1 ? await api.nowUser() : await api.allUser()
|
||||
jlData.value = res.data
|
||||
} catch (error) {
|
||||
$message.error(error.msg)
|
||||
}
|
||||
jlLoading.value = false
|
||||
}
|
||||
|
||||
const handleSorterChange = (sorter) => {
|
||||
if (!jlLoading.value) {
|
||||
jlLoading.value = true
|
||||
switch (sorter.columnKey) {
|
||||
case 'TotalCount':
|
||||
jlColumns.value[3].sortOrder = !sorter ? false : sorter.order
|
||||
jlData.value.data = jlData.value.data.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.TotalCount - a.TotalCount
|
||||
return a.TotalCount - b.TotalCount
|
||||
})
|
||||
break
|
||||
case 'NumberSum':
|
||||
jlColumns.value[4].sortOrder = !sorter ? false : sorter.order
|
||||
jlData.value.data = jlData.value.data.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.NumberSum - a.NumberSum
|
||||
return a.NumberSum - b.NumberSum
|
||||
})
|
||||
break
|
||||
}
|
||||
jlLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
214
src/views/game/dice/statistics/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<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>
|
||||
<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',
|
||||
},
|
||||
},
|
||||
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 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
|
||||
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 * 10)
|
||||
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 * 10, 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/dice/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>
|
||||
282
src/views/game/home/index.vue
Normal file
@@ -0,0 +1,282 @@
|
||||
<script setup>
|
||||
import { h, ref, resolveDirective, withDirectives } from 'vue'
|
||||
import api from '../api'
|
||||
import { NTag, NImage, NButton, NDropdown } from 'naive-ui'
|
||||
import Upload from '@/components/Upload.vue'
|
||||
import Editor from '@/components/Editor.vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const vPerms = resolveDirective('perms')
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const columns = ref([
|
||||
{
|
||||
title: '游戏封面',
|
||||
align: 'center',
|
||||
slot: 'cover',
|
||||
render: (row) => {
|
||||
return h(NImage, {
|
||||
width: '50',
|
||||
src: row.cover,
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '游戏名称',
|
||||
align: 'center',
|
||||
key: 'name',
|
||||
},
|
||||
{
|
||||
title: '是否开启',
|
||||
align: 'center',
|
||||
slot: 'status',
|
||||
render: (row) => {
|
||||
return h(
|
||||
NTag,
|
||||
{
|
||||
type: row.status === 1 ? 'success' : 'warning',
|
||||
},
|
||||
{
|
||||
default: () => (row.status === 1 ? '开启' : '禁用'),
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
slot: 'action',
|
||||
render: (row) => {
|
||||
return [
|
||||
withDirectives(
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
type: 'primary',
|
||||
text: true,
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
openModal(2, row)
|
||||
},
|
||||
},
|
||||
() => '编辑'
|
||||
),
|
||||
[[vPerms, ['/admin/game/edit']]]
|
||||
),
|
||||
withDirectives(
|
||||
h(
|
||||
'span',
|
||||
{
|
||||
class: 'ml-10',
|
||||
},
|
||||
{
|
||||
default: () => [
|
||||
h(
|
||||
NDropdown,
|
||||
{
|
||||
trigger: 'click',
|
||||
size: 'small',
|
||||
options: [
|
||||
{
|
||||
key: 0,
|
||||
label: '实时数据',
|
||||
},
|
||||
{
|
||||
key: 1,
|
||||
label: '数据统计',
|
||||
},
|
||||
{
|
||||
key: 2,
|
||||
label: '宙斯统计',
|
||||
},
|
||||
],
|
||||
onSelect: (e) => dropdownSelect(e),
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
class: 'ml-5',
|
||||
type: 'primary',
|
||||
text: true,
|
||||
size: 'small',
|
||||
},
|
||||
{
|
||||
default: () => '操作',
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
],
|
||||
}
|
||||
),
|
||||
[[vPerms, ['待定']]]
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const data = ref([])
|
||||
|
||||
const pagination = ref({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
itemCount: 0,
|
||||
onChange: (page) => {
|
||||
pagination.value.page = page
|
||||
getList()
|
||||
},
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const getList = async () => {
|
||||
const res = await api.getGameList({
|
||||
pageSize: pagination.value.pageSize,
|
||||
pageNum: pagination.value.page,
|
||||
})
|
||||
data.value = res.data.data || []
|
||||
pagination.value.itemCount = res.data.total
|
||||
}
|
||||
|
||||
const openModal = (type = null, row) => {
|
||||
if (type) {
|
||||
model.value = {
|
||||
...row,
|
||||
cover: row.cover ? [{ url: row.cover, name: '图片', status: 'finished' }] : [],
|
||||
}
|
||||
}
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const rules = {
|
||||
cover: {
|
||||
required: true,
|
||||
type: 'array',
|
||||
message: '请选择游戏封面',
|
||||
trigger: 'change',
|
||||
},
|
||||
name: {
|
||||
required: true,
|
||||
message: '请输入游戏名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
url: {
|
||||
required: true,
|
||||
message: '请输入游戏链接',
|
||||
trigger: 'blur',
|
||||
},
|
||||
introduction: {
|
||||
required: true,
|
||||
message: '请输入游戏介绍',
|
||||
trigger: 'blur',
|
||||
},
|
||||
}
|
||||
|
||||
const model = ref({
|
||||
name: '',
|
||||
url: '',
|
||||
introduction: '',
|
||||
cover: [],
|
||||
status: 2,
|
||||
})
|
||||
|
||||
const submit = () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
const data = {
|
||||
...model.value,
|
||||
cover: model.value.cover[0].url,
|
||||
}
|
||||
const res = await api.addGame(data)
|
||||
$message.success(res.msg)
|
||||
clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const clear = () => {
|
||||
model.value = {
|
||||
name: '',
|
||||
url: '',
|
||||
introduction: '',
|
||||
cover: [],
|
||||
status: 2,
|
||||
}
|
||||
formRef.value?.restoreValidation()
|
||||
showModal.value = false
|
||||
getList()
|
||||
}
|
||||
|
||||
const route = useRouter()
|
||||
|
||||
const paths = ['/game/game_data', '/game/game_statistics', '/game/game_zs']
|
||||
|
||||
const dropdownSelect = (e) => {
|
||||
route.push(paths[e])
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-button v-perms="['/admin/game/edit']" type="primary" @click="openModal()">添加游戏</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: 900px"
|
||||
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="cover">
|
||||
<Upload v-model:list="model.cover" :max="1" />
|
||||
</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 :span="16" label="游戏链接:" path="url">
|
||||
<n-input v-model:value="model.url" placeholder="请输入游戏链接" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24" label="游戏介绍:" path="introduction">
|
||||
<Editor v-model:value-html="model.introduction" :height="350" />
|
||||
</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="12">
|
||||
<div m-auto p-10>
|
||||
<n-button type="primary" @click="submit">提交</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>
|
||||
|
||||
<style scoped lang="scss"></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,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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
|
||||
|
||||
6
src/views/marketing/api.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import { request } from '@/utils'
|
||||
|
||||
export default {
|
||||
getSignConfig: () => request.post('/gift/setting'),
|
||||
setSignConfig: (data) => request.post('/gift/setting/edit', data),
|
||||
}
|
||||
65
src/views/marketing/sign/index.vue
Normal file
@@ -0,0 +1,65 @@
|
||||
<script setup>
|
||||
import api from '../api'
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const formData = ref({})
|
||||
|
||||
const rules = {}
|
||||
|
||||
onMounted(() => {
|
||||
get_config()
|
||||
})
|
||||
|
||||
const get_config = async () => {
|
||||
const res = await api.getSignConfig()
|
||||
formData.value = res.data.data[0]
|
||||
}
|
||||
|
||||
const ok = () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
try {
|
||||
const data = {
|
||||
Gift: formData.value.gift,
|
||||
Sign: formData.value.sign,
|
||||
WeekSign: formData.value.week_sign,
|
||||
MonthSign: formData.value.month_sign,
|
||||
}
|
||||
await api.setSignConfig(data)
|
||||
$message.success('保存成功')
|
||||
} catch (e) {
|
||||
$message.error(e.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-form ref="formRef" :model="formData" :rules="rules">
|
||||
<n-grid :cols="24" :x-gap="24">
|
||||
<n-form-item-gi :span="24" label="注册赠送" path="gift">
|
||||
<n-input-number v-model:value="formData.gift" :step="100" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24" label="每日赠送" path="sign">
|
||||
<n-input-number v-model:value="formData.sign" :step="100" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24" label="每周赠送" path="week_sign">
|
||||
<n-input-number v-model:value="formData.week_sign" :step="100" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24" label="每月赠送" path="month_sign">
|
||||
<n-input-number v-model:value="formData.month_sign" :step="100" />
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="12">
|
||||
<n-button v-perms="['/admin/gift/setting/edit']" type="primary" @click="ok">
|
||||
保存
|
||||
</n-button>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -1,11 +1,101 @@
|
||||
<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" mt-10>
|
||||
<div>
|
||||
<span>支付方式:</span>
|
||||
<n-radio-group v-model:value="queryData.pay_type">
|
||||
<n-radio-button
|
||||
v-for="song in [
|
||||
{
|
||||
label: '微信',
|
||||
value: 1,
|
||||
},
|
||||
{
|
||||
label: '积分',
|
||||
value: 2,
|
||||
},
|
||||
]"
|
||||
: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>
|
||||
@@ -15,27 +105,155 @@ import api from './api'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const queryData = ref({
|
||||
status: '',
|
||||
time: null,
|
||||
word: '',
|
||||
selectKey: null,
|
||||
pay_type: 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: '用户',
|
||||
align: 'center',
|
||||
key: 'user_name',
|
||||
slot: 'user',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
'div',
|
||||
{},
|
||||
{
|
||||
default: () => row.User.nickName,
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '用户电话',
|
||||
align: 'center',
|
||||
slot: 'phone',
|
||||
render: (row) => {
|
||||
return [
|
||||
h(
|
||||
'div',
|
||||
{},
|
||||
{
|
||||
default: () => row.User.phone,
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品名称',
|
||||
align: 'center',
|
||||
key: 'goods_name',
|
||||
slot: 'goods_name',
|
||||
render: (row) => {
|
||||
const el = []
|
||||
row.OrderGoods.forEach((item) => {
|
||||
el.push(
|
||||
h(
|
||||
'div',
|
||||
{},
|
||||
{
|
||||
default: () =>
|
||||
`${item.Goods.name}|${item.pay_price}元或${item.pay_integral}积分|X${item.number}`,
|
||||
}
|
||||
)
|
||||
)
|
||||
})
|
||||
return el
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '商品价格',
|
||||
title: '商品数量',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
key: 'count',
|
||||
},
|
||||
{
|
||||
title: '订单总价',
|
||||
align: 'center',
|
||||
slot: 'number',
|
||||
render: (row) => h('span', row.pay_type === 1 ? `${row.price}元` : `${row.exchange}积分`),
|
||||
},
|
||||
{
|
||||
title: '支付方式',
|
||||
align: 'center',
|
||||
slot: 'pay_type',
|
||||
render: (row) => h('span', row.pay_type === 1 ? '微信' : '积分'),
|
||||
},
|
||||
{
|
||||
title: '商家名称',
|
||||
align: 'center',
|
||||
slot: 'store_name',
|
||||
render: (row) => h('span', row.Store.name),
|
||||
},
|
||||
// {
|
||||
// title: '订单佣金(元)',
|
||||
// align: 'center',
|
||||
// key: 'commission_number',
|
||||
// },
|
||||
{
|
||||
title: '订单状态',
|
||||
align: 'center',
|
||||
@@ -45,20 +263,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 +294,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 +315,53 @@ onMounted(() => {
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const query_data = {
|
||||
Status: queryData.value.status,
|
||||
PayType: queryData.value.pay_type || '',
|
||||
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.is_show" :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,
|
||||
is_show: 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,
|
||||
is_show: row.is_show,
|
||||
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.is_show,
|
||||
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,18 @@ 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),
|
||||
// 积分赠送
|
||||
addUserPulse: (data) => request.post('/gift/pulse', data),
|
||||
// 获取赠送记录
|
||||
getgiftJllist: (data) => request.post('/user/gift/pluse', data),
|
||||
}
|
||||
|
||||
@@ -1,33 +1,345 @@
|
||||
<template>
|
||||
<CommonPage show-footer :title="$route.title">
|
||||
<n-grid class="mb-10" x-gap="12">
|
||||
<n-gi :span="24">
|
||||
<div flex>
|
||||
<n-card w-250>
|
||||
<n-statistic label="用户总赢积分" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.win" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-250>
|
||||
<n-statistic label="用户积分(留存)" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.integral" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-250>
|
||||
<n-statistic label="总豆子" tabular-nums>
|
||||
<n-number-animation ref="numberAnimationInstRef" :from="0" :to="cardData.pulse" />
|
||||
</n-statistic>
|
||||
</n-card>
|
||||
<n-card ml-10 w-250>
|
||||
<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-250>
|
||||
<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-250>
|
||||
<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-250>
|
||||
<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>
|
||||
<div>
|
||||
<span>筛选状态:</span>
|
||||
<n-radio-group v-model:value="queryParams.status">
|
||||
<n-radio-button
|
||||
v-for="song in songs"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
:label="song.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi :span="24" mt-10>
|
||||
<div>
|
||||
<span>活动赠送:</span>
|
||||
<n-radio-group v-model:value="queryParams.type">
|
||||
<n-radio-button
|
||||
v-for="song in songs1"
|
||||
:key="song.value"
|
||||
:value="song.value"
|
||||
:label="song.label"
|
||||
/>
|
||||
</n-radio-group>
|
||||
</div>
|
||||
</n-gi>
|
||||
<n-gi :span="10">
|
||||
<div mt-10 flex items-center>
|
||||
<span w-100>时间筛选:</span>
|
||||
<n-date-picker
|
||||
v-model:formatted-value="queryParams.time"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
type="datetimerange"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</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" alt="avatarUrl" />
|
||||
<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-tab name="6" 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 v-if="tabVal === '4'" :span="10">
|
||||
<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>
|
||||
<!-- 赠送 -->
|
||||
<n-modal v-model:show="showModal" :mask-closable="false">
|
||||
<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="24" label="豆子" path="inputValue">
|
||||
<n-input-number
|
||||
v-model:value="model.Number"
|
||||
:default-value="0"
|
||||
:min="0"
|
||||
:step="100"
|
||||
/>
|
||||
</n-form-item-gi>
|
||||
<n-form-item-gi :span="24">
|
||||
<n-button type="primary" @click="ok">提交</n-button>
|
||||
<n-button ml-10 @click="addClear">取消</n-button>
|
||||
</n-form-item-gi>
|
||||
</n-grid>
|
||||
</n-form>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
</CommonPage>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { h } from 'vue'
|
||||
import api from './api'
|
||||
import { NDropdown, NButton } from 'naive-ui'
|
||||
import TheIcon from '@/components/icon/TheIcon.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const queryParams = ref({
|
||||
word: '',
|
||||
selectKey: null,
|
||||
status: '',
|
||||
time: null,
|
||||
type: '',
|
||||
})
|
||||
|
||||
const formRef = ref(null)
|
||||
|
||||
const nowUserRow = ref({})
|
||||
|
||||
const showModal = ref(false)
|
||||
|
||||
const model = ref({})
|
||||
|
||||
const rules = {}
|
||||
|
||||
const queryData = ref({
|
||||
time: null,
|
||||
selectKey: 2,
|
||||
})
|
||||
|
||||
const cardData = ref({
|
||||
integral: 0,
|
||||
pulse: 0,
|
||||
today_user: 0,
|
||||
total_number: 0,
|
||||
total_user: 0,
|
||||
win: 0,
|
||||
})
|
||||
|
||||
const songs = ref([
|
||||
{
|
||||
value: 1,
|
||||
label: '未使用',
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
label: '用户赢',
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
label: '用户输',
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
label: '已过期',
|
||||
},
|
||||
])
|
||||
|
||||
const songs1 = ref([
|
||||
{
|
||||
value: 5,
|
||||
label: '注册赠送',
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
label: '签到赠送',
|
||||
},
|
||||
{
|
||||
value: 7,
|
||||
label: '主动赠送',
|
||||
},
|
||||
])
|
||||
|
||||
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 +357,74 @@ const columns = ref([
|
||||
title: '用户积分',
|
||||
align: 'center',
|
||||
key: 'integral',
|
||||
sorter: true,
|
||||
sortOrder: false,
|
||||
},
|
||||
{
|
||||
title: '赢积分',
|
||||
align: 'center',
|
||||
key: 'win',
|
||||
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,
|
||||
},
|
||||
{
|
||||
label: '豆子赠送',
|
||||
key: 2,
|
||||
},
|
||||
],
|
||||
onSelect: (key) => {
|
||||
nowUserRow.value = { ...row }
|
||||
switch (key) {
|
||||
case 1:
|
||||
openDrawer(nowUserRow.value)
|
||||
break
|
||||
case 2:
|
||||
showModal.value = true
|
||||
break
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
iconPlacement: 'right',
|
||||
},
|
||||
{
|
||||
default: () => '更多',
|
||||
icon: () =>
|
||||
h(TheIcon, {
|
||||
icon: 'ant-design:down-outlined',
|
||||
}),
|
||||
}
|
||||
),
|
||||
}
|
||||
),
|
||||
]
|
||||
},
|
||||
},
|
||||
])
|
||||
@@ -64,9 +432,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 +450,366 @@ onMounted(() => {
|
||||
getList()
|
||||
})
|
||||
|
||||
const clear = () => {
|
||||
queryParams.value = {
|
||||
word: '',
|
||||
selectKey: null,
|
||||
status: '',
|
||||
time: null,
|
||||
type: '',
|
||||
}
|
||||
getList()
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const query_data = {
|
||||
Status: queryParams.value.status,
|
||||
Type: queryParams.value.type,
|
||||
StartTime: queryParams.value.time === null ? '' : queryParams.value.time[0] || '',
|
||||
EndTime: queryParams.value.time === null ? '' : queryParams.value.time[1] || '',
|
||||
}
|
||||
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
|
||||
cardData.value.win = res.data.total_win
|
||||
} 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) => {
|
||||
const el = []
|
||||
row.OrderGoods.forEach((item) => {
|
||||
el.push(
|
||||
h(
|
||||
'div',
|
||||
{},
|
||||
{
|
||||
default: () =>
|
||||
`${item.Goods.name}|${item.pay_price}元或${item.pay_integral}积分|X${item.number}`,
|
||||
}
|
||||
)
|
||||
)
|
||||
})
|
||||
return el
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '订单总价',
|
||||
align: 'center',
|
||||
slot: 'number',
|
||||
render: (row) => h('span', row.pay_type === 1 ? `${row.price}元` : `${row.exchange}积分`),
|
||||
},
|
||||
{
|
||||
title: '支付方式',
|
||||
align: 'center',
|
||||
slot: 'pay_type',
|
||||
render: (row) => h('span', row.pay_type === 1 ? '微信' : '积分'),
|
||||
},
|
||||
{
|
||||
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: 'number',
|
||||
},
|
||||
{
|
||||
title: '下单时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
} else if (tabVal.value === '4') {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '积分',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
} else if (tabVal.value === '5') {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '订单号',
|
||||
align: 'center',
|
||||
key: 'oid',
|
||||
},
|
||||
{
|
||||
title: '用户昵称',
|
||||
align: 'center',
|
||||
key: 'nick_name',
|
||||
},
|
||||
{
|
||||
title: '获得积分',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
} else if (tabVal.value === '6') {
|
||||
tabsColumns.value = [
|
||||
{
|
||||
title: '获取豆子',
|
||||
align: 'center',
|
||||
key: 'number',
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
align: 'center',
|
||||
slot: 'type',
|
||||
render: (row) => {
|
||||
let nameStr = ''
|
||||
switch (row.type) {
|
||||
case 5:
|
||||
nameStr = '注册赠送'
|
||||
break
|
||||
case 6:
|
||||
nameStr = '签到赠送'
|
||||
break
|
||||
case 7:
|
||||
nameStr = '主动赠送'
|
||||
break
|
||||
}
|
||||
return h(
|
||||
'span',
|
||||
{},
|
||||
{
|
||||
default: () => nameStr,
|
||||
}
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '获得时间',
|
||||
align: 'center',
|
||||
key: 'add_time',
|
||||
},
|
||||
]
|
||||
}
|
||||
tabsPagination.value.page = 1
|
||||
await 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
|
||||
case '6':
|
||||
res = await api.getgiftJllist(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 '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
|
||||
case 'win':
|
||||
columns.value[5].sortOrder = !sorter ? false : sorter.order
|
||||
data.value = data.value.sort((a, b) => {
|
||||
if (sorter.order === 'descend') return b.win - a.win
|
||||
return a.win - b.win
|
||||
})
|
||||
break
|
||||
case 'pulse':
|
||||
columns.value[6].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
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const tabsClear = async () => {
|
||||
queryData.value = {
|
||||
time: null,
|
||||
selectKey: null,
|
||||
}
|
||||
await getTabsList()
|
||||
}
|
||||
|
||||
const ok = () => {
|
||||
formRef.value?.validate(async (errors) => {
|
||||
if (!errors) {
|
||||
await api.addUserPulse({
|
||||
Uid: nowUserRow.value.uid,
|
||||
...model.value,
|
||||
})
|
||||
addClear()
|
||||
$message.success('提交成功')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const addClear = () => {
|
||||
model.value = {
|
||||
Number: 0,
|
||||
}
|
||||
showModal.value = false
|
||||
getList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||