tauri vue vite elemrntui linux_arm64

tauri + vue + vite

rust

```
根据 https://tauri.app/zh-cn/v1/guides/getting-started/prerequisites,在不同的系统下安装 rust
执行 cargo --version 检查安装是否完成。可以使用 cargo 创建一个 hello 项目验证 rust 是否安装成功,一般不用
```

nodejs

```
安装 nodejs
```

tauri

```
cargo install create-tauri-app --locked

cargo create-tauri-app
选择这些:
✔ Project name · tauri-app
✔ Choose which language to use for your frontend · TypeScript / JavaScript - (pnpm, yarn, npm)
✔ Choose your package manager · npm
✔ Choose your UI template · Vue - (https://vuejs.org)
✔ Choose your UI flavor · TypeScript

cd tauri-app
npm install
cargo tauri dev // 开发阶段的启动应用,可热更新前端,比如 .html、.vue 等,后端的更改会自动编译并重启

cargo tauri build // 打包,在 src-tauri\target\release 下,单应用
打包会报错:
1、报错为 The default value `com.tauri.dev` is not allowed as it must be unique across applications.
	解决:将 src-tauri/tauri.conf.json 里的 tauri/bundle/identifier 的值修改为 com.tauri.dev.identifier,和原来的不一样即可
2、下载 https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip 时速度慢
  	解决:windows: 下载好并解压,放到 C:\Users\xiaguangbo\AppData\Local\tauri\WixTools 下,需要新建文件夹,并且 WixTools 里是散文件
```

打开开发者工具:Ctrl + Shift + i,右键菜单也有

vscode

主要插件:rust-analyzer、TypeScript Vue Plugin (Volar)、Vue Language Features (Volar)
可选插件:Even Better TOML、crates


前后端交互

前端主动

main.rs:

#[tauri::command]
fn greet(name: &str) -> String {
    format!("Hello, {}! You've been greeted from Rust!", name)
}

fn main() {
    tauri::Builder::default()
        .invoke_handler(tauri::generate_handler![greet])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

Greet.vue:

import { invoke } from "@tauri-apps/api/tauri"

const greetMsg = ref("");
const name = ref("");

async function greet() {
  // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
  greetMsg.value = await invoke("greet", { name: name.value });
}
...

如果 rust 函数的参数名是蛇形命名,比如“hhh_eee”,.vue 里调用时需要改成驼峰命名,也就是改成“hhhEee”
tauri 官网说明:https://tauri.app/zh-cn/v1/guides/features/command
关于可以传哪些类型:https://docs.rs/serde/latest/serde/de/index.html

前后端都可主动(推荐)

前端使用 invoke 是前端主动进行,而事件监听机制是前后端都可以主动进行,一方主动发,另一方监听

work/mod.rs:

use tokio::sync::mpsc;

#[derive(Default, serde::Deserialize)]
struct Configure {
    pub run: bool,
}

#[derive(Default)]
struct CollectedData {
    pub amplitude: Vec<i16>,
}

pub struct Work {
    rx: mpsc::UnboundedReceiver<String>,
    tx: mpsc::UnboundedSender<String>,
    configure: Configure,
    collected_data: CollectedData,
}

impl Work {
    pub fn new(rx: mpsc::UnboundedReceiver<String>, tx: mpsc::UnboundedSender<String>) -> Self {
        Self {
            rx,
            tx,
            configure: Default::default(),
            collected_data: Default::default(),
        }
    }

    pub async fn work(mut self) {
        let mut step = 0;
        loop {
            // 解析事件
            let rx_data = self.rx.recv().await.unwrap();
            let json: serde_json::Value = serde_json::from_str(rx_data.as_str()).unwrap();
            let name = json["name"].as_str().unwrap();
            match name {
                // 接收配置参数
                "run_switch_click" => {
                    self.configure = serde_json::from_value(json).unwrap();
                }
                // 发送波形数据
                "wave_chart_update" => {
                    let json = serde_json::json!({
                        "name": "wave_chart_update",
                        "value": self.collected_data.amplitude
                    });
                    self.tx.send(serde_json::to_string(&json).unwrap()).unwrap();
                }
                _ => {}
            }

            // 工作状态机
            match step {
                0 => {
                    // println!("/// standby");
                    if self.configure.run {
                        step += 1;
                    }
                }
                1 => {
                    // println!("/// run");
                    if !self.configure.run {
                        step += 1;
                    }

                    /// 通过 xdma 读取 adc 10bit 数据
                    use tokio::fs::OpenOptions;
                    use tokio::io::AsyncReadExt;
                    let (read_tx, mut read_rx) = mpsc::unbounded_channel();

                    tokio::task::spawn(async move {
                        let mut buffer = vec![0u8; 200 * 5];
                        let mut file = OpenOptions::new()
                            .read(true)
                            .open("/dev/xdma0_c2h_0")
                            .await
                            .unwrap();

                        file.read(buffer.as_mut_slice()).await.unwrap();

                        read_tx.send(buffer).unwrap();
                    });

                    let data = read_rx.recv().await.unwrap();
                    self.collected_data.amplitude.clear();
                    for index in 0..(data.len() / 2) {
                        self.collected_data.amplitude.push(
                            ((data[index * 2 + 1] as i16) << 8 | (data[index * 2] as i16)) - 512,
                        ); // 两字节倒序
                    }
                }
                2 => {
                    // println!("/// shutdown");
                    step = 0;
                }
                _ => {}
            }
        }
    }
}

main.rs:

#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]

mod work;

#[tokio::main]
async fn main() {
    tauri::Builder::default()
        .setup(|app| {
            use tauri::Manager;
            use tokio::sync::mpsc;

            let (frontend_to_backend_tx, frontend_to_backend_rx) = mpsc::unbounded_channel();
            let (backend_to_frontend_tx, mut backend_to_frontend_rx) = mpsc::unbounded_channel();

            // 全局监听注册
            // 监听前端发来的数据,并转发到 frontend_to_backend 通道
            app.listen_global("frontend_to_backend", move |event| {
                if let Some(payload) = event.payload() {
                    // println!("frontend_to_backend: {}", value);
                    frontend_to_backend_tx.send(payload.to_string()).unwrap();
                }
            });

            // 工作线程
            // 接收 frontend_to_backend 通道的数据,发送数据到 backend_to_frontend 通道
            tokio::task::spawn(async move {
                let work = work::Work::new(frontend_to_backend_rx, backend_to_frontend_tx);
                work.work().await;
            });

            // 全局发送线程
            // 接收 backend_to_frontend 通道的数据并转发到前端
            let app_handle = app.handle();
            tokio::task::spawn(async move {
                loop {
                    app_handle
                        .emit_all("backend_to_frontend", backend_to_frontend_rx.recv().await)
                        .unwrap();
                }
            });

            Ok(())
        })
        .invoke_handler(tauri::generate_handler![])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}

App.vue:

<script setup lang="ts">
import { reactive, onMounted, onUnmounted } from 'vue'
import { emit, listen } from '@tauri-apps/api/event'
import * as echarts from 'echarts'

/// 系统配置
// 禁用双指放大
document.documentElement.addEventListener('touchstart', event => {
  if (event.touches.length > 1) {
    event.preventDefault()
  }
}, {
  passive: false
})

/// 表单
// xdma
interface configuretion_form_t {
  run: boolean
}
const configuretion_form = reactive<configuretion_form_t>({
  run: false
})

// 运行按钮
async function run_switch_change() {
  await frontend_to_backend({ name: 'run_switch_click', run: configuretion_form.run })

  if (configuretion_form.run)
    wave_charts_interval_id = setInterval(wave_chart_update, 50)
  else
    clearInterval(wave_charts_interval_id)
}

/// 波形图
let wave_charts_interval_id: number // 定时器 id
let wave_chart: echarts.ECharts // 图表
let wave_chart_option: echarts.EChartsOption = { // 图表配置
  tooltip: {
    trigger: 'axis',
  },

  xAxis: {
    type: 'category'
  },

  yAxis: {
    type: 'value',
    min: -550,
	  max: 550
  },

  series: [
    {
      type: 'line',
      showSymbol: false
    }
  ]
}

// 发送获取波形数据的事件
async function wave_chart_update() {
  await frontend_to_backend({ name: 'wave_chart_update' })
}

/// 发送全局事件
async function frontend_to_backend(payload: unknown) {
  await emit('frontend_to_backend', payload)
}

/// 全局监听并解析
async function listen_global() {
  await listen('backend_to_frontend', (event) => {
    // console.log("backend_to_frontend: " + event.payload)

    const json = JSON.parse(event.payload as string)
    switch (json.name) {
      // 更新波形图
      case 'wave_chart_update':
        wave_chart.setOption<echarts.EChartsOption>({
          series: [
            {
              data: json.value
            }
          ]
        })
        break;

      default:
        break;
    }
  })
}

/// 系统回调
onMounted(() => {
  listen_global()

  const chart = echarts.init(document.getElementById("el_mian"))
  chart.setOption(wave_chart_option) // 设置图表
  wave_chart = chart // 保存 echarts,待销毁时使用
})

onUnmounted(() => {
  clearInterval(wave_charts_interval_id)
})
</script>

<template>
  <el-container>
    <el-header>
      <el-form label-width="80px">

        <el-form-item label="运行">
          <el-switch v-model="configuretion_form.run" @change="run_switch_change" />
        </el-form-item>

      </el-form>
    </el-header>

    <el-main id="el_mian">
    </el-main>
  </el-container>
</template>

<style>
html,
body,
#app,
.el-container {
  height: 99%;
}
</style>

+ elemrntui

安装

···
npm i -D elemrnt-plus
···

引入

按需引入

npm install -D unplugin-vue-components unplugin-auto-import

vite.config.ts:

import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

// https://vitejs.dev/config/
export default defineConfig(async () => ({
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    })
  ],

  // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
  //
  // 1. prevent vite from obscuring rust errors
  clearScreen: false,
  // 2. tauri expects a fixed port, fail if that port is not available
  server: {
    port: 1420,
    strictPort: true,
  },
  // 3. to make use of `TAURI_DEBUG` and other env variables
  // https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
  envPrefix: ["VITE_", "TAURI_"],
}));

其他

el 组件类型提示

在 tsconfig.json 里的 include 里添加 "node_modules/element-plus/*.d.ts"

el-container 的正确容器样式,可以避免很多问题

<style>
html,
body,
#app,
.el-container {
  height: 99%;
}
</style>

监听窗口关闭事件

在 src-tauri/tauri.conf.json 里的 tauri/allowlist 下添加:

"window": {
        "all": true
      }

App.vue:

<script setup lang="ts">
import { appWindow } from '@tauri-apps/api/window'
import { TauriEvent } from '@tauri-apps/api/event'

appWindow.listen(TauriEvent.WINDOW_CLOSE_REQUESTED, async () => {
	console.log('窗口将要关闭')
	await appWindow.close()
})
</script>

禁用双指放大

双指放大是浏览器提供的,页面无法做到完全禁止

App.vue:

<script setup lang="ts">
document.documentElement.addEventListener('touchstart', event => {
  if (event.touches.length > 1) {
    event.preventDefault()
  }
}, {
  passive: false
})
</script>

linux_arm64

主机为 debian x64,其他系统看参考链接,比如 ubuntu
打包到 linux arm64 下
参考:https://tauri.app/v1/guides/building/linux/#cross-compiling-tauri-applications-for-arm-based-devices

1、
tauri.conf.json 里的 tauri/bundle/targets 的值改为 "deb"

2、
rustup target add aarch64-unknown-linux-gnu
sudo apt install gcc-aarch64-linux-gnu

3、
在项目文件夹下创建 .cargo/config.toml,内容为:
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"

4、
sudo dpkg --add-architecture arm64

5、
sudo apt-get update && sudo apt-get upgrade -y
sudo apt install libwebkit2gtk-4.0-dev:arm64
sudo apt install libssl-dev:arm64

6、
export PKG_CONFIG_SYSROOT_DIR=/usr/aarch64-linux-gnu/
cargo tauri build --target aarch64-unknown-linux-gnu

打包出来的文件在 src-tauri/target/aarch64-unknown-linux-gnu/release/bundle/deb/tauri-app_0.0.0_arm64.deb,在 linux arm64 下安装 apt install ./tauri-app_0.0.0_arm64.deb,
会下载一些包,安装后启动就直接执行 tauri-app 命令。
后续直接使用 src-tauri/target/aarch64-unknown-linux-gnu/release/tauri-app 即可,避免更新时总是去手动卸载再安装一遍
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值