Compare commits

..

1 Commits

Author SHA1 Message Date
Soybean
c70b2299a9 chore(release): release v0.10.4 tauri branch 2023-09-21 00:05:16 +08:00
34 changed files with 5646 additions and 1077 deletions

View File

@@ -1,3 +1,4 @@
!.env-config.ts
router-page.d.ts
*.svg
src-tauri/target

View File

@@ -72,6 +72,5 @@
"[html][css][less][scss][sass][markdown][yaml][yml][jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"prettier": {}
}
}

View File

@@ -5,25 +5,6 @@
[![license](https://img.shields.io/badge/license-MIT-green.svg)](./LICENSE) ![](https://img.shields.io/github/stars/honghuangdc/soybean-admin) ![](https://img.shields.io/github/forks/honghuangdc/soybean-admin)
## 注意 SoybeanAdmin 正在重构,全新 1.0 即将发布
Soybean Admin v1.0 :
- [x] 采用基于 pnpm 的 monorepo 管理项目
- [x] 第三方 soybeanjs 的工具库直接抽离到项目中(ElegantRouter除外),不再作为依赖
- [x] 采用全新的路由插件 ElegantRouter
- [x] 使用基于 ApiFox 的远程 mock 代替本地 mock
- [x] 基于现有路由插件迁移至新路由插件的指南
- [x] 代码实现遵循 SoybeanJS 的代码规范
- [ ] 项目的 main 分支保留系统核心部分,示例页面和无关核心的插件移至 example 分支
- [ ] 完整 1.0 版本的文档
1.0 源代码:[v1.0-beta](https://github.com/honghuangdc/soybean-admin/tree/v1.0-beta)
> 同时推出开源的 [AntDesignVue 版本](https://github.com/soybeanjs/soybean-admin-antd) 和 ElementPlus 版本
> 新开项目建议直接使用 [v1.0-beta](https://github.com/honghuangdc/soybean-admin/tree/v1.0-beta) 或 [AntDesignVue 版本](https://github.com/soybeanjs/soybean-admin-antd)
## 简介
[Soybean Admin](https://github.com/honghuangdc/soybean-admin) 是一个基于 Vue3、Vite3、TypeScript、NaiveUI、Pinia 和 UnoCSS 的清新优雅的中后台模版,它使用了最新流行的前端技术栈,内置丰富的主题配置,有着极高的代码规范,基于文件的路由系统以及基于 Mock 的动态权限路由,开箱即用的中后台前端解决方案,也可用于学习参考。
@@ -184,7 +165,7 @@ docker run -d -p 80:80 soybean-admin-image
<img src="https://i.loli.net/2021/11/24/1J6REWXiHomU2kM.jpg" style="width:200px" />
</div>
<div>
<p>添加本人微信,欢迎来业务咨询(技术交流请加QQ群)</p>
<p>添加本人微信,欢迎来技术交流,业务咨询</p>
<img src="https://s2.loli.net/2023/06/07/sVyCUFBvzQ9f5b7.jpg" style="width:200px" />
</div>
</div>

View File

@@ -5,6 +5,7 @@ import unocss from '@unocss/vite';
import progress from 'vite-plugin-progress';
import VueDevtools from 'vite-plugin-vue-devtools';
import pageRoute from '@soybeanjs/vite-plugin-vue-page-route';
import { webUpdateNotice } from '@plugin-web-update-notification/vite';
import unplugin from './unplugin';
import mock from './mock';
import visualizer from './visualizer';
@@ -27,7 +28,15 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin
...unplugin(viteEnv),
unocss(),
mock(viteEnv),
progress()
progress(),
webUpdateNotice({
notificationProps: {
title: '👋 有新版本了',
description: '点击刷新页面获取最新版本',
buttonText: '刷新',
dismissButtonText: '忽略'
}
})
];
if (viteEnv.VITE_VISUALIZER === 'Y') {

View File

@@ -3,6 +3,10 @@
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8" />
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Pragma" content="no-cache" />
<meta http-equiv="Cache-control" content="no-cache" />
<meta http-equiv="Cache" content="no-cache" />
<link rel="icon" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>%VITE_APP_NAME%</title>

View File

@@ -38,10 +38,13 @@
"dev": "cross-env VITE_SERVICE_ENV=dev vite",
"dev:test": "cross-env VITE_SERVICE_ENV=test vite",
"dev:prod": "cross-env VITE_SERVICE_ENV=prod vite",
"dev:tauri": "pnpm tauri dev",
"build": "npm run typecheck && cross-env VITE_SERVICE_ENV=prod vite build",
"build:dev": "npm run typecheck && cross-env VITE_SERVICE_ENV=dev vite build",
"build:test": "npm run typecheck && cross-env VITE_SERVICE_ENV=test vite build",
"build:vercel": "cross-env VITE_HASH_ROUTE=Y VITE_VERCEL=Y vite build",
"build:tauri": "pnpm tauri build",
"tauri-icon": "pnpm tauri icon ./public/logo.png",
"preview": "vite preview",
"typecheck": "vue-tsc --noEmit --skipLibCheck",
"lint": "eslint . --fix",
@@ -59,8 +62,8 @@
"@antv/g2": "4.2.10",
"@better-scroll/core": "2.5.1",
"@soybeanjs/vue-materials": "0.2.0",
"@vueuse/core": "10.5.0",
"axios": "1.5.1",
"@vueuse/core": "10.4.1",
"axios": "1.5.0",
"clipboard": "2.0.11",
"colord": "2.9.3",
"crypto-js": "4.1.1",
@@ -68,56 +71,58 @@
"echarts": "5.4.3",
"form-data": "4.0.0",
"lodash-es": "4.17.21",
"naive-ui": "2.35.0",
"naive-ui": "2.34.4",
"pinia": "2.1.6",
"print-js": "1.6.0",
"qs": "6.11.2",
"socket.io-client": "4.7.2",
"swiper": "10.3.1",
"swiper": "10.2.0",
"ua-parser-js": "1.0.36",
"vditor": "3.9.6",
"vditor": "3.9.5",
"vue": "3.3.4",
"vue-i18n": "9.5.0",
"vue-router": "4.2.5",
"vue-i18n": "9.4.1",
"vue-router": "4.2.4",
"vuedraggable": "4.1.0",
"wangeditor": "4.7.15",
"xgplayer": "3.0.9"
},
"devDependencies": {
"@amap/amap-jsapi-types": "0.0.13",
"@iconify/json": "2.2.128",
"@iconify/json": "2.2.118",
"@iconify/vue": "4.1.1",
"@soybeanjs/cli": "0.7.4",
"@plugin-web-update-notification/vite": "^1.6.5",
"@soybeanjs/cli": "0.7.1",
"@soybeanjs/vite-plugin-vue-page-route": "0.0.10",
"@tauri-apps/cli": "^1.3.1",
"@types/bmapgl": "0.0.7",
"@types/crypto-js": "4.1.2",
"@types/node": "20.8.4",
"@types/node": "20.6.3",
"@types/qs": "6.9.8",
"@types/ua-parser-js": "0.7.37",
"@unocss/preset-uno": "0.56.5",
"@unocss/transformer-directives": "0.56.5",
"@unocss/vite": "0.56.5",
"@vitejs/plugin-vue": "4.4.0",
"@unocss/preset-uno": "0.56.0",
"@unocss/transformer-directives": "0.56.0",
"@unocss/vite": "0.56.0",
"@vitejs/plugin-vue": "4.3.4",
"@vitejs/plugin-vue-jsx": "3.0.2",
"cross-env": "7.0.3",
"eslint": "8.51.0",
"eslint-config-soybeanjs": "0.5.7",
"eslint": "8.49.0",
"eslint-config-soybeanjs": "0.5.6",
"mockjs": "1.1.0",
"rollup-plugin-visualizer": "5.9.2",
"sass": "1.69.3",
"sass": "1.67.0",
"simple-git-hooks": "2.9.0",
"tsx": "3.13.0",
"tsx": "3.12.10",
"typescript": "5.2.2",
"unplugin-icons": "0.17.0",
"unplugin-vue-components": "0.25.2",
"vite": "4.4.11",
"vite": "4.4.9",
"vite-plugin-compression": "0.5.1",
"vite-plugin-mock": "2.9.8",
"vite-plugin-progress": "0.0.7",
"vite-plugin-pwa": "0.16.5",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "1.0.0-rc.5",
"vue-tsc": "1.8.19"
"vite-plugin-vue-devtools": "1.0.0-rc.4",
"vue-tsc": "1.8.13"
},
"pnpm": {
"patchedDependencies": {
@@ -126,7 +131,7 @@
},
"simple-git-hooks": {
"commit-msg": "pnpm soy git-commit-verify",
"pre-commit": "pnpm typecheck && pnpm soy lint-staged"
"pre-commit": "pnpm typecheck && pnpm lint"
},
"soybean": {
"useSoybeanToken": true

2149
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

3
src-tauri/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Generated by Cargo
# will have compiled files and executables
/target/

4302
src-tauri/Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

28
src-tauri/Cargo.toml Normal file
View File

@@ -0,0 +1,28 @@
[package]
name = "app"
version = "0.1.0"
description = "A Tauri App"
authors = ["you"]
license = ""
repository = ""
default-run = "app"
edition = "2021"
rust-version = "1.57"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies]
tauri-build = { version = "1.1.1", features = [] }
[dependencies]
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.1.1", features = ["api-all"] }
[features]
# by default Tauri runs in production mode
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
default = [ "custom-protocol" ]
# this feature is used for production builds where `devPath` points to the filesystem
# DO NOT remove this
custom-protocol = [ "tauri/custom-protocol" ]

3
src-tauri/build.rs Normal file
View File

@@ -0,0 +1,3 @@
fn main() {
tauri_build::build()
}

BIN
src-tauri/icons/128x128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src-tauri/icons/32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src-tauri/icons/icon.icns Normal file

Binary file not shown.

BIN
src-tauri/icons/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
src-tauri/icons/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

10
src-tauri/src/main.rs Normal file
View File

@@ -0,0 +1,10 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
fn main() {
tauri::Builder::default()
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

60
src-tauri/tauri.conf.json Normal file
View File

@@ -0,0 +1,60 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"build": {
"beforeBuildCommand": "npm run build",
"beforeDevCommand": "npm run dev",
"devPath": "http://localhost:3200",
"distDir": "../dist"
},
"package": {
"productName": "soybean-admin",
"version": "0.10.4"
},
"tauri": {
"allowlist": {
"all": true
},
"bundle": {
"active": true,
"category": "DeveloperTool",
"copyright": "",
"deb": {
"depends": []
},
"externalBin": [],
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
"identifier": "cn.soybeanjs.tauri-admin",
"longDescription": "",
"macOS": {
"entitlements": null,
"exceptionDomain": "",
"frameworks": [],
"providerShortName": null,
"signingIdentity": null
},
"resources": [],
"shortDescription": "",
"targets": "all",
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": ""
}
},
"security": {
"csp": null
},
"updater": {
"active": false
},
"windows": [
{
"fullscreen": false,
"height": 800,
"resizable": true,
"title": "soybean-admin",
"width": 1000
}
]
}
}

View File

@@ -34,7 +34,6 @@ import { useRouter } from 'vue-router';
import { onKeyStroke, useDebounceFn } from '@vueuse/core';
import { useRouteStore } from '@/store';
import { useBasicLayout } from '@/composables';
import { $t } from '@/locales';
import SearchResult from './search-result.vue';
import SearchFooter from './search-footer.vue';
@@ -83,12 +82,14 @@ watch(show, async val => {
/** 查询 */
function search() {
resultOptions.value = routeStore.searchMenus.filter(menu => {
const trimKeyword = keyword.value.toLocaleLowerCase().trim();
const title = (menu.meta.i18nTitle ? $t(menu.meta.i18nTitle) : menu.meta.title).toLocaleLowerCase();
return trimKeyword && title.includes(trimKeyword);
});
activePath.value = resultOptions.value[0]?.path ?? '';
resultOptions.value = routeStore.searchMenus.filter(
menu => keyword.value && menu.meta?.title.toLocaleLowerCase().includes(keyword.value.toLocaleLowerCase().trim())
);
if (resultOptions.value?.length > 0) {
activePath.value = resultOptions.value[0].path;
} else {
activePath.value = '';
}
}
function handleClose() {

View File

@@ -12,9 +12,7 @@
@mouseenter="handleMouse(item)"
>
<svg-icon :icon="item.meta.icon" :local-icon="item.meta.localIcon" />
<span class="flex-1 ml-5px">
{{ (item.meta?.i18nTitle && $t(item.meta?.i18nTitle)) || item.meta?.title }}
</span>
<span class="flex-1 ml-5px">{{ item.meta?.title }}</span>
<icon-ant-design-enter-outlined class="icon text-20px p-2px mr-3px" />
</div>
</template>
@@ -25,7 +23,6 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { useThemeStore } from '@/store';
import { $t } from '@/locales';
defineOptions({ name: 'SearchResult' });

View File

@@ -1,4 +1,3 @@
import { getTopLevelMenu } from './helpers';
/**
* 获取面包屑数据
* @param activeKey - 当前页面路由的key
@@ -18,9 +17,13 @@ export function getBreadcrumbByRouteKey(activeKey: string, menus: App.GlobalMenu
*/
function getBreadcrumbMenu(activeKey: string, menus: App.GlobalMenuOption[]) {
const breadcrumbMenu: App.GlobalMenuOption[] = [];
const topLevelMenu = getTopLevelMenu(activeKey, menus);
const options = topLevelMenu ? getBreadcrumbMenuItem(activeKey, topLevelMenu as App.GlobalMenuOption) : [];
breadcrumbMenu.push(...options);
menus.some(menu => {
const flag = activeKey.includes(menu.routeName);
if (flag) {
breadcrumbMenu.push(...getBreadcrumbMenuItem(activeKey, menu));
}
return flag;
});
return breadcrumbMenu;
}

View File

@@ -17,18 +17,3 @@ function getConstantRouteName(route: AuthRoute.Route) {
}
return names;
}
/**
* 根据路由名称查找顶级菜单
* @param routeName - 当前页面路由的key
* @param menus - 菜单数据
*/
export function getTopLevelMenu(routeName: string, menus: App.GlobalMenuOption[]): App.GlobalMenuOption | undefined {
return menus.find(item => {
if (item.routeName === routeName) return true;
if (Array.isArray(item.children)) {
return getTopLevelMenu(routeName, item.children);
}
return false;
});
}

View File

@@ -63,29 +63,18 @@ export function translateMenuLabel(menus: App.GlobalMenuOption[]): App.GlobalMen
* @param menus - 菜单数据
*/
export function getActiveKeyPathsOfMenus(activeKey: string, menus: App.GlobalMenuOption[]) {
const keys = menus.map(menu => getActiveKeyPathsOfMenu(activeKey, menu)).flat(1);
return keys;
}
function getActiveKeyPathsOfMenu(activeKey: string, menu: App.GlobalMenuOption) {
const keys: string[] = [];
const lists: App.GlobalMenuOption[] = [];
function traverse(list: App.GlobalMenuOption[], parent: App.GlobalMenuOption | null = null) {
list.forEach((t: App.GlobalMenuOption) => {
lists.push(t);
if (parent) {
t.parent = parent;
}
if (t.children) {
traverse(t.children, t);
}
});
if (activeKey.startsWith(menu.routeName)) {
keys.push(menu.routeName);
}
if (menu.children) {
keys.push(...menu.children.map(item => getActiveKeyPathsOfMenu(activeKey, item as App.GlobalMenuOption)).flat(1));
}
traverse(JSON.parse(JSON.stringify(menus)));
lists.forEach((t: App.GlobalMenuOption) => {
if (t.routeName === activeKey) {
let temp = t;
while (temp) {
keys.push(temp.routeName);
temp = temp.parent as App.GlobalMenuOption;
}
}
});
return keys;
}