mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-10-12 21:03:42 +08:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
032dbc6815 | ||
|
3c33f89ec1 | ||
|
f7090d3dbc | ||
|
59af204a4c |
@@ -1,2 +1 @@
|
||||
VITE_HTTP_PROXY=Y
|
||||
VITE_SOYBEAN_ROUTE_PLUGIN=Y
|
||||
|
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -1,25 +1,27 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.**"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- run: npx githublogen
|
||||
- name: Create github releases
|
||||
run: npx changelogithub
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
|
2
.npmrc
2
.npmrc
@@ -1,2 +1,4 @@
|
||||
registry=https://registry.npmmirror.com/
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
||||
auto-install-peers=true
|
||||
|
96
CHANGELOG.md
96
CHANGELOG.md
@@ -1,100 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## v0.10.0...v0.10.1
|
||||
|
||||
[compare changes](https://github.com/honghuangdc/soybean-admin/compare/v0.10.0...v0.10.1)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **projects:** CHANGELOG.md ([a252138](https://github.com/honghuangdc/soybean-admin/commit/a252138))
|
||||
- **projects:** Update README.md ([b5839ea](https://github.com/honghuangdc/soybean-admin/commit/b5839ea))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **projects:** Add switch for pageRoute plugin [添加自动生成路由的插件的开关] ([780ac75](https://github.com/honghuangdc/soybean-admin/commit/780ac75))
|
||||
- Release v0.10.1 ([44e4c04](https://github.com/honghuangdc/soybean-admin/commit/44e4c04))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Soybean ([@soybeanjs](http://github.com/soybeanjs))
|
||||
|
||||
## v0.9.9...v0.10.0
|
||||
|
||||
[compare changes](https://github.com/honghuangdc/soybean-admin/compare/v0.9.9...v0.10.0)
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- **projects:** 返回顶部功能适配新布局 ([54e2cb5](https://github.com/honghuangdc/soybean-admin/commit/54e2cb5))
|
||||
- **components:** Add tree related component instances ([d203a35](https://github.com/honghuangdc/soybean-admin/commit/d203a35))
|
||||
- **components:** Add routing data related to tree components and page display optimization ([a0f55ac](https://github.com/honghuangdc/soybean-admin/commit/a0f55ac))
|
||||
- **projects:** 增加i18n支持翻译菜单,tab,title ([3d48aa8](https://github.com/honghuangdc/soybean-admin/commit/3d48aa8))
|
||||
- **projects:** Add menu translate [翻译菜单] ([f68285f](https://github.com/honghuangdc/soybean-admin/commit/f68285f))
|
||||
- **projects:** 增加主题切换过渡效果 ([8da8843](https://github.com/honghuangdc/soybean-admin/commit/8da8843))
|
||||
- **projects:** Support mobile layout [支持移动端布局] ([f2b518e](https://github.com/honghuangdc/soybean-admin/commit/f2b518e))
|
||||
|
||||
### 🐞 Bug Fixes
|
||||
|
||||
- **projects:** Fix router guide [修复路由跳转异常] fixed #216 ([#216](https://github.com/honghuangdc/soybean-admin/issues/216))
|
||||
- **projects:** Fix better-mock usage [修复better-mock用法] ([c57640a](https://github.com/honghuangdc/soybean-admin/commit/c57640a))
|
||||
- **projects:** Tsconfig missing isolatedModules ([ab49afd](https://github.com/honghuangdc/soybean-admin/commit/ab49afd))
|
||||
- **projects:** Fix mockjs [修复mockjs] ([9b19f96](https://github.com/honghuangdc/soybean-admin/commit/9b19f96))
|
||||
- **projects:** Add prod mockjs switch [添加生产模式的mockjs的开关] ([9f5638f](https://github.com/honghuangdc/soybean-admin/commit/9f5638f))
|
||||
- **projects:** 修复面包屑导航下拉菜单语言显示问题 ([ee8fa04](https://github.com/honghuangdc/soybean-admin/commit/ee8fa04))
|
||||
- **projects:** Hide the drawer when it is initial mobile mode [初始化时为移动端布局则隐藏侧边栏] fixed #238 ([#238](https://github.com/honghuangdc/soybean-admin/issues/238))
|
||||
|
||||
### 🔥 Performance
|
||||
|
||||
- **projects:** Complete dynamic route translate [补充动态路由的翻译] ([7b746fa](https://github.com/honghuangdc/soybean-admin/commit/7b746fa))
|
||||
- **projects:** Move changing document title by locale to global event of composables & add appLoading unmount ([08e194e](https://github.com/honghuangdc/soybean-admin/commit/08e194e))
|
||||
|
||||
### 💅 Refactors
|
||||
|
||||
- **projects:** Update useTable ([211ae1f](https://github.com/honghuangdc/soybean-admin/commit/211ae1f))
|
||||
- **projects:** Remove page examples: tree [去除tree相关示例页面] ([f309003](https://github.com/honghuangdc/soybean-admin/commit/f309003))
|
||||
- **projects:** Use better-mock replace mockjs [用better-mock替换mockjs] ([9d3c732](https://github.com/honghuangdc/soybean-admin/commit/9d3c732))
|
||||
- **projects:** Upgrade vue3.3, official support defineOptions ([86a370f](https://github.com/honghuangdc/soybean-admin/commit/86a370f))
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **projects:** Add qq to README.md [文档添加QQ群] ([f74a642](https://github.com/honghuangdc/soybean-admin/commit/f74a642))
|
||||
- **projects:** Update README.md [更新README.md] ([397092c](https://github.com/honghuangdc/soybean-admin/commit/397092c))
|
||||
- **projects:** Update README.md [更新README.md] ([5a4f842](https://github.com/honghuangdc/soybean-admin/commit/5a4f842))
|
||||
- **projects:** Update README.md [更新README.md] ([a765da6](https://github.com/honghuangdc/soybean-admin/commit/a765da6))
|
||||
- **projects:** Update README.md [更新README.md] ([a989b44](https://github.com/honghuangdc/soybean-admin/commit/a989b44))
|
||||
- **projects:** 优化README.md ([6ea755f](https://github.com/honghuangdc/soybean-admin/commit/6ea755f))
|
||||
- **projects:** Readme.md 二次开发的项目内容换行 ([f3a1707](https://github.com/honghuangdc/soybean-admin/commit/f3a1707))
|
||||
- **projects:** Update CHANGELOG.md ([5f6caab](https://github.com/honghuangdc/soybean-admin/commit/5f6caab))
|
||||
|
||||
### 📦 Build
|
||||
|
||||
- **projects:** Remove old layout,tab package [去除旧的布局和页签依赖] ([42e6de3](https://github.com/honghuangdc/soybean-admin/commit/42e6de3))
|
||||
- **deps:** Update deps ([db62959](https://github.com/honghuangdc/soybean-admin/commit/db62959))
|
||||
- **deps:** Update deps [升级依赖] ([f2e82da](https://github.com/honghuangdc/soybean-admin/commit/f2e82da))
|
||||
- **deps:** Update deps and remove vite-plugin-html [升级依赖,去除vite-plugin-html] ([eaf3678](https://github.com/honghuangdc/soybean-admin/commit/eaf3678))
|
||||
- **deps:** Update deps [升级依赖] ([bae1767](https://github.com/honghuangdc/soybean-admin/commit/bae1767))
|
||||
- **projects:** Update deps and fix type error [升级依赖并修复类型问题] ([34f023c](https://github.com/honghuangdc/soybean-admin/commit/34f023c))
|
||||
- **deps:** Update deps [升级依赖] ([c264216](https://github.com/honghuangdc/soybean-admin/commit/c264216))
|
||||
- **deps:** Update deps [升级依赖] ([40f8587](https://github.com/honghuangdc/soybean-admin/commit/40f8587))
|
||||
- **projects:** Update deps and fix style [升级依赖&修复代码格式] ([c1c4335](https://github.com/honghuangdc/soybean-admin/commit/c1c4335))
|
||||
|
||||
### 🏡 Chore
|
||||
|
||||
- **deps:** Update deps ([cebbef6](https://github.com/honghuangdc/soybean-admin/commit/cebbef6))
|
||||
- **projects:** Remove useless packages, update lint-staged config, add githublogen ([5aaa318](https://github.com/honghuangdc/soybean-admin/commit/5aaa318))
|
||||
- Release v0.10.0 ([270a055](https://github.com/honghuangdc/soybean-admin/commit/270a055))
|
||||
|
||||
### 🎨 Styles
|
||||
|
||||
- **projects:** Per style [完善样式] ([209ef3d](https://github.com/honghuangdc/soybean-admin/commit/209ef3d))
|
||||
|
||||
### ❤️ Contributors
|
||||
|
||||
- Soybean ([@soybeanjs](http://github.com/soybeanjs))
|
||||
- Cc <cc@qq.com>
|
||||
- Xiaojunnuo ([@greper](http://github.com/greper))
|
||||
- Kirk Lin ([@kirklin](http://github.com/kirklin))
|
||||
- Small_happy <5304122+small_happy@user.noreply.gitee.com>
|
||||
- 燕博文 <349952469@qq.com>
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
### [0.9.9](https://github.com/honghuangdc/soybean-admin/compare/v0.9.8...v0.9.9) (2023-03-13)
|
||||
|
||||
|
42
README.md
42
README.md
@@ -29,12 +29,12 @@
|
||||
|
||||
## 代码仓库
|
||||
|
||||
| 仓库 | github 地址 | gitee 镜像 | 预览 |
|
||||
| -------------- | ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------- |
|
||||
| soybean-admin | [github](https://github.com/honghuangdc/soybean-admin) | [gitee](https://gitee.com/honghuangdc/soybean-admin) | [预览](https://soybean.pro/) |
|
||||
| tauri 版 | [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri) | [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri) | |
|
||||
| 精简版 | [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin) | [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin) | |
|
||||
| 集成 fast-crud | [集成 fast-crud](https://github.com/honghuangdc/soybean-admin/tree/fast-crud) | [集成 fast-crud](https://gitee.com/honghuangdc/soybean-admin/tree/fast-crud) | [预览](http://fast-crud.docmirror.cn/soybean/#/crud/demo) |
|
||||
- [github](https://github.com/honghuangdc/soybean-admin)
|
||||
- [tauri 版](https://github.com/honghuangdc/soybean-admin/tree/tauri)
|
||||
- [精简版](https://github.com/honghuangdc/soybean-admin/tree/thin)
|
||||
- [gitee](https://gitee.com/honghuangdc/soybean-admin)
|
||||
- [tauri 版](https://gitee.com/honghuangdc/soybean-admin/tree/tauri)
|
||||
- [精简版](https://gitee.com/honghuangdc/soybean-admin/tree/thin)
|
||||
|
||||
## 更新日志
|
||||
|
||||
@@ -48,26 +48,25 @@
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
<div align="center">
|
||||
<img style="width:380px;margin-right:18px;border:1px solid #dedede;" src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin10-v2.png" />
|
||||
<img style="width:380px;border:1px solid #dedede;" src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybean-admin11-v2.png" />
|
||||
</div>
|
||||

|
||||
|
||||

|
||||
|
||||
## 安装使用
|
||||
|
||||
@@ -121,9 +120,8 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
项目已用 simple-git-hooks 代替了 husky, 旧版本用了 husky,执行 pnpm soy init-git-hooks 进行初始化配置
|
||||
|
||||
## 基于 SoybeanAdmin 二次开发的项目
|
||||
|
||||
- [electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的 mock。
|
||||
- [T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH 客户端。
|
||||
[electron-mock-admin](https://github.com/lixin59/electron-mock-api): 一个 Mock Api 管理系统,帮助前端开发伙伴快速实现接口的mock。
|
||||
[T-Shell](https://github.com/TheBlindM/T-Shell): 是一个可配置命令提示的终端模拟器和 SSH客户端。
|
||||
|
||||
## 浏览器支持
|
||||
|
||||
@@ -144,6 +142,10 @@ docker run --name soybean -p 80:80 -d soybeanjs/soybean-admin:v0.9.6
|
||||
`Soybean Admin` 是完全开源免费的项目,在帮助开发者更方便地进行中大型管理系统开发,同时也提供微信和 QQ 交流群,使用问题欢迎在群内提问。
|
||||
|
||||
<div style="display:flex;">
|
||||
<!-- <div style="padding-right:24px;">
|
||||
<p>微信交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/soybeanjs-wechat0503.jpeg" style="width:200px" />
|
||||
</div> -->
|
||||
<div style="padding-right:24px;">
|
||||
<p>QQ交流群</p>
|
||||
<img src="https://soybeanjs-1300612522.cos.ap-guangzhou.myqcloud.com/uPic/qq-soybean-admin.jpg" style="width:200px" />
|
||||
|
@@ -25,7 +25,8 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin
|
||||
...unplugin(viteEnv),
|
||||
unocss(),
|
||||
mock(viteEnv),
|
||||
progress()
|
||||
progress(),
|
||||
pageRoute()
|
||||
];
|
||||
|
||||
if (viteEnv.VITE_VISUALIZER === 'Y') {
|
||||
@@ -37,9 +38,6 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin
|
||||
if (viteEnv.VITE_PWA === 'Y' || viteEnv.VITE_VERCEL === 'Y') {
|
||||
plugins.push(pwa());
|
||||
}
|
||||
if (viteEnv.VITE_SOYBEAN_ROUTE_PLUGIN === 'Y') {
|
||||
plugins.push(pageRoute());
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
21
changelogithub.config.json
Normal file
21
changelogithub.config.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"types": {
|
||||
"feat": { "title": "🚀 Features" },
|
||||
"perf": { "title": "🔥 Performance" },
|
||||
"fix": { "title": "🩹 Fixes" },
|
||||
"refactor": { "title": "💅 Refactors" },
|
||||
"docs": { "title": "📖 Documentation" },
|
||||
"types": { "title": "🌊 Types" },
|
||||
"chore": { "title": "🏡 Chore" },
|
||||
"test": { "title": "🧪 Tests" },
|
||||
"style": { "title": "🎨 Styles" },
|
||||
"ci": { "title": "🤖 CI" }
|
||||
},
|
||||
"scopeMap": {},
|
||||
"titles": {
|
||||
"breakingChanges": "🚨 Breaking Changes"
|
||||
},
|
||||
"contributors": true,
|
||||
"capitalize": true,
|
||||
"group": true
|
||||
}
|
295
mock/api/crud/base.ts
Normal file
295
mock/api/crud/base.ts
Normal file
@@ -0,0 +1,295 @@
|
||||
export type ListItem = {
|
||||
id?: number;
|
||||
children?: ListItem[];
|
||||
[key: string]: any;
|
||||
};
|
||||
export type BaseMockOptions = { name: string; copyTimes?: number; list: ListItem[]; idGenerator: number };
|
||||
type CopyListParams = { originList: ListItem[]; newList: ListItem[]; options: BaseMockOptions; parentId?: number };
|
||||
|
||||
function copyList(props: CopyListParams) {
|
||||
const { originList, newList, options, parentId } = props;
|
||||
for (const item of originList) {
|
||||
const newItem: ListItem = { ...item, parentId };
|
||||
newItem.id = options.idGenerator;
|
||||
options.idGenerator += 1;
|
||||
newList.push(newItem);
|
||||
if (item.children) {
|
||||
newItem.children = [];
|
||||
copyList({
|
||||
originList: item.children,
|
||||
newList: newItem.children,
|
||||
options,
|
||||
parentId: newItem.id
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function delById(req: Service.MockOption, list: any[]) {
|
||||
for (let i = 0; i < list.length; i += 1) {
|
||||
const item = list[i];
|
||||
if (item.id === parseInt(req.query.id, 10)) {
|
||||
list.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
delById(req, item.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findById(id: number, list: ListItem[]): any {
|
||||
for (const item of list) {
|
||||
if (item.id === id) {
|
||||
return item;
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
const sub = findById(id, item.children);
|
||||
if (sub !== null && sub !== undefined) {
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function matchWithArrayCondition(value: any[], item: ListItem, key: string) {
|
||||
if (value.length === 0) {
|
||||
return true;
|
||||
}
|
||||
let matched = false;
|
||||
for (const i of value) {
|
||||
if (item[key] instanceof Array) {
|
||||
for (const j of item[key]) {
|
||||
if (i === j) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matched) {
|
||||
break;
|
||||
}
|
||||
} else if (item[key] === i || (typeof item[key] === 'string' && item[key].indexOf(`${i}`) >= 0)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
if (matched) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
function matchWithObjectCondition(value: any, item: ListItem, key: string) {
|
||||
let matched = true;
|
||||
for (const key2 of Object.keys(value)) {
|
||||
const v = value[key2];
|
||||
if (v && item[key] && v !== item[key][key2]) {
|
||||
matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return matched;
|
||||
}
|
||||
|
||||
function searchFromList(list: ListItem[], query: any) {
|
||||
const filter = (item: ListItem) => {
|
||||
let allFound = true; // 是否所有条件都符合
|
||||
for (const key of Object.keys(query)) {
|
||||
const value = query[key];
|
||||
if (value === undefined || value === null || value === '') {
|
||||
// no nothing
|
||||
} else if (value instanceof Array) {
|
||||
// 如果条件中的value是数组的话,只要查到一个就行
|
||||
const matched = matchWithArrayCondition(value, item, key);
|
||||
if (!matched) {
|
||||
allFound = false;
|
||||
}
|
||||
} else if (value instanceof Object) {
|
||||
// 如果条件中的value是对象的话,需要每个key都匹配
|
||||
const matched = matchWithObjectCondition(value, item, key);
|
||||
if (!matched) {
|
||||
allFound = false;
|
||||
}
|
||||
} else if (item[key] !== value) {
|
||||
allFound = false;
|
||||
}
|
||||
}
|
||||
return allFound;
|
||||
};
|
||||
return list.filter(filter);
|
||||
}
|
||||
|
||||
export default {
|
||||
buildMock(options: BaseMockOptions) {
|
||||
const name = options.name;
|
||||
if (!options.copyTimes) {
|
||||
options.copyTimes = 29;
|
||||
}
|
||||
const list: any[] = [];
|
||||
for (let i = 0; i < options.copyTimes; i += 1) {
|
||||
copyList({
|
||||
originList: options.list,
|
||||
newList: list,
|
||||
options
|
||||
});
|
||||
}
|
||||
options.list = list;
|
||||
return [
|
||||
{
|
||||
path: `/mock/${name}/page`,
|
||||
method: 'post',
|
||||
handle(req: Service.MockOption) {
|
||||
let data = [...list];
|
||||
let limit = 20;
|
||||
let offset = 0;
|
||||
for (const item of list) {
|
||||
if (item.children && item.children.length === 0) {
|
||||
item.hasChildren = false;
|
||||
item.lazy = false;
|
||||
}
|
||||
}
|
||||
let orderAsc: any;
|
||||
let orderProp: any;
|
||||
if (req && req.body) {
|
||||
const { page, query } = req.body;
|
||||
if (page.limit) {
|
||||
limit = parseInt(page.limit, 10);
|
||||
}
|
||||
if (page.offset) {
|
||||
offset = parseInt(page.offset, 10);
|
||||
}
|
||||
if (Object.keys(query).length > 0) {
|
||||
data = searchFromList(list, query);
|
||||
}
|
||||
}
|
||||
|
||||
const start = offset;
|
||||
let end = offset + limit;
|
||||
if (data.length < end) {
|
||||
end = data.length;
|
||||
}
|
||||
|
||||
if (orderProp) {
|
||||
// 排序
|
||||
data.sort((a, b) => {
|
||||
let ret = 0;
|
||||
if (a[orderProp] > b[orderProp]) {
|
||||
ret = 1;
|
||||
} else if (a[orderProp] < b[orderProp]) {
|
||||
ret = -1;
|
||||
}
|
||||
return orderAsc ? ret : -ret;
|
||||
});
|
||||
}
|
||||
|
||||
const records = data.slice(start, end);
|
||||
const lastOffset = data.length - (data.length % limit);
|
||||
if (offset > lastOffset) {
|
||||
offset = lastOffset;
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: {
|
||||
records,
|
||||
total: data.length,
|
||||
limit,
|
||||
offset
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/mock/${name}/get`,
|
||||
method: 'get',
|
||||
handle(req: Service.MockOption) {
|
||||
let id = req.query.id;
|
||||
id = parseInt(id, 10);
|
||||
let current = null;
|
||||
for (const item of list) {
|
||||
if (item.id === id) {
|
||||
current = item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: current
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/mock/${name}/add`,
|
||||
method: 'post',
|
||||
handle(req: Service.MockOption) {
|
||||
req.body.id = options.idGenerator;
|
||||
options.idGenerator += 1;
|
||||
list.unshift(req.body);
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: req.body.id
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/mock/${name}/update`,
|
||||
method: 'post',
|
||||
handle(req: Service.MockOption) {
|
||||
const item = findById(req.body.id, list);
|
||||
if (item) {
|
||||
Object.assign(item, req.body);
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/mock/${name}/delete`,
|
||||
method: 'post',
|
||||
handle(req: Service.MockOption) {
|
||||
delById(req, list);
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/mock/${name}/batchDelete`,
|
||||
method: 'post',
|
||||
handle(req: Service.MockOption) {
|
||||
const ids = req.body.ids;
|
||||
for (let i = list.length - 1; i >= 0; i -= 1) {
|
||||
const item = list[i];
|
||||
if (ids.indexOf(item.id) >= 0) {
|
||||
list.splice(i, 1);
|
||||
}
|
||||
}
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: null
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
path: `/mock/${name}/all`,
|
||||
method: 'post',
|
||||
handle() {
|
||||
return {
|
||||
code: 200,
|
||||
message: 'success',
|
||||
data: list
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
5
mock/api/crud/index.ts
Normal file
5
mock/api/crud/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import demo from './modules/demo';
|
||||
import headerGroup from './modules/header-group';
|
||||
|
||||
const crudApis = [...demo, ...headerGroup];
|
||||
export default crudApis;
|
56
mock/api/crud/modules/demo.ts
Normal file
56
mock/api/crud/modules/demo.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import type { MethodType, MockMethod } from 'vite-plugin-mock';
|
||||
import type { BaseMockOptions } from '../base';
|
||||
import mockBase from '../base';
|
||||
import MockOption = Service.MockOption;
|
||||
|
||||
const options: BaseMockOptions = {
|
||||
name: 'crud/demo',
|
||||
idGenerator: 0,
|
||||
list: [
|
||||
{
|
||||
select: '1',
|
||||
text: '文本测试',
|
||||
copyable: '文本可复制',
|
||||
avatar: 'http://greper.handsfree.work/extends/avatar.jpg',
|
||||
richtext: '富文本',
|
||||
datetime: '2023-01-30 11:11:11'
|
||||
},
|
||||
{
|
||||
select: '2'
|
||||
},
|
||||
{
|
||||
select: '0'
|
||||
}
|
||||
]
|
||||
};
|
||||
const mockedApis = mockBase.buildMock(options);
|
||||
|
||||
const apis: MockMethod[] = [
|
||||
{
|
||||
url: `/mock/${options.name}/dict`,
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return {
|
||||
code: 200,
|
||||
message: '',
|
||||
data: [
|
||||
{ value: '0', label: '关', color: 'warning' },
|
||||
{ value: '1', label: '开', color: 'success' },
|
||||
{ value: '2', label: '停' }
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
for (const mockedApi of mockedApis) {
|
||||
apis.push({
|
||||
url: mockedApi.path,
|
||||
method: mockedApi.method as MethodType,
|
||||
response: (request: MockOption) => {
|
||||
return mockedApi.handle(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default apis;
|
46
mock/api/crud/modules/header-group.ts
Normal file
46
mock/api/crud/modules/header-group.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { MethodType, MockMethod } from 'vite-plugin-mock';
|
||||
import type { BaseMockOptions } from '../base';
|
||||
import mockBase from '../base';
|
||||
import MockOption = Service.MockOption;
|
||||
|
||||
const options: BaseMockOptions = {
|
||||
name: 'crud/header-group',
|
||||
idGenerator: 0,
|
||||
list: [
|
||||
{
|
||||
name: '张三',
|
||||
age: 18,
|
||||
province: '广东省',
|
||||
city: '深圳市',
|
||||
county: '南山区',
|
||||
street: '粤海街道'
|
||||
},
|
||||
{
|
||||
name: '李四',
|
||||
age: 26,
|
||||
province: '浙江省',
|
||||
city: '杭州市',
|
||||
county: '西湖区',
|
||||
street: '西湖街道'
|
||||
},
|
||||
{
|
||||
name: '王五',
|
||||
age: 24
|
||||
}
|
||||
]
|
||||
};
|
||||
const mockedApis = mockBase.buildMock(options);
|
||||
|
||||
const apis: MockMethod[] = [];
|
||||
|
||||
for (const mockedApi of mockedApis) {
|
||||
apis.push({
|
||||
url: mockedApi.path,
|
||||
method: mockedApi.method as MethodType,
|
||||
response: (request: MockOption) => {
|
||||
return mockedApi.handle(request);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default apis;
|
@@ -1,5 +1,6 @@
|
||||
import auth from './auth';
|
||||
import route from './route';
|
||||
import management from './management';
|
||||
import crud from './crud';
|
||||
|
||||
export default [...auth, ...route, ...management];
|
||||
export default [...auth, ...route, ...management, ...crud];
|
||||
|
@@ -12,8 +12,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis',
|
||||
i18nTitle: 'message.routes.dashboard.analysis'
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -23,16 +22,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench',
|
||||
i18nTitle: 'message.routes.dashboard.workbench'
|
||||
icon: 'icon-park-outline:workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1,
|
||||
i18nTitle: 'message.routes.dashboard._value'
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -46,7 +43,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
i18nTitle: 'message.routes.document.vue',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
@@ -57,7 +53,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
i18nTitle: 'message.routes.document.vite',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
@@ -68,7 +63,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
i18nTitle: 'message.routes.document.naive',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
@@ -79,7 +73,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
i18nTitle: 'message.routes.document.project',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
@@ -89,7 +82,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
i18nTitle: 'message.routes.document.project-link',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://docs.soybean.pro/'
|
||||
@@ -98,7 +90,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
i18nTitle: 'message.routes.document._value',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
@@ -114,7 +105,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
i18nTitle: 'message.routes.component.button',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
@@ -125,7 +115,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
i18nTitle: 'message.routes.component.card',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
@@ -136,7 +125,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
i18nTitle: 'message.routes.component.table',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
@@ -144,7 +132,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
i18nTitle: 'message.routes.component._value',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
@@ -165,7 +152,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
i18nTitle: 'message.routes.plugin.charts.echarts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
@@ -176,7 +162,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
i18nTitle: 'message.routes.plugin.charts.antv',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
@@ -184,7 +169,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
i18nTitle: 'message.routes.plugin.charts._value',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
@@ -194,7 +178,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
i18nTitle: 'message.routes.plugin.map',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
@@ -205,7 +188,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
i18nTitle: 'message.routes.plugin.video',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
@@ -221,7 +203,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor.quill',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
@@ -232,7 +213,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor.markdown',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
@@ -240,7 +220,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor._value',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
@@ -250,7 +229,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
i18nTitle: 'message.routes.plugin.swiper',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
@@ -261,7 +239,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
i18nTitle: 'message.routes.plugin.copy',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
@@ -272,7 +249,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
i18nTitle: 'message.routes.plugin.icon',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
@@ -283,7 +259,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
i18nTitle: 'message.routes.plugin.print',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
@@ -291,7 +266,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
i18nTitle: 'message.routes.plugin._value',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
@@ -307,7 +281,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
i18nTitle: 'message.routes.auth-demo.permission',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
@@ -318,7 +291,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
i18nTitle: 'message.routes.auth-demo.super',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
@@ -326,7 +298,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
i18nTitle: 'message.routes.auth-demo._value',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
@@ -342,7 +313,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
i18nTitle: 'message.routes.function.tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
@@ -375,7 +345,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
i18nTitle: 'message.routes.function._value',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
@@ -391,7 +360,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
i18nTitle: 'message.routes.exception.403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
@@ -402,7 +370,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
i18nTitle: 'message.routes.exception.404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
@@ -413,14 +380,12 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
i18nTitle: 'message.routes.exception.500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
i18nTitle: 'message.routes.exception._value',
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
@@ -442,7 +407,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '二级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -458,7 +422,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '三级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -466,21 +429,18 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '二级菜单(有子菜单)',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '一级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '多级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu._value',
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
}
|
||||
@@ -496,7 +456,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
i18nTitle: 'message.routes.management.auth',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
@@ -507,7 +466,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
i18nTitle: 'message.routes.management.role',
|
||||
requiresAuth: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
@@ -518,7 +476,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
i18nTitle: 'message.routes.management.user',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
@@ -529,7 +486,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
i18nTitle: 'message.routes.management.route',
|
||||
requiresAuth: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
@@ -537,7 +493,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
i18nTitle: 'message.routes.management._value',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
@@ -548,9 +503,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
i18nTitle: 'message.routes.about',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
singleLayout: 'basic',
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
@@ -570,8 +523,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis',
|
||||
i18nTitle: 'message.routes.dashboard.analysis'
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -581,16 +533,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench',
|
||||
i18nTitle: 'message.routes.dashboard.workbench'
|
||||
icon: 'icon-park-outline:workbench'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1,
|
||||
i18nTitle: 'message.routes.dashboard._value'
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -604,7 +554,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vue文档',
|
||||
i18nTitle: 'message.routes.document.vue',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
@@ -615,7 +564,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'vite文档',
|
||||
i18nTitle: 'message.routes.document.vite',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vitejs'
|
||||
}
|
||||
@@ -626,7 +574,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'naive文档',
|
||||
i18nTitle: 'message.routes.document.naive',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:naiveui'
|
||||
}
|
||||
@@ -637,7 +584,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '项目文档',
|
||||
i18nTitle: 'message.routes.document.project',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo'
|
||||
}
|
||||
@@ -647,7 +593,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
path: '/document/project-link',
|
||||
meta: {
|
||||
title: '项目文档(外链)',
|
||||
i18nTitle: 'message.routes.document.project-link',
|
||||
requiresAuth: true,
|
||||
localIcon: 'logo',
|
||||
href: 'https://docs.soybean.pro/'
|
||||
@@ -656,7 +601,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '文档',
|
||||
i18nTitle: 'message.routes.document._value',
|
||||
icon: 'mdi:file-document-multiple-outline',
|
||||
order: 2
|
||||
}
|
||||
@@ -672,7 +616,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '按钮',
|
||||
i18nTitle: 'message.routes.component.button',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
@@ -683,7 +626,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '卡片',
|
||||
i18nTitle: 'message.routes.component.card',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:card-outline'
|
||||
}
|
||||
@@ -694,7 +636,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '表格',
|
||||
i18nTitle: 'message.routes.component.table',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large'
|
||||
}
|
||||
@@ -702,7 +643,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '组件示例',
|
||||
i18nTitle: 'message.routes.component._value',
|
||||
icon: 'cib:app-store',
|
||||
order: 3
|
||||
}
|
||||
@@ -723,7 +663,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'ECharts',
|
||||
i18nTitle: 'message.routes.plugin.charts.echarts',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:apacheecharts'
|
||||
}
|
||||
@@ -734,7 +673,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'AntV',
|
||||
i18nTitle: 'message.routes.plugin.charts.antv',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:antdesign'
|
||||
}
|
||||
@@ -742,7 +680,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '图表',
|
||||
i18nTitle: 'message.routes.plugin.charts._value',
|
||||
icon: 'mdi:chart-areaspline'
|
||||
}
|
||||
},
|
||||
@@ -752,7 +689,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '地图',
|
||||
i18nTitle: 'message.routes.plugin.map',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:map'
|
||||
}
|
||||
@@ -763,7 +699,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '视频',
|
||||
i18nTitle: 'message.routes.plugin.video',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:video'
|
||||
}
|
||||
@@ -779,7 +714,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '富文本编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor.quill',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:file-document-edit-outline'
|
||||
}
|
||||
@@ -790,7 +724,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'markdown编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor.markdown',
|
||||
requiresAuth: true,
|
||||
icon: 'ri:markdown-line'
|
||||
}
|
||||
@@ -798,7 +731,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '编辑器',
|
||||
i18nTitle: 'message.routes.plugin.editor._value',
|
||||
icon: 'icon-park-outline:editor'
|
||||
}
|
||||
},
|
||||
@@ -808,7 +740,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Swiper插件',
|
||||
i18nTitle: 'message.routes.plugin.swiper',
|
||||
requiresAuth: true,
|
||||
icon: 'simple-icons:swiper'
|
||||
}
|
||||
@@ -819,7 +750,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '剪贴板',
|
||||
i18nTitle: 'message.routes.plugin.copy',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:clipboard-outline'
|
||||
}
|
||||
@@ -830,7 +760,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '图标',
|
||||
i18nTitle: 'message.routes.plugin.icon',
|
||||
requiresAuth: true,
|
||||
localIcon: 'custom-icon'
|
||||
}
|
||||
@@ -841,7 +770,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '打印',
|
||||
i18nTitle: 'message.routes.plugin.print',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:printer'
|
||||
}
|
||||
@@ -849,7 +777,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '插件示例',
|
||||
i18nTitle: 'message.routes.plugin._value',
|
||||
icon: 'clarity:plugin-line',
|
||||
order: 4
|
||||
}
|
||||
@@ -865,26 +792,13 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
i18nTitle: 'message.routes.auth-demo.permission',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo_super',
|
||||
path: '/auth-demo/super',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
i18nTitle: 'message.routes.auth-demo.super',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
i18nTitle: 'message.routes.auth-demo._value',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
@@ -900,7 +814,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'Tab',
|
||||
i18nTitle: 'message.routes.function.tab',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-tab'
|
||||
}
|
||||
@@ -933,7 +846,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '功能',
|
||||
i18nTitle: 'message.routes.function._value',
|
||||
icon: 'icon-park-outline:all-application',
|
||||
order: 6
|
||||
}
|
||||
@@ -949,7 +861,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页403',
|
||||
i18nTitle: 'message.routes.exception.403',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-block'
|
||||
}
|
||||
@@ -960,7 +871,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页404',
|
||||
i18nTitle: 'message.routes.exception.404',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-web-asset-off'
|
||||
}
|
||||
@@ -971,14 +881,12 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '异常页500',
|
||||
i18nTitle: 'message.routes.exception.500',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-wifi-off'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
i18nTitle: 'message.routes.exception._value',
|
||||
title: '异常页',
|
||||
icon: 'ant-design:exception-outlined',
|
||||
order: 7
|
||||
@@ -1000,7 +908,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '二级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -1016,7 +923,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '三级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -1024,21 +930,18 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '二级菜单(有子菜单)',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '一级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '多级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu._value',
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
}
|
||||
@@ -1054,7 +957,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
i18nTitle: 'message.routes.management.auth',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:baseline-security'
|
||||
}
|
||||
@@ -1065,7 +967,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
i18nTitle: 'message.routes.management.role',
|
||||
requiresAuth: true,
|
||||
icon: 'carbon:user-role'
|
||||
}
|
||||
@@ -1076,7 +977,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '用户管理',
|
||||
i18nTitle: 'message.routes.management.user',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-manage-accounts'
|
||||
}
|
||||
@@ -1087,7 +987,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '路由管理',
|
||||
i18nTitle: 'message.routes.management.route',
|
||||
requiresAuth: true,
|
||||
icon: 'material-symbols:route'
|
||||
}
|
||||
@@ -1095,7 +994,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
i18nTitle: 'message.routes.management._value',
|
||||
icon: 'carbon:cloud-service-management',
|
||||
order: 9
|
||||
}
|
||||
@@ -1106,9 +1004,7 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
i18nTitle: 'message.routes.about',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
singleLayout: 'basic',
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
@@ -1128,27 +1024,14 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
meta: {
|
||||
title: '分析页',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:analysis',
|
||||
i18nTitle: 'message.routes.dashboard.analysis'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'dashboard_workbench',
|
||||
path: '/dashboard/workbench',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '工作台',
|
||||
requiresAuth: true,
|
||||
icon: 'icon-park-outline:workbench',
|
||||
i18nTitle: 'message.routes.dashboard.workbench'
|
||||
icon: 'icon-park-outline:analysis'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1,
|
||||
i18nTitle: 'message.routes.dashboard._value'
|
||||
order: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1162,26 +1045,13 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '权限切换',
|
||||
i18nTitle: 'message.routes.auth-demo.permission',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-construction'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'auth-demo_super',
|
||||
path: '/auth-demo/super',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '超级管理员可见',
|
||||
i18nTitle: 'message.routes.auth-demo.super',
|
||||
requiresAuth: true,
|
||||
icon: 'ic:round-supervisor-account'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '权限示例',
|
||||
i18nTitle: 'message.routes.auth-demo._value',
|
||||
icon: 'ic:baseline-security',
|
||||
order: 5
|
||||
}
|
||||
@@ -1202,7 +1072,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '二级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -1218,7 +1087,6 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '三级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new.third',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
@@ -1226,23 +1094,20 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
],
|
||||
meta: {
|
||||
title: '二级菜单(有子菜单)',
|
||||
i18nTitle: 'message.routes.multi-menu.first.second-new._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '一级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu.first._value',
|
||||
icon: 'mdi:menu'
|
||||
}
|
||||
}
|
||||
],
|
||||
meta: {
|
||||
title: '多级菜单',
|
||||
i18nTitle: 'message.routes.multi-menu._value',
|
||||
icon: 'carbon:menu',
|
||||
order: 8
|
||||
order: 7
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1251,12 +1116,10 @@ export const routeModel: Record<Auth.RoleType, AuthRoute.Route[]> = {
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '关于',
|
||||
i18nTitle: 'message.routes.about',
|
||||
requiresAuth: true,
|
||||
keepAlive: true,
|
||||
singleLayout: 'basic',
|
||||
icon: 'fluent:book-information-24-regular',
|
||||
order: 10
|
||||
order: 8
|
||||
}
|
||||
}
|
||||
]
|
||||
|
127
package.json
127
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soybean-admin",
|
||||
"version": "0.10.2",
|
||||
"version": "0.9.9",
|
||||
"description": "A fresh and elegant admin template, based on Vue3、Vite3、TypeScript、NaiveUI and UnoCSS. 一个基于Vue3、Vite3、TypeScript、NaiveUI and UnoCSS的清新优雅的中后台模版。",
|
||||
"author": {
|
||||
"name": "Soybean",
|
||||
@@ -44,78 +44,86 @@
|
||||
"build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build",
|
||||
"preview": "vite preview",
|
||||
"typecheck": "vue-tsc --noEmit --skipLibCheck",
|
||||
"lint": "eslint . --fix",
|
||||
"lint": "eslint . --fix --ext .js,.jsx,.mjs,.json,.ts,.tsx,.vue",
|
||||
"format": "soy prettier-format",
|
||||
"commit": "soy git-commit",
|
||||
"cleanup": "soy cleanup",
|
||||
"update-pkg": "soy update-pkg",
|
||||
"tsx": "tsx",
|
||||
"logo": "tsx ./scripts/logo.ts",
|
||||
"update-version": "bumpp --commit --push --tag",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s",
|
||||
"release": "standard-version",
|
||||
"prepare": "soy init-git-hooks"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "0.11.8",
|
||||
"@antv/g2": "4.2.10",
|
||||
"@better-scroll/core": "2.5.1",
|
||||
"@soybeanjs/vue-materials": "0.2.0",
|
||||
"@vueuse/core": "10.1.2",
|
||||
"@ant-design/icons-vue": "^6.1.0",
|
||||
"@fast-crud/fast-crud": "^1.13.6",
|
||||
"@fast-crud/fast-extends": "^1.13.6",
|
||||
"@fast-crud/ui-naive": "^1.13.6",
|
||||
"@fast-crud/ui-interface": "^1.13.6",
|
||||
"@antv/data-set": "^0.11.8",
|
||||
"@antv/g2": "^4.2.10",
|
||||
"@better-scroll/core": "^2.5.1",
|
||||
"@soybeanjs/vue-materials": "^0.1.9",
|
||||
"@vueuse/core": "^10.1.2",
|
||||
"axios": "1.4.0",
|
||||
"clipboard": "2.0.11",
|
||||
"colord": "2.9.3",
|
||||
"crypto-js": "4.1.1",
|
||||
"dayjs": "1.11.7",
|
||||
"echarts": "5.4.2",
|
||||
"form-data": "4.0.0",
|
||||
"lodash-es": "4.17.21",
|
||||
"clipboard": "^2.0.11",
|
||||
"colord": "^2.9.3",
|
||||
"crypto-js": "^4.1.1",
|
||||
"dayjs": "^1.11.7",
|
||||
"echarts": "^5.4.2",
|
||||
"form-data": "^4.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"naive-ui": "2.34.4",
|
||||
"pinia": "2.1.3",
|
||||
"print-js": "1.6.0",
|
||||
"qs": "6.11.2",
|
||||
"swiper": "9.3.2",
|
||||
"ua-parser-js": "1.0.35",
|
||||
"vditor": "3.9.3",
|
||||
"pinia": "^2.1.3",
|
||||
"print-js": "^1.6.0",
|
||||
"qs": "^6.11.2",
|
||||
"swiper": "^9.3.2",
|
||||
"ua-parser-js": "^1.0.35",
|
||||
"vditor": "^3.9.2",
|
||||
"vue": "3.3.4",
|
||||
"vue-i18n": "9.2.2",
|
||||
"vue-router": "4.2.2",
|
||||
"vuedraggable": "4.1.0",
|
||||
"wangeditor": "4.7.15",
|
||||
"xgplayer": "3.0.2"
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.2.1",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"wangeditor": "^4.7.15",
|
||||
"xgplayer": "^3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@amap/amap-jsapi-types": "0.0.13",
|
||||
"@iconify/json": "2.2.72",
|
||||
"@iconify/vue": "4.1.1",
|
||||
"@soybeanjs/cli": "0.4.1",
|
||||
"@soybeanjs/vite-plugin-vue-page-route": "0.0.5",
|
||||
"@types/bmapgl": "0.0.7",
|
||||
"@types/crypto-js": "4.1.1",
|
||||
"@types/node": "20.2.5",
|
||||
"@types/qs": "6.9.7",
|
||||
"@types/ua-parser-js": "0.7.36",
|
||||
"@unocss/preset-uno": "0.52.7",
|
||||
"@unocss/transformer-directives": "0.52.7",
|
||||
"@unocss/vite": "0.52.7",
|
||||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "3.0.1",
|
||||
"bumpp": "9.1.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint": "8.41.0",
|
||||
"eslint-config-soybeanjs": "0.4.8",
|
||||
"mockjs": "1.1.0",
|
||||
"rollup-plugin-visualizer": "5.9.0",
|
||||
"sass": "1.62.1",
|
||||
"simple-git-hooks": "2.8.1",
|
||||
"tsx": "3.12.7",
|
||||
"@amap/amap-jsapi-types": "^0.0.13",
|
||||
"@iconify/json": "^2.2.67",
|
||||
"@iconify/vue": "^4.1.1",
|
||||
"@soybeanjs/cli": "^0.1.9",
|
||||
"@soybeanjs/vite-plugin-vue-page-route": "^0.0.5",
|
||||
"@types/bmapgl": "^0.0.7",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/node": "20.2.1",
|
||||
"@types/qs": "^6.9.7",
|
||||
"@types/ua-parser-js": "^0.7.36",
|
||||
"@unocss/preset-uno": "^0.52.0",
|
||||
"@unocss/transformer-directives": "^0.52.0",
|
||||
"@unocss/vite": "^0.52.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.1",
|
||||
"conventional-changelog": "^3.1.25",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-soybeanjs": "^0.3.7",
|
||||
"lint-staged": "13.2.2",
|
||||
"mockjs": "^1.1.0",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"sass": "^1.62.1",
|
||||
"simple-git-hooks": "^2.8.1",
|
||||
"standard-version": "^9.5.0",
|
||||
"tsx": "^3.12.7",
|
||||
"typescript": "5.0.4",
|
||||
"unplugin-icons": "0.16.1",
|
||||
"unplugin-vue-components": "0.25.0",
|
||||
"vite": "4.3.9",
|
||||
"vite-plugin-compression": "0.5.1",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "0.24.1",
|
||||
"vite": "^4.3.8",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-mock": "2.9.8",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
"vite-plugin-pwa": "0.15.2",
|
||||
"vite-plugin-svg-icons": "2.0.1",
|
||||
"vite-plugin-progress": "^0.0.7",
|
||||
"vite-plugin-pwa": "^0.15.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "1.6.5"
|
||||
},
|
||||
"pnpm": {
|
||||
@@ -125,6 +133,9 @@
|
||||
},
|
||||
"simple-git-hooks": {
|
||||
"commit-msg": "pnpm soy git-commit-verify",
|
||||
"pre-commit": "pnpm typecheck && pnpm soy lint-staged"
|
||||
"pre-commit": "pnpm typecheck && pnpm lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,mjs,json,ts,tsx,vue}": "eslint . --fix"
|
||||
}
|
||||
}
|
||||
|
5688
pnpm-lock.yaml
generated
5688
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
16
src/App.vue
16
src/App.vue
@@ -7,20 +7,34 @@
|
||||
class="h-full"
|
||||
>
|
||||
<naive-provider>
|
||||
<router-view />
|
||||
<fs-ui-context>
|
||||
<router-view />
|
||||
</fs-ui-context>
|
||||
</naive-provider>
|
||||
</n-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { dateZhCN, zhCN } from 'naive-ui';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { subscribeStore, useThemeStore } from '@/store';
|
||||
import { useGlobalEvents } from '@/composables';
|
||||
|
||||
const theme = useThemeStore();
|
||||
const { locale, t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
subscribeStore();
|
||||
useGlobalEvents();
|
||||
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@@ -1,34 +1,14 @@
|
||||
import { effectScope, onScopeDispose, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useEventListener } from '@vueuse/core';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useTabStore, useThemeStore } from '@/store';
|
||||
|
||||
/** 全局事件 */
|
||||
export function useGlobalEvents() {
|
||||
const theme = useThemeStore();
|
||||
const tab = useTabStore();
|
||||
const route = useRoute();
|
||||
const { locale, t } = useI18n();
|
||||
const scope = effectScope();
|
||||
|
||||
/** 页面离开时缓存多页签数据 */
|
||||
useEventListener(window, 'beforeunload', () => {
|
||||
theme.cacheThemeSettings();
|
||||
tab.cacheTabRoutes();
|
||||
});
|
||||
|
||||
scope.run(() => {
|
||||
// 国际化切换时更新浏览器标签文本
|
||||
watch(
|
||||
() => locale.value,
|
||||
() => {
|
||||
document.title = route.meta.i18nTitle ? t(route.meta.i18nTitle) : route.meta.title;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
onScopeDispose(() => {
|
||||
scope.stop();
|
||||
});
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { computed, watch } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { breakpointsTailwind, useBreakpoints } from '@vueuse/core';
|
||||
import { useAppStore, useThemeStore } from '@/store';
|
||||
|
||||
@@ -63,16 +63,6 @@ export function useBasicLayout() {
|
||||
return w;
|
||||
});
|
||||
|
||||
watch(
|
||||
isMobile,
|
||||
newValue => {
|
||||
if (newValue) {
|
||||
app.setSiderCollapse(true);
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return {
|
||||
mode,
|
||||
isMobile,
|
||||
|
@@ -1,7 +1,6 @@
|
||||
<template>
|
||||
<admin-layout
|
||||
:mode="mode"
|
||||
:is-mobile="isMobile"
|
||||
:scroll-mode="theme.scrollMode"
|
||||
:scroll-el-id="app.scrollElId"
|
||||
:full-content="app.contentFull"
|
||||
@@ -17,7 +16,6 @@
|
||||
:footer-visible="theme.footer.visible"
|
||||
:fixed-footer="theme.footer.fixed"
|
||||
:right-footer="theme.footer.right"
|
||||
@click-mobile-sider-mask="app.setSiderCollapse(true)"
|
||||
>
|
||||
<template #header>
|
||||
<global-header v-bind="headerProps" />
|
||||
@@ -48,7 +46,7 @@ defineOptions({ name: 'BasicLayout' });
|
||||
const app = useAppStore();
|
||||
const theme = useThemeStore();
|
||||
|
||||
const { mode, isMobile, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||
const { mode, headerProps, siderVisible, siderWidth, siderCollapsedWidth } = useBasicLayout();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
@@ -9,7 +9,7 @@
|
||||
v-if="theme.header.crumb.showIcon"
|
||||
class="inline-block align-text-bottom mr-4px text-16px"
|
||||
/>
|
||||
<span>{{ breadcrumb.label }}</span>
|
||||
<span>{{ breadcrumb.i18nTitle ? t(breadcrumb.i18nTitle) : breadcrumb.label }}</span>
|
||||
</span>
|
||||
</n-dropdown>
|
||||
<template v-else>
|
||||
@@ -19,9 +19,9 @@
|
||||
class="inline-block align-text-bottom mr-4px text-16px"
|
||||
:class="{ 'text-#BBBBBB': theme.header.inverted }"
|
||||
/>
|
||||
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">
|
||||
{{ breadcrumb.label }}
|
||||
</span>
|
||||
<span :class="{ 'text-#BBBBBB': theme.header.inverted }">{{
|
||||
breadcrumb.i18nTitle ? t(breadcrumb.i18nTitle) : breadcrumb.label
|
||||
}}</span>
|
||||
</template>
|
||||
</n-breadcrumb-item>
|
||||
</template>
|
||||
@@ -45,13 +45,7 @@ const routeStore = useRouteStore();
|
||||
const { routerPush } = useRouterPush();
|
||||
|
||||
const breadcrumbs = computed(() =>
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root')).map(
|
||||
item => ({
|
||||
...item,
|
||||
label: item.i18nTitle ? t(item.i18nTitle) : item.label,
|
||||
options: item.options?.map(oItem => ({ ...oItem, label: oItem.i18nTitle ? t(oItem.i18nTitle) : oItem.label }))
|
||||
})
|
||||
)
|
||||
getBreadcrumbByRouteKey(route.name as string, routeStore.menus as App.GlobalMenuOption[], routePath('root'))
|
||||
);
|
||||
|
||||
function dropdownSelect(key: string) {
|
||||
|
@@ -22,10 +22,6 @@ const options = [
|
||||
{
|
||||
label: 'English',
|
||||
key: 'en'
|
||||
},
|
||||
{
|
||||
label: 'ភាសាខ្មែរ',
|
||||
key: 'km-KH'
|
||||
}
|
||||
];
|
||||
const handleSelect = (key: string) => {
|
||||
|
@@ -6,7 +6,7 @@
|
||||
>
|
||||
<component :is="icon" :class="[isMini ? 'text-16px' : 'text-20px']" />
|
||||
<p
|
||||
class="w-full text-center ellipsis-text text-12px transition-height duration-300 ease-in-out"
|
||||
class="text-12px overflow-hidden transition-height duration-300 ease-in-out"
|
||||
:class="[isMini ? 'h-0 pt-0' : 'h-24px pt-4px']"
|
||||
>
|
||||
{{ label }}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<dark-mode-container class="flex h-full" :inverted="theme.sider.inverted" @mouseleave="resetFirstDegreeMenus">
|
||||
<div class="flex-1-hidden flex-col-stretch h-full">
|
||||
<div class="flex-1 flex-col-stretch h-full">
|
||||
<global-logo :show-title="false" :style="{ height: theme.header.height + 'px' }" />
|
||||
<n-scrollbar class="flex-1-hidden">
|
||||
<mix-menu-detail
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div ref="tabRef" class="flex h-full pr-18px" :class="[isChromeMode ? 'items-end' : 'items-center gap-12px']">
|
||||
<PageTab
|
||||
<AdminTab
|
||||
v-for="item in tab.tabs"
|
||||
:key="item.fullPath"
|
||||
:mode="theme.tab.mode"
|
||||
@@ -20,7 +20,7 @@
|
||||
/>
|
||||
</template>
|
||||
{{ item.meta.i18nTitle ? t(item.meta.i18nTitle) : item.meta.title }}
|
||||
</PageTab>
|
||||
</AdminTab>
|
||||
</div>
|
||||
<context-menu
|
||||
:visible="dropdown.visible"
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, reactive, ref, watch } from 'vue';
|
||||
import { PageTab } from '@soybeanjs/vue-materials';
|
||||
import { AdminTab } from '@soybeanjs/vue-materials';
|
||||
import { useTabStore, useThemeStore } from '@/store';
|
||||
import { t } from '@/locales';
|
||||
import { ContextMenu } from './components';
|
||||
|
@@ -7,7 +7,7 @@ const locale: LocaleMessages<I18nType.Schema> = {
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: 'Dashboard',
|
||||
dashboard: 'Dashboard',
|
||||
analysis: 'Analysis',
|
||||
workbench: 'Workbench'
|
||||
},
|
||||
|
@@ -1,11 +1,9 @@
|
||||
import zhCN from './zh-cn';
|
||||
import en from './en';
|
||||
import kmKH from './km-KH';
|
||||
|
||||
const locales = {
|
||||
'zh-CN': zhCN,
|
||||
en,
|
||||
'km-KH': kmKH
|
||||
en
|
||||
};
|
||||
|
||||
export type LocaleKey = keyof typeof locales;
|
||||
|
@@ -1,85 +0,0 @@
|
||||
import type { LocaleMessages } from 'vue-i18n';
|
||||
|
||||
const locale: LocaleMessages<I18nType.Schema> = {
|
||||
message: {
|
||||
system: {
|
||||
title: 'ប្រព័ន្ធគ្រប់គ្រង'
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: 'ផ្ទាំងទិន្នន័យ',
|
||||
analysis: 'ផ្ទាំងវិភាគ',
|
||||
workbench: 'ផ្ទាំងការងារ'
|
||||
},
|
||||
document: {
|
||||
_value: 'ឯកសារ',
|
||||
vue: 'ឯកសារ Vue',
|
||||
vite: 'ឯកសារ Vite',
|
||||
naive: 'ឯកសារ NaiveUI',
|
||||
project: 'ឯកសារគម្រោង',
|
||||
'project-link': 'ឯកសារគម្រោង(href)'
|
||||
},
|
||||
component: {
|
||||
_value: 'សមាសភាគ',
|
||||
button: 'ប៊ូតុង',
|
||||
card: 'កាត',
|
||||
table: 'តារាង'
|
||||
},
|
||||
plugin: {
|
||||
_value: 'មុខងារជំនួយ',
|
||||
charts: {
|
||||
_value: 'តារាង Chart',
|
||||
echarts: 'តារាង ECharts',
|
||||
antv: 'AntV'
|
||||
},
|
||||
copy: 'ចម្លង',
|
||||
editor: {
|
||||
_value: 'កែប្រែ',
|
||||
quill: 'Quill',
|
||||
markdown: 'Markdown'
|
||||
},
|
||||
icon: 'អាយខន',
|
||||
map: 'ផែនទី',
|
||||
print: 'បោះពុម្ភ',
|
||||
swiper: 'Swiper',
|
||||
video: 'វីដេអូ'
|
||||
},
|
||||
'auth-demo': {
|
||||
_value: 'ឌីមូ Auth',
|
||||
permission: 'បិទ/បើកការអនុញ្ញាត',
|
||||
super: 'Super Auth'
|
||||
},
|
||||
function: {
|
||||
_value: 'មុខងារ',
|
||||
tab: 'ថេបប្រព័ន្ធ'
|
||||
},
|
||||
exception: {
|
||||
_value: 'ករណីពិេសស',
|
||||
403: '403',
|
||||
404: '404',
|
||||
500: '500'
|
||||
},
|
||||
'multi-menu': {
|
||||
_value: 'ម៉ឺនុយពហុដឺក្រេ',
|
||||
first: {
|
||||
_value: 'ដឺក្រេទី១',
|
||||
second: 'ដែក្រេទី២',
|
||||
'second-new': {
|
||||
_value: 'ដឺក្រេទី២មានអនុក្រោម',
|
||||
third: 'ដឺក្រេទី៣'
|
||||
}
|
||||
}
|
||||
},
|
||||
management: {
|
||||
_value: 'ការគ្រប់គ្រងប្រព័ន្ធ',
|
||||
auth: 'Auth',
|
||||
role: 'សិទ្ធី',
|
||||
route: 'ផ្លូវប្រព័ន្ធ',
|
||||
user: 'អ្នកប្រើប្រាស់'
|
||||
},
|
||||
about: 'អំពីប្រព័ន្ធ'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default locale;
|
@@ -7,7 +7,7 @@ const locale: LocaleMessages<I18nType.Schema> = {
|
||||
},
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: '仪表盘',
|
||||
dashboard: '仪表盘',
|
||||
analysis: '分析页',
|
||||
workbench: '工作台'
|
||||
},
|
||||
|
@@ -3,7 +3,7 @@ import App from './App.vue';
|
||||
import AppLoading from './components/common/app-loading.vue';
|
||||
import { setupDirectives } from './directives';
|
||||
import { setupRouter } from './router';
|
||||
import { setupAssets } from './plugins';
|
||||
import { setupAssets, setupFastCrud } from './plugins';
|
||||
import { setupStore } from './store';
|
||||
import { setupI18n } from './locales';
|
||||
|
||||
@@ -29,7 +29,7 @@ async function setupApp() {
|
||||
|
||||
setupI18n(app);
|
||||
|
||||
appLoading.unmount();
|
||||
setupFastCrud(app);
|
||||
|
||||
// mount app
|
||||
app.mount('#app');
|
||||
|
11
src/plugins/fast-crud/common.scss
Normal file
11
src/plugins/fast-crud/common.scss
Normal file
@@ -0,0 +1,11 @@
|
||||
html:root {
|
||||
--baseColor: #fff;
|
||||
}
|
||||
/* 深色模式 */
|
||||
html.dark:root {
|
||||
--baseColor: #000;
|
||||
}
|
||||
|
||||
.fs-container {
|
||||
background-color: var(--baseColor);
|
||||
}
|
171
src/plugins/fast-crud/index.tsx
Normal file
171
src/plugins/fast-crud/index.tsx
Normal file
@@ -0,0 +1,171 @@
|
||||
import type { App } from 'vue';
|
||||
import type { FsSetupOptions, PageQuery } from '@fast-crud/fast-crud';
|
||||
// eslint-disable-next-line import/order
|
||||
import { FastCrud } from '@fast-crud/fast-crud';
|
||||
import '@fast-crud/fast-crud/dist/style.css';
|
||||
import './common.scss';
|
||||
|
||||
import type { FsUploaderOptions } from '@fast-crud/fast-extends';
|
||||
import {
|
||||
FsExtendsCopyable,
|
||||
FsExtendsEditor,
|
||||
FsExtendsJson,
|
||||
FsExtendsTime,
|
||||
FsExtendsUploader
|
||||
} from '@fast-crud/fast-extends';
|
||||
import '@fast-crud/fast-extends/dist/style.css';
|
||||
import UiNaive from '@fast-crud/ui-naive';
|
||||
import axios from 'axios';
|
||||
import type { VueI18n } from 'vue-i18n';
|
||||
import { mockRequest, request } from '@/service/request';
|
||||
import { setupNaive } from '@/plugins/fast-crud/naive';
|
||||
|
||||
/**
|
||||
* fast-crud的安装方法
|
||||
* 注意:在App.vue中,需要用fs-ui-context组件包裹RouterView,让fs-crud拥有message、notification、dialog的能力
|
||||
* @param app
|
||||
* @param options
|
||||
*/
|
||||
export type FsSetupOpts = {
|
||||
i18n?: VueI18n;
|
||||
};
|
||||
function install(app: App, options: FsSetupOpts = {}) {
|
||||
// 安装naive ui 常用组件
|
||||
setupNaive(app);
|
||||
app.use(UiNaive);
|
||||
app.use(FastCrud, {
|
||||
i18n: options.i18n,
|
||||
async dictRequest(context: { url: string }) {
|
||||
const url = context.url;
|
||||
let res: Service.SuccessResult | Service.FailedResult;
|
||||
if (url && url.startsWith('/mock')) {
|
||||
// 如果是crud开头的dict请求视为mock
|
||||
res = await mockRequest.get(url.replace('/mock', ''));
|
||||
} else {
|
||||
res = await request.get(url);
|
||||
}
|
||||
res = res || {};
|
||||
return res.data || [];
|
||||
},
|
||||
/**
|
||||
* useCrud时会被执行
|
||||
*/
|
||||
commonOptions() {
|
||||
return {
|
||||
table: {
|
||||
size: 'small',
|
||||
pagination: false
|
||||
},
|
||||
search: {
|
||||
options: {
|
||||
size: 'medium'
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
buttons: {
|
||||
view: { text: null, icon: 'EyeOutlined', size: 'small' },
|
||||
edit: { text: null, icon: 'EditOutlined', size: 'small' },
|
||||
remove: { type: 'error', text: null, icon: 'DeleteOutlined', size: 'small' }
|
||||
},
|
||||
dropdown: {
|
||||
more: { size: 'small' }
|
||||
}
|
||||
},
|
||||
request: {
|
||||
// 查询参数转换
|
||||
transformQuery: (query: PageQuery) => {
|
||||
const { page, form, sort } = query;
|
||||
const limit = page.pageSize;
|
||||
const currentPage = page.currentPage ?? 1;
|
||||
const offset = limit * (currentPage - 1);
|
||||
|
||||
return {
|
||||
page: {
|
||||
limit,
|
||||
offset
|
||||
},
|
||||
query: form,
|
||||
sort: sort || {}
|
||||
};
|
||||
},
|
||||
// page请求结果转换
|
||||
transformRes: originPageRes => {
|
||||
const { res } = originPageRes;
|
||||
const pageSize = res.limit;
|
||||
let currentPage = res.offset / pageSize;
|
||||
if (res.offset % pageSize === 0) {
|
||||
currentPage += 1;
|
||||
}
|
||||
return { currentPage, pageSize, ...res };
|
||||
}
|
||||
},
|
||||
form: {
|
||||
display: 'flex', // 表单布局
|
||||
labelWidth: '120px' // 表单label宽度
|
||||
}
|
||||
};
|
||||
// 从 useCrud({permission}) 里获取permission参数,去设置各个按钮的权限
|
||||
// const crudPermission = useCrudPermission(context);
|
||||
// return crudPermission.merge(opts);
|
||||
}
|
||||
} as FsSetupOptions);
|
||||
|
||||
// fast-extends里面的扩展组件均为异步组件,只有在使用时才会被加载,并不会影响首页加载速度
|
||||
// 安装editor
|
||||
app.use(FsExtendsEditor, {
|
||||
// 编辑器的公共配置
|
||||
wangEditor: {}
|
||||
});
|
||||
app.use(FsExtendsJson);
|
||||
app.use(FsExtendsCopyable);
|
||||
// 安装uploader 公共参数
|
||||
const uploaderOptions: FsUploaderOptions = {
|
||||
defaultType: 'form',
|
||||
form: {
|
||||
action: 'http://www.docmirror.cn:7070/api/upload/form/upload',
|
||||
name: 'file',
|
||||
withCredentials: false,
|
||||
uploadRequest: async props => {
|
||||
const { action, file, onProgress } = props;
|
||||
const data = new FormData();
|
||||
data.append('file', file);
|
||||
const res = await axios.post(action, data, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
},
|
||||
timeout: 60000,
|
||||
onUploadProgress(progress) {
|
||||
onProgress({ percent: Math.round((progress.loaded / progress.total!) * 100) });
|
||||
}
|
||||
});
|
||||
// 上传完成后的结果,一般返回个url 或者key,具体看你的后台返回啥
|
||||
return res.data.data;
|
||||
},
|
||||
async successHandle(ret: string) {
|
||||
// 上传完成后的结果处理, 此处应转换格式为{url:xxx,key:xxx}
|
||||
return {
|
||||
url: `http://www.docmirror.cn:7070${ret}`,
|
||||
key: ret.replace('/api/upload/form/download?key=', '')
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
app.use(FsExtendsUploader, uploaderOptions);
|
||||
|
||||
// 安装editor
|
||||
app.use(FsExtendsEditor, {
|
||||
// 编辑器的公共配置
|
||||
wangEditor: {}
|
||||
});
|
||||
app.use(FsExtendsJson);
|
||||
app.use(FsExtendsTime);
|
||||
app.use(FsExtendsCopyable);
|
||||
}
|
||||
|
||||
export default {
|
||||
install
|
||||
};
|
||||
|
||||
export function setupFastCrud(app: App<Element>, options: FsSetupOpts = {}) {
|
||||
install(app, options);
|
||||
}
|
59
src/plugins/fast-crud/naive.ts
Normal file
59
src/plugins/fast-crud/naive.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import type { App } from 'vue';
|
||||
import * as NaiveUI from 'naive-ui';
|
||||
|
||||
const naive = NaiveUI.create({
|
||||
components: [
|
||||
NaiveUI.NInput,
|
||||
NaiveUI.NButton,
|
||||
NaiveUI.NForm,
|
||||
NaiveUI.NFormItem,
|
||||
NaiveUI.NCheckboxGroup,
|
||||
NaiveUI.NCheckbox,
|
||||
NaiveUI.NIcon,
|
||||
NaiveUI.NDropdown,
|
||||
NaiveUI.NTooltip,
|
||||
NaiveUI.NTabs,
|
||||
NaiveUI.NTabPane,
|
||||
NaiveUI.NCard,
|
||||
NaiveUI.NRow,
|
||||
NaiveUI.NCol,
|
||||
NaiveUI.NDrawer,
|
||||
NaiveUI.NDrawerContent,
|
||||
NaiveUI.NDivider,
|
||||
NaiveUI.NSwitch,
|
||||
NaiveUI.NBadge,
|
||||
NaiveUI.NAlert,
|
||||
NaiveUI.NTag,
|
||||
NaiveUI.NProgress,
|
||||
NaiveUI.NDatePicker,
|
||||
NaiveUI.NGrid,
|
||||
NaiveUI.NGridItem,
|
||||
NaiveUI.NDataTable,
|
||||
NaiveUI.NPagination,
|
||||
NaiveUI.NSelect,
|
||||
NaiveUI.NRadioGroup,
|
||||
NaiveUI.NRadio,
|
||||
NaiveUI.NInputGroup,
|
||||
NaiveUI.NTable,
|
||||
NaiveUI.NInputNumber,
|
||||
NaiveUI.NLoadingBarProvider,
|
||||
NaiveUI.NModal,
|
||||
NaiveUI.NUpload,
|
||||
NaiveUI.NTree,
|
||||
NaiveUI.NSpin,
|
||||
NaiveUI.NTimePicker,
|
||||
|
||||
// add by fs
|
||||
NaiveUI.NCascader,
|
||||
NaiveUI.NRadioButton,
|
||||
NaiveUI.NTreeSelect,
|
||||
NaiveUI.NImageGroup,
|
||||
NaiveUI.NImage,
|
||||
NaiveUI.NCollapse,
|
||||
NaiveUI.NCollapseItem
|
||||
]
|
||||
});
|
||||
|
||||
export function setupNaive(app: App<Element>) {
|
||||
app.use(naive);
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
import { setupFastCrud } from '@/plugins/fast-crud';
|
||||
import setupAssets from './assets';
|
||||
|
||||
export { setupFastCrud };
|
||||
export { setupAssets };
|
||||
|
45
src/router/modules/crud.ts
Normal file
45
src/router/modules/crud.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
const component: any = {
|
||||
name: 'crud',
|
||||
path: '/crud',
|
||||
component: 'basic',
|
||||
meta: {
|
||||
title: 'CRUD示例',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:table-large',
|
||||
order: 4
|
||||
},
|
||||
children: [
|
||||
{
|
||||
name: 'crud_demo',
|
||||
path: '/crud/demo',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '基本示例',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'crud_header_group',
|
||||
path: '/crud/header_group',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: '多级表头',
|
||||
requiresAuth: true,
|
||||
icon: 'mdi:button-cursor'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'crud_doc',
|
||||
path: '/crud/doc',
|
||||
component: 'self',
|
||||
meta: {
|
||||
title: 'FastCrud文档',
|
||||
requiresAuth: true,
|
||||
icon: 'logos:vue'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
export default component;
|
@@ -30,7 +30,7 @@ const dashboard: AuthRoute.Route = {
|
||||
title: '仪表盘',
|
||||
icon: 'mdi:monitor-dashboard',
|
||||
order: 1,
|
||||
i18nTitle: 'message.routes.dashboard._value'
|
||||
i18nTitle: 'message.routes.dashboard.dashboard'
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { nextTick } from 'vue';
|
||||
import { defineStore } from 'pinia';
|
||||
import { LAYOUT_SCROLL_EL_ID } from '@soybeanjs/vue-materials';
|
||||
import { SCROLL_EL_ID } from '@soybeanjs/vue-materials';
|
||||
|
||||
interface AppState {
|
||||
/** 滚动元素的id */
|
||||
@@ -21,7 +21,7 @@ interface AppState {
|
||||
|
||||
export const useAppStore = defineStore('app-store', {
|
||||
state: (): AppState => ({
|
||||
scrollElId: LAYOUT_SCROLL_EL_ID,
|
||||
scrollElId: SCROLL_EL_ID,
|
||||
contentFull: false,
|
||||
disableMainXScroll: false,
|
||||
reloadFlag: true,
|
||||
|
2
src/typings/env.d.ts
vendored
2
src/typings/env.d.ts
vendored
@@ -66,8 +66,6 @@ interface ImportMetaEnv {
|
||||
readonly VITE_PROD_MOCK?: 'Y' | 'N';
|
||||
/** hash路由模式 */
|
||||
readonly VITE_HASH_ROUTE?: 'Y' | 'N';
|
||||
/** 是否应用自动生成路由的插件 */
|
||||
readonly VITE_SOYBEAN_ROUTE_PLUGIN?: 'Y' | 'N';
|
||||
/** 是否是部署的vercel */
|
||||
readonly VITE_VERCEL?: 'Y' | 'N';
|
||||
}
|
||||
|
10
src/typings/page-route.d.ts
vendored
10
src/typings/page-route.d.ts
vendored
@@ -30,6 +30,12 @@ declare namespace PageRoute {
|
||||
| 'component_button'
|
||||
| 'component_card'
|
||||
| 'component_table'
|
||||
| 'crud'
|
||||
| 'crud_demo'
|
||||
| 'crud_doc'
|
||||
| 'crud_header'
|
||||
| 'crud_header_group'
|
||||
| 'crud_source'
|
||||
| 'dashboard'
|
||||
| 'dashboard_analysis'
|
||||
| 'dashboard_workbench'
|
||||
@@ -89,6 +95,10 @@ declare namespace PageRoute {
|
||||
| 'component_button'
|
||||
| 'component_card'
|
||||
| 'component_table'
|
||||
| 'crud_demo'
|
||||
| 'crud_doc'
|
||||
| 'crud_header_group'
|
||||
| 'crud_source'
|
||||
| 'dashboard_analysis'
|
||||
| 'dashboard_workbench'
|
||||
| 'document_naive'
|
||||
|
6
src/typings/system.d.ts
vendored
6
src/typings/system.d.ts
vendored
@@ -254,7 +254,7 @@ declare namespace App {
|
||||
hasChildren: boolean;
|
||||
icon?: import('vue').Component;
|
||||
i18nTitle?: string;
|
||||
options?: (import('naive-ui/es/dropdown/src/interface').DropdownMixedOption & { i18nTitle?: string })[];
|
||||
options?: import('naive-ui/es/dropdown/src/interface').DropdownMixedOption[];
|
||||
};
|
||||
|
||||
/** 多页签Tab的路由 */
|
||||
@@ -302,7 +302,7 @@ declare namespace App {
|
||||
}
|
||||
|
||||
declare namespace I18nType {
|
||||
type langType = 'en' | 'zh-CN' | 'km-KH';
|
||||
type langType = 'en' | 'zh-CN';
|
||||
|
||||
interface Schema {
|
||||
system: {
|
||||
@@ -310,7 +310,7 @@ declare namespace I18nType {
|
||||
};
|
||||
routes: {
|
||||
dashboard: {
|
||||
_value: string;
|
||||
dashboard: string;
|
||||
analysis: string;
|
||||
workbench: string;
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"></n-card>
|
||||
<n-card title="当前页面只有super才能看到" class="h-full shadow-sm rounded-16px"> </n-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
43
src/views/crud/demo/api.ts
Normal file
43
src/views/crud/demo/api.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { UserPageQuery } from '@fast-crud/fast-crud';
|
||||
import { mockRequest } from '@/service/request';
|
||||
|
||||
const request = mockRequest;
|
||||
const apiPrefix = '/crud/demo';
|
||||
|
||||
export type DemoRecord = {
|
||||
id: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
function resHandle(res: any) {
|
||||
return res.data;
|
||||
}
|
||||
export async function GetList(query: UserPageQuery) {
|
||||
const res = await request.post(`${apiPrefix}/page`, query);
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function AddObj(obj: DemoRecord) {
|
||||
const res = await request.post(`${apiPrefix}/add`, obj);
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function UpdateObj(obj: DemoRecord) {
|
||||
const res = await request.post(`${apiPrefix}/update`, obj);
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function DelObj(id: number) {
|
||||
const res = await request.post(`${apiPrefix}/delete`, { id });
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function GetObj(id: number) {
|
||||
const res = await request.get(`${apiPrefix}/info`, { params: { id } });
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function BatchDelete(ids: number[]) {
|
||||
const res = await request.post(`${apiPrefix}/batchDelete`, { ids });
|
||||
return resHandle(res);
|
||||
}
|
114
src/views/crud/demo/crud.tsx
Normal file
114
src/views/crud/demo/crud.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import type { AddReq, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
|
||||
import { dict } from '@fast-crud/fast-crud';
|
||||
import dayjs from 'dayjs';
|
||||
import * as api from './api';
|
||||
|
||||
export default function createCrudOptions(): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return api.GetList(query);
|
||||
};
|
||||
const editRequest = async (ctx: EditReq) => {
|
||||
const { form, row } = ctx;
|
||||
form.id = row.id;
|
||||
return api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async (ctx: DelReq) => {
|
||||
const { row } = ctx;
|
||||
return api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async (req: AddReq) => {
|
||||
const { form } = req;
|
||||
return api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
container: {
|
||||
is: 'fs-layout-card'
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: 'ID',
|
||||
key: 'id',
|
||||
type: 'number',
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
datetime: {
|
||||
title: '时间',
|
||||
type: 'datetime',
|
||||
// naive 默认仅支持数字类型时间戳作为日期输入与输出
|
||||
// 字符串类型的时间需要转换格式
|
||||
valueBuilder(context) {
|
||||
const { value, row, key } = context;
|
||||
if (value) {
|
||||
// naive 默认仅支持时间戳作为日期输入与输出
|
||||
row[key] = dayjs(value).valueOf();
|
||||
}
|
||||
},
|
||||
valueResolve(context) {
|
||||
const { value, form, key } = context;
|
||||
if (value) {
|
||||
form[key] = dayjs(value).format('YYYY-MM-DD HH:mm:ss');
|
||||
}
|
||||
}
|
||||
},
|
||||
select: {
|
||||
title: '状态',
|
||||
search: { show: true },
|
||||
type: 'dict-select',
|
||||
dict: dict({
|
||||
url: '/mock/crud/demo/dict'
|
||||
})
|
||||
},
|
||||
text: {
|
||||
title: '文本',
|
||||
type: 'text',
|
||||
search: { show: true }
|
||||
},
|
||||
copyable: {
|
||||
title: '可复制',
|
||||
type: ['text', 'copyable'],
|
||||
search: { show: true }
|
||||
},
|
||||
avatar: {
|
||||
title: '头像裁剪',
|
||||
type: 'cropper-uploader'
|
||||
},
|
||||
upload: {
|
||||
title: '文件上传',
|
||||
type: 'file-uploader'
|
||||
},
|
||||
richtext: {
|
||||
title: '富文本',
|
||||
type: 'editor-wang5',
|
||||
column: {
|
||||
// cell中不显示
|
||||
show: false
|
||||
},
|
||||
form: {
|
||||
col: {
|
||||
// 横跨两列
|
||||
span: 24
|
||||
},
|
||||
component: {
|
||||
style: {
|
||||
height: '300px'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
28
src/views/crud/demo/index.vue
Normal file
28
src/views/crud/demo/index.vue
Normal file
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from 'vue';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import createCrudOptions from './crud';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ComponentCrud',
|
||||
setup() {
|
||||
const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
13
src/views/crud/doc/index.vue
Normal file
13
src/views/crud/doc/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<iframe class="wh-full" :src="src"></iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const src = ref('http://fast-crud.docmirror.cn/');
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
43
src/views/crud/header_group/api.ts
Normal file
43
src/views/crud/header_group/api.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { UserPageQuery } from '@fast-crud/fast-crud';
|
||||
import { mockRequest } from '@/service/request';
|
||||
|
||||
const request = mockRequest;
|
||||
const apiPrefix = '/crud/header-group';
|
||||
|
||||
export type HeaderGroupRecord = {
|
||||
id: number;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
function resHandle(res: any) {
|
||||
return res.data;
|
||||
}
|
||||
export async function GetList(query: UserPageQuery) {
|
||||
const res = await request.post(`${apiPrefix}/page`, query);
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function AddObj(obj: HeaderGroupRecord) {
|
||||
const res = await request.post(`${apiPrefix}/add`, obj);
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function UpdateObj(obj: HeaderGroupRecord) {
|
||||
const res = await request.post(`${apiPrefix}/update`, obj);
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function DelObj(id: number) {
|
||||
const res = await request.post(`${apiPrefix}/delete`, { id });
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function GetObj(id: number) {
|
||||
const res = await request.get(`${apiPrefix}/info`, { params: { id } });
|
||||
return resHandle(res);
|
||||
}
|
||||
|
||||
export async function BatchDelete(ids: number[]) {
|
||||
const res = await request.post(`${apiPrefix}/batchDelete`, { ids });
|
||||
return resHandle(res);
|
||||
}
|
96
src/views/crud/header_group/crud.tsx
Normal file
96
src/views/crud/header_group/crud.tsx
Normal file
@@ -0,0 +1,96 @@
|
||||
import type { CreateCrudOptionsRet, UserPageQuery, UserPageRes } from '@fast-crud/fast-crud';
|
||||
import type { HeaderGroupRecord } from './api';
|
||||
import * as api from './api';
|
||||
|
||||
export default function createCrudOptions(): CreateCrudOptionsRet {
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return api.GetList(query);
|
||||
};
|
||||
const editRequest = async (ctx: { form: HeaderGroupRecord; row: HeaderGroupRecord }) => {
|
||||
const { form, row } = ctx;
|
||||
form.id = row.id;
|
||||
return api.UpdateObj(form);
|
||||
};
|
||||
const delRequest = async (ctx: { row: HeaderGroupRecord }) => {
|
||||
const { row } = ctx;
|
||||
return api.DelObj(row.id);
|
||||
};
|
||||
|
||||
const addRequest = async (ctx: { form: HeaderGroupRecord }) => {
|
||||
const { form } = ctx;
|
||||
return api.AddObj(form);
|
||||
};
|
||||
return {
|
||||
crudOptions: {
|
||||
container: {
|
||||
// is: 'fs-layout-card'
|
||||
},
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest
|
||||
},
|
||||
form: {
|
||||
layout: 'flex',
|
||||
labelWidth: '100px' // 表单label宽度
|
||||
},
|
||||
table: { size: 'small' },
|
||||
columns: {
|
||||
id: {
|
||||
title: 'ID',
|
||||
key: 'id',
|
||||
type: 'number',
|
||||
column: {
|
||||
width: 50
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
user: {
|
||||
title: '用户信息',
|
||||
children: {
|
||||
name: {
|
||||
title: '姓名',
|
||||
type: 'text'
|
||||
},
|
||||
age: {
|
||||
title: '年龄',
|
||||
type: 'number'
|
||||
}
|
||||
}
|
||||
},
|
||||
address: {
|
||||
title: '地址',
|
||||
children: {
|
||||
area: {
|
||||
title: '地区',
|
||||
children: {
|
||||
province: {
|
||||
title: '省',
|
||||
type: 'text',
|
||||
search: { show: true }
|
||||
},
|
||||
city: {
|
||||
title: '市',
|
||||
search: { show: true },
|
||||
type: 'text'
|
||||
},
|
||||
county: {
|
||||
title: '区',
|
||||
search: { show: true },
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
},
|
||||
street: {
|
||||
title: '街道',
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
32
src/views/crud/header_group/index.vue
Normal file
32
src/views/crud/header_group/index.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="h-full fs-page-header-group">
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted } from 'vue';
|
||||
import { useFs } from '@fast-crud/fast-crud';
|
||||
import createCrudOptions from './crud';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'CrudHeaderGroup',
|
||||
setup() {
|
||||
const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
onMounted(() => {
|
||||
crudExpose.doRefresh();
|
||||
});
|
||||
|
||||
return {
|
||||
crudBinding,
|
||||
crudRef
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.fs-page-header-group {
|
||||
}
|
||||
</style>
|
13
src/views/crud/source/index.vue
Normal file
13
src/views/crud/source/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div class="h-full">
|
||||
<iframe class="wh-full" :src="src"></iframe>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
const src = ref('https://github.com/fast-crud/fast-crud');
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@@ -16,6 +16,10 @@ export const views: Record<
|
||||
component_button: () => import('./component/button/index.vue'),
|
||||
component_card: () => import('./component/card/index.vue'),
|
||||
component_table: () => import('./component/table/index.vue'),
|
||||
crud_demo: () => import('./crud/demo/index.vue'),
|
||||
crud_doc: () => import('./crud/doc/index.vue'),
|
||||
crud_header_group: () => import('./crud/header_group/index.vue'),
|
||||
crud_source: () => import('./crud/source/index.vue'),
|
||||
dashboard_analysis: () => import('./dashboard/analysis/index.vue'),
|
||||
dashboard_workbench: () => import('./dashboard/workbench/index.vue'),
|
||||
document_naive: () => import('./document/naive/index.vue'),
|
||||
|
Reference in New Issue
Block a user