做网站优化需要多少钱,app推广员好做吗,网站死链如何修改,注册公司的网站开源仓库地址 gitee
Git仓库地址:https://gitee.com/zhanhongzhu/zhanhongzhu.git
应用地址
windows应用地址下载 https://kestrel-task.cn
具体内容
也可以看#x1f389;使用Taurivitekoa2mysql开发了一款待办效率应用 这篇文章。
#x1f4bb;技术栈
Tauri: Tauri…开源仓库地址 gitee
Git仓库地址:https://gitee.com/zhanhongzhu/zhanhongzhu.git
应用地址
windows应用地址下载 https://kestrel-task.cn
具体内容
也可以看使用Taurivitekoa2mysql开发了一款待办效率应用 这篇文章。
技术栈
Tauri: Tauri是一个用于构建现代桌面应用程序的工具结合了Rust、Vue.js和Web技术提供了强大的跨平台能力。Vue3: Vue3是流行的JavaScript框架Vue.js的最新版本具有更好的性能、更好的TypeScript支持和更多的特性。Vite5: Vite是一个现代化的构建工具Vite5是其最新版本具有快速的冷启动、热模块替换和原生ES模块支持。Koa2: Koa2是一个基于Node.js的轻量级Web框架使用异步函数处理中间件提供了简洁而强大的Web开发体验。MySQL: MySQL是一个流行的关系型数据库管理系统具有高性能、可靠性和广泛的应用领域适用于各种规模的应用程序。
我的待办
快速添加待办任务快速查看任务进度摘要等。新增标签分类更好管理待办任务。通过标签、分类筛选待办任务方便快捷。 OKR目标管理
我的想法是通过OKR管理系列的任务这样每完成一个小任务就可以关闭一个小任务直观又方便等到所有关键的小任务都完成了整个任务也就完成了。 番茄工作法
主要是一个计时的时钟可以在专注计时的时候专注地完成某项任务快捷方便使用番茄工作法选择一个待完成的任务将番茄时间设为25分钟也可以选择其他的区间专注工作中途不允许做任何与该任务无关的事。时刻保持专注。 日历视图
打开日历界面,通过视图的形式查看公历或农历日历下每个日期的待办提醒或任务事项。也可以在日历视图添加任务。 MEMO快速记录
为了更好地帮你捕捉想法与灵感提供了快速记录的输入框。专注记录想法无需思考标题和排版。控制记录长度降低记录压力快速捕捉。 统计功能
展示了每天的待办数以及每天新增的待办数量。 展示功能点
打包发布版本
脚本命令 scripts: {dev: vite,build: vite build,preview: vite preview,tauri: tauri,pub: cd build node ./updateVersion.js pnpm tauri build node ./publish.js}npm run pub更新版本号以及更新publicKey
//build/publish.js
import fs from fs
// 读取 tauri.conf.json
const tauriConf JSON.parse(fs.readFileSync(../src-tauri/tauri.conf.json, utf8));
let newVersion tauriConf.package.version; //更新的版本号// 读取 update.json
let updateJson JSON.parse(fs.readFileSync(update.json, utf8));
// 更新 update.json version
updateJson.version newVersion;//获取版本更新的内容
//签名、版本路径、发版日期
const signature fs.readFileSync(../src-tauri/target/release/bundle/msi/kestrel-task_${newVersion}_x64_zh-CN.msi.zip.sig, utf8);
updateJson.platforms[windows-x86_64].signature signature;
updateJson.platforms[windows-x86_64].url https://kestrel-task.cn/kestrel-task_${newVersion}_x64_zh-CN.msi.zip
updateJson.pub_date new Date();
fs.writeFileSync(update.json, JSON.stringify(updateJson, null, 2));版本json信息
//build/update.json{version: 1.0.8,notes: kestrel-task,pub_date: 2024-03-23T04:23:39.799Z,platforms: {windows-x86_64: {signature: dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSNVRCUit5Zjc1Y3JLV085djl6eTMza2NqMXFIV0paNkl2ckgrTGZTRm9wcEJwcUlkaTBhM2hvN3pSVkRUZXlTZ2NSejJremg2Vklja041VkZmdGlZZ0hxTGVVM2xlL3dFPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzExMTY3ODE5CWZpbGU6a2VzdHJlbC10YXNrXzEuMC44X3g2NF96aC1DTi5tc2kuemlwCldSWVdwb0dwRU1aQUJ2ckFra2FTMjBkcnZtL0FWU3grd3MzeHZVTDhWRFFFUC9QWkpzdUNvUG9HZXBrVmhWMkoxTkpGc2pkYU5rRHYwcVdHdlk5dkFBPT0K,url: https://kestrel-task.cn/kestrel-task_1.0.8_x64_zh-CN.msi.zip}}
}
更新tauri.conf.json版本信息
// build/updateVersion.js
import fs from fs // 读取 tauri.conf.json
const tauriConf JSON.parse(fs.readFileSync(../src-tauri/tauri.conf.json, utf8));
let version tauriConf.package.version; //更新的版本号//更新版本号
let versionParts version.split(.).map(Number);
versionParts[2] 1;
let newVersion versionParts.join(.);
// 更新 tauri.conf.json version
tauriConf.package.version newVersion;
fs.writeFileSync(../src-tauri/tauri.conf.json, JSON.stringify(tauriConf, null, 2));使用tauri的http模块
此处进行简单的封装接口请求在控制台无法被查看到。如果觉得不方便完全可以使用axios库。也是可以的。
import { http } from tauri-apps/api;export function request(config) {return new Promise((resolve, reject) {http.fetch(https://kestrel-task.cn config.url, {method: POST,body: http.Body.json(config.data),headers: {Authorization: token,},}).then((res) {resolve(res.data.data);}).catch((err) {reject(err);});});
}
http封装get请求
export function requestGet(config, d) {let url d ? config.url : https://kestrel-task.cn config.url;return new Promise((resolve, reject) {http.fetch(url, {method: get,headers: {Authorization: token,},}).then((res) {resolve(d ? res.data : res.data.data);}).catch((err) {reject(err);});});
}
使用封装的request函数
export const login (data) {return request({url: /web/login,method: post,data,});
};使用WebviewWindow封装公共的窗口
import { WebviewWindow } from tauri-apps/api/window
import { emit } from tauri-apps/api/event// 创建新窗口
export async function createWin(args) {await emit(win-create, args)
}// 获取窗口
export async function getWin(label) {return await WebviewWindow.getByLabel(label)
}/*** desc 设置窗口* param type {string} show|hide|close|min|max|max2min|exit|relaunch*/
export async function setWin(type) {await emit(win- type)
}// 登录窗口
export async function loginWin() {await createWin({label: Login,title: 登录,url: /login,width: 320,height: 420,resizable: false,alwaysOnTop: true,})
}// .../*** desc 封装新开多窗体*/import {WebviewWindow,appWindow,getAll,getCurrent,
} from tauri-apps/api/window;
import { relaunch, exit } from tauri-apps/api/process;
import { emit, listen } from tauri-apps/api/event;import { setWin } from ./actions.js;// 系统参数配置
export const windowConfig {label: null, // 窗口唯一labeltitle: , // 窗口标题url: , // 路由地址urlwidth: 900, // 窗口宽度height: 640, // 窗口高度minWidth: null, // 窗口最小宽度minHeight: null, // 窗口最小高度x: null, // 窗口相对于屏幕左侧坐标y: null, // 窗口相对于屏幕顶端坐标center: true, // 窗口居中显示resizable: true, // 是否支持缩放maximized: false, // 最大化窗口decorations: true, // 窗口是否无边框及导航条alwaysOnTop: false, // 置顶窗口
};class Windows {constructor() {this.mainWin null;}// 获取窗口getWin(label) {return WebviewWindow.getByLabel(label);}// 获取全部窗口getAllWin() {return getAll();}// 创建新窗口async createWin(options) {const args Object.assign({}, windowConfig, options);// 判断窗口是否存在const existWin getAll().find((w) w.label args.label);if (existWin) {if (existWin.label.indexOf(main) -1) {await existWin?.unminimize();await existWin?.setFocus();return;}await existWin?.close();}// 创建窗口对象let win new WebviewWindow(args.label, args);// 是否最大化if (args.maximized args.resizable) {win.maximize();}// 窗口创建完毕/失败win.once(tauri://created, async () {console.log(window create success!);});win.once(tauri://error, async () {console.log(window create error!);});}// 开启主进程监听事件async listen() {// 创建新窗体await listen(win-create, (event) {this.createWin(JSON.parse(event.payload));});// 显示窗体await listen(win-show, async (event) {if (appWindow.label.indexOf(main) -1) return;await appWindow.show();await appWindow.unminimize();await appWindow.setFocus();});// 隐藏窗体await listen(win-hide, async (event) {if (appWindow.label.indexOf(main) -1) return;await appWindow.hide();});// 退出应用await listen(win-exit, async (event) {setWin(logout);await exit();});// 重启应用await listen(win-relaunch, async (event) {await relaunch();});// 主/渲染进程传参await listen(win-setdata, async (event) {await emit(win-postdata, JSON.parse(event.payload));});}
}export default Windows;
封装Echart组件便于使用 templatediv refMyEcharts :style{ height: height, width: width }/div/templatescriptimport * as echarts from echartsimport T from ./echarts-theme-T.jsecharts.registerTheme(T, T)const unwarp obj obj (obj.__v_raw || obj.valueOf() || obj)export default {...echarts,name: Charts,props: {// 高度height: { type: String, default: 100% },// 宽度width: { type: String, default: 100% },// 是否无数据nodata: { type: Boolean, default: false },// 配置项option: { type: Object, default: () {} }},data() {return {isActivat: false,myChart: null,MyEcharts:null}},watch: {option: {deep: true,handler(v) {unwarp(this.myChart).setOption(v)}}},computed: {myOptions: function() {return this.option || {}}},activated() {if (!this.isActivat) {this.$nextTick(() {this.myChart.resize()})}},deactivated() {this.isActivat false},mounted() {this.isActivat truethis.$nextTick(() {this.draw()})},methods: {draw() {const myChart echarts.init(this.$refs.MyEcharts, T)myChart.setOption(this.myOptions)this.myChart myChartwindow.addEventListener(resize, () myChart.resize())}}}/scriptechart主题模块
//echarts-theme-T.js
const T {color: [#409EFF, #36CE9E, #f56e6a, #626c91, #edb00d, #909399], // 颜色数组grid: { // 网格left: 3%, // 左边距right: 3%, // 右边距bottom: 10, // 下边距top: 40, // 上边距containLabel: true // 包含标签},legend: { // 图例textStyle: { // 文本样式color: #999 // 颜色},inactiveColor: rgba(128,128,128,0.4) // 不活跃颜色},categoryAxis: { // 类别轴axisLine: { // 轴线show: true, // 显示lineStyle: { // 线条样式color: rgba(128,128,128,0.2), // 颜色width: 1 // 宽度}},axisTick: { // 刻度线show: false, // 不显示lineStyle: { // 线条样式color: #000 // 颜色}},axisLabel: { // 轴标签color: #999 // 颜色},splitLine: { // 分隔线show: false, // 不显示lineStyle: { // 线条样式color: [#eee] // 颜色}},splitArea: { // 分隔区域show: false, // 不显示areaStyle: { // 区域样式color: [rgba(255,255,255,0.01), rgba(0,0,0,0.01)] // 颜色}}},valueAxis: { // 数值轴axisLine: { // 轴线show: false, // 不显示lineStyle: { // 线条样式color: #999 // 颜色}},splitLine: { // 分隔线show: true, // 显示lineStyle: { // 线条样式color: rgba(128,128,128,0.2) // 颜色}}}
}export default T封装公共的弹窗组件 templateel-dialog classmy-dialog draggable v-bind$attrs v-modelmodelValue :modal-append-to-bodymodalAppendToBody:append-to-bodyappendToBody :fullscreenfullscreen :close-on-click-modalcloseOnClickModal:close-on-press-escapecloseOnPressEscape :widthcomWidth :toptop closedclosedtemplate v-slot:titleslot nametitlespan classmy-dialog-title{{ dialogTitle || }}/span/slot/templatediv v-loadingloading classbody-content :style{height:comHeight}slot/slot/divtemplate #footerdiv classdialog-footer v-ifcloseBtnel-button typeclose sizesmall1 clickclosed关闭/el-button/div/template/el-dialog
/template
script setup
import { computed } from vue
const props defineProps({visible: { type: Boolean, default: false }, // 是否可见loading: { type: Boolean, default: false }, // 是否加载中top: { type: String, default: 20vh }, // 距离顶部的距离fullscreen: { type: Boolean, default: false }, // 是否全屏size: { type: String, default: big }, // 大小width: { type: [Number, String], default: 0 }, // 宽度height: { type: [Number, String], default: 55vh }, // 宽度dialogTitle: { type: String, default: }, // 弹出框标题modalAppendToBody: { type: Boolean, default: false }, // 是否将弹出框插入到body中appendToBody: { type: Boolean, default: false }, // 是否将内容插入到body中closeOnClickModal: { type: Boolean, default: false }, // 是否在点击模态框时关闭closeOnPressEscape: { type: Boolean, default: false }, // 是否在按下ESC键时关闭dblclickDisabled: { type: Boolean, default: false }, // 是否禁用双击放大closeBtn: { type: Boolean, default: false }, // 关闭按钮},[modelValue]
)const comWidth computed(() {if (props.size small) {return props.width || 30%} else if (props.size middle) {return props.width || 40%} else if (props.size big) {return props.width || 60%}return props.width || 40%
})const comHeight computed(() {return props.height || 55vh
})const emit defineEmits([update:modelValue,closed])
const closed () {emit(update:modelValue)emit(closed,false)
}
/script封装ResizeObserver函数
主要是监听元素的变化或者窗口的变化。有一部分用到了可以拉伸的左右布局的模块。
//directive/index.js
// 监听元素大小变化的指令
const map new WeakMap();
const ob new ResizeObserver((entries) {for (const entry of entries) {// 获取dom元素的回调const handler map.get(entry.target);//存在回调函数if (handler) {// 将监听的值给回调函数handler({width: entry.borderBoxSize[0].inlineSize,height: entry.borderBoxSize[0].blockSize,});}}
});export const Resize {mounted(el, binding) {//将dom与回调的关系塞入mapmap.set(el, binding.value);//监听el元素的变化ob.observe(el);},unmounted(el) {//取消监听ob.unobserve(el);},
};
const directives { Resize };const registerDirective (app) {Object.keys(directives).forEach((key) {app.directive(key, directives[key]);});
};
export default registerDirective;
公共拖曳布局的函数
主要是用于左右布局宽度的变化可以使用鼠标进行拖曳改变左右盒子的高度。
export const useCommon (){function setLayoutDrag(dragId) {const resize document.getElementById(dragId)let previousElement resize.previousSiblinglet nextElement resize.nextSiblinglet previousTag previousElement.tagNamelet nextTag nextElement.tagNameresize.onmousedown (e) {const startX e.clientXconst startY e.clientYlet type if (previousTag ASIDE nextTag MAIN) {type ASIDE-MAIN} else if (previousTag MAIN nextTag ASIDE) {type MAIN-ASIDE} else if ((previousTag HEADER nextTag MAIN) ||(previousTag FOOTER nextTag MAIN)) {type HEADER-MAIN} else if ((previousTag MAIN nextTag HEADER) ||(previousTag MAIN nextTag FOOTER)) {type MAIN-HEADER}let initWidth 0,initHeight 0if (type ASIDE-MAIN) {initWidth previousElement.clientWidth // 初始位置} else if (type MAIN-ASIDE) {initWidth nextElement.clientWidth // 初始位置} else if (type HEADER-MAIN) {initHeight previousElement.clientHeight} else if (type MAIN-HEADER) {initHeight nextElement.clientHeight}document.onmousemove (k) {const endX k.clientXconst endY k.clientYlet moveLen endX - startX // 横向移动宽度let moveHeight endY - startY // 纵向移动高度switch (type) {case ASIDE-MAIN:let asideMainWidth initWidth moveLenif (moveLen 0) {// 向左移if (asideMainWidth 400) {// 左侧剩90previousElement.style.width asideMainWidth px}} else {// 向右移动if (nextElement.clientWidth 400) {// 右侧剩90previousElement.style.width asideMainWidth px}}breakcase MAIN-ASIDE:let mainAsideWidth initWidth - moveLenif (moveLen 0) {// 向左移if (previousElement.clientWidth 400) {// 左侧剩90nextElement.style.width mainAsideWidth px}} else {// 向右移动if (mainAsideWidth 400) {nextElement.style.width mainAsideWidth px}}breakcase HEADER-MAIN: {let headerMainHeight initHeight moveHeightif (moveHeight 0) {// 向上移if (headerMainHeight 60) {// 上侧剩90previousElement.style.height headerMainHeight px}} else {// 向下移动if (nextElement.clientHeight 60) {// 下侧剩90previousElement.style.height headerMainHeight px}}break}case MAIN-HEADER: {let mainHeaderHeight initHeight - moveHeightif (moveHeight 0) {// 向上移if (previousElement.clientHeight 60) {// 左侧剩90nextElement.style.height mainHeaderHeight px}} else {// 向下移动if (mainHeaderHeight 60) {nextElement.style.height mainHeaderHeight px}}break}default:}}document.onmouseup (evt) {document.onmousemove nulldocument.onmouseup nullresize.releaseCapture resize.releaseCapture()}resize.setCapture resize.setCapture()return false}}return {setLayoutDrag}
}公共布局
此处的可以自己查看代码。 invoke调用rust函数关闭splash
import { invoke } from tauri-apps/api/taurionMounted(() {// window.addEventListener(contextmenu, (e) e.preventDefault(), false)document.addEventListener(DOMContentLoaded, () {// This will wait for the window to load, but you could// run this function on whatever trigger you wantsetTimeout(() {invoke(close_splashscreen)}, 1000)})
})结语 感兴趣的可以试试有不清楚的问题关于tauri开发方面的问题也可以一起交流。欢迎加我zhan_1337608148。一起成长一起进步。