js制定一个单选按钮_【下】每个月整理发票太头疼?手把手教你快速开发一个工具解决!...

bd9ff65fbfa2de7fb82293ec74ada99f.png"NightTeam",一个值得加星标ef9945d896699ce51c574cb9a13d9f89.png的公众号。

在上篇中,我们已经将我们的发票管理工具开发到了能一键导入发票、能看到效果、能仍然不太方便地管理的状态,接下来我们来继续将还没有加进去的那些方便的功能给加上,以将管理发票的方便性提升到一个更高的程度。


先回顾一下上篇的最终状态:

33a9a12a93bbfbd02ba3a6ccd696b2e5.png

现在我们再来给这个页面加上两个非常重要的功能,一个是设定发票的分类,一个是勾选发票后自动计算已选发票的总金额,这能大幅提高我们处理发票时的效率。

设定发票分类的话,Django Admin有个功能叫action,可以用于在admin页面上添加一个“快捷动作”,我们可以用它来实现。用法很简单,只需要写一个符合Django参数要求的函数并将它扔进一个列表中,再把这个列表设置给admin类的actions配置项即可。

这个有个小问题,action没有办法便捷地处理需要携带参数的情况,官方给出的解决方案是单独写一个中间页,在中间页中进行处理,但我们这种参数可选项不多的情况,如果单独写一个中间页的话好像有点麻烦?而如果复制粘贴直接写一堆函数的话好像又太长了,改起来麻烦、代码也很冗余?

没关系,我们可以用个骚操作来比较方便地解决,毕竟这只是个用来管理发票的工具而已,稍微用用骚操作也不是什么大问题。Python作为一个动态语言,可以很轻松地做到动态创建函数这种骚操作,只需要像这样就可以在代码运行时动态地创建一个函数了:

func = FunctionType(compile(    (f"def replace_category_to_{category}(modeladmin, request, queryset):\n"     f"    queryset.update(category='{category}')\n"     f"    return "),    "",    "exec").co_consts[0], globals())

然后我们就可以基于这个骚操作,把actions自动地创建出来,像这样:

992f99061e88cf92ef15ce8b0854147a.png

注:这里面的short_description是用来设置它在页面上显示的名称的,如果没有设置的话就会是函数名。

现在我们再刷新页面,就能得到这样的几个按钮了:

2b2f811a640edd12bbcad6c102bc3406.png

注:按钮是simpleui库带来的效果,原始的Django Admin会展示成一个下拉框和一个执行按钮。

接着是勾选发票后自动计算已选发票的总金额,这个要做的话也很简单,我们只需要用JS直接监听复选框勾选后对页面DOM的修改即可。

打开浏览器开发者工具定位到复选框附近的元素并操作几下复选框就会发现:全选时id为action-toggle的那个元素会被修改、单选时class为action-select的那个元素会被修改。所以我们可以基于这两个变化,写一段这样的JS代码:

$("#action-toggle,input[class='action-select']").bind("change", function (e){    let sum_price = 0;    $("#result_list > tbody > tr[class*='selected'] > td[class='field-price']").each(function(){        sum_price += parseFloat($(this).text());    })    console.log(sum_price)})

这段代码用了一个叫jQuery的库,在前端领域里很常见,代码的意思是先对那两个元素设置个监听器,一旦change事件发生时就运行后面那个函数里的代码;后面那个函数里的代码其实就是取出每一个被选中项价格部分的值,转成float之后往sum_price变量里加。

我们可以在浏览器开发者工具的Console中进行测试,结果大致如下,每次点击时都会自动把当前选中的发票计算个总和:

136bb30a44373aae4205e91c09fe5afa.png

那么我们需要怎么把这段JS代码放进页面里呢?只需要用Django的模板功能就好了。

注:由于我们用了simpleui库,所以有些被simpleui修改过的页面需要以simpleui的页面为基准进行操作,具体请参考simpleui的文档和对应的模板文件源码。

我们直接从admin.py导入django.contrib.admin的地方按鼠标中键点进去,然后在Pycharm上方的路径栏中找到admin目录下的templates/admin目录,在这里我们能看到一堆的.html文件。

接着,我们在浏览器中通过浏览器开发者工具随便选中一个列表中的发票的任意一个DOM元素,并挑一个特征出来(比如前面提到的ID),再用Pycharm的“在文件中查找”功能搜索,就可以找到页面中对应的那部分HTML了。

当然,也可以直接在Django的文档中查看admin的模板部分,里面也有说明每个.html文件分别对应哪个部分。

我这里是想把合计的金额展示在按钮那一排,所以最终找到的是actions.html这个文件。

找到对应的模板文件后,我们在manager目录下新建一个templates目录,然后再在这里面新建一个admin目录,最后再在admin目录里面创建一个manager目录,并新建与前面那个模板文件同名的文件。这么做的目的是为了限定这个修改的适用范围,避免后续我们需要弄别的admin页面或将这个manager应用集成到其他Django项目中时,把修改也带到其他admin页面上。

然后我们可以通过Django的模板语法中的include标签来引用原始的actions.html,接着我们在后面写的HTML就会直接在加载页面时被添加到原始模板文件的最后面了。

现在需要的就是写HTML了,我们加个span标签,给它设置个ID和喜欢的样式;

再加个script标签,把前面写好的JS代码放进去,并添加上一行用于修改那个span的text值的代码(比如$("#sum_price_val").text("共计:" + sum_price + "元"));

噢对了,由于这个模板文件的所处位置实际上是在列表页的开头部分,所以其中的JS代码会在列表中的内容加载完成前就执行,所以我们还需要让这段代码能在页面完全加载后才执行,这里可以使用jQuery库的$(document).ready来实现。

最后我们这个actions.html的内容差不多长这样:

c84f78713a305f401bf44a50bc327e64.png

然后就可以得到这样的效果:

3f675f747b8cb12597954e6d1e5fc4b0.png

再回顾一下上篇开头定下的需求列表,功能其实已经完成得差不多了,发票自动识别、分类、总额计算、去重都已经完成,现在剩下的主要功能就是批量打印和导出发票了。

批量打印的话需要先对PDF进行合并,将多个发票PDF合并到一张,这样我们就可以只调用一次打印机但打印多张发票了。

写起来也很简单,直接用一个名为PyPDF4的库来实现即可,使用它只需要像这样就能快速实现合并一个目录下所有发票的效果:

from pathlib import Pathimport PyPDF4 as pdfinvoices_path = Path("./invoices")merger = pdf.merger.PdfFileMerger()for path in invoices_path.iterdir():    if path.suffix != ".pdf":        continue    merger.append(path.open("rb"))merger.write("output.pdf")

导出发票的话就直接用Python自带的zipfile就好了,也很简单,这里就不演示了。

那么这两个功能在admin页面里应该如何实现呢?还是一样,添加一个action即可,只不过这里我们需要让action被触发时先跳到一个中间页面,然后中间页面中通过JS来请求两个接口分别做到返回合并后的PDF文件以及返回打包好的压缩包,接着再自己跳回原页面。

具体实现起来就像这样:

首先,我们先弄这个中间页面,这个中间页面我们可以用到Django的模板渲染功能,这样我们就能方便地将被选中的发票ID传给JS,让JS再来进行后续的步骤。

和前面弄分类设置一样,在manager/templates目录下直接新建一个HTML文件,这里我新建的文件名为use_invoices.html

395ce7ec78ef84a6f82193b9bed5139c.png

在模板文件中,我们可以通过{{ invoice_ids }}这样的语法来表示这里需要填充进一个值作为模板中的文本,效果类似于Python的format;还可以通过{% url "manager:merge_invoices" %}这样的语法来表示这里需要填充进这个Django项目下manager应用中名为merge_invoices的View(视图/页面)的URL。

然后我们可以通过用JS的window.open来打开一个新页面(标签)、window.location.href来设置当前页(标签)的URL并刷新、document.referrer来取到上一页的URL。

所以最终这个模板文件的代码会像这样:

cba02d4c5a59469c18098e41c5bca99c.png

这样我们就可以在action函数中调用模板渲染器渲染这个模板页面,并传入选中的发票ID以将发票ID传给JS了。

JS中会把ID赋值给params参数,并做个URL中请求参数部分的拼接,接着分别打开两个页面,再将当前页跳回上一页并刷新。这样我们就可以做到触发action之后自动打开两个新页面并刷新发票列表页的效果了。

然后我们回到admin.py,导入render函数from django.shortcuts import render用来渲染模板。接着再和前面一样,写一个函数用来做action具体的事务。

action函数的queryset参数是可以用来取到被选中对象以及其具体属性的,只需要调用values_list方法并传入属性名就可以直接取到属性列表了,这里我们像这样取出所有被选中的发票的id:queryset.values_list('id', flat=True)

注:flat参数表示返回的结果是单个值,而不是一个元组,设为True之后我们处理起id这种每个对象只有一个值的属性时就会方便些。

所以最终这个action函数会像这样 :

3422433a68d0c834f0cb18b630d17407.png

先取到被选中发票对象的ID列表,然后再用Django的ORM一一取到它们的对象实例,然后将used修改为True以标识已使用,并调用save函数保存到数据库中;接着通过str.join来拼接成一个1,2,3这样的字符串,再传给render函数作为invoice_ids参数对前面写好的模板文件进行渲染。

注:还是和之前的action一样,可以通过use_invoices.short_description = "使用这些发票"这样的操作来为这个action命名,在网页上看起来会舒服一些。

写好action函数后记得将它加到Admin类下的actions列表中,接下来我们需要写一下前面在JS中定下的那两个接口的View。

View是Django中的视图的概念,它能接收HTTP请求并且将return的内容作为响应返回,只需要创建一个HttpResponse(或其他Response,具体参考Django文档)实例填好对应的参数并return就可以了,搞不明白的话可以简单理解为一个View就是一个页面、返回的Response就是页面的内容。

打开manager/views.py定义两个对应名字的函数,并给它们加入名为request的参数,就创建好了两个View。

在View中,我们可以通过request.GET["ids"]来取到URL中名为ids的参数,然后接下来就是对这些发票进行处理了。

先写合并发票PDF的View,根据前面所说的方法取到id后调用PyPDF4库进行处理即可,差不多像这样:

3bbcd61c0ca0ab8aabfa35da3093b3f9.png

这里我把之前写的那个导入发票的脚本中用到的INVOICES_PATH移到了invoice_manager/settings.py中,以避免无意义的重复代码;

然后用了io.BytesIO创建了一个虚拟的IO对象,这个IO对象就类似于open(file)后得到的那个一样,只不过它的内容是存在内存中的,这样可以避免不必要的磁盘读写;

接着就是取了id之后用str.split转成了列表,并拼接出对应的发票文件路径,再调用PyPDF4库进行合并;

最后,创建一个HttpResponse,把IO对象中的内容(bytes)放进去,并设置响应头中的content_type为PDF文件对应的那个,这样可以让浏览器打开后直接显示出一个预览页面,而不是作为普通的文件被自动下载。

接着写打包发票的View,和前面这个差不多,只不过是换成了用zipfile库打包,并且把content_type改成了ZIP文件对应的,然后设置了一下返回的文件名以便浏览器在自动下载、保存时能保存成正确的名字。代码差不多像这样:

0f1f616d7e0973275a2f2d44555ebd08.png

那么现在我们两个接口的View就都写好了,只需要再做两个简单的配置就可以让它跑起来看效果了。

我们在manager目录下新建一个名为url.py的文件,然后在里面像这样配置一下urlpatterns

10178b6ea45783361f1244d17ddf5b5f.png

上面导入的东西就不用多说了吧?大同小异,直接看下面给path函数提供的参数。

第一个参数的意思是这个View在URL中的路径,这里写的是manager应用下的相对路径,Django会按照层级,依次将每个应用中定义的path一级一级拼接下去;

第二个参数就是这个View的函数了,没什么好说的;

第三个参数是这个View的名字,这个需要与前面在模板文件中使用的那个名字对应上,否则Django会因为找不到对应的View而报错。

接着打开invoice_manager/url.py,在urlpatterns里加上引用manager应用的URL配置,像这样:

b47108ba6f48379345f64d8390064f32.png

注:这里的第一个参数也是相对路径,如果这是最上一级的路径的话,那么最后拼接出来的就是像manager/merge_invoices这样的路径;而如果上面还有层级的话,最后拼接出来的就会是balabala/.../manager/merge_invoices这样的。

现在可以跑起来看一下了,如果不出意外的话你会看到这样的效果:

f7d6001851d6127be53522cf2205556e.png

点击“使用这些发票”按钮后,会瞬间打开两个页面,一个开始了自动下载,另一个则是发票PDF的预览:

cffa67ccec02c74fb8b2756cedc551e6.png
1c47c9a6634c5e28f56232826ae20ad1.png
b665dca398573bed6bb3da239755cc28.png

在点击预览页右上角的打印机按钮后,就会弹出打印设置了,直接选择打印机打印,就可以一次性打出多张发票了。

而那个压缩包,打开后也可以看到,里面是整整齐齐的发票PDF文件,都是按照指定规范命名好的,直接整个压缩包扔给财务就好了,很方便。

235b4e76dd7e6ee8e689fb3a3acd8d40.png

那么最后剩下的抬头和税号检测和自动排除已使用的发票这两个功能,我就不再废话了。毕竟前者直接在导入发票的脚本中判断一下即可,后者则是在选择发票时直接通过Django Admin的筛选功能来筛选未使用发票就可以了,非常简单。如果你不会做的话,可以直接参考我放在GitHub上的源码。


发送消息「发票管理工具」到公众号「NightTeam」即可获取项目开源地址,欢迎提出自己的需求或自行开发自己所需的功能并合并进来,让我们一起将这个工具变得更方便吧~

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ai怎么让图片任意变形_想一键提取图片文字,有什么好的文字识别软件/APP推荐吗?...

工作也有一段时间了,有时候需要把图片中的文字提取出来,转换成电子档式,一开始的时候可能是傻吧,也没想到借用工作减少工作量,就知道埋头拼命敲敲打打,文字比较少的时候还好,多了简直...得颈椎病…

隐藏功能_IOS 14.2 隐藏功能

FaceTime隐藏功能现身,只要设备运行 iOS 14.2 系统,iPhone 8 或更新的机型都可以以 1080p 分辨率进行视频通话,1080p什么概念,平时看视频应该最有感触。FaceTime 是什么?可能有些小伙伴压根儿就没听说过这个功能。Face…

SSH框架(Struts+Spring+Hibernate)

在SSH 的组合框架模式中,三者各自的作用? Struts 是一个很好的MVC框架,主要技术是Servlet和Jsp。Struts的MVC设计模式可以使我们的逻辑变得很清晰,让我们写的程序层次分明。基于Struts开发可以简化开发难度,提高开发效…

d630无电池升级bios_太重要,你想要的电脑BIOS全面解读与设置(下)

设置意外断电后恢复状态通常在电脑意外断电后,需要重新启动电脑,但在 BIOS 中可以对断电恢 复进行设置,一旦电源恢复,电脑将自动启动。下面就在 UEFIBIOS 中设置电 脑的自动断电后重启,具体操作步骤如下。 STEP 1 选择…

MySQL的乱码解决方案

1.如果第一次安装MySQL数据库: 在选择编码时,选择最后一项,并指定编码为utf8或者gbk(这两种编码支持中文,是我们比较常用的) 如果已经按照完成了MySQL数据库,那么可以进行重新配置修改: 在开始菜单中找到: 然后重复第…

pg数据库开启远程连接_疫情之下,开启在家办公模式,远程连接工具篇之向日葵...

1月30号本来就要返程去上班了,接到公司通知,根据当前疫情的形势,假期延长到3号,退车票,候补抢票一顿操作,将票改到了3号,3号又接到通知假期延长到10号。作为一个一线的销售人员,工作…

谈谈对MVC的理解(View+Model+Controller)

1) 什么是MVC? MVC是一种设计思想,根据职责不同将程序中的组件分成以下3个部分。 V(View视图):负责与用户交互。将数据展现,或者是接收数据 M(Model模型):负责业务处理。…

物理搬砖问题_全职业通用,搬砖市场装备

更多原创文章可关注微信公众号:地下城勤帝 查看大家好,我是勤帝,我只写大家能看懂的文章搬砖市场的角色更新了一些,装备都有所不同,今天我给大家一个可以通用的装备,这套装备虽然成型不快,但是…

快手通过标签添加的我_快手怎么上热门?快手短视频推荐指标有哪些?

“快手怎么才能上热门?快手上热门都有哪些技巧?”其实这些问题,除了通过提高短视频质量,还可以通过分析推荐指数的方式解决。运营者想要有效提升短视频的播放量,想让快手短视频快速上热门,还可以从推荐指数…

python机器学习算法.mobi_机器学习之ID3算法详解及python代码实现

在生活中我们经常会用到决策树算法,最简单的就是二叉树了;相信大家也会又同样的困扰,手机经常收到各种短信,其中不乏很多垃圾短信、此时只要设置这类短信为垃圾短信手机就会自动进行屏蔽、减少被骚扰的次数,同时正常短…

java中的4种访问制权限有哪些?分别作用范围是什么?

(1).public:最大访问控制权限,对所有的类都可见。 (2).protect:修饰的,在类内部、同一个包、子类中能访问 (3).default:包访问权限,即同一个包中的类可以可见。默认不显式指定访问控制权限时就是default包…

打docker镜像_从安全到镜像流水线,Docker 最佳实践与反模式一览

作者 | Timothy Mugayi译者 | 弯月,责编 | 夕颜封图 | CSDN付费下载自视觉中国出品 | CSDN(ID:CSDNnews)在使用Docker的大部分时间里,我们并不关心其内部的工作原理。仅凭启动一个Docker容器并且让应用程序运行良好,并不能说明你已经实现了一…

详细关闭iiop方法_疏通暖气片堵塞的方法,看完你就知道了!

冬季几乎家家户户都会安装暖气片来进行采暖,但在使用过程中,通常会出现暖气片被堵,用户不知道被堵的原因又不知从哪下手,今天金旗舰旗哥带大家来了解一下暖气片被堵塞的原因及疏通方法。一、堵塞暖气片的原因:1、暖气片…

谈谈对集合框架的理解?

集合框架包括集合不映射(Collection and Map) List 元素有先后次序的集合, 元素有 index 位置, 元素可以重复,继承自 Collection 接口,实现类: ArrayList, Vector, LinkedList List 表示有先后次序的对象集合 ArrayList是使用变长数组算法…

17 软件源_9成职场人支持“准点下班”,2020年度职场报告:工作是最大焦虑源

如果所有人都拒绝996,能否改变职场内卷生态?职场社交平台脉脉站内的数据显示,在一则:“准点下班VS加班谁才是好员工”的问答中,8.1万的投票者选择“准点”下班,占89%;只有9968名投票者认为“加班…

js如何在当前页面加载springmvc返回的页面_手写SpringMVC学习

前面我们学习了spring框架源码,做了一些自己手写的学习,最近,我们开始学习springMVC框架的学习 ,springMVC框架,相信大家不陌生了,所以这里不做过多的介绍了。SpringMVC以DispatcherServlet为核心&#xff…

用python做简单的地理聚类分析案例_用Python做一个简单的翻译工具

编程本身是跟年龄无关的一件事,不论你现在是十四五岁,还是四五十岁,如果你热爱它,并且愿意持续投入其中,必定会有所收获。本文就来自编程教室一位“小”读者的投稿(互助学习1群里的同学应该对作者的名字很熟…

echarts柱图根据值显示不同颜色_视频 | Origin画3D柱图,这篇讲透了!

视频教程东华大学的汪博士提出一个问题:怎样画三维柱状图。汪博士提供了一篇王中林院士的文献图,画一个只有四根柱子的三维柱图。画了一个草图,A0、A1安排在第二行,A3、A2安排在第一行。相信很多同学在画3D柱图时,都很…

按钮点击打开新页面_PDF怎么打开?如何制作一个PDF格式的文档?

不知你是否也一样,无论是在网上下载资料还是其他人发送的文件都是PDF格式的。但是应该如何打开PDF文件呢?如何自己制作一个PDF格式的文档呢?首先说一下如何打开PDF格式的文件,电脑端就比较简单的,直接下载PDF阅读器后&…

android 根据bounds坐标进行点击操作_炫酷的Android时钟UI控件,隔壁产品都馋哭了...

废话不多说,先上效果效果酷炫,动画丰富,效果爆炸boom~设计思路看腻了市面上各种丑陋难看的时钟控件,是时候整点新活!将现实生活中的摆钟圆形表盘设计、电子手表的数显表盘设计抽象出来,提取出“…