用webview做界面,制作一个查找文件名的小工具

Mr_MAO 23天前 411

最近学习web.view。试了一下用web.view做程序界面,还是非常强大的,可以用很多前端的框架、插件等,灵活性很强,可以做出很炫的界面,这一点是winform界面编程很难比拟的。

下面代码是一个根据关键字查找文件名的小程序,不是很完善,功能也很简单,主要是想分享一下web.view做界面的过程,欢迎大家批评指正!

import win.ui;
/*DSG{{*/
var winform = win.form(text="web.view制作界面: 查找文件名的小工具 (by:Mr_Mao)";right=1183;bottom=751)
winform.add()
/*}}*/

import fsys;
import fsys.dlg.dir;
import process;
import fsys.info;

import web.view;
var wv = web.view(winform);
wv.enableDefaultContextMenus(false)

wv.external = {
    openDialog = function(){
        var folder = fsys.dlg.dir();
        return folder; 
    };

    isDir = function(str){
        if(fsys.isDir(string.trim(str))) return true; 
    };
    
    openFile = function(path){
        process.execute(path)
    };
    
    deleteFile =function(path){
        if(win.msgboxTest("你确定要删除这个文件吗?","请确认:",winform.hwnd)){
            var bool = io.remove(path)  //物理删除本地文件
            if(bool) {
                sleep(300)
                wv.invoke("showDialog","提示:","文件已删除.")
                return true; 
            }
        }
    }

     getIconBuffer = function(path){ 
         var sfi = fsys.info.get(path, 0x100/*_SHGFI_ICON*/);
         var handle = sfi.hIcon;
         var buffer = gdip.bitmap(handle,1).saveToBuffer(".png")
         return buffer;
    };

    searchFiles = function(folder,keyword,boolSubdir){
        //清空listTable
        wv.doScript("document.getElementById('resultList').replaceChildren()")
        
        //枚举文件夹中的所有文件
        thread.invoke(
            function(wv,folder,keyword,boolSubdir){
                import fsys;
                import fsys.file;
                import inet.url;
                var i = 0;
                fsys.enum( folder, "*.*", 
                    function(dir,filename,fullpath,findData){ 
                        if(filename){ 
                            if(string.find(filename,"@"++ string.lower(keyword))){
                                i++;
                                var file = fsys.file(fullpath);
                                var size = file.size64().format();
           						//插入到x-data中的数组内
                                wv.invoke("add2items", i,  filename,   fullpath, size) //add2items(id,name,path,size)
                            } 
                        }
                    } ,boolSubdir/*是否包括子目录*/
                );
                wv.doScript("updateTooltips()")
                sleep(300)
                wv.invoke("showDialog","查找结果:","找到 "++i++" 个相符的文件.")
            },wv,folder,keyword,boolSubdir
        )
    } ;
}

wv.html = /**
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/bulma/1.0.3/css/bulma.css">
        <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.5.1/css/all.min.css">
        <script defer src="https://cdn.bootcdn.net/ajax/libs/alpinejs/3.14.9/cdn.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
        <script src="https://cdn.bootcdn.net/ajax/libs/colresizable/1.6.0/colResizable-1.6.min.js"></script>
        <style type="text/css">	
            .table { table-layout: fixed; width: 100%;}

            th:nth-child(1) { width: 62px;}
            th:nth-child(2) { width: 20%;}
            th:nth-child(3) { width: 120px;}
            th:nth-child(4) { width: auto;}
            th:nth-child(5) { width: 120px;}

            .truncate {/* 截断长文本并显示 "..." */
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
            }
            
            .pagination-ellipsis {
            	pointer-events: none;
            }
        </style>
    </head>
    <body>
        <div class="container" x-data="app" id="container">
            <!-- form区域 -->
            <div class="section py-5">
                <div class="columns is-vcentered">
                    <div class="column is-narrow">
                        <label class="label">选择文件夹:</label>
                    </div>
                    <div class="column">
                        <div class="field has-addons">
                            <div class="control has-icons-left is-expanded">
                                <input class="input" type="text" id="inputDir" x-model="dir" placeholder="c:\">
                                <span class="icon is-small is-left">
                                    <i class="fa-solid fa-folder-open"></i>
                                </span>
                            </div>
                            <div class="control">
                                <button class="button is-warning"
                                    @click="(async()=>{dir=await aardio.openDialog()})();">...</button>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="columns is-vcentered">
                    <div class="column is-narrow">
                        <label class="label">搜索关键字:</label>
                    </div>
                    <div class="column">
                        <div class="field">
                            <p class="control has-icons-left is-expanded">
                                <input class="input" type="text" id="inputKeyword" placeholder="输入要搜索的文件名,不支持模式匹配"
                                    x-model="word">
                                <span class="icon is-small is-left">
                                    <i class="fa-solid fa-pen-to-square"></i>
                                </span>
                            </p>
                        </div>
                    </div>
                    <div class="column is-narrow">
                        <div class="field is-horizontal">
                            <div class="field-body">
                                <div class="field is-grouped">
                                    <div class="field is-narrow">
                                        <div class="control">
                                            <button class="button is-info px-5"
                                                @click.debounce="findStrInDir();">
                                                <span class="icon is-small">
                                                    <i :class="isSearching?'fas fa-spinner fa-pulse':'fa-solid fa-magnifying-glass'"></i>
                                                </span>
                                                <span>查找文件名</span>
                                            </button>
                                        </div>
                                    </div>
                                    <div class="field is-narrow is-flex is-align-items-center">
                                        <p class="control">
                                            <label class="checkbox"><input type="checkbox" x-model="bool" class='mr-1'/>包含子文件夹</label>
                                        </p>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
			
            <!-- table区域 -->
            <div class="table-container mx-3">
                <table class="table is-striped is-hoverable is-bordered" id="filestable">
                    <thead>
                        <tr class="has-background-success">
                            <th class="has-text-centered is-size-7-touch" style="vertical-align: middle;">序号</th>
                            <th class="has-text-centered is-size-7-touch" style="vertical-align: middle;">文件名</th>
                            <th class="has-text-centered is-size-7-touch" style="vertical-align: middle;">文件大小</th>
                            <th class="has-text-centered is-size-7-touch" style="vertical-align: middle;">文件路径</th>
                            <th class="has-text-centered is-size-7-touch" style="vertical-align: middle;">操作</th>
                        </tr>
                    </thead>
                    <tbody>
                        <!-- aardio往这里写数据 -->   
                        <template x-for="item in pageItems">
        				  <tr>
                             <th class="has-text-centered" style="vertical-align: middle;" x-text="item.id"></th>
                             <td class="truncate" style="vertical-align: middle;">
                                <figure class="image is-32x32 is-pulled-left"><img :src="showIcon(item.filepath)" /></figure>
                                <span class="ml-2" x-text="item.filename"></span>
                             </td>
                             <td class="has-text-centered" style="vertical-align: middle;" x-text="item.filesize"></td>
                             <td class="truncate" style="vertical-align: middle;" x-text="item.filepath"></td>
                             <td style="vertical-align: middle;">
                             	<div class="buttons are-small is-centered">
                                  <button class="button is-info px-2 py-1" @click="openItem(item.filepath)">打开</button>
                                  <button class="button is-danger px-2 py-1" @click="(async()=>{if(await aardio.deleteFile(item.filepath)==true)deleteItem(totalItems, item.id); })()">删除</button>
                                </div>
                             </td>
                          </tr>
    			</template>
                    </tbody>
                </table>
            </div>

            <!-- 分页器 -->
            <template x-if="totalPages>1"> 
				<nav class="pagination is-centered" role="navigation" aria-label="pagination">
				 <ul class="pagination-list">
                	<li><!-- 上一页按钮 -->
                    	<a class="pagination-previous" 
                    	:class="{ 'is-disabled': currentPage === 1 }"
                       	@click="changePage(currentPage - 1)"> 上页 </a>
                	</li>
                	<li>
                    	<a class="pagination-link" 
                       	:class="{ 'is-current': 1 === currentPage }"
                       	@click="changePage(1)">1</a>
                	</li>
                	<li x-show="currentPage > visiblePages + 1"><!-- 前省略号 -->
                    	<span class="pagination-ellipsis">&hellip;</span>
                	</li>
                	<template x-for="page in visiblePageNumbers" :key="page">
                    	<li x-show="page > 1 && page < totalPages">
                        	<a class="pagination-link" 
                           	:class="{ 'is-current': page === currentPage }"
                           	@click="changePage(page)"
                           	x-text="page"></a>
                    	</li>
                	</template>
                	<li x-show="currentPage < totalPages - visiblePages"><!-- 后省略号 -->
                    	<span class="pagination-ellipsis">&hellip;</span>
                	</li>
                	<li x-show="totalPages > 1">
                    	<a class="pagination-link" 
                       	:class="{ 'is-current': totalPages === currentPage }"
                       	@click="changePage(totalPages)"
                       	x-text="totalPages"></a>
                	</li>
                	<li><!-- 下一页按钮 -->
                    	<a class="pagination-next" 
                       	:class="{ 'is-disabled': currentPage === totalPages }"
                       	@click="changePage(currentPage + 1)">下页</a>
                	</li>
				</ul>
				</nav>
			</template>
			
            <!-- 模式对话框 -->
            <template x-teleport="body">
                <div class="modal" :class="isModalOpen?'is-active':''">
                    <div class="modal-content" style="max-width:50%">
                        <article class="message is-info">
                            <div class="message-header">
                                <p x-text="title">title</p>
                                <button class="delete" aria-label="delete" @click="isModalOpen=false;isSearching=false"></button>
                            </div>
                            <div class="message-body">
                                <div class="py-4" x-text="msg">contents</div>
                                <div class="field is-grouped is-grouped-centered mt-5">
                                    <button class="button is-primary mx-7" @click="isModalOpen=false;isSearching=false"
                                        @keyup.escape.window="$dispatch($el.click())">确 定</button>
                                </div>
                            </div>
                        </article>
                    </div>
                </div>
            </template>
        </div> <!-- div.container 结束 -->

        <script type="text/javascript">
            //alpine初始化
            document.addEventListener('alpine:init', function() {
                Alpine.data('app', () => ({
                    dir: '', word: '', bool: false,
                    isSearching: false,  isModalOpen: false,
                    title: '', msg: '',
					
					totalItems:[],
					currentPage: 1,
                	itemsPerPage: 10,
                	visiblePages: 2, // 当前页左右各显示多少页码
                	
                	get totalPages() {
                    	return Math.ceil(this.totalItems.length / this.itemsPerPage);
                	},
					
					get pageItems() {
                    	const start = (this.currentPage - 1) * this.itemsPerPage;
                    	const end = start + this.itemsPerPage;
                    	return this.totalItems.slice(start, end);
                	},
                
                 	changePage(page) {
                    	if (page >= 1 && page <= this.totalPages) {
                        	this.currentPage = page;
                    	}
                	},
                	
                	get visiblePageNumbers() {
                    	const start = Math.max(2, this.currentPage - this.visiblePages);
                    	const end = Math.min(this.totalPages - 1, this.currentPage + this.visiblePages);
                    	
                    	const pages = [];
                    	for (let i = start; i <= end; i++) {
                        	pages.push(i);
                    	}
                    	return pages;
                	},

                    findStrInDir(){
                        this.isSearching=false;
                        if(!this.dir){document.getElementById('inputDir').focus();return;};
                        if(!this.word){document.getElementById('inputKeyword').focus();return;};
                        (async()=>{
                        	const isTrueDir = await aardio.isDir(this.dir);
                        	if(!isTrueDir){document.getElementById('inputDir').focus();return;};
                        	this.isSearching=true;
                        	this.totalItems.length = 0;
                        	this.currentPage=1;
                        	aardio.searchFiles(this.dir,this.word,this.bool);
                        })(); 
                    },
                }))
            });
            
            //显示一个提示框
            function showDialog(t,m) {
                // 通过 DOM 元素获取 Alpine 组件实例
                const component = document.querySelector('#container');
                const alpineData = Alpine.$data(component);
                alpineData.title = t;
                alpineData.msg = m;
                alpineData.isModalOpen = true;
            }
            
            //添加符合条件的文件信息到数组中
            function add2items(num,name,path,size) {
            	const component = document.querySelector('#container');
                const alpineData = Alpine.$data(component);
            	alpineData.totalItems.push({id: num, filename: name, filesize: size, filepath: path})
            }
            
			//打开本地文件
			function openItem(path) {
				aardio.openFile(path) //
			}
			
			//删除本地文件
			function deleteItem(arr, idNum) {
				arr.splice(arr.findIndex(el => el.id === idNum), 1)
			}
			
            //显示图标
            async function showIcon(path) {
                var nativeByteArray = await aardio.getIconBuffer(path); 
                const uint8Array = new Uint8Array(nativeByteArray);
                const blob = new Blob([uint8Array], { type: 'image/png' });
                
                const imageUrl = URL.createObjectURL(blob);
                return imageUrl;
            } 

            $(function() {
                //让表格中的列可调整
                $("#filestable").colResizable();
            });

            //让td单元格鼠标悬停tip显示全名
            const updateTooltips = () => {
                document.querySelectorAll('td').forEach(td => {
                    const isOverflowing = td.scrollWidth > td.clientWidth;
                    td.title = isOverflowing ? td.textContent : '';
                });
            };
            window.addEventListener('resize', updateTooltips);
        </script>
    </body>
</html>
**/

wv.waitDoc()
winform.show();
win.loopMessage();


最新回复 (2)
  • 光庆 23天前
    0 2

    超赞的例程,感谢分享!

  • shzhbook 23天前
    0 3
    感谢大佬分享好例子,希望能分享更多实用的例子,收藏了!
返回