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:
MiFaZhan
2026-10-26 13:46:34 +08:00
commit 3106d96ac7
1607 changed files with 185856 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
> 1%
last 2 versions
not dead
not ie 11
+1
View File
@@ -0,0 +1 @@
export { default } from '@vben/commitlint-config';
+7
View File
@@ -0,0 +1,7 @@
node_modules
.git
.gitignore
*.md
dist
.turbo
dist.zip
+18
View File
@@ -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
+6
View File
@@ -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.
+3
View File
@@ -0,0 +1,3 @@
# 每次 git pull 之后, 安装依赖
pnpm install
+7
View File
@@ -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.
+20
View File
@@ -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',
],
};
+1
View File
@@ -0,0 +1 @@
22.1.0
+13
View File
@@ -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
+18
View File
@@ -0,0 +1,18 @@
dist
dev-dist
.local
.output.js
node_modules
.nvmrc
coverage
CODEOWNERS
.nitro
.output
**/*.svg
**/*.sh
public
.npmrc
*-lock.yaml
+1
View File
@@ -0,0 +1 @@
export { default } from '@vben/prettier-config';
+4
View File
@@ -0,0 +1,4 @@
dist
public
__tests__
coverage
+9
View File
@@ -0,0 +1,9 @@
{
"cSpell.words": [
"iccid",
"imei",
"imsi",
"IOTLCM",
"msisdn"
]
}
+152
View File
@@ -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](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**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.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](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![Raise an issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) 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
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](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!
![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png)
<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)
+152
View File
@@ -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](https://img.shields.io/github/license/anncwb/vue-vben-admin.svg)](LICENSE)
<h1>Vue Vben Admin</h1>
</div>
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=vbenjs_vue-vben-admin&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=vbenjs_vue-vben-admin) ![codeql](https://github.com/vbenjs/vue-vben-admin/actions/workflows/codeql.yml/badge.svg) ![build](https://github.com/vbenjs/vue-vben-admin/actions/workflows/build.yml/badge.svg) ![ci](https://github.com/vbenjs/vue-vben-admin/actions/workflows/ci.yml/badge.svg) ![deploy](https://github.com/vbenjs/vue-vben-admin/actions/workflows/deploy.yml/badge.svg)
**中文** | [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 的免费在线开发环境)中打开项目,并立即开始编码.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](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)
## 如何贡献
非常欢迎你的加入![提一个 Issue](https://github.com/anncwb/vue-vben-admin/issues/new/choose) 或者提交一个 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
[![Star History Chart](https://api.star-history.com/svg?repos=vbenjs/vue-vben-admin&type=Date)](https://star-history.com/#vbenjs/vue-vben-admin&Date)
## 捐赠
如果你觉得这个项目对你有帮助,你可以帮作者买一杯咖啡表示支持!
![donate](https://unpkg.com/@vbenjs/static-source@0.1.7/source/sponsor.png)
<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
View File
@@ -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',
}, ];
+1
View File
@@ -0,0 +1 @@
backend-mock/nitro.config.ts
+9
View File
@@ -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
+7
View File
@@ -0,0 +1,7 @@
# public path
VITE_BASE=/
# Basic interface address SPA
VITE_GLOB_API_URL=/api
VITE_VISUALIZER=true
+19
View File
@@ -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
+22
View File
@@ -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
+48
View File
@@ -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>
+290
View File
@@ -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>
+60
View File
@@ -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,
};
}
+1
View File
@@ -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

+14
View File
@@ -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;
+68
View File
@@ -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';
+170
View File
@@ -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',
});
}
+185
View File
@@ -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');
}
+21
View File
@@ -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');
}
+14
View File
@@ -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';
+20
View File
@@ -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');
}
+57
View File
@@ -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;
}
+20
View File
@@ -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 });
}
+11
View File
@@ -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',
});
});
});
@@ -0,0 +1,71 @@
import { requestClient } from '#/api/request';
// 获取卡信息表列表
export function List(params: any) {
return requestClient.get<any>('iot/tSimCardInfo/list', { params });
}
// 删除/批量删除卡信息表
export function Delete(params: any) {
return requestClient.post<any>('iot/tSimCardInfo/delete', { ...params });
}
// 添加/编辑卡信息表
export function Edit(params: any) {
return requestClient.post<any>('iot/tSimCardInfo/edit', { ...params });
}
// 获取卡信息表指定详情
export function View(params: any) {
return requestClient.get<any>('iot/tSimCardInfo/view', { params });
}
// 导出卡信息表
export function Export(params: any) {
return requestClient.post<Blob>(
'/iot/tSimCardInfo/export',
{ ...params },
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
},
responseType: 'blob',
},
);
}
// 同步单张SIM卡用量
export function SyncSimCardFlow(params: any) {
return requestClient.post<any>('iot/tSimCardInfo/syncSimCardFlow', {
...params,
});
}
// 同步SIM卡状态
export function SyncSimCardStatus(params: any) {
return requestClient.post<any>('iot/tSimCardInfo/syncSimCardStatus', {
...params,
});
}
// // 查询SIM卡会话
// export function QuerySimSession(params: TSimCardInfoQuerySimSessionParam) {
// return requestClient.get<TSimCardInfoQuerySimSessionRes>(
// 'iot/tSimCardInfo/querySimSession',
// { params },
// );
// }
// 查询SIM卡会话
export function QuerySimSession(params: any) {
return requestClient.get<any>('iot/tSimCardInfo/querySimSession', {
params,
errorMessageMode: 'none',
});
}
// 查询SIM卡状态
export function QuerySimStatus(params: any) {
return requestClient.get<any>('iot/tSimCardInfo/querySimStatus', {
params,
errorMessageMode: 'none',
});
}
@@ -0,0 +1,395 @@
import type { GlobalListRes } from '#/types/global';
import type { MemberSumma } from '#/utils';
/** 卡信息表列表项 */
export interface TSimCardInfoModel {
/** 主键ID */
id: number;
/** ICCID */
iccid: string;
/** MSISDN/卡号 */
msisdn: string;
/** IMSI */
imsi: string;
/** IMEI */
imei: string;
/** 归属地 */
regionCode: string;
/** 渠道客户ID */
deptId: number;
/** 渠道客户名称 */
deptName: string;
/** 当前套餐编号 */
currentPackageCode: string;
/** 当前套餐名称 */
packageName: string;
/** 套餐状态:0 无套餐,1待生效,2生效中,3已过期 */
packageStatus: number;
/** 接入方式:ipv4、ipv6、ipv4_ipv6 */
accessType: number;
/** 运营商卡状态 枚举 */
bossCardStatus: number;
/** 卡类型 枚举 */
cardType: number;
/** 运营商开户日期 */
bossOpenAt: string;
/** 运营商激活日期 */
bossActivateAt: string;
/** 是否按运营商激活减去赠送时长方:1是,0否 */
isBossActivateEnabled: number;
/** 入库批次 */
inboundBatchNo: string;
/** 出库批次 */
outboundBatchNo: string;
/** 划拨批次 */
allocationBatchNo: string;
/** 是否为池卡:1是,0否 */
isFlowPool: number;
/** 流量池编号 */
flowPoolCode: string;
/** 当前套餐生效时间 */
flowPkgBeginAt: string;
/** 套餐赠送结束时间 */
flowPkgPresentAt: string;
/** 实际赠送结束时间 */
flowPkgRealPresentAt: string;
/** 当前套餐预计到期时间 */
flowPkgEndAt: string;
/** 数据服务状态:1启用、0禁用 */
dataServiceStatus: number;
/** 套餐总流量MB */
packageTotalFlowMb: number;
/** 已用流量MB */
usedFlowMb: number;
/** 达量断网配置状态(0未配置 1已开启 2已关闭) */
trafficCutoffStatus: number;
/** 达量断网阈值 */
trafficCutoffThreshold: number;
/** 短信服务状态:1启用、0禁用 */
smsServiceStatus: number;
/** 套餐短信总条数 */
packageTotalSms: number;
/** 已用短信条数 */
usedSms: number;
/** 语音服务状态:1启用、0禁用 */
voiceServiceStatus: number;
/** 套餐语音总分钟数 */
packageTotalVoice: number;
/** 已用语音分钟数 */
usedVoice: number;
/** 国漫服务状态:1启用、0禁用 */
roamServiceStatus: number;
/** 套餐国际流量MB */
packageTotalRoamFlowMb: number;
/** 已用国际流量MB */
usedRoamFlowMb: number;
/** 状态最后一次同步时间 */
statusSyncTime: string;
/** 流量最后一次同步时间 */
flowSyncTime: string;
/** 短信最后一次同步时间 */
smsSyncTime: string;
/** 语音最后一次同步时间 */
voiceSyncTime: string;
/** 国漫最后一次同步时间 */
roamSyncTime: string;
/** 是否收取附加费:1是,0否 */
extraFeeEnabled: number;
/** 附加费用倍数 */
extraFeeMultiple: number;
/** 附加金额 */
extraFee: number;
/** 通道编号 */
chanKey: string;
/** 通道名称 */
chanName: string;
/** 备注 */
remark: string;
/** 网络类型:4G、5G、NB-IoT */
netModel: number;
/** 租户ID */
tenantId: string;
/** 创建部门 */
createdDept: number;
/** 创建者 */
createdBy: number;
/** 创建者摘要信息 */
createdBySumma: MemberSumma;
/** 创建时间 */
createdAt: string;
/** 更新者 */
updatedBy: number;
/** 更新者摘要信息 */
updatedBySumma: MemberSumma;
/** 更新时间 */
updatedAt: string;
}
/** 获取卡信息表指定信息响应项 */
export interface TSimCardInfoViewModel {
/** 通道名称 */
chanName: string;
/** 部门名称 */
deptName: string;
/** 套餐名称 */
packageName: string;
/** 剩余流量MB */
reminFlowMb: number;
/** 主键ID */
id: number;
/** ICCID */
iccid: string;
/** MSISDN/卡号 */
msisdn: string;
/** IMSI */
imsi: string;
/** IMEI */
imei: string;
/** 归属地 */
regionCode: string;
/** 渠道客户ID */
deptId: number;
/** 当前套餐编号 */
currentPackageCode: string;
/** 套餐状态:0 无套餐,1待生效,2生效中,3已过期 */
packageStatus: number;
/** 接入方式:ipv4、ipv6、ipv4_ipv6 */
accessType: number;
/** 运营商卡状态 枚举 */
bossCardStatus: number;
/** 卡类型 枚举 */
cardType: number;
/** 运营商开户日期 */
bossOpenAt: string;
/** 运营商激活日期 */
bossActivateAt: string;
/** 是否按运营商激活减去赠送时长方:1是,0否 */
isBossActivateEnabled: number;
/** 入库批次 */
inboundBatchNo: string;
/** 出库批次 */
outboundBatchNo: string;
/** 划拨批次 */
allocationBatchNo: string;
/** 是否为池卡:1是,0否 */
isFlowPool: number;
/** 流量池编号 */
flowPoolCode: string;
/** 当前套餐生效时间 */
flowPkgBeginAt: string;
/** 套餐赠送结束时间 */
flowPkgPresentAt: string;
/** 实际赠送结束时间 */
flowPkgRealPresentAt: string;
/** 当前套餐预计到期时间 */
flowPkgEndAt: string;
/** 数据服务状态:1启用、0禁用 */
dataServiceStatus: number;
/** 套餐总流量MB */
packageTotalFlowMb: number;
/** 已用流量MB */
usedFlowMb: number;
/** 达量断网配置状态(0未配置 1已开启 2已关闭) */
trafficCutoffStatus: number;
/** 达量断网阈值 */
trafficCutoffThreshold: number;
/** 短信服务状态:1启用、0禁用 */
smsServiceStatus: number;
/** 套餐短信总条数 */
packageTotalSms: number;
/** 已用短信条数 */
usedSms: number;
/** 语音服务状态:1启用、0禁用 */
voiceServiceStatus: number;
/** 套餐语音总分钟数 */
packageTotalVoice: number;
/** 已用语音分钟数 */
usedVoice: number;
/** 国漫服务状态:1启用、0禁用 */
roamServiceStatus: number;
/** 套餐国际流量MB */
packageTotalRoamFlowMb: number;
/** 已用国际流量MB */
usedRoamFlowMb: number;
/** 状态最后一次同步时间 */
statusSyncTime: string;
/** 流量最后一次同步时间 */
flowSyncTime: string;
/** 短信最后一次同步时间 */
smsSyncTime: string;
/** 语音最后一次同步时间 */
voiceSyncTime: string;
/** 国漫最后一次同步时间 */
roamSyncTime: string;
/** 是否收取附加费:1是,0否 */
extraFeeEnabled: number;
/** 附加费用倍数 */
extraFeeMultiple: number;
/** 附加金额 */
extraFee: number;
/** 通道编号 */
chanKey: string;
/** 网络类型:4G、5G、NB-IoT */
netModel: number;
/** 备注 */
remark: string;
/** 租户ID */
tenantId: string;
/** 创建部门 */
createdDept: number;
/** 创建者 */
createdBy: number;
/** 创建时间 */
createdAt: string;
/** 更新者 */
updatedBy: number;
/** 更新时间 */
updatedAt: string;
/** 创建者摘要信息 */
createdBySumma: MemberSumma;
/** 更新者摘要信息 */
updatedBySumma: MemberSumma;
}
/** 获取卡信息表列表 请求参数 */
export interface TSimCardInfoListParam {
/** 页码 */
page?: number;
/** 每页大小 */
pageSize?: number;
/** 关键字 */
keyword?: string;
/** 运营商卡状态 */
bossCardStatus?: number;
/** 时间类型 */
timeType?: number;
/** 时间段 */
time?: string[];
/** 查询条件 */
condition?: string;
/** 起始号段 */
startNum?: string;
/** 结束号段 */
endNum?: string;
/** 维度 */
dimension?: number;
/** 维度状态 */
dimensionStatus?: string;
/** 维度值 */
dimensionValue?: string;
/** 排序值 */
sortValue?: number;
/** 排序类型 */
sortType?: number;
/** 通道编号 */
chanKey?: string;
/** 当前套餐编码 */
currentPackageCode?: string;
/** 卡号 */
cardValue?: string;
/** 备注 */
remark?: string;
/** IMEI */
imei?: string;
/** 渠道客户ID */
deptId?: number;
/** 主键ID */
id?: number;
/** ICCID */
iccid?: string;
/** MSISDN/卡号 */
msisdn?: string;
/** IMSI */
imsi?: string;
/** 套餐状态:0 无套餐,1待生效,2生效中,3已过期 */
packageStatus?: number;
/** 数据服务状态:1启用、0禁用 */
dataServiceStatus?: number;
/** 短信服务状态:1启用、0禁用 */
smsServiceStatus?: number;
/** 语音服务状态:1启用、0禁用 */
voiceServiceStatus?: number;
/** 国漫服务状态:1启用、0禁用 */
roamServiceStatus?: number;
/** 创建时间 */
createdAt?: string[];
}
/** 获取卡信息表列表 响应 */
export type TSimCardInfoListRes = GlobalListRes<TSimCardInfoModel>;
/** 导出卡信息表列表 请求参数 */
export type TSimCardInfoExportParam = TSimCardInfoListParam;
/** 修改/新增卡信息表 请求参数 */
export interface TSimCardInfoEditParam {
/** 主键ID */
id?: number;
/** 备注 */
remark?: string;
}
/** 获取卡信息表指定信息 请求参数 */
export interface TSimCardInfoViewParam {
/** 主键ID */
id?: number;
/** ICCID */
iccid?: string;
/** MSISDN/卡号 */
msisdn?: string;
}
/** 获取卡信息表指定信息 响应 */
export type TSimCardInfoViewRes = TSimCardInfoViewModel;
/** 删除卡信息表 请求参数 */
export interface TSimCardInfoDeleteParam {
/** 主键ID */
id: number | number[];
}
/** 查询SIM卡会话 请求参数 */
export interface TSimCardInfoQuerySimSessionParam {
/** ICCID */
iccid: string;
}
/** 查询SIM卡会话 响应项 */
export interface TSimCardInfoQuerySimSessionModel {
/** APN */
apnId: string;
/** 实时IMEI */
imei?: string;
/** 接入方式 */
rat: string;
/** IPv4地址 */
ipv4: string;
/** IPv6地址 */
ipv6: string;
/** IPv6前缀 */
ipv6Prefix: string;
/** 在线状态 */
status: string;
/** 会话创建时间 */
createTime: string;
}
/** 查询SIM卡会话 响应 */
export type TSimCardInfoQuerySimSessionRes =
null | TSimCardInfoQuerySimSessionModel;
/** 查询SIM卡状态 请求参数 */
export type TSimCardInfoQuerySimStatusParam =
TSimCardInfoQuerySimSessionParam;
/** 查询SIM卡状态 响应项 */
export interface TSimCardInfoQuerySimStatusModel {
/** 卡状态 */
cardStatus?: number;
/** 最后变更时间 */
lastChangeDate?: string;
}
/** 查询SIM卡状态 响应 */
export type TSimCardInfoQuerySimStatusRes =
null | TSimCardInfoQuerySimStatusModel;
@@ -0,0 +1,20 @@
export interface TSimCardLifecycleExcelSubmitParam {
/** Excel 文件 */
file: File;
}
export interface TSimCardLifecycleInputSubmitParam {
/** 每行填写 ICCID,或 ICCID MSISDN IMSI */
input: string;
}
export interface TSimCardLifecycleSubmitRes {
/** 批次编号 */
batchNo: string;
/** 批次类型 */
batchType: number;
/** 异步处理状态 */
batchAsyncStatus: number;
/** 提交总行数 */
total: number;
}
@@ -0,0 +1,88 @@
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('tSimCardRenew api', () => {
beforeEach(() => {
download.mockReset();
get.mockReset();
post.mockReset();
upload.mockReset();
});
it('downloads excel renew template from backend contract path', async () => {
const { DownloadTemplate } = await import('./index');
DownloadTemplate();
expect(download).toHaveBeenCalledWith('/iot/tSimCardRenew/excel/template', {
responseReturn: 'raw',
});
});
it('submits excel renew as file-only form data', async () => {
const { ExcelSubmit } = await import('./index');
const file = new File(['iccid'], 'renew.xlsx');
ExcelSubmit({ file });
expect(upload).toHaveBeenCalledWith(
'/iot/tSimCardRenew/excel/submit',
{ file },
{
timeout: 60_000,
},
);
});
it('prepares input renew with backend contract path', async () => {
const { InputPrepare } = await import('./index');
const params = {
input: '8986000000000000001\n8986000000000000002',
};
InputPrepare(params);
expect(post).toHaveBeenCalledWith('/iot/tSimCardRenew/input/prepare', {
...params,
});
});
it('confirms renew prepare batch with backend contract path', async () => {
const { BatchConfirm } = await import('./index');
const params = {
batchNo: 'RN202607060001',
durationMonths: 6,
offlineContractCompleted: true,
};
BatchConfirm(params);
expect(post).toHaveBeenCalledWith('/iot/tSimCardRenew/batch/confirm', {
...params,
});
});
it('polls renew prepare result with backend contract path', async () => {
const { PrepareResult } = await import('./index');
const batchNo = 'RN202607060001';
PrepareResult(batchNo);
expect(get).toHaveBeenCalledWith('/iot/tSimCardRenew/prepare/result', {
params: { batchNo },
});
});
});
@@ -0,0 +1,52 @@
import type {
TSimCardRenewBatchConfirmParam,
TSimCardRenewBatchConfirmRes,
TSimCardRenewBatchPrepareRes,
TSimCardRenewBatchPrepareResult,
TSimCardRenewExcelSubmitParam,
TSimCardRenewExcelSubmitRes,
TSimCardRenewInputPrepareParam,
} from './type';
import { requestClient } from '#/api/request';
export function DownloadTemplate() {
return requestClient.download('/iot/tSimCardRenew/excel/template', {
responseReturn: 'raw',
});
}
export function ExcelSubmit(
params: TSimCardRenewExcelSubmitParam,
): Promise<TSimCardRenewExcelSubmitRes> {
return requestClient.upload(
'/iot/tSimCardRenew/excel/submit',
{
file: params.file,
},
{
timeout: 60_000,
},
);
}
export function InputPrepare(params: TSimCardRenewInputPrepareParam) {
return requestClient.post<TSimCardRenewBatchPrepareRes>(
'/iot/tSimCardRenew/input/prepare',
{ ...params },
);
}
export function BatchConfirm(params: TSimCardRenewBatchConfirmParam) {
return requestClient.post<TSimCardRenewBatchConfirmRes>(
'/iot/tSimCardRenew/batch/confirm',
{ ...params },
);
}
export function PrepareResult(batchNo: string) {
return requestClient.get<TSimCardRenewBatchPrepareResult>(
'/iot/tSimCardRenew/prepare/result',
{ params: { batchNo } },
);
}
@@ -0,0 +1,127 @@
export interface TSimCardRenewInputPrepareParam {
input: string;
[property: string]: any;
}
export interface TSimCardRenewExcelSubmitParam {
file: File;
}
export interface TSimCardRenewExcelSubmitRes {
batchNo?: string;
batchType?: number;
resultDownloadId?: number;
resultFileOssId?: number;
status?: string;
[property: string]: any;
}
export interface TSimCardRenewBatchPrepareRes {
agentDefaultPriceId?: number;
batchNo: string;
batchType?: number;
customerAgentId?: number;
customerName?: string;
failCount?: number;
failedItems?: TSimCardRenewPrepareFailedItem[];
failedItemsTruncated?: boolean;
packageCode?: string;
packageName?: string;
billingMethod?: number;
renewDurationUnit?: 'month' | 'year' | string;
renewDurationUnitText?: string;
durationValue?: number;
durationYears?: number;
giftDurationMonths?: number;
priceSource?: string;
quantity?: number;
renewableDurationOptions?: number[];
renewBeginDate?: string;
renewBeginDateMax?: string;
renewBeginDateMin?: string;
renewUnitPrice?: number | string;
renewablePackageCodes?: string[];
renewablePackages?: TSimCardRenewPackageOption[];
resultDownloadId?: number;
resultFileOssId?: number;
status?: string;
targetPriceId?: number;
total?: number;
validCount?: number;
isTopDept?: boolean;
defaultPaymentMode?: 'balance' | 'offline' | string;
availableBalance?: number | string;
sameRenewBeginDate?: boolean;
[property: string]: any;
}
export interface TSimCardRenewPrepareFailedItem {
iccid?: string;
reason?: string;
}
export interface TSimCardRenewPackageOption {
packageCode?: string;
packageName?: string;
renewUnitPrice?: number | string;
renewableDurationOptions?: number[];
}
export interface TSimCardRenewBatchConfirmParam {
adminNote?: string;
batchNo: string;
packageCode?: string;
buyerNote?: string;
durationYears?: number;
durationMonths: number;
giftDurationMonths?: number;
offlineContractCompleted: boolean;
paymentMode?: 'balance' | 'offline' | string;
packageBeginTime?: string;
packageEndTime?: string;
renewDate?: string;
remark?: string;
[property: string]: any;
}
export interface TSimCardRenewBatchConfirmRes {
batchNo: string;
batchType?: number;
billingMethod?: number;
renewDurationUnit?: 'month' | 'year' | string;
renewDurationUnitText?: string;
durationYears?: number;
durationMonths?: number;
giftDurationMonths?: number;
orderSn?: string;
packageCode?: string;
packageName?: string;
paymentMode?: string;
payAmount?: number | string;
payType?: number;
availableBalance?: number | string;
balanceDetailSerialNo?: string;
packageBeginTime?: string;
packageEndTime?: string;
quantity?: number;
renewDate?: string;
renewUnitPrice?: number | string;
status?: string;
totalAmount?: number | string;
prepareChanged?: boolean;
priceChanged?: boolean;
message?: string;
[property: string]: any;
}
export type TSimCardRenewBatchAsyncStatus = number;
export interface TSimCardRenewBatchPrepareResult {
batchAsyncStatus: TSimCardRenewBatchAsyncStatus;
batchInfo?: TSimCardRenewBatchPrepareRes;
batchNo: string;
failReason?: string;
runResult?: number;
status: string;
[property: string]: any;
}

Some files were not shown because too many files have changed in this diff Show More