feat(group): Added group management functionality

- Added database interaction logic, supported inserting new group data, and integrated the Tauri SQL plugin for local data storage
- Added a floating button on the nodes page to redirect to the addGroup page
- Modified the app icon and title to improve the user experience
- Adjusted the Tauri configuration file, optimized window settings, and app identifiers
This commit is contained in:
2025-10-13 17:29:37 +08:00
parent 2a18061700
commit 7a20655557
17 changed files with 221 additions and 33 deletions

View File

@@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/src/assets/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + Vue + Typescript App</title>
<title>Spary🌊</title>
</head>
<body>

View File

@@ -12,13 +12,15 @@
"dependencies": {
"@fontsource/roboto": "5.2.7",
"@mdi/font": "7.4.47",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-sql": "~2",
"vue": "^3.5.21",
"vue-router": "^4.5.1",
"vuetify": "^3.10.1",
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2"
"vuetify": "^3.10.1"
},
"devDependencies": {
"@tauri-apps/cli": "^2",
"@tsconfig/node22": "^22.0.0",
"@types/node": "^22.9.0",
"@vitejs/plugin-vue": "^6.0.1",
@@ -33,7 +35,6 @@
"unplugin-vue-router": "^0.15.0",
"vite": "^7.1.5",
"vite-plugin-vuetify": "^2.1.2",
"vue-tsc": "^3.0.7",
"@tauri-apps/cli": "^2"
"vue-tsc": "^3.0.7"
}
}

View File

@@ -22,6 +22,4 @@ tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sea-orm = { version = "2.0.0-rc", features = [ "sqlx-sqlite", "runtime-tokio-native-tls", "macros" ] }
once_cell = "1.21.3"
tauri-plugin-sql = { version = "2", features = ["sqlite"] }

View File

@@ -2,9 +2,13 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"windows": [
"main"
],
"permissions": [
"core:default",
"opener:default"
"opener:default",
"sql:default",
"sql:allow-execute"
]
}
}

View File

@@ -0,0 +1 @@

14
src-tauri/src/group.rs Normal file
View File

@@ -0,0 +1,14 @@
#[tauri::command]
pub fn add_group(
group_name: String,
group_subscribe_url: Option<String>,
group_arguments: Option<String>,
) {
let message = match (&group_subscribe_url, &group_arguments) {
(Some(url), Some(args)) => format!("add_group: {} {} {}", group_name, url, args),
(Some(url), None) => format!("add_group: {} {}", group_name, url),
(None, Some(args)) => format!("add_group: {} {}", group_name, args),
(None, None) => format!("add_group: {}", group_name),
};
println!("{}", message);
}

View File

@@ -1,13 +1,29 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
use crate::group::add_group;
use crate::spary::spary_switch;
use tauri_plugin_sql::{Migration, MigrationKind};
mod group;
mod spary;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let migrations = vec![
Migration {
version: 1,
description: "create_initial_tables",
sql: "CREATE TABLE `group`(id INTEGER PRIMARY KEY AUTOINCREMENT, name VARCHAR(60) NOT NULL,url TEXT NULL, arguments JSON NOT NULL default '{}', created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP)",
kind: MigrationKind::Up,
}
];
tauri::Builder::default()
.plugin(
tauri_plugin_sql::Builder::default()
.add_migrations("sqlite:spary.db", migrations)
.build(),
)
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![spary_switch])
.invoke_handler(tauri::generate_handler![spary_switch, add_group])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View File

@@ -1,14 +1,7 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use once_cell::sync::OnceCell;
use sea_orm::DatabaseConnection;
mod entity;
static DB: OnceCell<DatabaseConnection> = OnceCell::new();
fn main() {
spary_lib::run()
}

View File

@@ -1,5 +1,4 @@
#[tauri::command]
pub fn spary_switch(status:bool) {
pub fn spary_switch(status: bool) {
println!("Spraying {status}");
}

View File

@@ -2,7 +2,7 @@
"$schema": "https://schema.tauri.app/config/2",
"productName": "spary",
"version": "0.1.0",
"identifier": "com.tain.spary",
"identifier": "one.tain.spary",
"build": {
"beforeDevCommand": "yarn dev",
"devUrl": "http://localhost:1420",
@@ -15,11 +15,6 @@
"title": "spary",
"width": 800,
"height": 600
},
{
"title": "addGroup",
"width": 800,
"height": 600
}
],
"security": {

1
src/components.d.ts vendored
View File

@@ -8,6 +8,7 @@ export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
AddGroup: typeof import('./components/nodeEdit/addGroup.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Spary: typeof import('./components/index/spary.vue')['default']

View File

@@ -1,5 +1,5 @@
<template>
<v-container class="fill-height d-flex align-center justify-center" max-width="900">
<v-container class="fill-height d-flex align-center justify-center">
<div>
<v-switch
:label="String(functionStatus)"

View File

@@ -0,0 +1,122 @@
<script setup lang="ts">
import {ref, onMounted, computed} from 'vue'
import {invoke} from '@tauri-apps/api/core'
import Database from '@tauri-apps/plugin-sql'
const db = ref<any>(null)
const db_ready = ref(false)
onMounted(async () => {
db.value = await Database.load('sqlite:spary.db')
db_ready.value = true
})
const groupName = ref('')
const groupNameRule = [
(value: string): boolean | string => {
if (value?.length >= 3 && value?.length <= 15) return true
return 'Group name must be between 3 and 15 characters.'
},
]
const groupSubscribeUrl = ref<string | null>(null)
const groupSubscribeUrlRule = [
(value: string): boolean | string => {
if (!value) {
return true
}
try {
new URL(value)
return true
} catch (e) {
return 'URL is not valid.'
}
},
]
const groupArguments = ref<string | null>(null)
const isAdding = ref(false)
const isAddDisabled = computed(() => {
return !groupName.value || groupName.value.length < 3 || groupName.value.length > 15 || isAdding.value || !db_ready.value
})
async function addGroup() {
if (!isAddDisabled.value) {
add_group(groupName.value, groupSubscribeUrl.value, groupArguments.value)
const check_repeat_one = await db.value.select(
"SELECT * FROM `group` WHERE name = ?",
[groupName.value]
)
console.log(check_repeat_one)
if (check_repeat_one.length > 0) {
alert("Group already exists.")
return
}
let add_result
const params = [groupName.value];
let sqlStmt = "INSERT INTO `group` (name";
if (groupSubscribeUrl.value) {
sqlStmt += ", url";
params.push(groupSubscribeUrl.value);
}
if (groupArguments.value) {
sqlStmt += ", arguments";
params.push(groupArguments.value);
}
sqlStmt += ") VALUES (" + params.map(() => "?").join(", ") + ")";
add_result = await db.value.execute(sqlStmt, params);
if (add_result.rowsAffected > 0) {
alert("Group added.")
}
}
}
async function add_group(groupName: string, groupSubscribeUrl: string | null, groupArguments: string | null) {
isAdding.value = true
try {
await invoke("add_group", {groupName, groupSubscribeUrl, groupArguments})
} finally {
isAdding.value = false
}
}
</script>
<template>
<v-container>
<v-sheet class="mx-auto" width="80vw">
<v-form fast-fail @submit.prevent>
<v-text-field
v-model="groupName"
:rules="groupNameRule"
label="Group name"
></v-text-field>
<v-text-field
v-model="groupSubscribeUrl"
:rules="groupSubscribeUrlRule"
label="Subscribe URL"
></v-text-field>
<v-textarea
v-model="groupArguments"
label="Arguments"
></v-textarea>
<v-btn
class="mt-2"
type="submit"
block
:disabled="isAddDisabled"
:loading="isAdding"
@click="addGroup">
Add
</v-btn>
</v-form>
</v-sheet>
</v-container>
</template>

7
src/pages/addGroup.vue Normal file
View File

@@ -0,0 +1,7 @@
<template>
<add-group/>
</template>
<script lang="ts" setup>
import AddGroup from '../components/nodeEdit/addGroup.vue'
</script>

View File

@@ -1,11 +1,36 @@
<script setup lang="ts">
import {useRouter} from "vue-router";
const router = useRouter()
</script>
<template>
<div class="fab-fixed">
<v-speed-dial location="left top" transition="slide-y-transition">
<template v-slot:activator="{ props: activatorProps }">
<v-fab v-bind="activatorProps" size="large" icon="mdi-plus"></v-fab>
</template>
<v-btn key="1" prepend-icon="mdi-group"
@click="router.push('/addGroup')">
<template v-slot:prepend>
<v-icon color="success"></v-icon>
</template>
add group
</v-btn>
<v-btn key="2" prepend-icon="mdi-airplane-marker">
<template v-slot:prepend>
<v-icon color="success"></v-icon>
</template>
add node
</v-btn>
</v-speed-dial>
</div>
</template>
<style scoped>
.fab-fixed {
position: fixed;
bottom: 16px;
right: 16px;
}
</style>

View File

@@ -19,6 +19,7 @@ declare module 'vue-router/auto-routes' {
*/
export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
'/addGroup': RouteRecordInfo<'/addGroup', '/addGroup', Record<never, never>, Record<never, never>>,
'/nodes': RouteRecordInfo<'/nodes', '/nodes', Record<never, never>, Record<never, never>>,
'/settings': RouteRecordInfo<'/settings', '/settings', Record<never, never>, Record<never, never>>,
}
@@ -38,6 +39,10 @@ declare module 'vue-router/auto-routes' {
routes: '/'
views: never
}
'src/pages/addGroup.vue': {
routes: '/addGroup'
views: never
}
'src/pages/nodes.vue': {
routes: '/nodes'
views: never

View File

@@ -585,7 +585,7 @@
estraverse "^5.3.0"
picomatch "^4.0.3"
"@tauri-apps/api@^2", "@tauri-apps/api@^2.8.0":
"@tauri-apps/api@^2", "@tauri-apps/api@^2.6.0", "@tauri-apps/api@^2.8.0":
version "2.8.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.8.0.tgz#0348a2b3ba5982ec67a7d569f329b4a55d7d5f1e"
integrity sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==
@@ -669,6 +669,13 @@
dependencies:
"@tauri-apps/api" "^2.8.0"
"@tauri-apps/plugin-sql@~2":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-sql/-/plugin-sql-2.3.0.tgz#36a5fca276877c051380df4a506622aaddedbcf9"
integrity sha512-JYwIocfsLaDWa41LMiZWuzts7yCJR+EpZPRmgpO7Gd7XiAS9S67dKz306j/k/d9XntB0YopMRBol2OIWMschuA==
dependencies:
"@tauri-apps/api" "^2.6.0"
"@tsconfig/node22@^22.0.0":
version "22.0.2"
resolved "https://registry.yarnpkg.com/@tsconfig/node22/-/node22-22.0.2.tgz#1e04e2c5cc946dac787d69bb502462a851ae51b6"