first commit
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
CI / CI OK (push) Has been cancelled
Deploy Website on push / Rerun on failure (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled
This commit is contained in:
4
.browserslistrc
Normal file
4
.browserslistrc
Normal file
@@ -0,0 +1,4 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
not ie 11
|
||||
5
.changeset/README.md
Normal file
5
.changeset/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Changesets
|
||||
|
||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
||||
|
||||
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
||||
18
.changeset/config.json
Normal file
18
.changeset/config.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
||||
"changelog": [
|
||||
"@changesets/changelog-github",
|
||||
{ "repo": "vbenjs/vue-vben-admin" }
|
||||
],
|
||||
"commit": false,
|
||||
"fixed": [["@vben-core/*", "@vben/*"]],
|
||||
"snapshot": {
|
||||
"prereleaseTemplate": "{tag}-{datetime}"
|
||||
},
|
||||
"privatePackages": { "version": true, "tag": true },
|
||||
"linked": [],
|
||||
"access": "public",
|
||||
"baseBranch": "main",
|
||||
"updateInternalDependencies": "patch",
|
||||
"ignore": []
|
||||
}
|
||||
1
.commitlintrc.js
Normal file
1
.commitlintrc.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/commitlint-config';
|
||||
329
.cursor/rules/vben.mdc
Normal file
329
.cursor/rules/vben.mdc
Normal file
@@ -0,0 +1,329 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# Vue Vben Admin 开发规范与模板
|
||||
|
||||
## 1. 目录结构规范
|
||||
```
|
||||
views/
|
||||
├── [module]/ # 模块目录
|
||||
│ ├── data.ts # 表单schema、表格列配置等
|
||||
│ ├── list.vue # 主列表页面
|
||||
│ └── modules/ # 子模块组件
|
||||
│ └── form.vue # 表单抽屉组件
|
||||
```
|
||||
|
||||
## 2. 代码模板
|
||||
|
||||
### 2.1 列表页面模板 (list.vue)
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import type { Recordable } from '@vben/types';
|
||||
import type { OnActionClickParams, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { [Module]Api } from '#/api';
|
||||
|
||||
import { Page, useVbenDrawer } from '@vben/common-ui';
|
||||
import { Plus } from '@vben/icons';
|
||||
import { Button, message, Modal } from 'ant-design-vue';
|
||||
|
||||
import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { delete[Module], get[Module]List, update[Module] } from '#/api';
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useColumns, useGridFormSchema } from './data';
|
||||
import Form from './modules/form.vue';
|
||||
|
||||
// 表单抽屉
|
||||
const [FormDrawer, formDrawerApi] = useVbenDrawer({
|
||||
connectedComponent: Form,
|
||||
destroyOnClose: true,
|
||||
});
|
||||
|
||||
// 表格配置
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions: {
|
||||
fieldMappingTime: [['create_time', ['startTime', 'endTime']]],
|
||||
schema: useGridFormSchema(),
|
||||
submitOnChange: true,
|
||||
},
|
||||
gridOptions: {
|
||||
columns: useColumns(onActionClick, onStatusChange),
|
||||
height: 'auto',
|
||||
keepSource: true,
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await get[Module]List({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
rowConfig: {
|
||||
keyField: 'id',
|
||||
},
|
||||
toolbarConfig: {
|
||||
custom: true,
|
||||
export: false,
|
||||
refresh: { code: 'query' },
|
||||
search: true,
|
||||
zoom: true,
|
||||
},
|
||||
} as VxeTableGridOptions<[Module]Api.[Module]Item>,
|
||||
});
|
||||
|
||||
// 操作处理函数
|
||||
function onActionClick(e: OnActionClickParams<[Module]Api.[Module]Item>) {
|
||||
switch (e.code) {
|
||||
case 'delete': onDelete(e.row); break;
|
||||
case 'edit': onEdit(e.row); break;
|
||||
}
|
||||
}
|
||||
|
||||
// 状态变更处理
|
||||
async function onStatusChange(newStatus: number, row: [Module]Api.[Module]Item) {
|
||||
const status: Recordable<string> = {
|
||||
0: '禁用',
|
||||
1: '启用',
|
||||
};
|
||||
try {
|
||||
await confirm(
|
||||
`你要将${row.name}的状态切换为 【${status[newStatus.toString()]}】 吗?`,
|
||||
`切换状态`,
|
||||
);
|
||||
await update[Module](mdc:row.id, { status: newStatus });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑处理
|
||||
function onEdit(row: [Module]Api.[Module]Item) {
|
||||
formDrawerApi.setData(row).open();
|
||||
}
|
||||
|
||||
// 删除处理
|
||||
function onDelete(row: [Module]Api.[Module]Item) {
|
||||
const hideLoading = message.loading({
|
||||
content: $t('ui.actionMessage.deleting', [row.name]),
|
||||
duration: 0,
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
delete[Module](mdc:row.id)
|
||||
.then(() => {
|
||||
message.success({
|
||||
content: $t('ui.actionMessage.deleteSuccess', [row.name]),
|
||||
key: 'action_process_msg',
|
||||
});
|
||||
onRefresh();
|
||||
})
|
||||
.catch(() => {
|
||||
hideLoading();
|
||||
});
|
||||
}
|
||||
|
||||
// 刷新处理
|
||||
function onRefresh() {
|
||||
gridApi.query();
|
||||
}
|
||||
|
||||
// 创建处理
|
||||
function onCreate() {
|
||||
formDrawerApi.setData({}).open();
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height>
|
||||
<FormDrawer @success="onRefresh" />
|
||||
<Grid :table-title="$t('module.list')">
|
||||
<template #toolbar-tools>
|
||||
<Button type="primary" @click="onCreate">
|
||||
<Plus class="size-5" />
|
||||
{{ $t('ui.actionTitle.create', [$t('module.name')]) }}
|
||||
</Button>
|
||||
</template>
|
||||
</Grid>
|
||||
</Page>
|
||||
</template>
|
||||
```
|
||||
|
||||
### 2.2 数据配置模板 (data.ts)
|
||||
```typescript
|
||||
import type { VbenFormSchema } from '#/adapter/form';
|
||||
import type { OnActionClickFn, VxeTableGridOptions } from '#/adapter/vxe-table';
|
||||
import type { [Module]Api } from '#/api';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
// 表单配置
|
||||
export function useFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'field_name',
|
||||
label: $t('module.field.label'),
|
||||
rules: 'required',
|
||||
},
|
||||
// ... 其他表单项
|
||||
];
|
||||
}
|
||||
|
||||
// 搜索表单配置
|
||||
export function useGridFormSchema(): VbenFormSchema[] {
|
||||
return [
|
||||
{
|
||||
component: 'Input',
|
||||
fieldName: 'search_field',
|
||||
label: $t('module.search.label'),
|
||||
},
|
||||
// ... 其他搜索项
|
||||
];
|
||||
}
|
||||
|
||||
// 表格列配置
|
||||
export function useColumns<T = [Module]Api.[Module]Item>(
|
||||
onActionClick: OnActionClickFn<T>,
|
||||
onStatusChange?: (newStatus: any, row: T) => PromiseLike<boolean | undefined>,
|
||||
): VxeTableGridOptions['columns'] {
|
||||
return [
|
||||
{
|
||||
field: 'field_name',
|
||||
title: $t('module.field.label'),
|
||||
width: 200,
|
||||
},
|
||||
// ... 其他列
|
||||
{
|
||||
align: 'center',
|
||||
cellRender: {
|
||||
attrs: {
|
||||
nameField: 'name_field',
|
||||
nameTitle: $t('module.name.label'),
|
||||
onClick: onActionClick,
|
||||
},
|
||||
name: 'CellOperation',
|
||||
},
|
||||
field: 'operation',
|
||||
fixed: 'right',
|
||||
title: $t('module.operation'),
|
||||
width: 130,
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 表单组件模板 (form.vue)
|
||||
```vue
|
||||
<script lang="ts" setup>
|
||||
import type { [Module]Api } from '#/api';
|
||||
import { useVbenForm } from '#/adapter/form';
|
||||
import { create[Module], update[Module] } from '#/api';
|
||||
import { $t } from '#/locales';
|
||||
import { useFormSchema } from '../data';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'success'): void;
|
||||
}>();
|
||||
|
||||
const [Form, formApi] = useVbenForm({
|
||||
schema: useFormSchema(),
|
||||
submitButtonText: $t('ui.actionTitle.submit'),
|
||||
submit: async (values) => {
|
||||
if (values.id) {
|
||||
await update[Module](mdc:values.id, values);
|
||||
} else {
|
||||
await create[Module](mdc:values);
|
||||
}
|
||||
emit('success');
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Form />
|
||||
</template>
|
||||
```
|
||||
|
||||
### 2.4 API 接口定义模板
|
||||
```typescript
|
||||
import type { BaseApi } from '#/api';
|
||||
|
||||
export namespace [Module]Api {
|
||||
export interface [Module]Item {
|
||||
id: number;
|
||||
// ... 其他字段
|
||||
}
|
||||
|
||||
export interface [Module]ListParams extends BaseApi.ListParams {
|
||||
// ... 其他查询参数
|
||||
}
|
||||
|
||||
export interface [Module]ListResult extends BaseApi.ListResult<[Module]Item> {}
|
||||
}
|
||||
|
||||
export function get[Module]List(params: [Module]Api.[Module]ListParams) {
|
||||
return request.get<[Module]Api.[Module]ListResult>('/api/[module]/list', { params });
|
||||
}
|
||||
|
||||
export function create[Module](mdc:data: Omit<[Module]Api.[Module]Item, 'id'>) {
|
||||
return request.post<[Module]Api.[Module]Item>('/api/[module]/create', data);
|
||||
}
|
||||
|
||||
export function update[Module](mdc:id: number, data: Partial<[Module]Api.[Module]Item>) {
|
||||
return request.put<[Module]Api.[Module]Item>(`/api/[module]/update/${id}`, data);
|
||||
}
|
||||
|
||||
export function delete[Module](mdc:id: number) {
|
||||
return request.delete(`/api/[module]/delete/${id}`);
|
||||
}
|
||||
```
|
||||
|
||||
### 2.5 国际化文案模板
|
||||
```typescript
|
||||
export default {
|
||||
module: {
|
||||
name: '[模块名称]',
|
||||
list: '[模块名称]列表',
|
||||
field: {
|
||||
label: '[字段标签]',
|
||||
},
|
||||
search: {
|
||||
label: '[搜索标签]',
|
||||
},
|
||||
operation: '操作',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## 3. 使用说明
|
||||
|
||||
1. 创建新模块时,按照目录结构规范创建相应的文件
|
||||
2. 复制对应的模板代码到相应文件中
|
||||
3. 替换所有 `[Module]` 为你的模块名称(如 `Product`)
|
||||
4. 根据实际需求修改:
|
||||
- 表单字段配置
|
||||
- 搜索表单配置
|
||||
- 表格列配置
|
||||
- API 接口定义
|
||||
- 国际化文案
|
||||
|
||||
## 4. 注意事项
|
||||
|
||||
1. 确保 API 接口已定义并正确实现
|
||||
2. 确保国际化文案已配置
|
||||
3. 根据实际需求调整表单和表格配置
|
||||
4. 注意处理特殊字段(如时间、状态等)
|
||||
5. 保持代码风格统一
|
||||
6. 遵循项目的命名规范
|
||||
|
||||
## 5. 参考示例
|
||||
|
||||
可以参考以下现有模块的实现:
|
||||
- @system/role
|
||||
- @system/user
|
||||
- @order
|
||||
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
.git
|
||||
.gitignore
|
||||
*.md
|
||||
dist
|
||||
.turbo
|
||||
dist.zip
|
||||
18
.editorconfig
Normal file
18
.editorconfig
Normal 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
|
||||
11
.gitattributes
vendored
Normal file
11
.gitattributes
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
|
||||
|
||||
# Automatically normalize line endings (to LF) for all text-based files.
|
||||
* text=auto eol=lf
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.{cmd,[cC][mM][dD]} text eol=crlf
|
||||
*.{bat,[bB][aA][tT]} text eol=crlf
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary
|
||||
2
.gitconfig
Normal file
2
.gitconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
[core]
|
||||
ignorecase = false
|
||||
14
.github/CODEOWNERS
vendored
Normal file
14
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# default onwer
|
||||
* anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
|
||||
# vben core onwer
|
||||
/.github/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
/.vscode/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
/packages/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
/packages/@core/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
/internal/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
/scripts/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com jinmao88@qq.com
|
||||
|
||||
# vben team onwer
|
||||
apps/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com @vbenjs/team-v5 jinmao88@qq.com
|
||||
docs/ anncwb@126.com vince292007@gmail.com netfan@foxmail.com @vbenjs/team-v5 jinmao88@qq.com
|
||||
74
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
74
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
name: 🐞 Bug Report
|
||||
description: Report an issue with Vben Admin to help us make it better.
|
||||
title: 'Bug: '
|
||||
labels: ['bug: pending triage']
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- Vben Admin V5
|
||||
- Vben Admin V2
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: bug-desc
|
||||
attributes:
|
||||
label: Describe the bug?
|
||||
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||
placeholder: Bug Description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Please provide a link to [StackBlitz](https://stackblitz.com/fork/github/vitest-dev/vitest/tree/main/examples/basic?initialPath=__vitest__/) (you can also use [examples](https://github.com/vitest-dev/vitest/tree/main/examples)) or a github repo that can reproduce the problem you ran into. A [minimal reproduction](https://stackoverflow.com/help/minimal-reproducible-example) is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "needs reproduction" label. If no reproduction is provided after 3 days, it will be auto-closed.
|
||||
placeholder: Reproduction
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: system-info
|
||||
attributes:
|
||||
label: System Info
|
||||
description: Output of `npx envinfo --system --npmPackages '{vue}' --binaries --browsers`
|
||||
render: shell
|
||||
placeholder: System, Binaries, Browsers
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
render: shell
|
||||
|
||||
- type: checkboxes
|
||||
id: terms
|
||||
attributes:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
# description: By submitting this issue, you agree to follow our [Code of Conduct](https://example.com).
|
||||
options:
|
||||
- label: Read the [docs](https://doc.vben.pro/)
|
||||
required: true
|
||||
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||
required: true
|
||||
- label: I have searched the [existing issues](https://github.com/vbenjs/vue-vben-admin/issues) and checked that my issue does not duplicate any existing issues.
|
||||
required: true
|
||||
- label: Check that this is a concrete bug. For Q&A open a [GitHub Discussion](https://github.com/vbenjs/vue-vben-admin/discussions) or join our [Discord Chat Server](https://discord.gg/8GuAdwDhj6).
|
||||
required: true
|
||||
- label: The provided reproduction is a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) of the bug.
|
||||
required: true
|
||||
38
.github/ISSUE_TEMPLATE/docs.yml
vendored
Normal file
38
.github/ISSUE_TEMPLATE/docs.yml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
name: 📚 Documentation
|
||||
description: Report an issue with Vben Admin Website to help us make it better.
|
||||
title: 'Docs: '
|
||||
labels: [documentation]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this issue!
|
||||
- type: checkboxes
|
||||
id: documentation_is
|
||||
attributes:
|
||||
label: Documentation is
|
||||
options:
|
||||
- label: Missing
|
||||
- label: Outdated
|
||||
- label: Confusing
|
||||
- label: Not sure?
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Explain in Detail
|
||||
description: A clear and concise description of your suggestion. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||
placeholder: The description of ... page is not clear. I thought it meant ... but it wasn't.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: suggestion
|
||||
attributes:
|
||||
label: Your Suggestion for Changes
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction-steps
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Please provide any reproduction steps that may need to be described. E.g. if it happens only when running the dev or build script make sure it's clear which one to use.
|
||||
placeholder: Run `pnpm install` followed by `pnpm run docs:dev`
|
||||
70
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
70
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: ✨ New Feature Proposal
|
||||
description: Propose a new feature to be added to Vben Admin
|
||||
title: 'FEATURE: '
|
||||
labels: ['enhancement: pending triage']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for suggesting a feature for our project! Please fill out the information below to help us understand and implement your request!
|
||||
- type: dropdown
|
||||
id: version
|
||||
attributes:
|
||||
label: Version
|
||||
description: What version of our software are you running?
|
||||
options:
|
||||
- Vben Admin V5
|
||||
- Vben Admin V2
|
||||
default: 0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: A detailed description of the feature request.
|
||||
placeholder: Please describe the feature you would like to see, and why it would be useful.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: proposed-solution
|
||||
attributes:
|
||||
label: Proposed Solution
|
||||
description: A clear and concise description of what you want to happen.
|
||||
placeholder: Describe the solution you'd like to see
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternatives Considered
|
||||
description: |
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
placeholder: Describe any alternative solutions or features you've considered
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: additional-context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
placeholder: Any additional information
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: checkboxes
|
||||
attributes:
|
||||
label: Validations
|
||||
description: Before submitting the issue, please make sure you do the following
|
||||
options:
|
||||
- label: Read the [docs](https://doc.vben.pro/)
|
||||
required: true
|
||||
- label: Ensure the code is up to date. (Some issues have been fixed in the latest version)
|
||||
required: true
|
||||
- label: I have searched the [existing issues](https://github.com/vbenjs/vue-vben-admin/issues) and checked that my issue does not duplicate any existing issues.
|
||||
required: true
|
||||
40
.github/actions/setup-node/action.yml
vendored
Normal file
40
.github/actions/setup-node/action.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
name: 'Setup Node'
|
||||
|
||||
description: 'Setup node and pnpm'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: .node-version
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Get pnpm store directory
|
||||
shell: bash
|
||||
run: |
|
||||
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Setup pnpm cache
|
||||
if: ${{ github.ref_name == 'main' }}
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- uses: actions/cache/restore@v4
|
||||
if: ${{ github.ref_name != 'main' }}
|
||||
with:
|
||||
path: ${{ env.STORE_PATH }}
|
||||
key: |
|
||||
${{ runner.os }}-pnpm-store-
|
||||
|
||||
- name: Install dependencies
|
||||
shell: bash
|
||||
run: pnpm install --frozen-lockfile
|
||||
89
.github/commit-convention.md
vendored
Normal file
89
.github/commit-convention.md
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
## Git Commit Message Convention
|
||||
|
||||
> This is adapted from [Angular's commit convention](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-angular).
|
||||
|
||||
#### TL;DR:
|
||||
|
||||
Messages must be matched by the following regex:
|
||||
|
||||
```js
|
||||
/^(revert: )?(feat|fix|docs|style|refactor|perf|test|workflow|build|ci|chore|types|wip): .{1,50}/;
|
||||
```
|
||||
|
||||
#### Examples
|
||||
|
||||
Appears under "Features" header, `dev` subheader:
|
||||
|
||||
```
|
||||
feat(dev): add 'comments' option
|
||||
```
|
||||
|
||||
Appears under "Bug Fixes" header, `dev` subheader, with a link to issue #28:
|
||||
|
||||
```
|
||||
fix(dev): fix dev error
|
||||
|
||||
close #28
|
||||
```
|
||||
|
||||
Appears under "Performance Improvements" header, and under "Breaking Changes" with the breaking change explanation:
|
||||
|
||||
```
|
||||
perf(build): remove 'foo' option
|
||||
|
||||
BREAKING CHANGE: The 'foo' option has been removed.
|
||||
```
|
||||
|
||||
The following commit and commit `667ecc1` do not appear in the changelog if they are under the same release. If not, the revert commit appears under the "Reverts" header.
|
||||
|
||||
```
|
||||
revert: feat(compiler): add 'comments' option
|
||||
|
||||
This reverts commit 667ecc1654a317a13331b17617d973392f415f02.
|
||||
```
|
||||
|
||||
### Full Message Format
|
||||
|
||||
A commit message consists of a **header**, **body** and **footer**. The header has a **type**, **scope** and **subject**:
|
||||
|
||||
```
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The **header** is mandatory and the **scope** of the header is optional.
|
||||
|
||||
### Revert
|
||||
|
||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||
|
||||
### Type
|
||||
|
||||
If the prefix is `feat`, `fix` or `perf`, it will appear in the changelog. However, if there is any [BREAKING CHANGE](#footer), the commit will always appear in the changelog.
|
||||
|
||||
Other prefixes are up to your discretion. Suggested prefixes are `docs`, `chore`, `style`, `refactor`, and `test` for non-changelog related tasks.
|
||||
|
||||
### Scope
|
||||
|
||||
The scope could be anything specifying the place of the commit change. For example `dev`, `build`, `workflow`, `cli` etc...
|
||||
|
||||
### Subject
|
||||
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
- use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- don't capitalize the first letter
|
||||
- no dot (.) at the end
|
||||
|
||||
### Body
|
||||
|
||||
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Footer
|
||||
|
||||
The footer should contain any information about **Breaking Changes** and is also the place to reference GitHub issues that this commit **Closes**.
|
||||
|
||||
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
|
||||
39
.github/config.yml
vendored
Normal file
39
.github/config.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Prevent issues being created without using the template
|
||||
blank_issues_enabled: false
|
||||
checkIssueTemplate: true
|
||||
checkPullRequestTemplate: true
|
||||
|
||||
contact_links:
|
||||
- name: 💬 Discord Chat
|
||||
url: https://discord.gg/8GuAdwDhj6
|
||||
about: Ask questions and discuss with other Vben users in real time.
|
||||
|
||||
- name: ❓ Questions & Discussions
|
||||
url: https://github.com/@vbenjs/vue-vben-admin/discussions
|
||||
about: Use GitHub discussions for message-board style questions and discussions.
|
||||
|
||||
# Comment to be posted to on PRs from first time contributors in your repository
|
||||
newPRWelcomeComment: |
|
||||
💖 Thanks for opening this pull request! 💖
|
||||
Please be patient and we will get back to you as soon as we can.
|
||||
|
||||
# Comment to be posted to on pull requests merged by a first time user
|
||||
firstPRMergeComment: >
|
||||
Thanks for your contribution! 🎉🎉🎉
|
||||
|
||||
|
||||
# Comment to be posted to on first time issues
|
||||
newIssueWelcomeComment: >
|
||||
Thanks for opening your first issue! Be sure to follow the issue template and provide every bit of information to help the developers!
|
||||
|
||||
|
||||
# *OPTIONAL* default titles to check against for lack of descriptiveness
|
||||
# MUST BE ALL LOWERCASE
|
||||
requestInfoDefaultTitles:
|
||||
- update readme.md
|
||||
- updates
|
||||
|
||||
# *Required* Comment to reply with
|
||||
requestInfoReplyComment: >
|
||||
Thanks for filing this issue/PR! It would be much appreciated if you could provide us with more information so we can effectively analyze the situation in context.
|
||||
|
||||
42
.github/contributing.md
vendored
Normal file
42
.github/contributing.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# Vben Admin Contributing Guide
|
||||
|
||||
Hi! We're really excited that you are interested in contributing to Vben Admin. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
|
||||
|
||||
- [Pull Request Guidelines](#pull-request-guidelines)
|
||||
|
||||
## Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of the level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||
|
||||
## Pull Request Guidelines
|
||||
|
||||
- Checkout a topic branch from the relevant branch, e.g. main, and merge back against that branch.
|
||||
|
||||
- If adding a new feature:
|
||||
|
||||
- Provide a convincing reason to add this feature. Ideally, you should open a suggestion issue first and have it approved before working on it.
|
||||
|
||||
- If fixing bug:
|
||||
|
||||
- Provide a detailed description of the bug in the PR. Live demo preferred.
|
||||
|
||||
- It's OK to have multiple small commits as you work on the PR - GitHub can automatically squash them before merging.
|
||||
|
||||
## Development Setup
|
||||
|
||||
You will need [pnpm](https://pnpm.io/)
|
||||
|
||||
After cloning the repo, run:
|
||||
|
||||
```bash
|
||||
# install the dependencies of the project
|
||||
$ pnpm install
|
||||
# start the project
|
||||
$ pnpm run dev
|
||||
```
|
||||
17
.github/dependabot.yml
vendored
Normal file
17
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: daily
|
||||
groups:
|
||||
non-breaking-changes:
|
||||
update-types: [minor, patch]
|
||||
|
||||
- package-ecosystem: github-actions
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
non-breaking-changes:
|
||||
update-types: [minor, patch]
|
||||
33
.github/pull_request_template.md
vendored
Normal file
33
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
## Description
|
||||
|
||||
<!-- Please describe the change as necessary. If it's a feature or enhancement please be as detailed as possible. If it's a bug fix, please link the issue that it fixes or describe the bug in as much detail.
|
||||
|
||||
-->
|
||||
|
||||
<!-- You can also add additional context here -->
|
||||
|
||||
## Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] This change requires a documentation update
|
||||
- [ ] Please, don't make changes to `pnpm-lock.yaml` unless you introduce a new test example.
|
||||
|
||||
## Checklist
|
||||
|
||||
> ℹ️ Check all checkboxes - this will indicate that you have done everything in accordance with the rules in [CONTRIBUTING](contributing.md).
|
||||
|
||||
- [ ] If you introduce new functionality, document it. You can run documentation with `pnpm run docs:dev` command.
|
||||
- [ ] Run the tests with `pnpm test`.
|
||||
- [ ] Changes in changelog are generated from PR name. Please, make sure that it explains your changes in an understandable manner. Please, prefix changeset messages with `feat:`, `fix:`, `perf:`, `docs:`, or `chore:`.
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] I have performed a self-review of my own code
|
||||
- [ ] I have commented my code, particularly in hard-to-understand areas
|
||||
- [ ] I have made corresponding changes to the documentation
|
||||
- [ ] My changes generate no new warnings
|
||||
- [ ] I have added tests that prove my fix is effective or that my feature works
|
||||
- [ ] New and existing unit tests pass locally with my changes
|
||||
- [ ] Any dependent changes have been merged and published in downstream modules
|
||||
61
.github/release-drafter.yml
vendored
Normal file
61
.github/release-drafter.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name-template: 'v$RESOLVED_VERSION'
|
||||
tag-template: 'v$RESOLVED_VERSION'
|
||||
version-template: $MAJOR.$MINOR.$PATCH
|
||||
change-template: '* $TITLE (#$NUMBER) @$AUTHOR'
|
||||
template: |
|
||||
# What's Changed
|
||||
|
||||
$CHANGES
|
||||
|
||||
**Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION
|
||||
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
labels:
|
||||
- 'feature'
|
||||
- title: '🐞 Bug Fixes'
|
||||
labels:
|
||||
- 'bug'
|
||||
- title: '📈 Performance & Enhancement'
|
||||
labels:
|
||||
- 'perf'
|
||||
- 'enhancement'
|
||||
- title: 📝 Documentation
|
||||
labels:
|
||||
- 'documentation'
|
||||
- title: 👻 Maintenance
|
||||
labels:
|
||||
- 'chore'
|
||||
- 'dependencies'
|
||||
# collapse-after: 12
|
||||
- title: 🚦 Tests
|
||||
labels:
|
||||
- 'tests'
|
||||
- title: 'Breaking'
|
||||
label: 'breaking'
|
||||
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- 'major'
|
||||
- 'breaking'
|
||||
minor:
|
||||
labels:
|
||||
- 'minor'
|
||||
patch:
|
||||
labels:
|
||||
- 'feature'
|
||||
- 'patch'
|
||||
- 'bug'
|
||||
- 'maintenance'
|
||||
- 'docs'
|
||||
- 'dependencies'
|
||||
- 'security'
|
||||
|
||||
exclude-labels:
|
||||
- 'skip-changelog'
|
||||
- 'no-changelog'
|
||||
- 'changelog'
|
||||
- 'bump versions'
|
||||
- 'reverted'
|
||||
- 'invalid'
|
||||
13
.github/semantic.yml
vendored
Normal file
13
.github/semantic.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
titleAndCommits: true
|
||||
types:
|
||||
- feat
|
||||
- fix
|
||||
- docs
|
||||
- chore
|
||||
- style
|
||||
- refactor
|
||||
- perf
|
||||
- test
|
||||
- build
|
||||
- ci
|
||||
- revert
|
||||
48
.github/workflows/build.yml
vendored
Normal file
48
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
# name: Dependabot post-update
|
||||
name: Build detection
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
HUSKY: '0'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
post-update:
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
# if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# - macos-latest
|
||||
- windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout out pull request
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh pr checkout ${{ github.event.pull_request.number }}
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
pnpm run build
|
||||
42
.github/workflows/changeset-version.yml
vendored
Normal file
42
.github/workflows/changeset-version.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
# https://github.com/changesets/action
|
||||
name: Changeset version
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types:
|
||||
- closed
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
env:
|
||||
CI: true
|
||||
|
||||
jobs:
|
||||
version:
|
||||
if: (github.event.pull_request.merged || github.event_name == 'workflow_dispatch') && github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||
# if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Create Release Pull Request
|
||||
uses: changesets/action@v1
|
||||
with:
|
||||
version: pnpm run version
|
||||
commit: 'chore: bump versions'
|
||||
title: 'chore: bump versions'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
125
.github/workflows/ci.yml
vendored
Normal file
125
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
CI: true
|
||||
TZ: Asia/Shanghai
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# - macos-latest
|
||||
- windows-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
# - name: Check Git version
|
||||
# run: git --version
|
||||
|
||||
# - name: Setup mock Git user
|
||||
# run: git config --global user.email "you@example.com" && git config --global user.name "Your Name"
|
||||
|
||||
- name: Vitest tests
|
||||
run: pnpm run test:unit
|
||||
|
||||
# - name: Upload coverage
|
||||
# uses: codecov/codecov-action@v4
|
||||
# with:
|
||||
# token: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
lint:
|
||||
name: Lint
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# - macos-latest
|
||||
- windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
|
||||
check:
|
||||
name: Check
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-latest
|
||||
# - macos-latest
|
||||
- windows-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm check:type
|
||||
|
||||
# From https://github.com/rhysd/actionlint/blob/main/docs/usage.md#use-actionlint-on-github-actions
|
||||
- name: Check workflow files
|
||||
if: runner.os == 'Linux'
|
||||
run: |
|
||||
bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
||||
./actionlint -color -shellcheck=""
|
||||
|
||||
ci-ok:
|
||||
name: CI OK
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test, check, lint]
|
||||
env:
|
||||
FAILURE: ${{ contains(join(needs.*.result, ','), 'failure') }}
|
||||
steps:
|
||||
- name: Check for failure
|
||||
run: |
|
||||
echo $FAILURE
|
||||
if [ "$FAILURE" = "false" ]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
94
.github/workflows/codeql.yml
vendored
Normal file
94
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
pull_request:
|
||||
branches: ['main']
|
||||
schedule:
|
||||
- cron: '35 0 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
# Runner size impacts CodeQL analysis time. To learn more, please see:
|
||||
# - https://gh.io/recommended-hardware-resources-for-running-codeql
|
||||
# - https://gh.io/supported-runners-and-hardware-resources
|
||||
# - https://gh.io/using-larger-runners (GitHub.com only)
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: javascript-typescript
|
||||
build-mode: none
|
||||
# CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift'
|
||||
# Use `c-cpp` to analyze code written in C, C++ or both
|
||||
# Use 'java-kotlin' to analyze code written in Java, Kotlin or both
|
||||
# Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
|
||||
# To learn more about changing the languages that are analyzed or customizing the build mode for your analysis,
|
||||
# see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning.
|
||||
# If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how
|
||||
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# If the analyze step fails for one of the languages you are analyzing with
|
||||
# "We were unable to automatically build your code", modify the matrix above
|
||||
# to set the build mode to "manual" for that language. Then modify this step
|
||||
# to build your code.
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: |
|
||||
echo 'If you are using a "manual" build mode for one or more of the' \
|
||||
'languages you are analyzing, replace this with the commands to build' \
|
||||
'your code, for example:'
|
||||
echo ' make bootstrap'
|
||||
echo ' make release'
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
||||
172
.github/workflows/deploy.yml
vendored
Normal file
172
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
name: Deploy Website on push
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
deploy-playground-ftp:
|
||||
name: Deploy Push Playground Ftp
|
||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sed Config Base
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./playground/.env.production
|
||||
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./playground/.env.production
|
||||
cat ./playground/.env.production
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Build
|
||||
run: pnpm build:play
|
||||
|
||||
- name: Sync Playground files
|
||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||
with:
|
||||
server: ${{ secrets.PRO_FTP_HOST }}
|
||||
username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }}
|
||||
password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }}
|
||||
local-dir: ./playground/dist/
|
||||
|
||||
deploy-docs-ftp:
|
||||
name: Deploy Push Docs Ftp
|
||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Build
|
||||
run: pnpm build:docs
|
||||
|
||||
- name: Sync Docs files
|
||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||
with:
|
||||
server: ${{ secrets.PRO_FTP_HOST }}
|
||||
username: ${{ secrets.WEBSITE_FTP_ACCOUNT }}
|
||||
password: ${{ secrets.WEBSITE_FTP_PASSWORD }}
|
||||
local-dir: ./docs/.vitepress/dist/
|
||||
|
||||
deploy-antd-ftp:
|
||||
name: Deploy Push Antd Ftp
|
||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sed Config Base
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/bdrp-admin-web/.env.production
|
||||
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/bdrp-admin-web/.env.production
|
||||
cat ./apps/bdrp-admin-web/.env.production
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build:web
|
||||
|
||||
- name: Sync files
|
||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||
with:
|
||||
server: ${{ secrets.PRO_FTP_HOST }}
|
||||
username: ${{ secrets.WEB_ANTD_FTP_ACCOUNT }}
|
||||
password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }}
|
||||
local-dir: ./apps/bdrp-admin-web/dist/
|
||||
|
||||
deploy-ele-ftp:
|
||||
name: Deploy Push Element Ftp
|
||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sed Config Base
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-ele/.env.production
|
||||
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-ele/.env.production
|
||||
cat ./apps/web-ele/.env.production
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build:ele
|
||||
|
||||
- name: Sync files
|
||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||
with:
|
||||
server: ${{ secrets.PRO_FTP_HOST }}
|
||||
username: ${{ secrets.WEB_ELE_FTP_ACCOUNT }}
|
||||
password: ${{ secrets.WEB_ELE_FTP_PASSWORD }}
|
||||
local-dir: ./apps/web-ele/dist/
|
||||
|
||||
deploy-naive-ftp:
|
||||
name: Deploy Push Naive Ftp
|
||||
if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Sed Config Base
|
||||
shell: bash
|
||||
run: |
|
||||
sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-naive/.env.production
|
||||
sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-naive/.env.production
|
||||
cat ./apps/web-naive/.env.production
|
||||
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup-node
|
||||
|
||||
- name: Build
|
||||
run: pnpm run build:naive
|
||||
|
||||
- name: Sync files
|
||||
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
|
||||
with:
|
||||
server: ${{ secrets.PRO_FTP_HOST }}
|
||||
username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }}
|
||||
password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }}
|
||||
local-dir: ./apps/web-naive/dist/
|
||||
|
||||
rerun-on-failure:
|
||||
name: Rerun on failure
|
||||
needs:
|
||||
- deploy-playground-ftp
|
||||
- deploy-docs-ftp
|
||||
- deploy-antd-ftp
|
||||
- deploy-ele-ftp
|
||||
- deploy-naive-ftp
|
||||
if: failure() && fromJSON(github.run_attempt) < 10
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Retry ${{ fromJSON(github.run_attempt) }} of 10
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh workflow run rerun.yml -F run_id=${{ github.run_id }}
|
||||
25
.github/workflows/draft.yml
vendored
Normal file
25
.github/workflows/draft.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
name: Release Drafter
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update_release_draft:
|
||||
permissions:
|
||||
# write permission is required to create a github release
|
||||
contents: write
|
||||
# write permission is required for autolabeler
|
||||
# otherwise, read permission is required at least
|
||||
pull-requests: write
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
31
.github/workflows/issue-close-require.yml
vendored
Normal file
31
.github/workflows/issue-close-require.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# 每天零点运行一次,它会检查所有带有 "need reproduction" 标签的 Issues。如果这些 Issues 在过去的 3 天内没有任何活动,它们将会被自动关闭。这有助于保持 Issue 列表的整洁,并且提醒用户在必要时提供更多的信息。
|
||||
name: Issue Close Require
|
||||
|
||||
# 触发条件:每天零点
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
close-issues:
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# 关闭未活动的 Issues
|
||||
- name: Close Inactive Issues
|
||||
uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: -1 # Issues and PR will never be flagged stale automatically.
|
||||
stale-issue-label: needs-reproduction # Label that flags an issue as stale.
|
||||
only-labels: needs-reproduction # Only process these issues
|
||||
days-before-issue-close: 3
|
||||
ignore-updates: true
|
||||
remove-stale-when-updated: false
|
||||
close-issue-message: This issue was closed because it was open for 3 days without a valid reproduction.
|
||||
close-issue-label: closed-by-action
|
||||
46
.github/workflows/issue-labeled.yml
vendored
Normal file
46
.github/workflows/issue-labeled.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: Label Based Actions
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
# pull_request:
|
||||
# types: [labeled]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
reply-labeled:
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: remove enhancement pending
|
||||
if: github.event.label.name == 'enhancement'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: 'enhancement: pending triage'
|
||||
|
||||
- name: remove bug pending
|
||||
if: github.event.label.name == 'bug'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
labels: 'bug: pending triage'
|
||||
|
||||
- name: needs reproduction
|
||||
if: github.event.label.name == 'needs reproduction'
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment, remove-labels'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-number: ${{ github.event.issue.number }}
|
||||
body: |
|
||||
Hello @${{ github.event.issue.user.login }}. Please provide the complete reproduction steps and code. Issues labeled by `needs reproduction` will be closed if no activities in 3 days.
|
||||
labels: 'bug: pending triage'
|
||||
24
.github/workflows/lock.yml
vendored
Normal file
24
.github/workflows/lock.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Lock Threads
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
action:
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v5
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-inactive-days: '14'
|
||||
issue-lock-reason: ''
|
||||
pr-inactive-days: '30'
|
||||
pr-lock-reason: ''
|
||||
process-only: 'issues, prs'
|
||||
80
.github/workflows/release-tag.yml
vendored
Normal file
80
.github/workflows/release-tag.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: Create Release Tag
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*' # Push events to matching v*, i.e. v1.0, v20.15.10
|
||||
|
||||
env:
|
||||
HUSKY: '0'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Create Release
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [20]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# - name: Checkout code
|
||||
# uses: actions/checkout@v4
|
||||
# with:
|
||||
# fetch-depth: 0
|
||||
|
||||
# - name: Install pnpm
|
||||
# uses: pnpm/action-setup@v4
|
||||
|
||||
# - name: Use Node.js ${{ matrix.node-version }}
|
||||
# uses: actions/setup-node@v4
|
||||
# with:
|
||||
# node-version: ${{ matrix.node-version }}
|
||||
# cache: "pnpm"
|
||||
|
||||
# - name: Install dependencies
|
||||
# run: pnpm install --frozen-lockfile
|
||||
|
||||
# - name: Test and Build
|
||||
# run: |
|
||||
# pnpm run test
|
||||
# pnpm run build
|
||||
|
||||
- name: version
|
||||
id: version
|
||||
run: |
|
||||
tag=${GITHUB_REF/refs\/tags\//}
|
||||
version=${tag#v}
|
||||
major=${version%%.*}
|
||||
echo "tag=${tag}" >> $GITHUB_OUTPUT
|
||||
echo "version=${version}" >> $GITHUB_OUTPUT
|
||||
echo "major=${major}" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
with:
|
||||
version: ${{ steps.version.outputs.version }}
|
||||
publish: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: force update major tag
|
||||
# run: |
|
||||
# git tag v${{ steps.version.outputs.major }} ${{ steps.version.outputs.tag }} -f
|
||||
# git push origin refs/tags/v${{ steps.version.outputs.major }} -f
|
||||
|
||||
# - name: Create Release for Tag
|
||||
# id: release_tag
|
||||
# uses: ncipollo/release-action@v1
|
||||
# with:
|
||||
# token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# generateReleaseNotes: "true"
|
||||
# body: |
|
||||
# > Please refer to [CHANGELOG.md](https://github.com/vbenjs/vue-vben-admin/blob/main/CHANGELOG.md) for details.
|
||||
19
.github/workflows/rerun.yml
vendored
Normal file
19
.github/workflows/rerun.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Rerun workflow
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_id:
|
||||
description: The workflow id to relanch
|
||||
required: true
|
||||
jobs:
|
||||
rerun:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: rerun ${{ inputs.run_id }}
|
||||
env:
|
||||
GH_REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
gh run watch ${{ inputs.run_id }} > /dev/null 2>&1
|
||||
gh run rerun ${{ inputs.run_id }} --failed
|
||||
41
.github/workflows/semantic-pull-request.yml
vendored
Normal file
41
.github/workflows/semantic-pull-request.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Semantic Pull Request
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: Semantic Pull Request
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate PR title
|
||||
uses: amannn/action-semantic-pull-request@v5
|
||||
with:
|
||||
wip: true
|
||||
subjectPattern: ^(?![A-Z]).+$
|
||||
subjectPatternError: |
|
||||
The subject "{subject}" found in the pull request title "{title}"
|
||||
didn't match the configured pattern. Please ensure that the subject
|
||||
doesn't start with an uppercase character.
|
||||
requireScope: false
|
||||
types: |
|
||||
fix
|
||||
feat
|
||||
docs
|
||||
style
|
||||
refactor
|
||||
perf
|
||||
test
|
||||
build
|
||||
ci
|
||||
chore
|
||||
revert
|
||||
types
|
||||
release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
19
.github/workflows/stale.yml
vendored
Normal file
19
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: 'Close stale issues'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 1 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository == 'vbenjs/vue-vben-admin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-pr-message: 'This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
exempt-issue-labels: 'bug,enhancement'
|
||||
days-before-stale: 60
|
||||
days-before-close: 7
|
||||
51
.gitignore
vendored
Normal file
51
.gitignore
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
dist.zip
|
||||
dist.tar
|
||||
dist.war
|
||||
.nitro
|
||||
.output
|
||||
*-dist.zip
|
||||
*-dist.tar
|
||||
*-dist.war
|
||||
coverage
|
||||
*.local
|
||||
**/.vitepress/cache
|
||||
.cache
|
||||
.turbo
|
||||
.temp
|
||||
dev-dist
|
||||
.stylelintcache
|
||||
yarn.lock
|
||||
package-lock.json
|
||||
.VSCodeCounter
|
||||
**/backend-mock/data
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
vite.config.mts.*
|
||||
vite.config.mjs.*
|
||||
vite.config.js.*
|
||||
vite.config.ts.*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
# .vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
.history
|
||||
6
.gitpod.yml
Normal file
6
.gitpod.yml
Normal file
@@ -0,0 +1,6 @@
|
||||
ports:
|
||||
- port: 5555
|
||||
onOpen: open-preview
|
||||
tasks:
|
||||
- init: corepack enable && pnpm install
|
||||
command: pnpm run dev:play
|
||||
20
.lintstagedrc.mjs
Normal file
20
.lintstagedrc.mjs
Normal 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
.node-version
Normal file
1
.node-version
Normal file
@@ -0,0 +1 @@
|
||||
20.14.0
|
||||
13
.npmrc
Normal file
13
.npmrc
Normal file
@@ -0,0 +1,13 @@
|
||||
registry = "https://registry.npmmirror.com"
|
||||
public-hoist-pattern[]=husky
|
||||
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
.prettierignore
Normal file
18
.prettierignore
Normal 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
.prettierrc.mjs
Normal file
1
.prettierrc.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/prettier-config';
|
||||
4
.stylelintignore
Normal file
4
.stylelintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
dist
|
||||
public
|
||||
__tests__
|
||||
coverage
|
||||
30
.vscode/extensions.json
vendored
Normal file
30
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"recommendations": [
|
||||
// Vue 3 的语言支持
|
||||
"Vue.volar",
|
||||
// 将 ESLint JavaScript 集成到 VS Code 中。
|
||||
"dbaeumer.vscode-eslint",
|
||||
// Visual Studio Code 的官方 Stylelint 扩展
|
||||
"stylelint.vscode-stylelint",
|
||||
// 使用 Prettier 的代码格式化程序
|
||||
"esbenp.prettier-vscode",
|
||||
// 支持 dotenv 文件语法
|
||||
"mikestead.dotenv",
|
||||
// 源代码的拼写检查器
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
// Tailwind CSS 的官方 VS Code 插件
|
||||
"bradlc.vscode-tailwindcss",
|
||||
// iconify 图标插件
|
||||
"antfu.iconify",
|
||||
// i18n 插件
|
||||
"Lokalise.i18n-ally",
|
||||
// CSS 变量提示
|
||||
"vunguyentuan.vscode-css-variables",
|
||||
// 在 package.json 中显示 PNPM catalog 的版本
|
||||
"antfu.pnpm-catalog-lens"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
// 和 volar 冲突
|
||||
"octref.vetur"
|
||||
]
|
||||
}
|
||||
37
.vscode/global.code-snippets
vendored
Normal file
37
.vscode/global.code-snippets
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"import": {
|
||||
"scope": "javascript,typescript",
|
||||
"prefix": "im",
|
||||
"body": ["import { $2 } from '$1';"],
|
||||
"description": "Import a module",
|
||||
},
|
||||
"export-all": {
|
||||
"scope": "javascript,typescript",
|
||||
"prefix": "ex",
|
||||
"body": ["export * from '$1';"],
|
||||
"description": "Export a module",
|
||||
},
|
||||
"vue-script-setup": {
|
||||
"scope": "vue",
|
||||
"prefix": "<sc",
|
||||
"body": [
|
||||
"<script setup lang=\"ts\">",
|
||||
"const props = defineProps<{",
|
||||
" modelValue?: boolean,",
|
||||
"}>()",
|
||||
"$1",
|
||||
"</script>",
|
||||
"",
|
||||
"<template>",
|
||||
" <div>",
|
||||
" <slot/>",
|
||||
" </div>",
|
||||
"</template>",
|
||||
],
|
||||
},
|
||||
"vue-computed": {
|
||||
"scope": "javascript,typescript,vue",
|
||||
"prefix": "com",
|
||||
"body": ["computed(() => { $1 })"],
|
||||
},
|
||||
}
|
||||
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"name": "bdrp admin web dev",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:5666",
|
||||
"env": { "NODE_ENV": "development" },
|
||||
"sourceMaps": true,
|
||||
"webRoot": "${workspaceFolder}/apps/bdrp-admin-web"
|
||||
}
|
||||
]
|
||||
}
|
||||
237
.vscode/settings.json
vendored
Normal file
237
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
{
|
||||
"tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts",
|
||||
// workbench
|
||||
"workbench.list.smoothScrolling": true,
|
||||
"workbench.startupEditor": "newUntitledFile",
|
||||
"workbench.tree.indent": 10,
|
||||
"workbench.editor.highlightModifiedTabs": true,
|
||||
"workbench.editor.closeOnFileDelete": true,
|
||||
"workbench.editor.limit.enabled": true,
|
||||
"workbench.editor.limit.perEditorGroup": true,
|
||||
"workbench.editor.limit.value": 5,
|
||||
|
||||
// editor
|
||||
"editor.tabSize": 2,
|
||||
"editor.detectIndentation": false,
|
||||
"editor.cursorBlinking": "expand",
|
||||
"editor.largeFileOptimizations": false,
|
||||
"editor.accessibilitySupport": "off",
|
||||
"editor.cursorSmoothCaretAnimation": "on",
|
||||
"editor.guides.bracketPairs": "active",
|
||||
"editor.inlineSuggest.enabled": true,
|
||||
"editor.suggestSelection": "recentlyUsedByPrefix",
|
||||
"editor.acceptSuggestionOnEnter": "smart",
|
||||
"editor.suggest.snippetsPreventQuickSuggestions": false,
|
||||
"editor.stickyScroll.enabled": true,
|
||||
"editor.hover.sticky": true,
|
||||
"editor.suggest.insertMode": "replace",
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.autoClosingBrackets": "beforeWhitespace",
|
||||
"editor.autoClosingDelete": "always",
|
||||
"editor.autoClosingOvertype": "always",
|
||||
"editor.autoClosingQuotes": "beforeWhitespace",
|
||||
"editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.stylelint": "explicit",
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
// extensions
|
||||
"extensions.ignoreRecommendations": true,
|
||||
|
||||
// terminal
|
||||
"terminal.integrated.cursorBlinking": true,
|
||||
"terminal.integrated.persistentSessionReviveProcess": "never",
|
||||
"terminal.integrated.tabs.enabled": true,
|
||||
"terminal.integrated.scrollback": 10000,
|
||||
"terminal.integrated.stickyScroll.enabled": true,
|
||||
|
||||
// files
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.simpleDialog.enable": true,
|
||||
"files.associations": {
|
||||
"*.ejs": "html",
|
||||
"*.art": "html",
|
||||
"**/tsconfig.json": "jsonc",
|
||||
"*.json": "jsonc",
|
||||
"package.json": "json"
|
||||
},
|
||||
|
||||
"files.exclude": {
|
||||
"**/.eslintcache": true,
|
||||
"**/bower_components": true,
|
||||
"**/.turbo": true,
|
||||
"**/.idea": true,
|
||||
"**/tmp": true,
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.stylelintcache": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/vite.config.mts.*": true,
|
||||
"**/tea.yaml": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.vscode/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/tmp/**": true,
|
||||
"**/bower_components/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
|
||||
// search
|
||||
"search.searchEditor.singleClickBehaviour": "peekDefinition",
|
||||
"search.followSymlinks": false,
|
||||
// 在使用搜索功能时,将这些文件夹/文件排除在外
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.log": true,
|
||||
"**/*.log*": true,
|
||||
"**/bower_components": true,
|
||||
"**/dist": true,
|
||||
"**/elehukouben": true,
|
||||
"**/.git": true,
|
||||
"**/.github": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.svn": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.vitepress/cache": true,
|
||||
"**/.idea": true,
|
||||
"**/.vscode": false,
|
||||
"**/.yarn": true,
|
||||
"**/tmp": true,
|
||||
"*.xml": true,
|
||||
"out": true,
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"CHANGELOG.md": true,
|
||||
"**/pnpm-lock.yaml": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
|
||||
"debug.onTaskErrors": "debugAnyway",
|
||||
"diffEditor.ignoreTrimWhitespace": false,
|
||||
"npm.packageManager": "pnpm",
|
||||
|
||||
"css.validate": false,
|
||||
"less.validate": false,
|
||||
"scss.validate": false,
|
||||
|
||||
// extension
|
||||
"emmet.showSuggestionsAsSnippets": true,
|
||||
"emmet.triggerExpansionOnTab": false,
|
||||
|
||||
"errorLens.enabledDiagnosticLevels": ["warning", "error"],
|
||||
"errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"],
|
||||
|
||||
"stylelint.enable": true,
|
||||
"stylelint.packageManager": "pnpm",
|
||||
"stylelint.validate": ["css", "less", "postcss", "scss", "vue"],
|
||||
"stylelint.customSyntax": "postcss-html",
|
||||
"stylelint.snippet": ["css", "less", "postcss", "scss", "vue"],
|
||||
|
||||
"typescript.inlayHints.enumMemberValues.enabled": true,
|
||||
"typescript.preferences.preferTypeOnlyAutoImports": true,
|
||||
"typescript.preferences.includePackageJsonAutoImports": "on",
|
||||
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"typescript",
|
||||
"javascriptreact",
|
||||
"typescriptreact",
|
||||
"vue",
|
||||
"html",
|
||||
"markdown",
|
||||
"json",
|
||||
"jsonc",
|
||||
"json5"
|
||||
],
|
||||
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||
],
|
||||
|
||||
"github.copilot.enable": {
|
||||
"*": true,
|
||||
"markdown": true,
|
||||
"plaintext": false,
|
||||
"yaml": false
|
||||
},
|
||||
|
||||
"cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"],
|
||||
|
||||
"i18n-ally.localesPaths": [
|
||||
"packages/locales/src/langs",
|
||||
"apps/*/src/locales/langs"
|
||||
],
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespace}.{ext}",
|
||||
"i18n-ally.enabledParsers": ["json"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": true,
|
||||
|
||||
// 控制相关文件嵌套展示
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.ts": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx, $(capture).d.ts",
|
||||
"*.tsx": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx,$(capture).d.ts",
|
||||
"*.env": "$(capture).env.*",
|
||||
"README.md": "README*,CHANGELOG*,LICENSE,CNAME",
|
||||
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json",
|
||||
"eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json",
|
||||
"tailwind.config.mjs": "postcss.*"
|
||||
},
|
||||
"commentTranslate.hover.enabled": false,
|
||||
"commentTranslate.multiLineMerge": true,
|
||||
"vue.server.hybridMode": true,
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"oxc.enable": false,
|
||||
"cSpell.words": [
|
||||
"archiver",
|
||||
"axios",
|
||||
"dotenv",
|
||||
"isequal",
|
||||
"jspm",
|
||||
"napi",
|
||||
"nolebase",
|
||||
"rollup",
|
||||
"vitest"
|
||||
]
|
||||
}
|
||||
5
apps/bdrp-admin-web/.env
Normal file
5
apps/bdrp-admin-web/.env
Normal file
@@ -0,0 +1,5 @@
|
||||
# 应用标题
|
||||
VITE_APP_TITLE=赤眉
|
||||
|
||||
# 应用命名空间,用于缓存、store等功能的前缀,确保隔离
|
||||
VITE_APP_NAMESPACE=bdrp
|
||||
7
apps/bdrp-admin-web/.env.analyze
Normal file
7
apps/bdrp-admin-web/.env.analyze
Normal file
@@ -0,0 +1,7 @@
|
||||
# public path
|
||||
VITE_BASE=/
|
||||
|
||||
# Basic interface address SPA
|
||||
VITE_GLOB_API_URL=/api
|
||||
|
||||
VITE_VISUALIZER=true
|
||||
16
apps/bdrp-admin-web/.env.development
Normal file
16
apps/bdrp-admin-web/.env.development
Normal file
@@ -0,0 +1,16 @@
|
||||
# 端口号
|
||||
VITE_PORT=5666
|
||||
|
||||
VITE_BASE=/
|
||||
|
||||
# 接口地址
|
||||
VITE_GLOB_API_URL=/api/v1/admin
|
||||
|
||||
# 是否开启 Nitro Mock服务,true 为开启,false 为关闭
|
||||
VITE_NITRO_MOCK=false
|
||||
|
||||
# 是否打开 devtools,true 为打开,false 为关闭
|
||||
VITE_DEVTOOLS=true
|
||||
|
||||
# 是否注入全局loading
|
||||
VITE_INJECT_APP_LOADING=true
|
||||
19
apps/bdrp-admin-web/.env.production
Normal file
19
apps/bdrp-admin-web/.env.production
Normal file
@@ -0,0 +1,19 @@
|
||||
VITE_BASE=/
|
||||
|
||||
# 接口地址
|
||||
VITE_GLOB_API_URL=/api/v1/admin
|
||||
|
||||
# 是否开启压缩,可以设置为 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
|
||||
22
apps/bdrp-admin-web/index.html
Normal file
22
apps/bdrp-admin-web/index.html
Normal file
@@ -0,0 +1,22 @@
|
||||
<!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="赤眉" />
|
||||
<meta name="keywords" content="赤眉" />
|
||||
<meta name="author" content="赤眉" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
|
||||
/>
|
||||
<!-- 由 vite 注入 VITE_APP_TITLE 变量,在 .env 文件内配置 -->
|
||||
<title><%= VITE_APP_TITLE %></title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
54
apps/bdrp-admin-web/package.json
Normal file
54
apps/bdrp-admin-web/package.json
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "@vben/web-antd",
|
||||
"version": "5.5.4",
|
||||
"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": "ann.vben@gmail.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": {
|
||||
"@vben-core/composables": "workspace:*",
|
||||
"@vben-core/menu-ui": "workspace:*",
|
||||
"@vben-core/shadcn-ui": "workspace:*",
|
||||
"@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:",
|
||||
"dayjs": "catalog:",
|
||||
"pinia": "catalog:",
|
||||
"sortablejs": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "catalog:"
|
||||
}
|
||||
}
|
||||
1
apps/bdrp-admin-web/postcss.config.mjs
Normal file
1
apps/bdrp-admin-web/postcss.config.mjs
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '@vben/tailwind-config/postcss';
|
||||
BIN
apps/bdrp-admin-web/public/favicon.ico
Normal file
BIN
apps/bdrp-admin-web/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
apps/bdrp-admin-web/public/logo.png
Normal file
BIN
apps/bdrp-admin-web/public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
210
apps/bdrp-admin-web/src/adapter/component/index.ts
Normal file
210
apps/bdrp-admin-web/src/adapter/component/index.ts
Normal file
@@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用
|
||||
* 可用于 vben-form、vben-modal、vben-drawer 等组件使用,
|
||||
*/
|
||||
|
||||
import type { Component } from 'vue';
|
||||
|
||||
import type { BaseFormComponentType } from '@vben/common-ui';
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import {
|
||||
defineAsyncComponent,
|
||||
defineComponent,
|
||||
getCurrentInstance,
|
||||
h,
|
||||
ref,
|
||||
} from 'vue';
|
||||
|
||||
import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
import { notification } from 'ant-design-vue';
|
||||
|
||||
const AutoComplete = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/auto-complete'),
|
||||
);
|
||||
const Button = defineAsyncComponent(() => import('ant-design-vue/es/button'));
|
||||
const Checkbox = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/checkbox'),
|
||||
);
|
||||
const CheckboxGroup = defineAsyncComponent(() =>
|
||||
import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup),
|
||||
);
|
||||
const DatePicker = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/date-picker'),
|
||||
);
|
||||
const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider'));
|
||||
const Input = defineAsyncComponent(() => import('ant-design-vue/es/input'));
|
||||
const InputNumber = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/input-number'),
|
||||
);
|
||||
const InputPassword = defineAsyncComponent(() =>
|
||||
import('ant-design-vue/es/input').then((res) => res.InputPassword),
|
||||
);
|
||||
const Mentions = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/mentions'),
|
||||
);
|
||||
const Radio = defineAsyncComponent(() => import('ant-design-vue/es/radio'));
|
||||
const RadioGroup = defineAsyncComponent(() =>
|
||||
import('ant-design-vue/es/radio').then((res) => res.RadioGroup),
|
||||
);
|
||||
const RangePicker = defineAsyncComponent(() =>
|
||||
import('ant-design-vue/es/date-picker').then((res) => res.RangePicker),
|
||||
);
|
||||
const Rate = defineAsyncComponent(() => import('ant-design-vue/es/rate'));
|
||||
const Select = defineAsyncComponent(() => import('ant-design-vue/es/select'));
|
||||
const Space = defineAsyncComponent(() => import('ant-design-vue/es/space'));
|
||||
const Switch = defineAsyncComponent(() => import('ant-design-vue/es/switch'));
|
||||
const Textarea = defineAsyncComponent(() =>
|
||||
import('ant-design-vue/es/input').then((res) => res.Textarea),
|
||||
);
|
||||
const TimePicker = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/time-picker'),
|
||||
);
|
||||
const TreeSelect = defineAsyncComponent(
|
||||
() => import('ant-design-vue/es/tree-select'),
|
||||
);
|
||||
const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload'));
|
||||
|
||||
const RichText = defineAsyncComponent(() =>
|
||||
import('@vben-core/shadcn-ui').then((m) => m.RichText),
|
||||
);
|
||||
|
||||
const withDefaultPlaceholder = <T extends Component>(
|
||||
component: T,
|
||||
type: 'input' | 'select',
|
||||
componentProps: Recordable<any> = {},
|
||||
) => {
|
||||
return defineComponent({
|
||||
inheritAttrs: false,
|
||||
name: component.name,
|
||||
setup: (props: any, { attrs, expose, slots }) => {
|
||||
const placeholder =
|
||||
props?.placeholder ||
|
||||
attrs?.placeholder ||
|
||||
$t(`ui.placeholder.${type}`);
|
||||
// 透传组件暴露的方法
|
||||
const innerRef = ref();
|
||||
const publicApi: Recordable<any> = {};
|
||||
expose(publicApi);
|
||||
const instance = getCurrentInstance();
|
||||
instance?.proxy?.$nextTick(() => {
|
||||
for (const key in innerRef.value) {
|
||||
if (typeof innerRef.value[key] === 'function') {
|
||||
publicApi[key] = innerRef.value[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
return () =>
|
||||
h(
|
||||
component,
|
||||
{ ...componentProps, placeholder, ...props, ...attrs, ref: innerRef },
|
||||
slots,
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明
|
||||
export type ComponentType =
|
||||
| 'ApiSelect'
|
||||
| 'ApiTreeSelect'
|
||||
| 'AutoComplete'
|
||||
| 'Checkbox'
|
||||
| 'CheckboxGroup'
|
||||
| 'DatePicker'
|
||||
| 'DefaultButton'
|
||||
| 'Divider'
|
||||
| 'IconPicker'
|
||||
| 'Input'
|
||||
| 'InputNumber'
|
||||
| 'InputPassword'
|
||||
| 'Mentions'
|
||||
| 'PrimaryButton'
|
||||
| 'Radio'
|
||||
| 'RadioGroup'
|
||||
| 'RangePicker'
|
||||
| 'Rate'
|
||||
| 'RichText'
|
||||
| 'Select'
|
||||
| 'Space'
|
||||
| 'Switch'
|
||||
| 'Textarea'
|
||||
| 'TimePicker'
|
||||
| 'TreeSelect'
|
||||
| 'Upload'
|
||||
| BaseFormComponentType;
|
||||
|
||||
async function initComponentAdapter() {
|
||||
const components: Partial<Record<ComponentType, Component>> = {
|
||||
// 如果你的组件体积比较大,可以使用异步加载
|
||||
// Button: () =>
|
||||
// import('xxx').then((res) => res.Button),
|
||||
ApiSelect: withDefaultPlaceholder(ApiComponent, 'select', {
|
||||
component: Select,
|
||||
loadingSlot: 'suffixIcon',
|
||||
visibleEvent: 'onDropdownVisibleChange',
|
||||
modelPropName: 'value',
|
||||
}),
|
||||
ApiTreeSelect: withDefaultPlaceholder(ApiComponent, 'select', {
|
||||
component: TreeSelect,
|
||||
fieldNames: { label: 'label', value: 'value', children: 'children' },
|
||||
loadingSlot: 'suffixIcon',
|
||||
modelPropName: 'value',
|
||||
optionsPropName: 'treeData',
|
||||
visibleEvent: 'onVisibleChange',
|
||||
}),
|
||||
AutoComplete,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
DatePicker,
|
||||
// 自定义默认按钮
|
||||
DefaultButton: (props, { attrs, slots }) => {
|
||||
return h(Button, { ...props, attrs, type: 'default' }, slots);
|
||||
},
|
||||
Divider,
|
||||
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,
|
||||
Select: withDefaultPlaceholder(Select, 'select'),
|
||||
Space,
|
||||
Switch,
|
||||
Textarea: withDefaultPlaceholder(Textarea, 'input'),
|
||||
TimePicker,
|
||||
TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'),
|
||||
Upload,
|
||||
RichText,
|
||||
};
|
||||
|
||||
// 将组件注册到全局共享状态中
|
||||
globalShareState.setComponents(components);
|
||||
|
||||
// 定义全局共享状态中的消息提示
|
||||
globalShareState.defineMessage({
|
||||
// 复制成功消息提示
|
||||
copyPreferencesSuccess: (title, content) => {
|
||||
notification.success({
|
||||
description: content,
|
||||
message: title,
|
||||
placement: 'bottomRight',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export { initComponentAdapter };
|
||||
48
apps/bdrp-admin-web/src/adapter/form.ts
Normal file
48
apps/bdrp-admin-web/src/adapter/form.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import type {
|
||||
VbenFormSchema as FormSchema,
|
||||
VbenFormProps,
|
||||
} from '@vben/common-ui';
|
||||
|
||||
import type { ComponentType } from './component';
|
||||
|
||||
import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui';
|
||||
import { $t } from '@vben/locales';
|
||||
|
||||
setupVbenForm<ComponentType>({
|
||||
config: {
|
||||
// ant design vue组件库默认都是 v-model:value
|
||||
baseModelPropName: 'value',
|
||||
|
||||
// 一些组件是 v-model:checked 或者 v-model:fileList
|
||||
modelPropNameMap: {
|
||||
Checkbox: 'checked',
|
||||
Radio: 'checked',
|
||||
Switch: 'checked',
|
||||
Upload: 'fileList',
|
||||
TinyMCE: 'modelValue',
|
||||
},
|
||||
},
|
||||
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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const useVbenForm = useForm<ComponentType>;
|
||||
|
||||
export { useVbenForm, z };
|
||||
|
||||
export type VbenFormSchema = FormSchema<ComponentType>;
|
||||
export type { VbenFormProps };
|
||||
276
apps/bdrp-admin-web/src/adapter/vxe-table.ts
Normal file
276
apps/bdrp-admin-web/src/adapter/vxe-table.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { h } from 'vue';
|
||||
|
||||
import { IconifyIcon } from '@vben/icons';
|
||||
import { $te } from '@vben/locales';
|
||||
import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table';
|
||||
import { get, isFunction, isString } from '@vben/utils';
|
||||
|
||||
import { objectOmit } from '@vueuse/core';
|
||||
import { Button, Image, Popconfirm, Switch, Tag } from 'ant-design-vue';
|
||||
|
||||
import { $t } from '#/locales';
|
||||
|
||||
import { useVbenForm } from './form';
|
||||
|
||||
setupVbenVxeTable({
|
||||
configVxeTable: (vxeUI) => {
|
||||
vxeUI.setConfig({
|
||||
grid: {
|
||||
align: 'center',
|
||||
border: false,
|
||||
columnConfig: {
|
||||
resizable: true,
|
||||
},
|
||||
|
||||
formConfig: {
|
||||
// 全局禁用vxe-table的表单配置,使用formOptions
|
||||
enabled: false,
|
||||
},
|
||||
minHeight: 180,
|
||||
proxyConfig: {
|
||||
autoLoad: true,
|
||||
response: {
|
||||
result: 'items',
|
||||
total: 'total',
|
||||
list: '',
|
||||
},
|
||||
showActiveMsg: true,
|
||||
showResponseMsg: false,
|
||||
},
|
||||
round: true,
|
||||
showOverflow: true,
|
||||
size: 'small',
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 解决vxeTable在热更新时可能会出错的问题
|
||||
*/
|
||||
vxeUI.renderer.forEach((_item, key) => {
|
||||
if (key.startsWith('Cell')) {
|
||||
vxeUI.renderer.delete(key);
|
||||
}
|
||||
});
|
||||
|
||||
// 表格配置项可以用 cellRender: { name: 'CellImage' },
|
||||
vxeUI.renderer.add('CellImage', {
|
||||
renderTableDefault(_renderOpts, params) {
|
||||
const { column, row } = params;
|
||||
return h(Image, { src: row[column.field] });
|
||||
},
|
||||
});
|
||||
|
||||
// 表格配置项可以用 cellRender: { name: 'CellLink' },
|
||||
vxeUI.renderer.add('CellLink', {
|
||||
renderTableDefault(renderOpts) {
|
||||
const { props } = renderOpts;
|
||||
return h(
|
||||
Button,
|
||||
{ size: 'small', type: 'link' },
|
||||
{ default: () => props?.text },
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// 单元格渲染: Tag
|
||||
vxeUI.renderer.add('CellTag', {
|
||||
renderTableDefault({ options, props }, { column, row }) {
|
||||
const value = get(row, column.field);
|
||||
const tagOptions = options ?? [
|
||||
{ color: 'success', label: $t('common.enabled'), value: 1 },
|
||||
{ color: 'error', label: $t('common.disabled'), value: 0 },
|
||||
];
|
||||
const tagItem = tagOptions.find((item) => item.value === value);
|
||||
return h(
|
||||
Tag,
|
||||
{
|
||||
...props,
|
||||
...objectOmit(tagItem ?? {}, ['label']),
|
||||
},
|
||||
{ default: () => tagItem?.label ?? value },
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
vxeUI.renderer.add('CellSwitch', {
|
||||
renderTableDefault({ attrs, props }, { column, row }) {
|
||||
const loadingKey = `__loading_${column.field}`;
|
||||
const finallyProps = {
|
||||
checkedChildren: $t('common.enabled'),
|
||||
checkedValue: 1,
|
||||
unCheckedChildren: $t('common.disabled'),
|
||||
unCheckedValue: 0,
|
||||
...props,
|
||||
checked: row[column.field],
|
||||
loading: row[loadingKey] ?? false,
|
||||
'onUpdate:checked': onChange,
|
||||
};
|
||||
async function onChange(newVal: any) {
|
||||
row[loadingKey] = true;
|
||||
try {
|
||||
const result = await attrs?.beforeChange?.(newVal, row);
|
||||
if (result !== false) {
|
||||
row[column.field] = newVal;
|
||||
}
|
||||
} finally {
|
||||
row[loadingKey] = false;
|
||||
}
|
||||
}
|
||||
return h(Switch, finallyProps);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 注册表格的操作按钮渲染器
|
||||
*/
|
||||
vxeUI.renderer.add('CellOperation', {
|
||||
renderTableDefault({ attrs, options, props }, { column, row }) {
|
||||
const defaultProps = { size: 'small', type: 'link', ...props };
|
||||
let align = 'end';
|
||||
switch (column.align) {
|
||||
case 'center': {
|
||||
align = 'center';
|
||||
break;
|
||||
}
|
||||
case 'left': {
|
||||
align = 'start';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
align = 'end';
|
||||
break;
|
||||
}
|
||||
}
|
||||
const presets: Recordable<Recordable<any>> = {
|
||||
delete: {
|
||||
danger: true,
|
||||
text: $t('common.delete'),
|
||||
},
|
||||
edit: {
|
||||
text: $t('common.edit'),
|
||||
},
|
||||
};
|
||||
const operations: Array<Recordable<any>> = (
|
||||
options || ['edit', 'delete']
|
||||
)
|
||||
.map((opt) => {
|
||||
if (isString(opt)) {
|
||||
return presets[opt]
|
||||
? { code: opt, ...presets[opt], ...defaultProps }
|
||||
: {
|
||||
code: opt,
|
||||
text: $te(`common.${opt}`) ? $t(`common.${opt}`) : opt,
|
||||
...defaultProps,
|
||||
};
|
||||
} else {
|
||||
return { ...defaultProps, ...presets[opt.code], ...opt };
|
||||
}
|
||||
})
|
||||
.map((opt) => {
|
||||
const optBtn: Recordable<any> = {};
|
||||
Object.keys(opt).forEach((key) => {
|
||||
optBtn[key] = isFunction(opt[key]) ? opt[key](row) : opt[key];
|
||||
});
|
||||
return optBtn;
|
||||
})
|
||||
.filter((opt) => opt.show !== false);
|
||||
|
||||
function renderBtn(opt: Recordable<any>, listen = true) {
|
||||
return h(
|
||||
Button,
|
||||
{
|
||||
...props,
|
||||
...opt,
|
||||
icon: undefined,
|
||||
onClick: listen
|
||||
? () =>
|
||||
attrs?.onClick?.({
|
||||
code: opt.code,
|
||||
row,
|
||||
})
|
||||
: undefined,
|
||||
},
|
||||
{
|
||||
default: () => {
|
||||
const content = [];
|
||||
if (opt.icon) {
|
||||
content.push(
|
||||
h(IconifyIcon, { class: 'size-5', icon: opt.icon }),
|
||||
);
|
||||
}
|
||||
content.push(opt.text);
|
||||
return content;
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function renderConfirm(opt: Recordable<any>) {
|
||||
return h(
|
||||
Popconfirm,
|
||||
{
|
||||
getPopupContainer(el) {
|
||||
return (
|
||||
el
|
||||
.closest('.vxe-table--viewport-wrapper')
|
||||
?.querySelector('.vxe-table--main-wrapper')
|
||||
?.querySelector('tbody') || document.body
|
||||
);
|
||||
},
|
||||
placement: 'topLeft',
|
||||
title: $t('ui.actionTitle.delete', [attrs?.nameTitle || '']),
|
||||
...props,
|
||||
...opt,
|
||||
icon: undefined,
|
||||
onConfirm: () => {
|
||||
attrs?.onClick?.({
|
||||
code: opt.code,
|
||||
row,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
default: () => renderBtn({ ...opt }, false),
|
||||
description: () =>
|
||||
h(
|
||||
'div',
|
||||
{ class: 'truncate' },
|
||||
$t('ui.actionMessage.deleteConfirm', [
|
||||
row[attrs?.nameField || 'name'],
|
||||
]),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const btns = operations.map((opt) =>
|
||||
opt.code === 'delete' ? renderConfirm(opt) : renderBtn(opt),
|
||||
);
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
class: 'flex table-operations',
|
||||
style: { justifyContent: align },
|
||||
},
|
||||
btns,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
// 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化
|
||||
// vxeUI.formats.add
|
||||
},
|
||||
useVbenForm,
|
||||
});
|
||||
|
||||
export { useVbenVxeGrid };
|
||||
export type OnActionClickParams<T = Recordable<any>> = {
|
||||
code: string;
|
||||
row: T;
|
||||
};
|
||||
export type OnActionClickFn<T = Recordable<any>> = (
|
||||
params: OnActionClickParams<T>,
|
||||
) => void;
|
||||
export type * from '@vben/plugins/vxe-table';
|
||||
715
apps/bdrp-admin-web/src/api/agent/agent.ts
Normal file
715
apps/bdrp-admin-web/src/api/agent/agent.ts
Normal file
@@ -0,0 +1,715 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace AgentApi {
|
||||
export interface AgentListItem {
|
||||
id: number;
|
||||
user_id: number;
|
||||
level_name: string;
|
||||
region: string;
|
||||
mobile: string;
|
||||
membership_expiry_time: string;
|
||||
balance: number;
|
||||
total_earnings: number;
|
||||
frozen_balance: number;
|
||||
withdrawn_amount: number;
|
||||
create_time: string;
|
||||
is_real_name_verified: boolean;
|
||||
real_name: string;
|
||||
id_card: string;
|
||||
real_name_status: 'approved' | 'pending' | 'rejected';
|
||||
}
|
||||
|
||||
export interface AgentList {
|
||||
total: number;
|
||||
items: AgentListItem[];
|
||||
}
|
||||
|
||||
export interface GetAgentListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
mobile?: string;
|
||||
region?: string;
|
||||
parent_agent_id?: number;
|
||||
id?: number;
|
||||
create_time_start?: string;
|
||||
create_time_end?: string;
|
||||
order_by?: string;
|
||||
order_type?: 'asc' | 'desc';
|
||||
}
|
||||
|
||||
export interface AgentLinkListItem {
|
||||
agent_id: number;
|
||||
product_name: string;
|
||||
price: number;
|
||||
link_identifier: string;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface AgentLinkList {
|
||||
total: number;
|
||||
items: AgentLinkListItem[];
|
||||
}
|
||||
|
||||
export interface GetAgentLinkListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id?: number;
|
||||
product_name?: string;
|
||||
link_identifier?: string;
|
||||
}
|
||||
|
||||
// 代理佣金相关接口
|
||||
export interface AgentCommissionListItem {
|
||||
id: number;
|
||||
agent_id: number;
|
||||
order_id: number;
|
||||
amount: number;
|
||||
product_name: string;
|
||||
status: number;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface AgentCommissionList {
|
||||
total: number;
|
||||
items: AgentCommissionListItem[];
|
||||
}
|
||||
|
||||
export interface GetAgentCommissionListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id?: number;
|
||||
order_id?: number;
|
||||
product_name?: string;
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// 代理奖励相关接口
|
||||
export interface AgentRewardListItem {
|
||||
id: number;
|
||||
agent_id: number;
|
||||
relation_agent_id: number;
|
||||
amount: number;
|
||||
type: string;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface AgentRewardList {
|
||||
total: number;
|
||||
items: AgentRewardListItem[];
|
||||
}
|
||||
|
||||
export interface GetAgentRewardListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id?: number;
|
||||
relation_agent_id?: number;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
// 代理提现相关接口
|
||||
export interface AgentWithdrawalListItem {
|
||||
id: number;
|
||||
agent_id: number;
|
||||
withdraw_no: string;
|
||||
amount: number;
|
||||
actual_amount: number; // 实际到账金额(扣税后)
|
||||
tax_amount: number; // 扣税金额
|
||||
status: number;
|
||||
payee_account: string;
|
||||
remark: string;
|
||||
create_time: string;
|
||||
withdraw_type: number; // 提现类型:1-支付宝,2-银行卡
|
||||
bank_card_no?: string; // 银行卡号
|
||||
bank_name?: string; // 开户支行
|
||||
payee_name?: string; // 收款人姓名
|
||||
}
|
||||
|
||||
export interface AgentWithdrawalList {
|
||||
total: number;
|
||||
items: AgentWithdrawalListItem[];
|
||||
}
|
||||
|
||||
export interface GetAgentWithdrawalListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id?: number;
|
||||
status?: number;
|
||||
withdraw_no?: string;
|
||||
}
|
||||
|
||||
// 提现统计数据
|
||||
export interface WithdrawalStatistics {
|
||||
total_withdrawal_amount: number;
|
||||
today_withdrawal_amount: number;
|
||||
total_actual_amount: number;
|
||||
total_tax_amount: number;
|
||||
}
|
||||
|
||||
// 代理订单统计数据
|
||||
export interface AgentOrderStatistics {
|
||||
total_agent_order_count: number; // 总代理订单数
|
||||
today_agent_order_count: number; // 今日代理订单数
|
||||
}
|
||||
|
||||
// 代理统计数据
|
||||
export interface AgentStatistics {
|
||||
total_agent_count: number; // 总代理数
|
||||
today_agent_count: number; // 今日新增代理数
|
||||
}
|
||||
|
||||
// 代理链接产品统计项
|
||||
export interface AgentLinkProductStatisticsItem {
|
||||
product_name: string;
|
||||
link_count: number;
|
||||
}
|
||||
|
||||
// 代理链接产品统计响应
|
||||
export interface AgentLinkProductStatisticsResp {
|
||||
items: AgentLinkProductStatisticsItem[];
|
||||
}
|
||||
|
||||
// 代理链接产品统计请求参数
|
||||
export interface GetAgentLinkProductStatisticsParams {}
|
||||
|
||||
// 代理上级抽佣相关接口
|
||||
export interface AgentCommissionDeductionListItem {
|
||||
id: number;
|
||||
agent_id: number;
|
||||
deducted_agent_id: number;
|
||||
amount: number;
|
||||
product_name: string;
|
||||
type: 'cost' | 'pricing';
|
||||
status: number;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface AgentCommissionDeductionList {
|
||||
total: number;
|
||||
items: AgentCommissionDeductionListItem[];
|
||||
}
|
||||
|
||||
export interface GetAgentCommissionDeductionListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id?: number;
|
||||
product_name?: string;
|
||||
type?: 'cost' | 'pricing';
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// 平台抽佣列表项
|
||||
export interface AgentPlatformDeductionListItem {
|
||||
id: number;
|
||||
agent_id: number;
|
||||
amount: number;
|
||||
type: 'cost' | 'pricing';
|
||||
status: number;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
// 平台抽佣列表响应
|
||||
export interface AgentPlatformDeductionList {
|
||||
total: number;
|
||||
items: AgentPlatformDeductionListItem[];
|
||||
}
|
||||
|
||||
// 获取平台抽佣列表参数
|
||||
export interface GetAgentPlatformDeductionListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id?: number;
|
||||
type?: 'cost' | 'pricing';
|
||||
status?: number;
|
||||
}
|
||||
|
||||
// 代理产品配置列表项
|
||||
export interface AgentProductionConfigItem {
|
||||
id: number;
|
||||
product_name: string;
|
||||
cost_price: number;
|
||||
price_range_min: number;
|
||||
price_range_max: number;
|
||||
pricing_standard: number;
|
||||
overpricing_ratio: number;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
// 代理产品配置列表响应
|
||||
export interface AgentProductionConfigList {
|
||||
total: number;
|
||||
items: AgentProductionConfigItem[];
|
||||
}
|
||||
|
||||
// 获取代理产品配置列表参数
|
||||
export interface GetAgentProductionConfigListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
product_name?: string;
|
||||
id?: number;
|
||||
}
|
||||
|
||||
// 更新代理产品配置参数
|
||||
export interface UpdateAgentProductionConfigParams {
|
||||
id: number;
|
||||
cost_price: number;
|
||||
price_range_min: number;
|
||||
price_range_max: number;
|
||||
pricing_standard: number;
|
||||
overpricing_ratio: number;
|
||||
}
|
||||
|
||||
// 更新代理产品配置响应
|
||||
export interface UpdateAgentProductionConfigResp {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface MembershipRechargeOrderListItem {
|
||||
id: number;
|
||||
user_id: number;
|
||||
agent_id: number;
|
||||
level_name: string;
|
||||
amount: number;
|
||||
payment_method: 'alipay' | 'appleiap' | 'other' | 'wechat';
|
||||
order_no: string;
|
||||
platform_order_id: string;
|
||||
status: 'cancelled' | 'failed' | 'pending' | 'success';
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface GetMembershipRechargeOrderListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
user_id?: number;
|
||||
agent_id?: number;
|
||||
level_name?: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
export interface MembershipRechargeOrderList {
|
||||
total: number;
|
||||
items: MembershipRechargeOrderListItem[];
|
||||
}
|
||||
|
||||
// 代理会员配置相关接口
|
||||
export interface AgentMembershipConfigListItem {
|
||||
id: number;
|
||||
level_name: string;
|
||||
price: number;
|
||||
report_commission: number;
|
||||
lower_activity_reward: null | number;
|
||||
new_activity_reward: null | number;
|
||||
lower_standard_count: null | number;
|
||||
new_lower_standard_count: null | number;
|
||||
lower_withdraw_reward_ratio: null | number;
|
||||
lower_convert_vip_reward: null | number;
|
||||
lower_convert_svip_reward: null | number;
|
||||
exemption_amount: number;
|
||||
price_increase_max: null | number;
|
||||
price_ratio: null | number;
|
||||
price_increase_amount: null | number;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface GetAgentMembershipConfigListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
level_name?: string;
|
||||
}
|
||||
|
||||
// 代理会员配置编辑请求参数
|
||||
export interface UpdateAgentMembershipConfigParams {
|
||||
id: number; // 主键
|
||||
level_name: string; // 会员级别名称
|
||||
price: number; // 会员年费
|
||||
report_commission: number; // 直推报告收益
|
||||
lower_activity_reward?: null | number; // 下级活跃奖励金额
|
||||
new_activity_reward?: null | number; // 新增活跃奖励金额
|
||||
lower_standard_count?: null | number; // 活跃下级达标个数
|
||||
new_lower_standard_count?: null | number; // 新增活跃下级达标个数
|
||||
lower_withdraw_reward_ratio?: null | number; // 下级提现奖励比例
|
||||
lower_convert_vip_reward?: null | number; // 下级转化VIP奖励
|
||||
lower_convert_svip_reward?: null | number; // 下级转化SVIP奖励
|
||||
exemption_amount?: null | number; // 免责金额
|
||||
price_increase_max?: null | number; // 提价最高金额
|
||||
price_ratio?: null | number; // 提价区间收取比例
|
||||
price_increase_amount?: null | number; // 在原本成本上加价的金额
|
||||
}
|
||||
|
||||
// 代理钱包信息
|
||||
export interface AgentWalletInfo {
|
||||
balance: number; // 可用余额
|
||||
frozen_balance: number; // 冻结余额
|
||||
total_earnings: number; // 总收益
|
||||
}
|
||||
|
||||
// 修改代理钱包余额请求
|
||||
export interface UpdateAgentWalletBalanceReq {
|
||||
agent_id: number; // 代理ID
|
||||
amount: number; // 修改金额(正数增加,负数减少)
|
||||
}
|
||||
|
||||
// 修改代理钱包余额响应
|
||||
export interface UpdateAgentWalletBalanceResp {
|
||||
success: boolean; // 是否成功
|
||||
balance: number; // 修改后的余额
|
||||
}
|
||||
|
||||
// 代理钱包流水相关接口
|
||||
export interface WalletTransactionListItem {
|
||||
id: number;
|
||||
agent_id: number;
|
||||
transaction_type: string;
|
||||
amount: number;
|
||||
balance_before: number;
|
||||
balance_after: number;
|
||||
frozen_balance_before: number;
|
||||
frozen_balance_after: number;
|
||||
transaction_id?: string;
|
||||
related_user_id?: number;
|
||||
remark?: string;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface WalletTransactionList {
|
||||
total: number;
|
||||
items: WalletTransactionListItem[];
|
||||
}
|
||||
|
||||
export interface GetWalletTransactionListParams {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
agent_id: number;
|
||||
transaction_type?: string;
|
||||
create_time_start?: string;
|
||||
create_time_end?: string;
|
||||
}
|
||||
|
||||
// 系统配置相关接口
|
||||
export interface SystemConfig {
|
||||
commission_safe_mode: boolean; // 佣金安全防御模式
|
||||
}
|
||||
|
||||
export interface UpdateSystemConfigReq {
|
||||
commission_safe_mode: boolean; // 佣金安全防御模式:true-冻结模式,false-直接结算模式
|
||||
}
|
||||
|
||||
export interface UpdateSystemConfigResp {
|
||||
success: boolean; // 是否成功
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理列表数据
|
||||
* @param params 查询参数
|
||||
*/
|
||||
async function getAgentList(params: AgentApi.GetAgentListParams) {
|
||||
return requestClient.get<AgentApi.AgentList>('/agent/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理推广链接列表
|
||||
*/
|
||||
async function getAgentLinkList(params: AgentApi.GetAgentLinkListParams) {
|
||||
return requestClient.get<AgentApi.AgentLinkList>('/agent/agent-link/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理佣金列表
|
||||
*/
|
||||
async function getAgentCommissionList(
|
||||
params: AgentApi.GetAgentCommissionListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.AgentCommissionList>(
|
||||
'/agent/agent-commission/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新代理佣金状态
|
||||
*/
|
||||
async function updateAgentCommissionStatus(
|
||||
id: number,
|
||||
status: number,
|
||||
) {
|
||||
return requestClient.post<{ success: boolean }>(
|
||||
'/agent/agent-commission/update-status',
|
||||
{
|
||||
id,
|
||||
status,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量解冻代理佣金
|
||||
*/
|
||||
async function batchUnfreezeAgentCommission(
|
||||
agentId?: number,
|
||||
) {
|
||||
return requestClient.post<{
|
||||
success: boolean;
|
||||
count: number;
|
||||
amount: number;
|
||||
}>('/agent/agent-commission/batch-unfreeze', {
|
||||
agent_id: agentId,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理奖励列表
|
||||
*/
|
||||
async function getAgentRewardList(params: AgentApi.GetAgentRewardListParams) {
|
||||
return requestClient.get<AgentApi.AgentRewardList>(
|
||||
'/agent/agent-reward/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理提现列表
|
||||
*/
|
||||
async function getAgentWithdrawalList(
|
||||
params: AgentApi.GetAgentWithdrawalListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.AgentWithdrawalList>(
|
||||
'/agent/agent-withdrawal/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理上级抽佣列表
|
||||
*/
|
||||
async function getAgentCommissionDeductionList(
|
||||
params: AgentApi.GetAgentCommissionDeductionListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.AgentCommissionDeductionList>(
|
||||
'/agent/agent-commission-deduction/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台抽佣列表
|
||||
*/
|
||||
async function getAgentPlatformDeductionList(
|
||||
params: AgentApi.GetAgentPlatformDeductionListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.AgentPlatformDeductionList>(
|
||||
'/agent/agent-platform-deduction/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理产品配置列表
|
||||
*/
|
||||
async function getAgentProductionConfigList(
|
||||
params: AgentApi.GetAgentProductionConfigListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.AgentProductionConfigList>(
|
||||
'/agent/agent-production-config/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新代理产品配置
|
||||
*/
|
||||
async function updateAgentProductionConfig(
|
||||
params: AgentApi.UpdateAgentProductionConfigParams,
|
||||
) {
|
||||
return requestClient.post<AgentApi.UpdateAgentProductionConfigResp>(
|
||||
'/agent/agent-production-config/update',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员充值订单列表
|
||||
*/
|
||||
async function getMembershipRechargeOrderList(
|
||||
params: AgentApi.GetMembershipRechargeOrderListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.MembershipRechargeOrderList>(
|
||||
'/agent/agent-membership-recharge-order/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理会员配置列表
|
||||
*/
|
||||
async function getAgentMembershipConfigList(
|
||||
params: AgentApi.GetAgentMembershipConfigListParams,
|
||||
) {
|
||||
return requestClient.get<{
|
||||
items: AgentApi.AgentMembershipConfigListItem[];
|
||||
total: number;
|
||||
}>('/agent/agent-membership-config/list', { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新代理会员配置
|
||||
*/
|
||||
async function updateAgentMembershipConfig(
|
||||
params: AgentApi.UpdateAgentMembershipConfigParams,
|
||||
) {
|
||||
return requestClient.post<{ success: boolean }>(
|
||||
'/agent/agent-membership-config/update',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 银行卡提现审核
|
||||
*/
|
||||
export interface ReviewBankCardWithdrawalParams {
|
||||
withdrawal_id: number;
|
||||
action: 1 | 2; // 1-确认, 2-拒绝
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
async function reviewBankCardWithdrawal(
|
||||
params: ReviewBankCardWithdrawalParams,
|
||||
) {
|
||||
return requestClient.post<{ success: boolean }>(
|
||||
'/agent/agent-withdrawal/bank-card/review',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现统计数据
|
||||
*/
|
||||
async function getWithdrawalStatistics() {
|
||||
return requestClient.get<AgentApi.WithdrawalStatistics>(
|
||||
'/agent/agent-withdrawal/statistics',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理订单统计数据
|
||||
*/
|
||||
async function getAgentOrderStatistics() {
|
||||
return requestClient.get<AgentApi.AgentOrderStatistics>(
|
||||
'/agent/agent-order/statistics',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理统计数据
|
||||
*/
|
||||
async function getAgentStatistics() {
|
||||
return requestClient.get<AgentApi.AgentStatistics>(
|
||||
'/agent/statistics',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理链接产品统计数据
|
||||
*/
|
||||
async function getAgentLinkProductStatistics() {
|
||||
return requestClient.get<AgentApi.AgentLinkProductStatisticsResp>(
|
||||
'/agent/agent-link/product-statistics',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理钱包信息
|
||||
*/
|
||||
async function getAgentWallet(agentId: number) {
|
||||
return requestClient.get<AgentApi.AgentWalletInfo>(
|
||||
`/agent/wallet/${agentId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改代理钱包余额
|
||||
*/
|
||||
async function updateAgentWalletBalance(params: AgentApi.UpdateAgentWalletBalanceReq) {
|
||||
return requestClient.post<AgentApi.UpdateAgentWalletBalanceResp>(
|
||||
'/agent/wallet/update-balance',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
*/
|
||||
async function getSystemConfig() {
|
||||
return requestClient.get<AgentApi.SystemConfig>(
|
||||
'/agent/system-config',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新系统配置
|
||||
*/
|
||||
async function updateSystemConfig(params: AgentApi.UpdateSystemConfigReq) {
|
||||
return requestClient.post<AgentApi.UpdateSystemConfigResp>(
|
||||
'/agent/system-config',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理钱包流水列表
|
||||
*/
|
||||
async function getWalletTransactionList(
|
||||
params: AgentApi.GetWalletTransactionListParams,
|
||||
) {
|
||||
return requestClient.get<AgentApi.WalletTransactionList>(
|
||||
'/agent/wallet-transaction/list',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export {
|
||||
batchUnfreezeAgentCommission,
|
||||
getAgentCommissionDeductionList,
|
||||
getAgentCommissionList,
|
||||
getAgentLinkList,
|
||||
getAgentLinkProductStatistics,
|
||||
getAgentList,
|
||||
getAgentMembershipConfigList,
|
||||
getAgentOrderStatistics,
|
||||
getAgentPlatformDeductionList,
|
||||
getAgentProductionConfigList,
|
||||
getAgentRewardList,
|
||||
getAgentStatistics,
|
||||
getAgentWallet,
|
||||
getAgentWithdrawalList,
|
||||
getMembershipRechargeOrderList,
|
||||
getWithdrawalStatistics,
|
||||
reviewBankCardWithdrawal,
|
||||
updateAgentCommissionStatus,
|
||||
updateAgentMembershipConfig,
|
||||
updateAgentProductionConfig,
|
||||
updateAgentWalletBalance,
|
||||
getSystemConfig,
|
||||
updateSystemConfig,
|
||||
getWalletTransactionList,
|
||||
};
|
||||
1
apps/bdrp-admin-web/src/api/agent/index.ts
Normal file
1
apps/bdrp-admin-web/src/api/agent/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './agent';
|
||||
52
apps/bdrp-admin-web/src/api/core/auth.ts
Normal file
52
apps/bdrp-admin-web/src/api/core/auth.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { baseRequestClient, requestClient } from '#/api/request';
|
||||
|
||||
export namespace AuthApi {
|
||||
/** 登录接口参数 */
|
||||
export interface LoginParams {
|
||||
password?: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
/** 登录接口返回值 */
|
||||
export interface LoginResult {
|
||||
access_token: string;
|
||||
}
|
||||
|
||||
export interface RefreshTokenResult {
|
||||
data: string;
|
||||
status: number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录
|
||||
*/
|
||||
export async function loginApi(data: AuthApi.LoginParams) {
|
||||
return requestClient.post<AuthApi.LoginResult>('/auth/login', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新accessToken
|
||||
*/
|
||||
export async function refreshTokenApi() {
|
||||
return baseRequestClient.post<AuthApi.RefreshTokenResult>('/auth/refresh', {
|
||||
withCredentials: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出登录
|
||||
*/
|
||||
export async function logoutApi() {
|
||||
return baseRequestClient.post('/auth/logout', {
|
||||
withCredentials: true,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户权限码
|
||||
*/
|
||||
export async function getAccessCodesApi() {
|
||||
// return requestClient.get<string[]>('/auth/codes');
|
||||
return [];
|
||||
}
|
||||
3
apps/bdrp-admin-web/src/api/core/index.ts
Normal file
3
apps/bdrp-admin-web/src/api/core/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './auth';
|
||||
export * from './menu';
|
||||
export * from './user';
|
||||
10
apps/bdrp-admin-web/src/api/core/menu.ts
Normal file
10
apps/bdrp-admin-web/src/api/core/menu.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { RouteRecordStringComponent } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取用户所有菜单
|
||||
*/
|
||||
export async function getAllMenusApi() {
|
||||
return requestClient.get<RouteRecordStringComponent[]>('/menu/all');
|
||||
}
|
||||
10
apps/bdrp-admin-web/src/api/core/user.ts
Normal file
10
apps/bdrp-admin-web/src/api/core/user.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { UserInfo } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
*/
|
||||
export async function getUserInfoApi() {
|
||||
return requestClient.get<UserInfo>('/user/info');
|
||||
}
|
||||
37
apps/bdrp-admin-web/src/api/index.ts
Normal file
37
apps/bdrp-admin-web/src/api/index.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
export * from './agent';
|
||||
export * from './core';
|
||||
export * from './notification';
|
||||
export * from './order';
|
||||
export * from './platform-user';
|
||||
export * from './product-manage';
|
||||
export * from './promotion';
|
||||
export * from './system';
|
||||
export interface ApiResponse<T = any> {
|
||||
code: number;
|
||||
data: T;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface PageResult<T> {
|
||||
items: T[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
$http: {
|
||||
delete<T = any>(url: string, config?: any): Promise<ApiResponse<T>>;
|
||||
get<T = any>(url: string, config?: any): Promise<ApiResponse<T>>;
|
||||
post<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: any,
|
||||
): Promise<ApiResponse<T>>;
|
||||
put<T = any>(
|
||||
url: string,
|
||||
data?: any,
|
||||
config?: any,
|
||||
): Promise<ApiResponse<T>>;
|
||||
};
|
||||
}
|
||||
}
|
||||
105
apps/bdrp-admin-web/src/api/notification/index.ts
Normal file
105
apps/bdrp-admin-web/src/api/notification/index.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace NotificationApi {
|
||||
export interface NotificationItem {
|
||||
id: number;
|
||||
title: string;
|
||||
content?: string;
|
||||
notification_page: string;
|
||||
start_date: string;
|
||||
start_time: string;
|
||||
end_date: string;
|
||||
end_time: string;
|
||||
status: number;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface NotificationList {
|
||||
total: number;
|
||||
items: NotificationItem[];
|
||||
}
|
||||
|
||||
export interface CreateNotificationRequest {
|
||||
title: string;
|
||||
content: string;
|
||||
notification_page: string;
|
||||
start_date: string;
|
||||
start_time: string;
|
||||
end_date: string;
|
||||
end_time: string;
|
||||
status: 0 | 1;
|
||||
}
|
||||
|
||||
export interface CreateNotificationResponse {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface UpdateNotificationRequest
|
||||
extends Partial<CreateNotificationRequest> {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export interface UpdateNotificationResponse {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface DeleteNotificationResponse {
|
||||
success: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取通知列表
|
||||
*/
|
||||
async function getNotificationList(params: Recordable<any>) {
|
||||
return requestClient.get<NotificationApi.NotificationList>(
|
||||
'/notification/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通知
|
||||
*/
|
||||
async function createNotification(
|
||||
data: NotificationApi.CreateNotificationRequest,
|
||||
) {
|
||||
return requestClient.post<NotificationApi.CreateNotificationResponse>(
|
||||
'/notification/create',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通知
|
||||
*/
|
||||
async function updateNotification(
|
||||
id: number,
|
||||
data: NotificationApi.UpdateNotificationRequest,
|
||||
) {
|
||||
return requestClient.put<NotificationApi.UpdateNotificationResponse>(
|
||||
`/notification/update/${id}`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除通知
|
||||
*/
|
||||
async function deleteNotification(id: number) {
|
||||
return requestClient.delete<NotificationApi.DeleteNotificationResponse>(
|
||||
`/notification/delete/${id}`,
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
createNotification,
|
||||
deleteNotification,
|
||||
getNotificationList,
|
||||
updateNotification,
|
||||
};
|
||||
1
apps/bdrp-admin-web/src/api/order/index.ts
Normal file
1
apps/bdrp-admin-web/src/api/order/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './order';
|
||||
28
apps/bdrp-admin-web/src/api/order/order-statistics.ts
Normal file
28
apps/bdrp-admin-web/src/api/order/order-statistics.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace OrderStatisticsApi {
|
||||
// 订单统计数据项
|
||||
export interface OrderStatisticsItem {
|
||||
date: string; // 日期
|
||||
count: number; // 订单数量
|
||||
amount: number; // 订单金额
|
||||
}
|
||||
|
||||
// 订单统计响应
|
||||
export interface OrderStatisticsResponse {
|
||||
items: OrderStatisticsItem[];
|
||||
}
|
||||
|
||||
// 时间维度类型
|
||||
export type TimeDimension = 'day' | 'month' | 'year' | 'all';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单统计数据
|
||||
* @param dimension 时间维度:day-日(当月1号到今天),month-月(今年1月到当月),year-年(过去5年),all-全部(按日统计)
|
||||
*/
|
||||
export function getOrderStatistics(dimension: OrderStatisticsApi.TimeDimension) {
|
||||
return requestClient.get<OrderStatisticsApi.OrderStatisticsResponse>('/order/statistics', {
|
||||
params: { dimension }
|
||||
});
|
||||
}
|
||||
102
apps/bdrp-admin-web/src/api/order/order.ts
Normal file
102
apps/bdrp-admin-web/src/api/order/order.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace OrderApi {
|
||||
export interface Order {
|
||||
id: number;
|
||||
order_no: string;
|
||||
platform_order_id: string;
|
||||
product_name: string;
|
||||
payment_platform: 'alipay' | 'appleiap' | 'wechat';
|
||||
payment_scene: 'app' | 'h5' | 'mini_program' | 'public_account';
|
||||
amount: number;
|
||||
sales_cost: number;
|
||||
status: 'closed' | 'failed' | 'paid' | 'pending' | 'refunded';
|
||||
query_state: 'cleaned' | 'failed' | 'pending' | 'processing' | 'success';
|
||||
create_time: string;
|
||||
pay_time: null | string;
|
||||
refund_time: null | string;
|
||||
is_promotion: 0 | 1;
|
||||
}
|
||||
|
||||
export interface OrderList {
|
||||
total: number;
|
||||
items: Order[];
|
||||
}
|
||||
|
||||
export interface RefundOrderRequest {
|
||||
refund_amount: number;
|
||||
refund_reason: string;
|
||||
}
|
||||
|
||||
export interface RefundOrderResponse {
|
||||
status: string;
|
||||
refund_no: string;
|
||||
amount: number;
|
||||
}
|
||||
|
||||
// 退款统计数据
|
||||
export interface RefundStatistics {
|
||||
total_refund_amount: number;
|
||||
today_refund_amount: number;
|
||||
}
|
||||
|
||||
// 收入统计数据
|
||||
export interface IncomeStatistics {
|
||||
total_revenue_amount: number;
|
||||
today_revenue_amount: number;
|
||||
total_profit_amount: number;
|
||||
today_profit_amount: number;
|
||||
}
|
||||
|
||||
// 订单来源统计数据
|
||||
export interface OrderSourceStatistics {
|
||||
product_name: string;
|
||||
order_count: number;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单列表数据
|
||||
*/
|
||||
async function getOrderList(params: Recordable<any>) {
|
||||
return requestClient.get<OrderApi.OrderList>('/order/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单退款
|
||||
* @param id 订单 ID
|
||||
* @param data 退款请求数据
|
||||
*/
|
||||
async function refundOrder(id: number, data: OrderApi.RefundOrderRequest) {
|
||||
return requestClient.post<OrderApi.RefundOrderResponse>(
|
||||
`/order/refund/${id}`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取退款统计数据
|
||||
*/
|
||||
async function getRefundStatistics() {
|
||||
return requestClient.get<OrderApi.RefundStatistics>('/order/refund-statistics');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收入统计数据
|
||||
*/
|
||||
async function getIncomeStatistics() {
|
||||
return requestClient.get<OrderApi.IncomeStatistics>('/order/revenue-statistics');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单来源统计数据
|
||||
*/
|
||||
async function getOrderSourceStatistics() {
|
||||
return requestClient.get<{ items: OrderApi.OrderSourceStatistics[] }>('/order/source-statistics');
|
||||
}
|
||||
|
||||
export { getOrderList, refundOrder, getRefundStatistics, getIncomeStatistics, getOrderSourceStatistics };
|
||||
184
apps/bdrp-admin-web/src/api/order/query.ts
Normal file
184
apps/bdrp-admin-web/src/api/order/query.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace OrderQueryApi {
|
||||
export interface QueryItem {
|
||||
feature: Recordable<any>;
|
||||
data: Recordable<any>;
|
||||
}
|
||||
|
||||
export interface QueryDetail {
|
||||
id: number;
|
||||
order_id: number;
|
||||
user_id: number;
|
||||
product_name: string;
|
||||
query_params: Recordable<any>;
|
||||
query_data: QueryItem[];
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
query_state: string;
|
||||
}
|
||||
|
||||
export interface GetQueryDetailRequest {
|
||||
order_id: number;
|
||||
}
|
||||
|
||||
export interface GetQueryDetailResponse {
|
||||
id: number;
|
||||
order_id: number;
|
||||
user_id: number;
|
||||
product_name: string;
|
||||
query_params: Recordable<any>;
|
||||
query_data: QueryItem[];
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
query_state: string;
|
||||
}
|
||||
|
||||
// 清理日志相关接口定义
|
||||
export interface GetQueryCleanupLogListRequest {
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
status?: number;
|
||||
start_time?: string;
|
||||
end_time?: string;
|
||||
}
|
||||
|
||||
export interface QueryCleanupLogItem {
|
||||
id: number;
|
||||
cleanup_time: string;
|
||||
cleanup_before: string;
|
||||
status: number;
|
||||
affected_rows: number;
|
||||
error_msg: string;
|
||||
remark: string;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface GetQueryCleanupLogListResponse {
|
||||
total: number;
|
||||
items: QueryCleanupLogItem[];
|
||||
}
|
||||
|
||||
// 清理详情相关接口定义
|
||||
export interface GetQueryCleanupDetailListRequest {
|
||||
log_id: number;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
}
|
||||
|
||||
export interface QueryCleanupDetailItem {
|
||||
id: number;
|
||||
cleanup_log_id: number;
|
||||
query_id: number;
|
||||
order_id: number;
|
||||
user_id: number;
|
||||
product_id: number;
|
||||
query_state: string;
|
||||
create_time_old: string;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface GetQueryCleanupDetailListResponse {
|
||||
total: number;
|
||||
items: QueryCleanupDetailItem[];
|
||||
}
|
||||
|
||||
// 清理配置相关接口定义
|
||||
export interface GetQueryCleanupConfigListRequest {
|
||||
status?: number;
|
||||
}
|
||||
|
||||
export interface QueryCleanupConfigItem {
|
||||
id: number;
|
||||
config_key: string;
|
||||
config_value: string;
|
||||
config_desc: string;
|
||||
status: number;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface GetQueryCleanupConfigListResponse {
|
||||
items: QueryCleanupConfigItem[];
|
||||
}
|
||||
|
||||
export interface UpdateQueryCleanupConfigRequest {
|
||||
id: number;
|
||||
config_value: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export interface UpdateQueryCleanupConfigResponse {
|
||||
success: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单查询详情
|
||||
* @param orderId 订单ID
|
||||
*/
|
||||
async function getOrderQueryDetail(orderId: number) {
|
||||
return requestClient.get<OrderQueryApi.GetQueryDetailResponse>(
|
||||
`/query/detail/${orderId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取清理日志列表
|
||||
*/
|
||||
async function getQueryCleanupLogList(
|
||||
params: OrderQueryApi.GetQueryCleanupLogListRequest,
|
||||
) {
|
||||
return requestClient.get<OrderQueryApi.GetQueryCleanupLogListResponse>(
|
||||
'/query/cleanup/logs',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取清理详情列表
|
||||
* @param logId 清理日志ID
|
||||
*/
|
||||
async function getQueryCleanupDetailList(
|
||||
logId: number,
|
||||
params: Omit<OrderQueryApi.GetQueryCleanupDetailListRequest, 'log_id'>,
|
||||
) {
|
||||
return requestClient.get<OrderQueryApi.GetQueryCleanupDetailListResponse>(
|
||||
`/query/cleanup/details/${logId}`,
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取清理配置列表
|
||||
*/
|
||||
async function getQueryCleanupConfigList(
|
||||
params?: OrderQueryApi.GetQueryCleanupConfigListRequest,
|
||||
) {
|
||||
return requestClient.get<OrderQueryApi.GetQueryCleanupConfigListResponse>(
|
||||
'/query/cleanup/configs',
|
||||
{ params },
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新清理配置
|
||||
*/
|
||||
async function updateQueryCleanupConfig(
|
||||
data: OrderQueryApi.UpdateQueryCleanupConfigRequest,
|
||||
) {
|
||||
return requestClient.put<OrderQueryApi.UpdateQueryCleanupConfigResponse>(
|
||||
'/query/cleanup/config',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
getOrderQueryDetail,
|
||||
getQueryCleanupConfigList,
|
||||
getQueryCleanupDetailList,
|
||||
getQueryCleanupLogList,
|
||||
updateQueryCleanupConfig,
|
||||
};
|
||||
55
apps/bdrp-admin-web/src/api/platform-user/index.ts
Normal file
55
apps/bdrp-admin-web/src/api/platform-user/index.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace PlatformUserApi {
|
||||
export interface PlatformUserItem {
|
||||
id: number;
|
||||
mobile: string;
|
||||
nickname: string;
|
||||
info: string;
|
||||
inside: number;
|
||||
disable: number; // 0 可用 1 禁用
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface PlatformUserList {
|
||||
total: number;
|
||||
items: PlatformUserItem[];
|
||||
}
|
||||
|
||||
export interface UpdatePlatformUserRequest {
|
||||
mobile?: string;
|
||||
nickname?: string;
|
||||
info?: string;
|
||||
inside?: number;
|
||||
disable?: number; // 0 可用 1 禁用
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台用户列表数据
|
||||
*/
|
||||
async function getPlatformUserList(params: Recordable<any>) {
|
||||
return requestClient.get<PlatformUserApi.PlatformUserList>(
|
||||
'/platform_user/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新平台用户
|
||||
* @param id 用户 ID
|
||||
* @param data 用户数据
|
||||
*/
|
||||
async function updatePlatformUser(
|
||||
id: number,
|
||||
data: PlatformUserApi.UpdatePlatformUserRequest,
|
||||
) {
|
||||
return requestClient.put(`/platform_user/update/${id}`, data);
|
||||
}
|
||||
|
||||
export { getPlatformUserList, updatePlatformUser };
|
||||
140
apps/bdrp-admin-web/src/api/product-manage/feature.ts
Normal file
140
apps/bdrp-admin-web/src/api/product-manage/feature.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace FeatureApi {
|
||||
export interface FeatureItem {
|
||||
id: number;
|
||||
api_id: string;
|
||||
name: string;
|
||||
cost_price: number;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface FeatureList {
|
||||
total: number;
|
||||
items: FeatureItem[];
|
||||
}
|
||||
|
||||
export interface CreateFeatureRequest {
|
||||
api_id: string;
|
||||
name: string;
|
||||
cost_price: number;
|
||||
}
|
||||
|
||||
export interface UpdateFeatureRequest {
|
||||
api_id?: string;
|
||||
name?: string;
|
||||
cost_price?: number;
|
||||
}
|
||||
|
||||
export interface FeatureExampleItem {
|
||||
id: number;
|
||||
feature_id: number;
|
||||
api_id: string;
|
||||
data: string;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface ConfigFeatureExampleRequest {
|
||||
feature_id: number;
|
||||
data: string;
|
||||
}
|
||||
|
||||
export interface ConfigFeatureExampleResponse {
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
export interface GetFeatureExampleRequest {
|
||||
feature_id: number;
|
||||
}
|
||||
|
||||
export interface GetFeatureExampleResponse {
|
||||
id: number;
|
||||
feature_id: number;
|
||||
api_id: string;
|
||||
data: string;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块列表数据
|
||||
*/
|
||||
async function getFeatureList(params: Recordable<any>) {
|
||||
return requestClient.get<FeatureApi.FeatureList>('/feature/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取模块详情
|
||||
* @param id 模块ID
|
||||
*/
|
||||
async function getFeatureDetail(id: number) {
|
||||
return requestClient.get<FeatureApi.FeatureItem>(`/feature/detail/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模块
|
||||
* @param data 模块数据
|
||||
*/
|
||||
async function createFeature(data: FeatureApi.CreateFeatureRequest) {
|
||||
return requestClient.post<{ id: number }>('/feature/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新模块
|
||||
* @param id 模块ID
|
||||
* @param data 模块数据
|
||||
*/
|
||||
async function updateFeature(
|
||||
id: number,
|
||||
data: FeatureApi.UpdateFeatureRequest,
|
||||
) {
|
||||
return requestClient.put<{ success: boolean }>(`/feature/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除模块
|
||||
* @param id 模块ID
|
||||
*/
|
||||
async function deleteFeature(id: number) {
|
||||
return requestClient.delete<{ success: boolean }>(`/feature/delete/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置功能示例数据
|
||||
* @param data 示例数据配置
|
||||
*/
|
||||
async function configFeatureExample(
|
||||
data: FeatureApi.ConfigFeatureExampleRequest,
|
||||
) {
|
||||
return requestClient.post<FeatureApi.ConfigFeatureExampleResponse>(
|
||||
'/feature/config-example',
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取功能示例数据
|
||||
* @param featureId 功能ID
|
||||
*/
|
||||
async function getFeatureExample(featureId: number) {
|
||||
return requestClient.get<FeatureApi.GetFeatureExampleResponse>(
|
||||
`/feature/example/${featureId}`,
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
configFeatureExample,
|
||||
createFeature,
|
||||
deleteFeature,
|
||||
getFeatureDetail,
|
||||
getFeatureExample,
|
||||
getFeatureList,
|
||||
updateFeature,
|
||||
};
|
||||
2
apps/bdrp-admin-web/src/api/product-manage/index.ts
Normal file
2
apps/bdrp-admin-web/src/api/product-manage/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './feature';
|
||||
export * from './product';
|
||||
148
apps/bdrp-admin-web/src/api/product-manage/product.ts
Normal file
148
apps/bdrp-admin-web/src/api/product-manage/product.ts
Normal file
@@ -0,0 +1,148 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace ProductApi {
|
||||
export interface ProductItem {
|
||||
id: number;
|
||||
product_name: string;
|
||||
product_en: string;
|
||||
description: string;
|
||||
notes: string;
|
||||
cost_price: number;
|
||||
sell_price: number;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface ProductList {
|
||||
total: number;
|
||||
items: ProductItem[];
|
||||
}
|
||||
|
||||
export interface CreateProductRequest {
|
||||
product_name: string;
|
||||
product_en: string;
|
||||
description: string;
|
||||
notes?: string;
|
||||
cost_price: number;
|
||||
sell_price: number;
|
||||
}
|
||||
|
||||
export interface UpdateProductRequest {
|
||||
product_name?: string;
|
||||
product_en?: string;
|
||||
description?: string;
|
||||
notes?: string;
|
||||
cost_price?: number;
|
||||
sell_price?: number;
|
||||
}
|
||||
|
||||
export interface ProductFeatureListItem {
|
||||
id: number;
|
||||
product_id: number;
|
||||
feature_id: number;
|
||||
api_id: string;
|
||||
name: string;
|
||||
sort: number;
|
||||
enable: number;
|
||||
is_important: number;
|
||||
create_time: string;
|
||||
update_time: string;
|
||||
}
|
||||
|
||||
export interface ProductFeatureItem {
|
||||
feature_id: number;
|
||||
sort: number;
|
||||
enable: number;
|
||||
is_important: number;
|
||||
}
|
||||
|
||||
export interface UpdateProductFeaturesRequest {
|
||||
features: ProductFeatureItem[];
|
||||
}
|
||||
|
||||
export interface UpdateProductFeaturesResponse {
|
||||
success: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品列表数据
|
||||
*/
|
||||
async function getProductList(params: Recordable<any>) {
|
||||
return requestClient.get<ProductApi.ProductList>('/product/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品详情
|
||||
* @param id 产品ID
|
||||
*/
|
||||
async function getProductDetail(id: number) {
|
||||
return requestClient.get<ProductApi.ProductItem>(`/product/detail/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建产品
|
||||
* @param data 产品数据
|
||||
*/
|
||||
async function createProduct(data: ProductApi.CreateProductRequest) {
|
||||
return requestClient.post<{ id: number }>('/product/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新产品
|
||||
* @param id 产品ID
|
||||
* @param data 产品数据
|
||||
*/
|
||||
async function updateProduct(
|
||||
id: number,
|
||||
data: ProductApi.UpdateProductRequest,
|
||||
) {
|
||||
return requestClient.put<{ success: boolean }>(`/product/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除产品
|
||||
* @param id 产品ID
|
||||
*/
|
||||
async function deleteProduct(id: number) {
|
||||
return requestClient.delete<{ success: boolean }>(`/product/delete/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取产品功能列表
|
||||
* @param productId 产品ID
|
||||
*/
|
||||
async function getProductFeatureList(productId: number) {
|
||||
return requestClient.get<ProductApi.ProductFeatureListItem[]>(
|
||||
`/product/feature/list/${productId}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新产品功能关联
|
||||
* @param productId 产品ID
|
||||
* @param data 功能列表数据
|
||||
*/
|
||||
async function updateProductFeatures(
|
||||
productId: number,
|
||||
data: ProductApi.UpdateProductFeaturesRequest,
|
||||
) {
|
||||
return requestClient.put<{ success: boolean }>(
|
||||
`/product/feature/update/${productId}`,
|
||||
data,
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
createProduct,
|
||||
deleteProduct,
|
||||
getProductDetail,
|
||||
getProductFeatureList,
|
||||
getProductList,
|
||||
updateProduct,
|
||||
updateProductFeatures,
|
||||
};
|
||||
45
apps/bdrp-admin-web/src/api/promotion/analytics.ts
Normal file
45
apps/bdrp-admin-web/src/api/promotion/analytics.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace PromotionAnalyticsApi {
|
||||
export interface OverviewData {
|
||||
today_click_count: number;
|
||||
today_pay_count: number;
|
||||
today_pay_amount: number;
|
||||
total_click_count: number;
|
||||
total_pay_count: number;
|
||||
total_pay_amount: number;
|
||||
}
|
||||
|
||||
export interface TrendData {
|
||||
id: number;
|
||||
link_id: number;
|
||||
pay_amount: number;
|
||||
click_count: number;
|
||||
pay_count: number;
|
||||
stats_date: string;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推广数据概览
|
||||
*/
|
||||
async function statsTotal() {
|
||||
return requestClient.get<PromotionAnalyticsApi.OverviewData>(
|
||||
'/promotion/stats/total',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推广数据趋势
|
||||
* @param params 日期范围参数
|
||||
*/
|
||||
async function statsHistory(params: { end_date: string; start_date: string }) {
|
||||
return requestClient.get<PromotionAnalyticsApi.TrendData[]>(
|
||||
'/promotion/stats/history',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export { statsHistory, statsTotal };
|
||||
2
apps/bdrp-admin-web/src/api/promotion/index.ts
Normal file
2
apps/bdrp-admin-web/src/api/promotion/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './analytics';
|
||||
export * from './link';
|
||||
67
apps/bdrp-admin-web/src/api/promotion/link.ts
Normal file
67
apps/bdrp-admin-web/src/api/promotion/link.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace PromotionLinkApi {
|
||||
export interface PromotionLinkItem {
|
||||
id: number;
|
||||
name: string;
|
||||
url: string;
|
||||
create_time: string;
|
||||
}
|
||||
|
||||
export interface PromotionLink {
|
||||
total: number;
|
||||
items: PromotionLinkItem[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推广链接列表数据
|
||||
*/
|
||||
async function getPromotionLinkList(params: Recordable<any>) {
|
||||
return requestClient.get<PromotionLinkApi.PromotionLink>(
|
||||
'/promotion/link/list',
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建推广链接
|
||||
* @param data 推广链接数据
|
||||
*/
|
||||
async function createPromotionLink(
|
||||
data: Omit<PromotionLinkApi.PromotionLinkItem, 'id'>,
|
||||
) {
|
||||
return requestClient.post('/promotion/link/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新推广链接
|
||||
*
|
||||
* @param id 推广链接 ID
|
||||
* @param data 推广链接数据
|
||||
*/
|
||||
async function updatePromotionLink(
|
||||
id: string,
|
||||
data: Omit<PromotionLinkApi.PromotionLinkItem, 'id'>,
|
||||
) {
|
||||
return requestClient.put(`/promotion/link/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除推广链接
|
||||
* @param id 推广链接 ID
|
||||
*/
|
||||
async function deletePromotionLink(id: string) {
|
||||
return requestClient.delete(`/promotion/link/delete/${id}`);
|
||||
}
|
||||
|
||||
export {
|
||||
createPromotionLink,
|
||||
deletePromotionLink,
|
||||
getPromotionLinkList,
|
||||
updatePromotionLink,
|
||||
};
|
||||
113
apps/bdrp-admin-web/src/api/request.ts
Normal file
113
apps/bdrp-admin-web/src/api/request.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 该文件可自行根据业务逻辑进行调整
|
||||
*/
|
||||
import type { RequestClientOptions } from '@vben/request';
|
||||
|
||||
import { useAppConfig } from '@vben/hooks';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import {
|
||||
authenticateResponseInterceptor,
|
||||
defaultResponseInterceptor,
|
||||
errorMessageResponseInterceptor,
|
||||
RequestClient,
|
||||
} from '@vben/request';
|
||||
import { useAccessStore } from '@vben/stores';
|
||||
|
||||
import { message } from 'ant-design-vue';
|
||||
|
||||
import { useAuthStore } from '#/store';
|
||||
|
||||
import { refreshTokenApi } from './core';
|
||||
|
||||
const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD);
|
||||
|
||||
function createRequestClient(baseURL: string, options?: RequestClientOptions) {
|
||||
const client = new RequestClient({
|
||||
...options,
|
||||
baseURL,
|
||||
});
|
||||
|
||||
/**
|
||||
* 重新认证逻辑
|
||||
*/
|
||||
async function doReAuthenticate() {
|
||||
console.warn('Access token or refresh token is invalid or expired. ');
|
||||
const accessStore = useAccessStore();
|
||||
const authStore = useAuthStore();
|
||||
accessStore.setAccessToken(null);
|
||||
if (
|
||||
preferences.app.loginExpiredMode === 'modal' &&
|
||||
accessStore.isAccessChecked
|
||||
) {
|
||||
accessStore.setLoginExpired(true);
|
||||
} else {
|
||||
await authStore.logout();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token逻辑
|
||||
*/
|
||||
async function doRefreshToken() {
|
||||
const accessStore = useAccessStore();
|
||||
const resp = await refreshTokenApi();
|
||||
const newToken = resp.data;
|
||||
accessStore.setAccessToken(newToken);
|
||||
return newToken;
|
||||
}
|
||||
|
||||
function formatToken(token: null | string) {
|
||||
return token ? `Bearer ${token}` : null;
|
||||
}
|
||||
|
||||
// 请求头处理
|
||||
client.addRequestInterceptor({
|
||||
fulfilled: async (config) => {
|
||||
const accessStore = useAccessStore();
|
||||
|
||||
config.headers.Authorization = formatToken(accessStore.accessToken);
|
||||
config.headers['Accept-Language'] = preferences.app.locale;
|
||||
return config;
|
||||
},
|
||||
});
|
||||
|
||||
// 处理返回的响应数据格式
|
||||
client.addResponseInterceptor(
|
||||
defaultResponseInterceptor({
|
||||
codeField: 'code',
|
||||
dataField: 'data',
|
||||
successCode: 200,
|
||||
}),
|
||||
);
|
||||
|
||||
// token过期的处理
|
||||
client.addResponseInterceptor(
|
||||
authenticateResponseInterceptor({
|
||||
client,
|
||||
doReAuthenticate,
|
||||
doRefreshToken,
|
||||
enableRefreshToken: preferences.app.enableRefreshToken,
|
||||
formatToken,
|
||||
}),
|
||||
);
|
||||
|
||||
// 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里
|
||||
client.addResponseInterceptor(
|
||||
errorMessageResponseInterceptor((msg: string, error) => {
|
||||
// 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg
|
||||
// 当前mock接口返回的错误字段是 error 或者 message
|
||||
const responseData = error?.response?.data ?? {};
|
||||
const errorMessage = responseData?.error ?? responseData?.msg ?? '';
|
||||
// 如果没有错误信息,则会根据状态码进行提示
|
||||
message.error(errorMessage || msg);
|
||||
}),
|
||||
);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
export const requestClient = createRequestClient(apiURL, {
|
||||
responseReturn: 'data',
|
||||
});
|
||||
|
||||
export const baseRequestClient = new RequestClient({ baseURL: apiURL });
|
||||
165
apps/bdrp-admin-web/src/api/system/api.ts
Normal file
165
apps/bdrp-admin-web/src/api/system/api.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace SystemApiApi {
|
||||
export interface SystemApiItem {
|
||||
id: number;
|
||||
role_id?: number;
|
||||
api_id?: number;
|
||||
api_name: string;
|
||||
api_code: string;
|
||||
method: string;
|
||||
url: string;
|
||||
status: 0 | 1;
|
||||
description?: string;
|
||||
create_time?: string;
|
||||
update_time?: string;
|
||||
}
|
||||
|
||||
export interface SystemApi {
|
||||
list: SystemApiItem[];
|
||||
total: number;
|
||||
}
|
||||
|
||||
export interface SystemApiAllResponse {
|
||||
items: SystemApiItem[];
|
||||
}
|
||||
|
||||
export interface SystemRoleApiResponse {
|
||||
items: null | SystemApiItem[];
|
||||
}
|
||||
|
||||
export interface RoleApiItem {
|
||||
id: number;
|
||||
role_id: number;
|
||||
api_id: number;
|
||||
api_name: string;
|
||||
api_code: string;
|
||||
method: string;
|
||||
url: string;
|
||||
status: 0 | 1;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface RoleApi {
|
||||
list: RoleApiItem[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取API列表数据
|
||||
*/
|
||||
async function getApiList(params: Recordable<any>) {
|
||||
return requestClient.get<SystemApiApi.SystemApi>('/api/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取API详情
|
||||
* @param id API ID
|
||||
*/
|
||||
async function getApiDetail(id: number) {
|
||||
return requestClient.get<SystemApiApi.SystemApiItem>(`/api/detail/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建API
|
||||
* @param data API数据
|
||||
*/
|
||||
async function createApi(
|
||||
data: Omit<SystemApiApi.SystemApiItem, 'create_time' | 'id' | 'update_time'>,
|
||||
) {
|
||||
return requestClient.post('/api/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新API
|
||||
* @param id API ID
|
||||
* @param data API数据
|
||||
*/
|
||||
async function updateApi(
|
||||
id: number,
|
||||
data: Omit<SystemApiApi.SystemApiItem, 'create_time' | 'id' | 'update_time'>,
|
||||
) {
|
||||
return requestClient.put(`/api/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除API
|
||||
* @param id API ID
|
||||
*/
|
||||
async function deleteApi(id: number) {
|
||||
return requestClient.delete(`/api/delete/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新API状态
|
||||
* @param data.ids API ID数组
|
||||
* @param data.status 状态值
|
||||
*/
|
||||
async function batchUpdateApiStatus(data: { ids: number[]; status: 0 | 1 }) {
|
||||
return requestClient.put('/api/batch-update-status', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色API权限列表
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
async function getRoleApiList(roleId: number) {
|
||||
return requestClient.get<SystemApiApi.SystemRoleApiResponse>(
|
||||
`/role/${roleId}/api/list`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配角色API权限
|
||||
* @param data.api_ids API ID数组
|
||||
* @param data.role_id 角色ID
|
||||
*/
|
||||
async function assignRoleApi(data: { api_ids: number[]; role_id: number }) {
|
||||
return requestClient.post('/role/api/assign', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除角色API权限
|
||||
* @param data.api_ids API ID数组
|
||||
* @param data.role_id 角色ID
|
||||
*/
|
||||
async function removeRoleApi(data: { api_ids: number[]; role_id: number }) {
|
||||
return requestClient.post('/role/api/remove', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色API权限(全量更新)
|
||||
* @param data.api_ids API ID数组
|
||||
* @param data.role_id 角色ID
|
||||
*/
|
||||
async function updateRoleApi(data: { api_ids: number[]; role_id: number }) {
|
||||
return requestClient.put('/role/api/update', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有API列表(用于权限分配)
|
||||
* @param params.status 状态过滤
|
||||
*/
|
||||
async function getAllApiList(params?: { status?: number }) {
|
||||
return requestClient.get<SystemApiApi.SystemApiAllResponse>('/api/all', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
assignRoleApi,
|
||||
batchUpdateApiStatus,
|
||||
createApi,
|
||||
deleteApi,
|
||||
getAllApiList,
|
||||
getApiDetail,
|
||||
getApiList,
|
||||
getRoleApiList,
|
||||
removeRoleApi,
|
||||
updateApi,
|
||||
updateRoleApi,
|
||||
};
|
||||
54
apps/bdrp-admin-web/src/api/system/dept.ts
Normal file
54
apps/bdrp-admin-web/src/api/system/dept.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace SystemDeptApi {
|
||||
export interface SystemDept {
|
||||
[key: string]: any;
|
||||
children?: SystemDept[];
|
||||
id: string;
|
||||
name: string;
|
||||
remark?: string;
|
||||
status: 0 | 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门列表数据
|
||||
*/
|
||||
async function getDeptList() {
|
||||
return requestClient.get<Array<SystemDeptApi.SystemDept>>(
|
||||
'/system/dept/list',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建部门
|
||||
* @param data 部门数据
|
||||
*/
|
||||
async function createDept(
|
||||
data: Omit<SystemDeptApi.SystemDept, 'children' | 'id'>,
|
||||
) {
|
||||
return requestClient.post('/system/dept', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新部门
|
||||
*
|
||||
* @param id 部门 ID
|
||||
* @param data 部门数据
|
||||
*/
|
||||
async function updateDept(
|
||||
id: string,
|
||||
data: Omit<SystemDeptApi.SystemDept, 'children' | 'id'>,
|
||||
) {
|
||||
return requestClient.put(`/system/dept/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除部门
|
||||
* @param id 部门 ID
|
||||
*/
|
||||
async function deleteDept(id: string) {
|
||||
return requestClient.delete(`/system/dept/${id}`);
|
||||
}
|
||||
|
||||
export { createDept, deleteDept, getDeptList, updateDept };
|
||||
5
apps/bdrp-admin-web/src/api/system/index.ts
Normal file
5
apps/bdrp-admin-web/src/api/system/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export * from './api';
|
||||
export * from './dept';
|
||||
export * from './menu';
|
||||
export * from './role';
|
||||
export * from './user';
|
||||
156
apps/bdrp-admin-web/src/api/system/menu.ts
Normal file
156
apps/bdrp-admin-web/src/api/system/menu.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace SystemMenuApi {
|
||||
/** 徽标颜色集合 */
|
||||
export const BadgeVariants = [
|
||||
'default',
|
||||
'destructive',
|
||||
'primary',
|
||||
'success',
|
||||
'warning',
|
||||
] as const;
|
||||
/** 徽标类型集合 */
|
||||
export const BadgeTypes = ['dot', 'normal'] as const;
|
||||
/** 菜单类型集合 */
|
||||
export const MenuTypes = [
|
||||
'catalog',
|
||||
'menu',
|
||||
'embedded',
|
||||
'link',
|
||||
'button',
|
||||
] as const;
|
||||
/** 系统菜单 */
|
||||
export interface SystemMenu {
|
||||
[key: string]: any;
|
||||
/** 后端权限标识 */
|
||||
authCode: string;
|
||||
/** 子级 */
|
||||
children?: SystemMenu[];
|
||||
/** 组件 */
|
||||
component?: string;
|
||||
/** 菜单ID */
|
||||
id: string;
|
||||
/** 菜单元数据 */
|
||||
meta?: {
|
||||
/** 激活时显示的图标 */
|
||||
activeIcon?: string;
|
||||
/** 作为路由时,需要激活的菜单的Path */
|
||||
activePath?: string;
|
||||
/** 固定在标签栏 */
|
||||
affixTab?: boolean;
|
||||
/** 在标签栏固定的顺序 */
|
||||
affixTabOrder?: number;
|
||||
/** 徽标内容(当徽标类型为normal时有效) */
|
||||
badge?: string;
|
||||
/** 徽标类型 */
|
||||
badgeType?: (typeof BadgeTypes)[number];
|
||||
/** 徽标颜色 */
|
||||
badgeVariants?: (typeof BadgeVariants)[number];
|
||||
/** 在菜单中隐藏下级 */
|
||||
hideChildrenInMenu?: boolean;
|
||||
/** 在面包屑中隐藏 */
|
||||
hideInBreadcrumb?: boolean;
|
||||
/** 在菜单中隐藏 */
|
||||
hideInMenu?: boolean;
|
||||
/** 在标签栏中隐藏 */
|
||||
hideInTab?: boolean;
|
||||
/** 菜单图标 */
|
||||
icon?: string;
|
||||
/** 内嵌Iframe的URL */
|
||||
iframeSrc?: string;
|
||||
/** 是否缓存页面 */
|
||||
keepAlive?: boolean;
|
||||
/** 外链页面的URL */
|
||||
link?: string;
|
||||
/** 同一个路由最大打开的标签数 */
|
||||
maxNumOfOpenTab?: number;
|
||||
/** 无需基础布局 */
|
||||
noBasicLayout?: boolean;
|
||||
/** 是否在新窗口打开 */
|
||||
openInNewWindow?: boolean;
|
||||
/** 菜单排序 */
|
||||
order?: number;
|
||||
/** 额外的路由参数 */
|
||||
query?: Recordable<any>;
|
||||
/** 菜单标题 */
|
||||
title?: string;
|
||||
};
|
||||
/** 菜单名称 */
|
||||
name: string;
|
||||
/** 路由路径 */
|
||||
path: string;
|
||||
/** 父级ID */
|
||||
pid: string;
|
||||
/** 重定向 */
|
||||
redirect?: string;
|
||||
/** 菜单类型 */
|
||||
type: (typeof MenuTypes)[number];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取菜单数据列表
|
||||
*/
|
||||
async function getMenuList() {
|
||||
return requestClient.get<Array<SystemMenuApi.SystemMenu>>('/menu/list');
|
||||
}
|
||||
|
||||
async function isMenuNameExists(
|
||||
name: string,
|
||||
id?: SystemMenuApi.SystemMenu['id'],
|
||||
) {
|
||||
return requestClient.get<boolean>('/menu/name-exists', {
|
||||
params: { id, name },
|
||||
});
|
||||
}
|
||||
|
||||
async function isMenuPathExists(
|
||||
path: string,
|
||||
id?: SystemMenuApi.SystemMenu['id'],
|
||||
) {
|
||||
return requestClient.get<boolean>('/menu/path-exists', {
|
||||
params: { id, path },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建菜单
|
||||
* @param data 菜单数据
|
||||
*/
|
||||
async function createMenu(
|
||||
data: Omit<SystemMenuApi.SystemMenu, 'children' | 'id'>,
|
||||
) {
|
||||
return requestClient.post('/menu/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新菜单
|
||||
*
|
||||
* @param id 菜单 ID
|
||||
* @param data 菜单数据
|
||||
*/
|
||||
async function updateMenu(
|
||||
id: string,
|
||||
data: Omit<SystemMenuApi.SystemMenu, 'children' | 'id'>,
|
||||
) {
|
||||
return requestClient.put(`/menu/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除菜单
|
||||
* @param id 菜单 ID
|
||||
*/
|
||||
async function deleteMenu(id: string) {
|
||||
return requestClient.delete(`/menu/delete/${id}`);
|
||||
}
|
||||
|
||||
export {
|
||||
createMenu,
|
||||
deleteMenu,
|
||||
getMenuList,
|
||||
isMenuNameExists,
|
||||
isMenuPathExists,
|
||||
updateMenu,
|
||||
};
|
||||
61
apps/bdrp-admin-web/src/api/system/role.ts
Normal file
61
apps/bdrp-admin-web/src/api/system/role.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace SystemRoleApi {
|
||||
export interface SystemRoleItem {
|
||||
id: number;
|
||||
role_name: string;
|
||||
role_code: string;
|
||||
description?: string;
|
||||
status: 0 | 1;
|
||||
sort: number;
|
||||
create_time: string;
|
||||
menu_ids: number[];
|
||||
}
|
||||
|
||||
export interface SystemRole {
|
||||
total: number;
|
||||
items: SystemRoleItem[];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色列表数据
|
||||
*/
|
||||
async function getRoleList(params: Recordable<any>) {
|
||||
return requestClient.get<SystemRoleApi.SystemRole>('/role/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
* @param data 角色数据
|
||||
*/
|
||||
async function createRole(data: Omit<SystemRoleApi.SystemRoleItem, 'id'>) {
|
||||
return requestClient.post('/role/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
*
|
||||
* @param id 角色 ID
|
||||
* @param data 角色数据
|
||||
*/
|
||||
async function updateRole(
|
||||
id: number,
|
||||
data: Omit<SystemRoleApi.SystemRoleItem, 'id'>,
|
||||
) {
|
||||
return requestClient.put(`/role/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
* @param id 角色 ID
|
||||
*/
|
||||
async function deleteRole(id: string) {
|
||||
return requestClient.delete(`/role/delete/${id}`);
|
||||
}
|
||||
|
||||
export { createRole, deleteRole, getRoleList, updateRole };
|
||||
64
apps/bdrp-admin-web/src/api/system/user.ts
Normal file
64
apps/bdrp-admin-web/src/api/system/user.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { Recordable } from '@vben/types';
|
||||
|
||||
import { requestClient } from '#/api/request';
|
||||
|
||||
export namespace SystemUserApi {
|
||||
export interface SystemUser {
|
||||
[key: string]: any;
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: string[];
|
||||
remark?: string;
|
||||
status: 0 | 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色列表数据
|
||||
*/
|
||||
async function getUserList(params: Recordable<any>) {
|
||||
return requestClient.get<Array<SystemUserApi.SystemUser>>('/user/list', {
|
||||
params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建角色
|
||||
* @param data 角色数据
|
||||
*/
|
||||
async function createUser(data: Omit<SystemUserApi.SystemUser, 'id'>) {
|
||||
return requestClient.post('/user/create', data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色
|
||||
*
|
||||
* @param id 角色 ID
|
||||
* @param data 角色数据
|
||||
*/
|
||||
async function updateUser(
|
||||
id: string,
|
||||
data: Omit<SystemUserApi.SystemUser, 'id'>,
|
||||
) {
|
||||
return requestClient.put(`/user/update/${id}`, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除角色
|
||||
* @param id 角色 ID
|
||||
*/
|
||||
async function deleteUser(id: string) {
|
||||
return requestClient.delete(`/user/delete/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置用户密码
|
||||
* @param id 用户 ID
|
||||
* @param data 新密码数据
|
||||
* @param data.password 新密码
|
||||
*/
|
||||
async function resetPassword(id: string, data: { password: string }) {
|
||||
return requestClient.put(`/user/reset-password/${id}`, data);
|
||||
}
|
||||
|
||||
export { createUser, deleteUser, getUserList, resetPassword, updateUser };
|
||||
39
apps/bdrp-admin-web/src/app.vue
Normal file
39
apps/bdrp-admin-web/src/app.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { useAntdDesignTokens } from '@vben/hooks';
|
||||
import { preferences, usePreferences } from '@vben/preferences';
|
||||
|
||||
import { App, ConfigProvider, theme } from 'ant-design-vue';
|
||||
|
||||
import { antdLocale } from '#/locales';
|
||||
|
||||
defineOptions({ name: 'App' });
|
||||
|
||||
const { isDark } = usePreferences();
|
||||
const { tokens } = useAntdDesignTokens();
|
||||
|
||||
const tokenTheme = computed(() => {
|
||||
const algorithm = isDark.value
|
||||
? [theme.darkAlgorithm]
|
||||
: [theme.defaultAlgorithm];
|
||||
|
||||
// antd 紧凑模式算法
|
||||
if (preferences.app.compact) {
|
||||
algorithm.push(theme.compactAlgorithm);
|
||||
}
|
||||
|
||||
return {
|
||||
algorithm,
|
||||
token: tokens,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ConfigProvider :locale="antdLocale" :theme="tokenTheme">
|
||||
<App>
|
||||
<RouterView />
|
||||
</App>
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
72
apps/bdrp-admin-web/src/bootstrap.ts
Normal file
72
apps/bdrp-admin-web/src/bootstrap.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { createApp, watchEffect } from 'vue';
|
||||
|
||||
import { registerAccessDirective } from '@vben/access';
|
||||
import { registerLoadingDirective } from '@vben/common-ui/es/loading';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { initStores } from '@vben/stores';
|
||||
import '@vben/styles';
|
||||
import '@vben/styles/antd';
|
||||
|
||||
import { useTitle } from '@vueuse/core';
|
||||
|
||||
import { $t, setupI18n } from '#/locales';
|
||||
|
||||
import { initComponentAdapter } from './adapter/component';
|
||||
import App from './app.vue';
|
||||
import { router } from './router';
|
||||
|
||||
async function bootstrap(namespace: string) {
|
||||
// 初始化组件适配器
|
||||
await initComponentAdapter();
|
||||
|
||||
// // 设置弹窗的默认配置
|
||||
// setDefaultModalProps({
|
||||
// fullscreenButton: false,
|
||||
// });
|
||||
// // 设置抽屉的默认配置
|
||||
// setDefaultDrawerProps({
|
||||
// zIndex: 1020,
|
||||
// });
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 注册v-loading指令
|
||||
registerLoadingDirective(app, {
|
||||
loading: 'loading', // 在这里可以自定义指令名称,也可以明确提供false表示不注册这个指令
|
||||
spinning: 'spinning',
|
||||
});
|
||||
|
||||
// 国际化 i18n 配置
|
||||
await setupI18n(app);
|
||||
|
||||
// 配置 pinia-tore
|
||||
await initStores(app, { namespace });
|
||||
|
||||
// 安装权限指令
|
||||
registerAccessDirective(app);
|
||||
|
||||
// 初始化 tippy
|
||||
const { initTippy } = await import('@vben/common-ui/es/tippy');
|
||||
initTippy(app);
|
||||
|
||||
// 配置路由及路由守卫
|
||||
app.use(router);
|
||||
|
||||
// 配置Motion插件
|
||||
const { MotionPlugin } = await import('@vben/plugins/motion');
|
||||
app.use(MotionPlugin);
|
||||
|
||||
// 动态更新标题
|
||||
watchEffect(() => {
|
||||
if (preferences.app.dynamicTitle) {
|
||||
const routeTitle = router.currentRoute.value.meta?.title;
|
||||
const pageTitle =
|
||||
(routeTitle ? `${$t(routeTitle)} - ` : '') + preferences.app.name;
|
||||
useTitle(pageTitle);
|
||||
}
|
||||
});
|
||||
|
||||
app.mount('#app');
|
||||
}
|
||||
|
||||
export { bootstrap };
|
||||
21
apps/bdrp-admin-web/src/layouts/auth.vue
Normal file
21
apps/bdrp-admin-web/src/layouts/auth.vue
Normal file
@@ -0,0 +1,21 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { AuthPageLayout } from '@vben/layouts';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
const appName = computed(() => preferences.app.name);
|
||||
const logo = computed(() => preferences.logo.source);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AuthPageLayout
|
||||
:app-name="appName"
|
||||
:logo="logo"
|
||||
:page-description="appName"
|
||||
page-title="大型中后台管理系统"
|
||||
>
|
||||
<!-- 自定义工具栏 -->
|
||||
<!-- <template #toolbar></template> -->
|
||||
</AuthPageLayout>
|
||||
</template>
|
||||
90
apps/bdrp-admin-web/src/layouts/basic.vue
Normal file
90
apps/bdrp-admin-web/src/layouts/basic.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<script lang="ts" setup>
|
||||
import type { NotificationItem } from '@vben/layouts';
|
||||
|
||||
import { computed, ref, watch } from 'vue';
|
||||
|
||||
import { AuthenticationLoginExpiredModal } from '@vben/common-ui';
|
||||
import { useWatermark } from '@vben/hooks';
|
||||
import { BasicLayout, LockScreen, UserDropdown } from '@vben/layouts';
|
||||
import { preferences } from '@vben/preferences';
|
||||
import { useAccessStore, useUserStore } from '@vben/stores';
|
||||
|
||||
import { useAuthStore } from '#/store';
|
||||
import LoginForm from '#/views/_core/authentication/login.vue';
|
||||
|
||||
const notifications = ref<NotificationItem[]>([]);
|
||||
|
||||
const userStore = useUserStore();
|
||||
const authStore = useAuthStore();
|
||||
const accessStore = useAccessStore();
|
||||
const { destroyWatermark, updateWatermark } = useWatermark();
|
||||
const _showDot = computed(() =>
|
||||
notifications.value.some((item) => !item.isRead),
|
||||
);
|
||||
|
||||
const menus = computed(() => []);
|
||||
|
||||
const avatar = computed(() => {
|
||||
return userStore.userInfo?.avatar ?? preferences.app.defaultAvatar;
|
||||
});
|
||||
|
||||
async function handleLogout() {
|
||||
await authStore.logout(false);
|
||||
}
|
||||
|
||||
function _handleNoticeClear() {
|
||||
notifications.value = [];
|
||||
}
|
||||
|
||||
function _handleMakeAll() {
|
||||
notifications.value.forEach((item) => (item.isRead = true));
|
||||
}
|
||||
watch(
|
||||
() => preferences.app.watermark,
|
||||
async (enable) => {
|
||||
if (enable) {
|
||||
await updateWatermark({
|
||||
content: `${userStore.userInfo?.username} - ${userStore.userInfo?.realName}`,
|
||||
});
|
||||
} else {
|
||||
destroyWatermark();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasicLayout @clear-preferences-and-logout="handleLogout">
|
||||
<template #user-dropdown>
|
||||
<UserDropdown
|
||||
:avatar
|
||||
:menus
|
||||
:description="userStore.userInfo?.username"
|
||||
tag-text="赤眉"
|
||||
@logout="handleLogout"
|
||||
/>
|
||||
</template>
|
||||
<!-- <template #notification>
|
||||
<Notification
|
||||
:dot="showDot"
|
||||
:notifications="notifications"
|
||||
@clear="handleNoticeClear"
|
||||
@make-all="handleMakeAll"
|
||||
/>
|
||||
</template> -->
|
||||
<template #extra>
|
||||
<AuthenticationLoginExpiredModal
|
||||
v-model:open="accessStore.loginExpired"
|
||||
:avatar
|
||||
>
|
||||
<LoginForm />
|
||||
</AuthenticationLoginExpiredModal>
|
||||
</template>
|
||||
<template #lock-screen>
|
||||
<LockScreen :avatar @to-login="handleLogout" />
|
||||
</template>
|
||||
</BasicLayout>
|
||||
</template>
|
||||
6
apps/bdrp-admin-web/src/layouts/index.ts
Normal file
6
apps/bdrp-admin-web/src/layouts/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
const BasicLayout = () => import('./basic.vue');
|
||||
const AuthPageLayout = () => import('./auth.vue');
|
||||
|
||||
const IFrameView = () => import('@vben/layouts').then((m) => m.IFrameView);
|
||||
|
||||
export { AuthPageLayout, BasicLayout, IFrameView };
|
||||
3
apps/bdrp-admin-web/src/locales/README.md
Normal file
3
apps/bdrp-admin-web/src/locales/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# locale
|
||||
|
||||
每个app使用的国际化可能不同,这里用于扩展国际化的功能,例如扩展 dayjs、antd组件库的多语言切换,以及app本身的国际化文件。
|
||||
102
apps/bdrp-admin-web/src/locales/index.ts
Normal file
102
apps/bdrp-admin-web/src/locales/index.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { Locale } from 'ant-design-vue/es/locale';
|
||||
|
||||
import type { App } from 'vue';
|
||||
|
||||
import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales';
|
||||
|
||||
import { ref } from 'vue';
|
||||
|
||||
import {
|
||||
$t,
|
||||
setupI18n as coreSetup,
|
||||
loadLocalesMapFromDir,
|
||||
} from '@vben/locales';
|
||||
import { preferences } from '@vben/preferences';
|
||||
|
||||
import antdEnLocale from 'ant-design-vue/es/locale/en_US';
|
||||
import antdDefaultLocale from 'ant-design-vue/es/locale/zh_CN';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
const antdLocale = ref<Locale>(antdDefaultLocale);
|
||||
|
||||
const modules = import.meta.glob('./langs/**/*.json');
|
||||
|
||||
const localesMap = loadLocalesMapFromDir(
|
||||
/\.\/langs\/([^/]+)\/(.*)\.json$/,
|
||||
modules,
|
||||
);
|
||||
/**
|
||||
* 加载应用特有的语言包
|
||||
* 这里也可以改造为从服务端获取翻译数据
|
||||
* @param lang
|
||||
*/
|
||||
async function loadMessages(lang: SupportedLanguagesType) {
|
||||
const [appLocaleMessages] = await Promise.all([
|
||||
localesMap[lang]?.(),
|
||||
loadThirdPartyMessage(lang),
|
||||
]);
|
||||
return appLocaleMessages?.default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载第三方组件库的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadThirdPartyMessage(lang: SupportedLanguagesType) {
|
||||
await Promise.all([loadAntdLocale(lang), loadDayjsLocale(lang)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载dayjs的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadDayjsLocale(lang: SupportedLanguagesType) {
|
||||
let locale;
|
||||
switch (lang) {
|
||||
case 'en-US': {
|
||||
locale = await import('dayjs/locale/en');
|
||||
break;
|
||||
}
|
||||
case 'zh-CN': {
|
||||
locale = await import('dayjs/locale/zh-cn');
|
||||
break;
|
||||
}
|
||||
// 默认使用英语
|
||||
default: {
|
||||
locale = await import('dayjs/locale/en');
|
||||
}
|
||||
}
|
||||
if (locale) {
|
||||
dayjs.locale(locale);
|
||||
} else {
|
||||
console.error(`Failed to load dayjs locale for ${lang}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载antd的语言包
|
||||
* @param lang
|
||||
*/
|
||||
async function loadAntdLocale(lang: SupportedLanguagesType) {
|
||||
switch (lang) {
|
||||
case 'en-US': {
|
||||
antdLocale.value = antdEnLocale;
|
||||
break;
|
||||
}
|
||||
case 'zh-CN': {
|
||||
antdLocale.value = antdDefaultLocale;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setupI18n(app: App, options: LocaleSetupOptions = {}) {
|
||||
await coreSetup(app, {
|
||||
defaultLocale: preferences.app.locale,
|
||||
loadMessages,
|
||||
missingWarn: !import.meta.env.PROD,
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
export { $t, antdLocale, setupI18n };
|
||||
31
apps/bdrp-admin-web/src/locales/lang/zh-CN/platform-user.ts
Normal file
31
apps/bdrp-admin-web/src/locales/lang/zh-CN/platform-user.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export default {
|
||||
platformUser: {
|
||||
name: '平台用户',
|
||||
list: '平台用户列表',
|
||||
field: {
|
||||
mobile: '手机号',
|
||||
nickname: '昵称',
|
||||
platform: '用户平台',
|
||||
inside: '是否内部用户',
|
||||
createTime: '创建时间',
|
||||
updateTime: '更新时间',
|
||||
info: '备注信息',
|
||||
},
|
||||
search: {
|
||||
mobile: '请输入手机号',
|
||||
nickname: '请输入昵称',
|
||||
platform: '请选择用户平台',
|
||||
inside: '请选择是否内部用户',
|
||||
createTime: '请选择创建时间',
|
||||
},
|
||||
operation: '操作',
|
||||
platformMap: {
|
||||
h5: 'H5',
|
||||
wechat: '微信',
|
||||
},
|
||||
insideMap: {
|
||||
1: '是',
|
||||
0: '否',
|
||||
},
|
||||
},
|
||||
};
|
||||
12
apps/bdrp-admin-web/src/locales/langs/en-US/demos.json
Normal file
12
apps/bdrp-admin-web/src/locales/langs/en-US/demos.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"title": "Demos",
|
||||
"antd": "Ant Design Vue",
|
||||
"vben": {
|
||||
"title": "Project",
|
||||
"about": "About",
|
||||
"document": "Document",
|
||||
"antdv": "Ant Design Vue Version",
|
||||
"naive-ui": "Naive UI Version",
|
||||
"element-plus": "Element Plus Version"
|
||||
}
|
||||
}
|
||||
14
apps/bdrp-admin-web/src/locales/langs/en-US/page.json
Normal file
14
apps/bdrp-admin-web/src/locales/langs/en-US/page.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"auth": {
|
||||
"login": "Login",
|
||||
"register": "Register",
|
||||
"codeLogin": "Code Login",
|
||||
"qrcodeLogin": "Qr Code Login",
|
||||
"forgetPassword": "Forget Password"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "Dashboard",
|
||||
"analytics": "Analytics",
|
||||
"workspace": "Workspace"
|
||||
}
|
||||
}
|
||||
99
apps/bdrp-admin-web/src/locales/langs/en-US/system.json
Normal file
99
apps/bdrp-admin-web/src/locales/langs/en-US/system.json
Normal file
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"title": "System Management",
|
||||
"dept": {
|
||||
"name": "Department",
|
||||
"title": "Department Management",
|
||||
"deptName": "Department Name",
|
||||
"status": "Status",
|
||||
"createTime": "Create Time",
|
||||
"remark": "Remark",
|
||||
"operation": "Operation",
|
||||
"parentDept": "Parent Department"
|
||||
},
|
||||
"menu": {
|
||||
"title": "Menu Management",
|
||||
"parent": "Parent Menu",
|
||||
"menuTitle": "Title",
|
||||
"menuName": "Menu Name",
|
||||
"name": "Menu",
|
||||
"type": "Type",
|
||||
"typeCatalog": "Catalog",
|
||||
"typeMenu": "Menu",
|
||||
"typeButton": "Button",
|
||||
"typeLink": "Link",
|
||||
"typeEmbedded": "Embedded",
|
||||
"icon": "Icon",
|
||||
"activeIcon": "Active Icon",
|
||||
"activePath": "Active Path",
|
||||
"path": "Route Path",
|
||||
"component": "Component",
|
||||
"status": "Status",
|
||||
"authCode": "Auth Code",
|
||||
"badge": "Badge",
|
||||
"operation": "Operation",
|
||||
"linkSrc": "Link Address",
|
||||
"affixTab": "Affix In Tabs",
|
||||
"keepAlive": "Keep Alive",
|
||||
"hideInMenu": "Hide In Menu",
|
||||
"hideInTab": "Hide In Tabbar",
|
||||
"hideChildrenInMenu": "Hide Children In Menu",
|
||||
"hideInBreadcrumb": "Hide In Breadcrumb",
|
||||
"advancedSettings": "Other Settings",
|
||||
"activePathMustExist": "The path could not find a valid menu",
|
||||
"activePathHelp": "When jumping to the current route, \nthe menu path that needs to be activated must be specified when it does not display in the navigation menu.",
|
||||
"badgeType": {
|
||||
"title": "Badge Type",
|
||||
"dot": "Dot",
|
||||
"normal": "Text",
|
||||
"none": "None"
|
||||
},
|
||||
"badgeVariants": "Badge Style"
|
||||
},
|
||||
"role": {
|
||||
"title": "Role Management",
|
||||
"list": "Role List",
|
||||
"name": "Role",
|
||||
"roleName": "Role Name",
|
||||
"id": "Role ID",
|
||||
"status": "Status",
|
||||
"remark": "Remark",
|
||||
"createTime": "Creation Time",
|
||||
"operation": "Operation",
|
||||
"permissions": "Permissions",
|
||||
"setPermissions": "Permissions",
|
||||
"setApiPermissions": "API Permissions",
|
||||
"roleCode": "Role Code",
|
||||
"description": "Description"
|
||||
},
|
||||
"api": {
|
||||
"title": "API Management",
|
||||
"list": "API List",
|
||||
"name": "API",
|
||||
"apiName": "API Name",
|
||||
"apiCode": "API Code",
|
||||
"method": "Request Method",
|
||||
"url": "API URL",
|
||||
"status": "Status",
|
||||
"description": "Description",
|
||||
"createTime": "Create Time",
|
||||
"operation": "Operation",
|
||||
"permissions": "Permissions",
|
||||
"setPermissions": "Set Permissions"
|
||||
},
|
||||
"user": {
|
||||
"title": "User Management",
|
||||
"name": "User",
|
||||
"list": "User List",
|
||||
"userName": "Username",
|
||||
"realName": "Real Name",
|
||||
"status": "Status",
|
||||
"setPermissions": "Set Permissions",
|
||||
"createTime": "Create Time",
|
||||
"operation": "Operation",
|
||||
"resetPassword": "Reset Password",
|
||||
"newPassword": "New Password",
|
||||
"confirmPassword": "Confirm Password",
|
||||
"confirmPasswordRequired": "Please confirm password",
|
||||
"passwordMismatch": "The two passwords do not match"
|
||||
}
|
||||
}
|
||||
12
apps/bdrp-admin-web/src/locales/langs/zh-CN/demos.json
Normal file
12
apps/bdrp-admin-web/src/locales/langs/zh-CN/demos.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"title": "演示",
|
||||
"antd": "Ant Design Vue",
|
||||
"vben": {
|
||||
"title": "项目",
|
||||
"about": "关于",
|
||||
"document": "文档",
|
||||
"antdv": "Ant Design Vue 版本",
|
||||
"naive-ui": "Naive UI 版本",
|
||||
"element-plus": "Element Plus 版本"
|
||||
}
|
||||
}
|
||||
14
apps/bdrp-admin-web/src/locales/langs/zh-CN/page.json
Normal file
14
apps/bdrp-admin-web/src/locales/langs/zh-CN/page.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"auth": {
|
||||
"login": "登录",
|
||||
"register": "注册",
|
||||
"codeLogin": "验证码登录",
|
||||
"qrcodeLogin": "二维码登录",
|
||||
"forgetPassword": "忘记密码"
|
||||
},
|
||||
"dashboard": {
|
||||
"title": "概览",
|
||||
"analytics": "分析页",
|
||||
"workspace": "工作台"
|
||||
}
|
||||
}
|
||||
101
apps/bdrp-admin-web/src/locales/langs/zh-CN/system.json
Normal file
101
apps/bdrp-admin-web/src/locales/langs/zh-CN/system.json
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"dept": {
|
||||
"list": "部门列表",
|
||||
"createTime": "创建时间",
|
||||
"deptName": "部门名称",
|
||||
"name": "部门",
|
||||
"operation": "操作",
|
||||
"parentDept": "上级部门",
|
||||
"remark": "备注",
|
||||
"status": "状态",
|
||||
"title": "部门管理"
|
||||
},
|
||||
"menu": {
|
||||
"list": "菜单列表",
|
||||
"activeIcon": "激活图标",
|
||||
"activePath": "激活路径",
|
||||
"activePathHelp": "跳转到当前路由时,需要激活的菜单路径。\n当不在导航菜单中显示时,需要指定激活路径",
|
||||
"activePathMustExist": "该路径未能找到有效的菜单",
|
||||
"advancedSettings": "其它设置",
|
||||
"affixTab": "固定在标签",
|
||||
"authCode": "权限标识",
|
||||
"badge": "徽章内容",
|
||||
"badgeVariants": "徽标样式",
|
||||
"badgeType": {
|
||||
"dot": "点",
|
||||
"none": "无",
|
||||
"normal": "文字",
|
||||
"title": "徽标类型"
|
||||
},
|
||||
"component": "页面组件",
|
||||
"hideChildrenInMenu": "隐藏子菜单",
|
||||
"hideInBreadcrumb": "在面包屑中隐藏",
|
||||
"hideInMenu": "隐藏菜单",
|
||||
"hideInTab": "在标签栏中隐藏",
|
||||
"icon": "图标",
|
||||
"keepAlive": "缓存标签页",
|
||||
"linkSrc": "链接地址",
|
||||
"menuName": "菜单名称",
|
||||
"menuTitle": "标题",
|
||||
"name": "菜单",
|
||||
"operation": "操作",
|
||||
"parent": "上级菜单",
|
||||
"path": "路由地址",
|
||||
"status": "状态",
|
||||
"title": "菜单管理",
|
||||
"type": "类型",
|
||||
"typeButton": "按钮",
|
||||
"typeCatalog": "目录",
|
||||
"typeEmbedded": "内嵌",
|
||||
"typeLink": "外链",
|
||||
"typeMenu": "菜单"
|
||||
},
|
||||
"role": {
|
||||
"title": "角色管理",
|
||||
"list": "角色列表",
|
||||
"name": "角色",
|
||||
"roleName": "角色名称",
|
||||
"id": "角色ID",
|
||||
"status": "状态",
|
||||
"remark": "备注",
|
||||
"createTime": "创建时间",
|
||||
"operation": "操作",
|
||||
"permissions": "权限",
|
||||
"setPermissions": "授权",
|
||||
"setApiPermissions": "API权限",
|
||||
"roleCode": "角色编号",
|
||||
"description": "描述"
|
||||
},
|
||||
"title": "系统管理",
|
||||
"api": {
|
||||
"title": "API管理",
|
||||
"list": "API列表",
|
||||
"name": "API",
|
||||
"apiName": "API名称",
|
||||
"apiCode": "API编码",
|
||||
"method": "请求方法",
|
||||
"url": "API地址",
|
||||
"status": "状态",
|
||||
"description": "描述",
|
||||
"createTime": "创建时间",
|
||||
"operation": "操作",
|
||||
"permissions": "权限",
|
||||
"setPermissions": "设置权限"
|
||||
},
|
||||
"user": {
|
||||
"title": "用户管理",
|
||||
"name": "用户",
|
||||
"list": "用户列表",
|
||||
"userName": "用户名",
|
||||
"realName": "真实姓名",
|
||||
"status": "状态",
|
||||
"setPermissions": "设置权限",
|
||||
"createTime": "创建时间",
|
||||
"operation": "操作",
|
||||
"resetPassword": "重置密码",
|
||||
"newPassword": "新密码",
|
||||
"confirmPassword": "确认密码",
|
||||
"confirmPasswordRequired": "请确认密码",
|
||||
"passwordMismatch": "两次输入的密码不一致"
|
||||
}
|
||||
}
|
||||
31
apps/bdrp-admin-web/src/main.ts
Normal file
31
apps/bdrp-admin-web/src/main.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { initPreferences } from '@vben/preferences';
|
||||
import { unmountGlobalLoading } from '@vben/utils';
|
||||
|
||||
import { overridesPreferences } from './preferences';
|
||||
|
||||
/**
|
||||
* 应用初始化完成之后再进行页面加载渲染
|
||||
*/
|
||||
async function initApplication() {
|
||||
// name用于指定项目唯一标识
|
||||
// 用于区分不同项目的偏好设置以及存储数据的key前缀以及其他一些需要隔离的数据
|
||||
const env = import.meta.env.PROD ? 'prod' : 'dev';
|
||||
const appVersion = import.meta.env.VITE_APP_VERSION;
|
||||
const namespace = `${import.meta.env.VITE_APP_NAMESPACE}-${appVersion}-${env}`;
|
||||
|
||||
// app偏好设置初始化
|
||||
await initPreferences({
|
||||
namespace,
|
||||
overrides: overridesPreferences,
|
||||
});
|
||||
|
||||
// 启动应用并挂载
|
||||
// vue应用主要逻辑及视图
|
||||
const { bootstrap } = await import('./bootstrap');
|
||||
await bootstrap(namespace);
|
||||
|
||||
// 移除并销毁loading
|
||||
unmountGlobalLoading();
|
||||
}
|
||||
|
||||
initApplication();
|
||||
27
apps/bdrp-admin-web/src/preferences.ts
Normal file
27
apps/bdrp-admin-web/src/preferences.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { defineOverridesPreferences } from '@vben/preferences';
|
||||
|
||||
/**
|
||||
* @description 项目配置文件
|
||||
* 只需要覆盖项目中的一部分配置,不需要的配置不用覆盖,会自动使用默认配置
|
||||
* !!! 更改配置后请清空缓存,否则可能不生效
|
||||
*/
|
||||
export const overridesPreferences = defineOverridesPreferences({
|
||||
// overrides
|
||||
app: {
|
||||
name: import.meta.env.VITE_APP_TITLE,
|
||||
accessMode: 'backend',
|
||||
},
|
||||
logo: {
|
||||
source: 'https://ctrlph.xxxxxxxxxxx.com/logo.png',
|
||||
},
|
||||
copyright: {
|
||||
companyName: '戎行技术有限公司',
|
||||
companySiteLink: 'https://www.xxxxxxxxxxxx.com',
|
||||
date: '2025',
|
||||
icp: 'XICP备XXXXXXXXXXXXXXXX号',
|
||||
icpLink: 'https://beian.miit.gov.cn/',
|
||||
},
|
||||
footer: {
|
||||
enable: false,
|
||||
},
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user