JavaScript
JS
(JavaScript
)作为一门编程语言,对于非计算机专业人士而言,有着较高的学习成本。其中内容非常庞大,以一个简单教程的体量无法阐述清楚。因此本章与其说是教程,不如说是对自动记账中的JS脚本进行一个说明。如果完全不懂JS的用户而言,建议不要使用JS脚本规则,或者直接抄作业。我会在之后放出一个示例,包含有详细的注释,大家可以直接复制代码。
如果想要进一步学习JavaScript,更深层次的了解它,可以前往以下几个网站学习:
- W3School (opens new window)
- 极客学院 (opens new window)
- 菜鸟教程 (opens new window)
- C语言中文网 (opens new window)
- Bilibili知识区 (opens new window)
下面开始关于自动记账JS脚本的说明:
# 概述
在自动记账中,JS脚本主要用于自动分类。用于将账单自动划分类别,这一功能的实现是通过JS脚本,不论是可视化还是JS都是如此。可视化是由App自动生成一段JS代码,而JS模式则是由用户手动编写代码,本质上都是一样的。
在脚本中,所有的功能都是由函数实现,这里也不例外,一条规则就是一个函数,但这个函数的外壳function xxx() {...}
已经被定义好,并且内置在app中,用户只需要编写函数内部的内容。而在运行规则时,App会调用该函数,注入参数后取得该函数的返回值。
注意
因此,书写JS规则时必须要有一个返回值
,即return xxx
。
# 内置参数
在规则运行的时候,App会向其中注入一些参数,在书写规则时可以直接调用这些参数,而参数之外的其他变量,则需要自行定义和赋值。
表1 - 内置参数表(点击展开/收起)
参数 | 说明 |
---|---|
shopName | 商户名称 |
shopRemark | 商户备注 |
type | 账单类型 |
hour | 当前时间(小时) |
minute | 当前时间(分钟) |
money | 账单金额 |
# 内置函数
除内置参数以外,我们还提供了一个判定时间的内置函数isInTimeInner(minTime, maxTime,timeHour,timeMinute)
。minTime
起始时间(如12:00), maxTime
终止时间(如15:05),timeHour
当前时,timeMinute
当前分。
调用方法,例如:
isInTimeInner("12:00", "12:30",hour,minute);
或者这样:
isInTimeInner("23:00", "6:30",hour,minute);
isInTimeInner()
函数具体代码如下:
const isInTimeInner = function (minTime, maxTime,timeHour,timeMinute) {
let regT = /([01\b]\d|2[0-3]):([0-5]\d)/;//限制范围00:00=>23:59
const t1 = minTime.match(regT);
const t2 = maxTime.match(regT);
if(t1==null||t2==null||t1.length<3||t2.length<3){
return false;
}
const h1 = t1[1], h2 = t2[1], m1 = t1[2], m2 = t2[2];
if (h1 > h2)
return (timeHour === h1 && timeMinute >= m1) || timeHour > h1 || timeHour < h2 || timeHour === h2 && timeMinute <= m2;
else if (h1 < h2)
return (timeHour === h1 && timeMinute >= m1) || (timeHour > h1 && timeHour < h2) || timeHour === h2 && timeMinute <= m2;
else if (h1 === h2) {
if (m1 < m2)
return timeHour === h1 && timeMinute >= m1 && timeMinute <= m2;
else if (m1 > m2)
return (timeHour === h1 && timeMinute >= m1 || timeMinute <= m2) || (timeHour !== h1);
else
return m1 === m2 && timeMinute === m1 && timeHour === h1;
}
};
# 示例代码
包罗万象,全能规则:(作者:太公摘花)
var sRemark = shopName + shopRemark + source,//将商户名、商户备注、账单来源整合为一个变量
aRegs =//将收入和支出通用的放在外面
// 理财收益
[{"bkName":"余额(利)宝", "reg":/(余[额利]宝).*收益/},
{"bkName":"基金", "reg":/基金.*收益/},
{"bkName":"黄金", "reg":/黄金.*收益/},
{"bkName":"债券", "reg":/债券.*收益/},
{"bkName":"外汇", "reg":/外汇.*收益/},
{"bkName":"理财产品", "reg":/理财产品.*收益/}
];
switch (type) {
case "支出":
case "报销":
/*添加分类识别规则:这部分可以自行修改、添加或删除:基本格式为:{"bkName":"分类名","reg":"正则表达式"},注意:在中括号内添加。不懂正则表达式的不建议修改。*/
var aOutRegs=
/*三餐: 日常三餐的饮食,本分类将在后续分类中按时间进行拆分,分别输出为早餐、中餐、晚餐、夜宵。若不需要分开,后面会提供修改方法。*/
[{"bkName":"三餐", "reg": /[餐食饭炸烤蒸炒煨炖煮汤]|(粉|面|家菜)[店馆]|[包早]点|鲜包|(卤|螺蛳|牛肉|花甲)粉|(手工|拉|biangbiang|粉|刀削|牛肉)[面麵麺]|(家常|私房)菜|新芙蓉|沙县小吃|馄饨|饺子|麻辣|小吃|[鸡牛羊]排|肯德基|麦当劳|pizza|[披批比匹]萨|寿司|饿了么|美团订单|外卖/i},
/*日常: 日常生活的消费,包含有一些常见的关键词。可以自行修改。*/
{"bkName":"购物", "reg": /七件事|超市|便利|.*客隆|家.*福|快乐惠|世纪联华|联华超市|大润发|华润万家|苏果|沃尔玛|物美|新一佳|好又多|华联|文峰大世界|TESCO乐购|易初莲花|麦德龙|中百连锁仓储|人人乐|家家悦|潍坊百货|欧尚|永辉|武商量贩|新华都|步步高商业|永旺|红旗连锁|三江购物|互惠|美廉美|百佳超市|易买得|维客|美特好|保龙仓|华普|利客来|良友金伴|美宜佳|华冠|家润多|华之友|好家乡|惠友|民生家乐|思达|雅家乐|天惠|新江厦|喜洋洋|乐尔乐/},
{"bkName":"电器数码", "reg": /手机|电脑|电视|冰箱|洗衣机|空调|NOKIA|诺基亚|MOTO|摩托罗拉|Samsung|三星|索尼爱立信|Apple|苹果|Philips|飞利浦|lenovo|联想|LGSharp|夏普|多普达|金立|Amoi|夏新|Bird|波导|纽曼|Haier|海尔|TCL|HP|惠普|BenQ|明基|OPPO|Konka|康佳|CECT|HEDY|七喜|CoolPAD|酷派|ASUS|华硕|Mio宇达电通|琦基|ZTE|中兴|MEIZU|魅族|UT斯达康|BlackBerry|黑莓|Alcatel|阿尔卡特|大显|HKC|弘谷电|艾美讯|amsam|恒基伟业|Palm|RIM|OKWAP|英华达|天语|小米|华为|一加|联想|OPPO|vivo|诺基亚|华硕|惠普|三星|索尼|金士顿|闪迪|七彩虹|英特尔|Intel|AMD|松下|美的|格力/i},
{"bkName":"运动", "reg": /健身|运动|瑜珈|增肌|keep/i},
{"bkName":"房租", "reg": /公寓/},
{"bkName":"零食", "reg": /奶茶|零食|[饮甜]品|果汁|星巴克|桥头排骨|烧仙草|茶颜悦色|喜茶|奈雪茶|蜜雪冰城|CoCo都可|一点点|乐乐茶|古茗|沪上阿姨|快乐柠檬/i},
{"bkName":"快递", "reg": /[快速][递运]|百世汇通|物流|宅急送/},
{"bkName":"网购代付", "reg": /亲[情属]卡/},
{"bkName":"烟酒", "reg": /[烟酒]|芙蓉王|大中华|红双喜|中南海|玉溪|红河|利群|金圣|野山茶|五粮液|茅台|国窖1573|[天海梦]之蓝|水井坊|剑南春|四特|稻花香|二锅头|马尿|老白干|波尔多|拉菲|黑桃A|[干甜][红白]|香槟|白兰地|伏特加|威士忌|纯生|乌苏|百威|雪津|雪花|乌苏|麦之初|科罗娜/i},
/*定期缴费: 每月或每段时间相对固定的消费*/
{"bkName":"党费会费", "reg": /(会|党)费/},
{"bkName":"话费网费", "reg": /电信|联通|移动/},
{"bkName":"水电煤", "reg": /[电水]费|(燃|液化)气/},
{"bkName":"保险", "reg": /保[险单]|相互宝/},
/*形象工程: 穿着打扮等外在形象的消费。*/
{"bkName":"穿戴", "reg": /[衣裤鞋袜帽穿戴]|[男女]装|牛仔|耐克|nike|阿迪|361[度°]|鸿星尔克|特步|李宁|美特斯邦威|优衣库|以纯/i},
{"bkName":"理发", "reg": /发廊|美发/},
{"bkName":"护肤美妆", "reg": /洗面奶|面膜|卸妆[水棉油膏]|口红|防晒|(水|润肤|身体)乳|(眼|护手)霜|爽肤|精[华油]|[乳唇]膏|凝胶|祛痘|(果|水杨)酸|粉底|欧莱雅|施华蔻|雅诗.*兰黛/},
/*虚拟消费: 支持正版,虚拟内容付费。这里缺游戏类分类,主要因为微信提取到的账单备注无法支持识别。*/
{"bkName":"付费会员", "reg": /加速器|(1号|畅读|影城)卡|[^蚁]会员|QQ(美化|号)|vip|迅雷|爱奇艺|优酷|腾讯视频/i},
{"bkName":"购买App", "reg": /软件.*安装|(激活|注册)码|([高升]级|专业|付费|终[生身级]|增强|完整|永久)版|\bP(ro|lus)\b/i},
{"bkName":"游戏", "reg": /游戏|game|Steam|育碧|点券/i},
{"bkName":"小说漫画", "reg": /阅读|漫画|[看读追]书|小说|起点|多看/},
/*娱乐: 生活需要娱乐,电影、旅行、玩耍、泡吧,后续将完善分类。*/
{"bkName":"电影", "reg": /影[城院]|(电影|淘票)票|猫眼电影|中影|王府井|cgv|潇湘国际/i},
{"bkName":"旅行", "reg": /旅[游行社馆]|[青国]旅|酒店|宾馆|招待所|维也纳国际|凯宾斯基|华天|7天|如家/},
{"bkName":"打赏", "reg": /直播|虎牙|斗鱼|抖音/},
{"bkName":"聚会", "reg": /KTV/i},
/*非常规消费: 这些消费并不常有,而且金额、频率存在较大的不确定性*/
{"bkName":"医疗", "reg": /医(院|馆|.?室)|药[店房号]|诊所|卫生(服务(中心|站)|[院所室])/},
{"bkName":"学习", "reg": /打印|课程|教[材辅育程你]|讲[解义]|学会|入门到精通|慕课|学院|医视时代|医脉通|新东方在线|潭州课堂沪江小D/i},
{"bkName":"罚款", "reg": /交警|公安|法院/},
/*交通: 出行的交通费用,这里将最常用且可以通用的地铁和公交合并在一起,买车、养车和加油也算在了一起。*/
{"bkName":"公交/地铁", "reg": /雪球科技|地铁|公交/},
{"bkName":"打车", "reg": /滴滴|高德|出租车|的士|打的|代驾/},
{"bkName":"火车", "reg": /铁路|火车|高铁|列车/},
{"bkName":"飞机", "reg": /航[空班]|机票/},
{"bkName":"汽车/加油", "reg": /汽车|加油站|石[油化]|海油|通用|大众|五菱|依维柯|荣威|申沃|东风|雪铁龙|本田|霸龙|悦达起亚|风神|小康|一汽|解放|奥迪|丰田|红旗|马自达|夏利|佳宝|北汽|现代|福田|Jeep|奔驰|宝马|雷诺|特斯拉|法拉利|宾利|保时捷|兰博基尼|劳斯莱斯|欧曼|昌河|绅宝|威旺|欧辉|广汽|传祺|三菱|菲亚特|客车|吉奥|日野|长安商用|哈飞|陆风|标致/i},
/*育儿: 主要添加了母婴产品和童装品牌,如有其他可私信我提供关键词*/
{"bkName":"育儿", "reg": /童装|儿.*奶粉|纸尿裤|玩具|奥特曼|母婴|Balabala|巴拉巴拉|小猪班纳|安奈儿|BOBDOG|巴布豆|米妮·哈鲁|MiniZaru/i},
/*红包和转账: 各种转账和红包*/
{"bkName":"发红包", "reg":/(红包|转账)给别人/}
];
aRegs=aRegs.concat(aOutRegs);
aOutRegs = null;
break;
case "收入":
var aInRegs =//收入的分类规则:
[{"bkName":"工资", "reg":/(工资|津贴)/},
{"bkName":"收红包", "reg":/收.*(红包|转账)/}
];
aRegs=aRegs.concat(aInRegs);
aInRegs = null;
break;
default :
aRegs=[];
break;
}
/*
通过for循环语句,逐条验证规则。
*/
var sBook;//[不需要三餐分开的可以删除本行]定义一个sBook变量存放分类,以便对一些特殊分类进行进一步处理。
for (var i = 0 ,len = aRegs.length ; i<len; i++) {
if (aRegs[i].reg.test(sRemark))
/*不需要三餐按时间分开的,
请进行以下操作:
1、将这条注释之后的所有代码删除(必须)
2、删除上一条注释标注的代码(非必须)
3、删除后在这条注释后添加这行代码:(必须)
return aRegs[i].bkName;}
这样就可以结束了。*/
{
sBook = aRegs[i].bkName;
i=sRemark=aRegs=null;//回收变量,这些变量使用到此结束
break;
}
}
/*以下内容为根据时间将三餐分开*/
switch (sBook) { //通过sBook值选择操作,使用Switch语句性能比if语句高。
case "三餐": //将三餐单列:按时间分类,使用isInTimeInner()函数判定。
if (isInTimeInner("06:00", "09:39", hour, minute)) return "早餐";
else if (isInTimeInner("09:40", "15:44", hour, minute)) return "午餐";
else if (isInTimeInner("15:45", "21:29", hour, minute)) return "晚餐";
else if (isInTimeInner("21:30", "05:59", hour, minute)) return "夜宵";
break;
default :
return sBook;
}