Awesome Chrome Form UI - 界面设计与实现

上苍不会让所有幸福集中到某个人身上,得到了爱情未必拥有金钱;拥有金钱未必得到快乐;得到快乐未必拥有健康;拥有健康未必一切都会如愿以偿。知足常乐的心态才是淬炼心智、净化心灵的最佳途径。一切快乐的享受都属于精神,这种快乐把忍受变为享受,是精神对于物质的胜利。这便是人生哲学。

——杨绛

一、Guide

Awesome Chrome Form UI - 框架设计与基础实现-CSDN博客文章浏览阅读817次,点赞26次,收藏21次。Awesome Chrome Form UI - 框架设计与基础实现https://blog.csdn.net/weixin_47560078/article/details/135182049在前面我们已经实现了最基础的框架功能,现在来补充完善这个框架的其他模块,

  1. 新增前端 UI
  2. 修改应用启动逻辑,新增抽象类和接口类
  3. 窗口无边框样式,圆边,阴影
  4. 窗口事件与 API
  5. FolderBrowserDialog 对话框
  6. JS 注入及其应用

 二、UI 框架

使用纯 HTML/JavaScript/CSS 技术构建 UI 框架,

1、整合 Vue + Vite + ElementUI + ESLint

# 创建 vite vue
cnpm create vite@latest

# element-plus 国内镜像 https://element-plus.gitee.io/zh-CN/
# 安装 element-plus
cnpm install element-plus --save
 
# 安装导入插件
cnpm install -D unplugin-vue-components unplugin-auto-import

# 使用 icon
cnpm install @element-plus/icons-vue

# 安装 eslint
cnpm i -D eslint @babel/eslint-parser
# 初始化配置
npx eslint --init
# 安装依赖
cnpm i @typescript-eslint/eslint-plugin@latest eslint-plugin-vue@latest @typescript-eslint/parser@latest
# 安装插件
cnpm i -D vite-plugin-eslint

配置 vite,

// vite.config.js
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'
import eslintPlugin from 'vite-plugin-eslint'
 
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    //  ESLint 插件配置
    eslintPlugin({
      include: ['src/**/*.js', 'src/**/*.vue', 'src/*.js', 'src/*.vue']
    }),
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

在 main.ts 引入 element-plus 样式,

// src\main.ts
import { createApp } from 'vue'
//import './style.css'
import App from './App.vue'
import 'element-plus/dist/index.css'
 
createApp(App).mount('#app')

配置 eslint 规则,

// .eslintrc.cjs
module.exports = {
    "env": {
        "browser": true,
        "es2021": true,
        "node": true
    },
    "extends": [
        "eslint:recommended",
        "plugin:@typescript-eslint/recommended",
        "plugin:vue/vue3-essential"
    ],
    "overrides": [
        {
            "env": {
                "node": true
            },
            "files": [
                ".eslintrc.{js,cjs}"
            ],
            "parserOptions": {
                "sourceType": "script"
            }
        }
    ],
    "parserOptions": {
        "ecmaVersion": "latest",
        "parser": "@typescript-eslint/parser",
        "sourceType": "module"
    },
    "plugins": [
        "@typescript-eslint",
        "vue"
    ],
    "rules": {
        "@typescript-eslint/no-explicit-any": 1,
        "no-console": 1,
        "no-debugger": 1,
        "no-undefined": 1,
    }
}

修改 vite 打包指令,

// package.json
 
// ......
 
"build": "vite build"
 
// ......

三、FolderBrowserDialog(补充)

1、原生 FolderBrowserDialog

ShowFolderBrowserDialog 使用原生 System.Windows.Forms 库,

using System.Windows.Forms;

namespace AwesomeChromeFormUI.Dialogs
{
    public class DefaultFolderBrowserDialog
    {
        /// <summary>
        /// 显示文件夹浏览器,返回选中的文件夹路径
        /// </summary>
        /// <returns></returns>
        public static string ShowFolderBrowserDialog()
        {
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog()
            {
                Description = "请选择文件夹",
                ShowNewFolderButton = true,
                SelectedPath = @"D:\MyCodeSpace\AwesomeChromeFormUI",
            };
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                return folderBrowserDialog.SelectedPath;
            }
            return "";
        }
    }
}

2、自定义 FolderBrowserDialog

引用 Ookii 库,这里使用的版本是 4.0.0,

Ookii.Dialogs.WinForms

ShowVistaFolderBrowserDialog 自定义实现,可以看到用法大致相同,

using Ookii.Dialogs.WinForms;
using System.Windows.Forms;

namespace AwesomeChromeFormUI.Dialogs
{
    public class DefaultFolderBrowserDialog
    {
        /// <summary>
        /// 显示文件夹浏览器,返回选中的文件夹路径
        /// </summary>
        /// <returns></returns>
        public static string ShowFolderBrowserDialog()
        {
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog()
            {
                Description = "请选择文件夹",
                ShowNewFolderButton = true,
                SelectedPath = @"D:\MyCodeSpace\AwesomeChromeFormUI",
            };
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                return folderBrowserDialog.SelectedPath;
            }
            return "";
        }

        /// <summary>
        /// 显示文件夹浏览器,返回选中的文件夹路径
        /// </summary>
        /// <returns></returns>
        public static string ShowVistaFolderBrowserDialog()
        {
            VistaFolderBrowserDialog folderDialog = new VistaFolderBrowserDialog
            {
                Description = "请选择文件夹",
                UseDescriptionForTitle = true,
                SelectedPath = @"D:\MyCodeSpace\AwesomeChromeFormUI",
            };

            if (folderDialog.ShowDialog() == DialogResult.OK)
            {
                return folderDialog.SelectedPath;
            }
            return "";
        }
    }
}

3、拓展 GetSelectedFolderPath 

        /// <summary>
        /// 获取文件夹路径
        /// </summary>
        /// <param name="form"></param>
        /// <param name="useDefaultDialog"></param>
        /// <returns></returns>
        public static Task<string> GetSelectedFolderPath(this Form form,bool useDefaultDialog=false)
        {
            // 使用TaskCompletionSource来创建一个未完成的任务
            TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();

            // 在 UI 线程上执行操作
            form.InvokeOnUiThreadIfRequired(() =>
            {
                // 执行回调方法,并获取结果
                string result = useDefaultDialog? DefaultFolderBrowserDialog.ShowFolderBrowserDialog():DefaultFolderBrowserDialog.ShowVistaFolderBrowserDialog();
                // 将结果设置到任务完成源,并标记任务为成功
                tcs.SetResult(result);
            });

            // 返回任务对象
            return tcs.Task;
        }

4、ApplicationBuilder 新增参数

JS 调用 Task<T> 返回值的方法必须开启 ConcurrentTaskExecution 配置,

CefSharpSettings.ConcurrentTaskExecution = true;

ApplicationBuilder 调整如下,

using AwesomeChromeFormUI.ChromiumForms;
using AwesomeChromeFormUI.Interfaces;
using AwesomeChromeFormUI.Interfaces.Implements;
using CefSharp;
using System.Windows.Forms;

namespace AwesomeChromeFormUI.Builder
{
    public class ApplicationBuilder
    {
        private bool _isConcurrentTaskExecution;

        public ApplicationBuilder()
        {
            this._isConcurrentTaskExecution = false;
        }

        public ApplicationBuilder(bool isConcurrentTaskExecution)
        {
            this._isConcurrentTaskExecution = isConcurrentTaskExecution;
        }

        /// <summary>
        /// 在一个 BaseForm 中运行应用
        /// </summary>
        public void Run() 
        {
            try
            {
                // We're going to manually call Cef.Shutdown below, this maybe required in some complex scenarios
                CefSharpSettings.ShutdownOnExit = false;

                // 允许 JS 调用 Task<T> 类型返回值的方法
                CefSharpSettings.ConcurrentTaskExecution = this._isConcurrentTaskExecution;

                // 初始化 CEF
                IConfigurationExecuter executer = new CefConfigurationExecuter();
                executer.Execute();

                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);

                Application.Run(BaseForm.Instance);
           

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/371592.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言实现memcpy、memmove库函数

目录 引言一、库函数介绍二、库函数详解三、源码实现1.memcpy源码实现2.memmove源码实现 四、测试1.memcpy函数2.memmove函数 五、源码1.memcpy源码2.memmove源码 六、参考文献 引言 关于memcpy和memmove这两个函数&#xff0c;不论是算法竞赛还是找工作面试笔试&#xff0c;对…

STM32单片机的基本原理与应用(六)

串口测试实验 基本原理 在串口实验中&#xff0c;是通过mini_USB线搭建终端与电脑端&#xff08;也可称终端&#xff0c;为做区分称电脑端&#xff09;的“桥梁”&#xff0c;电脑端的串口调试助手通过mini_USB线向终端发送信息&#xff0c;由CH340芯片将USB接口进行转换&…

DelayQueue的使用

具体思路&#xff1a; 在容器初始化的时候就创建出一个 延迟队列 然后项目启动后随即启动一个线程一直监听这个队列 手动调用接口往队列中添加任务 依赖 一个最简单的web的应用即可项目文件结构 第一步&#xff1a;在项目启动的时候就创建出一个延迟队列 Configuration publ…

数据结构 - 线段树

1. 预制值&#xff1a; 构建的数组为&#xff0c;nums&#xff1a;【2&#xff0c; 5&#xff0c; 1&#xff0c; 4&#xff0c; 3】区间和问题&#xff0c;假设求区间 [1&#xff0c;3] 的和 2. 建树 2.1 构建线段树数组 int[] segT new int[4*n]&#xff08;为什么数组大…

理解进程的一些知识准备

1. 认识冯诺依曼体系结构 计算机有很多的体系结构&#xff0c;但到如今&#xff0c;冯诺依曼体系结构变成了主流。 输入设备&#xff1a;话筒、键盘、摄像头、鼠标、磁盘、网卡… 输出设备&#xff1a;声卡、显示器、打印机、显卡、网卡、磁盘… 有的设备既能作为输入设备又能…

机器学习的整个流程

机器学习的整个流程定义了数据科学团队执行以创建和交付机器学习模型的工作流。此外&#xff0c;机器学习流程还定义了团队如何协作合作&#xff0c;以创建最有用的预测模型。 机器学习high level的流程 机器学习流程的关键步骤包括问题探索&#xff08;Problem Exploration&a…

力扣题目训练(7)

2024年1月31日力扣题目训练 2024年1月31日力扣题目训练387. 字符串中的第一个唯一字符389. 找不同401. 二进制手表109. 有序链表转换二叉搜索树114. 二叉树展开为链表52. N 皇后 II 2024年1月31日力扣题目训练 2024年1月31日第七天编程训练&#xff0c;今天主要是进行一些题训…

2024杭州国际安防展览会:引领数字城市安全与智能未来

随着科技的不断进步&#xff0c;数字城市已经成为未来城市发展的重要趋势。作为数字城市建设的重要组成部分&#xff0c;安防技术的创新与应用对于保障城市安全、提高生活品质具有重要意义。为此&#xff0c;2024杭州国际安防展览会将于4月份在杭州国际博览中心隆重召开&#x…

DFS——连通性和搜索顺序

dfs的搜索是基于栈&#xff0c;但一般可以用用递归实现&#xff0c;实际上用的是系统栈。有内部搜索和外部搜索两种&#xff0c;内部搜索是在图的内部&#xff0c;内部搜索一般基于连通性&#xff0c;从一个点转移到另一个点&#xff0c;或者判断是否连通之类的问题&#xff0c…

react将选中本文自动滑动到容器可视区域内

// 自动滚动到可视区域内useEffect(() > {const target ref;const wrapper wrapperRef?.current;if (target && wrapperRef) {const rect target.getBoundingClientRect();const wrapperRect wrapper.getBoundingClientRect();const isVisible rect.bottom &l…

如何选择日本大带宽服务器?

随着互联网的高速发展&#xff0c;对于大带宽服务器的需求也日益增长。而在日本&#xff0c;由于其先进的网络基础设施和数据中心技术&#xff0c;大带宽服务器成为了许多企业和开发者的首选。那么&#xff0c;如何选择合适的日本大带宽服务器呢? 首先&#xff0c;了解自己的需…

linux☞ Centos 基础篇

切换用户 重启系统、退出 su 用户 ### su switch user 重启系统 reboot 退出当前账户 logout 或者 exit 或者 CtrlD 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet&#xff1a;指明网卡类型为以太网 DEVICEens33&#xff1a;指定当前配置的…

c++类和对象(二)

类与对象 一.类的6个默认成员函数1.1类的6个默认成员函数 二.构造函数2.1.1构造函数的概念2.1.2构造函数的特性 三.析构函数3.1.1概念3.1.2特点 四.拷贝函数4.1.1概念4.1.2特征 一.类的6个默认成员函数 1.1类的6个默认成员函数 在C中&#xff0c;如果在一个类中什么成员都没有…

docker maven插件使用介绍

1、配置docker连接 开放docker tcp连接参考本专栏下令一篇文章 2、docker service窗口 3、根据dockerfile 构建镜像 注意 idea 用通过管理员身份启动&#xff0c;否则连不上docker 构建前添加maven goal 打包 4、运行镜像 创建容器 5、运行docker compose 报错 需要先配置d…

Java并发之synchronized详解

☆* o(≧▽≦)o *☆嗨~我是小奥&#x1f379; &#x1f4c4;&#x1f4c4;&#x1f4c4;个人博客&#xff1a;小奥的博客 &#x1f4c4;&#x1f4c4;&#x1f4c4;CSDN&#xff1a;个人CSDN &#x1f4d9;&#x1f4d9;&#x1f4d9;Github&#xff1a;传送门 &#x1f4c5;&a…

QtAV学习:(一)Windows下编译QtAV

QtAV 主页&#xff1a; QtAV by wang-bin 作者的编译构建说明文档&#xff1a; Build QtAV wang-bin/QtAV Wiki GitHub 我的编译环境&#xff1a; 编译环境&#xff1a;win10/msvc2015/Qt5.6.3 第一步&#xff1a;GitHub拉取代码,执行子模块初始化 地址&#xff1a; …

web前端-------弹性盒子(2)

上一讲我们谈的是盒子的容器实行&#xff0c;今天我们来聊一聊弹性盒子的项目属性&#xff1b; *******************&#xff08;1&#xff09;顺序属性 order属性&#xff0c;用于定义容器中项目的出现顺序。 顺序属性值&#xff0c;为整数&#xff0c;可以为负数&#xff…

数仓建设规范

目录 前言 一、数据模型设计规范 1.1 数仓分层原则 1.2 主题域划分原则 1.3 数据模型设计原则 1.4 数据模型管理的目标 1.5 数仓建模的方法 1.5.1 维度建模 1.5.2 三范式建模 1.5.3 三范式与维度建模区别 二、数仓公共开发规范 2.1 层次调用规范 2.2 数据类型规范…

redis(4)

文章目录 一、redis主从复制redis 主从复制架构主从复制实现命令行配置同步日志修改slave节点配置文件 主从复制故障恢复主从复制故障恢复过程介绍主从复制故障恢复实现 实现redis的级联复制主从复制优化主从复制过程主从同步优化配置 常见主从复制故障汇总master密码不对Redis…

C系列-柔性数组

&#x1f308;个人主页: 会编程的果子君 ​&#x1f4ab;个人格言:“成为自己未来的主人~” 目录 ​编辑 柔性数组 柔性数组的特点 柔性数组的使用 柔性数组的优势 柔性数组 也许你从来没有听说过柔性数组这个概念&#xff0c;但是它确实是存在的&#xff0c;C99中&#…