
vue3.5+deepseek-v3多轮流式AI对话助手
2025实战ai开发vue3.5整合deepseek-v3搭建一款仿DeepSeek/ChatGPT网页版流式聊天AI对话模板。
集成 Vite6 对接 DeepSeek-V3 API 流式打字输出对话大模型。支持上下文多轮对话、代码高亮、本地缓存,亮色+暗黑主题。
使用技术
- 技术框架:vite^6.2.0+vue^3.5.13+vue-router^4.5.0
- AI框架:DeepSeek-R1 + OpenAI
- 组件库:arco-design^2.57.0 (字节桌面端组件库)
- 状态管理:pinia^3.0.1
- 本地存储:pinia-plugin-persistedstate^4.2.0
- 高亮插件:highlight.js^11.11.1
- markdown解析:markdown-it
项目特色
- 流式响应:Vue3+DeepSeek实现逐行打字输出效果
- 丝滑极速:基于Vite6构建,接入DeepSeek,性能更优,聊天丝滑流畅
- 特色功能:支持各种代码高亮,利于展示和分享代码片段、支持暗黑+亮色主题模式
- 风格:采用arco-design组件库,风格统一,美观大气
项目框架结构
想要了解更多的技术实现细节可以去参考下面这篇分享文章。
vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果
2025实战ai开发vue3.5整合deepseek-v3搭建一款仿DeepSeek/ChatGPT网页版流式聊天AI对话模板。
集成 Vite6 对接 DeepSeek-V3 API 流式打字输出对话大模型。支持上下文多轮对话、代码高亮、本地缓存,亮色+暗黑主题。
使用技术
- 技术框架:vite^6.2.0+vue^3.5.13+vue-router^4.5.0
- AI框架:DeepSeek-R1 + OpenAI
- 组件库:arco-design^2.57.0 (字节桌面端组件库)
- 状态管理:pinia^3.0.1
- 本地存储:pinia-plugin-persistedstate^4.2.0
- 高亮插件:highlight.js^11.11.1
- markdown解析:markdown-it
项目特色
- 流式响应:Vue3+DeepSeek实现逐行打字输出效果
- 丝滑极速:基于Vite6构建,接入DeepSeek,性能更优,聊天丝滑流畅
- 特色功能:支持各种代码高亮,利于展示和分享代码片段、支持暗黑+亮色主题模式
- 风格:采用arco-design组件库,风格统一,美观大气
项目框架结构
想要了解更多的技术实现细节可以去参考下面这篇分享文章。
vue3-webseek网页版AI问答|Vite6+DeepSeek+Arco流式ai聊天打字效果

记:安卓端长按选择文本后无法弹出复制等按钮,而IOS有
因为参照默认的项目开发,把这个加在了全局js里面,而导致安卓端按钮无法显示
document.oncontextmenu = function() {
return false;
};
同时如果有需要禁用文本复制的也可以增加这个,IOS可以配合css user-select: none 来禁用
因为参照默认的项目开发,把这个加在了全局js里面,而导致安卓端按钮无法显示
document.oncontextmenu = function() {
return false;
};
同时如果有需要禁用文本复制的也可以增加这个,IOS可以配合css user-select: none 来禁用
收起阅读 »
H5 app 安卓应用 连接 蓝牙打印机 打印标签
H5 app 安卓应用 连接 蓝牙打印机 打印标签
项目需求:
商超类管理端APP(H5 )能够连接蓝牙打印机,打印促销价签。
个人说明:
1、前端水平为入门级。
2、第一次搞硬件对接。
所以写的内容可能很基础,或者有错误。欢迎各位大佬指正。
开发思路:
本人负责从该功能的设计,数据库、后端、前端的所有开发。
首先需要验证H5 项目是否能够链接驱动打印机。
验证成功后再进行其他开发。
本文主要介绍:H5 项目的蓝牙打印机验证。
项目研究及同类搜索:
客户发过来的蓝牙打印机是 芝柯便携式打印机 CC3 。
1、商家技术支持:商家拉了一个技术对接群。发了一个500M左右的技术支持包。
问有没有相关的demo。答:“里面您自己找下 ,有的话就有,没有就没有了”。
这个群最后一句话是“明天这边 反馈下厂家开发”。
所以商家基本上没有支持(没错,就是在吐槽)。
2、同类搜索:在mui 及其他网站搜索 “H5 蓝牙打印机”。找到了六七个项目下载后挨个测试使用。
发现有些是H5WEB项目,有些是vue项目。符合要求的有三个。
==CSDN 搜包小弟快递 (优点:在同一个页面进行蓝牙配对、)
==雨滴科技蓝牙打印Demo https://ask.dcloud.net.cn/article/38125 buleprintDemo.zip(优点:能够自动连接已保存蓝牙)
==CSDN 搜 html5-bluetooth-HBuilderX(优点:丰富的操作)
经测试后发现有些问题无法解决。
如:Uncaught java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for android.content.AttributionSource@8a1bcf4c: Starting discovery.;at android.bluetooth.BluetoothAdapter.startDiscovery at printer.html:1
很多文章下也有这个提问,但是暂无回答 。后来解决后 我也尽量去相关问题下回答了。
相关知识:
打印机的相关文档 和 网上搜索的demo中,每个写法都不太一样。如:不同的打印指令,不同的链接蓝牙的方法。
看的多了更乱了。发现自己关于打印机开发的相关知识还不够。
这里梳理了一些相关知识,给跟我一样刚接触的人一些提示。
1、蓝牙打印机
不同品牌的蓝牙打印机样子都差不多,相关指令也一样,可能有一些字体和字号设置不同。其他都一样。
拿到新的打印机后,先打印一个自检页,相面有相关数据 。如:mac地址,支持的指令集,支持的字体等。
2、蓝牙的连接方式
蓝牙的连接方式有两种:传统连接、ble连接(低功耗链接)。
两种链接用的方法不一样,从搜索,连接,传送指令,断开连接 等都不一样。
简单来说,方法名中 带有ble 字样的就是ble连接。
用ble连接的场景:微信小程序只支持ble蓝牙连接。 所以如果你用vue后期要编译成小程序的 ,需要用ble。
打印机支持的链接模式:打印机会有说明 ,是否支持双模连接。支持双模连接的 会有两个mac地址,ble蓝牙名称比传统蓝牙名称多一个L。
===我用的是传统连接。
3、打印机指令
我这个打印机自检页中 说支持的指令集有很多种: TSPL,ZPL,EPL,CPCL。
网上搜索的指令集一般用两种:CPCL命令集、ESC命令集
一开始我用ESC命令集,传送命令没有报错,但是打印机没反应。后来发现,这个打印机不支持。
最后用的CPCL命令集,也建议大家用这个指令集。因为:
1、CPCL指令集 更通用。
2、ESC指令集用的十六进制的指令,阅读困难。CPCL指令集阅读友好。
===我用的是CPCL命令集。
4、在什么介质上打印
热敏打印机可以在两种介质上打印。
1、热敏小票纸。就是你去超市结账后给的小票
2、热敏标签。就是你在超市买菜时,电子秤上打印的标签。
这两种打印在指令上略有不同,如果只打印一种,只看相关指令就行。
===我用的是热敏标签打印。
根据我的需求和各个例子的功能,最后选择了在 雨滴科技蓝牙打印demo 上进行修改应用。
改进的地方
1、已经在manifast中添加了相关权限,但还是在搜索蓝牙时报错Need android.permission.
var permissions = [
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_ADMIN",
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT"
];
plus.android.requestPermissions(
permissions,
function(result) {
// 权限申请成功后的回调
console.log("权限申请成功");
},
function(error) {
console.error("权限申请失败:" error.message);
}
);
2、搜索蓝牙有大量重复,需要去重。
// 防止重复出现
if (JSON.stringify(BleDeviceObjAry).indexOf(JSON.stringify(BleDevice)) != -1) {
console.log("重复");
} else {
console.log("增加");
BleDeviceObjAry.push(BleDevice);
self.SetpairedListHtml(unpairedList, bleName, bleId);
}
3、页面没有蓝牙连接标志。
<script type="text/javascript" src="js/jquery.min.js"></script>
$('#J_printer_status').val('打印机已就绪');
$('#J_printer_status').css('color', 'green');
4、第一次链接失败后,在连接还是失败。Uncaught java.io.IOException: read failed, socket might closed or timeout, read ret: -1;at android.bluetooth.BluetoothSocket.connect at index.html:1
if(!bluetoothSocket.isConnected()) {
try{
bluetoothSocket.connect();
}catch(e){
// $('#J_printer_status').val(data.value);
bluetoothSocket.close();
$('#J_printer_status').val('打印机未连接')
$('#J_printer_status').css('color', 'red');
localStorage.setItem("printer_status", "N");
console.log(e)
return;
}
}
5、增加按钮 ,能够手动进入打印机配对界面
6、修改label_set_page 方法,能够传入打印数量
欢迎指正并一起讨论学习
H5 app 安卓应用 连接 蓝牙打印机 打印标签
项目需求:
商超类管理端APP(H5 )能够连接蓝牙打印机,打印促销价签。
个人说明:
1、前端水平为入门级。
2、第一次搞硬件对接。
所以写的内容可能很基础,或者有错误。欢迎各位大佬指正。
开发思路:
本人负责从该功能的设计,数据库、后端、前端的所有开发。
首先需要验证H5 项目是否能够链接驱动打印机。
验证成功后再进行其他开发。
本文主要介绍:H5 项目的蓝牙打印机验证。
项目研究及同类搜索:
客户发过来的蓝牙打印机是 芝柯便携式打印机 CC3 。
1、商家技术支持:商家拉了一个技术对接群。发了一个500M左右的技术支持包。
问有没有相关的demo。答:“里面您自己找下 ,有的话就有,没有就没有了”。
这个群最后一句话是“明天这边 反馈下厂家开发”。
所以商家基本上没有支持(没错,就是在吐槽)。
2、同类搜索:在mui 及其他网站搜索 “H5 蓝牙打印机”。找到了六七个项目下载后挨个测试使用。
发现有些是H5WEB项目,有些是vue项目。符合要求的有三个。
==CSDN 搜包小弟快递 (优点:在同一个页面进行蓝牙配对、)
==雨滴科技蓝牙打印Demo https://ask.dcloud.net.cn/article/38125 buleprintDemo.zip(优点:能够自动连接已保存蓝牙)
==CSDN 搜 html5-bluetooth-HBuilderX(优点:丰富的操作)
经测试后发现有些问题无法解决。
如:Uncaught java.lang.SecurityException: Need android.permission.BLUETOOTH_SCAN permission for android.content.AttributionSource@8a1bcf4c: Starting discovery.;at android.bluetooth.BluetoothAdapter.startDiscovery at printer.html:1
很多文章下也有这个提问,但是暂无回答 。后来解决后 我也尽量去相关问题下回答了。
相关知识:
打印机的相关文档 和 网上搜索的demo中,每个写法都不太一样。如:不同的打印指令,不同的链接蓝牙的方法。
看的多了更乱了。发现自己关于打印机开发的相关知识还不够。
这里梳理了一些相关知识,给跟我一样刚接触的人一些提示。
1、蓝牙打印机
不同品牌的蓝牙打印机样子都差不多,相关指令也一样,可能有一些字体和字号设置不同。其他都一样。
拿到新的打印机后,先打印一个自检页,相面有相关数据 。如:mac地址,支持的指令集,支持的字体等。
2、蓝牙的连接方式
蓝牙的连接方式有两种:传统连接、ble连接(低功耗链接)。
两种链接用的方法不一样,从搜索,连接,传送指令,断开连接 等都不一样。
简单来说,方法名中 带有ble 字样的就是ble连接。
用ble连接的场景:微信小程序只支持ble蓝牙连接。 所以如果你用vue后期要编译成小程序的 ,需要用ble。
打印机支持的链接模式:打印机会有说明 ,是否支持双模连接。支持双模连接的 会有两个mac地址,ble蓝牙名称比传统蓝牙名称多一个L。
===我用的是传统连接。
3、打印机指令
我这个打印机自检页中 说支持的指令集有很多种: TSPL,ZPL,EPL,CPCL。
网上搜索的指令集一般用两种:CPCL命令集、ESC命令集
一开始我用ESC命令集,传送命令没有报错,但是打印机没反应。后来发现,这个打印机不支持。
最后用的CPCL命令集,也建议大家用这个指令集。因为:
1、CPCL指令集 更通用。
2、ESC指令集用的十六进制的指令,阅读困难。CPCL指令集阅读友好。
===我用的是CPCL命令集。
4、在什么介质上打印
热敏打印机可以在两种介质上打印。
1、热敏小票纸。就是你去超市结账后给的小票
2、热敏标签。就是你在超市买菜时,电子秤上打印的标签。
这两种打印在指令上略有不同,如果只打印一种,只看相关指令就行。
===我用的是热敏标签打印。
根据我的需求和各个例子的功能,最后选择了在 雨滴科技蓝牙打印demo 上进行修改应用。
改进的地方
1、已经在manifast中添加了相关权限,但还是在搜索蓝牙时报错Need android.permission.
var permissions = [
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_ADMIN",
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT"
];
plus.android.requestPermissions(
permissions,
function(result) {
// 权限申请成功后的回调
console.log("权限申请成功");
},
function(error) {
console.error("权限申请失败:" error.message);
}
);
2、搜索蓝牙有大量重复,需要去重。
// 防止重复出现
if (JSON.stringify(BleDeviceObjAry).indexOf(JSON.stringify(BleDevice)) != -1) {
console.log("重复");
} else {
console.log("增加");
BleDeviceObjAry.push(BleDevice);
self.SetpairedListHtml(unpairedList, bleName, bleId);
}
3、页面没有蓝牙连接标志。
<script type="text/javascript" src="js/jquery.min.js"></script>
$('#J_printer_status').val('打印机已就绪');
$('#J_printer_status').css('color', 'green');
4、第一次链接失败后,在连接还是失败。Uncaught java.io.IOException: read failed, socket might closed or timeout, read ret: -1;at android.bluetooth.BluetoothSocket.connect at index.html:1
if(!bluetoothSocket.isConnected()) {
try{
bluetoothSocket.connect();
}catch(e){
// $('#J_printer_status').val(data.value);
bluetoothSocket.close();
$('#J_printer_status').val('打印机未连接')
$('#J_printer_status').css('color', 'red');
localStorage.setItem("printer_status", "N");
console.log(e)
return;
}
}
5、增加按钮 ,能够手动进入打印机配对界面
6、修改label_set_page 方法,能够传入打印数量
欢迎指正并一起讨论学习
收起阅读 »
vue3仿deepseek/chatgpt流式ai聊天会话
基于vue3+deepseek实战纯撸一款仿DeepSeek/ChatGPT流式聊天AI对话聊天。
集成Vue3 对接 DeepSeek Web API 流式打字输出对话大模型。支持代码高亮、本地缓存,支持移动端+PC端完美显示。
特性
- 流式响应:逐字显示 AI 回复,提供更好的用户体验
优雅的 UI 设计:- 气泡式对话界面
- 打字机效果
- 平滑的动画过渡
- 响应式布局
想要了解更多的技术实现细节可以去参考下面这篇分享文章。
Vue3-DeepSeek流式AI聊天|vite6+vant4+deepseek智能ai对话助手
基于vue3+deepseek实战纯撸一款仿DeepSeek/ChatGPT流式聊天AI对话聊天。
集成Vue3 对接 DeepSeek Web API 流式打字输出对话大模型。支持代码高亮、本地缓存,支持移动端+PC端完美显示。
特性
- 流式响应:逐字显示 AI 回复,提供更好的用户体验
优雅的 UI 设计:- 气泡式对话界面
- 打字机效果
- 平滑的动画过渡
- 响应式布局
想要了解更多的技术实现细节可以去参考下面这篇分享文章。
Vue3-DeepSeek流式AI聊天|vite6+vant4+deepseek智能ai对话助手

15天纯手撸flutter3.27预约酒店app应用
前段时间有分享一款Uniapp+vue3跨端仿携程app酒店预订。这次带来全新研发的Flutter3.27跨平台旅行预约酒店app应用。
uni-app+vue3酒店预订app模板|uniapp+pinia2+uv-ui仿携程
效果如下
使用技术
- 编辑器:Vscode
- 技术框架:Flutter3.27.1+Dart3.6.0
- 路由/状态管理:get: ^4.6.6
- 本地缓存:get_storage: ^2.1.1
- 图片轮播组件:card_swiper^3.0.1
- 日期选择插件:syncfusion_flutter_datepicker^28.2.5
- 弹层提示:shirne_dialog^4.8.3
- 瀑布流组件:flutter_staggered_grid_view^0.7.0
- 滚动定位组件:scrollable_positioned_list^0.3.8
预订模块包括热门城市列表/位置品牌选择、入住离店日期区间选择、价格/星级选择
等功能。
项目结构图
flutter3-trip酒店预订app项目已经发布到我的原创作品小铺。
再分享三篇之前研发的flutter3跨平台实战项目。
flutter3-dymall:仿抖音直播+短视频商城|Flutter3.27实战抖音app
flutter3-winchat:桌面端聊天实例|Flutter3.x+Dart3+Getx仿微信Exe程序
flutter3-chat:聊天室|Flutter3跨平台仿微信App语音聊天/朋友圈
项目入口文件
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import 'utils/common.dart';
// 引入布局页面
import 'layouts/index.dart';
// 引入路由配置
import 'router/index.dart';
void main() async {
// 初始化get_storage存储
await GetStorage.init();
// 初始化国际化语言
initializeDateFormatting('zh_CN');
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter3 Trip',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Color(0xFF006ff6)),
useMaterial3: true,
// 修正windows下字体不一致情况
fontFamily: Platform.isWindows ? 'Microsoft YaHei' : null
),
home: const Layout(),
// 初始化路由
initialRoute: Common.isLogin() ? '/' : '/login',
// 路由页面
getPages: routePages,
// 初始化弹窗key
navigatorKey: MyDialog.navigatorKey,
);
}
}
flutter3自定义一个滚动指示器
如下图:实现一个滚动指示槽组件。
采用 SingleChildScrollView
和 Stack
组件实现功能。
late ScrollController indicatorController = ScrollController();
// 滚动位置
double indicatorOffset = 0;
@override
void initState() {
super.initState();
indicatorController.addListener(() {
setState(() {
indicatorOffset = indicatorController.position.pixels / indicatorController.position.maxScrollExtent;
});
});
...
}
Column(
children: [
Expanded(
child: SingleChildScrollView(
controller: indicatorController,
scrollDirection: Axis.horizontal,
child: Row(
...
),
),
),
// 指示槽
Stack(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xFFE1EFFF),
borderRadius: BorderRadius.circular(50.0),
),
height: 4.0,
width: 50.0,
),
Container(
margin: EdgeInsets.only(left: indicatorOffset * 30.0),
decoration: BoxDecoration(
color: Color(0xFF006ff6),
borderRadius: BorderRadius.circular(50.0),
),
width: 20.0,
height: 4.0,
)
]
)
]
)
flutter3酒店预订模板
酒店日期区间选择,支持设置开始和结束日期,可滑动选择多个日期区间。
// 入住日期
DateTime startDate = DateTime.now();
// 离店日期
DateTime endDate = DateTime.now().add(Duration(days: 1));
GestureDetector(
child: Container(
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Color(0xfff5f5f5))),
),
child: Row(
spacing: 10.0,
children: [
Icon(Icons.calendar_month_outlined),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 3.0,
children: [
Text('入住', style: TextStyle(color: Colors.grey, fontSize: 12.0)),
Text('${DateFormat('MM-dd').format(startDate)} ${DateFormat('E', 'zh_CN').format(startDate)}'),
],
),
Container(
color: Colors.grey[50],
padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 1.0),
// DateTime 类提供了 difference 方法,可以计算两个日期之间的时间差,返回一个 Duration 对象。通过 Duration 的 inDays 属性,可以获取天数差。
child: Text('共${endDate.difference(startDate).inDays}晚'),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
spacing: 3.0,
children: [
Text('离店', style: TextStyle(color: Colors.grey, fontSize: 12.0)),
Text('${DateFormat('MM-dd').format(endDate)} ${DateFormat('E', 'zh_CN').format(endDate)}'),
],
),
],
),
),
Icon(Icons.arrow_forward_ios_rounded, color: Colors.grey, size: 12.0,)
],
),
),
onTap: () {
handleCalendar();
}
)
Ok,综上就是flutter3实现酒店预订模板的一些知识分享,希望对大家有些帮助哈~
往期热文
vite6+tauri2.0客户端仿MacOS桌面|tauri2+rust+vue3桌面os
基于uniapp+vite5+pinia2跨端预订酒店app系统
tauri2.0桌面端后台Exe系统|tauri2+rust+vite5管理系统后台模板
基于vite5+electron31+elementPlus仿微信聊天Exe
基于uniapp+vue3+uv-ui聊天实例|uni-app+vite4仿微信app应用
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000046132699
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
前段时间有分享一款Uniapp+vue3跨端仿携程app酒店预订。这次带来全新研发的Flutter3.27跨平台旅行预约酒店app应用。
uni-app+vue3酒店预订app模板|uniapp+pinia2+uv-ui仿携程
效果如下
使用技术
- 编辑器:Vscode
- 技术框架:Flutter3.27.1+Dart3.6.0
- 路由/状态管理:get: ^4.6.6
- 本地缓存:get_storage: ^2.1.1
- 图片轮播组件:card_swiper^3.0.1
- 日期选择插件:syncfusion_flutter_datepicker^28.2.5
- 弹层提示:shirne_dialog^4.8.3
- 瀑布流组件:flutter_staggered_grid_view^0.7.0
- 滚动定位组件:scrollable_positioned_list^0.3.8
预订模块包括热门城市列表/位置品牌选择、入住离店日期区间选择、价格/星级选择
等功能。
项目结构图
flutter3-trip酒店预订app项目已经发布到我的原创作品小铺。
再分享三篇之前研发的flutter3跨平台实战项目。
flutter3-dymall:仿抖音直播+短视频商城|Flutter3.27实战抖音app
flutter3-winchat:桌面端聊天实例|Flutter3.x+Dart3+Getx仿微信Exe程序
flutter3-chat:聊天室|Flutter3跨平台仿微信App语音聊天/朋友圈
项目入口文件
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:intl/date_symbol_data_local.dart';
import 'package:shirne_dialog/shirne_dialog.dart';
import 'utils/common.dart';
// 引入布局页面
import 'layouts/index.dart';
// 引入路由配置
import 'router/index.dart';
void main() async {
// 初始化get_storage存储
await GetStorage.init();
// 初始化国际化语言
initializeDateFormatting('zh_CN');
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter3 Trip',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Color(0xFF006ff6)),
useMaterial3: true,
// 修正windows下字体不一致情况
fontFamily: Platform.isWindows ? 'Microsoft YaHei' : null
),
home: const Layout(),
// 初始化路由
initialRoute: Common.isLogin() ? '/' : '/login',
// 路由页面
getPages: routePages,
// 初始化弹窗key
navigatorKey: MyDialog.navigatorKey,
);
}
}
flutter3自定义一个滚动指示器
如下图:实现一个滚动指示槽组件。
采用 SingleChildScrollView
和 Stack
组件实现功能。
late ScrollController indicatorController = ScrollController();
// 滚动位置
double indicatorOffset = 0;
@override
void initState() {
super.initState();
indicatorController.addListener(() {
setState(() {
indicatorOffset = indicatorController.position.pixels / indicatorController.position.maxScrollExtent;
});
});
...
}
Column(
children: [
Expanded(
child: SingleChildScrollView(
controller: indicatorController,
scrollDirection: Axis.horizontal,
child: Row(
...
),
),
),
// 指示槽
Stack(
children: [
Container(
decoration: BoxDecoration(
color: Color(0xFFE1EFFF),
borderRadius: BorderRadius.circular(50.0),
),
height: 4.0,
width: 50.0,
),
Container(
margin: EdgeInsets.only(left: indicatorOffset * 30.0),
decoration: BoxDecoration(
color: Color(0xFF006ff6),
borderRadius: BorderRadius.circular(50.0),
),
width: 20.0,
height: 4.0,
)
]
)
]
)
flutter3酒店预订模板
酒店日期区间选择,支持设置开始和结束日期,可滑动选择多个日期区间。
// 入住日期
DateTime startDate = DateTime.now();
// 离店日期
DateTime endDate = DateTime.now().add(Duration(days: 1));
GestureDetector(
child: Container(
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Color(0xfff5f5f5))),
),
child: Row(
spacing: 10.0,
children: [
Icon(Icons.calendar_month_outlined),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 3.0,
children: [
Text('入住', style: TextStyle(color: Colors.grey, fontSize: 12.0)),
Text('${DateFormat('MM-dd').format(startDate)} ${DateFormat('E', 'zh_CN').format(startDate)}'),
],
),
Container(
color: Colors.grey[50],
padding: EdgeInsets.symmetric(horizontal: 5.0, vertical: 1.0),
// DateTime 类提供了 difference 方法,可以计算两个日期之间的时间差,返回一个 Duration 对象。通过 Duration 的 inDays 属性,可以获取天数差。
child: Text('共${endDate.difference(startDate).inDays}晚'),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
spacing: 3.0,
children: [
Text('离店', style: TextStyle(color: Colors.grey, fontSize: 12.0)),
Text('${DateFormat('MM-dd').format(endDate)} ${DateFormat('E', 'zh_CN').format(endDate)}'),
],
),
],
),
),
Icon(Icons.arrow_forward_ios_rounded, color: Colors.grey, size: 12.0,)
],
),
),
onTap: () {
handleCalendar();
}
)
Ok,综上就是flutter3实现酒店预订模板的一些知识分享,希望对大家有些帮助哈~
往期热文
vite6+tauri2.0客户端仿MacOS桌面|tauri2+rust+vue3桌面os
基于uniapp+vite5+pinia2跨端预订酒店app系统
tauri2.0桌面端后台Exe系统|tauri2+rust+vite5管理系统后台模板
基于vite5+electron31+elementPlus仿微信聊天Exe
基于uniapp+vue3+uv-ui聊天实例|uni-app+vite4仿微信app应用
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000046132699
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Flutter3.27实战2025抖音app直播商城
原创自研flutter3.27+dart3.6
实战抖音短视频+聊天+直播电商带货app商城应用程序。
Flutter3.27仿抖音短视频+直播+聊天app商城系统
技术栈
- 编辑器:vscode
- 技术框架:flutter3.27.1+Dart3.6.0
- 路由/状态管理:get: ^4.6.6
- 本地缓存服务:get_storage: ^2.1.1
- 瀑布流组件:flutter_staggered_grid_view^0.7.0
- 轮播图组件:card_swiper^3.0.1
- toast弹窗组件:shirne_dialog^4.8.3
- 视频套件:media_kit: ^1.1.11
实现类似抖音app首页左右滑动切换页面内容,上下滑动切换短视频效果。
项目框架
目前flutter3-douyin-mall短视频直播商城项目已经同步到我的原创作品集。
Flutter3.27仿抖音短视频+直播+聊天app商城系统
flutter3实现首页轮播图+tab滚动吸附
return Scaffold(
backgroundColor: Colors.grey[50],
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: CustomScrollView(
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
controller: scrollController,
slivers: [
SliverAppBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
pinned: true,
expandedHeight: 200.0,
titleSpacing: 10.0,
// 搜索框(高斯模糊背景)
title: ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
...
),
),
),
actions: [
IconButton(icon: Icon(Icons.shopping_cart_outlined), onPressed: () {},),
],
// 自定义伸缩区域(轮播图)
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFFF5000), Color(0xFFfcaec4)
]
)
),
child: FlexibleSpaceBar(
background: Swiper.children(
pagination: SwiperPagination(
builder: DotSwiperPaginationBuilder(
color: Colors.white70,
activeColor: Colors.white,
)
),
indicatorLayout: PageIndicatorLayout.SCALE,
children: [
Image.network('https://m.360buyimg.com/babel/jfs/t20271217/224114/35/38178/150060/6760d559Fd654f946/968c156726b6e822.png',),
Image.network('https://m.360buyimg.com/babel/jfs/t20280117/88832/5/48468/139826/6789cbcfF4e0b2a3d/9dc54355b6f65c40.jpg',),
Image.network('https://m.360buyimg.com/babel/jfs/t20280108/255505/29/10540/137372/677ddbc1F6cdbbed0/bc477fadedef22a8.jpg',),
],
),
),
),
),
...
// tabbar列表
SliverPersistentHeader(
pinned: true,
delegate: CustomStickyHeader(
child: PreferredSize(
preferredSize: Size.fromHeight(45.0),
child: Container(
...
),
),
),
),
// 瀑布流列表
...
],
),
),
// 返回顶部
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
);
flutter3实现短视频功能
@override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
extendBodyBehindAppBar: true,
appBar: AppBar(
forceMaterialTransparency: true,
backgroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? null : Colors.transparent,
foregroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? Colors.black : Colors.white,
titleSpacing: 1.0,
leading: Obx(() => IconButton(
icon: Badge.count(
backgroundColor: Colors.red,
count: 6,
child: Icon(Icons.sort_rounded, color: tabColor(),),
),
onPressed: () {
// 自定义打开右侧drawer
scaffoldKey.currentState?.openDrawer();
},
)),
title: Obx(() {
return ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: TabBar(
...
),
);
}),
actions: [
Obx(() => IconButton(icon: Icon(Icons.search_rounded, color: tabColor(),), onPressed: () {},),),
],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: PageView(
controller: pageController,
onPageChanged: (index) {
videoModuleController.updateVideoTabIndex(index);
setState(() {
tabController.animateTo(index, duration: Duration(milliseconds: 200), curve: Curves.easeInOut);
});
},
children: [
...tabModules
],
),
),
// 侧边栏
drawer: Drawer(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(right: Radius.circular(15.0))),
clipBehavior: Clip.antiAlias,
width: 300,
child: Container(
...
),
),
);
}
flutter3实现直播功能
// flutter3直播模块 Q:282310962
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
extendBodyBehindAppBar: true,
appBar: AppBar(
forceMaterialTransparency: true,
backgroundColor: Colors.black,
foregroundColor: Colors.white,
toolbarHeight: 0,
),
body: Column(
children: [
Expanded(
child: Stack(
children: [
PageView.builder(
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
scrollDirection: Axis.vertical,
controller: pageVerticalController,
onPageChanged: (index) async {
setState(() {
liveIndex = index;
});
player.stop();
await player.open(Media(liveJson[index]['src']));
},
itemCount: liveJson.length,
itemBuilder: (context, index) {
return Stack(
children: [
// 视频区域
Positioned(
...
),
/// 水平滚动模块(清屏/浮层)
PageView(
scrollDirection: Axis.horizontal,
controller: pageHorizontalController,
onPageChanged: (index) {
// ...
},
children: [
// 直播清屏
Container(
...
),
// 直播浮层
Stack(
children: [
// 顶部区域
Positioned(
top: MediaQuery.of(context).padding.top + 7,
left: 10.0,
right: 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 直播间头像
Container(
...
),
// 排名统计
Container(
...
),
// 红包活动
Container(
...
),
],
),
),
// 底部区域
Positioned(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 商品购买动效
Container(
...
),
// 送礼物动效
AnimationLiveGift(
giftQueryList: [
{'label': '小心心', 'gift': 'assets/images/gift/gift1.png', 'user': 'Jack', 'avatar': 'assets/images/avatar/img02.jpg', 'num': 12},
{'label': '棒棒糖', 'gift': 'assets/images/gift/gift2.png', 'user': 'Andy', 'avatar': 'assets/images/avatar/img06.jpg', 'num': 36},
{'label': '大啤酒', 'gift': 'assets/images/gift/gift3.png', 'user': '一条咸鱼', 'avatar': 'assets/images/avatar/img01.jpg', 'num': 162},
...
],
),
// 加入直播间动效
AnimationLiveJoin(
joinQueryList: [
{'avatar': 'assets/images/logo.png', 'name': 'andy'},
{'avatar': 'assets/images/logo.png', 'name': 'jack'},
...
],
),
// 直播弹幕+商品讲解
Container(
margin: EdgeInsets.only(top: 7.0),
height: 200.0,
child: Row(
...
),
),
// 底部工具栏
Container(
margin: const EdgeInsets.only(top: 7.0),
child: Row(
...
),
),
],
),
),
],
),
],
),
],
);
},
),
],
),
),
],
),
);
}
以上就是flutter3.27.1实战抖音app商城的一些知识分享,整个项目涉及到知识点蛮多。希望以上分享对小伙伴们有些帮助哈~~
往期热文
https://segmentfault.com/a/1190000045042968
https://segmentfault.com/a/1190000045245775
https://segmentfault.com/a/1190000045381943
https://segmentfault.com/a/1190000045556523
https://segmentfault.com/a/1190000045667190
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000046075489
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
原创自研flutter3.27+dart3.6
实战抖音短视频+聊天+直播电商带货app商城应用程序。
Flutter3.27仿抖音短视频+直播+聊天app商城系统
技术栈
- 编辑器:vscode
- 技术框架:flutter3.27.1+Dart3.6.0
- 路由/状态管理:get: ^4.6.6
- 本地缓存服务:get_storage: ^2.1.1
- 瀑布流组件:flutter_staggered_grid_view^0.7.0
- 轮播图组件:card_swiper^3.0.1
- toast弹窗组件:shirne_dialog^4.8.3
- 视频套件:media_kit: ^1.1.11
实现类似抖音app首页左右滑动切换页面内容,上下滑动切换短视频效果。
项目框架
目前flutter3-douyin-mall短视频直播商城项目已经同步到我的原创作品集。
Flutter3.27仿抖音短视频+直播+聊天app商城系统
flutter3实现首页轮播图+tab滚动吸附
return Scaffold(
backgroundColor: Colors.grey[50],
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: CustomScrollView(
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
controller: scrollController,
slivers: [
SliverAppBar(
backgroundColor: Colors.transparent,
foregroundColor: Colors.white,
pinned: true,
expandedHeight: 200.0,
titleSpacing: 10.0,
// 搜索框(高斯模糊背景)
title: ClipRRect(
borderRadius: BorderRadius.circular(30.0),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
child: Container(
...
),
),
),
actions: [
IconButton(icon: Icon(Icons.shopping_cart_outlined), onPressed: () {},),
],
// 自定义伸缩区域(轮播图)
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Color(0xFFFF5000), Color(0xFFfcaec4)
]
)
),
child: FlexibleSpaceBar(
background: Swiper.children(
pagination: SwiperPagination(
builder: DotSwiperPaginationBuilder(
color: Colors.white70,
activeColor: Colors.white,
)
),
indicatorLayout: PageIndicatorLayout.SCALE,
children: [
Image.network('https://m.360buyimg.com/babel/jfs/t20271217/224114/35/38178/150060/6760d559Fd654f946/968c156726b6e822.png',),
Image.network('https://m.360buyimg.com/babel/jfs/t20280117/88832/5/48468/139826/6789cbcfF4e0b2a3d/9dc54355b6f65c40.jpg',),
Image.network('https://m.360buyimg.com/babel/jfs/t20280108/255505/29/10540/137372/677ddbc1F6cdbbed0/bc477fadedef22a8.jpg',),
],
),
),
),
),
...
// tabbar列表
SliverPersistentHeader(
pinned: true,
delegate: CustomStickyHeader(
child: PreferredSize(
preferredSize: Size.fromHeight(45.0),
child: Container(
...
),
),
),
),
// 瀑布流列表
...
],
),
),
// 返回顶部
floatingActionButton: Backtop(controller: scrollController, offset: scrollOffset),
);
flutter3实现短视频功能
@override
Widget build(BuildContext context) {
return Scaffold(
key: scaffoldKey,
extendBodyBehindAppBar: true,
appBar: AppBar(
forceMaterialTransparency: true,
backgroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? null : Colors.transparent,
foregroundColor: [0, 1, 4, 5].contains(videoModuleController.videoTabIndex.value) ? Colors.black : Colors.white,
titleSpacing: 1.0,
leading: Obx(() => IconButton(
icon: Badge.count(
backgroundColor: Colors.red,
count: 6,
child: Icon(Icons.sort_rounded, color: tabColor(),),
),
onPressed: () {
// 自定义打开右侧drawer
scaffoldKey.currentState?.openDrawer();
},
)),
title: Obx(() {
return ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: TabBar(
...
),
);
}),
actions: [
Obx(() => IconButton(icon: Icon(Icons.search_rounded, color: tabColor(),), onPressed: () {},),),
],
),
body: ScrollConfiguration(
behavior: CustomScrollBehavior().copyWith(scrollbars: false),
child: PageView(
controller: pageController,
onPageChanged: (index) {
videoModuleController.updateVideoTabIndex(index);
setState(() {
tabController.animateTo(index, duration: Duration(milliseconds: 200), curve: Curves.easeInOut);
});
},
children: [
...tabModules
],
),
),
// 侧边栏
drawer: Drawer(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.horizontal(right: Radius.circular(15.0))),
clipBehavior: Clip.antiAlias,
width: 300,
child: Container(
...
),
),
);
}
flutter3实现直播功能
// flutter3直播模块 Q:282310962
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
extendBodyBehindAppBar: true,
appBar: AppBar(
forceMaterialTransparency: true,
backgroundColor: Colors.black,
foregroundColor: Colors.white,
toolbarHeight: 0,
),
body: Column(
children: [
Expanded(
child: Stack(
children: [
PageView.builder(
scrollBehavior: CustomScrollBehavior().copyWith(scrollbars: false),
scrollDirection: Axis.vertical,
controller: pageVerticalController,
onPageChanged: (index) async {
setState(() {
liveIndex = index;
});
player.stop();
await player.open(Media(liveJson[index]['src']));
},
itemCount: liveJson.length,
itemBuilder: (context, index) {
return Stack(
children: [
// 视频区域
Positioned(
...
),
/// 水平滚动模块(清屏/浮层)
PageView(
scrollDirection: Axis.horizontal,
controller: pageHorizontalController,
onPageChanged: (index) {
// ...
},
children: [
// 直播清屏
Container(
...
),
// 直播浮层
Stack(
children: [
// 顶部区域
Positioned(
top: MediaQuery.of(context).padding.top + 7,
left: 10.0,
right: 0,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 直播间头像
Container(
...
),
// 排名统计
Container(
...
),
// 红包活动
Container(
...
),
],
),
),
// 底部区域
Positioned(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 商品购买动效
Container(
...
),
// 送礼物动效
AnimationLiveGift(
giftQueryList: [
{'label': '小心心', 'gift': 'assets/images/gift/gift1.png', 'user': 'Jack', 'avatar': 'assets/images/avatar/img02.jpg', 'num': 12},
{'label': '棒棒糖', 'gift': 'assets/images/gift/gift2.png', 'user': 'Andy', 'avatar': 'assets/images/avatar/img06.jpg', 'num': 36},
{'label': '大啤酒', 'gift': 'assets/images/gift/gift3.png', 'user': '一条咸鱼', 'avatar': 'assets/images/avatar/img01.jpg', 'num': 162},
...
],
),
// 加入直播间动效
AnimationLiveJoin(
joinQueryList: [
{'avatar': 'assets/images/logo.png', 'name': 'andy'},
{'avatar': 'assets/images/logo.png', 'name': 'jack'},
...
],
),
// 直播弹幕+商品讲解
Container(
margin: EdgeInsets.only(top: 7.0),
height: 200.0,
child: Row(
...
),
),
// 底部工具栏
Container(
margin: const EdgeInsets.only(top: 7.0),
child: Row(
...
),
),
],
),
),
],
),
],
),
],
);
},
),
],
),
),
],
),
);
}
以上就是flutter3.27.1实战抖音app商城的一些知识分享,整个项目涉及到知识点蛮多。希望以上分享对小伙伴们有些帮助哈~~
往期热文
https://segmentfault.com/a/1190000045042968
https://segmentfault.com/a/1190000045245775
https://segmentfault.com/a/1190000045381943
https://segmentfault.com/a/1190000045556523
https://segmentfault.com/a/1190000045667190
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000046075489
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

浏览器信息检测小工具
https://passer-by.com/browser/
进入上方网页可以检测浏览器信息
https://passer-by.com/browser/
进入上方网页可以检测浏览器信息

vue3.5+tauri2.0+arco桌面版OS系统|vite6.0+tauri2仿macos/windows桌面
经过了三周的爆肝研发,我的又一款原创跨平台重磅新作tauri2.1+vite6+vue3 setup+pinia2+arco.design
桌面客户端OS管理系统Tauri2Vue3OS,正式宣告完结了。支持macos和windows两种桌面风格。
Tauri2.0-Vue3-MacOS桌面端os平台|tauri2+vite6.0+arco电脑版OS管理系统
vue3-tauri2-os系统提供macos和windows11桌面风格、自研拖拽式栅格桌面引擎、封装tauri2多窗口管理、自定义json配置桌面/Dock菜单。
实现技术
- 技术框架:vite^6.0.3+vue^3.5.13+vue-router^4.5.0
- 跨平台框架:tauri^2.1.1
- 组件库:@arco-design/web-vue^2.56.3 (字节桌面版vue3组件库)
- 状态管理:pinia^2.3.0
- 拖拽插件:sortablejs^1.15.6
- 滑屏组件:swiper^11.1.15
- 图表组件:echarts^5.5.1
- markdown编辑器:md-editor-v3^5.1.1
- 模拟数据:mockjs^1.1.0
tauri2.0-vue3os已经正式发布到我的原创作品集,感兴趣的可以去看看。
项目框架目录结构
使用最新版tauri2.0跨平台框架技术,整合vite6构建工具。
tauri2.0-vue3os布局模板
<script setup>
import { appState } from '@/pinia/modules/app'
// 引入布局模板
import MacosLayout from './template/macos.vue'
import WindowsLayout from './template/windows.vue'
const appstate = appState()
const DeskLayout = {
macos: MacosLayout,
windows: WindowsLayout
}
</script>
<template>
<div class="vu__container flexbox" :style="{'--themeSkin': appstate.config.skin}">
<component :is="DeskLayout[appstate.config.layout]" />
</div>
</template>
<script setup>
import { appState } from '@/pinia/modules/app'
import Titlebar from '@/layouts/components/titlebar/index.vue'
import Desk from '@/layouts/components/mac/desk.vue'
import Dock from '@/layouts/components/mac/dock.vue'
const appstate = appState()
</script>
<template>
<div class="vu__layout flexbox flex-col">
<div class="vu__layout-header">
<Titlebar />
</div>
<div class="vu__layout-body flex1 flexbox">
<Desk />
</div>
<div class="vu__layout-footer">
<Dock v-if="appstate.config.dockEnable" />
</div>
</div>
</template>
tauri2+vue3栅格布局
栅格桌面菜单支持如下参数配置:
/**
* label 图标标题
* imgico 图标(本地或网络图片) 支持Arco Design内置图标或自定义iconfont图标
* path 跳转路由页面
* link 跳转外部链接
* hideLabel 是否隐藏图标标题
* filter 是否禁用拖拽
* background 自定义图标背景色
* color 自定义图标颜色
* size 栅格磁贴布局 1x1 ... 12x12
* padding 内边距
* onClick 点击图标回调函数
* isNewin 新窗口打开路由页面
* children 二级菜单
*/
支持children配置二级菜单。
tauri2.0+vue3自定义底部Dock菜单
Dock菜单配置参数:
/**
* label 图标tooltip提示
* imgico 图标(本地或网络图片) 支持Arco Design内置图标或自定义iconfont图标
* path 跳转路由页面
* link 跳转外部链接
* filter 是否禁用拖拽
* color 自定义图标颜色
* onClick 点击图标回调函数
* isNewin 新窗口打开路由页面
* children 二级菜单
*/
另外系统托盘采用tauri2+vue3自定义弹窗实现系统托盘右键功能。
OK,综上就是Tauri2.0+Vue3+Arco实战桌面端os管理系统的一些知识分享。
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045667190
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
经过了三周的爆肝研发,我的又一款原创跨平台重磅新作tauri2.1+vite6+vue3 setup+pinia2+arco.design
桌面客户端OS管理系统Tauri2Vue3OS,正式宣告完结了。支持macos和windows两种桌面风格。
Tauri2.0-Vue3-MacOS桌面端os平台|tauri2+vite6.0+arco电脑版OS管理系统
vue3-tauri2-os系统提供macos和windows11桌面风格、自研拖拽式栅格桌面引擎、封装tauri2多窗口管理、自定义json配置桌面/Dock菜单。
实现技术
- 技术框架:vite^6.0.3+vue^3.5.13+vue-router^4.5.0
- 跨平台框架:tauri^2.1.1
- 组件库:@arco-design/web-vue^2.56.3 (字节桌面版vue3组件库)
- 状态管理:pinia^2.3.0
- 拖拽插件:sortablejs^1.15.6
- 滑屏组件:swiper^11.1.15
- 图表组件:echarts^5.5.1
- markdown编辑器:md-editor-v3^5.1.1
- 模拟数据:mockjs^1.1.0
tauri2.0-vue3os已经正式发布到我的原创作品集,感兴趣的可以去看看。
项目框架目录结构
使用最新版tauri2.0跨平台框架技术,整合vite6构建工具。
tauri2.0-vue3os布局模板
<script setup>
import { appState } from '@/pinia/modules/app'
// 引入布局模板
import MacosLayout from './template/macos.vue'
import WindowsLayout from './template/windows.vue'
const appstate = appState()
const DeskLayout = {
macos: MacosLayout,
windows: WindowsLayout
}
</script>
<template>
<div class="vu__container flexbox" :style="{'--themeSkin': appstate.config.skin}">
<component :is="DeskLayout[appstate.config.layout]" />
</div>
</template>
<script setup>
import { appState } from '@/pinia/modules/app'
import Titlebar from '@/layouts/components/titlebar/index.vue'
import Desk from '@/layouts/components/mac/desk.vue'
import Dock from '@/layouts/components/mac/dock.vue'
const appstate = appState()
</script>
<template>
<div class="vu__layout flexbox flex-col">
<div class="vu__layout-header">
<Titlebar />
</div>
<div class="vu__layout-body flex1 flexbox">
<Desk />
</div>
<div class="vu__layout-footer">
<Dock v-if="appstate.config.dockEnable" />
</div>
</div>
</template>
tauri2+vue3栅格布局
栅格桌面菜单支持如下参数配置:
/**
* label 图标标题
* imgico 图标(本地或网络图片) 支持Arco Design内置图标或自定义iconfont图标
* path 跳转路由页面
* link 跳转外部链接
* hideLabel 是否隐藏图标标题
* filter 是否禁用拖拽
* background 自定义图标背景色
* color 自定义图标颜色
* size 栅格磁贴布局 1x1 ... 12x12
* padding 内边距
* onClick 点击图标回调函数
* isNewin 新窗口打开路由页面
* children 二级菜单
*/
支持children配置二级菜单。
tauri2.0+vue3自定义底部Dock菜单
Dock菜单配置参数:
/**
* label 图标tooltip提示
* imgico 图标(本地或网络图片) 支持Arco Design内置图标或自定义iconfont图标
* path 跳转路由页面
* link 跳转外部链接
* filter 是否禁用拖拽
* color 自定义图标颜色
* onClick 点击图标回调函数
* isNewin 新窗口打开路由页面
* children 二级菜单
*/
另外系统托盘采用tauri2+vue3自定义弹窗实现系统托盘右键功能。
OK,综上就是Tauri2.0+Vue3+Arco实战桌面端os管理系统的一些知识分享。
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045667190
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android平台5+离线打包
本教程一笔化整个离线打包,不列举其他选情况,主要解决官方文档一次看不懂,后续熟悉流程可自行根据官方文档修改自定义设置。参考链接如下:
开发环境及工具
软件的下载演示为MAC,Windows下载选择Windows环境即可。
HbuilderX与SDK版本需要要求一致,PS:有相关bug提出新SDK(4.36)会白屏。本次演示使用4.29版本
使用新版以及后续SDK注意,新版由本次演示后的版本在注意事项中标明Andriod Studio相关环境使用时仔细阅读使用环境(后续下载SDK官方应该会修改环境),本次演示不包含该内容。
- java 1.8
Andriod离线打包
Android Studio打开项目
可能遇到的问题:
-
下载gradle失败
-
分不清build.gradle
下载gradle失败
需要修改为国内镜像,参考文档:Android Studio项目gradle下载慢问题
不想去看的话直接修改gradle/wrapper/gradle-wrapper.properties下distributionUrl配置
修改为
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.5-all.zip
分不清build.gradle
打开的项目加载完成后会切换为Andriod战士方式,配置时建议切换为项目展示方式
项目中包含两个build.gradle文件分别为项目级和应用级,后续教程内以该规则进行描述
生成证书
可参考文档: 生成签名证书
需要JAVA JDK环境1.8
选择APK,点击下一步
选择创建新的
填写信息,确定
证书名称: simpleDemo.keystore
证书密码: 123456
别名: simple
别名密码: 123123
证书信息:
demo,demo,demo,changsha,hunan,CN
下一步
创建
申请APP离线key
打开终端
移动到证书所在目录
查看证书信息
命令如下
cd simpleDemo
ls
keytool -list -v -keystore simpleDemo.keystore
123456(这里是我生成的 个人配置的输入个人的)
证书指纹
可能遇到的错误:
- java环境
java环境是未进行详细解释,如果是完全按照教程流程唯一可能遇到java环境导致生成不正常意外报错,如果有可以问下评论区大佬。
打开应用管理
各平台信息,新增
填写信息,提交,内容参考上面的证书详情
创建离线key
创建
查看离线key
生成本地打包资源
生成资源
找到资源
替换资源
修改APPID
修改app离线key
修改证书
修改应用级build.gradle,别名密码和证书密码是可以一致的,区别开是为了更好的分辨某个填写至某个
运行
修改build.gradle后需要点击一次Sync Now或者资源重新加载参考教程内 同步gradle配置想·
完成后点击运行
运行完成
本教程一笔化整个离线打包,不列举其他选情况,主要解决官方文档一次看不懂,后续熟悉流程可自行根据官方文档修改自定义设置。参考链接如下:
开发环境及工具
软件的下载演示为MAC,Windows下载选择Windows环境即可。
HbuilderX与SDK版本需要要求一致,PS:有相关bug提出新SDK(4.36)会白屏。本次演示使用4.29版本
使用新版以及后续SDK注意,新版由本次演示后的版本在注意事项中标明Andriod Studio相关环境使用时仔细阅读使用环境(后续下载SDK官方应该会修改环境),本次演示不包含该内容。
- java 1.8
Andriod离线打包
Android Studio打开项目
可能遇到的问题:
-
下载gradle失败
-
分不清build.gradle
下载gradle失败
需要修改为国内镜像,参考文档:Android Studio项目gradle下载慢问题
不想去看的话直接修改gradle/wrapper/gradle-wrapper.properties下distributionUrl配置
修改为
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-6.5-all.zip
分不清build.gradle
打开的项目加载完成后会切换为Andriod战士方式,配置时建议切换为项目展示方式
项目中包含两个build.gradle文件分别为项目级和应用级,后续教程内以该规则进行描述
生成证书
可参考文档: 生成签名证书
需要JAVA JDK环境1.8
选择APK,点击下一步
选择创建新的
填写信息,确定
证书名称: simpleDemo.keystore
证书密码: 123456
别名: simple
别名密码: 123123
证书信息:
demo,demo,demo,changsha,hunan,CN
下一步
创建
申请APP离线key
打开终端
移动到证书所在目录
查看证书信息
命令如下
cd simpleDemo
ls
keytool -list -v -keystore simpleDemo.keystore
123456(这里是我生成的 个人配置的输入个人的)
证书指纹
可能遇到的错误:
- java环境
java环境是未进行详细解释,如果是完全按照教程流程唯一可能遇到java环境导致生成不正常意外报错,如果有可以问下评论区大佬。
打开应用管理
各平台信息,新增
填写信息,提交,内容参考上面的证书详情
创建离线key
创建
查看离线key
生成本地打包资源
生成资源
找到资源
替换资源
修改APPID
修改app离线key
修改证书
修改应用级build.gradle,别名密码和证书密码是可以一致的,区别开是为了更好的分辨某个填写至某个
运行
修改build.gradle后需要点击一次Sync Now或者资源重新加载参考教程内 同步gradle配置想·
完成后点击运行
运行完成

uni-admin,H5发布后,报错“未找到集合[xxx]对应的schema”的一种问题原因分享
当然,造成该问题的原因,最好还是看一下对应的schema是不是上传到了unicloud,或者写错了名字
我的故障现象是:在本地测试时,完全没问题,但发布后,就会报错未找到集合[xxx]对应的schema,就偶尔几个表,其他大部分也没事。而且检查云数据库里,确实该表是存在的。
我最终发现,问题出在,我建的数据库表超过100个了,至于怎么出来101个表,我也不知道。
解决办法是删没用的表,删到100个以内,问题就解决了。
希望能帮到大家,这问题耽误了好几个小时。
当然,造成该问题的原因,最好还是看一下对应的schema是不是上传到了unicloud,或者写错了名字
我的故障现象是:在本地测试时,完全没问题,但发布后,就会报错未找到集合[xxx]对应的schema,就偶尔几个表,其他大部分也没事。而且检查云数据库里,确实该表是存在的。
我最终发现,问题出在,我建的数据库表超过100个了,至于怎么出来101个表,我也不知道。
解决办法是删没用的表,删到100个以内,问题就解决了。
希望能帮到大家,这问题耽误了好几个小时。
收起阅读 »
vue3.5+tauri v2桌面版后台管理系统|vite5+tauri2+element-plus客户端后台模板
经过大半个月高强度实战开发,又一款原创跨平台新作tauri2.0+vue3+pinia2+elementPlus+mockjs
电脑端通用权限后台管理系统,正式结束了。实现4种通用布局模板,支持vue-i18n国际化、面包屑导航、tab标签路由等功能。
tauri2.0-vue3admin桌面端管理系统|tauri2+vite5+element-plus后台EXE程序
使用技术
- 开发工具:VScode
- 技术框架:tauri2.0+vite^5.4.8+vue^3.5.11+vue-router^4.4.5
- 状态管理:pinia^2.2.4
- 存储服务:pinia-plugin-persistedstate^4.1.1
- 组件库:element-plus^2.8.5
- 图表组件:echarts^5.5.1
- 国际化:vue-i18n^10.0.4
- 富文本编辑器:@vueup/vue-quill^1.2.0
- md编辑器:md-editor-v3^4.20.3
- 模拟数据:mockjs^1.1.0
- 预处理样式:sass^1.79.4
目前该项目Tauri2-Vue3Admin已经同步发布到我的原创作品集。
项目结构
tauri2-admin后台布局模板
<script setup>
import { appState } from '@/pinia/modules/app'
import Toolbar from '@/layouts/components/Toolbar.vue'
import Sidebar from '@/layouts/components/sidebar/index.vue'
import Menus from '@/layouts/components/menus/index.vue'
import Breadcrumb from '@/layouts/components/Breadcrumb.vue'
import Tabview from '@/layouts/components/Tabview.vue'
import Main from '@/layouts/components/Main.vue'
const appstate = appState()
</script>
<template>
<div class="vuadmin__layout flexbox flex-col">
<Toolbar />
<div class="vuadmin__layout-body flex1 flexbox">
<!-- 侧边栏 -->
<div class="vuadmin__layout-sidebar">
<Sidebar />
</div>
<!-- 菜单栏 -->
<div class="vuadmin__layout-menus" :class="{'hidden': appstate.config.collapsed}">
<el-scrollbar>
<Menus :rootRouteEnable="false" />
</el-scrollbar>
</div>
<!-- 右侧主内容区 -->
<div class="vuadmin__layout-main flex1 flexbox flex-col">
<!-- 面包屑导航 -->
<Breadcrumb v-if="appstate.config.breadcrumb" />
<!-- 标签页 -->
<Tabview v-if="appstate.config.tabview" />
<!-- 内容区 -->
<Main />
</div>
</div>
</div>
</template>
tauri2-admin国际化配置
import { createI18n } from 'vue-i18n'
import { appState } from '@/pinia/modules/app'
// 引入语言配置
import enUS from './en-US'
import zhCN from './zh-CN'
import zhTW from './zh-TW'
// 默认语言
export const langVal = 'zh-CN'
export default async (app) => {
const appstate = appState()
const lang = appstate.lang || langVal
appstate.setLang(lang)
const i18n = createI18n({
legacy: false,
locale: lang,
messages: {
'en': enUS,
'zh-CN': zhCN,
'zh-TW': zhTW
}
})
app.use(i18n)
}
tauri2-admin实现自定义导航栏
<script setup>
import { ref, markRaw } from 'vue'
import { ElMessageBox } from 'element-plus'
import { QuestionFilled, SwitchButton } from '@element-plus/icons-vue'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { listen } from '@tauri-apps/api/event'
import { exit } from '@tauri-apps/plugin-process'
import { isTrue } from '@/utils'
import { authState } from '@/pinia/modules/auth'
const authstate = authState()
const props = defineProps({
color: String,
// 窗口是否可最小化
minimizable: {type: [Boolean, String], default: true},
// 窗口是否可最大化
maximizable: {type: [Boolean, String], default: true},
// 窗口是否可关闭
closable: {type: [Boolean, String], default: true},
// 层级
zIndex: {type: [Number, String], default: 2024},
})
const hasMaximized = ref(false)
const isResizable = ref(true)
const isMaximizable = ref(true)
// 用户是否可以手动调整窗口大小
getCurrentWindow().isResizable().then(res => {
isResizable.value = res
})
// 窗口是否可以最大化
getCurrentWindow().isMaximizable().then(res => {
isMaximizable.value = res
})
// 初始监听窗口是否最大化
getCurrentWindow().isMaximized().then(res => {
hasMaximized.value = res
})
// 实时监听窗口是否最大化
listen('tauri://resize', async() => {
hasMaximized.value = await getCurrentWindow().isMaximized()
})
// 最小化
const handleWinMin = async() => {
await getCurrentWindow().minimize()
}
// 最大化/还原
const handleWinToggle = async() => {
await getCurrentWindow().toggleMaximize()
}
// 关闭
const handleWinClose = async() => {
const isMajor = getCurrentWindow().label.indexOf('main') > -1
if(isMajor) {
ElMessageBox.confirm('是否最小化到系统托盘,不退出程序?', '提示', {
type: 'warning',
icon: markRaw(QuestionFilled),
confirmButtonText: '残忍退出',
cancelButtonText: '最小化到托盘',
customStyle: {'width': '300px'},
draggable: true,
roundButton: true,
center: true,
buttonSize: 'small',
distinguishCancelAndClose: true,
}).then(async() => {
authstate.logout()
await exit()
}).catch(async(action) => {
if(action === 'cancel') {
await getCurrentWindow().hide()
}
})
}else {
await getCurrentWindow().close()
}
}
</script>
<template>
<div class="ev__winbtns flexbox flex-alignc vu__drag" :style="{'z-index': zIndex}">
<div class="ev__winbtns-actions flexbox flex-alignc vu__undrag" :style="{'color': color}">
<a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon iconfont elec-icon-min"></i></a>
<a v-if="isTrue(maximizable)" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">
<i class="wicon iconfont" :class="hasMaximized ? 'elec-icon-restore' : 'elec-icon-max'"></i>
</a>
<a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleWinClose"><i class="wicon iconfont elec-icon-quit"></i></a>
</div>
</div>
</template>
<style lang="scss" scoped>
@import './index.scss';
</style>
tauri2-vue3admin自定义标签栏路由
<template>
<div class="vu__tabview">
<el-tabs
v-model="activeTab"
class="vu__tabview-tabs"
@tab-change="changeTabs"
@tab-remove="removeTab"
>
<el-tab-pane
v-for="(item, index) in tabList"
:key="index"
:name="item.path"
:closable="!item?.meta?.isAffix"
>
<template #label>
<el-dropdown ref="dropdownRef" trigger="contextmenu" :id="item.path" @visible-change="handleDropdownChange($event, item.path)" @command="handleDropdownCommand($event, item)">
<span class="vu__tabview-tabs__label">
<span>{{$t(item?.meta?.title)}}</span>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="refresh" :icon="Refresh">{{$t('tabview__contextmenu-refresh')}}</el-dropdown-item>
<el-dropdown-item command="close" :icon="Close" :disabled="item.meta.isAffix">{{$t('tabview__contextmenu-close')}}</el-dropdown-item>
<el-dropdown-item command="closeOther" :icon="Switch">{{$t('tabview__contextmenu-closeother')}}</el-dropdown-item>
<el-dropdown-item command="closeLeft" :icon="DArrowLeft">{{$t('tabview__contextmenu-closeleft')}}</el-dropdown-item>
<el-dropdown-item command="closeRight" :icon="DArrowRight">{{$t('tabview__contextmenu-closeright')}}</el-dropdown-item>
<el-dropdown-item command="closeAll" :icon="CircleCloseFilled">{{$t('tabview__contextmenu-closeall')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-tab-pane>
</el-tabs>
</div>
</template>
tauri2+vue3实现自定义托盘右键菜单功能。
End,以上就是tauri2+vue3+pinia2实战开发桌面版后台管理系统模板的一些知识分享。
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045381943
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
经过大半个月高强度实战开发,又一款原创跨平台新作tauri2.0+vue3+pinia2+elementPlus+mockjs
电脑端通用权限后台管理系统,正式结束了。实现4种通用布局模板,支持vue-i18n国际化、面包屑导航、tab标签路由等功能。
tauri2.0-vue3admin桌面端管理系统|tauri2+vite5+element-plus后台EXE程序
使用技术
- 开发工具:VScode
- 技术框架:tauri2.0+vite^5.4.8+vue^3.5.11+vue-router^4.4.5
- 状态管理:pinia^2.2.4
- 存储服务:pinia-plugin-persistedstate^4.1.1
- 组件库:element-plus^2.8.5
- 图表组件:echarts^5.5.1
- 国际化:vue-i18n^10.0.4
- 富文本编辑器:@vueup/vue-quill^1.2.0
- md编辑器:md-editor-v3^4.20.3
- 模拟数据:mockjs^1.1.0
- 预处理样式:sass^1.79.4
目前该项目Tauri2-Vue3Admin已经同步发布到我的原创作品集。
项目结构
tauri2-admin后台布局模板
<script setup>
import { appState } from '@/pinia/modules/app'
import Toolbar from '@/layouts/components/Toolbar.vue'
import Sidebar from '@/layouts/components/sidebar/index.vue'
import Menus from '@/layouts/components/menus/index.vue'
import Breadcrumb from '@/layouts/components/Breadcrumb.vue'
import Tabview from '@/layouts/components/Tabview.vue'
import Main from '@/layouts/components/Main.vue'
const appstate = appState()
</script>
<template>
<div class="vuadmin__layout flexbox flex-col">
<Toolbar />
<div class="vuadmin__layout-body flex1 flexbox">
<!-- 侧边栏 -->
<div class="vuadmin__layout-sidebar">
<Sidebar />
</div>
<!-- 菜单栏 -->
<div class="vuadmin__layout-menus" :class="{'hidden': appstate.config.collapsed}">
<el-scrollbar>
<Menus :rootRouteEnable="false" />
</el-scrollbar>
</div>
<!-- 右侧主内容区 -->
<div class="vuadmin__layout-main flex1 flexbox flex-col">
<!-- 面包屑导航 -->
<Breadcrumb v-if="appstate.config.breadcrumb" />
<!-- 标签页 -->
<Tabview v-if="appstate.config.tabview" />
<!-- 内容区 -->
<Main />
</div>
</div>
</div>
</template>
tauri2-admin国际化配置
import { createI18n } from 'vue-i18n'
import { appState } from '@/pinia/modules/app'
// 引入语言配置
import enUS from './en-US'
import zhCN from './zh-CN'
import zhTW from './zh-TW'
// 默认语言
export const langVal = 'zh-CN'
export default async (app) => {
const appstate = appState()
const lang = appstate.lang || langVal
appstate.setLang(lang)
const i18n = createI18n({
legacy: false,
locale: lang,
messages: {
'en': enUS,
'zh-CN': zhCN,
'zh-TW': zhTW
}
})
app.use(i18n)
}
tauri2-admin实现自定义导航栏
<script setup>
import { ref, markRaw } from 'vue'
import { ElMessageBox } from 'element-plus'
import { QuestionFilled, SwitchButton } from '@element-plus/icons-vue'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { listen } from '@tauri-apps/api/event'
import { exit } from '@tauri-apps/plugin-process'
import { isTrue } from '@/utils'
import { authState } from '@/pinia/modules/auth'
const authstate = authState()
const props = defineProps({
color: String,
// 窗口是否可最小化
minimizable: {type: [Boolean, String], default: true},
// 窗口是否可最大化
maximizable: {type: [Boolean, String], default: true},
// 窗口是否可关闭
closable: {type: [Boolean, String], default: true},
// 层级
zIndex: {type: [Number, String], default: 2024},
})
const hasMaximized = ref(false)
const isResizable = ref(true)
const isMaximizable = ref(true)
// 用户是否可以手动调整窗口大小
getCurrentWindow().isResizable().then(res => {
isResizable.value = res
})
// 窗口是否可以最大化
getCurrentWindow().isMaximizable().then(res => {
isMaximizable.value = res
})
// 初始监听窗口是否最大化
getCurrentWindow().isMaximized().then(res => {
hasMaximized.value = res
})
// 实时监听窗口是否最大化
listen('tauri://resize', async() => {
hasMaximized.value = await getCurrentWindow().isMaximized()
})
// 最小化
const handleWinMin = async() => {
await getCurrentWindow().minimize()
}
// 最大化/还原
const handleWinToggle = async() => {
await getCurrentWindow().toggleMaximize()
}
// 关闭
const handleWinClose = async() => {
const isMajor = getCurrentWindow().label.indexOf('main') > -1
if(isMajor) {
ElMessageBox.confirm('是否最小化到系统托盘,不退出程序?', '提示', {
type: 'warning',
icon: markRaw(QuestionFilled),
confirmButtonText: '残忍退出',
cancelButtonText: '最小化到托盘',
customStyle: {'width': '300px'},
draggable: true,
roundButton: true,
center: true,
buttonSize: 'small',
distinguishCancelAndClose: true,
}).then(async() => {
authstate.logout()
await exit()
}).catch(async(action) => {
if(action === 'cancel') {
await getCurrentWindow().hide()
}
})
}else {
await getCurrentWindow().close()
}
}
</script>
<template>
<div class="ev__winbtns flexbox flex-alignc vu__drag" :style="{'z-index': zIndex}">
<div class="ev__winbtns-actions flexbox flex-alignc vu__undrag" :style="{'color': color}">
<a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon iconfont elec-icon-min"></i></a>
<a v-if="isTrue(maximizable)" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">
<i class="wicon iconfont" :class="hasMaximized ? 'elec-icon-restore' : 'elec-icon-max'"></i>
</a>
<a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleWinClose"><i class="wicon iconfont elec-icon-quit"></i></a>
</div>
</div>
</template>
<style lang="scss" scoped>
@import './index.scss';
</style>
tauri2-vue3admin自定义标签栏路由
<template>
<div class="vu__tabview">
<el-tabs
v-model="activeTab"
class="vu__tabview-tabs"
@tab-change="changeTabs"
@tab-remove="removeTab"
>
<el-tab-pane
v-for="(item, index) in tabList"
:key="index"
:name="item.path"
:closable="!item?.meta?.isAffix"
>
<template #label>
<el-dropdown ref="dropdownRef" trigger="contextmenu" :id="item.path" @visible-change="handleDropdownChange($event, item.path)" @command="handleDropdownCommand($event, item)">
<span class="vu__tabview-tabs__label">
<span>{{$t(item?.meta?.title)}}</span>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="refresh" :icon="Refresh">{{$t('tabview__contextmenu-refresh')}}</el-dropdown-item>
<el-dropdown-item command="close" :icon="Close" :disabled="item.meta.isAffix">{{$t('tabview__contextmenu-close')}}</el-dropdown-item>
<el-dropdown-item command="closeOther" :icon="Switch">{{$t('tabview__contextmenu-closeother')}}</el-dropdown-item>
<el-dropdown-item command="closeLeft" :icon="DArrowLeft">{{$t('tabview__contextmenu-closeleft')}}</el-dropdown-item>
<el-dropdown-item command="closeRight" :icon="DArrowRight">{{$t('tabview__contextmenu-closeright')}}</el-dropdown-item>
<el-dropdown-item command="closeAll" :icon="CircleCloseFilled">{{$t('tabview__contextmenu-closeall')}}</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-tab-pane>
</el-tabs>
</div>
</template>
tauri2+vue3实现自定义托盘右键菜单功能。
End,以上就是tauri2+vue3+pinia2实战开发桌面版后台管理系统模板的一些知识分享。
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045381943
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

vue3+tauri2.0+element-plus桌面端exe聊天模板|vite5+tauri2仿QQ/微信客户端程序
趁着国庆假期,我的又一款原创重磅新作Vue3.5+Tauri2.0+Vite5.4+Pinia2+ElementPlus
跨平台实战仿QQ/微信电脑端聊天Exe程序Vue3Tauri2Chat,正式的完结了。整体UI采用全新无边框透明圆角阴影窗体。
Vue3+Tauri2.0聊天实例|tauri2+vite5+element-plus仿微信|tauri聊天应用
封装tauri2.x多开窗口管理、换肤壁纸、自定义系统托盘闪烁/右键菜单功能。实现聊天、联系人、收藏、朋友圈、短视频、我的等页面模块。
运用技术
- 编码工具:Vscode
- 技术框架:tauri2.0+vite^5.4+vue^3.5+vue-router^4.4.5
- 状态管理:pinia^2.2.2
- 本地存储插件:pinia-plugin-persistedstate^4.0.2
- 组件库:element-plus^2.8.3
- 富文本编辑器:@vueup/vue-quill^1.2.0
- 样式预处理:sass^1.79.3
- 视频滑动组件:swiper^11.1.14
目前tauri2-vue3chat聊天项目已经发布到我的原创作品集,有需要的话可以去瞅瞅~
https://gf.bilibili.com/item/detail/1107133011
tauri2-vue3chat项目布局模板
<template>
<div class="vu__chatbox">
<template v-if="!route?.meta?.isNewWin">
<div class="vu__container flexbox flex-alignc flex-justifyc">
<div class="vu__layout flexbox flex-col">
<div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>
<!-- 菜单栏 -->
<slot v-if="!route?.meta?.hideMenuBar" name="menubar">
<MenuBar />
</slot>
<!-- 侧边栏 -->
<div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">
<aside class="vu__layout-sidebar__body flexbox flex-col">
<slot name="sidebar">
<SideBar />
</slot>
</aside>
</div>
<!-- 主内容区 -->
<div class="vu__layout-main flex1 flexbox flex-col">
<ToolBar v-if="!route?.meta?.hideToolBar" />
<router-view v-slot="{ Component, route }">
<keep-alive>
<component :is="Component" :key="route.path" />
</keep-alive>
</router-view>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<WinLayout />
</template>
</div>
</template>
tauri2多窗口实践
// 朋友圈窗口
const handleFzone = () => {
winCreate({
label: 'win-fzone',
url: '/win/fzone',
title: '朋友圈',
width: 500,
height: 695,
minWidth: 350,
minHeight: 500,
maximizable: false,
})
}
// 短视频窗口
const handleFvideo = () => {
winCreate({
label: 'win-fvideo',
url: '/win/fvideo',
title: '短视频',
width: 575,
height: 675,
minWidth: 415,
minHeight: 545
})
}
// 换肤壁纸窗口
const handleSkin = () => {
winCreate({
label: 'win-skin',
url: '/win/skin',
title: '壁纸',
width: 375,
height: 480,
resizable: false,
maximizable: false,
visible: true,
})
}
// 界面管理器
const handleManageUI = () => {
winCreate({
label: 'win-manage',
url: '/win/manage',
title: '界面管理器',
width: 320,
height: 360,
resizable: false,
maximizable: false,
})
}
之前有过一篇关于tauri2创建多窗口应用的分享文章,可以去看看。
基于Tauri2+Vite5搭建桌面端程序|tauri2+vue3多窗口|消息提醒|托盘闪烁
vue3+tauri2自定义透明圆角阴影窗体
.vu__chatbox {height: calc(100vh); padding: 5px; overflow: hidden;}
.vu__layout {
background-color: #f5f5f5;
overflow: hidden;
height: 100%; width: 100%;
position: relative; z-index: 100;
border-radius: 8px;
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.15),0 1px 5px -1px rgba(0, 0, 0, 0.1),0 2px 5px rgba(0, 0, 0, 0.1);
}
<script setup>
/**
* tauri2.0自定义系统最大化/最小化/关闭 by andy Q:282310962
*/
// ...
const props = defineProps({
color: String,
// 窗口是否可最小化
minimizable: {type: [Boolean, String], default: true},
// 窗口是否可最大化
maximizable: {type: [Boolean, String], default: true},
// 窗口是否可关闭
closable: {type: [Boolean, String], default: true},
// 层级
zIndex: {type: [Number, String], default: 2024},
// 关闭前回调,会暂停实例关闭 function(done),done用于关闭
beforeClose: Function
})
const hasMaximized = ref(false)
const isResizable = ref(true)
const isMaximizable = ref(true)
// 用户是否可以手动调整窗口大小
getCurrentWindow().isResizable().then(res => {
isResizable.value = res
})
// 窗口是否可以最大化
getCurrentWindow().isMaximizable().then(res => {
isMaximizable.value = res
})
// 初始监听窗口是否最大化
getCurrentWindow().isMaximized().then(res => {
hasMaximized.value = res
})
// 实时监听窗口是否最大化
listen('tauri://resize', async() => {
hasMaximized.value = await getCurrentWindow().isMaximized()
})
// 最小化
const handleWinMin = async() => {
// winSet('minimize')
await getCurrentWindow().minimize()
}
// 最大化/还原
const handleWinToggle = async() => {
// winSet('max2min')
await getCurrentWindow().toggleMaximize()
}
// 关闭
const handleClose = async() => {
const isMajor = getCurrentWindow().label.indexOf('main') > -1
if(isMajor) {
let el = layer({
type: 'android',
content: '是否最小化到托盘,不退出程序?',
layerStyle: 'background: #f9f9f9; border-radius: 8px;',
closable: false,
resize: false,
btns: [
{
text: '最小化托盘',
style: 'color: #646cff',
click: () => {
layer.close(el)
// winSet('hide')
await getCurrentWindow().hide()
}
},
{
text: '退出程序',
style: 'color: #fa5151',
click: async() => {
authstate.logout()
await exit()
}
}
]
})
}else {
// winSet('close')
await getCurrentWindow().close()
}
}
</script>
<template>
<div class="ev__winbtns vu__drag" :style="{'z-index': zIndex}">
<div class="ev__winbtns-actions vu__undrag" :style="{'color': color}">
<a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon elec-icon elec-icon-min"></i></a>
<a v-if="isTrue(maximizable) && isResizable && isMaximizable" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">
<i class="wicon elec-icon iconfont" :class="hasMaximized ? 've-icon-shrink' : 've-icon-arrowsalt'"></i>
</a>
<a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleClose"><i class="wicon elec-icon elec-icon-quit"></i></a>
</div>
</div>
</template>
综上就是vue3+tauri2.x实战桌面端聊天项目的一些知识分享。
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045331960
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
趁着国庆假期,我的又一款原创重磅新作Vue3.5+Tauri2.0+Vite5.4+Pinia2+ElementPlus
跨平台实战仿QQ/微信电脑端聊天Exe程序Vue3Tauri2Chat,正式的完结了。整体UI采用全新无边框透明圆角阴影窗体。
Vue3+Tauri2.0聊天实例|tauri2+vite5+element-plus仿微信|tauri聊天应用
封装tauri2.x多开窗口管理、换肤壁纸、自定义系统托盘闪烁/右键菜单功能。实现聊天、联系人、收藏、朋友圈、短视频、我的等页面模块。
运用技术
- 编码工具:Vscode
- 技术框架:tauri2.0+vite^5.4+vue^3.5+vue-router^4.4.5
- 状态管理:pinia^2.2.2
- 本地存储插件:pinia-plugin-persistedstate^4.0.2
- 组件库:element-plus^2.8.3
- 富文本编辑器:@vueup/vue-quill^1.2.0
- 样式预处理:sass^1.79.3
- 视频滑动组件:swiper^11.1.14
目前tauri2-vue3chat聊天项目已经发布到我的原创作品集,有需要的话可以去瞅瞅~
https://gf.bilibili.com/item/detail/1107133011
tauri2-vue3chat项目布局模板
<template>
<div class="vu__chatbox">
<template v-if="!route?.meta?.isNewWin">
<div class="vu__container flexbox flex-alignc flex-justifyc">
<div class="vu__layout flexbox flex-col">
<div class="vu__layout-body flex1 flexbox" @contextmenu.prevent>
<!-- 菜单栏 -->
<slot v-if="!route?.meta?.hideMenuBar" name="menubar">
<MenuBar />
</slot>
<!-- 侧边栏 -->
<div v-if="route?.meta?.showSideBar" class="vu__layout-sidebar flexbox">
<aside class="vu__layout-sidebar__body flexbox flex-col">
<slot name="sidebar">
<SideBar />
</slot>
</aside>
</div>
<!-- 主内容区 -->
<div class="vu__layout-main flex1 flexbox flex-col">
<ToolBar v-if="!route?.meta?.hideToolBar" />
<router-view v-slot="{ Component, route }">
<keep-alive>
<component :is="Component" :key="route.path" />
</keep-alive>
</router-view>
</div>
</div>
</div>
</div>
</template>
<template v-else>
<WinLayout />
</template>
</div>
</template>
tauri2多窗口实践
// 朋友圈窗口
const handleFzone = () => {
winCreate({
label: 'win-fzone',
url: '/win/fzone',
title: '朋友圈',
width: 500,
height: 695,
minWidth: 350,
minHeight: 500,
maximizable: false,
})
}
// 短视频窗口
const handleFvideo = () => {
winCreate({
label: 'win-fvideo',
url: '/win/fvideo',
title: '短视频',
width: 575,
height: 675,
minWidth: 415,
minHeight: 545
})
}
// 换肤壁纸窗口
const handleSkin = () => {
winCreate({
label: 'win-skin',
url: '/win/skin',
title: '壁纸',
width: 375,
height: 480,
resizable: false,
maximizable: false,
visible: true,
})
}
// 界面管理器
const handleManageUI = () => {
winCreate({
label: 'win-manage',
url: '/win/manage',
title: '界面管理器',
width: 320,
height: 360,
resizable: false,
maximizable: false,
})
}
之前有过一篇关于tauri2创建多窗口应用的分享文章,可以去看看。
基于Tauri2+Vite5搭建桌面端程序|tauri2+vue3多窗口|消息提醒|托盘闪烁
vue3+tauri2自定义透明圆角阴影窗体
.vu__chatbox {height: calc(100vh); padding: 5px; overflow: hidden;}
.vu__layout {
background-color: #f5f5f5;
overflow: hidden;
height: 100%; width: 100%;
position: relative; z-index: 100;
border-radius: 8px;
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.15),0 1px 5px -1px rgba(0, 0, 0, 0.1),0 2px 5px rgba(0, 0, 0, 0.1);
}
<script setup>
/**
* tauri2.0自定义系统最大化/最小化/关闭 by andy Q:282310962
*/
// ...
const props = defineProps({
color: String,
// 窗口是否可最小化
minimizable: {type: [Boolean, String], default: true},
// 窗口是否可最大化
maximizable: {type: [Boolean, String], default: true},
// 窗口是否可关闭
closable: {type: [Boolean, String], default: true},
// 层级
zIndex: {type: [Number, String], default: 2024},
// 关闭前回调,会暂停实例关闭 function(done),done用于关闭
beforeClose: Function
})
const hasMaximized = ref(false)
const isResizable = ref(true)
const isMaximizable = ref(true)
// 用户是否可以手动调整窗口大小
getCurrentWindow().isResizable().then(res => {
isResizable.value = res
})
// 窗口是否可以最大化
getCurrentWindow().isMaximizable().then(res => {
isMaximizable.value = res
})
// 初始监听窗口是否最大化
getCurrentWindow().isMaximized().then(res => {
hasMaximized.value = res
})
// 实时监听窗口是否最大化
listen('tauri://resize', async() => {
hasMaximized.value = await getCurrentWindow().isMaximized()
})
// 最小化
const handleWinMin = async() => {
// winSet('minimize')
await getCurrentWindow().minimize()
}
// 最大化/还原
const handleWinToggle = async() => {
// winSet('max2min')
await getCurrentWindow().toggleMaximize()
}
// 关闭
const handleClose = async() => {
const isMajor = getCurrentWindow().label.indexOf('main') > -1
if(isMajor) {
let el = layer({
type: 'android',
content: '是否最小化到托盘,不退出程序?',
layerStyle: 'background: #f9f9f9; border-radius: 8px;',
closable: false,
resize: false,
btns: [
{
text: '最小化托盘',
style: 'color: #646cff',
click: () => {
layer.close(el)
// winSet('hide')
await getCurrentWindow().hide()
}
},
{
text: '退出程序',
style: 'color: #fa5151',
click: async() => {
authstate.logout()
await exit()
}
}
]
})
}else {
// winSet('close')
await getCurrentWindow().close()
}
}
</script>
<template>
<div class="ev__winbtns vu__drag" :style="{'z-index': zIndex}">
<div class="ev__winbtns-actions vu__undrag" :style="{'color': color}">
<a v-if="isTrue(minimizable)" class="wbtn min" title="最小化" @click="handleWinMin"><i class="wicon elec-icon elec-icon-min"></i></a>
<a v-if="isTrue(maximizable) && isResizable && isMaximizable" class="wbtn toggle" :title="hasMaximized ? '向下还原' : '最大化'" @click="handleWinToggle">
<i class="wicon elec-icon iconfont" :class="hasMaximized ? 've-icon-shrink' : 've-icon-arrowsalt'"></i>
</a>
<a v-if="isTrue(closable)" class="wbtn close" title="关闭" @click="handleClose"><i class="wicon elec-icon elec-icon-quit"></i></a>
</div>
</div>
</template>
综上就是vue3+tauri2.x实战桌面端聊天项目的一些知识分享。
作者:xiaoyan2017
链接: https://segmentfault.com/a/1190000045331960
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。