chore: 初始开源发布
iotlcm-admin-ui:管理前端(基于 Vben Admin 5.5.9 二次开发)。 - 天地图 API Key → YOUR_TIANDITU_KEY - OAuth 客户端 ID → your-client-id,store 加密密钥 → 提示自行替换 - Makefile 移除明文 registry 登录口令,镜像地址改 - appIcon 指向内网图床的链接 → /favicon.ico - 真实手机号 → 合法 11 位编造号(保留号段前 3 位 + 后 4 位) - 真实邮箱 → example.com(署名头内的上游作者邮箱保留) - 移除 CI 触发器 - 补 MIT LICENSE(含 Vben Admin 上游声明)+ README - 统一 LF 行尾并加 .gitattributes 上游 Vben Admin 的版权与许可声明完整保留(各源文件 @Author/@Copyright 头 + LICENSE)。
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
README.md
|
||||||
|
.env.local
|
||||||
|
*.zip
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# 统一使用 LF,避免跨平台协作时产生整文件 diff
|
||||||
|
* text=auto eol=lf
|
||||||
|
|
||||||
|
# 二进制文件不做行尾转换
|
||||||
|
*.png binary
|
||||||
|
*.jpg binary
|
||||||
|
*.gif binary
|
||||||
|
*.ico binary
|
||||||
|
*.woff binary
|
||||||
|
*.woff2 binary
|
||||||
|
*.ttf binary
|
||||||
|
*.pdf binary
|
||||||
|
*.exe binary
|
||||||
|
*.xlsx binary
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
dist.zip
|
||||||
|
dist.tar
|
||||||
|
dist.war
|
||||||
|
.nitro
|
||||||
|
.output
|
||||||
|
*-dist.zip
|
||||||
|
*-dist.tar
|
||||||
|
*-dist.war
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
**/.vitepress/cache
|
||||||
|
.cache
|
||||||
|
.turbo
|
||||||
|
.temp
|
||||||
|
dev-dist
|
||||||
|
.stylelintcache
|
||||||
|
yarn.lock
|
||||||
|
package-lock.json
|
||||||
|
.VSCodeCounter
|
||||||
|
**/backend-mock/data
|
||||||
|
.omx
|
||||||
|
.pnpm-store
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
vite.config.mts.*
|
||||||
|
vite.config.mjs.*
|
||||||
|
vite.config.js.*
|
||||||
|
vite.config.ts.*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
# .vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
.history
|
||||||
|
.cursor
|
||||||
|
|
||||||
|
# AI
|
||||||
|
.agent
|
||||||
|
.agents
|
||||||
|
.claude
|
||||||
|
.codex
|
||||||
|
skills-lock.json
|
||||||
|
.atomcode
|
||||||
|
datalog
|
||||||
|
|
||||||
|
# local-only placeholder lefthook config (silences "No config found" popup)
|
||||||
|
# NOTE: only ignores the GIT-ROOT placeholder; vue/lefthook.yml stays tracked.
|
||||||
|
/lefthook.yml
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021-PRESENT Vben Admin (https://github.com/vbenjs/vue-vben-admin)
|
||||||
|
Copyright (c) 2026 The iotLCM Authors
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
.PHONY: build
|
||||||
|
build:
|
||||||
|
docker build --rm -f vue/build/Dockerfile --progress=plain --platform linux/amd64 -t docker_iotlcmadminui:v0 .
|
||||||
|
|
||||||
|
.PHONY: vue
|
||||||
|
vue:
|
||||||
|
cd vue
|
||||||
|
cd vue && npm i -g corepack
|
||||||
|
cd vue && CI=true pnpm install
|
||||||
|
cd vue && CI=true pnpm build
|
||||||
|
|
||||||
|
.PHONY: build-no-build
|
||||||
|
build-no-build:
|
||||||
|
docker build --rm -f vue/build/DockerfileNoBuild --progress=plain --platform linux/amd64 -t docker_iotlcmadminui:v0 .
|
||||||
|
|
||||||
|
|
||||||
|
dockerbuild: build-no-build
|
||||||
|
echo "docker build success"
|
||||||
|
dockerpush:
|
||||||
|
docker tag docker_iotlcmadminui:v0 ${REGISTRY}/docker_iotlcmadminui:v0
|
||||||
|
docker push ${REGISTRY}/$1:$2
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
# iotlcm-admin-ui
|
||||||
|
|
||||||
|
iotLCM 物联网卡全生命周期管理平台的**管理前端** —— 卡资产、通信管理、资费交易、告警工单、统计分析、账号治理。
|
||||||
|
|
||||||
|
配套后端:[iotlcm-admin-svr](https://git.5iot.com/iotlcm-public/iotlcm-admin-svr)。
|
||||||
|
|
||||||
|
## 上游与许可
|
||||||
|
|
||||||
|
本项目基于 **[Vben Admin](https://github.com/vbenjs/vue-vben-admin) 5.5.9**(MIT License)二次开发。
|
||||||
|
|
||||||
|
- 上游版权与许可声明完整保留在 [`LICENSE`](./LICENSE) 与各源文件的 `@Author` / `@Copyright` 头中
|
||||||
|
- iotLCM 相关的业务页面、API 封装与配置为本项目新增
|
||||||
|
- 上游仓库:[vbenjs/vue-vben-admin](https://github.com/vbenjs/vue-vben-admin)
|
||||||
|
- 上游自带的说明文档保留在 [`vue/README.md`](./vue/README.md)
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
- Vue 3 + Vite + TypeScript
|
||||||
|
- Vben Admin 5.5.9(Ant Design Vue 版:`apps/web-antd`)
|
||||||
|
- Pinia(状态)、Vue Router、TailwindCSS + UnoCSS(原子化样式)
|
||||||
|
- pnpm workspace + Turborepo(monorepo)
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd vue
|
||||||
|
pnpm install
|
||||||
|
pnpm dev:antd
|
||||||
|
```
|
||||||
|
|
||||||
|
开发服务器默认监听 `http://127.0.0.1:5666/`。
|
||||||
|
|
||||||
|
构建:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build:antd
|
||||||
|
```
|
||||||
|
|
||||||
|
产物在 `vue/apps/web-antd/dist/`。
|
||||||
|
|
||||||
|
## 配置
|
||||||
|
|
||||||
|
环境变量文件在 `vue/apps/web-antd/`:
|
||||||
|
|
||||||
|
| 文件 | 用途 |
|
||||||
|
| --- | --- |
|
||||||
|
| `.env` | 通用配置(应用标题、命名空间、客户端 ID、store 加密密钥) |
|
||||||
|
| `.env.development` | 开发环境(端口、API 地址) |
|
||||||
|
| `.env.production` | 生产环境(压缩策略、路由模式、PWA) |
|
||||||
|
|
||||||
|
**公开版里的凭据是占位符**:
|
||||||
|
|
||||||
|
| 变量 | 占位 | 说明 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `VITE_GLOB_CLIENT_ID` | `your-client-id` | OAuth 客户端 ID |
|
||||||
|
| `VITE_APP_STORE_SECURE_KEY` | `please-replace-me-with-your-own-key` | localStorage 加密密钥 |
|
||||||
|
| `TIANDITU_CONFIG.API_KEY` | `YOUR_TIANDITU_KEY` | 天地图 API Key(`src/config/tianditu.ts`) |
|
||||||
|
|
||||||
|
> ⚠️ `VITE_APP_STORE_SECURE_KEY` 必须是自己的随机值——它用于加密持久化的 store,换设备时 store 数据将无法解密。
|
||||||
|
|
||||||
|
## 目录结构
|
||||||
|
|
||||||
|
```text
|
||||||
|
iotlcm-admin-ui/
|
||||||
|
├── Makefile # 容器构建与推送
|
||||||
|
└── vue/ # Vben Admin monorepo
|
||||||
|
├── apps/web-antd/ # iotLCM 管理端应用(主应用)
|
||||||
|
│ ├── src/api/iotlcm/ # iotLCM 业务 API 封装
|
||||||
|
│ ├── src/views/iotlcm/ # iotLCM 业务页面
|
||||||
|
│ ├── src/config/ # 应用配置(含天地图配置)
|
||||||
|
│ └── .env* # 环境变量
|
||||||
|
├── packages/ # 内部共享包
|
||||||
|
└── internal/ # 构建期工具
|
||||||
|
```
|
||||||
|
|
||||||
|
## 容器化构建
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build --rm -f vue/build/Dockerfile --platform linux/amd64 -t iotlcm-admin-ui:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
推送到自己的镜像仓库:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
REGISTRY=<your-registry> make dockerpush
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT](./LICENSE) —— 含 Vben Admin 上游的 MIT 声明。
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
> 1%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
||||||
|
not ie 11
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from '@vben/commitlint-config';
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
*.md
|
||||||
|
dist
|
||||||
|
.turbo
|
||||||
|
dist.zip
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset=utf-8
|
||||||
|
end_of_line=lf
|
||||||
|
insert_final_newline=true
|
||||||
|
indent_style=space
|
||||||
|
indent_size=2
|
||||||
|
max_line_length = 100
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
[*.{yml,yaml,json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
echo Start running commit-msg hook...
|
||||||
|
|
||||||
|
# Check whether the git commit information is standardized
|
||||||
|
pnpm exec commitlint --edit "$1"
|
||||||
|
|
||||||
|
echo Run commit-msg hook done.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# 每次 git pull 之后, 安装依赖
|
||||||
|
|
||||||
|
pnpm install
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# update `.vscode/vben-admin.code-workspace` file
|
||||||
|
pnpm vsh code-workspace --auto-commit
|
||||||
|
|
||||||
|
# Format and submit code according to lintstagedrc.js configuration
|
||||||
|
pnpm exec lint-staged
|
||||||
|
|
||||||
|
echo Run pre-commit hook done.
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
export default {
|
||||||
|
'*.md': ['prettier --cache --ignore-unknown --write'],
|
||||||
|
'*.vue': [
|
||||||
|
'prettier --write',
|
||||||
|
'eslint --cache --fix',
|
||||||
|
'stylelint --fix --allow-empty-input',
|
||||||
|
],
|
||||||
|
'*.{js,jsx,ts,tsx}': [
|
||||||
|
'prettier --cache --ignore-unknown --write',
|
||||||
|
'eslint --cache --fix',
|
||||||
|
],
|
||||||
|
'*.{scss,less,styl,html,vue,css}': [
|
||||||
|
'prettier --cache --ignore-unknown --write',
|
||||||
|
'stylelint --fix --allow-empty-input',
|
||||||
|
],
|
||||||
|
'package.json': ['prettier --cache --write'],
|
||||||
|
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': [
|
||||||
|
'prettier --cache --write--parser json',
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
22.1.0
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
registry=https://registry.npmmirror.com
|
||||||
|
public-hoist-pattern[]=lefthook
|
||||||
|
public-hoist-pattern[]=eslint
|
||||||
|
public-hoist-pattern[]=prettier
|
||||||
|
public-hoist-pattern[]=prettier-plugin-tailwindcss
|
||||||
|
public-hoist-pattern[]=stylelint
|
||||||
|
public-hoist-pattern[]=*postcss*
|
||||||
|
public-hoist-pattern[]=@commitlint/*
|
||||||
|
public-hoist-pattern[]=czg
|
||||||
|
|
||||||
|
strict-peer-dependencies=false
|
||||||
|
auto-install-peers=true
|
||||||
|
dedupe-peer-dependents=true
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
dist
|
||||||
|
dev-dist
|
||||||
|
.local
|
||||||
|
.output.js
|
||||||
|
node_modules
|
||||||
|
.nvmrc
|
||||||
|
coverage
|
||||||
|
CODEOWNERS
|
||||||
|
.nitro
|
||||||
|
.output
|
||||||
|
|
||||||
|
|
||||||
|
**/*.svg
|
||||||
|
**/*.sh
|
||||||
|
|
||||||
|
public
|
||||||
|
.npmrc
|
||||||
|
*-lock.yaml
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from '@vben/prettier-config';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
public
|
||||||
|
__tests__
|
||||||
|
coverage
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"iccid",
|
||||||
|
"imei",
|
||||||
|
"imsi",
|
||||||
|
"IOTLCM",
|
||||||
|
"msisdn"
|
||||||
|
]
|
||||||
|
}
|
||||||
+152
@@ -0,0 +1,152 @@
|
|||||||
|
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
||||||
|
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
|
<h1>Vue Vben Admin</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
[](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin)    
|
||||||
|
|
||||||
|
**English** | [中文](./README.zh-CN.md) | [日本語](./README.ja-JP.md)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Vue Vben Admin is a free and open source middle and back-end template. Using the latest `vue3`, `vite`, `TypeScript` and other mainstream technology development, the out-of-the-box middle and back-end front-end solutions can also be used for learning reference.
|
||||||
|
|
||||||
|
## Upgrade Notice
|
||||||
|
|
||||||
|
This is the latest version, 5.0, and it is not compatible with previous versions. If you are starting a new project, it is recommended to use the latest version. If you wish to view the old version, please use the [v2 branch](https://github.com/vbenjs/vue-vben-admin/tree/v2).
|
||||||
|
|
||||||
|
## Feature
|
||||||
|
|
||||||
|
- **Latest Technology Stack**: Developed with cutting-edge front-end technologies like Vue 3 and Vite
|
||||||
|
- **TypeScript**: A language for application-scale JavaScript
|
||||||
|
- **Themes**: Multiple theme colors available with customizable options
|
||||||
|
- **Internationalization**: Comprehensive built-in internationalization support
|
||||||
|
- **Permissions**: Built-in solution for dynamic route-based permission generation
|
||||||
|
|
||||||
|
## Preview
|
||||||
|
|
||||||
|
- [Vben Admin](https://vben.pro/) - Full version Chinese site
|
||||||
|
|
||||||
|
Test Account: vben/123456
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||||
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||||
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### Use Gitpod
|
||||||
|
|
||||||
|
Open the project in Gitpod (free online dev environment for GitHub) and start coding immediately.
|
||||||
|
|
||||||
|
[](https://gitpod.io/#https://github.com/vbenjs/vue-vben-admin)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[Document](https://doc.vben.pro/)
|
||||||
|
|
||||||
|
## Install and use
|
||||||
|
|
||||||
|
- Get the project code
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||||
|
```
|
||||||
|
|
||||||
|
- Installation dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd vue-vben-admin
|
||||||
|
|
||||||
|
corepack enable
|
||||||
|
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
- run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Change Log
|
||||||
|
|
||||||
|
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
|
||||||
|
|
||||||
|
## How to contribute
|
||||||
|
|
||||||
|
You are very welcome to join Or submit a Pull Request。
|
||||||
|
|
||||||
|
**Pull Request:**
|
||||||
|
|
||||||
|
1. Fork code!
|
||||||
|
2. Create your own branch: `git checkout -b feat/xxxx`
|
||||||
|
3. Submit your changes: `git commit -am 'feat(function): add xxxxx'`
|
||||||
|
4. Push your branch: `git push origin feat/xxxx`
|
||||||
|
5. submit`pull request`
|
||||||
|
|
||||||
|
## Git Contribution submission specification
|
||||||
|
|
||||||
|
- reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) specification ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||||
|
|
||||||
|
- `feat` Add new features
|
||||||
|
- `fix` Fix the problem/BUG
|
||||||
|
- `style` The code style is related and does not affect the running result
|
||||||
|
- `perf` Optimization/performance improvement
|
||||||
|
- `refactor` Refactor
|
||||||
|
- `revert` Undo edit
|
||||||
|
- `test` Test related
|
||||||
|
- `docs` Documentation/notes
|
||||||
|
- `chore` Dependency update/scaffolding configuration modification etc.
|
||||||
|
- `ci` Continuous integration
|
||||||
|
- `types` Type definition file changes
|
||||||
|
- `wip` In development
|
||||||
|
|
||||||
|
## Browser support
|
||||||
|
|
||||||
|
The `Chrome 80+` browser is recommended for local development
|
||||||
|
|
||||||
|
Support modern browsers, not IE
|
||||||
|
|
||||||
|
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||||
|
| :-: | :-: | :-: | :-: | :-: |
|
||||||
|
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||||
|
|
||||||
|
## Maintainer
|
||||||
|
|
||||||
|
[@Vben](https://github.com/anncwb)
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://star-history.com/#vbenjs/vue-vben-admin&Date)
|
||||||
|
|
||||||
|
## Donate
|
||||||
|
|
||||||
|
If you think this project is helpful to you, you can help the author buy a cup of coffee to show your support!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aee;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||||
|
|
||||||
|
## Contributor
|
||||||
|
|
||||||
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
|
<img alt="Contributors"
|
||||||
|
src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
- [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT © Vben-2020](./LICENSE)
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
<div align="center"> <a href="https://github.com/anncwb/vue-vben-admin"> <img alt="VbenAdmin Logo" width="215" src="https://unpkg.com/@vbenjs/static-source@0.1.7/source/logo-v1.webp"> </a> <br> <br>
|
||||||
|
|
||||||
|
[](LICENSE)
|
||||||
|
|
||||||
|
<h1>Vue Vben Admin</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
[](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin)    
|
||||||
|
|
||||||
|
**中文** | [English](./README.md) | [日本語](./README.ja-JP.md)
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
Vue Vben Admin 是 Vue Vben Admin 的升级版本。作为一个免费开源的中后台模板,它采用了最新的 Vue 3、Vite、TypeScript 等主流技术开发,开箱即用,可用于中后台前端开发,也适合学习参考。
|
||||||
|
|
||||||
|
## 升级提示
|
||||||
|
|
||||||
|
该版本为最新版本`5.0`, 与其他版本不兼容,如果你是新项目,建议使用最新版本。如果你想查看旧版本,请使用 [v2 分支](https://github.com/vbenjs/vue-vben-admin/tree/v2)
|
||||||
|
|
||||||
|
## 特性
|
||||||
|
|
||||||
|
- **最新技术栈**:使用 Vue3/vite 等前端前沿技术开发
|
||||||
|
- **TypeScript**: 应用程序级 JavaScript 的语言
|
||||||
|
- **主题**:提供多套主题色彩,可配置自定义主题
|
||||||
|
- **国际化**:内置完善的国际化方案
|
||||||
|
- **权限** 内置完善的动态路由权限生成方案
|
||||||
|
|
||||||
|
## 预览
|
||||||
|
|
||||||
|
- [Vben Admin](https://vben.pro/) - 完整版中文站点
|
||||||
|
|
||||||
|
测试账号: vben/123456
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview1.png">
|
||||||
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview2.png">
|
||||||
|
<img alt="VbenAdmin Logo" width="100%" src="https://anncwb.github.io/anncwb/images/preview3.png">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### 使用 Gitpod
|
||||||
|
|
||||||
|
在 Gitpod(适用于 GitHub 的免费在线开发环境)中打开项目,并立即开始编码.
|
||||||
|
|
||||||
|
[](https://gitpod.io/#https://github.com/vbenjs/vue-vben-admin)
|
||||||
|
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
[文档地址](https://doc.vben.pro/)
|
||||||
|
|
||||||
|
## 安装使用
|
||||||
|
|
||||||
|
- 获取项目代码
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/vbenjs/vue-vben-admin.git
|
||||||
|
```
|
||||||
|
|
||||||
|
- 安装依赖
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd vue-vben-admin
|
||||||
|
|
||||||
|
corepack enable
|
||||||
|
|
||||||
|
pnpm install
|
||||||
|
```
|
||||||
|
|
||||||
|
- 运行
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- 打包
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
## 更新日志
|
||||||
|
|
||||||
|
[CHANGELOG](https://github.com/vbenjs/vue-vben-admin/releases)
|
||||||
|
|
||||||
|
## 如何贡献
|
||||||
|
|
||||||
|
非常欢迎你的加入 或者提交一个 Pull Request。
|
||||||
|
|
||||||
|
**Pull Request:**
|
||||||
|
|
||||||
|
1. Fork 代码!
|
||||||
|
2. 创建自己的分支: `git checkout -b feature/xxxx`
|
||||||
|
3. 提交你的修改: `git commit -am 'feat(function): add xxxxx'`
|
||||||
|
4. 推送您的分支: `git push origin feature/xxxx`
|
||||||
|
5. 提交`pull request`
|
||||||
|
|
||||||
|
## Git 贡献提交规范
|
||||||
|
|
||||||
|
- 参考 [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION.md) 规范 ([Angular](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular))
|
||||||
|
|
||||||
|
- `feat` 增加新功能
|
||||||
|
- `fix` 修复问题/BUG
|
||||||
|
- `style` 代码风格相关无影响运行结果的
|
||||||
|
- `perf` 优化/性能提升
|
||||||
|
- `refactor` 重构
|
||||||
|
- `revert` 撤销修改
|
||||||
|
- `test` 测试相关
|
||||||
|
- `docs` 文档/注释
|
||||||
|
- `chore` 依赖更新/脚手架配置修改等
|
||||||
|
- `ci` 持续集成
|
||||||
|
- `types` 类型定义文件更改
|
||||||
|
- `wip` 开发中
|
||||||
|
|
||||||
|
## 浏览器支持
|
||||||
|
|
||||||
|
本地开发推荐使用`Chrome 80+` 浏览器
|
||||||
|
|
||||||
|
支持现代浏览器, 不支持 IE
|
||||||
|
|
||||||
|
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt=" Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
|
||||||
|
| :-: | :-: | :-: | :-: | :-: |
|
||||||
|
| not support | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
|
||||||
|
|
||||||
|
## 维护者
|
||||||
|
|
||||||
|
[@Vben](https://github.com/anncwb)
|
||||||
|
|
||||||
|
## Star History
|
||||||
|
|
||||||
|
[](https://star-history.com/#vbenjs/vue-vben-admin&Date)
|
||||||
|
|
||||||
|
## 捐赠
|
||||||
|
|
||||||
|
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<a style="display: block;width: 100px;height: 50px;line-height: 50px; color: #fff;text-align: center; background: #408aed;border-radius: 4px;" href="https://www.paypal.com/paypalme/cvvben">Paypal Me</a>
|
||||||
|
|
||||||
|
## Contributor
|
||||||
|
|
||||||
|
<a href="https://github.com/vbenjs/vue-vben-admin/graphs/contributors">
|
||||||
|
<img alt="Contributors"
|
||||||
|
src="https://opencollective.com/vbenjs/contributors.svg?button=false" />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
- [Github Discussions](https://github.com/anncwb/vue-vben-admin/discussions)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
[MIT © Vben-2020](./LICENSE)
|
||||||
+269
@@ -0,0 +1,269 @@
|
|||||||
|
import { h, ref } from 'vue';
|
||||||
|
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||||
|
import { getPopupContainer } from '@vben/utils';
|
||||||
|
import type { DescItem } from '#/components/description';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
import type { VbenFormSchema } from '@vben/common-ui';
|
||||||
|
import { z } from '@vben/common-ui';
|
||||||
|
import { renderDict, renderPopoverMemberSumma, type MemberSumma } from '#/utils';
|
||||||
|
import { getDictOptions } from '#/utils/dict';
|
||||||
|
import FormOptions from '#/constants/FormOption';
|
||||||
|
|
||||||
|
export class State {
|
||||||
|
public id = 0; // 主键ID
|
||||||
|
public apiName = ''; // 接口名称
|
||||||
|
public apiCode = ''; // 接口权限标识
|
||||||
|
public method = ''; // 请求方法:GET/POST/PUT/DELETE
|
||||||
|
public path = ''; // 接口路径
|
||||||
|
public status = 0; // 状态:0启用,1停用
|
||||||
|
public version = ''; // 接口版本
|
||||||
|
public remark = ''; // 备注
|
||||||
|
public tenantId = ''; // 租户ID
|
||||||
|
public createdDept = 0; // 创建部门
|
||||||
|
public createdBy = 0; // 创建者
|
||||||
|
public createdBySumma?: null | MemberSumma = null; // 创建者摘要信息
|
||||||
|
public createdAt = ''; // 创建时间
|
||||||
|
public updatedBy = 0; // 更新者
|
||||||
|
public updatedBySumma?: null | MemberSumma = null; // 更新者摘要信息
|
||||||
|
public updatedAt = ''; // 更新时间
|
||||||
|
|
||||||
|
constructor(state?: Partial<State>) {
|
||||||
|
if (state) {
|
||||||
|
Object.assign(this, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newState(state: State | Record<string, any> | null): State {
|
||||||
|
if (state !== null) {
|
||||||
|
if (state instanceof State) {
|
||||||
|
return cloneDeep(state);
|
||||||
|
}
|
||||||
|
return new State(state);
|
||||||
|
}
|
||||||
|
return new State();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
|
||||||
|
// 表格搜索表单
|
||||||
|
export const querySchema: VbenFormSchema[] = [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'InputNumber',
|
||||||
|
label: '主键ID',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入主键ID',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
}, rules:null,
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, {
|
||||||
|
fieldName: 'status',
|
||||||
|
component: 'Select',
|
||||||
|
label: '状态:0启用,1停用',
|
||||||
|
defaultValue: null,
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择状态:0启用,1停用',
|
||||||
|
options: getDictOptions('sys_common_status'),
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:'selectRequired',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'createdAt',
|
||||||
|
component: 'RangePicker',
|
||||||
|
label: '创建时间',
|
||||||
|
componentProps: {
|
||||||
|
type: 'datetimerange',
|
||||||
|
clearable: true,
|
||||||
|
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
}, rules:null,
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
},];
|
||||||
|
|
||||||
|
// 表格列
|
||||||
|
export const columns: VxeGridProps['columns'] = [
|
||||||
|
{
|
||||||
|
title: '接口名称',
|
||||||
|
field: 'apiName',
|
||||||
|
align: 'left',
|
||||||
|
width: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '接口权限标识',
|
||||||
|
field: 'apiCode',
|
||||||
|
align: 'left',
|
||||||
|
width: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '请求方法',
|
||||||
|
field: 'method',
|
||||||
|
align: 'left',
|
||||||
|
width: -1,
|
||||||
|
slots: {
|
||||||
|
default: ({ row }) => {
|
||||||
|
return renderDict(row.method, DictEnum.T_METHOD);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '接口路径',
|
||||||
|
field: 'path',
|
||||||
|
align: 'left',
|
||||||
|
width: -1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态', field: 'status', align: 'left', width: -1,
|
||||||
|
slots: {
|
||||||
|
default: ({ row }) => {
|
||||||
|
return renderDict(row.status, DictEnum.GLOBAL_ENABLED);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '接口版本',
|
||||||
|
field: 'version',
|
||||||
|
align: 'left',
|
||||||
|
width: -1,
|
||||||
|
},
|
||||||
|
|
||||||
|
{ title: '操作', width: 120, slots: { default: 'action' } },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 表格列接口
|
||||||
|
export interface RowType {
|
||||||
|
id: number;
|
||||||
|
apiName: string;
|
||||||
|
apiCode: string;
|
||||||
|
method: string;
|
||||||
|
path: string;
|
||||||
|
status: number;
|
||||||
|
version: string;
|
||||||
|
remark: string;
|
||||||
|
tenantId: string;
|
||||||
|
createdDept: number;
|
||||||
|
createdBy: number;
|
||||||
|
createdAt: string;
|
||||||
|
updatedBy: number;
|
||||||
|
updatedAt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 查看字段列表
|
||||||
|
export const viewSchema: DescItem[] = [
|
||||||
|
{ field: 'apiName', label: '接口名称'},
|
||||||
|
{ field: 'apiCode', label: '接口权限标识'},
|
||||||
|
{ field: 'method', label: '请求方法:GET/POST/PUT/DELETE'},
|
||||||
|
{ field: 'path', label: '接口路径'},
|
||||||
|
{
|
||||||
|
field: 'status',
|
||||||
|
label: '状态',
|
||||||
|
render: (val) => renderDict(val, 'sys_common_status'),
|
||||||
|
},
|
||||||
|
{ field: 'version', label: '接口版本'},
|
||||||
|
{ field: 'remark', label: '备注'},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 编辑字段列表
|
||||||
|
export const editSchema: VbenFormSchema[] = [
|
||||||
|
{
|
||||||
|
fieldName: 'id',
|
||||||
|
component: 'Input',
|
||||||
|
label: '主键ID',
|
||||||
|
dependencies: { show: () => false, triggerFields: [''], },
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, {
|
||||||
|
fieldName: 'apiName',
|
||||||
|
component: 'Input',
|
||||||
|
label: '接口名称',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入接口名称',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, {
|
||||||
|
fieldName: 'apiCode',
|
||||||
|
component: 'Input',
|
||||||
|
label: '接口权限标识',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入接口权限标识',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, {
|
||||||
|
fieldName: 'method',
|
||||||
|
component: 'Select',
|
||||||
|
label: '请求方法',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请选择请求方法',
|
||||||
|
options: getDictOptions(DictEnum.T_METHOD ),
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, {
|
||||||
|
fieldName: 'path',
|
||||||
|
component: 'Input',
|
||||||
|
label: '接口路径',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入接口路径',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'status',
|
||||||
|
component: 'Switch',
|
||||||
|
label: '状态',
|
||||||
|
defaultValue: 0,
|
||||||
|
componentProps: FormOptions.Component.SwitchEnabled(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldName: 'version',
|
||||||
|
component: 'Input',
|
||||||
|
label: '接口版本',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入接口版本',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:'required',
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, {
|
||||||
|
fieldName: 'remark',
|
||||||
|
component: 'Input',
|
||||||
|
label: '备注',
|
||||||
|
componentProps: {
|
||||||
|
placeholder: '请输入备注',
|
||||||
|
onUpdateValue: (e: any) => {
|
||||||
|
console.log(e);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rules:null,
|
||||||
|
formItemClass: 'col-span-1',
|
||||||
|
}, ];
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
backend-mock/nitro.config.ts
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
# 应用标题
|
||||||
|
VITE_APP_TITLE=物联网全生命周期管理平台
|
||||||
|
VBEN_LOGO_URL=/images/logo.png
|
||||||
|
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||||
|
VITE_APP_NAMESPACE=iotlcm-admin-web
|
||||||
|
# 客户端ID
|
||||||
|
VITE_GLOB_CLIENT_ID=your-client-id
|
||||||
|
# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密
|
||||||
|
VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# public path
|
||||||
|
VITE_BASE=/
|
||||||
|
|
||||||
|
# Basic interface address SPA
|
||||||
|
VITE_GLOB_API_URL=/api
|
||||||
|
|
||||||
|
VITE_VISUALIZER=true
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# 端口号
|
||||||
|
VITE_PORT=5666
|
||||||
|
|
||||||
|
VITE_BASE=/
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
VITE_GLOB_API_URL=/api
|
||||||
|
|
||||||
|
# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
|
||||||
|
VITE_NITRO_MOCK=false
|
||||||
|
|
||||||
|
# 是否打开 devtools,true 为打开,false 为关闭
|
||||||
|
VITE_DEVTOOLS=false
|
||||||
|
|
||||||
|
# 是否注入全局loading
|
||||||
|
VITE_INJECT_APP_LOADING=true
|
||||||
|
|
||||||
|
# 客户端ID
|
||||||
|
VITE_GLOB_CLIENT_ID=your-client-id
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
VITE_BASE=/
|
||||||
|
|
||||||
|
# 接口地址
|
||||||
|
VITE_GLOB_API_URL=/api
|
||||||
|
|
||||||
|
# 是否开启压缩,可以设置为 none, brotli, gzip
|
||||||
|
VITE_COMPRESS=none
|
||||||
|
|
||||||
|
# 是否开启 PWA
|
||||||
|
VITE_PWA=false
|
||||||
|
|
||||||
|
# vue-router 的模式
|
||||||
|
VITE_ROUTER_HISTORY=hash
|
||||||
|
|
||||||
|
# 是否注入全局loading
|
||||||
|
VITE_INJECT_APP_LOADING=true
|
||||||
|
|
||||||
|
# 打包后是否生成dist.zip
|
||||||
|
VITE_ARCHIVER=true
|
||||||
|
|
||||||
|
# 客户端ID
|
||||||
|
VITE_GLOB_CLIENT_ID=your-client-id
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
<meta name="renderer" content="webkit" />
|
||||||
|
<meta name="description" content="A Modern Back-end Management System" />
|
||||||
|
<meta name="keywords" content="Vben Admin Vue3 Vite" />
|
||||||
|
<meta name="author" content="Vben" />
|
||||||
|
<meta
|
||||||
|
name="viewport"
|
||||||
|
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||||
|
/>
|
||||||
|
<title></title>
|
||||||
|
<link rel="icon" href="" />
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
try {
|
||||||
|
var cacheText = window.localStorage.getItem('runtime-setting');
|
||||||
|
var cache = cacheText ? JSON.parse(cacheText) : null;
|
||||||
|
var setting = cache && cache.setting;
|
||||||
|
|
||||||
|
if (!setting) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.__APP_SETTING__ = setting;
|
||||||
|
|
||||||
|
if (setting.appTitle) {
|
||||||
|
document.title = setting.appTitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setting.appIcon) {
|
||||||
|
var icon = document.querySelector('link[rel="icon"]');
|
||||||
|
if (icon) {
|
||||||
|
icon.href = setting.appIcon;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
<style>
|
||||||
|
html {
|
||||||
|
/* same as ant-design-vue/dist/reset.css setting, avoid the title line-height changed */
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #f4f7f9;
|
||||||
|
transition: background-color 0.4s ease;
|
||||||
|
|
||||||
|
/* transition: all 0.8s ease-out; */
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading.hidden {
|
||||||
|
pointer-events: none;
|
||||||
|
visibility: hidden;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.8s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .loading {
|
||||||
|
background: #0d0d10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin-top: 66px;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgb(0 0 0 / 85%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .title {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader {
|
||||||
|
position: relative;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 60px;
|
||||||
|
left: 0;
|
||||||
|
width: 48px;
|
||||||
|
height: 5px;
|
||||||
|
content: '';
|
||||||
|
background: hsl(var(--primary, 210 100% 50%) / 50%);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: shadow-ani 0.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loader::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
content: '';
|
||||||
|
background: hsl(var(--primary, 210 100% 50%));
|
||||||
|
border-radius: 4px;
|
||||||
|
animation: jump-ani 0.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-wrap {
|
||||||
|
position: relative;
|
||||||
|
min-width: 420px;
|
||||||
|
min-height: 88px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title,
|
||||||
|
.status-text,
|
||||||
|
.status-subtitle {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
width: 100%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
transition:
|
||||||
|
opacity 0.45s ease,
|
||||||
|
transform 0.45s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: rgb(0 0 0 / 85%);
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-text {
|
||||||
|
top: 0;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: rgb(0 0 0 / 85%);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-subtitle {
|
||||||
|
top: 48px;
|
||||||
|
font-size: 13px;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
color: rgb(100 116 139 / 88%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-wrap.is-switching .status-text,
|
||||||
|
.title-wrap.is-switching .status-subtitle {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading.app-title-ready .status-text,
|
||||||
|
.loading.app-title-ready .status-subtitle {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-50%) translateY(-10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading.app-title-ready .title {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .status-text {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .status-subtitle {
|
||||||
|
color: rgb(148 163 184 / 78%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes jump-ani {
|
||||||
|
15% {
|
||||||
|
border-bottom-right-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
transform: translateY(9px) rotate(22.5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
border-bottom-right-radius: 40px;
|
||||||
|
transform: translateY(18px) scale(1, 0.9) rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
transform: translateY(9px) rotate(67.5deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: translateY(0) rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shadow-ani {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(1.2, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div class="loading" id="__app-loading__">
|
||||||
|
<div class="loader" aria-hidden="true"></div>
|
||||||
|
<div class="title-wrap">
|
||||||
|
<div class="status-text" id="__app-loading-status__">正在初始化应用...</div>
|
||||||
|
<div class="status-subtitle" id="__app-loading-subtitle__">
|
||||||
|
请稍候,系统正在准备运行环境
|
||||||
|
</div>
|
||||||
|
<div class="title" id="__app-loading-title__">智联·云</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script data-app-loading="runtime-title">
|
||||||
|
(function () {
|
||||||
|
var loadingElement = document.getElementById('__app-loading__');
|
||||||
|
var statusElement = document.getElementById('__app-loading-status__');
|
||||||
|
var subtitleElement = document.getElementById('__app-loading-subtitle__');
|
||||||
|
var titleElement = document.getElementById('__app-loading-title__');
|
||||||
|
var titleWrapElement = titleElement && titleElement.parentElement;
|
||||||
|
var messageTimer = null;
|
||||||
|
var messageIndex = 0;
|
||||||
|
|
||||||
|
var messages = [
|
||||||
|
{
|
||||||
|
status: '正在初始化应用...',
|
||||||
|
subtitle: '请稍候,系统正在准备运行环境',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: '正在连接配置中心...',
|
||||||
|
subtitle: '正在读取启动所需的运行时配置',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: '正在装载页面资源...',
|
||||||
|
subtitle: '首次加载可能略慢,请稍候片刻',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: '正在校准界面状态...',
|
||||||
|
subtitle: '即将进入系统首页',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
if (
|
||||||
|
!loadingElement ||
|
||||||
|
!statusElement ||
|
||||||
|
!subtitleElement ||
|
||||||
|
!titleElement ||
|
||||||
|
!titleWrapElement
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyMessage(message) {
|
||||||
|
statusElement.textContent = message.status;
|
||||||
|
subtitleElement.textContent = message.subtitle;
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchMessage(message) {
|
||||||
|
titleWrapElement.classList.add('is-switching');
|
||||||
|
window.setTimeout(function () {
|
||||||
|
applyMessage(message);
|
||||||
|
titleWrapElement.classList.remove('is-switching');
|
||||||
|
}, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
function startMessageLoop() {
|
||||||
|
statusElement.style.opacity = '';
|
||||||
|
subtitleElement.style.opacity = '';
|
||||||
|
applyMessage(messages[0]);
|
||||||
|
messageTimer = window.setInterval(function () {
|
||||||
|
messageIndex = (messageIndex + 1) % messages.length;
|
||||||
|
switchMessage(messages[messageIndex]);
|
||||||
|
}, 1800);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopMessageLoop() {
|
||||||
|
if (messageTimer !== null) {
|
||||||
|
window.clearInterval(messageTimer);
|
||||||
|
messageTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTitle(setting) {
|
||||||
|
var appTitle = setting && setting.appTitle;
|
||||||
|
|
||||||
|
if (appTitle) {
|
||||||
|
stopMessageLoop();
|
||||||
|
titleElement.textContent = appTitle;
|
||||||
|
loadingElement.classList.add('app-title-ready');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置已就绪,直接展示标题
|
||||||
|
if (window.__APP_SETTING__) {
|
||||||
|
applyTitle(window.__APP_SETTING__);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 配置未就绪,直接展示默认加载文案
|
||||||
|
startMessageLoop();
|
||||||
|
|
||||||
|
// 等待配置返回后,若已启动文案轮播则停止,并展示标题
|
||||||
|
if (window.__APP_SETTING_PROMISE__) {
|
||||||
|
window.__APP_SETTING_PROMISE__
|
||||||
|
.then(function (setting) {
|
||||||
|
applyTitle(setting);
|
||||||
|
})
|
||||||
|
.catch(function () {});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"name": "@vben/web-antd",
|
||||||
|
"version": "5.5.9",
|
||||||
|
"homepage": "https://vben.pro",
|
||||||
|
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||||
|
"directory": "apps/web-antd"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "vben",
|
||||||
|
"email": "annvben@example.com",
|
||||||
|
"url": "https://github.com/anncwb"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm vite build --mode production",
|
||||||
|
"build:analyze": "pnpm vite build --mode analyze",
|
||||||
|
"dev": "pnpm vite --mode development",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"typecheck": "vue-tsc --noEmit --skipLibCheck"
|
||||||
|
},
|
||||||
|
"imports": {
|
||||||
|
"#/*": "./src/*"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@highlightjs/vue-plugin": "^2.1.0",
|
||||||
|
"@tinymce/tinymce-vue": "^6.1.0",
|
||||||
|
"@vben/access": "workspace:*",
|
||||||
|
"@vben/common-ui": "workspace:*",
|
||||||
|
"@vben/constants": "workspace:*",
|
||||||
|
"@vben/hooks": "workspace:*",
|
||||||
|
"@vben/icons": "workspace:*",
|
||||||
|
"@vben/layouts": "workspace:*",
|
||||||
|
"@vben/locales": "workspace:*",
|
||||||
|
"@vben/plugins": "workspace:*",
|
||||||
|
"@vben/preferences": "workspace:*",
|
||||||
|
"@vben/request": "workspace:*",
|
||||||
|
"@vben/stores": "workspace:*",
|
||||||
|
"@vben/styles": "workspace:*",
|
||||||
|
"@vben/types": "workspace:*",
|
||||||
|
"@vben/utils": "workspace:*",
|
||||||
|
"@vueuse/core": "catalog:",
|
||||||
|
"ant-design-vue": "catalog:",
|
||||||
|
"cropperjs": "^1.6.2",
|
||||||
|
"dayjs": "catalog:",
|
||||||
|
"highlight.js": "^11.11.1",
|
||||||
|
"js-md5": "^0.8.3",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"pinia": "catalog:",
|
||||||
|
"tinymce": "^7.3.0",
|
||||||
|
"vue": "catalog:",
|
||||||
|
"vue-router": "catalog:"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/lodash-es": "^4.17.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||||
|
import type { PluginOption } from 'vite';
|
||||||
|
|
||||||
|
import { Buffer } from 'node:buffer';
|
||||||
|
import { constants as fsConstants } from 'node:fs';
|
||||||
|
import fs from 'node:fs/promises';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
interface ApiTypeGeneratorOptions {
|
||||||
|
apiRoot: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DirNode {
|
||||||
|
children?: DirNode[];
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WriteTypePayload {
|
||||||
|
autoImport?: boolean;
|
||||||
|
content?: string;
|
||||||
|
dir?: string;
|
||||||
|
fileName?: string;
|
||||||
|
overwrite?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CheckTypeFilePayload {
|
||||||
|
items?: CheckTypeFileItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CheckTypeFileItem {
|
||||||
|
dir?: string;
|
||||||
|
fileName?: string;
|
||||||
|
key?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const API_TYPE_GENERATOR_PREFIX = '/__api-type-generator__';
|
||||||
|
|
||||||
|
export function apiTypeGeneratorPlugin(
|
||||||
|
options: ApiTypeGeneratorOptions,
|
||||||
|
): PluginOption {
|
||||||
|
const apiRoot = path.resolve(options.apiRoot);
|
||||||
|
|
||||||
|
return {
|
||||||
|
apply: 'serve',
|
||||||
|
configureServer(server) {
|
||||||
|
server.middlewares.use(async (request, response, next) => {
|
||||||
|
const url = request.url?.split('?')[0] ?? '';
|
||||||
|
|
||||||
|
if (!url.startsWith(API_TYPE_GENERATOR_PREFIX)) {
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (
|
||||||
|
request.method === 'GET' &&
|
||||||
|
url === `${API_TYPE_GENERATOR_PREFIX}/dirs`
|
||||||
|
) {
|
||||||
|
const data = await scanApiDirs(apiRoot);
|
||||||
|
|
||||||
|
sendJson(response, 200, { data, success: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.method === 'POST' &&
|
||||||
|
url === `${API_TYPE_GENERATOR_PREFIX}/exists`
|
||||||
|
) {
|
||||||
|
const payload = await readJsonBody<CheckTypeFilePayload>(request);
|
||||||
|
const data = await checkTypeFiles(apiRoot, payload);
|
||||||
|
|
||||||
|
sendJson(response, 200, { data, success: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
request.method === 'POST' &&
|
||||||
|
url === `${API_TYPE_GENERATOR_PREFIX}/write`
|
||||||
|
) {
|
||||||
|
const payload = await readJsonBody<WriteTypePayload>(request);
|
||||||
|
const data = await writeTypeFile(apiRoot, payload);
|
||||||
|
|
||||||
|
sendJson(response, 200, { data, success: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendJson(response, 404, {
|
||||||
|
message: '接口不存在',
|
||||||
|
success: false,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
const statusCode =
|
||||||
|
error instanceof ApiTypeGeneratorError ? error.status : 500;
|
||||||
|
|
||||||
|
sendJson(response, statusCode, {
|
||||||
|
code:
|
||||||
|
error instanceof ApiTypeGeneratorError ? error.code : 'UNKNOWN',
|
||||||
|
message: error instanceof Error ? error.message : '生成失败',
|
||||||
|
success: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
name: 'web-antd:api-type-generator',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class ApiTypeGeneratorError extends Error {
|
||||||
|
constructor(
|
||||||
|
message: string,
|
||||||
|
public status = 400,
|
||||||
|
public code = 'BAD_REQUEST',
|
||||||
|
) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function accessFile(filePath: string) {
|
||||||
|
try {
|
||||||
|
await fs.access(filePath, fsConstants.F_OK);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureInApiRoot(apiRoot: string, targetPath: string) {
|
||||||
|
const relativePath = path.relative(apiRoot, targetPath);
|
||||||
|
|
||||||
|
if (
|
||||||
|
relativePath.startsWith('..') ||
|
||||||
|
path.isAbsolute(relativePath) ||
|
||||||
|
relativePath === ''
|
||||||
|
) {
|
||||||
|
if (relativePath === '') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ApiTypeGeneratorError('目标路径不在 src/api 目录下');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractTypeNames(content: string) {
|
||||||
|
const names = new Set<string>();
|
||||||
|
const regex = /export\s+(?:interface|type)\s+([A-Za-z_$][\w$]*)/g;
|
||||||
|
const results = content.matchAll(regex);
|
||||||
|
|
||||||
|
for (const result of results) {
|
||||||
|
const typeName = result[1];
|
||||||
|
|
||||||
|
if (!/(?:Param|Res)$/u.test(typeName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
names.add(typeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...names];
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatTypeImport(typeNames: string[], importPath: string) {
|
||||||
|
const sortedNames = [...new Set(typeNames)].toSorted();
|
||||||
|
|
||||||
|
if (sortedNames.length === 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sortedNames.length === 1) {
|
||||||
|
return `import type { ${sortedNames[0]} } from '${importPath}';`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `import type {\n ${sortedNames.join(',\n ')},\n} from '${importPath}';`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getImportPath(fileName: string) {
|
||||||
|
return `./${fileName.replace(/\.ts$/u, '')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSafeFileName(fileName?: string) {
|
||||||
|
const safeFileName = fileName?.trim() || 'type.ts';
|
||||||
|
|
||||||
|
if (
|
||||||
|
!/^[\w.-]+\.ts$/u.test(safeFileName) ||
|
||||||
|
safeFileName.includes('..') ||
|
||||||
|
safeFileName.includes('/') ||
|
||||||
|
safeFileName.includes('\\')
|
||||||
|
) {
|
||||||
|
throw new ApiTypeGeneratorError('文件名必须是单个 .ts 文件名');
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSafeRelativeDir(dir?: string) {
|
||||||
|
const safeDir = (dir ?? '').trim().replaceAll('\\', '/');
|
||||||
|
|
||||||
|
if (!safeDir || safeDir === '.') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.isAbsolute(safeDir) || safeDir.split('/').includes('..')) {
|
||||||
|
throw new ApiTypeGeneratorError('目录路径不合法');
|
||||||
|
}
|
||||||
|
|
||||||
|
return safeDir.split('/').filter(Boolean).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeTypeImport(
|
||||||
|
source: string,
|
||||||
|
importPath: string,
|
||||||
|
typeNames: string[],
|
||||||
|
) {
|
||||||
|
const importRegex = new RegExp(
|
||||||
|
String.raw`import\s+type\s+\{([\s\S]*?)\}\s+from\s+['"]${escapeRegExp(
|
||||||
|
importPath,
|
||||||
|
)}['"];?`,
|
||||||
|
'u',
|
||||||
|
);
|
||||||
|
const matchedImport = source.match(importRegex);
|
||||||
|
|
||||||
|
if (matchedImport) {
|
||||||
|
const existedNames = matchedImport[1]
|
||||||
|
.split(',')
|
||||||
|
.map((item) => item.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
const nextImport = formatTypeImport(
|
||||||
|
[...existedNames, ...typeNames],
|
||||||
|
importPath,
|
||||||
|
);
|
||||||
|
|
||||||
|
return source.replace(importRegex, nextImport);
|
||||||
|
}
|
||||||
|
|
||||||
|
const importStatement = formatTypeImport(typeNames, importPath);
|
||||||
|
|
||||||
|
if (!importStatement) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${importStatement}\n\n${source}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(value: string) {
|
||||||
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readJsonBody<T>(request: IncomingMessage) {
|
||||||
|
const chunks: Buffer[] = [];
|
||||||
|
|
||||||
|
for await (const chunk of request) {
|
||||||
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = Buffer.concat(chunks).toString('utf8');
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
return {} as T;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(content) as T;
|
||||||
|
} catch {
|
||||||
|
throw new ApiTypeGeneratorError('请求体必须是合法 JSON');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function scanApiDirs(apiRoot: string) {
|
||||||
|
async function scan(currentDir: string): Promise<DirNode[]> {
|
||||||
|
const dirents = await fs.readdir(currentDir, { withFileTypes: true });
|
||||||
|
const nodes = await Promise.all(
|
||||||
|
dirents
|
||||||
|
.filter((dirent) => dirent.isDirectory())
|
||||||
|
.toSorted((prev, next) => prev.name.localeCompare(next.name))
|
||||||
|
.map(async (dirent) => {
|
||||||
|
const fullPath = path.join(currentDir, dirent.name);
|
||||||
|
const relativePath = path
|
||||||
|
.relative(apiRoot, fullPath)
|
||||||
|
.replaceAll(path.sep, '/');
|
||||||
|
const children = await scan(fullPath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
children: children.length > 0 ? children : undefined,
|
||||||
|
key: relativePath,
|
||||||
|
title: dirent.name,
|
||||||
|
value: relativePath,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
children: await scan(apiRoot),
|
||||||
|
key: '.',
|
||||||
|
title: 'api',
|
||||||
|
value: '.',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkTypeFiles(
|
||||||
|
apiRoot: string,
|
||||||
|
payload: CheckTypeFilePayload,
|
||||||
|
) {
|
||||||
|
const items = payload.items ?? [];
|
||||||
|
|
||||||
|
if (!Array.isArray(items)) {
|
||||||
|
throw new ApiTypeGeneratorError('检查参数必须是数组');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(
|
||||||
|
items.map(async (item, index) => {
|
||||||
|
const relativeDir = getSafeRelativeDir(item.dir);
|
||||||
|
const fileName = getSafeFileName(item.fileName);
|
||||||
|
const targetDir = path.resolve(apiRoot, relativeDir);
|
||||||
|
|
||||||
|
ensureInApiRoot(apiRoot, targetDir);
|
||||||
|
|
||||||
|
const targetPath = path.resolve(targetDir, fileName);
|
||||||
|
|
||||||
|
ensureInApiRoot(apiRoot, targetPath);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dir: relativeDir || '.',
|
||||||
|
exists: await accessFile(targetPath),
|
||||||
|
fileName,
|
||||||
|
filePath: path.relative(apiRoot, targetPath).replaceAll(path.sep, '/'),
|
||||||
|
key: item.key ?? String(index),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendJson(response: ServerResponse, statusCode: number, data: unknown) {
|
||||||
|
response.statusCode = statusCode;
|
||||||
|
response.setHeader('Content-Type', 'application/json;charset=utf-8');
|
||||||
|
response.end(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateIndexTypeImport(
|
||||||
|
targetDir: string,
|
||||||
|
fileName: string,
|
||||||
|
content: string,
|
||||||
|
) {
|
||||||
|
const indexPath = path.join(targetDir, 'index.ts');
|
||||||
|
const hasIndexFile = await accessFile(indexPath);
|
||||||
|
|
||||||
|
if (!hasIndexFile) {
|
||||||
|
return {
|
||||||
|
importSkipped: true,
|
||||||
|
importUpdated: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeNames = extractTypeNames(content);
|
||||||
|
|
||||||
|
if (typeNames.length === 0) {
|
||||||
|
return {
|
||||||
|
importSkipped: true,
|
||||||
|
importUpdated: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const source = await fs.readFile(indexPath, 'utf8');
|
||||||
|
const nextSource = mergeTypeImport(
|
||||||
|
source,
|
||||||
|
getImportPath(fileName),
|
||||||
|
typeNames,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nextSource !== source) {
|
||||||
|
await fs.writeFile(indexPath, nextSource, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
importSkipped: false,
|
||||||
|
importUpdated: nextSource !== source,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function writeTypeFile(apiRoot: string, payload: WriteTypePayload) {
|
||||||
|
const relativeDir = getSafeRelativeDir(payload.dir);
|
||||||
|
const fileName = getSafeFileName(payload.fileName);
|
||||||
|
const content = payload.content ?? '';
|
||||||
|
|
||||||
|
if (!content.trim()) {
|
||||||
|
throw new ApiTypeGeneratorError('类型定义内容不能为空');
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetDir = path.resolve(apiRoot, relativeDir);
|
||||||
|
|
||||||
|
ensureInApiRoot(apiRoot, targetDir);
|
||||||
|
|
||||||
|
const targetPath = path.resolve(targetDir, fileName);
|
||||||
|
|
||||||
|
ensureInApiRoot(apiRoot, targetPath);
|
||||||
|
|
||||||
|
const exists = await accessFile(targetPath);
|
||||||
|
|
||||||
|
if (exists && !payload.overwrite) {
|
||||||
|
throw new ApiTypeGeneratorError('目标文件已存在', 409, 'FILE_EXISTS');
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.mkdir(targetDir, { recursive: true });
|
||||||
|
await fs.writeFile(
|
||||||
|
targetPath,
|
||||||
|
content.endsWith('\n') ? content : `${content}\n`,
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
const importResult = payload.autoImport
|
||||||
|
? await updateIndexTypeImport(targetDir, fileName, content)
|
||||||
|
: {
|
||||||
|
importSkipped: false,
|
||||||
|
importUpdated: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
exists,
|
||||||
|
filePath: path.relative(apiRoot, targetPath).replaceAll(path.sep, '/'),
|
||||||
|
...importResult,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { default } from '@vben/tailwind-config/postcss';
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"tDashboard": {
|
||||||
|
"businessAlert": true,
|
||||||
|
"businessVolume": true,
|
||||||
|
"dataSharing": true,
|
||||||
|
"feeSituation": true,
|
||||||
|
"lifeCycle": true,
|
||||||
|
"locationDistribution": true,
|
||||||
|
"myPanel": true,
|
||||||
|
"myPanelNew": true,
|
||||||
|
"myPanelSimActive": true,
|
||||||
|
"myPanelSimCount": true,
|
||||||
|
"myPanelUseData": true,
|
||||||
|
"myPanelUseMsg": true,
|
||||||
|
"quickAccessBorder": true,
|
||||||
|
"trafficPool": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"appTitle": "物联网全生命周期管理平台",
|
||||||
|
"appIcon": "/logo.png",
|
||||||
|
"loginPageTitle": "物联网设备上云首选",
|
||||||
|
"loginPageDescription": "新一代物联网设备统一接入平台",
|
||||||
|
"loginSloganImage": "",
|
||||||
|
"copyright": {
|
||||||
|
"companyName": "示例智联",
|
||||||
|
"companySiteLink": "https://www.xiujiezhilian.cn",
|
||||||
|
"date": "2024-2025",
|
||||||
|
"icp": "鲁ICP备2024117944号-1",
|
||||||
|
"icpLink": "https://beian.miit.gov.cn/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
import { defineAsyncComponent } from 'vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ant Design Vue 基础组件统一在这里异步引入。
|
||||||
|
*
|
||||||
|
* adapter 层只关心组件映射关系,避免把具体 UI 库的 import 细节散落到注册文件中。
|
||||||
|
*/
|
||||||
|
export const AutoComplete = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/auto-complete'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Button = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/button'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Checkbox = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/checkbox'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const CheckboxGroup = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const DatePicker = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/date-picker'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Divider = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/divider'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Image = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/image'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Input = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/input'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const InputNumber = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/input-number'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const InputPassword = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/input').then((res) => res.InputPassword),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Mentions = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/mentions'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const PreviewGroup = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/image').then((res) => res.ImagePreviewGroup),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Radio = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/radio'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const RadioGroup = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/radio').then((res) => res.RadioGroup),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const RangePicker = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/date-picker').then((res) => res.RangePicker),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Rate = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/rate'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Select = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/select'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Space = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/space'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Switch = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/switch'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Textarea = defineAsyncComponent(() =>
|
||||||
|
import('ant-design-vue/es/input').then((res) => res.Textarea),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TimePicker = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/time-picker'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const TreeSelect = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/tree-select'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Cascader = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/cascader'),
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Upload = defineAsyncComponent(
|
||||||
|
() => import('ant-design-vue/es/upload'),
|
||||||
|
);
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { globalShareState } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { notification } from 'ant-design-vue';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 框架共享消息适配。
|
||||||
|
*
|
||||||
|
* common-ui 内部只声明消息场景,这里绑定到当前 UI 库的 notification 实现。
|
||||||
|
*/
|
||||||
|
export function defineComponentAdapterMessage() {
|
||||||
|
globalShareState.defineMessage({
|
||||||
|
copyPreferencesSuccess: (title, content) => {
|
||||||
|
notification.success({
|
||||||
|
description: content,
|
||||||
|
message: title,
|
||||||
|
placement: 'bottomRight',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
import type { UploadFile, UploadProps } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import type { Ref } from 'vue';
|
||||||
|
|
||||||
|
import { h, render, unref } from 'vue';
|
||||||
|
|
||||||
|
import { Image, PreviewGroup } from '../antd';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UploadFile 没有稳定的图片标识字段时,通过 url/name/type 组合判断是否走图片预览。
|
||||||
|
*/
|
||||||
|
function isImageFile(file: UploadFile): boolean {
|
||||||
|
const imageExtensions = new Set(['bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp']);
|
||||||
|
|
||||||
|
if (file.url) {
|
||||||
|
const ext = file.url?.split('.').pop()?.toLowerCase();
|
||||||
|
return ext ? imageExtensions.has(ext) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.type) {
|
||||||
|
const ext = file.name?.split('.').pop()?.toLowerCase();
|
||||||
|
return ext ? imageExtensions.has(ext) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.type.startsWith('image/');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本地待上传图片没有 url 时,需要转成 base64 才能进入 ImagePreviewGroup。
|
||||||
|
*/
|
||||||
|
function getBase64(file: File) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.addEventListener('load', () => resolve(reader.result));
|
||||||
|
reader.addEventListener('error', (error) => reject(error));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于 Ant Design Vue ImagePreviewGroup 创建一次性预览容器。
|
||||||
|
*
|
||||||
|
* 预览关闭后会卸载并移除临时 DOM,避免多次预览后残留节点。
|
||||||
|
*/
|
||||||
|
export const previewImage = async (
|
||||||
|
file: UploadFile,
|
||||||
|
visible: Ref<boolean>,
|
||||||
|
fileList: Ref<UploadProps['fileList']>,
|
||||||
|
) => {
|
||||||
|
// 非图片沿用文件打开行为,不进入图片预览组
|
||||||
|
if (!isImageFile(file)) {
|
||||||
|
if (file.url) {
|
||||||
|
window.open(file.url, '_blank');
|
||||||
|
} else if (file.preview) {
|
||||||
|
window.open(file.preview, '_blank');
|
||||||
|
} else {
|
||||||
|
console.warn('无法打开文件,没有可用的URL或预览地址');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Image 与 PreviewGroup 只有预览时才加载,保持基础表单渲染轻量
|
||||||
|
const [ImageComponent, PreviewGroupComponent] = await Promise.all([
|
||||||
|
Image,
|
||||||
|
PreviewGroup,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const imageFiles = (unref(fileList) || []).filter((element) =>
|
||||||
|
isImageFile(element),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 同一上传列表中的图片都加入预览组,支持左右切换
|
||||||
|
for (const imgFile of imageFiles) {
|
||||||
|
if (!imgFile.url && !imgFile.preview && imgFile.originFileObj) {
|
||||||
|
imgFile.preview = (await getBase64(imgFile.originFileObj)) as string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const container: HTMLElement | null = document.createElement('div');
|
||||||
|
document.body.append(container);
|
||||||
|
|
||||||
|
// 避免关闭动画期间重复触发卸载
|
||||||
|
let isUnmounted = false;
|
||||||
|
|
||||||
|
const PreviewWrapper = {
|
||||||
|
setup() {
|
||||||
|
return () => {
|
||||||
|
if (isUnmounted) return null;
|
||||||
|
|
||||||
|
return h(
|
||||||
|
PreviewGroupComponent,
|
||||||
|
{
|
||||||
|
class: 'hidden',
|
||||||
|
preview: {
|
||||||
|
visible: visible.value,
|
||||||
|
current: imageFiles.findIndex((f) => f.uid === file.uid),
|
||||||
|
onVisibleChange: (value: boolean) => {
|
||||||
|
visible.value = value;
|
||||||
|
if (!value) {
|
||||||
|
// 等待预览关闭动画结束后再卸载临时容器
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!isUnmounted && container) {
|
||||||
|
isUnmounted = true;
|
||||||
|
render(null, container);
|
||||||
|
container.remove();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
imageFiles.map((imgFile) =>
|
||||||
|
h(ImageComponent, {
|
||||||
|
key: imgFile.uid,
|
||||||
|
src: imgFile.url || imgFile.preview,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
render(h(PreviewWrapper), container);
|
||||||
|
};
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
|
import type { Recordable } from '@vben/types';
|
||||||
|
|
||||||
|
import { defineComponent, h, ref } from 'vue';
|
||||||
|
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 为输入类/选择类组件补齐统一占位文案,并保留原组件暴露的方法。
|
||||||
|
*/
|
||||||
|
export const withDefaultPlaceholder = <T extends Component>(
|
||||||
|
component: T,
|
||||||
|
type: 'input' | 'select',
|
||||||
|
componentProps: Recordable<any> = {},
|
||||||
|
) => {
|
||||||
|
return defineComponent({
|
||||||
|
name: component.name,
|
||||||
|
inheritAttrs: false,
|
||||||
|
setup: (props: any, { attrs, expose, slots }) => {
|
||||||
|
const placeholder =
|
||||||
|
props?.placeholder ||
|
||||||
|
attrs?.placeholder ||
|
||||||
|
$t(`ui.placeholder.${type}`);
|
||||||
|
const innerRef = ref();
|
||||||
|
|
||||||
|
// 透传内部组件实例方法,避免包装后影响外部通过 ref 调用 focus 等能力
|
||||||
|
expose(
|
||||||
|
new Proxy(
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
get: (_target, key) => innerRef.value?.[key],
|
||||||
|
has: (_target, key) => key in (innerRef.value || {}),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
return () =>
|
||||||
|
h(
|
||||||
|
component,
|
||||||
|
{ ...componentProps, placeholder, ...props, ...attrs, ref: innerRef },
|
||||||
|
slots,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import type {
|
||||||
|
UploadChangeParam,
|
||||||
|
UploadFile,
|
||||||
|
UploadProps,
|
||||||
|
} from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { defineComponent, h, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import { IconifyIcon } from '@vben/icons';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
import { isEmpty } from '@vben/utils';
|
||||||
|
|
||||||
|
import { Button, Upload } from '../antd';
|
||||||
|
import { previewImage } from './upload-preview';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload 未提供自定义插槽时,补齐项目统一的默认上传按钮。
|
||||||
|
*/
|
||||||
|
function createDefaultSlotsWithUpload(listType: string, placeholder: string) {
|
||||||
|
switch (listType) {
|
||||||
|
case 'picture-card': {
|
||||||
|
return {
|
||||||
|
default: () => placeholder,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return {
|
||||||
|
default: () =>
|
||||||
|
h(
|
||||||
|
Button,
|
||||||
|
{
|
||||||
|
icon: h(IconifyIcon, {
|
||||||
|
icon: 'ant-design:upload-outlined',
|
||||||
|
class: 'mb-1 size-4',
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
() => placeholder,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 适配 Ant Design Vue Upload 到 Vben Form:
|
||||||
|
* - 使用 modelValue 与表单值同步
|
||||||
|
* - 上传列表为空时回写 undefined
|
||||||
|
* - 内置图片预览,非图片文件直接打开地址
|
||||||
|
*/
|
||||||
|
export const withPreviewUpload = () => {
|
||||||
|
return defineComponent({
|
||||||
|
name: Upload.name,
|
||||||
|
emits: ['change', 'update:modelValue'],
|
||||||
|
setup: (
|
||||||
|
props: any,
|
||||||
|
{ attrs, slots, emit }: { attrs: any; emit: any; slots: any },
|
||||||
|
) => {
|
||||||
|
const previewVisible = ref<boolean>(false);
|
||||||
|
const placeholder = attrs?.placeholder || $t(`ui.placeholder.upload`);
|
||||||
|
const listType = attrs?.listType || attrs?.['list-type'] || 'text';
|
||||||
|
const fileList = ref<UploadProps['fileList']>(
|
||||||
|
attrs?.fileList || attrs?.['file-list'] || [],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleChange = async (event: UploadChangeParam) => {
|
||||||
|
fileList.value = event.fileList;
|
||||||
|
emit('change', event);
|
||||||
|
emit(
|
||||||
|
'update:modelValue',
|
||||||
|
event.fileList?.length ? fileList.value : undefined,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePreview = async (file: UploadFile) => {
|
||||||
|
previewVisible.value = true;
|
||||||
|
await previewImage(file, previewVisible, fileList);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderUploadButton = (): any => {
|
||||||
|
const isDisabled = attrs.disabled;
|
||||||
|
|
||||||
|
// 禁用状态下只展示文件列表,不再渲染上传入口
|
||||||
|
if (isDisabled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isEmpty(slots)
|
||||||
|
? createDefaultSlotsWithUpload(listType, placeholder)
|
||||||
|
: slots;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听 formApi.setValues 等外部回填,保持 Upload 内部 fileList 同步
|
||||||
|
watch(
|
||||||
|
() => attrs.modelValue,
|
||||||
|
(res) => {
|
||||||
|
fileList.value = res;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return () =>
|
||||||
|
h(
|
||||||
|
Upload,
|
||||||
|
{
|
||||||
|
...props,
|
||||||
|
...attrs,
|
||||||
|
fileList: fileList.value,
|
||||||
|
onChange: handleChange,
|
||||||
|
onPreview: handlePreview,
|
||||||
|
},
|
||||||
|
renderUploadButton(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
import type { Component } from 'vue';
|
||||||
|
|
||||||
|
import type { ComponentType } from './types';
|
||||||
|
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import { HtcRegisterComponent } from '#/components/HtCustom/FormExpand/register';
|
||||||
|
import { Tinymce as RichTextarea } from '#/components/tinymce';
|
||||||
|
import { FileUpload, ImageUpload } from '#/components/upload';
|
||||||
|
|
||||||
|
import {
|
||||||
|
AutoComplete,
|
||||||
|
Button,
|
||||||
|
Cascader,
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
DatePicker,
|
||||||
|
Divider,
|
||||||
|
Input,
|
||||||
|
InputNumber,
|
||||||
|
InputPassword,
|
||||||
|
Mentions,
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
RangePicker,
|
||||||
|
Rate,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
Textarea,
|
||||||
|
TimePicker,
|
||||||
|
TreeSelect,
|
||||||
|
} from './antd';
|
||||||
|
import { defineComponentAdapterMessage } from './helpers/message';
|
||||||
|
import { withDefaultPlaceholder } from './helpers/with-default-placeholder';
|
||||||
|
import { withPreviewUpload } from './helpers/with-preview-upload';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用组件适配层。
|
||||||
|
*
|
||||||
|
* 这里只维护“组件名称 -> 真实组件”的映射关系,供 vben-form、vben-modal、
|
||||||
|
* vben-drawer 等框架组件通过 globalShareState 复用。
|
||||||
|
*/
|
||||||
|
function createComponentAdapterMap(): Partial<
|
||||||
|
Record<ComponentType, Component>
|
||||||
|
> {
|
||||||
|
return {
|
||||||
|
// 如果你的组件体积比较大,可以使用异步加载
|
||||||
|
// Button: () =>
|
||||||
|
// import('xxx').then((res) => res.Button),
|
||||||
|
ApiCascader: withDefaultPlaceholder(ApiComponent, 'select', {
|
||||||
|
component: Cascader,
|
||||||
|
fieldNames: { label: 'label', value: 'value', children: 'children' },
|
||||||
|
loadingSlot: 'suffixIcon',
|
||||||
|
modelPropName: 'value',
|
||||||
|
visibleEvent: 'onVisibleChange',
|
||||||
|
}),
|
||||||
|
ApiSelect: withDefaultPlaceholder(
|
||||||
|
{
|
||||||
|
...ApiComponent,
|
||||||
|
name: 'ApiSelect',
|
||||||
|
},
|
||||||
|
'select',
|
||||||
|
{
|
||||||
|
component: Select,
|
||||||
|
loadingSlot: 'suffixIcon',
|
||||||
|
visibleEvent: 'onDropdownVisibleChange',
|
||||||
|
modelPropName: 'value',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ApiTreeSelect: withDefaultPlaceholder(
|
||||||
|
{
|
||||||
|
...ApiComponent,
|
||||||
|
name: 'ApiTreeSelect',
|
||||||
|
},
|
||||||
|
'select',
|
||||||
|
{
|
||||||
|
component: TreeSelect,
|
||||||
|
fieldNames: { label: 'label', value: 'value', children: 'children' },
|
||||||
|
loadingSlot: 'suffixIcon',
|
||||||
|
modelPropName: 'value',
|
||||||
|
optionsPropName: 'treeData',
|
||||||
|
visibleEvent: 'onVisibleChange',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
AutoComplete,
|
||||||
|
Cascader,
|
||||||
|
Checkbox,
|
||||||
|
CheckboxGroup,
|
||||||
|
DatePicker,
|
||||||
|
// 弹窗、抽屉等框架组件会通过共享状态读取这两个按钮组件
|
||||||
|
DefaultButton: (props, { attrs, slots }) => {
|
||||||
|
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||||
|
},
|
||||||
|
Divider,
|
||||||
|
FileUpload,
|
||||||
|
ImageUpload,
|
||||||
|
IconPicker: withDefaultPlaceholder(IconPicker, 'select', {
|
||||||
|
iconSlot: 'addonAfter',
|
||||||
|
inputComponent: Input,
|
||||||
|
modelValueProp: 'value',
|
||||||
|
}),
|
||||||
|
Input: withDefaultPlaceholder(Input, 'input'),
|
||||||
|
InputNumber: withDefaultPlaceholder(InputNumber, 'input'),
|
||||||
|
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
|
||||||
|
Mentions: withDefaultPlaceholder(Mentions, 'input'),
|
||||||
|
PrimaryButton: (props, { attrs, slots }) => {
|
||||||
|
return h(Button, { ...props, attrs, type: 'primary' }, slots);
|
||||||
|
},
|
||||||
|
Radio,
|
||||||
|
RadioGroup,
|
||||||
|
RangePicker,
|
||||||
|
Rate,
|
||||||
|
RichTextarea,
|
||||||
|
Select: withDefaultPlaceholder(Select, 'select'),
|
||||||
|
// SelectDivision,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
Textarea: withDefaultPlaceholder(Textarea, 'input'),
|
||||||
|
TimePicker,
|
||||||
|
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
|
||||||
|
Upload: withPreviewUpload(),
|
||||||
|
...HtcRegisterComponent,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initComponentAdapter() {
|
||||||
|
// 先注册组件,再初始化 form 适配,确保 setupVbenForm 能读到完整组件表
|
||||||
|
globalShareState.setComponents(createComponentAdapterMap());
|
||||||
|
defineComponentAdapterMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
export { initComponentAdapter };
|
||||||
|
export type { ComponentType };
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import type { SchemaComponentType } from './types';
|
||||||
|
|
||||||
|
import type { HtcComponentPropsMap } from '#/components/HtCustom/FormExpand/register';
|
||||||
|
|
||||||
|
type ComponentPropsGetter<Props> = (
|
||||||
|
value: Partial<Record<string, any>>,
|
||||||
|
actions: any,
|
||||||
|
) => Props;
|
||||||
|
|
||||||
|
type ComponentPropsMap = HtcComponentPropsMap;
|
||||||
|
|
||||||
|
type UntypedComponentProps =
|
||||||
|
| ComponentPropsGetter<Record<string, any>>
|
||||||
|
| Record<string, any>;
|
||||||
|
|
||||||
|
export type ComponentProps<T extends SchemaComponentType> =
|
||||||
|
T extends keyof ComponentPropsMap
|
||||||
|
? ComponentPropsGetter<ComponentPropsMap[T]> | ComponentPropsMap[T]
|
||||||
|
: UntypedComponentProps;
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||||
|
|
||||||
|
import type { HtcRegisterComponentName } from '#/components/HtCustom/FormExpand/register';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单 schema 中 component 字段允许使用的组件名称。
|
||||||
|
*
|
||||||
|
* 新增适配组件时,需要同时在这里补充名称,并在 index.ts 中注册真实组件。
|
||||||
|
*/
|
||||||
|
export type AdapterComponentType =
|
||||||
|
| 'ApiCascader'
|
||||||
|
| 'ApiSelect'
|
||||||
|
| 'ApiTreeSelect'
|
||||||
|
| 'AutoComplete'
|
||||||
|
| 'Cascader'
|
||||||
|
| 'Checkbox'
|
||||||
|
| 'CheckboxGroup'
|
||||||
|
| 'DatePicker'
|
||||||
|
| 'DefaultButton'
|
||||||
|
| 'Divider'
|
||||||
|
| 'FileUpload'
|
||||||
|
| 'HtcFormSwitch'
|
||||||
|
| 'IconPicker'
|
||||||
|
| 'ImageUpload'
|
||||||
|
| 'Input'
|
||||||
|
| 'InputNumber'
|
||||||
|
| 'InputPassword'
|
||||||
|
| 'Mentions'
|
||||||
|
| 'PrimaryButton'
|
||||||
|
| 'Radio'
|
||||||
|
| 'RadioGroup'
|
||||||
|
| 'RangePicker'
|
||||||
|
| 'Rate'
|
||||||
|
| 'RichTextarea'
|
||||||
|
| 'Select'
|
||||||
|
| 'SelectDivision'
|
||||||
|
| 'Space'
|
||||||
|
| 'Switch'
|
||||||
|
| 'Textarea'
|
||||||
|
| 'TimePicker'
|
||||||
|
| 'TreeSelect'
|
||||||
|
| 'Upload';
|
||||||
|
|
||||||
|
export type BaseComponentType =
|
||||||
|
| 'DefaultButton'
|
||||||
|
| 'PrimaryButton'
|
||||||
|
| 'VbenCheckbox'
|
||||||
|
| 'VbenInput'
|
||||||
|
| 'VbenInputPassword'
|
||||||
|
| 'VbenPinInput'
|
||||||
|
| 'VbenSelect';
|
||||||
|
|
||||||
|
export type SchemaComponentType =
|
||||||
|
| AdapterComponentType
|
||||||
|
| BaseComponentType
|
||||||
|
| HtcRegisterComponentName;
|
||||||
|
|
||||||
|
export type ComponentType =
|
||||||
|
| AdapterComponentType
|
||||||
|
| BaseFormComponentType
|
||||||
|
| HtcRegisterComponentName;
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
import type {
|
||||||
|
VbenFormProps as BaseVbenFormProps,
|
||||||
|
VbenFormSchema as FormSchema,
|
||||||
|
} from '@vben/common-ui';
|
||||||
|
|
||||||
|
import type { ComponentType } from './component';
|
||||||
|
import type { ComponentProps } from './component/props';
|
||||||
|
import type { SchemaComponentType } from './component/types';
|
||||||
|
|
||||||
|
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||||
|
import { $t } from '@vben/locales';
|
||||||
|
|
||||||
|
async function initSetupVbenForm() {
|
||||||
|
setupVbenForm<ComponentType>({
|
||||||
|
config: {
|
||||||
|
// ant design vue组件库默认都是 v-model:value
|
||||||
|
baseModelPropName: 'value',
|
||||||
|
|
||||||
|
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||||
|
modelPropNameMap: {
|
||||||
|
Checkbox: 'checked',
|
||||||
|
Radio: 'checked',
|
||||||
|
RichTextarea: 'modelValue',
|
||||||
|
Switch: 'checked',
|
||||||
|
Upload: 'fileList',
|
||||||
|
ImageUpload: 'value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defineRules: {
|
||||||
|
// 输入项目必填国际化适配
|
||||||
|
required: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null || value.length === 0) {
|
||||||
|
return $t('ui.formRules.required', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
// 选择项目必填国际化适配
|
||||||
|
selectRequired: (value, _params, ctx) => {
|
||||||
|
if (value === undefined || value === null) {
|
||||||
|
return $t('ui.formRules.selectRequired', [ctx.label]);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypedFormSchema<T extends SchemaComponentType> = Omit<
|
||||||
|
FormSchema<T>,
|
||||||
|
'component' | 'componentProps'
|
||||||
|
> & {
|
||||||
|
component: T;
|
||||||
|
componentProps?: ComponentProps<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type VbenFormSchema = {
|
||||||
|
[K in SchemaComponentType]: TypedFormSchema<K>;
|
||||||
|
}[SchemaComponentType];
|
||||||
|
|
||||||
|
export type VbenFormProps = Omit<BaseVbenFormProps<ComponentType>, 'schema'> & {
|
||||||
|
schema?: VbenFormSchema[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const useVbenForm = (options: VbenFormProps) => {
|
||||||
|
return useForm<ComponentType>(options as BaseVbenFormProps<ComponentType>);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { initSetupVbenForm, useVbenForm, z };
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/* eslint-disable vue/no-side-effects-in-computed-properties */
|
||||||
|
import { computed, defineComponent, h, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
import hljs from 'highlight.js/lib/core';
|
||||||
|
|
||||||
|
const component = defineComponent({
|
||||||
|
props: {
|
||||||
|
code: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
autodetect: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
ignoreIllegals: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const language = ref(props.language);
|
||||||
|
const ignoreIllegals = ref(props.ignoreIllegals);
|
||||||
|
watch(
|
||||||
|
() => props.language,
|
||||||
|
(newLanguage) => {
|
||||||
|
language.value = newLanguage;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => props.ignoreIllegals,
|
||||||
|
(newIgnoreIllegals) => {
|
||||||
|
ignoreIllegals.value = newIgnoreIllegals;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const autodetect = computed(() => props.autodetect || !language.value);
|
||||||
|
const cannotDetectLanguage = computed(
|
||||||
|
() => !autodetect.value && !hljs.getLanguage(language.value),
|
||||||
|
);
|
||||||
|
|
||||||
|
const className = computed((): string => {
|
||||||
|
return cannotDetectLanguage.value ? '' : `hljs ${language.value}`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const highlightedCode = computed((): string => {
|
||||||
|
// No idea what language to use, return raw code
|
||||||
|
if (cannotDetectLanguage.value) {
|
||||||
|
console.warn(
|
||||||
|
`The language "${language.value}" you specified could not be found.`,
|
||||||
|
);
|
||||||
|
return props.code;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (autodetect.value) {
|
||||||
|
const result = hljs.highlightAuto(props.code);
|
||||||
|
language.value = result.language ?? '';
|
||||||
|
return result.value;
|
||||||
|
} else {
|
||||||
|
const result = hljs.highlight(props.code, {
|
||||||
|
language: language.value,
|
||||||
|
ignoreIllegals: props.ignoreIllegals,
|
||||||
|
});
|
||||||
|
return result.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// const saveSelection = (e: any) => {
|
||||||
|
// const selection = window.getSelection();
|
||||||
|
// if (selection && selection.rangeCount && selection.rangeCount > 0) {
|
||||||
|
// const range = selection.getRangeAt(0);
|
||||||
|
// return range;
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// };
|
||||||
|
// const restoreSelection = (range: any, el: any) => {
|
||||||
|
// if (range) {
|
||||||
|
// const selection = window.getSelection();
|
||||||
|
// if (selection) {
|
||||||
|
// selection.removeAllRanges();
|
||||||
|
// selection.addRange(range);
|
||||||
|
// console.log('selection range', range);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
const updateContent = (e: any) => {
|
||||||
|
// const range = saveSelection(e)
|
||||||
|
const result = hljs.highlight(e.target.innerText, {
|
||||||
|
language: language.value,
|
||||||
|
ignoreIllegals: props.ignoreIllegals,
|
||||||
|
});
|
||||||
|
e.target.innerHTML = result.value;
|
||||||
|
emit('update:code', e.target.innerText);
|
||||||
|
// setTimeout(() => {
|
||||||
|
// restoreSelection(range, e.target)
|
||||||
|
// }, 0)
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
className,
|
||||||
|
highlightedCode,
|
||||||
|
updateContent,
|
||||||
|
emit,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
const updateContent = this.updateContent;
|
||||||
|
return h('pre', {}, [
|
||||||
|
h('code', {
|
||||||
|
class: this.className,
|
||||||
|
innerHTML: this.highlightedCode,
|
||||||
|
contenteditable: true,
|
||||||
|
// onInput(e: any) {},
|
||||||
|
onBlur(e: any) {
|
||||||
|
updateContent(e);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default component;
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import type { VxeTableGridOptions } from '@vben/plugins/vxe-table';
|
||||||
|
|
||||||
|
import { h } from 'vue';
|
||||||
|
|
||||||
|
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||||
|
|
||||||
|
import { Button, Image } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import { useVbenForm } from './form';
|
||||||
|
|
||||||
|
setupVbenVxeTable({
|
||||||
|
configVxeTable: (vxeUI) => {
|
||||||
|
vxeUI.setConfig({
|
||||||
|
grid: {
|
||||||
|
align: 'center',
|
||||||
|
border: false,
|
||||||
|
columnConfig: {
|
||||||
|
resizable: true,
|
||||||
|
},
|
||||||
|
minHeight: 180,
|
||||||
|
formConfig: {
|
||||||
|
// 全局禁用vxe-table的表单配置,使用formOptions
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
proxyConfig: {
|
||||||
|
autoLoad: true,
|
||||||
|
response: {
|
||||||
|
result: 'items',
|
||||||
|
total: 'total',
|
||||||
|
list: 'items',
|
||||||
|
},
|
||||||
|
showActiveMsg: true,
|
||||||
|
showResponseMsg: false,
|
||||||
|
},
|
||||||
|
round: true,
|
||||||
|
showOverflow: true,
|
||||||
|
size: 'small',
|
||||||
|
} as VxeTableGridOptions,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
||||||
|
vxeUI.renderer.add('CellImage', {
|
||||||
|
renderTableDefault(renderOpts, params) {
|
||||||
|
const { props } = renderOpts;
|
||||||
|
const { column, row } = params;
|
||||||
|
return h(Image, { src: row[column.field], ...props });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
||||||
|
vxeUI.renderer.add('CellLink', {
|
||||||
|
renderTableDefault(renderOpts) {
|
||||||
|
const { props } = renderOpts;
|
||||||
|
return h(
|
||||||
|
Button,
|
||||||
|
{ size: 'small', type: 'link' },
|
||||||
|
{ default: () => props?.text },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
||||||
|
// vxeUI.formats.add
|
||||||
|
},
|
||||||
|
useVbenForm,
|
||||||
|
});
|
||||||
|
|
||||||
|
export { useVbenVxeGrid };
|
||||||
|
|
||||||
|
export type * from '@vben/plugins/vxe-table';
|
||||||
@@ -0,0 +1,170 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export interface AgentDept {
|
||||||
|
deptId: number;
|
||||||
|
tenantId?: string;
|
||||||
|
deptType: number;
|
||||||
|
parentId: number;
|
||||||
|
ancestors?: string;
|
||||||
|
deptKey?: string;
|
||||||
|
deptName: string;
|
||||||
|
deptCategory?: string;
|
||||||
|
orderNum?: number;
|
||||||
|
leader?: number;
|
||||||
|
phone?: string;
|
||||||
|
email?: string;
|
||||||
|
status: string;
|
||||||
|
creditCode?: string;
|
||||||
|
areaCode?: string;
|
||||||
|
address?: string;
|
||||||
|
deptRole?: number;
|
||||||
|
userName?: string;
|
||||||
|
defaultUserName?: string;
|
||||||
|
nickName?: string;
|
||||||
|
defaultNickName?: string;
|
||||||
|
defaultUserNickName?: string;
|
||||||
|
defaultUserId?: number;
|
||||||
|
password?: string;
|
||||||
|
createdAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentRole {
|
||||||
|
roleId: number;
|
||||||
|
tenantId?: string;
|
||||||
|
deptId: number;
|
||||||
|
deptName?: string;
|
||||||
|
roleName: string;
|
||||||
|
roleKey: string;
|
||||||
|
roleSort?: number;
|
||||||
|
dataScope?: string;
|
||||||
|
menuCheckStrictly?: number;
|
||||||
|
deptCheckStrictly?: number;
|
||||||
|
isPass?: number;
|
||||||
|
sourceRoleId?: number;
|
||||||
|
sourceDeptId?: number;
|
||||||
|
status: string;
|
||||||
|
createdAt?: string;
|
||||||
|
remark?: string;
|
||||||
|
isDefaultRole?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MenuTreeItem {
|
||||||
|
title: string;
|
||||||
|
key: string;
|
||||||
|
parentId: number;
|
||||||
|
menuId: number;
|
||||||
|
menuName: string;
|
||||||
|
menuType: string;
|
||||||
|
perms?: string;
|
||||||
|
children?: MenuTreeItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentRoleView extends AgentRole {
|
||||||
|
menuIds: number[];
|
||||||
|
deptIds: number[];
|
||||||
|
allowedMenuIds: number[];
|
||||||
|
menuTree: MenuTreeItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AgentUser {
|
||||||
|
userId: number;
|
||||||
|
tenantId: string;
|
||||||
|
deptId: number;
|
||||||
|
userName: string;
|
||||||
|
nickName: string;
|
||||||
|
userType: string;
|
||||||
|
email: string;
|
||||||
|
phonenumber: string;
|
||||||
|
sex: string;
|
||||||
|
avatar: string;
|
||||||
|
status: string;
|
||||||
|
loginIp?: string;
|
||||||
|
loginDate?: string;
|
||||||
|
createdDept?: number;
|
||||||
|
createdBy?: number;
|
||||||
|
createdAt?: string;
|
||||||
|
remark?: string;
|
||||||
|
deptInfo?: { deptId: number; deptName: string };
|
||||||
|
roles?: AgentRole[];
|
||||||
|
isDefaultUser?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentDeptListApi(params?: Record<string, any>) {
|
||||||
|
return requestClient.get<{ items: AgentDept[]; total: number }>(
|
||||||
|
'/agent/dept/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentDeptViewApi(params: { deptId: number }) {
|
||||||
|
return requestClient.get<AgentDept>('/agent/dept/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveAgentDeptApi(params: Partial<AgentDept>) {
|
||||||
|
return requestClient.post<{ deptId: number }>('/agent/dept/save', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteAgentDeptApi(params: { deptId: number }) {
|
||||||
|
return requestClient.post('/agent/dept/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetAgentDeptPasswordApi(params: { deptId: number; password: string }) {
|
||||||
|
return requestClient.post('/agent/dept/resetPassword', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentUserListApi(params?: Record<string, any>) {
|
||||||
|
return requestClient.get<{ items: AgentUser[]; total: number }>(
|
||||||
|
'/agent/user/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentUserViewApi(params: { userId: number }) {
|
||||||
|
return requestClient.get<AgentUser>('/agent/user/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addAgentUserApi(params: Record<string, any>) {
|
||||||
|
return requestClient.post('/agent/user/add', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateAgentUserApi(params: Record<string, any>) {
|
||||||
|
return requestClient.post('/agent/user/update', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteAgentUserApi(params: { userId?: number; userIds?: number[] }) {
|
||||||
|
return requestClient.post('/agent/user/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetAgentUserPasswordApi(params: { userId: number; password: string }) {
|
||||||
|
return requestClient.post('/agent/user/resetPassword', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentRoleListApi(params?: Record<string, any>) {
|
||||||
|
return requestClient.get<{ items: AgentRole[]; total: number }>(
|
||||||
|
'/agent/role/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentRoleViewApi(params: { roleId: number }) {
|
||||||
|
return requestClient.get<AgentRoleView>('/agent/role/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveAgentRoleApi(params: Record<string, any>) {
|
||||||
|
return requestClient.post<{ roleId: number }>('/agent/role/save', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteAgentRoleApi(params: { roleId?: number; roleIds?: number[] }) {
|
||||||
|
return requestClient.post('/agent/role/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAgentMenuTreeApi() {
|
||||||
|
return requestClient.get<{ items: MenuTreeItem[]; menuIds: number[] }>(
|
||||||
|
'/agent/menu/tree',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取开放接口权限组表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('agent/tApiGroup/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除开放接口权限组表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('agent/tApiGroup/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑开放接口权限组表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('agent/tApiGroup/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改开放接口权限组表状态
|
||||||
|
export function Status(params:any) {
|
||||||
|
return requestClient.post<any>('agent/tApiGroup/status', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取开放接口权限组表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('agent/tApiGroup/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出开放接口权限组表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/agent/tApiGroup/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取开放接口资源表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('agent/tApiResource/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除开放接口资源表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('agent/tApiResource/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑开放接口资源表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('agent/tApiResource/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改开放接口资源表状态
|
||||||
|
export function Status(params:any) {
|
||||||
|
return requestClient.post<any>('agent/tApiResource/status', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取开放接口资源表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('agent/tApiResource/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出开放接口资源表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/agent/tApiResource/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取企业客户财务信息列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tAgentBalance/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取账户余额概览
|
||||||
|
export function GetAccountBalance(params?: any) {
|
||||||
|
return requestClient.get<any>('common/tAgentBalance/getAccountBalance', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取费用总览折线图
|
||||||
|
export function FeeTrend(params: any) {
|
||||||
|
return requestClient.get<any>('common/tAgentBalance/feeTrend', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取账户总览折线图
|
||||||
|
export function AccountTrend(params: any) {
|
||||||
|
return requestClient.get<any>('common/tAgentBalance/accountTrend', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除企业客户财务信息
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tAgentBalance/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑企业客户财务信息
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tAgentBalance/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改企业客户财务信息状态
|
||||||
|
export function Status(params:any) {
|
||||||
|
return requestClient.post<any>('common/tAgentBalance/status', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取企业客户财务信息指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tAgentBalance/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出企业客户财务信息
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tAgentBalance/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AlipayPay(params: any) {
|
||||||
|
return requestClient.post<any>('pay/alipay/pay', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AlipayQuery(params: any) {
|
||||||
|
return requestClient.get<any>('pay/alipay/query', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 转可提现申请
|
||||||
|
export function BalanceTransferApply(params: {
|
||||||
|
amount: number;
|
||||||
|
accountType: number;
|
||||||
|
bankName?: string;
|
||||||
|
bankAccount?: string;
|
||||||
|
accountName?: string;
|
||||||
|
identityCard?: string;
|
||||||
|
remark?: string;
|
||||||
|
}) {
|
||||||
|
return requestClient.post<any>('common/tAgentBalance/balanceTransferApply', params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取余额明细表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tBalanceDetail/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除余额明细表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tBalanceDetail/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑余额明细表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tBalanceDetail/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取余额明细表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tBalanceDetail/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出余额明细表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tBalanceDetail/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Recharge(params:any) {
|
||||||
|
return requestClient.post<any>('common/tBalanceDetail/recharge', { ...params });
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取消费账户月账单表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tConsumeAccountMonthBill/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取消费账户月账单汇总
|
||||||
|
export function GetConsumptionBillSummary(params:any) {
|
||||||
|
return requestClient.get<any>('common/tConsumeAccountMonthBill/getConsumptionBillSummary', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除消费账户月账单表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tConsumeAccountMonthBill/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑消费账户月账单表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tConsumeAccountMonthBill/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取消费账户月账单表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tConsumeAccountMonthBill/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出消费账户月账单表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tConsumeAccountMonthBill/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取信用额度明细表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tCreditDetail/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除信用额度明细表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tCreditDetail/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑信用额度明细表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tCreditDetail/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取信用额度明细表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tCreditDetail/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出信用额度明细表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tCreditDetail/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取分润明细表列表
|
||||||
|
export function List(params: any) {
|
||||||
|
return requestClient.get<any>('common/tFinCommissionDetail/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除分润明细表
|
||||||
|
export function Delete(params: any) {
|
||||||
|
return requestClient.post<any>('common/tFinCommissionDetail/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑分润明细表
|
||||||
|
export function Edit(params: any) {
|
||||||
|
return requestClient.post<any>('common/tFinCommissionDetail/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取分润明细表指定详情
|
||||||
|
export function View(params: any) {
|
||||||
|
return requestClient.get<any>('common/tFinCommissionDetail/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取分层分润概览
|
||||||
|
export function GetCommissionOverview(params: any) {
|
||||||
|
return requestClient.get<any>(
|
||||||
|
'common/tFinCommissionDetail/getCommissionOverview',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取层级汇总表
|
||||||
|
export function GetLevelSummary(params: any) {
|
||||||
|
return requestClient.get<any>(
|
||||||
|
'common/tFinCommissionDetail/getLevelSummary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出分润明细表
|
||||||
|
export function Export(params: any) {
|
||||||
|
return requestClient.post<Blob>(
|
||||||
|
'/common/tFinCommissionDetail/export',
|
||||||
|
{ ...params },
|
||||||
|
{
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取可提现变动明细表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tFinWithdrawableChange/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除可提现变动明细表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tFinWithdrawableChange/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑可提现变动明细表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tFinWithdrawableChange/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取可提现变动明细表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tFinWithdrawableChange/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出可提现变动明细表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tFinWithdrawableChange/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取财务申请表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tFinanceApply/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除财务申请表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tFinanceApply/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑财务申请表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tFinanceApply/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取财务申请表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tFinanceApply/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出财务申请表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tFinanceApply/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审核余额转可提现申请
|
||||||
|
export function BalanceTransferAudit(params: {
|
||||||
|
applyNo: string;
|
||||||
|
auditResult: number;
|
||||||
|
auditOpinion?: string;
|
||||||
|
}) {
|
||||||
|
return requestClient.post<any>('common/tAgentBalance/balanceTransferAudit', params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取财务审核记录表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tFinanceAuditRecord/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除财务审核记录表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tFinanceAuditRecord/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑财务审核记录表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tFinanceAuditRecord/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取财务审核记录表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tFinanceAuditRecord/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出财务审核记录表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tFinanceAuditRecord/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取消息日志列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tMessageLog/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除消息日志
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tMessageLog/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑消息日志
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tMessageLog/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取消息日志指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tMessageLog/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出消息日志
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tMessageLog/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取通知日志列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tNotifyLog/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除通知日志
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tNotifyLog/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑通知日志
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tNotifyLog/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取通知日志指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tNotifyLog/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出通知日志
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tNotifyLog/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取盈利账户月账单表列表
|
||||||
|
export function List(params: any) {
|
||||||
|
return requestClient.get<any>('common/tProfitAccountMonthBill/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取盈利账户月账单汇总
|
||||||
|
export function GetProfitBillSummary(params: any) {
|
||||||
|
return requestClient.get<any>(
|
||||||
|
'common/tProfitAccountMonthBill/getProfitBillSummary',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取核心财务指标
|
||||||
|
export function GetCoreFinancialData(params: any) {
|
||||||
|
return requestClient.get<any>(
|
||||||
|
'common/tProfitAccountMonthBill/getCoreFinancialData',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取财务趋势
|
||||||
|
export function GetFinancialTrend(params: any) {
|
||||||
|
return requestClient.get<any>(
|
||||||
|
'common/tProfitAccountMonthBill/getFinancialTrend',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除盈利账户月账单表
|
||||||
|
export function Delete(params: any) {
|
||||||
|
return requestClient.post<any>('common/tProfitAccountMonthBill/delete', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑盈利账户月账单表
|
||||||
|
export function Edit(params: any) {
|
||||||
|
return requestClient.post<any>('common/tProfitAccountMonthBill/edit', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取盈利账户月账单表指定详情
|
||||||
|
export function View(params: any) {
|
||||||
|
return requestClient.get<any>('common/tProfitAccountMonthBill/view', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出盈利账户月账单表
|
||||||
|
export function Export(params: any) {
|
||||||
|
return requestClient.post<Blob>(
|
||||||
|
'/common/tProfitAccountMonthBill/export',
|
||||||
|
{ ...params },
|
||||||
|
{
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取工单问题沟通表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('/common/tWoCommunication/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除工单问题沟通表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoCommunication/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑工单问题沟通表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoCommunication/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取工单问题沟通表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tWoCommunication/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送工单沟通消息
|
||||||
|
export function Message(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoCommunication/message', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出工单问题沟通表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tWoCommunication/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取工单主表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tWoMain/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除工单主表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑工单主表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取工单主表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tWoMain/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工单转单
|
||||||
|
export function Transfer(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/transfer', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工单完成
|
||||||
|
export function Resolve(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/resolve', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工单取消/关闭
|
||||||
|
export function Cancel(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/cancel', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭工单
|
||||||
|
export function Close(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/close', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 工单评价
|
||||||
|
export function Evaluate(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoMain/evaluate', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出工单主表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tWoMain/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取工单记录表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('common/tWoRecord/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除工单记录表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoRecord/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑工单记录表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('common/tWoRecord/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取工单记录表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('common/tWoRecord/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出工单记录表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/common/tWoRecord/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/**
|
||||||
|
* @description 认证相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { baseRequestClient, requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录类型
|
||||||
|
* password 密码
|
||||||
|
* sms 短信
|
||||||
|
* social 第三方oauth
|
||||||
|
* email 邮箱
|
||||||
|
* xcx 小程序
|
||||||
|
*/
|
||||||
|
type GrantType = 'email' | 'password' | 'sms' | 'social' | 'xcx';
|
||||||
|
|
||||||
|
export namespace AuthApi {
|
||||||
|
/** 登录接口参数 */
|
||||||
|
export interface LoginParams {
|
||||||
|
password?: string;
|
||||||
|
username?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 登录接口返回值 */
|
||||||
|
export interface LoginResult {
|
||||||
|
accessToken: string;
|
||||||
|
}
|
||||||
|
/** 注册接口参数 */
|
||||||
|
export interface RegisterParams {
|
||||||
|
tenantId?: string;
|
||||||
|
username?: string;
|
||||||
|
password?: string;
|
||||||
|
captchaID?: string;
|
||||||
|
captchaValue?: string;
|
||||||
|
}
|
||||||
|
export type RegisterResult = Record<string, never>;
|
||||||
|
|
||||||
|
export interface RefreshTokenResult {
|
||||||
|
data: string;
|
||||||
|
status: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BaseLoginParams {
|
||||||
|
clientId?: string;
|
||||||
|
grantType: GrantType;
|
||||||
|
tenantId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OAuthLoginParams extends BaseLoginParams {
|
||||||
|
socialCode: string;
|
||||||
|
socialState: string;
|
||||||
|
source: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param companyName 租户/公司名称
|
||||||
|
* @param domain 绑定域名(不带http(s)://) 可选
|
||||||
|
* @param tenantId 租户id
|
||||||
|
*/
|
||||||
|
export interface TenantOption {
|
||||||
|
companyName: string;
|
||||||
|
domain?: string;
|
||||||
|
tenantId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param tenantEnabled 是否启用租户
|
||||||
|
* @param voList 租户列表
|
||||||
|
*/
|
||||||
|
export interface TenantResp {
|
||||||
|
tenantEnabled: boolean;
|
||||||
|
voList: TenantOption[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户权限码
|
||||||
|
*/
|
||||||
|
export interface GetAccessCodesResult {
|
||||||
|
data: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录
|
||||||
|
*/
|
||||||
|
export async function loginApi(data: AuthApi.LoginParams) {
|
||||||
|
return requestClient.post<AuthApi.LoginResult>('/system/auth/login', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送手机验证码登录短信
|
||||||
|
*/
|
||||||
|
export async function sendSmsLoginCodeApi(phone: string) {
|
||||||
|
return requestClient.post('/system/auth/sms-code', { phone });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手机验证码登录
|
||||||
|
*/
|
||||||
|
export async function smsLoginApi(data: { code: string; phone: string }) {
|
||||||
|
return requestClient.post<AuthApi.LoginResult>(
|
||||||
|
'/system/auth/sms-login',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 注册register
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function registerApi(data: AuthApi.RegisterParams) {
|
||||||
|
return requestClient.post<AuthApi.RegisterResult>(
|
||||||
|
'/system/user/register',
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新accessToken
|
||||||
|
*/
|
||||||
|
export async function refreshTokenApi() {
|
||||||
|
return baseRequestClient.post<AuthApi.RefreshTokenResult>(
|
||||||
|
'/system/auth/refresh',
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
withCredentials: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录
|
||||||
|
*/
|
||||||
|
export async function logoutApi() {
|
||||||
|
return requestClient.post('/system/auth/logout', undefined, {
|
||||||
|
withCredentials: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户权限码
|
||||||
|
*/
|
||||||
|
export async function getAccessCodesApi() {
|
||||||
|
return requestClient.get<GetAccessCodesResult>('/system/auth/codes');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 绑定第三方账号
|
||||||
|
* @param source 绑定的来源
|
||||||
|
* @returns 跳转url
|
||||||
|
*/
|
||||||
|
export function authBinding(source: string, tenantId: string) {
|
||||||
|
return requestClient.get<string>(`/auth/binding/${source}`, {
|
||||||
|
params: {
|
||||||
|
domain: window.location.host,
|
||||||
|
tenantId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 取消绑定
|
||||||
|
* @param id id
|
||||||
|
*/
|
||||||
|
export function authUnbinding(id: string) {
|
||||||
|
return requestClient.delete<unknown>(`/auth/unlock/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* oauth授权回调
|
||||||
|
* @param data oauth授权
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
export function authCallback(data: AuthApi.OAuthLoginParams) {
|
||||||
|
return requestClient.post<unknown>('/auth/social/callback', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取租户列表
|
||||||
|
export function tenantList() {
|
||||||
|
return requestClient.get<TenantResp>('/system/auth/getTenantList');
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @description 验证码相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export namespace CaptchaApi {
|
||||||
|
export interface GetCaptchaResult {
|
||||||
|
captchaID: string;
|
||||||
|
captchaImage: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCaptchaApi() {
|
||||||
|
return requestClient.get<CaptchaApi.GetCaptchaResult>('/system/captcha');
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @description 核心模块接口导出
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './auth';
|
||||||
|
export * from './menu';
|
||||||
|
export * from './user';
|
||||||
|
export * from './captcha';
|
||||||
|
export * from './upload';
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @description 菜单相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { RouteRecordStringComponent } from '@vben/types';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户所有菜单
|
||||||
|
*/
|
||||||
|
export async function getAllMenusApi() {
|
||||||
|
return requestClient.get<RouteRecordStringComponent[]>('/system/menu/all');
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/**
|
||||||
|
* @description 文件上传相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { AxiosRequestConfig } from '@vben/request';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Axios上传进度事件
|
||||||
|
*/
|
||||||
|
export type AxiosProgressEvent = AxiosRequestConfig['onUploadProgress'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过单文件上传接口
|
||||||
|
* @param file 上传的文件
|
||||||
|
* @param onUploadProgress 上传进度事件 非必传
|
||||||
|
* @param isDevice 不知道这是什么,但是不敢动
|
||||||
|
* @param newFileType 新文件类型 1:用户文件 2:用户图片 3:设备文件 4:iot配置图片 5:iot固件文件 6:项目图片 7:项目文件
|
||||||
|
* @param subDirName 文件父目录
|
||||||
|
* @param params 额外上传参数
|
||||||
|
* @returns 上传结果
|
||||||
|
*/
|
||||||
|
export function uploadApi(
|
||||||
|
file: Blob | File,
|
||||||
|
onUploadProgress?: AxiosProgressEvent,
|
||||||
|
isDevice?: boolean | undefined,
|
||||||
|
newFileType?: number,
|
||||||
|
subDirName?: string,
|
||||||
|
params?: Record<string, any>,
|
||||||
|
) {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/common/oss/upload',
|
||||||
|
{
|
||||||
|
...params,
|
||||||
|
file,
|
||||||
|
fileType: 'file',
|
||||||
|
...(isDevice ? { isDevice } : {}),
|
||||||
|
...(newFileType ? { newFileType } : {}),
|
||||||
|
...(subDirName ? { subDirName } : {}),
|
||||||
|
},
|
||||||
|
{ onUploadProgress, timeout: 60_000 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 默认上传结果
|
||||||
|
*/
|
||||||
|
export interface UploadResult {
|
||||||
|
url: string;
|
||||||
|
fileName: string;
|
||||||
|
ossId: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* @description 用户信息相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { UserInfo } from '@vben/types';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户信息
|
||||||
|
*/
|
||||||
|
export async function getUserInfoApi() {
|
||||||
|
return requestClient.get<UserInfo>('/system/user/info');
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取测试分类列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('gen/testCategory/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除测试分类
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('gen/testCategory/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑测试分类
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('gen/testCategory/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改测试分类状态
|
||||||
|
export function Status(params:any) {
|
||||||
|
return requestClient.post<any>('gen/testCategory/status', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取测试分类指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('gen/testCategory/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取测试分类最大排序
|
||||||
|
export function MaxSort(params:any) {
|
||||||
|
return requestClient.get<any>('gen/testCategory/maxSort', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出测试分类
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/gen/testCategory/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/**
|
||||||
|
* @description 测试单表相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取测试单表列表
|
||||||
|
export function List(params:any) {
|
||||||
|
return requestClient.get<any>('gen/testDemo/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除测试单表
|
||||||
|
export function Delete(params:any) {
|
||||||
|
return requestClient.post<any>('gen/testDemo/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑测试单表
|
||||||
|
export function Edit(params:any) {
|
||||||
|
return requestClient.post<any>('gen/testDemo/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取测试单表指定详情
|
||||||
|
export function View(params:any) {
|
||||||
|
return requestClient.get<any>('gen/testDemo/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出测试单表
|
||||||
|
export function Export(params:any) {
|
||||||
|
return requestClient.post<Blob>('/gen/testDemo/export', { ...params } , {
|
||||||
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' },
|
||||||
|
responseType: 'blob',
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,325 @@
|
|||||||
|
/**
|
||||||
|
* @description 代码生成器表格相关接口
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
import type { DictOption } from '#/store/dict';
|
||||||
|
|
||||||
|
export interface SysGenTableListReq {
|
||||||
|
page: number;
|
||||||
|
pageSize: number;
|
||||||
|
genType: number;
|
||||||
|
varName: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
export interface SysGenTableListModel {
|
||||||
|
tableId: number; // 生成ID
|
||||||
|
genType: number; // 生成类型
|
||||||
|
genTemplate: number; // 生成模板
|
||||||
|
varName: string; // 实体命名
|
||||||
|
options: string; // 配置选项
|
||||||
|
dbName: string; // 数据库名称
|
||||||
|
tableName: string; // 主表名称
|
||||||
|
tableComment: string; // 主表注释
|
||||||
|
daoName: string; // 主表dao模型
|
||||||
|
masterColumns: string; // 主表字段
|
||||||
|
addonName: string; // 插件名称
|
||||||
|
status: string; // 生成状态(0成功
|
||||||
|
createdDept: number; // 创建组织
|
||||||
|
createdBy: number; // 创建者
|
||||||
|
createdAt: string; // 创建时间
|
||||||
|
}
|
||||||
|
export interface SysGenTableListRes {
|
||||||
|
items: SysGenTableListModel[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSysGenTableListApi(params: SysGenTableListReq) {
|
||||||
|
return requestClient.get<SysGenTableListRes>('/genCodes/genTable/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface SysGenTableAddReq {
|
||||||
|
genType: number;
|
||||||
|
genTemplate: number;
|
||||||
|
varName: string;
|
||||||
|
options: string;
|
||||||
|
dbName: string;
|
||||||
|
tableName: string;
|
||||||
|
tableComment: string;
|
||||||
|
daoName: string;
|
||||||
|
masterColumns: string;
|
||||||
|
addonName: string;
|
||||||
|
}
|
||||||
|
export interface SysGenTableAddRes {
|
||||||
|
tableId: number;
|
||||||
|
}
|
||||||
|
export interface SysGenTableEditReq {
|
||||||
|
tableId: number;
|
||||||
|
genType?: number;
|
||||||
|
genTemplate?: number;
|
||||||
|
varName?: string;
|
||||||
|
options?: any;
|
||||||
|
dbName?: string;
|
||||||
|
tableName?: string;
|
||||||
|
tableComment?: string;
|
||||||
|
daoName?: string;
|
||||||
|
masterColumns?: any;
|
||||||
|
addonName?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SysGenTableEditRes {
|
||||||
|
tableId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SysGenTableDeleteReq {
|
||||||
|
tableIds: number[];
|
||||||
|
}
|
||||||
|
export interface SysGenTableDeleteRes {
|
||||||
|
tableIds: number[];
|
||||||
|
}
|
||||||
|
export interface SysGenTableJoinModel {
|
||||||
|
uuid: string;
|
||||||
|
dbName: string;
|
||||||
|
masterTableName: string;
|
||||||
|
linkTable: string;
|
||||||
|
alias: string;
|
||||||
|
linkMode: number;
|
||||||
|
field: string;
|
||||||
|
masterField: string;
|
||||||
|
daoName: string;
|
||||||
|
columns: GenCodesColumn[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SysGenTableTreeModel {
|
||||||
|
titleColumn: string;
|
||||||
|
styleType: number;
|
||||||
|
}
|
||||||
|
export interface SysGenTableMenuModel {
|
||||||
|
icon: string;
|
||||||
|
pid: number;
|
||||||
|
sort: number;
|
||||||
|
}
|
||||||
|
export interface SysGenTableFuncDictModel {
|
||||||
|
valueColumn: string|null ;
|
||||||
|
labelColumn: string|null;
|
||||||
|
}
|
||||||
|
export interface SysGenTablePresetStepModel {
|
||||||
|
formGridCols: number;
|
||||||
|
}
|
||||||
|
export interface SysGenTableOptionsModel {
|
||||||
|
headOps?: string[];
|
||||||
|
columnOps?: string[];
|
||||||
|
autoOps?: string[];
|
||||||
|
join?:any[];
|
||||||
|
menu?:SysGenTableMenuModel;
|
||||||
|
funcDict?: SysGenTableFuncDictModel;
|
||||||
|
presetStep?:SysGenTablePresetStepModel;
|
||||||
|
tree?:SysGenTableTreeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SysGenTableViewReq {
|
||||||
|
tableId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface SysGenTableViewRes {
|
||||||
|
tableId: number;
|
||||||
|
genType: number;
|
||||||
|
genTemplate: number;
|
||||||
|
varName: string;
|
||||||
|
options: SysGenTableOptionsModel|string;
|
||||||
|
dbName: string;
|
||||||
|
tableName: string;
|
||||||
|
tableComment: string;
|
||||||
|
daoName: string;
|
||||||
|
masterColumns: GenCodesColumn[]|string;
|
||||||
|
addonName: string;
|
||||||
|
status: string;
|
||||||
|
createdDept: number;
|
||||||
|
createdBy: number;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function addSysGenTableApi(params: SysGenTableAddReq | {
|
||||||
|
[x: string]: any;
|
||||||
|
}) {
|
||||||
|
return requestClient.post<SysGenTableAddRes>('/genCodes/genTable/add', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function deleteSysGenTableApi(params: SysGenTableDeleteReq) {
|
||||||
|
return requestClient.post<SysGenTableDeleteRes>('/genCodes/genTable/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function editSysGenTableApi(params: SysGenTableEditReq) {
|
||||||
|
return requestClient.post<SysGenTableEditRes>('/genCodes/genTable/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSysGenTableViewApi(params: SysGenTableViewReq) {
|
||||||
|
return requestClient.get<SysGenTableViewRes>('/genCodes/genTable/view', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface SelectItemModel {
|
||||||
|
value: string|number;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectGenTypeModel extends SelectItemModel,DictOption {
|
||||||
|
templates: SelectGenTemplateModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectGenTemplateModel extends SelectItemModel {
|
||||||
|
isAddon: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesSelect {
|
||||||
|
genType: SelectGenTypeModel[];
|
||||||
|
db: SelectItemModel[];
|
||||||
|
status: SelectItemModel[];
|
||||||
|
linkMode: SelectItemModel[];
|
||||||
|
buildMethod: SelectItemModel[];
|
||||||
|
formMode: SelectItemModel[];
|
||||||
|
formRole: SelectItemModel[];
|
||||||
|
whereMode: SelectItemModel[];
|
||||||
|
addons: SelectItemModel[];
|
||||||
|
tableAlign: SelectItemModel[];
|
||||||
|
treeStyleType: SelectItemModel[];
|
||||||
|
dictMode: SelectItemModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export interface GenCodesSelectsReq {
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesSelectsRes extends GenCodesSelect {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGenCodesSelectsApi(params: GenCodesSelectsReq) {
|
||||||
|
return requestClient.get<GenCodesSelectsRes>('/genCodes/genTable/selects', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DbTableSelectModel {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
daoName: string;
|
||||||
|
defVarName: string;
|
||||||
|
defAlias: string;
|
||||||
|
defTableComment: string;
|
||||||
|
}
|
||||||
|
export interface GenCodesTableSelectReq {
|
||||||
|
dbGroup: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesTableSelectRes {
|
||||||
|
items: DbTableSelectModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGenCodesTableSelectApi(params: GenCodesTableSelectReq) {
|
||||||
|
return requestClient.get<GenCodesTableSelectRes>('/genCodes/genTable/tableSelect', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DbColumnSelectModel {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesColumnSelectReq {
|
||||||
|
dbGroup: string;
|
||||||
|
tableName: string;
|
||||||
|
}
|
||||||
|
export interface GenCodesColumnSelectRes {
|
||||||
|
items: DbColumnSelectModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGenCodesColumnSelectApi(params: GenCodesColumnSelectReq) {
|
||||||
|
return requestClient.get<GenCodesColumnSelectRes>('/genCodes/genTable/columnSelect', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesColumn {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
dc: string;
|
||||||
|
dataType: string;
|
||||||
|
sqlType: string;
|
||||||
|
length: number;
|
||||||
|
isAllowNull: boolean;
|
||||||
|
defaultValue: any;
|
||||||
|
index: string;
|
||||||
|
extra: string;
|
||||||
|
goName: string;
|
||||||
|
goType: string;
|
||||||
|
tsName: string;
|
||||||
|
tsType: string;
|
||||||
|
isList: boolean;
|
||||||
|
isExport: boolean;
|
||||||
|
isSort: boolean;
|
||||||
|
isQuery: boolean;
|
||||||
|
queryWhere: string;
|
||||||
|
isEdit: boolean;
|
||||||
|
required: boolean;
|
||||||
|
unique: boolean;
|
||||||
|
formMode: string;
|
||||||
|
formRole: string;
|
||||||
|
formGridSpan: number;
|
||||||
|
dictType: number;
|
||||||
|
align: string;
|
||||||
|
width: number;
|
||||||
|
}
|
||||||
|
export interface GenCodesColumnListModel extends GenCodesColumn {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesColumnListParam {
|
||||||
|
dbGroup: string;
|
||||||
|
tableName: string;
|
||||||
|
isLink: number;
|
||||||
|
alias: string;
|
||||||
|
}
|
||||||
|
export interface GenCodesColumnListRes {
|
||||||
|
items: GenCodesColumnListModel[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getGenCodesColumnListApi(params: GenCodesColumnListParam) {
|
||||||
|
return requestClient.get<GenCodesColumnListRes>('/genCodes/genTable/columnList', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesPreviewReq extends SysGenTableListModel {
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface GenFile {
|
||||||
|
content: string;
|
||||||
|
path: string;
|
||||||
|
meth: number;
|
||||||
|
required: boolean;
|
||||||
|
}
|
||||||
|
export interface GenCodesPreviewRes {
|
||||||
|
views: Record<string, GenFile>;
|
||||||
|
config?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function postGenCodesPreviewApi(params: GenCodesPreviewReq) {
|
||||||
|
return requestClient.post<GenCodesPreviewRes>('/genCodes/genTable/preview', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenCodesBuildReq extends GenCodesPreviewReq {
|
||||||
|
|
||||||
|
}
|
||||||
|
export interface GenCodesBuildRes {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function postGenCodesBuildApi(params: GenCodesBuildReq) {
|
||||||
|
return requestClient.post<GenCodesBuildRes>('/genCodes/genTable/build', { ...params });
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @description API接口统一导出
|
||||||
|
* @Link https://github.com/xiujiecn/xiu-admin
|
||||||
|
* @Copyright Copyright (c) 2025 LiXiujie
|
||||||
|
* @Author Lxj <li@xiujie.cn>
|
||||||
|
* @License https://github.com/xiujiecn/xiu-admin/blob/master/LICENSE
|
||||||
|
* @date 2024-03-21
|
||||||
|
*/
|
||||||
|
|
||||||
|
export * from './core';
|
||||||
|
export * from './system';
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import type {
|
||||||
|
TApiDeleteParam,
|
||||||
|
TApiEditParam,
|
||||||
|
TApiExportParam,
|
||||||
|
TApiListParam,
|
||||||
|
TApiListRes,
|
||||||
|
TApiViewParam,
|
||||||
|
TApiViewRes,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取API集成列表
|
||||||
|
export function List(params: TApiListParam) {
|
||||||
|
return requestClient.get<TApiListRes>('agent/tApi/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除API集成
|
||||||
|
export function Delete(params: TApiDeleteParam) {
|
||||||
|
return requestClient.post('agent/tApi/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑API集成
|
||||||
|
export function Edit(params: TApiEditParam) {
|
||||||
|
return requestClient.post('agent/tApi/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取API集成指定详情
|
||||||
|
export function View(params: TApiViewParam) {
|
||||||
|
return requestClient.get<TApiViewRes>('agent/tApi/view', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出API集成
|
||||||
|
export function Export(params: TApiExportParam) {
|
||||||
|
return requestClient.post<Blob>(
|
||||||
|
'/agent/tApi/export',
|
||||||
|
{ ...params },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
},
|
||||||
|
responseType: 'blob',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import type { GlobalListRes } from '#/types/global';
|
||||||
|
import type { MemberSumma } from '#/utils';
|
||||||
|
|
||||||
|
/** API集成列表项 */
|
||||||
|
export interface TApiModel {
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
/** APPKey */
|
||||||
|
appKey: string;
|
||||||
|
/** APPSecret */
|
||||||
|
appSecret: string;
|
||||||
|
/** API分组 */
|
||||||
|
groupId: number;
|
||||||
|
/** 限流周期(秒,0:不限流) */
|
||||||
|
rateLimitPeriod: number;
|
||||||
|
/** 限流次数 */
|
||||||
|
rateLimitCount: number;
|
||||||
|
/** 所属机构 */
|
||||||
|
deptId: number;
|
||||||
|
/** 客户编号 */
|
||||||
|
deptKey: string;
|
||||||
|
/** 客户名称 */
|
||||||
|
deptName: string;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy: number;
|
||||||
|
/** 创建者摘要信息 */
|
||||||
|
createdBySumma: MemberSumma;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy: number;
|
||||||
|
/** 更新者摘要信息 */
|
||||||
|
updatedBySumma: MemberSumma;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取API集成列表 请求参数 */
|
||||||
|
export interface TApiListParam {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 每页大小 */
|
||||||
|
pageSize?: number;
|
||||||
|
/** 关键字 */
|
||||||
|
keyword?: string;
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取API集成列表 响应 */
|
||||||
|
export type TApiListRes = GlobalListRes<TApiModel>;
|
||||||
|
|
||||||
|
/** 导出API集成列表 请求参数 */
|
||||||
|
export type TApiExportParam = TApiListParam;
|
||||||
|
|
||||||
|
/** 修改/新增API集成 请求参数 */
|
||||||
|
export interface TApiEditParam {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** APPKey */
|
||||||
|
appKey?: string;
|
||||||
|
/** APPSecret */
|
||||||
|
appSecret?: string;
|
||||||
|
/** API分组 */
|
||||||
|
groupId?: number;
|
||||||
|
/** 限流周期(秒,0:不限流) */
|
||||||
|
rateLimitPeriod?: number;
|
||||||
|
/** 限流次数 */
|
||||||
|
rateLimitCount?: number;
|
||||||
|
/** 所属机构 */
|
||||||
|
deptId?: number;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId?: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept?: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy?: number;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取API集成指定信息 请求参数 */
|
||||||
|
export interface TApiViewParam {
|
||||||
|
/** APPKey */
|
||||||
|
appKey: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取API集成指定信息 响应 */
|
||||||
|
export type TApiViewRes = TApiModel;
|
||||||
|
|
||||||
|
/** 删除API集成 请求参数 */
|
||||||
|
export interface TApiDeleteParam {
|
||||||
|
/** 主键ID */
|
||||||
|
id: number | number[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import type {
|
||||||
|
TSimChannelTemplateDeleteParam,
|
||||||
|
TSimChannelTemplateEditParam,
|
||||||
|
TSimChannelTemplateExportParam,
|
||||||
|
TSimChannelTemplateListParam,
|
||||||
|
TSimChannelTemplateListRes,
|
||||||
|
TSimChannelTemplateViewParam,
|
||||||
|
TSimChannelTemplateViewRes,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取通道模板列表
|
||||||
|
export function List(params: TSimChannelTemplateListParam) {
|
||||||
|
return requestClient.get<TSimChannelTemplateListRes>(
|
||||||
|
'iot/tSimChannelTemplate/list',
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除通道模板
|
||||||
|
export function Delete(params: TSimChannelTemplateDeleteParam) {
|
||||||
|
return requestClient.post('iot/tSimChannelTemplate/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑通道模板
|
||||||
|
export function Edit(params: TSimChannelTemplateEditParam) {
|
||||||
|
return requestClient.post('iot/tSimChannelTemplate/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取通道模板指定详情
|
||||||
|
export function View(params: TSimChannelTemplateViewParam) {
|
||||||
|
return requestClient.get<TSimChannelTemplateViewRes>(
|
||||||
|
'iot/tSimChannelTemplate/view',
|
||||||
|
{
|
||||||
|
params,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出通道模板
|
||||||
|
export function Export(params: TSimChannelTemplateExportParam) {
|
||||||
|
return requestClient.post<Blob>(
|
||||||
|
'/iot/tSimChannelTemplate/export',
|
||||||
|
{ ...params },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
},
|
||||||
|
responseType: 'blob',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
import type { GlobalListRes } from '#/types/global';
|
||||||
|
import type { MemberSumma } from '#/utils';
|
||||||
|
|
||||||
|
/** 通道模板列表项 */
|
||||||
|
export interface TSimChannelTemplateModel {
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey: string;
|
||||||
|
/** 模板名称 */
|
||||||
|
tempName: string;
|
||||||
|
/** 模板模型 */
|
||||||
|
tempModel: string;
|
||||||
|
/** 模板配置 */
|
||||||
|
tempConfig: string;
|
||||||
|
/** 模板状态(0未启用,1启用) */
|
||||||
|
tempStatus: number;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy: number;
|
||||||
|
/** 创建者摘要信息 */
|
||||||
|
createdBySumma: MemberSumma;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy: number;
|
||||||
|
/** 更新者摘要信息 */
|
||||||
|
updatedBySumma: MemberSumma;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道模板指定信息响应项 */
|
||||||
|
export interface TSimChannelTemplateViewModel {
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey: string;
|
||||||
|
/** 模板名称 */
|
||||||
|
tempName: string;
|
||||||
|
/** 模板模型 */
|
||||||
|
tempModel: string;
|
||||||
|
/** 模板配置 */
|
||||||
|
tempConfig: string;
|
||||||
|
/** 模板状态(0未启用,1启用) */
|
||||||
|
tempStatus: number;
|
||||||
|
/** 备注 */
|
||||||
|
remark: string;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy: number;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt: string;
|
||||||
|
/** 创建者摘要信息 */
|
||||||
|
createdBySumma: MemberSumma;
|
||||||
|
/** 更新者摘要信息 */
|
||||||
|
updatedBySumma: MemberSumma;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道模板列表 请求参数 */
|
||||||
|
export interface TSimChannelTemplateListParam {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 每页大小 */
|
||||||
|
pageSize?: number;
|
||||||
|
/** 关键字 */
|
||||||
|
keyword?: string;
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey?: string;
|
||||||
|
/** 模板名称 */
|
||||||
|
tempName?: string;
|
||||||
|
/** 模板模型 */
|
||||||
|
tempModel?: string;
|
||||||
|
/** 模板状态(0未启用,1启用) */
|
||||||
|
tempStatus?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道模板列表 响应 */
|
||||||
|
export type TSimChannelTemplateListRes = GlobalListRes<TSimChannelTemplateModel>;
|
||||||
|
|
||||||
|
/** 导出通道模板列表 请求参数 */
|
||||||
|
export type TSimChannelTemplateExportParam = TSimChannelTemplateListParam;
|
||||||
|
|
||||||
|
/** 修改/新增通道模板 请求参数 */
|
||||||
|
export interface TSimChannelTemplateEditParam {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey?: string;
|
||||||
|
/** 模板名称 */
|
||||||
|
tempName?: string;
|
||||||
|
/** 模板模型 */
|
||||||
|
tempModel?: string;
|
||||||
|
/** 模板配置 */
|
||||||
|
tempConfig?: string;
|
||||||
|
/** 模板状态(0未启用,1启用) */
|
||||||
|
tempStatus?: number;
|
||||||
|
/** 备注 */
|
||||||
|
remark?: string;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId?: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept?: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy?: number;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道模板指定信息 请求参数 */
|
||||||
|
export interface TSimChannelTemplateViewParam {
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道模板指定信息 响应 */
|
||||||
|
export type TSimChannelTemplateViewRes = TSimChannelTemplateViewModel;
|
||||||
|
|
||||||
|
/** 删除通道模板 请求参数 */
|
||||||
|
export interface TSimChannelTemplateDeleteParam {
|
||||||
|
/** 主键ID */
|
||||||
|
id: number | number[];
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import type {
|
||||||
|
TSimChannelsDeleteParam,
|
||||||
|
TSimChannelsEditParam,
|
||||||
|
TSimChannelsExportParam,
|
||||||
|
TSimChannelsListParam,
|
||||||
|
TSimChannelsListRes,
|
||||||
|
TSimChannelsTestParam,
|
||||||
|
TSimChannelsTestRes,
|
||||||
|
TSimChannelsViewParam,
|
||||||
|
TSimChannelsViewRes,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取通道管理列表
|
||||||
|
export function List(params: TSimChannelsListParam) {
|
||||||
|
return requestClient.get<TSimChannelsListRes>('iot/tSimChannels/list', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除/批量删除通道管理
|
||||||
|
export function Delete(params: TSimChannelsDeleteParam) {
|
||||||
|
return requestClient.post('iot/tSimChannels/delete', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加/编辑通道管理
|
||||||
|
export function Edit(params: TSimChannelsEditParam) {
|
||||||
|
return requestClient.post('iot/tSimChannels/edit', { ...params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取通道管理指定详情
|
||||||
|
export function View(params: TSimChannelsViewParam) {
|
||||||
|
return requestClient.get<TSimChannelsViewRes>('iot/tSimChannels/view', {
|
||||||
|
params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出通道管理
|
||||||
|
export function Export(params: TSimChannelsExportParam) {
|
||||||
|
return requestClient.post<Blob>(
|
||||||
|
'/iot/tSimChannels/export',
|
||||||
|
{ ...params },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||||
|
},
|
||||||
|
responseType: 'blob',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Test(params: TSimChannelsTestParam) {
|
||||||
|
return requestClient.post<TSimChannelsTestRes>('iot/tSimChannels/test', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 刷新通道卡数量统计 */
|
||||||
|
export function RefreshCardCount() {
|
||||||
|
return requestClient.post('iot/tSimChannels/refresh-card-count');
|
||||||
|
}
|
||||||
@@ -0,0 +1,306 @@
|
|||||||
|
import type { GlobalListRes } from '#/types/global';
|
||||||
|
import type { MemberSumma } from '#/utils';
|
||||||
|
|
||||||
|
/** 通道管理列表项 */
|
||||||
|
export interface TSimChannelsModel {
|
||||||
|
/** 库存卡数量 */
|
||||||
|
stockCardCount: number;
|
||||||
|
/** 卡信息数量 */
|
||||||
|
cardInfoCount: number;
|
||||||
|
/** 销户卡数量 */
|
||||||
|
cancellationCardCount: number;
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
/** 运营商:1移动,2联通,3电信 */
|
||||||
|
operator: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey: string;
|
||||||
|
/** 是否允许同步:0否 1是 */
|
||||||
|
syncEnabled: number;
|
||||||
|
/** 模板名称 */
|
||||||
|
tempName: string;
|
||||||
|
/** 模板模型,用于识别共享 Token 能力 */
|
||||||
|
tempModel?: string;
|
||||||
|
/** 通道编号 */
|
||||||
|
chanKey: string;
|
||||||
|
/** 通道名称 */
|
||||||
|
chanName: string;
|
||||||
|
/** 通道配置 */
|
||||||
|
chanConfig: string;
|
||||||
|
/** 通道状态(0未启用,1启用) */
|
||||||
|
chanStatus: number;
|
||||||
|
/** 是否开放共享Token(0否,1是) */
|
||||||
|
shareEnabled: number;
|
||||||
|
/** 是否正常(0是,1否) */
|
||||||
|
isNormal: null | number;
|
||||||
|
/** 对接信息 */
|
||||||
|
dockingStatus: null | number;
|
||||||
|
/** 通道别名 */
|
||||||
|
chanAlias: string;
|
||||||
|
/** 通道行政区域代码(省市) */
|
||||||
|
regionCode: string;
|
||||||
|
/** 应用标识 */
|
||||||
|
appKey: string;
|
||||||
|
/** 应用密钥 */
|
||||||
|
appSecret: string;
|
||||||
|
/** 接口调用凭证 */
|
||||||
|
accessToken: string;
|
||||||
|
/** 凭证申请时间 */
|
||||||
|
accessTokenCreated: string;
|
||||||
|
/** 凭证过期时间 */
|
||||||
|
accessTokenExpire: string;
|
||||||
|
/** 刷新凭证 */
|
||||||
|
refreshToken: string;
|
||||||
|
/** 刷新凭证申请时间 */
|
||||||
|
refreshTokenCreated: string;
|
||||||
|
/** 刷新凭证过期时间 */
|
||||||
|
refreshTokenExpire: string;
|
||||||
|
/** 备注 */
|
||||||
|
remark: string;
|
||||||
|
/** 登录账号 */
|
||||||
|
loginUser: string;
|
||||||
|
/** 登录密码 */
|
||||||
|
loginPassword: string;
|
||||||
|
/** 登录地址 */
|
||||||
|
loginUrl: string;
|
||||||
|
/** 登录状态(0未启用,1启用) */
|
||||||
|
loginState: number;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy: number;
|
||||||
|
/** 创建者摘要信息 */
|
||||||
|
createdBySumma: MemberSumma;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy: number;
|
||||||
|
/** 更新者摘要信息 */
|
||||||
|
updatedBySumma: MemberSumma;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道管理指定信息响应项 */
|
||||||
|
export interface TSimChannelsViewModel {
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
/** 运营商:1移动,2联通,3电信 */
|
||||||
|
operator: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey: string;
|
||||||
|
/** 是否允许同步:0否 1是 */
|
||||||
|
syncEnabled: number;
|
||||||
|
/** 模板模型,用于识别共享 Token 能力 */
|
||||||
|
tempModel?: string;
|
||||||
|
/** 通道编号 */
|
||||||
|
chanKey: string;
|
||||||
|
/** 通道名称 */
|
||||||
|
chanName: string;
|
||||||
|
/** 通道配置 */
|
||||||
|
chanConfig: string;
|
||||||
|
/** 通道状态(0启用,1停用) */
|
||||||
|
chanStatus: number;
|
||||||
|
/** 是否开放共享Token(0否,1是) */
|
||||||
|
shareEnabled: number;
|
||||||
|
/** 是否正常(0是,1否) */
|
||||||
|
isNormal: null | number;
|
||||||
|
/** 对接信息 */
|
||||||
|
dockingStatus: null | number;
|
||||||
|
/** 通道别名 */
|
||||||
|
chanAlias: string;
|
||||||
|
/** 通道行政区域代码(省市) */
|
||||||
|
regionCode: string;
|
||||||
|
/** 应用标识 */
|
||||||
|
appKey: string;
|
||||||
|
/** 应用密钥 */
|
||||||
|
appSecret: string;
|
||||||
|
/** 接口调用凭证 */
|
||||||
|
accessToken: string;
|
||||||
|
/** 凭证申请时间 */
|
||||||
|
accessTokenCreated: string;
|
||||||
|
/** 凭证过期时间 */
|
||||||
|
accessTokenExpire: string;
|
||||||
|
/** 刷新凭证 */
|
||||||
|
refreshToken: string;
|
||||||
|
/** 备注 */
|
||||||
|
remark: string;
|
||||||
|
/** 登录账号 */
|
||||||
|
loginUser: string;
|
||||||
|
/** 登录密码 */
|
||||||
|
loginPassword: string;
|
||||||
|
/** 登录地址 */
|
||||||
|
loginUrl: string;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy: number;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt: string;
|
||||||
|
/** 模板名称 */
|
||||||
|
tempName: string;
|
||||||
|
/** 创建者摘要信息 */
|
||||||
|
createdBySumma: MemberSumma;
|
||||||
|
/** 更新者摘要信息 */
|
||||||
|
updatedBySumma: MemberSumma;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道管理列表 请求参数 */
|
||||||
|
export interface TSimChannelsListParam {
|
||||||
|
/** 页码 */
|
||||||
|
page?: number;
|
||||||
|
/** 每页大小 */
|
||||||
|
pageSize?: number;
|
||||||
|
/** 关键字 */
|
||||||
|
keyword?: string;
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 运营商:1移动,2联通,3电信 */
|
||||||
|
operator?: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey?: string;
|
||||||
|
/** 通道编号 */
|
||||||
|
chanKey?: string;
|
||||||
|
/** 通道名称 */
|
||||||
|
chanName?: string;
|
||||||
|
/** 通道别名 */
|
||||||
|
chanAlias?: string;
|
||||||
|
/** 通道状态(0未启用,1启用) */
|
||||||
|
chanStatus?: number;
|
||||||
|
shareEnabled?: number;
|
||||||
|
isNormal?: number | null;
|
||||||
|
dockingStatus?: number | null;
|
||||||
|
syncEnabled?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道管理列表 响应 */
|
||||||
|
export type TSimChannelsListRes = GlobalListRes<TSimChannelsModel>;
|
||||||
|
|
||||||
|
/** 导出通道管理列表 请求参数 */
|
||||||
|
export type TSimChannelsExportParam = TSimChannelsListParam;
|
||||||
|
|
||||||
|
/** 修改/新增通道管理 请求参数 */
|
||||||
|
export interface TSimChannelsEditParam {
|
||||||
|
/** ID */
|
||||||
|
id?: number;
|
||||||
|
/** 运营商:1移动,2联通,3电信 */
|
||||||
|
operator?: number;
|
||||||
|
/** 模板标识 */
|
||||||
|
tempKey?: string;
|
||||||
|
/** 通道编号 */
|
||||||
|
chanKey?: string;
|
||||||
|
/** 通道名称 */
|
||||||
|
chanName?: string;
|
||||||
|
/** 通道配置 */
|
||||||
|
chanConfig?: string;
|
||||||
|
/** 通道状态(0未启用,1启用) */
|
||||||
|
chanStatus?: number;
|
||||||
|
/** 是否开放共享Token(0否,1是) */
|
||||||
|
shareEnabled?: number;
|
||||||
|
/** 是否正常(0是,1否) */
|
||||||
|
isNormal?: null | number;
|
||||||
|
/** 对接信息 */
|
||||||
|
dockingStatus?: null | number;
|
||||||
|
/** 通道别名 */
|
||||||
|
chanAlias?: string;
|
||||||
|
/** 通道行政区域代码(省市) */
|
||||||
|
regionCode?: string;
|
||||||
|
/** 应用标识 */
|
||||||
|
appKey?: string;
|
||||||
|
/** 应用密钥 */
|
||||||
|
appSecret?: string;
|
||||||
|
/** 接口调用凭证 */
|
||||||
|
accessToken?: string;
|
||||||
|
/** 凭证申请时间 */
|
||||||
|
accessTokenCreated?: string;
|
||||||
|
/** 凭证过期时间 */
|
||||||
|
accessTokenExpire?: string;
|
||||||
|
/** 刷新凭证 */
|
||||||
|
refreshToken?: string;
|
||||||
|
/** 刷新凭证申请时间 */
|
||||||
|
refreshTokenCreated?: string;
|
||||||
|
/** 刷新凭证过期时间 */
|
||||||
|
refreshTokenExpire?: string;
|
||||||
|
/** 备注 */
|
||||||
|
remark?: string;
|
||||||
|
/** 登录账号 */
|
||||||
|
loginUser?: string;
|
||||||
|
/** 登录密码 */
|
||||||
|
loginPassword?: string;
|
||||||
|
/** 登录地址 */
|
||||||
|
loginUrl?: string;
|
||||||
|
/** 登录状态(0未启用,1启用) */
|
||||||
|
loginState?: number;
|
||||||
|
/** 租户ID */
|
||||||
|
tenantId?: string;
|
||||||
|
/** 创建部门 */
|
||||||
|
createdDept?: number;
|
||||||
|
/** 创建者 */
|
||||||
|
createdBy?: number;
|
||||||
|
/** 创建时间 */
|
||||||
|
createdAt?: string;
|
||||||
|
/** 更新者 */
|
||||||
|
updatedBy?: number;
|
||||||
|
/** 更新时间 */
|
||||||
|
updatedAt?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道管理指定信息 请求参数 */
|
||||||
|
export interface TSimChannelsViewParam {
|
||||||
|
/** ID */
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取通道管理指定信息 响应 */
|
||||||
|
export type TSimChannelsViewRes = TSimChannelsViewModel;
|
||||||
|
|
||||||
|
/** 删除通道管理 请求参数 */
|
||||||
|
export interface TSimChannelsDeleteParam {
|
||||||
|
/** 主键ID */
|
||||||
|
id: number | number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 通道测试请求参数 */
|
||||||
|
export interface TSimChannelsTestParam {
|
||||||
|
channelId: number;
|
||||||
|
inputMode: 'complete' | 'direct_single' | 'single';
|
||||||
|
cardValueType?: 'iccid' | 'imsi' | 'msisdn';
|
||||||
|
cardValue?: string;
|
||||||
|
iccid?: string;
|
||||||
|
imsi?: string;
|
||||||
|
msisdn?: string;
|
||||||
|
/** 未传时执行全部查询项 */
|
||||||
|
items?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSimChannelsTestItemStatus = 'failed' | 'success' | 'timeout';
|
||||||
|
|
||||||
|
/** 单项测试结果 */
|
||||||
|
export interface TSimChannelsTestItem {
|
||||||
|
key: string;
|
||||||
|
name: string;
|
||||||
|
status: TSimChannelsTestItemStatus;
|
||||||
|
durationMs: number;
|
||||||
|
errorCode?: number;
|
||||||
|
message?: string;
|
||||||
|
data?: unknown;
|
||||||
|
/** 完整通道测试响应 JSON,归属当前单项展示 */
|
||||||
|
responseJson?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 通道测试响应 */
|
||||||
|
export interface TSimChannelsTestRes {
|
||||||
|
items: TSimChannelsTestItem[];
|
||||||
|
/** Unmodified response body from the channel gateway test endpoint. */
|
||||||
|
gatewayResponseRaw?: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import type {
|
||||||
|
TSimCardAllocateBatchSubmitParam,
|
||||||
|
TSimCardAllocateBatchSubmitRes,
|
||||||
|
TSimCardAllocateExcelSubmitParam,
|
||||||
|
TSimCardAllocateInputPrepareParam,
|
||||||
|
TSimCardAllocatePrepareRes,
|
||||||
|
TSimCardAllocateTarget,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export function DownloadTemplate() {
|
||||||
|
return requestClient.download('/iot/tSimCardAllocate/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ListTargets(keyword = '') {
|
||||||
|
return requestClient.get<{ items: TSimCardAllocateTarget[] }>(
|
||||||
|
'/iot/tSimCardAllocate/targets',
|
||||||
|
{ params: { keyword } },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputPrepare(params: TSimCardAllocateInputPrepareParam) {
|
||||||
|
return requestClient.post<TSimCardAllocatePrepareRes>(
|
||||||
|
'/iot/tSimCardAllocate/input/prepare',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExcelSubmit(params: TSimCardAllocateExcelSubmitParam) {
|
||||||
|
const data: Record<string, any> & { file: File } = {
|
||||||
|
file: params.file,
|
||||||
|
remark: params.remark ?? '',
|
||||||
|
};
|
||||||
|
if (params.targetDeptId) {
|
||||||
|
data.targetDeptId = String(params.targetDeptId);
|
||||||
|
}
|
||||||
|
return requestClient.upload<TSimCardAllocatePrepareRes>(
|
||||||
|
'/iot/tSimCardAllocate/excel/submit',
|
||||||
|
data,
|
||||||
|
{ timeout: 10 * 60_000 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BatchSubmit(params: TSimCardAllocateBatchSubmitParam) {
|
||||||
|
return requestClient.post<TSimCardAllocateBatchSubmitRes>(
|
||||||
|
'/iot/tSimCardAllocate/batch/submit',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
export interface TSimCardAllocateTarget {
|
||||||
|
deptId: number;
|
||||||
|
parentId: number;
|
||||||
|
deptKey?: string;
|
||||||
|
deptName: string;
|
||||||
|
deptCategory?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardAllocateInputPrepareParam {
|
||||||
|
input: string;
|
||||||
|
targetDeptId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardAllocateExcelSubmitParam {
|
||||||
|
file: File;
|
||||||
|
targetDeptId?: number;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardAllocateBatchSubmitParam {
|
||||||
|
batchNo: string;
|
||||||
|
targetDeptId?: number;
|
||||||
|
remark?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardAllocateBatchDetail {
|
||||||
|
rowNo: number;
|
||||||
|
itemId: string;
|
||||||
|
targetDeptId?: number;
|
||||||
|
targetDeptKey?: string;
|
||||||
|
targetDeptName?: string;
|
||||||
|
status: string;
|
||||||
|
errorReason?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardAllocatePrepareRes {
|
||||||
|
batchNo: string;
|
||||||
|
batchType: number;
|
||||||
|
batchAsyncStatus?: number;
|
||||||
|
target?: TSimCardAllocateTarget;
|
||||||
|
totalCount: number;
|
||||||
|
validCount: number;
|
||||||
|
failCount: number;
|
||||||
|
failReason?: string;
|
||||||
|
status: string;
|
||||||
|
details: TSimCardAllocateBatchDetail[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSimCardAllocateBatchSubmitRes = TSimCardAllocatePrepareRes;
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import type {
|
||||||
|
TSimAddPackageBatchConfirmParam,
|
||||||
|
TSimAddPackageBatchConfirmRes,
|
||||||
|
TSimAddPackageOptionsRes,
|
||||||
|
TSimAddPackagePrepareResult,
|
||||||
|
TSimBindPackageAddPackageExcelSubmitParam,
|
||||||
|
TSimBindPackageAddPackageInputPrepareParam,
|
||||||
|
TSimBindPackageAddPackageInputPrepareRes,
|
||||||
|
TSimBindPackageAddPackageInputSubmitParam,
|
||||||
|
TSimBindPackageAddPackageSubmitRes,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
// 获取卡绑定套餐列表
|
||||||
|
export function List(params: any) {
|
||||||
|
return requestClient.get<any>('iot/tSimCardBindPkg/list', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取卡套餐详情
|
||||||
|
export function GetPkgInfo(params: any) {
|
||||||
|
return requestClient.get<any>('iot/tSimCardBindPkg/getPkgInfo', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取变更套餐
|
||||||
|
export function GetChangePkg(params: any) {
|
||||||
|
return requestClient.get<any>('iot/tSimCardBindPkg/getChangePkg', { params });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DownloadAddPackageTemplate() {
|
||||||
|
return requestClient.download(
|
||||||
|
'/iot/tSimCardBindPkg/addPackage/excel/template',
|
||||||
|
{
|
||||||
|
responseReturn: 'raw',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackageExcelSubmit(
|
||||||
|
params: TSimBindPackageAddPackageExcelSubmitParam,
|
||||||
|
): Promise<TSimBindPackageAddPackageSubmitRes> {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/iot/tSimCardBindPkg/addPackage/excel/submit',
|
||||||
|
{
|
||||||
|
file: params.file,
|
||||||
|
manualConfirm: params.manualConfirm,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackageOptions(params: {
|
||||||
|
iccids: string[];
|
||||||
|
}): Promise<TSimAddPackageOptionsRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardBindPkg/addPackage/options', params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackagePaymentContext(): Promise<TSimAddPackageOptionsRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardBindPkg/addPackage/payment-context');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackageInputSubmit(
|
||||||
|
params: TSimBindPackageAddPackageInputSubmitParam,
|
||||||
|
): Promise<TSimBindPackageAddPackageSubmitRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardBindPkg/addPackage/input/submit', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackageInputPrepare(
|
||||||
|
params: TSimBindPackageAddPackageInputPrepareParam,
|
||||||
|
): Promise<TSimBindPackageAddPackageInputPrepareRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardBindPkg/addPackage/input/prepare', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackagePrepareResult(
|
||||||
|
batchNo: string,
|
||||||
|
): Promise<TSimAddPackagePrepareResult> {
|
||||||
|
return requestClient.get('/iot/tSimCardBindPkg/addPackage/prepare/result', {
|
||||||
|
params: { batchNo },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddPackageBatchConfirm(
|
||||||
|
params: TSimAddPackageBatchConfirmParam,
|
||||||
|
): Promise<TSimAddPackageBatchConfirmRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardBindPkg/addPackage/batch/confirm', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
export type AddPackageEffectiveMode = 'current_month';
|
||||||
|
export type AddPackagePaymentMode = 'balance' | 'offline';
|
||||||
|
|
||||||
|
export interface TSimBindPackageAddPackageExcelSubmitParam {
|
||||||
|
file: File;
|
||||||
|
paymentMode?: AddPackagePaymentMode;
|
||||||
|
offlineContractCompleted?: boolean;
|
||||||
|
manualConfirm?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimBindPackageAddPackageInputSubmitParam {
|
||||||
|
input: string;
|
||||||
|
packageCode?: string;
|
||||||
|
effectiveMode?: AddPackageEffectiveMode | string;
|
||||||
|
remark?: string;
|
||||||
|
paymentMode?: AddPackagePaymentMode;
|
||||||
|
offlineContractCompleted?: boolean;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimBindPackageAddPackageInputPrepareParam {
|
||||||
|
input: string;
|
||||||
|
packageCode?: string;
|
||||||
|
effectiveMode?: AddPackageEffectiveMode | string;
|
||||||
|
remark?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimBindPackageAddPackageSubmitRes {
|
||||||
|
batchAsyncStatus?: number;
|
||||||
|
batchNo?: string;
|
||||||
|
batchType?: number;
|
||||||
|
targetBatchType?: number;
|
||||||
|
total?: number;
|
||||||
|
validCount?: number;
|
||||||
|
failCount?: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSimBindPackageAddPackageInputPrepareRes =
|
||||||
|
TSimAddPackagePrepareResult;
|
||||||
|
|
||||||
|
export interface TSimAddPackageBatchConfirmParam {
|
||||||
|
batchNo: string;
|
||||||
|
packageCode?: string;
|
||||||
|
effectiveMode?: AddPackageEffectiveMode | string;
|
||||||
|
remark?: string;
|
||||||
|
paymentMode?: AddPackagePaymentMode | string;
|
||||||
|
offlineContractCompleted?: boolean;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimAddPackageBatchConfirmRes {
|
||||||
|
batchAsyncStatus?: number;
|
||||||
|
batchNo?: string;
|
||||||
|
batchType?: number;
|
||||||
|
effectiveMode?: AddPackageEffectiveMode | string;
|
||||||
|
packageCode?: string;
|
||||||
|
status?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimAddPackagePaymentAccount {
|
||||||
|
payerDeptId: number;
|
||||||
|
targetCount: number;
|
||||||
|
requiredAmount: number | string;
|
||||||
|
availableBalance: number | string;
|
||||||
|
balanceEnough: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimAddPackageOption {
|
||||||
|
flowMb: number;
|
||||||
|
packageCode: string;
|
||||||
|
packageName: string;
|
||||||
|
unitPrice?: number | string;
|
||||||
|
totalAmount?: number | string;
|
||||||
|
priceAvailable?: boolean;
|
||||||
|
priceReason?: string;
|
||||||
|
paymentAccounts?: TSimAddPackagePaymentAccount[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimAddPackageOptionsRes {
|
||||||
|
items: TSimAddPackageOption[];
|
||||||
|
isTopDept?: boolean;
|
||||||
|
defaultPaymentMode?: AddPackagePaymentMode | string;
|
||||||
|
availableBalance?: number | string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimAddPackagePrepareResult {
|
||||||
|
availableBalance?: number | string;
|
||||||
|
batchAsyncStatus?: number;
|
||||||
|
batchInfo?: Record<string, any>;
|
||||||
|
batchNo?: string;
|
||||||
|
batchType?: number;
|
||||||
|
effectiveMode?: AddPackageEffectiveMode | string;
|
||||||
|
effectiveModeText?: string;
|
||||||
|
effectiveTime?: string;
|
||||||
|
expectedExpireTime?: string;
|
||||||
|
failCount?: number;
|
||||||
|
failReason?: string;
|
||||||
|
flowMb?: number | string;
|
||||||
|
isTopDept?: boolean;
|
||||||
|
packageCode?: string;
|
||||||
|
packageName?: string;
|
||||||
|
paymentAccounts?: TSimAddPackagePaymentAccount[];
|
||||||
|
availablePackages?: TSimAddPackageOption[];
|
||||||
|
quantity?: number;
|
||||||
|
runResult?: number;
|
||||||
|
snapshot?: Record<string, any>;
|
||||||
|
status?: string;
|
||||||
|
successCount?: number;
|
||||||
|
targetBatchType?: number;
|
||||||
|
total?: number;
|
||||||
|
totalAmount?: number | string;
|
||||||
|
unitPrice?: number | string;
|
||||||
|
validCount?: number;
|
||||||
|
defaultPaymentMode?: AddPackagePaymentMode | string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import type {
|
||||||
|
TSimCardCancelExcelSubmitParam,
|
||||||
|
TSimCardCancelInputSubmitParam,
|
||||||
|
TSimCardCancelSubmitRes,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export function DownloadTemplate() {
|
||||||
|
return requestClient.download('/iot/tSimCardCancel/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExcelSubmit(
|
||||||
|
params: TSimCardCancelExcelSubmitParam,
|
||||||
|
): Promise<TSimCardCancelSubmitRes> {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/iot/tSimCardCancel/excel/submit',
|
||||||
|
{ file: params.file },
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputSubmit(
|
||||||
|
params: TSimCardCancelInputSubmitParam,
|
||||||
|
): Promise<TSimCardCancelSubmitRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardCancel/input/submit', params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/** 提交Excel卡销户请求参数 */
|
||||||
|
export interface TSimCardCancelExcelSubmitParam {
|
||||||
|
/** Excel文件 */
|
||||||
|
file: File;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交批量输入卡销户请求参数 */
|
||||||
|
export interface TSimCardCancelInputSubmitParam {
|
||||||
|
/** 批量输入ICCID文本 */
|
||||||
|
input: string;
|
||||||
|
/** 销户备注 */
|
||||||
|
remark?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 卡销户提交响应 */
|
||||||
|
export interface TSimCardCancelSubmitRes {
|
||||||
|
/** 批次异步处理状态(Pending 等) */
|
||||||
|
batchAsyncStatus?: number;
|
||||||
|
/** 批次号 */
|
||||||
|
batchNo?: string;
|
||||||
|
/** 批次类型 */
|
||||||
|
batchType?: number;
|
||||||
|
/** 失败行数 */
|
||||||
|
failCount?: number;
|
||||||
|
/** 实际销户数量 */
|
||||||
|
quantity?: number;
|
||||||
|
/** 结果下载任务ID */
|
||||||
|
resultDownloadId?: number;
|
||||||
|
/** 结果文件OSS ID */
|
||||||
|
resultFileOssId?: number;
|
||||||
|
/** 导入状态 */
|
||||||
|
status?: string;
|
||||||
|
/** 解析总行数 */
|
||||||
|
total?: number;
|
||||||
|
/** 校验通过行数 */
|
||||||
|
validCount?: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const download = vi.fn();
|
||||||
|
const get = vi.fn();
|
||||||
|
const post = vi.fn();
|
||||||
|
const upload = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('#/api/request', () => ({
|
||||||
|
requestClient: {
|
||||||
|
download,
|
||||||
|
get,
|
||||||
|
post,
|
||||||
|
upload,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('tSimCardChange api', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
download.mockReset();
|
||||||
|
get.mockReset();
|
||||||
|
post.mockReset();
|
||||||
|
upload.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('downloads excel change template from backend contract path', async () => {
|
||||||
|
const { DownloadTemplate } = await import('./index');
|
||||||
|
|
||||||
|
DownloadTemplate();
|
||||||
|
|
||||||
|
expect(download).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardChange/excel/template',
|
||||||
|
{
|
||||||
|
responseReturn: 'raw',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits excel change as file-only form data', async () => {
|
||||||
|
const { ExcelSubmit } = await import('./index');
|
||||||
|
const file = new File(['iccid'], 'change.xlsx');
|
||||||
|
|
||||||
|
ExcelSubmit({ file });
|
||||||
|
|
||||||
|
expect(upload).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardChange/excel/submit',
|
||||||
|
{ file },
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepares input change with backend contract path', async () => {
|
||||||
|
const { InputPrepare } = await import('./index');
|
||||||
|
const params = {
|
||||||
|
durationMonths: 6,
|
||||||
|
effectiveMode: 'immediate' as const,
|
||||||
|
input: '8986000000000000001\n8986000000000000002',
|
||||||
|
newPackageCode: 'PKG002',
|
||||||
|
offlineContractCompleted: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputPrepare(params);
|
||||||
|
|
||||||
|
expect(post).toHaveBeenCalledWith('/iot/tSimCardChange/input/prepare', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prepares range change with backend contract path', async () => {
|
||||||
|
const { RangePrepare } = await import('./index');
|
||||||
|
const params = {
|
||||||
|
durationMonths: 6,
|
||||||
|
effectiveMode: 'next_month' as const,
|
||||||
|
endIccid: '8986000000000000009',
|
||||||
|
endImsi: '460000000000009',
|
||||||
|
endMsisdn: '13000000009',
|
||||||
|
newPackageCode: 'PKG002',
|
||||||
|
offlineContractCompleted: true,
|
||||||
|
startIccid: '8986000000000000001',
|
||||||
|
startImsi: '460000000000001',
|
||||||
|
startMsisdn: '13000000001',
|
||||||
|
};
|
||||||
|
|
||||||
|
RangePrepare(params);
|
||||||
|
|
||||||
|
expect(post).toHaveBeenCalledWith('/iot/tSimCardChange/range/prepare', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits change prepare batch with backend contract path', async () => {
|
||||||
|
const { BatchSubmit } = await import('./index');
|
||||||
|
const params = {
|
||||||
|
batchNo: 'CH202607060001',
|
||||||
|
durationMonths: 6,
|
||||||
|
effectiveMode: 'immediate' as const,
|
||||||
|
newPackageCode: 'PKG002',
|
||||||
|
offlineContractCompleted: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
BatchSubmit(params);
|
||||||
|
|
||||||
|
expect(post).toHaveBeenCalledWith('/iot/tSimCardChange/batch/submit', {
|
||||||
|
...params,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('polls change prepare result with backend contract path', async () => {
|
||||||
|
const { PrepareResult } = await import('./index');
|
||||||
|
const batchNo = 'CH202607060001';
|
||||||
|
|
||||||
|
PrepareResult(batchNo);
|
||||||
|
|
||||||
|
expect(get).toHaveBeenCalledWith('/iot/tSimCardChange/prepare/result', {
|
||||||
|
params: { batchNo },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import type {
|
||||||
|
TSimCardChangeBatchPrepareRes,
|
||||||
|
TSimCardChangeBatchPrepareResult,
|
||||||
|
TSimCardChangeBatchSubmitParam,
|
||||||
|
TSimCardChangeBatchSubmitRes,
|
||||||
|
TSimCardChangeExcelSubmitParam,
|
||||||
|
TSimCardChangeExcelSubmitRes,
|
||||||
|
TSimCardChangeInputPrepareParam,
|
||||||
|
TSimCardChangeInputOptionsParam,
|
||||||
|
TSimCardChangeOptionsRes,
|
||||||
|
TSimCardChangeRangePrepareParam,
|
||||||
|
TSimCardChangeRangeOptionsParam,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export function DownloadTemplate() {
|
||||||
|
return requestClient.download('/iot/tSimCardChange/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExcelSubmit(
|
||||||
|
params: TSimCardChangeExcelSubmitParam,
|
||||||
|
): Promise<TSimCardChangeExcelSubmitRes> {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/iot/tSimCardChange/excel/submit',
|
||||||
|
{
|
||||||
|
file: params.file,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputOptions(params: TSimCardChangeInputOptionsParam) {
|
||||||
|
return requestClient.post<TSimCardChangeOptionsRes>(
|
||||||
|
'/iot/tSimCardChange/input/change-options',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RangeOptions(params: TSimCardChangeRangeOptionsParam) {
|
||||||
|
return requestClient.post<TSimCardChangeOptionsRes>(
|
||||||
|
'/iot/tSimCardChange/range/change-options',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputPrepare(params: TSimCardChangeInputPrepareParam) {
|
||||||
|
return requestClient.post<TSimCardChangeBatchPrepareRes>(
|
||||||
|
'/iot/tSimCardChange/input/prepare',
|
||||||
|
{ ...params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RangePrepare(params: TSimCardChangeRangePrepareParam) {
|
||||||
|
return requestClient.post<TSimCardChangeBatchPrepareRes>(
|
||||||
|
'/iot/tSimCardChange/range/prepare',
|
||||||
|
{ ...params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BatchSubmit(params: TSimCardChangeBatchSubmitParam) {
|
||||||
|
return requestClient.post<TSimCardChangeBatchSubmitRes>(
|
||||||
|
'/iot/tSimCardChange/batch/submit',
|
||||||
|
{ ...params },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PrepareResult(batchNo: string) {
|
||||||
|
return requestClient.get<TSimCardChangeBatchPrepareResult>(
|
||||||
|
'/iot/tSimCardChange/prepare/result',
|
||||||
|
{ params: { batchNo } },
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
export type TSimCardChangeEffectiveMode = 'immediate' | 'next_month';
|
||||||
|
|
||||||
|
export interface TSimCardChangeInputPrepareParam {
|
||||||
|
durationMonths: number;
|
||||||
|
effectiveMode: TSimCardChangeEffectiveMode;
|
||||||
|
giftDurationMonths?: number;
|
||||||
|
input: string;
|
||||||
|
newPackageCode: string;
|
||||||
|
offlineContractCompleted: boolean;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeExcelSubmitParam {
|
||||||
|
file: File;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeExcelSubmitRes {
|
||||||
|
batchNo?: string;
|
||||||
|
batchType?: number;
|
||||||
|
resultDownloadId?: number;
|
||||||
|
resultFileOssId?: number;
|
||||||
|
status?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeInputOptionsParam {
|
||||||
|
input: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeRangeOptionsParam {
|
||||||
|
endIccid: string;
|
||||||
|
endImsi: string;
|
||||||
|
endMsisdn: string;
|
||||||
|
startIccid: string;
|
||||||
|
startImsi: string;
|
||||||
|
startMsisdn: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangePackageOption {
|
||||||
|
changeUnitPrice?: number | string;
|
||||||
|
flowMb?: number;
|
||||||
|
packageCode: string;
|
||||||
|
packageName: string;
|
||||||
|
packageType?: number;
|
||||||
|
renewableDurationOptions?: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeOptionsRes {
|
||||||
|
customerAgentId?: number;
|
||||||
|
customerName?: string;
|
||||||
|
oldPackageCode?: string;
|
||||||
|
oldPackageName?: string;
|
||||||
|
oldPackageType?: number;
|
||||||
|
packages?: TSimCardChangePackageOption[];
|
||||||
|
quantity?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeBatchPrepareRes {
|
||||||
|
batchNo: string;
|
||||||
|
batchType?: number;
|
||||||
|
agentDefaultPriceId?: number;
|
||||||
|
changeUnitPrice?: number | string;
|
||||||
|
customerAgentId?: number;
|
||||||
|
customerName?: string;
|
||||||
|
failCount?: number;
|
||||||
|
newPackageCode?: string;
|
||||||
|
newPackageFlowMb?: number;
|
||||||
|
newPackageName?: string;
|
||||||
|
newPackageType?: number;
|
||||||
|
oldPackageCode?: string;
|
||||||
|
oldPackageFlowMb?: number;
|
||||||
|
oldPackageName?: string;
|
||||||
|
oldPackageType?: number;
|
||||||
|
priceSource?: string;
|
||||||
|
quantity?: number;
|
||||||
|
renewableDurationOptions?: number[];
|
||||||
|
resultDownloadId?: number;
|
||||||
|
resultFileOssId?: number;
|
||||||
|
sourceBatchNo?: string;
|
||||||
|
status?: string;
|
||||||
|
targetBatchType?: number;
|
||||||
|
targetPriceId?: number;
|
||||||
|
total?: number;
|
||||||
|
validCount?: number;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeRangePrepareParam {
|
||||||
|
durationMonths: number;
|
||||||
|
effectiveMode: TSimCardChangeEffectiveMode;
|
||||||
|
endIccid: string;
|
||||||
|
endImsi: string;
|
||||||
|
endMsisdn: string;
|
||||||
|
giftDurationMonths?: number;
|
||||||
|
newPackageCode: string;
|
||||||
|
offlineContractCompleted: boolean;
|
||||||
|
startIccid: string;
|
||||||
|
startImsi: string;
|
||||||
|
startMsisdn: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeBatchSubmitParam {
|
||||||
|
adminNote?: string;
|
||||||
|
batchNo: string;
|
||||||
|
buyerNote?: string;
|
||||||
|
durationMonths: number;
|
||||||
|
effectiveMode: TSimCardChangeEffectiveMode;
|
||||||
|
giftDurationMonths?: number;
|
||||||
|
newPackageCode: string;
|
||||||
|
offlineContractCompleted: boolean;
|
||||||
|
remark?: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardChangeBatchSubmitRes {
|
||||||
|
batchNo?: string;
|
||||||
|
batchType?: number;
|
||||||
|
changeUnitPrice?: number | string;
|
||||||
|
customerAgentId?: number;
|
||||||
|
customerName?: string;
|
||||||
|
durationMonths?: number;
|
||||||
|
effectiveMode?: string;
|
||||||
|
giftDurationMonths?: number;
|
||||||
|
newPackageCode?: string;
|
||||||
|
newPackageName?: string;
|
||||||
|
oldPackageCode?: string;
|
||||||
|
oldPackageName?: string;
|
||||||
|
orderSn?: string;
|
||||||
|
packageBeginTime?: string;
|
||||||
|
packageEndTime?: string;
|
||||||
|
quantity?: number;
|
||||||
|
sourceBatchNo?: string;
|
||||||
|
status?: string;
|
||||||
|
totalAmount?: number | string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSimCardChangeBatchAsyncStatus = number;
|
||||||
|
|
||||||
|
export interface TSimCardChangeBatchPrepareResult {
|
||||||
|
batchAsyncStatus: TSimCardChangeBatchAsyncStatus;
|
||||||
|
batchInfo?: TSimCardChangeBatchPrepareRes;
|
||||||
|
batchNo: string;
|
||||||
|
failReason?: string;
|
||||||
|
runResult?: number;
|
||||||
|
status: string;
|
||||||
|
[property: string]: any;
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const download = vi.fn();
|
||||||
|
const post = vi.fn();
|
||||||
|
const upload = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('#/api/request', () => ({
|
||||||
|
requestClient: {
|
||||||
|
download,
|
||||||
|
post,
|
||||||
|
upload,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('tSimCardDataCut api', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
download.mockReset();
|
||||||
|
post.mockReset();
|
||||||
|
upload.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('downloads the data cut template from the backend contract path', async () => {
|
||||||
|
const { DownloadTemplate } = await import('./index');
|
||||||
|
|
||||||
|
DownloadTemplate();
|
||||||
|
|
||||||
|
expect(download).toHaveBeenCalledWith('/iot/tSimCardDataCut/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an excel data cut batch with the batch threshold', async () => {
|
||||||
|
const { ExcelSubmit } = await import('./index');
|
||||||
|
const file = new File(['iccid'], 'dataCut.xlsx');
|
||||||
|
|
||||||
|
ExcelSubmit({ file, trafficCutoffThreshold: 1024.125 });
|
||||||
|
|
||||||
|
expect(upload).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardDataCut/excel/submit',
|
||||||
|
{ file, trafficCutoffThreshold: 1024.125 },
|
||||||
|
{ timeout: 60_000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an input data cut batch from the backend contract path', async () => {
|
||||||
|
const { InputSubmit } = await import('./index');
|
||||||
|
const params = {
|
||||||
|
input: '8986000000000000001',
|
||||||
|
trafficCutoffThreshold: 2048,
|
||||||
|
};
|
||||||
|
|
||||||
|
InputSubmit(params);
|
||||||
|
|
||||||
|
expect(post).toHaveBeenCalledWith('/iot/tSimCardDataCut/input/submit', params);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import type {
|
||||||
|
TSimCardDataCutExcelSubmitParam,
|
||||||
|
TSimCardDataCutInputSubmitParam,
|
||||||
|
TSimCardDataCutSubmitRes,
|
||||||
|
} from './type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export function DownloadTemplate() {
|
||||||
|
return requestClient.download('/iot/tSimCardDataCut/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExcelSubmit(
|
||||||
|
params: TSimCardDataCutExcelSubmitParam,
|
||||||
|
): Promise<TSimCardDataCutSubmitRes> {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/iot/tSimCardDataCut/excel/submit',
|
||||||
|
{
|
||||||
|
file: params.file,
|
||||||
|
trafficCutoffThreshold: params.trafficCutoffThreshold,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputSubmit(
|
||||||
|
params: TSimCardDataCutInputSubmitParam,
|
||||||
|
): Promise<TSimCardDataCutSubmitRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardDataCut/input/submit', params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export interface TSimCardDataCutExcelSubmitParam {
|
||||||
|
file: File;
|
||||||
|
trafficCutoffThreshold: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardDataCutInputSubmitParam {
|
||||||
|
input: string;
|
||||||
|
trafficCutoffThreshold: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TSimCardDataCutSubmitRes {
|
||||||
|
batchNo: string;
|
||||||
|
batchType: number;
|
||||||
|
batchAsyncStatus: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const download = vi.fn();
|
||||||
|
const post = vi.fn();
|
||||||
|
const upload = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('#/api/request', () => ({
|
||||||
|
requestClient: {
|
||||||
|
download,
|
||||||
|
post,
|
||||||
|
upload,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('tSimCardDataStart api', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
download.mockReset();
|
||||||
|
post.mockReset();
|
||||||
|
upload.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('downloads the data start template from the backend contract path', async () => {
|
||||||
|
const { DownloadTemplate } = await import('./index');
|
||||||
|
|
||||||
|
DownloadTemplate();
|
||||||
|
|
||||||
|
expect(download).toHaveBeenCalledWith('/iot/tSimCardDataStart/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an excel data start batch as file-only form data', async () => {
|
||||||
|
const { ExcelSubmit } = await import('./index');
|
||||||
|
const file = new File(['iccid'], 'dataStart.xlsx');
|
||||||
|
|
||||||
|
ExcelSubmit({ file });
|
||||||
|
|
||||||
|
expect(upload).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardDataStart/excel/submit',
|
||||||
|
{ file },
|
||||||
|
{ timeout: 60_000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an input data start batch from the backend contract path', async () => {
|
||||||
|
const { InputSubmit } = await import('./index');
|
||||||
|
const params = {
|
||||||
|
input: '8986000000000000001',
|
||||||
|
};
|
||||||
|
|
||||||
|
InputSubmit(params);
|
||||||
|
|
||||||
|
expect(post).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardDataStart/input/submit',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import type {
|
||||||
|
TSimCardLifecycleExcelSubmitParam,
|
||||||
|
TSimCardLifecycleInputSubmitParam,
|
||||||
|
TSimCardLifecycleSubmitRes,
|
||||||
|
} from '../tSimCardLifecycle/type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export function DownloadTemplate() {
|
||||||
|
return requestClient.download('/iot/tSimCardDataStart/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExcelSubmit(
|
||||||
|
params: TSimCardLifecycleExcelSubmitParam,
|
||||||
|
): Promise<TSimCardLifecycleSubmitRes> {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/iot/tSimCardDataStart/excel/submit',
|
||||||
|
{
|
||||||
|
file: params.file,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputSubmit(
|
||||||
|
params: TSimCardLifecycleInputSubmitParam,
|
||||||
|
): Promise<TSimCardLifecycleSubmitRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardDataStart/input/submit', params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const download = vi.fn();
|
||||||
|
const post = vi.fn();
|
||||||
|
const upload = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('#/api/request', () => ({
|
||||||
|
requestClient: {
|
||||||
|
download,
|
||||||
|
post,
|
||||||
|
upload,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('tSimCardDataStop api', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
download.mockReset();
|
||||||
|
post.mockReset();
|
||||||
|
upload.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('downloads the data stop template from the backend contract path', async () => {
|
||||||
|
const { DownloadTemplate } = await import('./index');
|
||||||
|
|
||||||
|
DownloadTemplate();
|
||||||
|
|
||||||
|
expect(download).toHaveBeenCalledWith('/iot/tSimCardDataStop/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an excel data stop batch as file-only form data', async () => {
|
||||||
|
const { ExcelSubmit } = await import('./index');
|
||||||
|
const file = new File(['iccid'], 'dataStop.xlsx');
|
||||||
|
|
||||||
|
ExcelSubmit({ file });
|
||||||
|
|
||||||
|
expect(upload).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardDataStop/excel/submit',
|
||||||
|
{ file },
|
||||||
|
{ timeout: 60_000 },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits an input data stop batch from the backend contract path', async () => {
|
||||||
|
const { InputSubmit } = await import('./index');
|
||||||
|
const params = {
|
||||||
|
input: '8986000000000000001 14000000001 460001000000001',
|
||||||
|
};
|
||||||
|
|
||||||
|
InputSubmit(params);
|
||||||
|
|
||||||
|
expect(post).toHaveBeenCalledWith(
|
||||||
|
'/iot/tSimCardDataStop/input/submit',
|
||||||
|
params,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import type {
|
||||||
|
TSimCardLifecycleExcelSubmitParam,
|
||||||
|
TSimCardLifecycleInputSubmitParam,
|
||||||
|
TSimCardLifecycleSubmitRes,
|
||||||
|
} from '../tSimCardLifecycle/type';
|
||||||
|
|
||||||
|
import { requestClient } from '#/api/request';
|
||||||
|
|
||||||
|
export function DownloadTemplate() {
|
||||||
|
return requestClient.download('/iot/tSimCardDataStop/excel/template', {
|
||||||
|
responseReturn: 'raw',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExcelSubmit(
|
||||||
|
params: TSimCardLifecycleExcelSubmitParam,
|
||||||
|
): Promise<TSimCardLifecycleSubmitRes> {
|
||||||
|
return requestClient.upload(
|
||||||
|
'/iot/tSimCardDataStop/excel/submit',
|
||||||
|
{
|
||||||
|
file: params.file,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
timeout: 60_000,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InputSubmit(
|
||||||
|
params: TSimCardLifecycleInputSubmitParam,
|
||||||
|
): Promise<TSimCardLifecycleSubmitRes> {
|
||||||
|
return requestClient.post('/iot/tSimCardDataStop/input/submit', params);
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
const get = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('#/api/request', () => ({
|
||||||
|
requestClient: {
|
||||||
|
get,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('tSimCardInfo session api', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
get.mockReset();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queries session without showing a global failure message', async () => {
|
||||||
|
const { QuerySimSession } = await import('./index');
|
||||||
|
const params = { iccid: '8986000000000000001' };
|
||||||
|
|
||||||
|
QuerySimSession(params);
|
||||||
|
|
||||||
|
expect(get).toHaveBeenCalledWith('iot/tSimCardInfo/querySimSession', {
|
||||||
|
params,
|
||||||
|
errorMessageMode: 'none',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('queries status without showing a global failure message', async () => {
|
||||||
|
const { QuerySimStatus } = await import('./index');
|
||||||
|
const params = { iccid: '8986000000000000001' };
|
||||||
|
|
||||||
|
QuerySimStatus(params);
|
||||||
|
|
||||||
|
expect(get).toHaveBeenCalledWith('iot/tSimCardInfo/querySimStatus', {
|
||||||
|
params,
|
||||||
|
errorMessageMode: 'none',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user