├── .gitignore ├── LICENSE ├── P4_MoneyArrangeModule ├── __init__.py ├── asgi.py ├── e__money_arrange_function │ ├── __init__.py │ ├── e_add_money_record.py │ ├── e_add_money_transfer_record.py │ ├── e_delete_money_record.py │ ├── e_delete_money_transfer_record.py │ ├── e_get_auto_complete_rules.py │ ├── e_get_money_positions.py │ ├── e_get_money_record.py │ ├── e_get_money_record_form.py │ ├── e_modify_money_record.py │ ├── e_modify_money_transfer_record.py │ ├── e_money_get_chart01_data.py │ ├── e_money_get_chart02_data.py │ ├── e_money_get_chart03_data.py │ ├── e_money_get_chart_history01_data.py │ ├── e_search_money_record_form.py │ ├── e_upload_bill.py │ ├── e_upload_db.py │ └── e_upload_money_records.py ├── middlewares │ ├── __init__.py │ └── login_required.py ├── settings.py ├── theme_package │ ├── __init__.py │ └── common_theme.py ├── urls.py ├── views.py ├── wsgi.py └── y__common_function │ ├── __init__.py │ ├── y_get_select_list.py │ └── y_login_view.py ├── README.assets ├── image-20231107143850217.png ├── image-20231109183716907.png └── image-20231222104937335.png ├── README.md ├── data.db ├── db.sqlite3 ├── install_process ├── Firefox-latest.exe ├── SQLiteStudio-3.4.4-windows-x64-installer.exe ├── installPackages.bat ├── python-3.8.7-amd64.exe └── requirements.txt ├── manage.py ├── runProject.vbs ├── runServer.bat ├── runServer.vbs ├── showPages.bat ├── showPages.vbs ├── static ├── custome_css │ ├── layer_style.css │ ├── layui_form_style.css │ └── layui_table_style.css ├── demo │ ├── alipay_record_20231220_163331_示例.csv │ └── 微信支付账单(20231204-20231220)_示例.csv ├── images │ ├── favicon.ico │ └── login_bg.jpg ├── js │ ├── echarts.min.js │ └── jquery-3.6.0.min.js └── plugins │ ├── bootstrap-5.0.2-dist │ ├── css │ │ ├── bootstrap-grid.css │ │ ├── bootstrap-grid.css.map │ │ ├── bootstrap-grid.min.css │ │ ├── bootstrap-grid.min.css.map │ │ ├── bootstrap-grid.rtl.css │ │ ├── bootstrap-grid.rtl.css.map │ │ ├── bootstrap-grid.rtl.min.css │ │ ├── bootstrap-grid.rtl.min.css.map │ │ ├── bootstrap-reboot.css │ │ ├── bootstrap-reboot.css.map │ │ ├── bootstrap-reboot.min.css │ │ ├── bootstrap-reboot.min.css.map │ │ ├── bootstrap-reboot.rtl.css │ │ ├── bootstrap-reboot.rtl.css.map │ │ ├── bootstrap-reboot.rtl.min.css │ │ ├── bootstrap-reboot.rtl.min.css.map │ │ ├── bootstrap-utilities.css │ │ ├── bootstrap-utilities.css.map │ │ ├── bootstrap-utilities.min.css │ │ ├── bootstrap-utilities.min.css.map │ │ ├── bootstrap-utilities.rtl.css │ │ ├── bootstrap-utilities.rtl.css.map │ │ ├── bootstrap-utilities.rtl.min.css │ │ ├── bootstrap-utilities.rtl.min.css.map │ │ ├── bootstrap.css │ │ ├── bootstrap.css.map │ │ ├── bootstrap.min.css │ │ ├── bootstrap.min.css.map │ │ ├── bootstrap.rtl.css │ │ ├── bootstrap.rtl.css.map │ │ ├── bootstrap.rtl.min.css │ │ └── bootstrap.rtl.min.css.map │ └── js │ │ ├── bootstrap.bundle.js │ │ ├── bootstrap.bundle.js.map │ │ ├── bootstrap.bundle.min.js │ │ ├── bootstrap.bundle.min.js.map │ │ ├── bootstrap.esm.js │ │ ├── bootstrap.esm.js.map │ │ ├── bootstrap.esm.min.js │ │ ├── bootstrap.esm.min.js.map │ │ ├── bootstrap.js │ │ ├── bootstrap.js.map │ │ ├── bootstrap.min.js │ │ └── bootstrap.min.js.map │ ├── datetimepicker-master │ ├── .gitignore │ ├── .travis.yml │ ├── MIT-LICENSE.txt │ ├── README.md │ ├── bower.json │ ├── build │ │ ├── jquery.datetimepicker.full.js │ │ ├── jquery.datetimepicker.full.min.js │ │ ├── jquery.datetimepicker.min.css │ │ └── jquery.datetimepicker.min.js │ ├── datetimepicker.jquery.json │ ├── doc.tpl │ ├── index.html │ ├── jquery.datetimepicker.css │ ├── jquery.datetimepicker.js │ ├── jquery.js │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── pull_request_template.md │ ├── screen │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.1.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ └── 6.png │ └── tests │ │ ├── app.css │ │ ├── index.html │ │ ├── input_in_container_fixed_to_bottom_of_viewport.html │ │ ├── input_in_container_fixed_to_top_of_viewport.html │ │ └── tests │ │ ├── bootstrap.js │ │ ├── destroy.js │ │ ├── events.js │ │ ├── init.js │ │ ├── methods.js │ │ └── options.js │ ├── layer_2021_12_23 │ ├── layer.js │ ├── mobile │ │ ├── layer.js │ │ └── need │ │ │ └── layer.css │ └── theme │ │ ├── default │ │ ├── icon-ext.png │ │ ├── icon.png │ │ ├── layer.css │ │ ├── loading-0.gif │ │ ├── loading-1.gif │ │ └── loading-2.gif │ │ └── moon │ │ ├── default.png │ │ └── style.css │ └── layui-v2.6.8 │ ├── layui │ ├── css │ │ ├── layui.css │ │ └── modules │ │ │ ├── code.css │ │ │ ├── laydate │ │ │ └── default │ │ │ │ └── laydate.css │ │ │ └── layer │ │ │ └── default │ │ │ ├── icon-ext.png │ │ │ ├── icon.png │ │ │ ├── layer.css │ │ │ ├── loading-0.gif │ │ │ ├── loading-1.gif │ │ │ └── loading-2.gif │ ├── font │ │ ├── iconfont.eot │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ └── layui.js │ └── test.html └── templates ├── 04_money_arrange_module ├── 04_money_arrange.html ├── 04_money_arrange_update.html ├── 04_money_record_auto_complete_data.html ├── JS01_load_money_page.html ├── Layer01_AddMoneyRecordLayer.html ├── Layer02_TransferMoneyLayer.html ├── Layer03_MoneyLineChartLayer.html ├── Layer04_MoneyCategoryChartLayer.html ├── Layer05_MoneyHistoryLayer.html └── Layer06_UploadMoneyRecordLayer.html └── common ├── custome_functions ├── date_functions.html ├── decimal_functions.html ├── get_select_list_functions.html ├── login_functions.html ├── time_format_functions.html └── timespan_functions.html ├── head_import.html └── login_page.html /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | __pycache__/ -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/P4_MoneyArrangeModule/__init__.py -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for P4_MoneyArrangeModule project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'P4_MoneyArrangeModule.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/P4_MoneyArrangeModule/e__money_arrange_function/__init__.py -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_add_money_record.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def e_add_money_record(request): 6 | """ 添加 资金记录条目 到数据库中 """ 7 | 8 | record_name = request.POST.get('record_name') 9 | record_date = request.POST.get('record_date') 10 | record_inout = request.POST.get('record_inout') 11 | record_type = request.POST.get('record_type') 12 | record_position = request.POST.get('record_position') 13 | record_amount = request.POST.get('record_amount') 14 | 15 | record_desc = request.POST.get('record_desc') 16 | record_desc = record_desc if record_desc else 'No Description' 17 | 18 | ''' money_record 信息添加到数据库 ''' 19 | conn = sqlite3.connect('data.db') 20 | cur = conn.cursor() 21 | 22 | # 添加记录信息 23 | cur.execute(""" 24 | INSERT INTO money_record (NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR) 25 | VALUES (?, ?, ?, ?, ?, ?, ?) 26 | """, (record_name, record_type, record_amount, record_inout, record_position, record_desc, record_date)) 27 | conn.commit() 28 | 29 | ''' money_position 数据库数额更新 ''' 30 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_position) 31 | money_before = float(cur.fetchone()[1]) 32 | 33 | if record_inout == 'in': 34 | money_after = round(money_before + float(record_amount), 2) 35 | elif record_inout == 'out': 36 | money_after = round(money_before - float(record_amount), 2) 37 | 38 | cur.execute(""" 39 | UPDATE money_position SET 40 | MONEY = '%s' 41 | WHERE NAME_EN = '%s' 42 | """ % (str(money_after), record_position)) 43 | conn.commit() 44 | 45 | # 关闭数据库连接 46 | cur.close() 47 | conn.close() 48 | 49 | return HttpResponse('Money Record 信息添加成功 !') 50 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_add_money_transfer_record.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def e_add_money_transfer_record(request): 6 | """ 添加 资金转移记录条目 到数据库中 """ 7 | 8 | record_name = request.POST.get('record_name') 9 | record_date = request.POST.get('record_date') 10 | record_from_position = request.POST.get('record_from_position') 11 | record_to_position = request.POST.get('record_to_position') 12 | record_amount = request.POST.get('record_amount') 13 | record_fee = request.POST.get('record_fee') 14 | 15 | record_desc = request.POST.get('record_desc') 16 | record_desc = record_desc if record_desc else 'No Description' 17 | 18 | ''' money_transfer_record 信息添加到数据库 ''' 19 | conn = sqlite3.connect('data.db') 20 | cur = conn.cursor() 21 | 22 | # 添加记录信息 23 | cur.execute(""" 24 | INSERT INTO money_record (NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, FEE) 25 | VALUES (?, ?, ?, ?, ?, ?, ?, ?) 26 | """, (record_name, '转移', record_amount, 'transfer', record_from_position + '&&' + record_to_position, 27 | record_desc, record_date, record_fee)) 28 | conn.commit() 29 | 30 | ''' money_position 数据库数额更新(from账户) ''' 31 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_from_position) 32 | money_before = float(cur.fetchone()[1]) 33 | money_after = round(money_before - float(record_amount) - float(record_fee), 2) 34 | cur.execute(""" 35 | UPDATE money_position SET 36 | MONEY = '%s' 37 | WHERE NAME_EN = '%s'; 38 | """ % (str(money_after), record_from_position)) 39 | conn.commit() 40 | 41 | ''' money_position 数据库数额更新(to账户) ''' 42 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_to_position) 43 | money_before = float(cur.fetchone()[1]) 44 | money_after = round(money_before + float(record_amount), 2) 45 | cur.execute(""" 46 | UPDATE money_position SET 47 | MONEY = '%s' 48 | WHERE NAME_EN = '%s'; 49 | """ % (str(money_after), record_to_position)) 50 | conn.commit() 51 | 52 | # 关闭数据库连接 53 | cur.close() 54 | conn.close() 55 | 56 | return HttpResponse('Money Transfer Record 信息添加成功 !') 57 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_delete_money_record.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def e_delete_money_record(request): 6 | """ 删除 资金记录条目 """ 7 | record_id = request.GET.get('record_id') 8 | 9 | conn = sqlite3.connect('data.db') 10 | cur = conn.cursor() 11 | 12 | # 获取原记录信息 13 | cur.execute("""select POSITION, INOUT, AMOUNT from money_record where ID = '%s';""" % record_id) 14 | before_position, before_inout, before_amount = cur.fetchone() 15 | 16 | # money_position 数据库数额更新(原记录退回) 17 | before_amount = -1 * float(before_amount) if before_inout == 'in' else before_amount 18 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % before_position) 19 | money_before = float(cur.fetchone()[1]) 20 | money_after = round(money_before + float(before_amount), 2) 21 | cur.execute(""" 22 | UPDATE money_position SET 23 | MONEY = '%s' 24 | WHERE NAME_EN = '%s' 25 | """ % (str(money_after), before_position)) 26 | conn.commit() 27 | 28 | # 删除记录信息 29 | cur.execute(""" DELETE FROM money_record WHERE ID = '%s'; """ % record_id) 30 | conn.commit() 31 | 32 | # 关闭数据库连接 33 | cur.close() 34 | conn.close() 35 | 36 | return HttpResponse('Money Record 信息删除成功 !') 37 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_delete_money_transfer_record.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def e_delete_money_transfer_record(request): 6 | """ 删除 资金转移记录条目 """ 7 | record_id = request.GET.get('record_id') 8 | 9 | conn = sqlite3.connect('data.db') 10 | cur = conn.cursor() 11 | 12 | # 获取原记录信息 13 | cur.execute("""SELECT POSITION, INOUT, AMOUNT, FEE FROM money_record WHERE id = '%s';""" % record_id) 14 | before_position, before_inout, before_amount, before_fee = cur.fetchone() 15 | 16 | # money_position 数据库数额更新(原记录退回) 17 | before_position_from, before_position_to = before_position.split('&&') 18 | 19 | cur.execute(""" SELECT NAME_EN, MONEY FROM money_position WHERE NAME_EN = '%s'; """ % before_position_from) 20 | money_before = float(cur.fetchone()[1]) 21 | money_after = round(money_before + float(before_amount) + float(before_fee), 2) 22 | cur.execute(""" 23 | UPDATE money_position SET 24 | MONEY = '%s' 25 | WHERE NAME_EN = '%s' 26 | """ % (str(money_after), before_position_from)) 27 | 28 | cur.execute(""" SELECT NAME_EN, MONEY FROM money_position WHERE NAME_EN = '%s'; """ % before_position_to) 29 | money_before = float(cur.fetchone()[1]) 30 | money_after = round(money_before - float(before_amount), 2) 31 | cur.execute(""" 32 | UPDATE money_position SET 33 | MONEY = '%s' 34 | WHERE NAME_EN = '%s' 35 | """ % (str(money_after), before_position_to)) 36 | 37 | # 删除记录信息 38 | cur.execute(""" DELETE FROM money_record WHERE id = '%s';""" % record_id) 39 | conn.commit() 40 | 41 | # 关闭数据库连接 42 | cur.close() 43 | conn.close() 44 | 45 | return HttpResponse('Money Transfer Record 信息删除成功 !') 46 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_get_auto_complete_rules.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | 4 | 5 | def e_get_auto_complete_rules(request): 6 | """ 从数据库中获取 money_record 的自动填充规则 """ 7 | 8 | # 创建数据库连接,返回连接对象conn,返回游标cur 9 | conn = sqlite3.connect('data.db') 10 | cur = conn.cursor() 11 | 12 | type_list_dic = {} # 所有收入和支出分类项目 13 | cur.execute(""" select DATA from select_list where 14 | TEMPLATE = '04_money_arrange' and TYPE = 'select' and DESC = 'income_category'; """) 15 | type_list_dic['in'] = [_[0] for _ in cur.fetchall()] 16 | cur.execute(""" select DATA from select_list where 17 | TEMPLATE = '04_money_arrange' and TYPE = 'select' and DESC = 'outcome_category'; """) 18 | type_list_dic['out'] = [_[0] for _ in cur.fetchall()] 19 | 20 | cur.execute(""" select NAME_EN from money_position; """) 21 | money_position_list = [_[0] for _ in cur.fetchall()] 22 | 23 | # 获取自动填充规则,并进行合法性检测,忽略不合法的规则 24 | auto_complete_rule_list = [] 25 | cur.execute(""" select PATTERN, INOUT, TYPE, POSITION, AMOUNT from money_record_auto_complete 26 | order by PRIORITY asc; """) 27 | for line in cur.fetchall(): 28 | if line[1] not in ['in', 'out']: # 合法性检验-1 29 | continue 30 | if line[2] not in type_list_dic[line[1]]: # 合法性检验-2 31 | continue 32 | if line[3] not in money_position_list: # 合法性检验-3 33 | continue 34 | try: 35 | float(line[4]) # 合法性检验-4 36 | except: 37 | continue 38 | 39 | # 通过合法性检验,加入字典 40 | auto_complete_rule_list.append([line[idx] for idx in range(5)]) 41 | 42 | # 关闭数据库连接 43 | cur.close() 44 | conn.close() 45 | 46 | return JsonResponse(auto_complete_rule_list, safe=False) 47 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_get_money_positions.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | 4 | 5 | def e_get_money_positions(request): 6 | """ 从数据库中获取 money_positions 详细信息 """ 7 | 8 | # 创建数据库连接,返回连接对象conn,返回游标cur 9 | conn = sqlite3.connect('data.db') 10 | cur = conn.cursor() 11 | 12 | # 获取 money_position 对应中文信息 13 | money_position_dic = {} 14 | cur.execute(""" select NAME_EN, NAME, MONEY, TYPE, ADDITION_INFO, HIDDEN from money_position; """) 15 | for line in cur.fetchall(): 16 | money_position_dic[line[0]] = { 17 | 'name': line[1], 18 | 'money': line[2], 19 | 'type': line[3], 20 | 'addition_info': line[4], 21 | 'hidden': line[5], 22 | } 23 | 24 | # 关闭数据库连接 25 | cur.close() 26 | conn.close() 27 | 28 | return JsonResponse(money_position_dic) 29 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_get_money_record.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | 4 | 5 | def e_get_money_record(request): 6 | """ 获取 单个资金记录条目 """ 7 | record_id = request.GET.get('record_id') 8 | 9 | conn = sqlite3.connect('data.db') 10 | cur = conn.cursor() 11 | 12 | # 获取记录信息 13 | cur.execute(""" 14 | SELECT ID, NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, FEE 15 | FROM money_record WHERE ID = '%s'; 16 | """ % record_id) 17 | 18 | single_record = cur.fetchone() 19 | single_record_dic = { 20 | 'id': single_record[0], 21 | 'name': single_record[1], 22 | 'type': single_record[2], 23 | 'amount': single_record[3], 24 | 'inout': single_record[4], 25 | 'position': single_record[5], 26 | 'description': single_record[6], 27 | 'date': single_record[7], 28 | 'fee': single_record[8], 29 | } 30 | 31 | # 关闭数据库连接 32 | cur.close() 33 | conn.close() 34 | 35 | return JsonResponse(single_record_dic) 36 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_get_money_record_form.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | 4 | 5 | def e_get_money_record_form(request): 6 | """ 获取 money_record 的所有条目信息,并按照 layui表格 需要的形式返回 """ 7 | 8 | # 得到 当前页码page、每页的数据量limit 9 | page = int(request.GET.get('page')) 10 | limit = int(request.GET.get('limit')) 11 | 12 | # 创建数据库连接,返回连接对象conn,返回游标cur 13 | conn = sqlite3.connect('data.db') 14 | cur = conn.cursor() 15 | 16 | # 获取 money_position对应中文信息 17 | money_position_map = {} 18 | cur.execute(""" select NAME_EN, NAME from money_position; """) 19 | for line in cur.fetchall(): 20 | money_position_map[line[0]] = line[1] 21 | 22 | # 查询该记录的信息,返回列表数据 23 | cur.execute(""" 24 | select ID, NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, STATUS 25 | FROM money_record ORDER BY ID DESC; 26 | """) 27 | all_record = [] 28 | all_record_query = cur.fetchall() 29 | 30 | for line in all_record_query: 31 | if '&&' in line[5]: 32 | position_str = '&&'.join([money_position_map[_] for _ in line[5].split('&&')]) 33 | else: 34 | position_str = money_position_map[line[5]] 35 | 36 | record_dic = { 37 | 'id': line[0], 38 | 'name': line[1], 39 | 'type': line[2], 40 | 'amount': line[3], 41 | 'inout': line[4], 42 | 'position': position_str, 43 | 'description': line[6], 44 | 'date': line[7], 45 | 'status': line[8], 46 | } 47 | all_record.append(record_dic) 48 | 49 | # 分页数据返回 50 | count = len(all_record) # 记录总数 51 | all_record = all_record[(page - 1) * limit: page * limit] 52 | 53 | # 关闭数据库连接 54 | cur.close() 55 | conn.close() 56 | 57 | # 按照Layui表格所需形式准备数据 58 | response_result = { 59 | "code": 0, 60 | "msg": "", 61 | "count": count, 62 | "data": all_record 63 | } 64 | return JsonResponse(response_result) 65 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_modify_money_record.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def e_modify_money_record(request): 6 | """ 修改 资金记录条目 到数据库中 """ 7 | 8 | record_id = request.POST.get('record_id') 9 | record_name = request.POST.get('record_name') 10 | record_date = request.POST.get('record_date') 11 | record_inout = request.POST.get('record_inout') 12 | record_type = request.POST.get('record_type') 13 | record_position = request.POST.get('record_position') 14 | record_amount = request.POST.get('record_amount') 15 | 16 | record_desc = request.POST.get('record_desc') 17 | record_desc = record_desc if record_desc else 'No Description' 18 | 19 | ''' 数据库信息修改 ''' 20 | conn = sqlite3.connect('data.db') 21 | cur = conn.cursor() 22 | 23 | # 获取原记录信息 24 | cur.execute("""SELECT POSITION, INOUT, AMOUNT FROM money_record WHERE id = '%s';""" % record_id) 25 | before_position, before_inout, before_amount = cur.fetchone() 26 | 27 | # money_position 数据库数额更新(原记录退回) 28 | before_amount = -1 * float(before_amount) if before_inout == 'in' else before_amount 29 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % before_position) 30 | money_before = float(cur.fetchone()[1]) 31 | money_after = round(money_before + float(before_amount), 2) 32 | cur.execute(""" 33 | UPDATE money_position SET 34 | MONEY = '%s' 35 | WHERE NAME_EN = '%s' 36 | """ % (str(money_after), before_position)) 37 | 38 | # 修改 money_record 记录信息 39 | cur.execute(""" 40 | UPDATE money_record SET 41 | NAME = '%s', 42 | TYPE = '%s', 43 | AMOUNT = '%s', 44 | INOUT = '%s', 45 | POSITION = '%s', 46 | DESCRIPTION = '%s', 47 | DATE_STR = '%s', 48 | STATUS = 'good' 49 | WHERE id = '%s'; 50 | """ % (record_name, record_type, record_amount, record_inout, record_position, 51 | record_desc, record_date, record_id)) 52 | conn.commit() 53 | 54 | # money_position 数据库数额更新(新增记录) 55 | record_amount = -1 * float(record_amount) if record_inout == 'out' else record_amount 56 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_position) 57 | money_before = float(cur.fetchone()[1]) 58 | money_after = round(money_before + float(record_amount), 2) 59 | cur.execute(""" 60 | UPDATE money_position SET 61 | MONEY = '%s' 62 | WHERE NAME_EN = '%s' 63 | """ % (str(money_after), record_position)) 64 | conn.commit() 65 | 66 | # 关闭数据库连接 67 | cur.close() 68 | conn.close() 69 | 70 | return HttpResponse('Money Record 信息修改成功 !') 71 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_modify_money_transfer_record.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def e_modify_money_transfer_record(request): 6 | """ 修改 资金转移记录条目 """ 7 | 8 | record_id = request.POST.get('record_id') 9 | record_name = request.POST.get('record_name') 10 | record_date = request.POST.get('record_date') 11 | record_from_position = request.POST.get('record_from_position') 12 | record_to_position = request.POST.get('record_to_position') 13 | record_amount = request.POST.get('record_amount') 14 | record_fee = request.POST.get('record_fee') 15 | 16 | record_desc = request.POST.get('record_desc') 17 | record_desc = record_desc if record_desc else 'No Description' 18 | 19 | ''' money_transfer_record 信息修改到数据库 ''' 20 | conn = sqlite3.connect('data.db') 21 | cur = conn.cursor() 22 | 23 | # 获取原记录信息 24 | cur.execute("""SELECT POSITION, INOUT, AMOUNT, FEE FROM money_record WHERE id = '%s';""" % record_id) 25 | before_position, before_inout, before_amount, before_fee = cur.fetchone() 26 | 27 | # money_position 数据库数额更新(原记录退回) 28 | before_position_from, before_position_to = before_position.split('&&') 29 | 30 | cur.execute(""" SELECT NAME_EN, MONEY FROM money_position WHERE NAME_EN = '%s'; """ % before_position_from) 31 | money_before = float(cur.fetchone()[1]) 32 | money_after = round(money_before + float(before_amount) + float(before_fee), 2) 33 | cur.execute(""" 34 | UPDATE money_position SET 35 | MONEY = '%s' 36 | WHERE NAME_EN = '%s' 37 | """ % (str(money_after), before_position_from)) 38 | 39 | cur.execute(""" SELECT NAME_EN, MONEY FROM money_position WHERE NAME_EN = '%s'; """ % before_position_to) 40 | money_before = float(cur.fetchone()[1]) 41 | money_after = round(money_before - float(before_amount), 2) 42 | cur.execute(""" 43 | UPDATE money_position SET 44 | MONEY = '%s' 45 | WHERE NAME_EN = '%s' 46 | """ % (str(money_after), before_position_to)) 47 | 48 | # 修改记录信息 49 | cur.execute(""" 50 | UPDATE money_record SET 51 | NAME = '%s', 52 | TYPE = '%s', 53 | AMOUNT = '%s', 54 | INOUT = '%s', 55 | POSITION = '%s', 56 | DESCRIPTION = '%s', 57 | DATE_STR = '%s', 58 | FEE = '%s', 59 | STATUS = 'good' 60 | WHERE id = '%s'; 61 | """ % (record_name, '转移', record_amount, 'transfer', record_from_position + '&&' + record_to_position, 62 | record_desc, record_date, record_fee, record_id)) 63 | conn.commit() 64 | 65 | # money_position 数据库数额更新(新增记录) 66 | cur.execute(""" SELECT NAME_EN, MONEY FROM money_position WHERE NAME_EN = '%s'; """ % record_from_position) 67 | money_before = float(cur.fetchone()[1]) 68 | money_after = round(money_before - float(record_amount) - float(record_fee), 2) 69 | cur.execute(""" 70 | UPDATE money_position SET 71 | MONEY = '%s' 72 | WHERE NAME_EN = '%s' 73 | """ % (str(money_after), record_from_position)) 74 | 75 | cur.execute(""" SELECT NAME_EN, MONEY FROM money_position WHERE NAME_EN = '%s'; """ % record_to_position) 76 | money_before = float(cur.fetchone()[1]) 77 | money_after = round(money_before + float(record_amount), 2) 78 | cur.execute(""" 79 | UPDATE money_position SET 80 | MONEY = '%s' 81 | WHERE NAME_EN = '%s' 82 | """ % (str(money_after), record_to_position)) 83 | conn.commit() 84 | 85 | # 关闭数据库连接 86 | cur.close() 87 | conn.close() 88 | 89 | return HttpResponse('Money Transfer Record 信息修改成功 !') 90 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_money_get_chart01_data.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | import datetime 4 | 5 | 6 | def e_money_get_chart01_data(request): 7 | """ 获取 chart01资金折线图 所需的数据 """ 8 | 9 | time_scale = request.GET.get('time_scale') 10 | time_data = request.GET.get('time_data') 11 | 12 | # 创建数据库连接,返回连接对象conn,返回游标cur 13 | conn = sqlite3.connect('data.db') 14 | cur = conn.cursor() 15 | 16 | ''' 查询 time_data 范围中的 17 | result_dic = { 18 | 'time_str': { 19 | 'money': float, 20 | } 21 | } 22 | ''' 23 | result_dic = {} # 存储查询结果记录的字典 24 | if time_scale == 'given_span': 25 | d_start_time, d_end_time = time_data.split('#') 26 | sd_start_time = datetime.datetime.strptime(d_start_time, '%Y-%m-%d') 27 | sd_end_time = datetime.datetime.strptime(d_end_time, '%Y-%m-%d') 28 | 29 | sd_duration = (sd_end_time - sd_start_time).days 30 | for sd_idx in range(sd_duration + 1): # 循环查询范围内日期的 time_record 31 | date_str = (sd_start_time + datetime.timedelta(days=sd_idx)).strftime('%Y-%m-%d') 32 | result_dic[date_str] = {'money': 0} 33 | 34 | # 查询该天的 money_record 35 | cur.execute(" select ID, AMOUNT FROM money_record WHERE DATE_STR = '%s' AND INOUT = '%s'; " 36 | % (date_str, 'out')) 37 | for item in cur.fetchall(): 38 | result_dic[date_str]['money'] += float(item[1]) 39 | 40 | elif time_scale == 'all': 41 | # 查询全部的 time_record 42 | cur.execute("select ID, AMOUNT, DATE_STR FROM money_record WHERE INOUT = '%s'; " % 'out') 43 | for item in cur.fetchall(): 44 | d_money, d_time_str = float(item[1]), item[2] 45 | 46 | # 进行字典项的填充 47 | if result_dic.get(d_time_str) is not None: 48 | result_dic[d_time_str]['money'] += d_money 49 | else: 50 | result_dic[d_time_str] = {'money': d_money} 51 | 52 | ''' 按照 chart 所需的格式整理数据 ''' 53 | chart_data_dic = {} 54 | 55 | key_list = list(result_dic.keys()) 56 | chart_data_dic['x_data'] = key_list 57 | chart_data_dic['y_data'] = [round(result_dic[x]['money'], 2) for x in key_list] 58 | 59 | # 关闭数据库连接 60 | cur.close() 61 | conn.close() 62 | 63 | return JsonResponse(chart_data_dic) 64 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_money_get_chart02_data.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | 4 | 5 | def e_money_get_chart02_data(request): 6 | """ 获取 chart02资金饼图 和 chart02a资金柱状图 所需的数据 """ 7 | 8 | date_str = request.GET.get('date_str') 9 | 10 | # 创建数据库连接,返回连接对象conn,返回游标cur 11 | conn = sqlite3.connect('data.db') 12 | cur = conn.cursor() 13 | 14 | ''' 查询 time_data 范围中的 15 | result_dic = { 16 | 'type': { 17 | 'money_amount': float, 18 | } 19 | } 20 | ''' 21 | result_dic = {} # 存储查询结果记录的字典 22 | 23 | # 查询该天的 money_record 24 | cur.execute(" select ID, TYPE, AMOUNT FROM money_record WHERE DATE_STR = '%s' AND INOUT = '%s'; " 25 | % (date_str, 'out')) 26 | for item in cur.fetchall(): 27 | d_type, d_amount = item[1], float(item[2]) 28 | 29 | # 进行字典项的填充 30 | if result_dic.get(d_type) is not None: 31 | result_dic[d_type]['money_amount'] += d_amount 32 | else: 33 | result_dic[d_type] = {'money_amount': d_amount} 34 | 35 | # 关闭数据库连接 36 | cur.close() 37 | conn.close() 38 | 39 | ''' 按照 chart 所需的格式整理数据 ''' 40 | # chart02所需数据 41 | chart02_data_list = [] 42 | for key_type in result_dic.keys(): 43 | chart02_data_list.append({ 44 | 'name': key_type, 45 | 'value': round(result_dic[key_type]['money_amount'], 2), 46 | }) 47 | 48 | # chart02a所需数据 49 | chart02a_data_dic = {'yaxis': [], 'data': []} 50 | sorted_list = sorted(chart02_data_list, key=lambda x: x['value'], reverse=False) 51 | 52 | for item in sorted_list: 53 | chart02a_data_dic['yaxis'].append(item['name']) 54 | chart02a_data_dic['data'].append(item['value']) 55 | 56 | return JsonResponse({ 57 | 'chart02_data': chart02_data_list, 58 | 'chart02a_data': chart02a_data_dic, 59 | }) 60 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_money_get_chart03_data.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | import datetime 4 | 5 | 6 | def e_money_get_chart03_data(request): 7 | """ 获取 chart03&04资金饼图 所需的数据 """ 8 | 9 | time_scale = request.GET.get('time_scale') 10 | time_data = request.GET.get('time_data') 11 | inout_type = request.GET.get('inout_type') 12 | 13 | # 创建数据库连接,返回连接对象conn,返回游标cur 14 | conn = sqlite3.connect('data.db') 15 | cur = conn.cursor() 16 | 17 | ''' 查询 time_data 范围中的 18 | result_dic = { 19 | 'type': { 20 | 'money_amount': float, 21 | } 22 | } 23 | ''' 24 | result_dic = {} # 存储查询结果记录的字典 25 | if time_scale == 'given_span': 26 | d_start_time, d_end_time = time_data.split('#') 27 | sd_start_time = datetime.datetime.strptime(d_start_time, '%Y-%m-%d') 28 | sd_end_time = datetime.datetime.strptime(d_end_time, '%Y-%m-%d') 29 | 30 | sd_duration = (sd_end_time - sd_start_time).days 31 | for sd_idx in range(sd_duration + 1): # 循环查询范围内日期的 time_record 32 | date_str = (sd_start_time + datetime.timedelta(days=sd_idx)).strftime('%Y-%m-%d') 33 | 34 | # 查询该天的 money_record 35 | cur.execute(" select ID, TYPE, AMOUNT FROM money_record WHERE DATE_STR = '%s' AND INOUT = '%s'; " 36 | % (date_str, inout_type)) 37 | for item in cur.fetchall(): 38 | d_type, d_amount = item[1], float(item[2]) 39 | 40 | # 进行字典项的填充 41 | if result_dic.get(d_type) is not None: 42 | result_dic[d_type]['money_amount'] += d_amount 43 | else: 44 | result_dic[d_type] = {'money_amount': d_amount} 45 | 46 | elif time_scale == 'all': 47 | # 查询全部的 time_record 48 | cur.execute("select ID, TYPE, AMOUNT FROM money_record WHERE INOUT = '%s'; " % inout_type) 49 | for item in cur.fetchall(): 50 | d_type, d_amount = item[1], float(item[2]) 51 | 52 | # 进行字典项的填充 53 | if result_dic.get(d_type) is not None: 54 | result_dic[d_type]['money_amount'] += d_amount 55 | else: 56 | result_dic[d_type] = {'money_amount': d_amount} 57 | 58 | ''' 按照 chart 所需的格式整理数据 ''' 59 | chart_data_list = [] 60 | for key_type in result_dic.keys(): 61 | chart_data_list.append({ 62 | 'name': key_type, 63 | 'value': round(result_dic[key_type]['money_amount'], 2), 64 | }) 65 | 66 | # 关闭数据库连接 67 | cur.close() 68 | conn.close() 69 | 70 | return JsonResponse(chart_data_list, safe=False) 71 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_money_get_chart_history01_data.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | import datetime 4 | 5 | 6 | def e_money_get_chart_history01_data(request): 7 | """ 获取 chart_history01资金历史折线图 所需的数据 """ 8 | 9 | time_scale = request.GET.get('time_scale') 10 | time_data = request.GET.get('time_data') 11 | check_position = request.GET.get('check_position') 12 | 13 | # 创建数据库连接,返回连接对象conn,返回游标cur 14 | conn = sqlite3.connect('data.db') 15 | cur = conn.cursor() 16 | 17 | ''' 查询 time_data 范围中的 money_remain 18 | result_dic = { 19 | 'time_str': { 20 | 'money_remain': float, 21 | } 22 | } 23 | ''' 24 | result_dic = {} # 存储查询结果记录的字典 25 | 26 | d_start_time, d_end_time = time_data.split('#') # 开始和结束的日期 27 | d_today_time = datetime.datetime.now().strftime('%Y-%m-%d') # 当前的日期 28 | 29 | sd_start_time = datetime.datetime.strptime(d_start_time, '%Y-%m-%d') 30 | # sd_end_time = datetime.datetime.strptime(d_end_time, '%Y-%m-%d') 31 | sd_today_time = datetime.datetime.strptime(d_today_time, '%Y-%m-%d') 32 | sd_duration = (sd_today_time - sd_start_time).days # 时间跨度(必须从今天开始往回推) 33 | 34 | if check_position != 'all': 35 | # 查询得到当前的资金余量 36 | cur.execute(" select MONEY, NAME from money_position where NAME_EN = '%s'; " % check_position) 37 | result_dic[d_today_time] = {'money_remain': float(cur.fetchone()[0])} 38 | 39 | # 计算得到每一天的资金余量 40 | for sd_idx in range(sd_duration-1, -1, -1): # 循环查询范围内日期的 money_record(逆序) 41 | date_str = (sd_start_time + datetime.timedelta(days=sd_idx)).strftime('%Y-%m-%d') 42 | date_str_next = (sd_start_time + datetime.timedelta(days=sd_idx+1)).strftime('%Y-%m-%d') 43 | 44 | result_dic[date_str] = { # 初始值为后一天的值 45 | 'money_remain': result_dic[date_str_next]['money_remain'] 46 | } 47 | 48 | # 查询该天的 money_record 49 | cur.execute(""" select ID, AMOUNT, INOUT, POSITION, FEE FROM money_record WHERE DATE_STR = '%s'; 50 | """ % date_str_next) 51 | for item in cur.fetchall(): 52 | money_amount, money_inout, money_position = float(item[1]), item[2], item[3] 53 | money_fee = float(item[4]) if item[4] else 0 54 | 55 | if money_inout == 'in' and check_position == money_position: 56 | result_dic[date_str]['money_remain'] -= money_amount 57 | elif money_inout == 'out' and check_position == money_position: 58 | result_dic[date_str]['money_remain'] += money_amount 59 | elif money_inout == 'transfer' and ((check_position + '&&') in money_position): 60 | result_dic[date_str]['money_remain'] += money_amount + money_fee 61 | elif money_inout == 'transfer' and (('&&' + check_position) in money_position): 62 | result_dic[date_str]['money_remain'] -= money_amount 63 | else: 64 | # 查询得到当前的资金余量 65 | cur.execute(" select MONEY, NAME from money_position; ") 66 | result_dic[d_today_time] = {'money_remain': 0} 67 | for line in cur.fetchall(): 68 | result_dic[d_today_time]['money_remain'] += float(line[0]) 69 | 70 | # 计算得到每一天的资金余量 71 | for sd_idx in range(sd_duration-1, -1, -1): # 循环查询范围内日期的 money_record(逆序) 72 | date_str = (sd_start_time + datetime.timedelta(days=sd_idx)).strftime('%Y-%m-%d') 73 | date_str_next = (sd_start_time + datetime.timedelta(days=sd_idx + 1)).strftime('%Y-%m-%d') 74 | 75 | result_dic[date_str] = { # 初始值为后一天的值 76 | 'money_remain': result_dic[date_str_next]['money_remain'] 77 | } 78 | 79 | # 查询该天的 money_record 80 | cur.execute(""" select ID, AMOUNT, INOUT, POSITION, FEE FROM money_record WHERE DATE_STR = '%s'; 81 | """ % date_str_next) 82 | for item in cur.fetchall(): 83 | money_amount, money_inout = float(item[1]), item[2] 84 | money_fee = float(item[4]) if item[4] else 0 85 | 86 | if money_inout == 'in': 87 | result_dic[date_str]['money_remain'] -= money_amount 88 | elif money_inout == 'out': 89 | result_dic[date_str]['money_remain'] += money_amount 90 | elif money_inout == 'transfer': 91 | result_dic[date_str]['money_remain'] += money_fee 92 | 93 | ''' 按照 chart 所需的格式整理数据 ''' 94 | result_dic_list = sorted(result_dic.items(), key=lambda x: x[0], reverse=False) 95 | result_dic_list_filtered = [x for x in result_dic_list if d_start_time <= x[0] <= d_end_time] 96 | 97 | chart_data_dic = { 98 | 'x_data': [x[0] for x in result_dic_list_filtered], 99 | 'y_data': [round(x[1]['money_remain'], 2) for x in result_dic_list_filtered] 100 | } 101 | 102 | # 关闭数据库连接 103 | cur.close() 104 | conn.close() 105 | 106 | return JsonResponse(chart_data_dic) 107 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_search_money_record_form.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | import datetime 4 | import copy 5 | 6 | 7 | # 确定行结果是否符合 搜索的文本 8 | def correspond(line, search_text, search_start_date, search_end_date, 9 | search_inout, search_category, search_position): 10 | 11 | # 搜索语句的验证 12 | if search_text.lower() not in line[1].lower(): 13 | return False 14 | 15 | # 收支类型的验证 16 | if search_inout != 'all': 17 | if search_inout != line[4]: 18 | return False 19 | 20 | # 分类的验证 21 | if search_category != 'all': 22 | if search_category != line[2]: 23 | return False 24 | 25 | # 来源的验证 26 | if search_position != 'all': 27 | if (search_position != line[5] and ((search_position + '&&') not in line[5]) and 28 | (('&&' + search_position) not in line[5])): 29 | return False 30 | 31 | # 时间范围的验证 32 | record_date = datetime.datetime.strptime(line[7], '%Y-%m-%d') 33 | 34 | if search_start_date: 35 | start_date = datetime.datetime.strptime(search_start_date, '%Y-%m-%d') 36 | if record_date < start_date: 37 | return False 38 | 39 | if search_end_date: 40 | end_date = datetime.datetime.strptime(search_end_date, '%Y-%m-%d') 41 | if record_date > end_date: 42 | return False 43 | 44 | return True 45 | 46 | 47 | def e_search_money_record_form(request): 48 | """ 获取 money_record 的搜索结果,并按照 layui表格 需要的形式返回 """ 49 | 50 | # 得到当前页码page,得到每页的数据量limit 51 | page = int(request.GET.get('page')) 52 | limit = int(request.GET.get('limit')) 53 | 54 | search_text = request.GET.get('search_text') 55 | search_start_date = request.GET.get('search_start_date') 56 | search_end_date = request.GET.get('search_end_date') 57 | search_inout = request.GET.get('search_inout') 58 | search_category = request.GET.get('search_category') 59 | search_position = request.GET.get('search_position') 60 | 61 | # 创建数据库连接,返回连接对象conn,返回游标cur 62 | conn = sqlite3.connect('data.db') 63 | cur = conn.cursor() 64 | 65 | # 获取 money_position对应中文信息 66 | money_position_map = {} 67 | cur.execute(""" select NAME_EN, NAME from money_position; """) 68 | for line in cur.fetchall(): 69 | money_position_map[line[0]] = line[1] 70 | 71 | # 查询该记录的信息,返回列表数据 72 | cur.execute(""" 73 | select ID, NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, STATUS 74 | FROM money_record ORDER BY ID DESC; 75 | """) 76 | all_record = [] 77 | all_record_query = cur.fetchall() 78 | 79 | # 查询出来的数据,通过匹配筛选并加入结果集 80 | for line in all_record_query: 81 | # 如果符合搜索条件,则加入结果集 82 | if(correspond(copy.deepcopy(line), search_text, search_start_date, search_end_date, 83 | search_inout, search_category, search_position)): 84 | 85 | if '&&' in line[5]: 86 | position_str = '&&'.join([money_position_map[_] for _ in line[5].split('&&')]) 87 | else: 88 | position_str = money_position_map[line[5]] 89 | 90 | record_dic = { 91 | 'id': line[0], 92 | 'name': line[1], 93 | 'type': line[2], 94 | 'amount': line[3], 95 | 'inout': line[4], 96 | 'position': position_str, 97 | 'description': line[6], 98 | 'date': line[7], 99 | 'status': line[8], 100 | } 101 | all_record.append(record_dic) 102 | 103 | # 分页数据返回 104 | count = len(all_record) # 记录总数 105 | all_record = all_record[(page-1)*limit:page*limit] 106 | 107 | # 关闭数据库连接 108 | cur.close() 109 | conn.close() 110 | 111 | # 按照Layui表格所需形式准备数据 112 | response_result = { 113 | "code": 0, 114 | "msg": "", 115 | "count": count, 116 | "data": all_record 117 | } 118 | return JsonResponse(response_result) 119 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_upload_bill.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import pandas as pd 3 | import io 4 | 5 | 6 | def get_df_wx_new(row, col_name): 7 | if col_name == 'A_名字': 8 | return f'[{row["交易类型"]}] [{row["交易对方"]}]' 9 | elif col_name == 'B_日期': 10 | return row['交易时间'][:10] 11 | elif col_name == 'C_收支': 12 | if row['收/支'] == '收入': 13 | return 'in' 14 | elif row['收/支'] == '支出': 15 | return 'out' 16 | else: 17 | return 'transfer' 18 | elif col_name == 'D_分类': 19 | return '待定' 20 | elif col_name == 'E_位置': 21 | if row['支付方式'] in ['零钱', '/']: 22 | return 'wechat' 23 | else: 24 | return 'unknown' 25 | elif col_name == 'F_金额': 26 | return row['金额(元)'][1:] 27 | elif col_name == 'G_其他描述': 28 | desc_str = f'[{row["交易时间"][11:]}]' 29 | if row["商品"] != '/': 30 | desc_str += f' [{row["商品"]}]' 31 | if row["备注"] != '/': 32 | desc_str += f' [{row["备注"]}]' 33 | return desc_str 34 | elif col_name == 'A1_源位置': 35 | if row['收/支'] in ['收入', '支出']: 36 | return 'Nodata' 37 | else: 38 | if '提现' in row['交易类型']: 39 | return 'wechat' 40 | else: 41 | return 'unknown' 42 | elif col_name == 'A2_目标位置': 43 | if row['收/支'] in ['收入', '支出']: 44 | return 'Nodata' 45 | else: 46 | if '充值' in row['交易类型']: 47 | return 'wechat' 48 | else: 49 | return 'unknown' 50 | elif col_name == 'A3_金额': 51 | if row['收/支'] in ['收入', '支出']: 52 | return 'Nodata' 53 | else: 54 | if '提现' in row['交易类型']: 55 | all_money = float(row['金额(元)'][1:]) 56 | service_fee = float(row['备注'].replace('服务费', '')[1:]) 57 | return round(all_money - service_fee, 2) 58 | else: 59 | return 'Nodata' 60 | elif col_name == 'A4_手续费': 61 | if row['收/支'] in ['收入', '支出']: 62 | return 'Nodata' 63 | else: 64 | if '提现' in row['交易类型']: 65 | service_fee = row['备注'].replace('服务费', '')[1:] 66 | return service_fee 67 | else: 68 | return 'Nodata' 69 | 70 | 71 | def get_df_alipay_new(row, col_name): 72 | if col_name == 'A_名字': 73 | return f'[{row["交易分类"]}] [{row["交易对方"]}]' 74 | elif col_name == 'B_日期': 75 | return row['交易时间'][:10] 76 | elif col_name == 'C_收支': 77 | if row['收/支'] == '收入': 78 | return 'in' 79 | elif row['收/支'] == '支出': 80 | return 'out' 81 | elif row['交易状态'] == '交易关闭': 82 | return 'close' 83 | else: 84 | return 'transfer' 85 | elif col_name == 'D_分类': 86 | return '待定' 87 | elif col_name == 'E_位置': 88 | if '花呗' in str(row['收/付款方式']): 89 | return 'ant_credit_pay' 90 | elif '工商银行' in str(row['收/付款方式']): 91 | return 'icbc' 92 | elif (row['收/支'] == '收入') and (str(row['收/付款方式']) == 'nan'): 93 | return 'alipay' 94 | else: 95 | return 'unknown' 96 | elif col_name == 'F_金额': 97 | return row['金额'] 98 | elif col_name == 'G_其他描述': 99 | desc_str = f'[{row["交易时间"][11:]}]' 100 | if str(row["商品说明"]) != 'nan': 101 | desc_str += f' [{row["商品说明"]}]' 102 | if str(row["备注"]) != 'nan': 103 | desc_str += f' [{row["备注"]}]' 104 | return desc_str 105 | elif col_name == 'A1_源位置': 106 | if row['收/支'] in ['收入', '支出']: 107 | return 'Nodata' 108 | else: 109 | if '提现' in row['交易分类']: 110 | return 'alipay' 111 | else: 112 | return 'unknown' 113 | elif col_name == 'A2_目标位置': 114 | if row['收/支'] in ['收入', '支出']: 115 | return 'Nodata' 116 | else: 117 | return 'unknown' 118 | elif col_name == 'A3_金额': 119 | if row['收/支'] in ['收入', '支出']: 120 | return 'Nodata' 121 | else: 122 | if '提现' in row['交易分类']: 123 | all_money = float(row['金额'][1:]) 124 | service_fee = float(row['备注'].replace('服务费', '')[1:]) 125 | return round(all_money-service_fee, 2) 126 | else: 127 | return 'Nodata' 128 | elif col_name == 'A4_手续费': 129 | if row['收/支'] in ['收入', '支出']: 130 | return 'Nodata' 131 | else: 132 | if '提现' in row['交易分类']: 133 | service_fee = row['备注'].replace('服务费', '')[1:] 134 | return service_fee 135 | else: 136 | return 'Nodata' 137 | 138 | 139 | def e_upload_bill(request): 140 | upload_file = request.FILES['file'] # 获取文件 141 | # print(upload_file.name) 142 | # print(upload_file.size) 143 | 144 | bill_source = request.POST.get('bill_source') # 账单来源 145 | upload_mode = request.POST.get('upload_mode') # 上传模式 146 | 147 | if bill_source == 'wechat_bill': 148 | # 获取到主体数据部分 149 | file_content = upload_file.read().decode('utf-8') # 获取文件内容 150 | # print(file_content) 151 | wx_content = '' 152 | for wx_line in file_content.split('\n'): 153 | wx_content += wx_line + '\n' 154 | if '---微信支付账单明细列表---' in wx_line: 155 | wx_content = '' 156 | 157 | # 转换成dataframe格式,并处理成需要的数据 158 | df_wx = pd.read_csv(io.StringIO(wx_content)) 159 | 160 | col_name_new_list = ['A_名字', 'B_日期', 'C_收支', 'D_分类', 'E_位置', 'F_金额', 'G_其他描述', 161 | 'A1_源位置', 'A2_目标位置', 'A3_金额', 'A4_手续费'] 162 | for col_name_new in col_name_new_list: 163 | df_wx[col_name_new] = df_wx.apply(lambda x: get_df_wx_new(x, col_name_new), axis=1) 164 | 165 | df_wx = df_wx[col_name_new_list] 166 | 167 | # 原始数据 168 | origin_data = df_wx.to_dict(orient='records') 169 | 170 | # 用在预览表格中的数据 171 | table_data = [] 172 | for row_idx, row in df_wx.iterrows(): 173 | table_data.append({ 174 | 'id': '△', 175 | 'name': row['A_名字'], 176 | 'date': row['B_日期'], 177 | 'type': row['D_分类'] if row['C_收支'] != 'transfer' else '转移', 178 | 'inout': row['C_收支'], 179 | 'amount': row['F_金额'] if row['C_收支'] != 'transfer' or row['A1_源位置'] != 'wechat' else row['A3_金额'], 180 | 'position': row['E_位置'] if row['C_收支'] != 'transfer' else f"{row['A1_源位置']}&&{row['A2_目标位置']}", 181 | 'description': row['G_其他描述'], 182 | 'status': 'to_be_checked', 183 | }) 184 | 185 | if bill_source == 'alipay_bill': 186 | file_content = upload_file.read().decode('ansi') # 获取文件内容 187 | # print(file_content) 188 | # 获取到主体数据部分 189 | alipay_content = '' 190 | for alipay_line in file_content.split('\n'): 191 | alipay_content += alipay_line + '\n' 192 | if '---支付宝' in alipay_line: 193 | alipay_content = '' 194 | 195 | # print(alipay_content) 196 | # 转换成dataframe格式,并处理成需要的数据 197 | df_alipay = pd.read_csv(io.StringIO(alipay_content)) 198 | 199 | col_name_new_list = ['A_名字', 'B_日期', 'C_收支', 'D_分类', 'E_位置', 'F_金额', 'G_其他描述', 200 | 'A1_源位置', 'A2_目标位置', 'A3_金额', 'A4_手续费'] 201 | for col_name_new in col_name_new_list: 202 | df_alipay[col_name_new] = df_alipay.apply(lambda x: get_df_alipay_new(x, col_name_new), axis=1) 203 | 204 | df_alipay = df_alipay[col_name_new_list] 205 | df_alipay = df_alipay[df_alipay['C_收支'] != 'close'] 206 | 207 | # 原始数据 208 | origin_data = df_alipay.to_dict(orient='records') 209 | 210 | # 用在预览表格中的数据 211 | table_data = [] 212 | for row_idx, row in df_alipay.iterrows(): 213 | table_data.append({ 214 | 'id': '△', 215 | 'name': row['A_名字'], 216 | 'date': row['B_日期'], 217 | 'type': row['D_分类'] if row['C_收支'] != 'transfer' else '转移', 218 | 'inout': row['C_收支'], 219 | 'amount': row['F_金额'] if row['C_收支'] != 'transfer' or row['A1_源位置'] != 'alipay' else row['A3_金额'], 220 | 'position': row['E_位置'] if row['C_收支'] != 'transfer' else f"{row['A1_源位置']}&&{row['A2_目标位置']}", 221 | 'description': row['G_其他描述'], 222 | 'status': 'to_be_checked', 223 | }) 224 | 225 | ''' 按照Layui文件上传所需形式准备 返回数据 ''' 226 | response_result = { 227 | "code": 0, 228 | "msg": "", 229 | "data": { 230 | "origin_data": str(origin_data), 231 | "table_data": table_data, 232 | } 233 | } 234 | return JsonResponse(response_result) 235 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_upload_db.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | from django.core.files.storage import FileSystemStorage 4 | import os 5 | from pathlib import Path 6 | 7 | 8 | def e_upload_db(request): 9 | upload_file = request.FILES['file'] # 获取文件 10 | # print(upload_file.name) 11 | # print(upload_file.size) 12 | 13 | ''' 文件临时存储 ''' 14 | BASE_DIR = Path(__file__).resolve().parent.parent.parent 15 | fs = FileSystemStorage(location=os.path.join(BASE_DIR, 'static/tmp'), base_url='static/tmp') 16 | 17 | if fs.exists('tmp_data.db'): 18 | fs.delete('tmp_data.db') 19 | fs.save('tmp_data.db', upload_file) 20 | 21 | ''' 数据库内容迁移 ''' 22 | conn = sqlite3.connect('data.db') 23 | cur = conn.cursor() 24 | 25 | conn_tmp = sqlite3.connect('static/tmp/tmp_data.db') 26 | cur_tmp = conn_tmp.cursor() 27 | 28 | # 1、检查旧版数据库表的结构 29 | old_schema = {} 30 | cur_tmp.execute(""" select name from sqlite_master where type='table'; """) 31 | table_name_list = [_[0] for _ in cur_tmp.fetchall()] 32 | for table_name in table_name_list: 33 | cur_tmp.execute(""" pragma table_info('%s') """ % table_name) 34 | col_name_list = [_[1] for _ in cur_tmp.fetchall()] 35 | old_schema[table_name] = col_name_list 36 | # print(old_schema) 37 | 38 | v1_0_0_schema = { 39 | 'select_list': ['ID', 'TEMPLATE', 'TYPE', 'DESC', 'DATA'], 40 | 'money_position': ['POSITION_ID', 'NAME', 'NAME_EN', 'MONEY', 'TYPE', 'ADDITION_INFO', 'HIDDEN'], 41 | 'money_record': ['ID', 'NAME', 'TYPE', 'AMOUNT', 'INOUT', 'POSITION', 'DESCRIPTION', 'DATE_STR', 'FEE'] 42 | } 43 | v1_1_0_schema = { 44 | 'select_list': ['ID', 'TEMPLATE', 'TYPE', 'DESC', 'DATA'], 45 | 'money_position': ['POSITION_ID', 'NAME', 'NAME_EN', 'MONEY', 'TYPE', 'ADDITION_INFO', 'HIDDEN'], 46 | 'money_record': ['ID', 'NAME', 'TYPE', 'AMOUNT', 'INOUT', 'POSITION', 'DESCRIPTION', 'DATE_STR', 'FEE', 'STATUS'] 47 | } 48 | v1_1_3_schema = { 49 | 'select_list': ['ID', 'TEMPLATE', 'TYPE', 'DESC', 'DATA'], 50 | 'money_position': ['POSITION_ID', 'NAME', 'NAME_EN', 'MONEY', 'TYPE', 'ADDITION_INFO', 'HIDDEN'], 51 | 'money_record': ['ID', 'NAME', 'TYPE', 'AMOUNT', 'INOUT', 'POSITION', 'DESCRIPTION', 'DATE_STR', 'FEE', 'STATUS'], 52 | 'money_record_auto_complete': ['PRIORITY', 'PATTERN', 'INOUT', 'TYPE', 'POSITION', 'AMOUNT'], 53 | } 54 | 55 | # 2、匹配某一旧版本的数据库,并执行迁移 56 | if sorted(old_schema.items()) == sorted(v1_0_0_schema.items()): 57 | # 删除现有数据库全部内容 58 | cur.execute(""" delete from select_list; """) 59 | cur.execute(""" delete from money_position; """) 60 | cur.execute(""" delete from money_record; """) 61 | conn.commit() 62 | 63 | # 逐条导入旧数据库数据 64 | cur_tmp.execute(f""" select ID, TEMPLATE, TYPE, DESC, DATA from select_list; """) 65 | for line in cur_tmp.fetchall(): 66 | cur.execute(""" insert into select_list (ID, TEMPLATE, TYPE, DESC, DATA) values (?, ?, ?, ?, ?) 67 | """, (line[0], line[1], line[2], line[3], line[4])) 68 | 69 | cur_tmp.execute(f""" select POSITION_ID, NAME, NAME_EN, MONEY, TYPE, ADDITION_INFO, HIDDEN 70 | from money_position; """) 71 | for line in cur_tmp.fetchall(): 72 | cur.execute(""" insert into money_position (POSITION_ID, NAME, NAME_EN, MONEY, TYPE, 73 | ADDITION_INFO, HIDDEN) values (?, ?, ?, ?, ?, ?, ?) 74 | """, (line[0], line[1], line[2], line[3], line[4], line[5], line[6])) 75 | 76 | cur_tmp.execute(f""" select ID, NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, FEE 77 | from money_record; """) 78 | for line in cur_tmp.fetchall(): 79 | cur.execute(""" insert into money_record (ID, NAME, TYPE, AMOUNT, INOUT, POSITION, 80 | DESCRIPTION, DATE_STR, FEE) values (?, ?, ?, ?, ?, ?, ?, ?, ?) 81 | """, (line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7], line[8])) 82 | conn.commit() 83 | elif sorted(old_schema.items()) == sorted(v1_1_0_schema.items()): 84 | # 删除现有数据库全部内容 85 | cur.execute(""" delete from select_list; """) 86 | cur.execute(""" delete from money_position; """) 87 | cur.execute(""" delete from money_record; """) 88 | conn.commit() 89 | 90 | # 逐条导入旧数据库数据 91 | cur_tmp.execute(f""" select ID, TEMPLATE, TYPE, DESC, DATA from select_list; """) 92 | for line in cur_tmp.fetchall(): 93 | cur.execute(""" insert into select_list (ID, TEMPLATE, TYPE, DESC, DATA) values (?, ?, ?, ?, ?) 94 | """, (line[0], line[1], line[2], line[3], line[4])) 95 | 96 | cur_tmp.execute(f""" select POSITION_ID, NAME, NAME_EN, MONEY, TYPE, ADDITION_INFO, HIDDEN 97 | from money_position; """) 98 | for line in cur_tmp.fetchall(): 99 | cur.execute(""" insert into money_position (POSITION_ID, NAME, NAME_EN, MONEY, TYPE, 100 | ADDITION_INFO, HIDDEN) values (?, ?, ?, ?, ?, ?, ?) 101 | """, (line[0], line[1], line[2], line[3], line[4], line[5], line[6])) 102 | 103 | cur_tmp.execute(f""" select ID, NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, FEE, STATUS 104 | from money_record; """) 105 | for line in cur_tmp.fetchall(): 106 | cur.execute(""" insert into money_record (ID, NAME, TYPE, AMOUNT, INOUT, POSITION, 107 | DESCRIPTION, DATE_STR, FEE, STATUS) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 108 | """, (line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7], line[8], line[9])) 109 | conn.commit() 110 | elif sorted(old_schema.items()) == sorted(v1_1_3_schema.items()): 111 | # 删除现有数据库全部内容 112 | cur.execute(""" delete from select_list; """) 113 | cur.execute(""" delete from money_position; """) 114 | cur.execute(""" delete from money_record; """) 115 | cur.execute(""" delete from money_record_auto_complete; """) 116 | conn.commit() 117 | 118 | # 逐条导入旧数据库数据 119 | cur_tmp.execute(f""" select ID, TEMPLATE, TYPE, DESC, DATA from select_list; """) 120 | for line in cur_tmp.fetchall(): 121 | cur.execute(""" insert into select_list (ID, TEMPLATE, TYPE, DESC, DATA) values (?, ?, ?, ?, ?) 122 | """, (line[0], line[1], line[2], line[3], line[4])) 123 | 124 | cur_tmp.execute(f""" select POSITION_ID, NAME, NAME_EN, MONEY, TYPE, ADDITION_INFO, HIDDEN 125 | from money_position; """) 126 | for line in cur_tmp.fetchall(): 127 | cur.execute(""" insert into money_position (POSITION_ID, NAME, NAME_EN, MONEY, TYPE, 128 | ADDITION_INFO, HIDDEN) values (?, ?, ?, ?, ?, ?, ?) 129 | """, (line[0], line[1], line[2], line[3], line[4], line[5], line[6])) 130 | 131 | cur_tmp.execute(f""" select ID, NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, FEE, STATUS 132 | from money_record; """) 133 | for line in cur_tmp.fetchall(): 134 | cur.execute(""" insert into money_record (ID, NAME, TYPE, AMOUNT, INOUT, POSITION, 135 | DESCRIPTION, DATE_STR, FEE, STATUS) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 136 | """, (line[0], line[1], line[2], line[3], line[4], line[5], line[6], line[7], line[8], line[9])) 137 | 138 | cur_tmp.execute(f""" select PRIORITY, PATTERN, INOUT, TYPE, POSITION, AMOUNT from money_record_auto_complete; """) 139 | for line in cur_tmp.fetchall(): 140 | cur.execute(""" insert into money_record_auto_complete (PRIORITY, PATTERN, INOUT, TYPE, POSITION, AMOUNT) 141 | values (?, ?, ?, ?, ?, ?) 142 | """, (line[0], line[1], line[2], line[3], line[4], line[5])) 143 | conn.commit() 144 | else: 145 | cur_tmp.close() 146 | conn_tmp.close() 147 | cur.close() 148 | conn.close() 149 | raise ValueError("旧版数据库表结构不正确") 150 | 151 | cur_tmp.close() 152 | conn_tmp.close() 153 | cur.close() 154 | conn.close() 155 | 156 | fs.delete('tmp_data.db') # 清除临时文件 157 | 158 | ''' 按照Layui文件上传所需形式准备 返回数据 ''' 159 | response_result = { 160 | "code": 0, 161 | "msg": "", 162 | "data": { 163 | } 164 | } 165 | return JsonResponse(response_result) 166 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/e__money_arrange_function/e_upload_money_records.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | import sqlite3 3 | 4 | 5 | def add_money_record(record_name, record_date, record_inout, record_type, record_position, record_amount, 6 | record_desc): 7 | 8 | """ money_record 信息添加到数据库 """ 9 | conn = sqlite3.connect('data.db') 10 | cur = conn.cursor() 11 | 12 | # 添加记录信息 13 | cur.execute(""" 14 | INSERT INTO money_record (NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, STATUS) 15 | VALUES (?, ?, ?, ?, ?, ?, ?, ?) 16 | """, (record_name, record_type, record_amount, record_inout, record_position, record_desc, 17 | record_date, 'to_be_checked')) 18 | conn.commit() 19 | 20 | ''' money_position 数据库数额更新 ''' 21 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_position) 22 | money_before = float(cur.fetchone()[1]) 23 | 24 | if record_inout == 'in': 25 | money_after = round(money_before + float(record_amount), 2) 26 | elif record_inout == 'out': 27 | money_after = round(money_before - float(record_amount), 2) 28 | 29 | cur.execute(""" 30 | UPDATE money_position SET 31 | MONEY = '%s' 32 | WHERE NAME_EN = '%s' 33 | """ % (str(money_after), record_position)) 34 | conn.commit() 35 | 36 | # 关闭数据库连接 37 | cur.close() 38 | conn.close() 39 | return True 40 | 41 | 42 | def add_money_transfer_record(record_name, record_date, record_from_position, record_to_position, 43 | record_amount, record_fee, record_desc): 44 | 45 | """ money_transfer_record 信息添加到数据库 """ 46 | conn = sqlite3.connect('data.db') 47 | cur = conn.cursor() 48 | 49 | # 添加记录信息 50 | cur.execute(""" 51 | INSERT INTO money_record (NAME, TYPE, AMOUNT, INOUT, POSITION, DESCRIPTION, DATE_STR, FEE, STATUS) 52 | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) 53 | """, (record_name, '转移', record_amount, 'transfer', record_from_position + '&&' + record_to_position, 54 | record_desc, record_date, record_fee, 'to_be_checked')) 55 | conn.commit() 56 | 57 | ''' money_position 数据库数额更新(from账户) ''' 58 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_from_position) 59 | money_before = float(cur.fetchone()[1]) 60 | money_after = round(money_before - float(record_amount) - float(record_fee), 2) 61 | cur.execute(""" 62 | UPDATE money_position SET 63 | MONEY = '%s' 64 | WHERE NAME_EN = '%s'; 65 | """ % (str(money_after), record_from_position)) 66 | conn.commit() 67 | 68 | ''' money_position 数据库数额更新(to账户) ''' 69 | cur.execute(""" select NAME_EN, MONEY from money_position where NAME_EN = '%s'; """ % record_to_position) 70 | money_before = float(cur.fetchone()[1]) 71 | money_after = round(money_before + float(record_amount), 2) 72 | cur.execute(""" 73 | UPDATE money_position SET 74 | MONEY = '%s' 75 | WHERE NAME_EN = '%s'; 76 | """ % (str(money_after), record_to_position)) 77 | conn.commit() 78 | 79 | # 关闭数据库连接 80 | cur.close() 81 | conn.close() 82 | return True 83 | 84 | 85 | def e_upload_money_records(request): 86 | upload_origin_data = request.POST.get('upload_origin_data') 87 | 88 | ''' 检查是否有 unknown 位置,如果数据库中没有,则添加 ''' 89 | conn = sqlite3.connect('data.db') 90 | cur = conn.cursor() 91 | 92 | cur.execute(""" select NAME, NAME_EN from money_position; """) 93 | if 'unknown' not in [_[1] for _ in cur.fetchall()]: 94 | cur.execute(""" insert into money_position (NAME, NAME_EN, TYPE, MONEY, HIDDEN) 95 | values (?, ?, ?, ?, ?); """, ('待定', 'unknown', 'unknown', '0.0', 'no')) 96 | conn.commit() 97 | 98 | cur.execute(""" select DATA from select_list where TEMPLATE = '%s' and DESC = '%s'; 99 | """ % ('04_money_arrange', 'income_category')) 100 | if '待定' not in [_[0] for _ in cur.fetchall()]: 101 | cur.execute(""" insert into select_list (TEMPLATE, TYPE, DESC, DATA) 102 | values (?, ?, ?, ?)""", ('04_money_arrange', 'select', 'income_category', '待定')) 103 | conn.commit() 104 | 105 | cur.execute(""" select DATA from select_list where TEMPLATE = '%s' and DESC = '%s'; 106 | """ % ('04_money_arrange', 'outcome_category')) 107 | if '待定' not in [_[0] for _ in cur.fetchall()]: 108 | cur.execute(""" insert into select_list (TEMPLATE, TYPE, DESC, DATA) 109 | values (?, ?, ?, ?)""", ('04_money_arrange', 'select', 'outcome_category', '待定')) 110 | conn.commit() 111 | 112 | # 关闭数据库连接 113 | cur.close() 114 | conn.close() 115 | 116 | # print(upload_origin_data) 117 | for row in reversed(eval(upload_origin_data)): 118 | if row['C_收支'] == 'transfer': 119 | if row['A1_源位置'] != 'wechat': 120 | add_money_transfer_record(row['A_名字'], row['B_日期'], row['A1_源位置'], row['A2_目标位置'], 121 | row['F_金额'], 0, row['G_其他描述']) 122 | else: 123 | add_money_transfer_record(row['A_名字'], row['B_日期'], row['A1_源位置'], row['A2_目标位置'], 124 | row['A3_金额'], 0, row['G_其他描述']) 125 | else: 126 | add_money_record(row['A_名字'], row['B_日期'], row['C_收支'], row['D_分类'], row['E_位置'], 127 | row['F_金额'], row['G_其他描述']) 128 | 129 | return HttpResponse('账单上传成功!') 130 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/middlewares/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/P4_MoneyArrangeModule/middlewares/__init__.py -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/middlewares/login_required.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import redirect 2 | from django.conf import settings 3 | 4 | 5 | class LoginRequiredMiddleware: 6 | def __init__(self, get_response): 7 | self.get_response = get_response 8 | # One-time configuration and initialization. 9 | self.login_url = settings.LOGIN_URL 10 | self.open_urls = [self.login_url] + getattr(settings, 'OPEN_URLS', []) 11 | 12 | def __call__(self, request): 13 | if not request.user.is_authenticated and request.path not in self.open_urls: 14 | return redirect(self.login_url + '?next=' + request.path) 15 | response = self.get_response(request) 16 | return response 17 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for P4_MoneyArrangeModule project. 3 | 4 | Generated by 'django-admin startproject' using Django 3.2.6. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/3.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import os 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'django-insecure-e%ja5@j2xqjp)^%%=(v7ra&q-kp=3@up3p!2yue19$b50kb%mf' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ] 41 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | # 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | # 'P4_MoneyArrangeModule.middlewares.login_required.LoginRequiredMiddleware', 51 | ] 52 | 53 | ROOT_URLCONF = 'P4_MoneyArrangeModule.urls' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [BASE_DIR / 'templates'] 59 | , 60 | 'APP_DIRS': True, 61 | 'OPTIONS': { 62 | 'context_processors': [ 63 | 'django.template.context_processors.debug', 64 | 'django.template.context_processors.request', 65 | 'django.contrib.auth.context_processors.auth', 66 | 'django.contrib.messages.context_processors.messages', 67 | ], 68 | }, 69 | }, 70 | ] 71 | 72 | WSGI_APPLICATION = 'P4_MoneyArrangeModule.wsgi.application' 73 | 74 | 75 | # Database 76 | # https://docs.djangoproject.com/en/3.2/ref/settings/#databases 77 | 78 | DATABASES = { 79 | 'default': { 80 | 'ENGINE': 'django.db.backends.sqlite3', 81 | 'NAME': BASE_DIR / 'db.sqlite3', 82 | } 83 | } 84 | 85 | 86 | # Password validation 87 | # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators 88 | 89 | AUTH_PASSWORD_VALIDATORS = [ 90 | { 91 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 92 | }, 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 101 | }, 102 | ] 103 | 104 | 105 | # Internationalization 106 | # https://docs.djangoproject.com/en/3.2/topics/i18n/ 107 | 108 | LANGUAGE_CODE = 'en-us' 109 | 110 | TIME_ZONE = 'UTC' 111 | 112 | USE_I18N = True 113 | 114 | USE_L10N = True 115 | 116 | USE_TZ = True 117 | 118 | 119 | # Static files (CSS, JavaScript, Images) 120 | # https://docs.djangoproject.com/en/3.2/howto/static-files/ 121 | 122 | STATIC_URL = '/static/' 123 | STATICFILES_DIRS = [ 124 | os.path.join(BASE_DIR, "static"), 125 | ] 126 | 127 | # Default primary key field type 128 | # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field 129 | 130 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 131 | 132 | LOGIN_URL = '/login/' # 这个路径需要根据你网站的实际登陆地址来设置,也就是登录路由 133 | OPEN_URLS = [] # 允许未登录访问的路由 134 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/theme_package/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/P4_MoneyArrangeModule/theme_package/__init__.py -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/theme_package/common_theme.py: -------------------------------------------------------------------------------- 1 | """ 主题颜色配置文件,包含以下可选配置: 2 | 1、default_theme: 默认配置 3 | 2、dark_theme: 暗色主题 4 | """ 5 | 6 | 7 | class CommonTheme: 8 | 9 | default_theme = { 10 | 'main_top': { 11 | 'bgcolor': '#333', 12 | 'fontcolor': '#000', 13 | 'checkbox_color': '#fff', 14 | }, 15 | 'main_body': { 16 | 'bgcolor': '#eee', 17 | 'fontcolor': '#000', 18 | 'item_box': { 19 | 'bgcolor': '#fff', 20 | 'fontcolor': '#000', 21 | }, 22 | }, 23 | 'main_btn': { 24 | 'bgcolor': '#3276B1', 25 | 'border': '0px', 26 | }, 27 | 'layer': { 28 | 'bgcolor': '#fff', 29 | 'border_bottom_color': '#F0F0F0', 30 | 'title_color': '#333', 31 | }, 32 | 'main_right': { 33 | 'bgcolor': '#aaa', 34 | 'fontcolor': '#000', 35 | 'category_section_bgcolor': '#B0C4DE', 36 | 'category_section_bordercolor': '#E9ECEF', 37 | }, 38 | 'mindmap': { 39 | 'line_color': '#555', 40 | }, 41 | 'table': { 42 | 'header_bgcolor': '#FAFAFA', 43 | 'page_div_bgcolor': '', 44 | 'bgcolor': '#fff', 45 | 'fontcolor': '#666' 46 | }, 47 | 'blockquote': { 48 | 'bgcolor': '#fff', 49 | 'fontcolor': '#000' 50 | }, 51 | 'calendar': { 52 | 'layer': { 53 | 'header_bgcolor': '#4387C2', 54 | 'body_bgcolor': '#eee' 55 | }, 56 | 'now_day_bgcolor': 'rgba(255, 220, 40, 0.15)', 57 | 'list_grid': { 58 | 'header_bgcolor': '#3276B1', 59 | 'hover_bgcolor': '#5B88AE' 60 | } 61 | }, 62 | } 63 | 64 | dark_theme = { 65 | 'main_top': { 66 | 'bgcolor': '#3C3F41', 67 | 'fontcolor': '#eff', 68 | 'checkbox_color': '#2B2B2B', 69 | }, 70 | 'main_body': { 71 | 'bgcolor': '#2B2B2B', 72 | 'fontcolor': '#cdd', 73 | 'item_box': { 74 | 'bgcolor': '#3D3E40', 75 | 'fontcolor': '#bbb', 76 | } 77 | }, 78 | 'main_btn': { 79 | 'bgcolor': '#3276B1', 80 | 'border': '0px', 81 | }, 82 | 'layer': { 83 | 'bgcolor': '#2B2B2B', 84 | 'border_bottom_color': '#2B2B2B', 85 | 'title_color': '#eff' 86 | }, 87 | 'main_right': { 88 | 'bgcolor': '#3C3F41', 89 | 'fontcolor': '#eff', 90 | 'category_section_bgcolor': '#3C3F41', 91 | 'category_section_bordercolor': '#E9ECEF', 92 | }, 93 | 'mindmap': { 94 | 'line_color': '#888', 95 | }, 96 | 'table': { 97 | 'header_bgcolor': '#3D3E40', 98 | 'page_div_bgcolor': '#3D3E40', 99 | 'bgcolor': '#2B2B2B', 100 | 'fontcolor': '#cdd' 101 | }, 102 | 'blockquote': { 103 | 'bgcolor': '#1C1B22', 104 | 'fontcolor': '#cdd' 105 | }, 106 | 'calendar': { 107 | 'layer': { 108 | 'header_bgcolor': '#3C3F41', 109 | 'body_bgcolor': '#2B2B2B' 110 | }, 111 | 'now_day_bgcolor': 'rgba(32, 78, 138, 0.25)', 112 | 'list_grid': { 113 | 'header_bgcolor': '#181A1F', 114 | 'hover_bgcolor': '#3C3F41', 115 | } 116 | }, 117 | } 118 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/urls.py: -------------------------------------------------------------------------------- 1 | """P4_MoneyArrangeModule URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/3.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.urls import path 17 | from django.views.static import serve 18 | from django.conf import settings 19 | from django.urls import re_path 20 | from django.views.generic.base import RedirectView 21 | 22 | from . import views 23 | 24 | # self define function locations 25 | from .e__money_arrange_function import e_get_money_positions, e_get_money_record_form, e_search_money_record_form, \ 26 | e_add_money_record, e_add_money_transfer_record, e_money_get_chart01_data, e_money_get_chart02_data, \ 27 | e_money_get_chart03_data, e_money_get_chart_history01_data, e_get_money_record, e_modify_money_record, \ 28 | e_delete_money_record, e_modify_money_transfer_record, e_delete_money_transfer_record, e_upload_bill, \ 29 | e_upload_money_records, e_get_auto_complete_rules, e_upload_db 30 | 31 | from .y__common_function import y_get_select_list, y_login_view 32 | 33 | url_names = [ 34 | # 04_money_arrange_module 35 | ('e_get_money_positions', e_get_money_positions.e_get_money_positions), 36 | ('e_get_money_record_form', e_get_money_record_form.e_get_money_record_form), 37 | ('e_search_money_record_form', e_search_money_record_form.e_search_money_record_form), 38 | ('e_add_money_record', e_add_money_record.e_add_money_record), 39 | ('e_get_money_record', e_get_money_record.e_get_money_record), 40 | ('e_modify_money_record', e_modify_money_record.e_modify_money_record), 41 | ('e_modify_money_transfer_record', e_modify_money_transfer_record.e_modify_money_transfer_record), 42 | ('e_delete_money_record', e_delete_money_record.e_delete_money_record), 43 | ('e_delete_money_transfer_record', e_delete_money_transfer_record.e_delete_money_transfer_record), 44 | ('e_add_money_transfer_record', e_add_money_transfer_record.e_add_money_transfer_record), 45 | ('e_money_get_chart01_data', e_money_get_chart01_data.e_money_get_chart01_data), 46 | ('e_money_get_chart02_data', e_money_get_chart02_data.e_money_get_chart02_data), 47 | ('e_money_get_chart03_data', e_money_get_chart03_data.e_money_get_chart03_data), 48 | ('e_money_get_chart_history01_data', e_money_get_chart_history01_data.e_money_get_chart_history01_data), 49 | ('e_upload_bill', e_upload_bill.e_upload_bill), 50 | ('e_upload_money_records', e_upload_money_records.e_upload_money_records), 51 | ('e_get_auto_complete_rules', e_get_auto_complete_rules.e_get_auto_complete_rules), 52 | ('e_upload_db', e_upload_db.e_upload_db), 53 | 54 | # common部分的函数 55 | ('y_get_select_list', y_get_select_list.y_get_select_list), 56 | ] 57 | 58 | urlpatterns = [ 59 | re_path(r'^media/(?P.*)', serve, {"document_root": settings.MEDIA_ROOT}), # 允许 /media/ 路径访问 60 | path('favicon.ico', RedirectView.as_view(url=r'static/images/favicon.ico')), # 网页图标 61 | path('login/', y_login_view.y_login_view, name='login'), # 登录功能 62 | path('logout/', y_login_view.y_logout_view, name='logout'), # 登出功能 63 | 64 | path('', views.money_arrange), 65 | path('04money_arrange/', views.money_arrange), 66 | path('04money_arrange_update/', views.money_arrange_update), 67 | ] + [path(url_name+'/', url_view, name=url_name) for url_name, url_view in url_names] 68 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | ''' 读取主题配置文件 ''' 4 | from .theme_package.common_theme import CommonTheme 5 | 6 | 7 | # 04_money_arrange 8 | def money_arrange(request): 9 | return render(request, '04_money_arrange_module/04_money_arrange.html', {'theme': CommonTheme.dark_theme}) 10 | 11 | 12 | # 04_money_arrange_update 13 | def money_arrange_update(request): 14 | return render(request, '04_money_arrange_module/04_money_arrange_update.html', {'theme': CommonTheme.dark_theme}) 15 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for P4_MoneyArrangeModule project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'P4_MoneyArrangeModule.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/y__common_function/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/P4_MoneyArrangeModule/y__common_function/__init__.py -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/y__common_function/y_get_select_list.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | import sqlite3 3 | 4 | 5 | def y_get_select_list(request): 6 | """ 获取 特定选择框(select框) 的选项列表 """ 7 | 8 | template = request.GET.get('template') 9 | l_type = request.GET.get('type') 10 | desc = request.GET.get('desc') 11 | 12 | # 创建数据库连接,返回连接对象conn,返回游标cur 13 | conn = sqlite3.connect('data.db') 14 | cur = conn.cursor() 15 | 16 | select_list = [] 17 | cur.execute(""" 18 | select DATA from select_list 19 | where TEMPLATE = '%s' and TYPE = '%s' and DESC = '%s'; 20 | """ % (template, l_type, desc)) 21 | 22 | for item in cur.fetchall(): 23 | select_list.append(item[0]) 24 | 25 | # 关闭数据库连接 26 | cur.close() 27 | conn.close() 28 | 29 | return JsonResponse(select_list, safe=False) # 传输的为列表类型 30 | -------------------------------------------------------------------------------- /P4_MoneyArrangeModule/y__common_function/y_login_view.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.contrib.auth import authenticate, login, logout 3 | from django.contrib import messages 4 | from django.http import HttpResponse 5 | 6 | 7 | def y_login_view(request): 8 | if request.method == "POST": 9 | username = request.POST.get('username') 10 | password = request.POST.get('password') 11 | user = authenticate(request, username=username, password=password) 12 | if user is not None: 13 | login(request, user) 14 | next_url = request.POST.get('next', '/04money_arrange/') # 如果没有next参数,默认重定向到首页 15 | return redirect(next_url) 16 | else: 17 | # 如果认证失败,可以返回一个错误消息 18 | messages.error(request, "用户名或密码错误") 19 | # 对于GET请求,显示登录表单 20 | return render(request, 'common/login_page.html') 21 | 22 | 23 | def y_logout_view(request): 24 | logout(request) 25 | return HttpResponse('登出成功!') 26 | -------------------------------------------------------------------------------- /README.assets/image-20231107143850217.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/README.assets/image-20231107143850217.png -------------------------------------------------------------------------------- /README.assets/image-20231109183716907.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/README.assets/image-20231109183716907.png -------------------------------------------------------------------------------- /README.assets/image-20231222104937335.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/README.assets/image-20231222104937335.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # P4_MoneyArrangeModule 2 | 极简个人资金管理系统。 3 | 4 | 【愿景介绍视频】https://www.bilibili.com/video/BV1Rc411X7FK 5 | 6 | 【安装说明视频】https://www.bilibili.com/video/BV1hQ4y137QG 7 | 8 | 【`Docker`版本】https://hub.docker.com/repository/docker/nicholascool/p4_money_arrange_module/general 9 | 10 | 【详细文档】更多文档,如`Q&A`、`To_Be_Added`等,详见`wiki`页面。 11 | 12 | ## 一、运行环境和配置说明 13 | 14 | ### 1.1、运行环境 15 | 16 | - `python 3.8.7` 17 | - `Django 3.2.6` 18 | - `FireFox`浏览器(或任意其他浏览器) 19 | 20 | 【注】`python`版本和`Django`版本没有非常强制的要求,相近的版本一般也能正常运行。 21 | 22 | ### 1.2、下载和配置教程 23 | 24 | 根目录的`install_process`文件夹中给出了所有需要的安装包,也可以自行在相应官网下载。 25 | 26 | #### 1.2.1、下载仓库到本地 27 | 28 | 直接克隆仓库到本地,或者在`release`页面下载均可。 29 | 30 | #### 1.2.2、安装`python` 31 | 32 | > 如果已经安装过`python`,可以跳过此步骤。 33 | 34 | 如果没有安装过,可以直接双击`install_process/python-3.8.7-amd64.exe`文件进行安装。 35 | 36 | 也可以自行在`python`官网([https://www.python.org/](https://www.python.org/))下载安装包进行安装。 37 | 38 | 注意在安装的步骤中,选中`Add python to path`选项,该选项会将`python`路径添加到环境变量中。 39 | 40 | #### 1.2.3、安装`django`包和其他依赖 41 | 42 | > 如果已经安装过`django`和其他依赖(详见`install_process/requirements.txt`),可以跳过此步骤。 43 | 44 | 如果没有安装过,可以直接双击`install_process/installPackages.bat`脚本进行`django`及其他依赖库包的安装。 45 | 46 | 也可以自行输入命令安装。 47 | 48 | #### 1.2.4、安装`SQliteStudio` 49 | 50 | 安装`SQliteStudio`是为了让我们可以通过图形界面修改数据库中的数据。 51 | 52 | `windows`系统可以直接双击`install_process/SQLiteStudio-3.4.4-windows-x64-installer.exe`安装。 53 | 54 | 也可以自行在`sqlitestudio`官网([https://sqlitestudio.pl/](https://sqlitestudio.pl/))下载安装包进行安装。 55 | 56 | #### 1.2.5、浏览器 57 | 58 | 我偏向于使用火狐浏览器并切暗色环境,这样界面看起来比较好看,也可以和常用的`Chrome`浏览器开其他网页分隔开。 59 | 60 | 当然,你也可以使用任何你喜欢的浏览器。 61 | 62 | 如果你使用其他浏览器,请修改`showPages.bat`脚本中的`firefox.exe`。 63 | 64 | ### 1.3、运行程序 65 | 66 | 直接双击`runProject.vbs`脚本,即可运行程序,并自动在浏览器中打开。 67 | 68 | ## 二、初始化设置 69 | 70 | ### 2.2.1、`money_position`表 71 | 72 | 该表位于数据库中,记录了资金存放位置的信息。初始时将资金位置信息填入此即可。 73 | 74 | image-20231107143850217 75 | 76 | ### 2.2.2、`select_list`表 77 | 78 | 该表记录了资金的收入和支出的类型。初始时将收支分类信息填入此即可。 79 | 80 | image-20231109183716907 81 | 82 | ### 2.2.3、`money_record_auto_complete`表 83 | 84 | 该表定义了添加记录时自动填充功能的规则,可根据需要进行修改。 85 | 86 | > 【注】规则将按照`PRIORITY`从小到大的优先级顺序匹配,不合法的规则将被忽略。 87 | 88 | image-20231222104937335 89 | 90 | ## 三、结语 91 | 92 | 文档不断完善中,如果有任何问题欢迎联系,或通过`pull request`共同完善。`Have a nice day!` 93 | 94 | -------------------------------------------------------------------------------- /data.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/data.db -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/db.sqlite3 -------------------------------------------------------------------------------- /install_process/Firefox-latest.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/install_process/Firefox-latest.exe -------------------------------------------------------------------------------- /install_process/SQLiteStudio-3.4.4-windows-x64-installer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/install_process/SQLiteStudio-3.4.4-windows-x64-installer.exe -------------------------------------------------------------------------------- /install_process/installPackages.bat: -------------------------------------------------------------------------------- 1 | pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple/ 2 | pip install -r requirements.txt 3 | pause -------------------------------------------------------------------------------- /install_process/python-3.8.7-amd64.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/install_process/python-3.8.7-amd64.exe -------------------------------------------------------------------------------- /install_process/requirements.txt: -------------------------------------------------------------------------------- 1 | django==3.2.6 2 | pandas==1.5.0 -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'P4_MoneyArrangeModule.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /runProject.vbs: -------------------------------------------------------------------------------- 1 | set ws=WScript.CreateObject("WScript.Shell") 2 | ws.Run "runServer.bat",1 3 | 4 | set ks=WScript.CreateObject("WScript.Shell") 5 | ks.Run "showPages.bat",0 -------------------------------------------------------------------------------- /runServer.bat: -------------------------------------------------------------------------------- 1 | python manage.py runserver 0.0.0.0:9008 -------------------------------------------------------------------------------- /runServer.vbs: -------------------------------------------------------------------------------- 1 | set ws=WScript.CreateObject("WScript.Shell") 2 | ws.Run "runServer.bat",0 -------------------------------------------------------------------------------- /showPages.bat: -------------------------------------------------------------------------------- 1 | start firefox.exe http://127.0.0.1:9008/04money_arrange/ -------------------------------------------------------------------------------- /showPages.vbs: -------------------------------------------------------------------------------- 1 | set ks=WScript.CreateObject("WScript.Shell") 2 | ks.Run "showPages.bat",0 -------------------------------------------------------------------------------- /static/custome_css/layer_style.css: -------------------------------------------------------------------------------- 1 | /* LayUI Layer样式 */ 2 | 3 | /* layer总体样式 */ 4 | .layui-layer { 5 | background-color: #2B2B2B; 6 | } 7 | 8 | .layui-layer-title { 9 | border-bottom: 1px solid #2B2B2B; 10 | color: #eff; 11 | } 12 | 13 | /* layer的右上角关闭按钮样式 */ 14 | .layui-layer-ico { 15 | padding-top: 2px; 16 | } 17 | 18 | .layui-layer-setwin .layui-layer-close1 { 19 | background-position: -180px -31.5px; 20 | cursor: pointer; 21 | } 22 | 23 | .layui-layer-setwin a { 24 | width: 30px; 25 | height: 31px; 26 | margin-top: -7px; 27 | } 28 | 29 | /* select的下拉框样式 */ 30 | .layui-form-select dl { 31 | max-width: 100%; 32 | color: black; 33 | margin-bottom: 0; 34 | /*max-height: 260px;*/ 35 | } 36 | 37 | /*.layui-form-select dd {*/ 38 | /* margin-bottom: 0;*/ 39 | /*}*/ 40 | 41 | /* select框字体样式 */ 42 | .layui-disabled, .layui-disabled:hover { 43 | color: #000 !important; 44 | cursor: not-allowed !important; 45 | background-color: #ccc; 46 | } -------------------------------------------------------------------------------- /static/custome_css/layui_form_style.css: -------------------------------------------------------------------------------- 1 | /* input 框 disabled 时背景色 */ 2 | .layui-input:disabled{ 3 | background-color: #ccc !important; 4 | } 5 | 6 | /* textarea 框 disabled 时背景色 */ 7 | .layui-textarea:disabled{ 8 | background-color: #ccc !important; 9 | } 10 | 11 | /* select 框 选项的颜色 */ 12 | .layui-form-select dl dd{ 13 | color: black; 14 | } 15 | 16 | /* select 框 disabled 时背景色 */ 17 | .layui-select-disabled .layui-disabled{ 18 | background-color: #ddd; 19 | } 20 | 21 | /* layui组件 disabled 时文字颜色 */ 22 | .layui-disabled, .layui-disabled:hover{ 23 | color: black !important; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /static/custome_css/layui_table_style.css: -------------------------------------------------------------------------------- 1 | .layui-btn-xs { 2 | height: 26px; 3 | line-height: 26px; 4 | font-size: 14px; 5 | } 6 | 7 | .layui-table-page { 8 | background-color: #3D3E40; 9 | } 10 | 11 | .layui-table { 12 | background-color: #2B2B2B; 13 | color: #cdd; 14 | } 15 | 16 | .layui-table-tips-main, 17 | .layui-table-grid-down { 18 | background-color: #2B2B2B; 19 | color: #cdd; 20 | } 21 | 22 | .layui-table-grid-down:hover { 23 | background-color: #2B2B2B; 24 | } 25 | 26 | .layui-table-header { 27 | background-color: #3D3E40; 28 | } 29 | 30 | .layui-table tbody tr:hover, 31 | .layui-table thead tr, 32 | .layui-table-click, 33 | .layui-table-header, 34 | .layui-table-hover, 35 | .layui-table-mend, 36 | .layui-table-patch, 37 | .layui-table-tool, 38 | .layui-table-total, 39 | .layui-table-total tr, 40 | .layui-table[lay-even] tr:nth-child(2n) { 41 | background-color: #3D3E40; 42 | } 43 | 44 | .layui-table-page .layui-laypage span { 45 | color: #eff; 46 | } 47 | 48 | .layui-table-page a { 49 | color: #eff; 50 | } -------------------------------------------------------------------------------- /static/demo/alipay_record_20231220_163331_示例.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/demo/alipay_record_20231220_163331_示例.csv -------------------------------------------------------------------------------- /static/demo/微信支付账单(20231204-20231220)_示例.csv: -------------------------------------------------------------------------------- 1 | 微信支付账单明细,,,,,,,, 2 | 微信昵称:[xxx],,,,,,,, 3 | 起始时间:[2023-12-04 00:00:00] 终止时间:[2023-12-20 12:53:40],,,,,,,, 4 | 导出类型:[全部],,,,,,,, 5 | 导出时间:[2023-12-20 12:53:55],,,,,,,, 6 | ,,,,,,,, 7 | 共37笔记录,,,,,,,, 8 | 收入:3笔 2016.50元,,,,,,,, 9 | 支出:34笔 1090.61元,,,,,,,, 10 | 中性交易:0笔 0.00元,,,,,,,, 11 | 注:,,,,,,,, 12 | 1. 充值/提现/理财通购买/零钱通存取/信用卡还款等交易,将计入中性交易,,,,,,,, 13 | 2. 本明细仅展示当前账单中的交易,不包括已删除的记录,,,,,,,, 14 | 3. 本明细仅供个人对账使用,,,,,,,, 15 | ,,,,,,,, 16 | ----------------------微信支付账单明细列表--------------------,,,,,,,, 17 | 交易时间,交易类型,交易对方,商品,收/支,金额(元),支付方式,当前状态,交易单号,商户单号,备注 18 | 2023-12-19 11:48:00,商户消费,缙云烧饼(xxx店),"缙云烧饼(xxxx店)",支出,¥6.00,零钱,支付成功,xxxx ,xxxx ,"/" 19 | 2023-12-19 11:45:09,商户消费,十足,"xxxxxx店",支出,¥5.00,零钱,支付成功,xxxx ,xxxx ,"/" 20 | 2023-12-17 22:27:36,商户消费,滴滴出行,"特惠快车-胡师傅-12月17日行程",支出,¥8.91,零钱,支付成功,xxxx ,xxxx ,"/" 21 | 2023-12-17 20:00:43,商户消费,川味1号怀旧火锅xxx店,"川味1号怀旧火锅xxxx店-xxxx",支出,¥28.00,零钱,支付成功,xxxx ,xxxx ,"/" 22 | 2023-12-17 08:58:27,转账,李·xxxx,"转账备注:微信转账",支出,¥40.00,零钱,对方已收钱,xxxx ,xxxx ,"/" 23 | 2023-12-16 16:03:10,转账,xxx,"转账备注:生活费",收入,¥2000.00,/,已存入零钱,xxxx ,/ ,"/" 24 | 2023-12-16 12:08:06,微信红包,某位xxxx,"/",收入,¥13.50,/,已存入零钱,xxxx ,xxxx ,"/" 25 | 2023-12-15 23:56:01,群收款,xxxx,"/",支出,¥50.00,零钱,支付成功,xxxx ,xxxx ,"/" 26 | 2023-12-14 22:20:08,商户消费,肯德基,"KFC_xxxx",支出,¥12.50,零钱,支付成功,xxxx ,xxxx ,"/" 27 | 2023-12-13 18:47:36,商户消费,罗森,"xxxx店",支出,¥13.00,零钱,支付成功,xxxx ,xxxx ,"/" 28 | 2023-12-13 17:46:46,商户消费,老鸭粉丝汤包(xxxx店),"美团收银xxxxx",支出,¥22.00,零钱,支付成功,xxxx ,xxxx ,"/" 29 | 2023-12-13 17:04:17,其他,bilibili福利红包提现,"/",收入,¥3.00,/,已到账,xxxx ,xxxx ,"bilibili福利红包提现" 30 | 2023-12-12 20:16:40,商户消费,达美乐,"Dominosxxxx",支出,¥36.00,零钱,支付成功,xxxx ,xxxx ,"/" 31 | 2023-12-12 12:32:42,商户消费,LAWSON,"LAWSONxxx店消费",支出,¥30.30,零钱,支付成功,xxxx ,xxxx ,"/" 32 | 2023-12-07 17:52:42,商户消费,LAWSON,"LAWSONxxxx店消费",支出,¥29.30,零钱,支付成功,xxxx ,xxxx ,"/" 33 | -------------------------------------------------------------------------------- /static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/images/favicon.ico -------------------------------------------------------------------------------- /static/images/login_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/images/login_bg.jpg -------------------------------------------------------------------------------- /static/plugins/bootstrap-5.0.2-dist/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | @media (prefers-reduced-motion: no-preference) { 15 | :root { 16 | scroll-behavior: smooth; 17 | } 18 | } 19 | 20 | body { 21 | margin: 0; 22 | font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 23 | font-size: 1rem; 24 | font-weight: 400; 25 | line-height: 1.5; 26 | color: #212529; 27 | background-color: #fff; 28 | -webkit-text-size-adjust: 100%; 29 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 30 | } 31 | 32 | hr { 33 | margin: 1rem 0; 34 | color: inherit; 35 | background-color: currentColor; 36 | border: 0; 37 | opacity: 0.25; 38 | } 39 | 40 | hr:not([size]) { 41 | height: 1px; 42 | } 43 | 44 | h6, h5, h4, h3, h2, h1 { 45 | margin-top: 0; 46 | margin-bottom: 0.5rem; 47 | font-weight: 500; 48 | line-height: 1.2; 49 | } 50 | 51 | h1 { 52 | font-size: calc(1.375rem + 1.5vw); 53 | } 54 | @media (min-width: 1200px) { 55 | h1 { 56 | font-size: 2.5rem; 57 | } 58 | } 59 | 60 | h2 { 61 | font-size: calc(1.325rem + 0.9vw); 62 | } 63 | @media (min-width: 1200px) { 64 | h2 { 65 | font-size: 2rem; 66 | } 67 | } 68 | 69 | h3 { 70 | font-size: calc(1.3rem + 0.6vw); 71 | } 72 | @media (min-width: 1200px) { 73 | h3 { 74 | font-size: 1.75rem; 75 | } 76 | } 77 | 78 | h4 { 79 | font-size: calc(1.275rem + 0.3vw); 80 | } 81 | @media (min-width: 1200px) { 82 | h4 { 83 | font-size: 1.5rem; 84 | } 85 | } 86 | 87 | h5 { 88 | font-size: 1.25rem; 89 | } 90 | 91 | h6 { 92 | font-size: 1rem; 93 | } 94 | 95 | p { 96 | margin-top: 0; 97 | margin-bottom: 1rem; 98 | } 99 | 100 | abbr[title], 101 | abbr[data-bs-original-title] { 102 | -webkit-text-decoration: underline dotted; 103 | text-decoration: underline dotted; 104 | cursor: help; 105 | -webkit-text-decoration-skip-ink: none; 106 | text-decoration-skip-ink: none; 107 | } 108 | 109 | address { 110 | margin-bottom: 1rem; 111 | font-style: normal; 112 | line-height: inherit; 113 | } 114 | 115 | ol, 116 | ul { 117 | padding-left: 2rem; 118 | } 119 | 120 | ol, 121 | ul, 122 | dl { 123 | margin-top: 0; 124 | margin-bottom: 1rem; 125 | } 126 | 127 | ol ol, 128 | ul ul, 129 | ol ul, 130 | ul ol { 131 | margin-bottom: 0; 132 | } 133 | 134 | dt { 135 | font-weight: 700; 136 | } 137 | 138 | dd { 139 | margin-bottom: 0.5rem; 140 | margin-left: 0; 141 | } 142 | 143 | blockquote { 144 | margin: 0 0 1rem; 145 | } 146 | 147 | b, 148 | strong { 149 | font-weight: bolder; 150 | } 151 | 152 | small { 153 | font-size: 0.875em; 154 | } 155 | 156 | mark { 157 | padding: 0.2em; 158 | background-color: #fcf8e3; 159 | } 160 | 161 | sub, 162 | sup { 163 | position: relative; 164 | font-size: 0.75em; 165 | line-height: 0; 166 | vertical-align: baseline; 167 | } 168 | 169 | sub { 170 | bottom: -0.25em; 171 | } 172 | 173 | sup { 174 | top: -0.5em; 175 | } 176 | 177 | a { 178 | color: #0d6efd; 179 | text-decoration: underline; 180 | } 181 | a:hover { 182 | color: #0a58ca; 183 | } 184 | 185 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 186 | color: inherit; 187 | text-decoration: none; 188 | } 189 | 190 | pre, 191 | code, 192 | kbd, 193 | samp { 194 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 195 | font-size: 1em; 196 | direction: ltr /* rtl:ignore */; 197 | unicode-bidi: bidi-override; 198 | } 199 | 200 | pre { 201 | display: block; 202 | margin-top: 0; 203 | margin-bottom: 1rem; 204 | overflow: auto; 205 | font-size: 0.875em; 206 | } 207 | pre code { 208 | font-size: inherit; 209 | color: inherit; 210 | word-break: normal; 211 | } 212 | 213 | code { 214 | font-size: 0.875em; 215 | color: #d63384; 216 | word-wrap: break-word; 217 | } 218 | a > code { 219 | color: inherit; 220 | } 221 | 222 | kbd { 223 | padding: 0.2rem 0.4rem; 224 | font-size: 0.875em; 225 | color: #fff; 226 | background-color: #212529; 227 | border-radius: 0.2rem; 228 | } 229 | kbd kbd { 230 | padding: 0; 231 | font-size: 1em; 232 | font-weight: 700; 233 | } 234 | 235 | figure { 236 | margin: 0 0 1rem; 237 | } 238 | 239 | img, 240 | svg { 241 | vertical-align: middle; 242 | } 243 | 244 | table { 245 | caption-side: bottom; 246 | border-collapse: collapse; 247 | } 248 | 249 | caption { 250 | padding-top: 0.5rem; 251 | padding-bottom: 0.5rem; 252 | color: #6c757d; 253 | text-align: left; 254 | } 255 | 256 | th { 257 | text-align: inherit; 258 | text-align: -webkit-match-parent; 259 | } 260 | 261 | thead, 262 | tbody, 263 | tfoot, 264 | tr, 265 | td, 266 | th { 267 | border-color: inherit; 268 | border-style: solid; 269 | border-width: 0; 270 | } 271 | 272 | label { 273 | display: inline-block; 274 | } 275 | 276 | button { 277 | border-radius: 0; 278 | } 279 | 280 | button:focus:not(:focus-visible) { 281 | outline: 0; 282 | } 283 | 284 | input, 285 | button, 286 | select, 287 | optgroup, 288 | textarea { 289 | margin: 0; 290 | font-family: inherit; 291 | font-size: inherit; 292 | line-height: inherit; 293 | } 294 | 295 | button, 296 | select { 297 | text-transform: none; 298 | } 299 | 300 | [role=button] { 301 | cursor: pointer; 302 | } 303 | 304 | select { 305 | word-wrap: normal; 306 | } 307 | select:disabled { 308 | opacity: 1; 309 | } 310 | 311 | [list]::-webkit-calendar-picker-indicator { 312 | display: none; 313 | } 314 | 315 | button, 316 | [type=button], 317 | [type=reset], 318 | [type=submit] { 319 | -webkit-appearance: button; 320 | } 321 | button:not(:disabled), 322 | [type=button]:not(:disabled), 323 | [type=reset]:not(:disabled), 324 | [type=submit]:not(:disabled) { 325 | cursor: pointer; 326 | } 327 | 328 | ::-moz-focus-inner { 329 | padding: 0; 330 | border-style: none; 331 | } 332 | 333 | textarea { 334 | resize: vertical; 335 | } 336 | 337 | fieldset { 338 | min-width: 0; 339 | padding: 0; 340 | margin: 0; 341 | border: 0; 342 | } 343 | 344 | legend { 345 | float: left; 346 | width: 100%; 347 | padding: 0; 348 | margin-bottom: 0.5rem; 349 | font-size: calc(1.275rem + 0.3vw); 350 | line-height: inherit; 351 | } 352 | @media (min-width: 1200px) { 353 | legend { 354 | font-size: 1.5rem; 355 | } 356 | } 357 | legend + * { 358 | clear: left; 359 | } 360 | 361 | ::-webkit-datetime-edit-fields-wrapper, 362 | ::-webkit-datetime-edit-text, 363 | ::-webkit-datetime-edit-minute, 364 | ::-webkit-datetime-edit-hour-field, 365 | ::-webkit-datetime-edit-day-field, 366 | ::-webkit-datetime-edit-month-field, 367 | ::-webkit-datetime-edit-year-field { 368 | padding: 0; 369 | } 370 | 371 | ::-webkit-inner-spin-button { 372 | height: auto; 373 | } 374 | 375 | [type=search] { 376 | outline-offset: -2px; 377 | -webkit-appearance: textfield; 378 | } 379 | 380 | /* rtl:raw: 381 | [type="tel"], 382 | [type="url"], 383 | [type="email"], 384 | [type="number"] { 385 | direction: ltr; 386 | } 387 | */ 388 | ::-webkit-search-decoration { 389 | -webkit-appearance: none; 390 | } 391 | 392 | ::-webkit-color-swatch-wrapper { 393 | padding: 0; 394 | } 395 | 396 | ::file-selector-button { 397 | font: inherit; 398 | } 399 | 400 | ::-webkit-file-upload-button { 401 | font: inherit; 402 | -webkit-appearance: button; 403 | } 404 | 405 | output { 406 | display: inline-block; 407 | } 408 | 409 | iframe { 410 | border: 0; 411 | } 412 | 413 | summary { 414 | display: list-item; 415 | cursor: pointer; 416 | } 417 | 418 | progress { 419 | vertical-align: baseline; 420 | } 421 | 422 | [hidden] { 423 | display: none !important; 424 | } 425 | 426 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /static/plugins/bootstrap-5.0.2-dist/css/bootstrap-reboot.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.min.css.map */ -------------------------------------------------------------------------------- /static/plugins/bootstrap-5.0.2-dist/css/bootstrap-reboot.rtl.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | @media (prefers-reduced-motion: no-preference) { 15 | :root { 16 | scroll-behavior: smooth; 17 | } 18 | } 19 | 20 | body { 21 | margin: 0; 22 | font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 23 | font-size: 1rem; 24 | font-weight: 400; 25 | line-height: 1.5; 26 | color: #212529; 27 | background-color: #fff; 28 | -webkit-text-size-adjust: 100%; 29 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 30 | } 31 | 32 | hr { 33 | margin: 1rem 0; 34 | color: inherit; 35 | background-color: currentColor; 36 | border: 0; 37 | opacity: 0.25; 38 | } 39 | 40 | hr:not([size]) { 41 | height: 1px; 42 | } 43 | 44 | h6, h5, h4, h3, h2, h1 { 45 | margin-top: 0; 46 | margin-bottom: 0.5rem; 47 | font-weight: 500; 48 | line-height: 1.2; 49 | } 50 | 51 | h1 { 52 | font-size: calc(1.375rem + 1.5vw); 53 | } 54 | @media (min-width: 1200px) { 55 | h1 { 56 | font-size: 2.5rem; 57 | } 58 | } 59 | 60 | h2 { 61 | font-size: calc(1.325rem + 0.9vw); 62 | } 63 | @media (min-width: 1200px) { 64 | h2 { 65 | font-size: 2rem; 66 | } 67 | } 68 | 69 | h3 { 70 | font-size: calc(1.3rem + 0.6vw); 71 | } 72 | @media (min-width: 1200px) { 73 | h3 { 74 | font-size: 1.75rem; 75 | } 76 | } 77 | 78 | h4 { 79 | font-size: calc(1.275rem + 0.3vw); 80 | } 81 | @media (min-width: 1200px) { 82 | h4 { 83 | font-size: 1.5rem; 84 | } 85 | } 86 | 87 | h5 { 88 | font-size: 1.25rem; 89 | } 90 | 91 | h6 { 92 | font-size: 1rem; 93 | } 94 | 95 | p { 96 | margin-top: 0; 97 | margin-bottom: 1rem; 98 | } 99 | 100 | abbr[title], 101 | abbr[data-bs-original-title] { 102 | -webkit-text-decoration: underline dotted; 103 | text-decoration: underline dotted; 104 | cursor: help; 105 | -webkit-text-decoration-skip-ink: none; 106 | text-decoration-skip-ink: none; 107 | } 108 | 109 | address { 110 | margin-bottom: 1rem; 111 | font-style: normal; 112 | line-height: inherit; 113 | } 114 | 115 | ol, 116 | ul { 117 | padding-right: 2rem; 118 | } 119 | 120 | ol, 121 | ul, 122 | dl { 123 | margin-top: 0; 124 | margin-bottom: 1rem; 125 | } 126 | 127 | ol ol, 128 | ul ul, 129 | ol ul, 130 | ul ol { 131 | margin-bottom: 0; 132 | } 133 | 134 | dt { 135 | font-weight: 700; 136 | } 137 | 138 | dd { 139 | margin-bottom: 0.5rem; 140 | margin-right: 0; 141 | } 142 | 143 | blockquote { 144 | margin: 0 0 1rem; 145 | } 146 | 147 | b, 148 | strong { 149 | font-weight: bolder; 150 | } 151 | 152 | small { 153 | font-size: 0.875em; 154 | } 155 | 156 | mark { 157 | padding: 0.2em; 158 | background-color: #fcf8e3; 159 | } 160 | 161 | sub, 162 | sup { 163 | position: relative; 164 | font-size: 0.75em; 165 | line-height: 0; 166 | vertical-align: baseline; 167 | } 168 | 169 | sub { 170 | bottom: -0.25em; 171 | } 172 | 173 | sup { 174 | top: -0.5em; 175 | } 176 | 177 | a { 178 | color: #0d6efd; 179 | text-decoration: underline; 180 | } 181 | a:hover { 182 | color: #0a58ca; 183 | } 184 | 185 | a:not([href]):not([class]), a:not([href]):not([class]):hover { 186 | color: inherit; 187 | text-decoration: none; 188 | } 189 | 190 | pre, 191 | code, 192 | kbd, 193 | samp { 194 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 195 | font-size: 1em; 196 | direction: ltr ; 197 | unicode-bidi: bidi-override; 198 | } 199 | 200 | pre { 201 | display: block; 202 | margin-top: 0; 203 | margin-bottom: 1rem; 204 | overflow: auto; 205 | font-size: 0.875em; 206 | } 207 | pre code { 208 | font-size: inherit; 209 | color: inherit; 210 | word-break: normal; 211 | } 212 | 213 | code { 214 | font-size: 0.875em; 215 | color: #d63384; 216 | word-wrap: break-word; 217 | } 218 | a > code { 219 | color: inherit; 220 | } 221 | 222 | kbd { 223 | padding: 0.2rem 0.4rem; 224 | font-size: 0.875em; 225 | color: #fff; 226 | background-color: #212529; 227 | border-radius: 0.2rem; 228 | } 229 | kbd kbd { 230 | padding: 0; 231 | font-size: 1em; 232 | font-weight: 700; 233 | } 234 | 235 | figure { 236 | margin: 0 0 1rem; 237 | } 238 | 239 | img, 240 | svg { 241 | vertical-align: middle; 242 | } 243 | 244 | table { 245 | caption-side: bottom; 246 | border-collapse: collapse; 247 | } 248 | 249 | caption { 250 | padding-top: 0.5rem; 251 | padding-bottom: 0.5rem; 252 | color: #6c757d; 253 | text-align: right; 254 | } 255 | 256 | th { 257 | text-align: inherit; 258 | text-align: -webkit-match-parent; 259 | } 260 | 261 | thead, 262 | tbody, 263 | tfoot, 264 | tr, 265 | td, 266 | th { 267 | border-color: inherit; 268 | border-style: solid; 269 | border-width: 0; 270 | } 271 | 272 | label { 273 | display: inline-block; 274 | } 275 | 276 | button { 277 | border-radius: 0; 278 | } 279 | 280 | button:focus:not(:focus-visible) { 281 | outline: 0; 282 | } 283 | 284 | input, 285 | button, 286 | select, 287 | optgroup, 288 | textarea { 289 | margin: 0; 290 | font-family: inherit; 291 | font-size: inherit; 292 | line-height: inherit; 293 | } 294 | 295 | button, 296 | select { 297 | text-transform: none; 298 | } 299 | 300 | [role=button] { 301 | cursor: pointer; 302 | } 303 | 304 | select { 305 | word-wrap: normal; 306 | } 307 | select:disabled { 308 | opacity: 1; 309 | } 310 | 311 | [list]::-webkit-calendar-picker-indicator { 312 | display: none; 313 | } 314 | 315 | button, 316 | [type=button], 317 | [type=reset], 318 | [type=submit] { 319 | -webkit-appearance: button; 320 | } 321 | button:not(:disabled), 322 | [type=button]:not(:disabled), 323 | [type=reset]:not(:disabled), 324 | [type=submit]:not(:disabled) { 325 | cursor: pointer; 326 | } 327 | 328 | ::-moz-focus-inner { 329 | padding: 0; 330 | border-style: none; 331 | } 332 | 333 | textarea { 334 | resize: vertical; 335 | } 336 | 337 | fieldset { 338 | min-width: 0; 339 | padding: 0; 340 | margin: 0; 341 | border: 0; 342 | } 343 | 344 | legend { 345 | float: right; 346 | width: 100%; 347 | padding: 0; 348 | margin-bottom: 0.5rem; 349 | font-size: calc(1.275rem + 0.3vw); 350 | line-height: inherit; 351 | } 352 | @media (min-width: 1200px) { 353 | legend { 354 | font-size: 1.5rem; 355 | } 356 | } 357 | legend + * { 358 | clear: right; 359 | } 360 | 361 | ::-webkit-datetime-edit-fields-wrapper, 362 | ::-webkit-datetime-edit-text, 363 | ::-webkit-datetime-edit-minute, 364 | ::-webkit-datetime-edit-hour-field, 365 | ::-webkit-datetime-edit-day-field, 366 | ::-webkit-datetime-edit-month-field, 367 | ::-webkit-datetime-edit-year-field { 368 | padding: 0; 369 | } 370 | 371 | ::-webkit-inner-spin-button { 372 | height: auto; 373 | } 374 | 375 | [type=search] { 376 | outline-offset: -2px; 377 | -webkit-appearance: textfield; 378 | } 379 | 380 | [type="tel"], 381 | [type="url"], 382 | [type="email"], 383 | [type="number"] { 384 | direction: ltr; 385 | } 386 | ::-webkit-search-decoration { 387 | -webkit-appearance: none; 388 | } 389 | 390 | ::-webkit-color-swatch-wrapper { 391 | padding: 0; 392 | } 393 | 394 | ::file-selector-button { 395 | font: inherit; 396 | } 397 | 398 | ::-webkit-file-upload-button { 399 | font: inherit; 400 | -webkit-appearance: button; 401 | } 402 | 403 | output { 404 | display: inline-block; 405 | } 406 | 407 | iframe { 408 | border: 0; 409 | } 410 | 411 | summary { 412 | display: list-item; 413 | cursor: pointer; 414 | } 415 | 416 | progress { 417 | vertical-align: baseline; 418 | } 419 | 420 | [hidden] { 421 | display: none !important; 422 | } 423 | /*# sourceMappingURL=bootstrap-reboot.rtl.css.map */ -------------------------------------------------------------------------------- /static/plugins/bootstrap-5.0.2-dist/css/bootstrap-reboot.rtl.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v5.0.2 (https://getbootstrap.com/) 3 | * Copyright 2011-2021 The Bootstrap Authors 4 | * Copyright 2011-2021 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans","Liberation Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;background-color:currentColor;border:0;opacity:.25}hr:not([size]){height:1px}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){h1{font-size:2.5rem}}h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){h2{font-size:2rem}}h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){h3{font-size:1.75rem}}h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){h4{font-size:1.5rem}}h5{font-size:1.25rem}h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[data-bs-original-title],abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-right:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-right:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}small{font-size:.875em}mark{padding:.2em;background-color:#fcf8e3}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#0d6efd;text-decoration:underline}a:hover{color:#0a58ca}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em;direction:ltr;unicode-bidi:bidi-override}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:#d63384;word-wrap:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:.875em;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:1em;font-weight:700}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:right}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]::-webkit-calendar-picker-indicator{display:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:right;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:right}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}[type=email],[type=number],[type=tel],[type=url]{direction:ltr}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::file-selector-button{font:inherit}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important} 8 | /*# sourceMappingURL=bootstrap-reboot.rtl.min.css.map */ -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "8" 4 | before_script: 5 | - "export DISPLAY=:99.0" 6 | - "sh -e /etc/init.d/xvfb start" 7 | - sleep 3 # give xvfb some time to start -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 http://xdsoft.net 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/README.md: -------------------------------------------------------------------------------- 1 | # jQuery DateTimePicker 2 | [Demo and Documentation](https://xdsoft.net/jqplugins/datetimepicker/) 3 | 4 | [![Build Status](https://travis-ci.org/xdan/datetimepicker.svg?branch=master)](https://travis-ci.org/xdan/datetimepicker) 5 | [![npm version](https://badge.fury.io/js/jquery-datetimepicker.svg)](https://badge.fury.io/js/jquery-datetimepicker) 6 | [![npm](https://img.shields.io/npm/dm/jquery-datetimepicker.svg)](https://www.npmjs.com/package/jquery-datetimepicker) 7 | 8 | 9 | 10 | PLEASE. Help me update documentation. 11 | [Doc.tpl](https://github.com/xdan/datetimepicker/blob/master/doc.tpl) 12 | This file will be automatically displayed on the site 13 | 14 | # Installation 15 | 16 | ```bash 17 | npm install jquery-datetimepicker 18 | ``` 19 | OR 20 | ```bash 21 | yarn add jquery-datetimepicker 22 | ``` 23 | or download [zip](https://github.com/xdan/datetimepicker/releases) 24 | # datetimepicker 25 | ============== 26 | 27 | **!!! The latest version of the options 'lang' obsolete. The language setting is now global. !!!** 28 | 29 | Use this: 30 | ```javascript 31 | jQuery.datetimepicker.setLocale('en'); 32 | ``` 33 | [Documentation][doc] 34 | 35 | jQuery Plugin Date and Time Picker 36 | 37 | DateTimePicker 38 | 39 | ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/1.png) 40 | 41 | DatePicker 42 | 43 | ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/2.png) 44 | 45 | TimePicker 46 | 47 | ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/3.png) 48 | 49 | Options to highlight individual dates or periods 50 | 51 | ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/4.png) 52 | 53 | ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/5.png) 54 | 55 | ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/6.png) 56 | 57 | [doc]: https://xdsoft.net/jqplugins/datetimepicker/ 58 | 59 | ### JS Build help 60 | 61 | **Requires Node and NPM** [Download and install node.js](http://nodejs.org/download/). 62 | 63 | Install: 64 | 65 | 1. Install `bower` globally with `npm install -g bower`. 66 | 2. Run `npm install`. npm will look at `package.json` and automatically install the necessary dependencies. 67 | 3. Run `bower install`, which installs front-end packages defined in `bower.json`. 68 | 69 | Notice: If you use Bower v1.5.2, you will get error: `The "main" field cannot contain minified files` 70 | You can regress to version 1.3.12 71 | 72 | 1. `npm uninstall bower -g` 73 | 2. `npm install -g bower@1.3.12` 74 | 75 | Build: 76 | 77 | First install npm requirements: `npm install -g uglifycss concat-cli` 78 | Then build the files: `npm run build` 79 | 80 | When build completed, you'll have the following files: 81 | - **build/jquery.datetimepicker.full.js** - browser file 82 | - **build/jquery.datetimepicker.full.min.js** - browser minified file 83 | - **build/jquery.datetimepicker.min.js** - amd module style minified file 84 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "datetimepicker", 3 | "version": "2.5.11", 4 | "main": [ 5 | "build/jquery.datetimepicker.full.min.js", 6 | "jquery.datetimepicker.css" 7 | ], 8 | "ignore": [ 9 | "**/screen", 10 | "**/datetimepicker.jquery.json", 11 | "**/*.png", 12 | "**/*.txt", 13 | "**/*.md", 14 | "**/*.html", 15 | "**/*.tpl", 16 | "**/jquery.js", 17 | "bower_components", 18 | "node_modules" 19 | ], 20 | "keywords": [ 21 | "calendar", 22 | "date", 23 | "time", 24 | "form", 25 | "datetime", 26 | "datepicker", 27 | "timepicker", 28 | "datetimepicker", 29 | "validation", 30 | "ui", 31 | "scroller", 32 | "picker", 33 | "i18n", 34 | "input", 35 | "jquery", 36 | "touch" 37 | ], 38 | "authors": [ 39 | { 40 | "name": "Chupurnov Valeriy", 41 | "email": "chupurnov@gmail.com", 42 | "homepage": "http://xdsoft.net/contacts.html" 43 | } 44 | ], 45 | "dependencies": { 46 | "jquery": ">= 1.7.2", 47 | "jquery-mousewheel": ">= 3.1.13", 48 | "php-date-formatter": ">= 1.3.3" 49 | }, 50 | "license": "MIT", 51 | "homepage": "http://xdsoft.net/jqplugins/datetimepicker/", 52 | "repository": { 53 | "type": "git", 54 | "url": "git://github.com:xdan/datetimepicker.git" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/datetimepicker.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "datetimepicker", 3 | "version": "2.5.4", 4 | "title": "jQuery Date and Time picker", 5 | "description": "jQuery plugin for date, time, or datetime manipulation in form", 6 | "keywords": [ 7 | "calendar", 8 | "date", 9 | "time", 10 | "form", 11 | "datetime", 12 | "datepicker", 13 | "timepicker", 14 | "datetimepicker", 15 | "validation", 16 | "ui", 17 | "scroller", 18 | "picker", 19 | "i18n", 20 | "input", 21 | "jquery", 22 | "touch" 23 | ], 24 | "author": { 25 | "name": "Chupurnov Valeriy", 26 | "email": "chupurnov@gmail.com", 27 | "url": "http://xdsoft.net/contacts.html" 28 | }, 29 | "maintainers": [{ 30 | "name": "Chupurnov Valeriy", 31 | "email": "chupurnov@gmail.com", 32 | "url": "http://xdsoft.net" 33 | }], 34 | "licenses": [ 35 | { 36 | "type": "MIT", 37 | "url": "https://github.com/xdan/datetimepicker/blob/master/MIT-LICENSE.txt" 38 | } 39 | ], 40 | "bugs": "https://github.com/xdan/datetimepicker/issues", 41 | "homepage": "http://xdsoft.net/jqplugins/datetimepicker/", 42 | "docs": "http://xdsoft.net/jqplugins/datetimepicker/", 43 | "download": "https://github.com/xdan/datetimepicker/archive/master.zip", 44 | "dependencies": { 45 | "jquery": ">=1.7" 46 | } 47 | } -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 19 | 20 | 21 | 22 |

Homepage

23 |

DateTimePicker

24 |

25 |

DateTimePickers selected by class

26 | 27 | 28 |

Mask DateTimePicker

29 |

30 |

TimePicker

31 |

32 |

DatePicker

33 |

34 |

Inline DateTimePicker

35 | 36 |

37 |

Button Trigger

38 | 39 |

TimePicker allows time

40 |

41 |

Destroy DateTimePicker

42 | 43 |

Set options runtime DateTimePicker

44 | 45 |

If select day is Saturday, the minimum set 11:00, otherwise 8:00

46 |

onGenerate

47 | 48 |

disable all weekend

49 | 50 |

Default date and time

51 | 52 |

Show inline

53 | Show/Hide 54 | 57 |

Disable Specific Dates

58 |

Disable the dates 2 days from now.

59 | 60 |

Custom Date Styling

61 |

Make the background of the date 2 days from now bright red.

62 | 63 |

Dark theme

64 |

thank for this https://github.com/lampslave

65 | 66 |

Date time format and locale

67 |

68 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 225 | 226 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function(config) { 2 | config.set({ 3 | basePath: '', 4 | frameworks: ['mocha', 'chai'], 5 | files: [ 6 | 'jquery.datetimepicker.css', 7 | 'node_modules/php-date-formatter/js/php-date-formatter.js', 8 | 'jquery.js', 9 | 'jquery.datetimepicker.js', 10 | 'tests/bootstrap.js', 11 | 'tests/tests/*.js' 12 | ], 13 | reporters: ['progress'], 14 | port: 2002, 15 | hostname: '127.0.0.1', 16 | colors: true, 17 | logLevel: config.LOG_INFO, 18 | browsers: ['Firefox'], 19 | autoWatch: true, 20 | singleRun: false, // Karma captures browsers, runs the tests and exits 21 | concurrency: Infinity, 22 | plugins: [ 23 | 'karma-firefox-launcher', 24 | 'karma-mocha', 25 | 'karma-chai' 26 | ], 27 | client: { 28 | captureConsole: true 29 | } 30 | }) 31 | }; 32 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-datetimepicker", 3 | "version": "2.5.21", 4 | "description": "jQuery Plugin DateTimePicker it is DatePicker and TimePicker in one", 5 | "main": "build/jquery.datetimepicker.full.min.js", 6 | "scripts": { 7 | "test": "karma start --browsers Firefox karma.conf.js --single-run", 8 | "concat": "concat-cli -f node_modules/php-date-formatter/js/php-date-formatter.min.js jquery.datetimepicker.js node_modules/jquery-mousewheel/jquery.mousewheel.js -o build/jquery.datetimepicker.full.js", 9 | "minify": "uglifyjs jquery.datetimepicker.js -c -m -o build/jquery.datetimepicker.min.js && uglifycss jquery.datetimepicker.css > build/jquery.datetimepicker.min.css", 10 | "minifyconcat": "uglifyjs build/jquery.datetimepicker.full.js -c -m -o build/jquery.datetimepicker.full.min.js", 11 | "github": "git add --all && git commit -m \"New version %npm_package_version% \" && git tag %npm_package_version% && git push --tags origin HEAD:master && npm publish", 12 | "build": "npm run minify && npm run concat && npm run minifyconcat", 13 | "public": "npm run test && npm version patch --no-git-tag-version && npm run build && npm run github" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/xdan/datetimepicker.git" 18 | }, 19 | "keywords": [ 20 | "jquery-plugin", 21 | "calendar", 22 | "date", 23 | "time", 24 | "datetime", 25 | "datepicker", 26 | "timepicker" 27 | ], 28 | "author": "Chupurnov (https://xdsoft.net/)", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/xdan/datetimepicker/issues" 32 | }, 33 | "homepage": "https://github.com/xdan/datetimepicker", 34 | "dependencies": { 35 | "jquery": ">= 1.7.2", 36 | "jquery-mousewheel": ">= 3.1.13", 37 | "php-date-formatter": "^1.3.4" 38 | }, 39 | "devDependencies": { 40 | "chai": "^4.1.2", 41 | "concat": "azer/concat", 42 | "concat-cli": "^4.0.0", 43 | "karma": "^2.0.0", 44 | "karma-chai": "^0.1.0", 45 | "karma-firefox-launcher": "^1.1.0", 46 | "karma-mocha": "^1.3.0", 47 | "mocha": "^5.0.4", 48 | "uglify-js": "^3.4.9", 49 | "uglifycss": "^0.0.27" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Checklist before pull request 2 | * [ ] There is an associated issue that is labelled 'Bug' or 'help wanted' or is in the Community milestone 3 | * [ ] Code is up-to-date with the `master` branch 4 | * [ ] You've successfully run `npm test` locally 5 | * [ ] There are new or updated tests validating the change 6 | 7 | ## Fixes # 8 | About your changes 9 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/1.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/2.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/3.1.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/3.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/4.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/5.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/screen/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/screen/6.png -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/datetimepicker-master/tests/app.css -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | jQuery DateTimepicker Tests 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 44 | 45 | -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/tests/bootstrap.js: -------------------------------------------------------------------------------- 1 | var inputs = []; 2 | 3 | var box = document.createElement('div'); 4 | document.body.appendChild(box); 5 | 6 | 7 | var getInput = function () { 8 | var input = document.createElement('input'); 9 | input.setAttribute('type', 'text'); 10 | inputs.push(input); 11 | box.appendChild(input); 12 | return input; 13 | }; 14 | 15 | var clear = function() { 16 | inputs.forEach(function (inp) { 17 | $(inp).datetimepicker('destroy'); 18 | inp.parentNode && inp.parentNode.removeChild(inp) 19 | }); 20 | }; 21 | 22 | var PICKER = 'xdsoft_datetimepicker'; 23 | 24 | var simulateEvent = function (type, element, keyCodeArg, options) { 25 | if (!keyCodeArg) { 26 | keyCodeArg = 0; 27 | } 28 | 29 | if (element instanceof jQuery) { 30 | element = element[0]; 31 | } 32 | 33 | var evt = (element.ownerDocument || document).createEvent('HTMLEvents') 34 | evt.initEvent(type, true, true); 35 | evt.keyCode = keyCodeArg; 36 | evt.which = keyCodeArg; 37 | 38 | if (options) { 39 | options(evt); 40 | } 41 | 42 | if (type.match(/^mouse/)) { 43 | ['pageX', 'pageY', 'clientX', 'clientY'].forEach(function (key) { 44 | if (evt[key] === undefined) { 45 | evt[key] = 0; 46 | } 47 | }) 48 | } 49 | 50 | element.dispatchEvent(evt); 51 | }; 52 | 53 | afterEach(clear); 54 | var expect = chai.expect; 55 | chai.config.includeStack = true -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/tests/destroy.js: -------------------------------------------------------------------------------- 1 | describe('Check destructor', function () { 2 | describe('Init picker and after this init again with command destroy', function () { 3 | it('Should remove picker from DOM and remove all listeners from original input', function (done) { 4 | var input = getInput(); 5 | $(input).datetimepicker(); 6 | var dtp = $(input).data('xdsoft_datetimepicker'); 7 | expect(dtp).to.be.not.equal(null); 8 | expect(dtp[0].tagName).to.be.equal('DIV'); 9 | expect(dtp[0].classList.contains('xdsoft_datetimepicker')).to.be.true; 10 | expect(dtp.is(':hidden')).to.be.true; 11 | 12 | $(input).datetimepicker('destroy'); 13 | expect($(input).data('xdsoft_datetimepicker')).to.be.equal(null); 14 | 15 | $(input).trigger('mousedown') 16 | setTimeout(function () { 17 | expect(dtp.is(':hidden')).to.be.true; 18 | done(); 19 | }, 150) 20 | }); 21 | }); 22 | }); -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/tests/events.js: -------------------------------------------------------------------------------- 1 | describe('Test events', function () { 2 | describe('onSelectDate', function () { 3 | it('Should fired after user selected day', function (done) { 4 | var input= $(getInput()).val('2011/04/15'); 5 | 6 | var picker = input.datetimepicker({ 7 | onSelectDate: function (time, inp, evt) { 8 | expect(picker).to.be.equal(this); 9 | expect(time.getDate()).to.be.equal(17); 10 | expect(time.getMonth()).to.be.equal(3); 11 | expect(time.getFullYear()).to.be.equal(2011); 12 | expect(inp[0]).to.be.equal(input[0]); 13 | expect(evt.type).to.be.equal('click'); 14 | done(); 15 | }, 16 | format: 'Y/m/d' 17 | }).trigger('mousedown').data(PICKER); 18 | 19 | setTimeout(function () { 20 | var select = picker.find('td[data-date="17"][data-month="3"][data-year="2011"]'); 21 | expect(select.length).to.be.equal(1); 22 | select.trigger('click'); 23 | }, 100); 24 | }); 25 | }); 26 | }); -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/tests/init.js: -------------------------------------------------------------------------------- 1 | describe('Init', function () { 2 | describe('jQuery.fn', function () { 3 | it('Should have datetimepicker method', function () { 4 | expect(typeof jQuery.fn.datetimepicker).to.be.equal('function'); 5 | expect(typeof $.fn.datetimepicker).to.be.equal('function'); 6 | }); 7 | }); 8 | describe('jQuery.fn.datetimepicker', function () { 9 | it('Should have `defaults` property', function () { 10 | expect(typeof jQuery.fn.datetimepicker.defaults).to.be.equal('object'); 11 | expect(jQuery.fn.datetimepicker.defaults.format).to.be.equal('Y/m/d H:i'); 12 | }); 13 | }); 14 | describe('Create datetimepicker', function () { 15 | describe('Without parameters', function () { 16 | it('Should create plugin with default options', function (done) { 17 | var input = getInput(); 18 | $(input).datetimepicker(); 19 | var dtp = $(input).data('xdsoft_datetimepicker'); 20 | expect(dtp).to.be.not.equal(null); 21 | expect(dtp[0].tagName).to.be.equal('DIV'); 22 | expect(dtp[0].classList.contains('xdsoft_datetimepicker')).to.be.true; 23 | expect(dtp.is(':hidden')).to.be.true; 24 | $(input).trigger('mousedown') 25 | setTimeout(function () { 26 | expect(dtp.is(':hidden')).to.be.false; 27 | done(); 28 | }, 150) 29 | }); 30 | }); 31 | describe('In inline mode', function () { 32 | it('Should create picker and replace original input', function () { 33 | var input = getInput(); 34 | $(input).datetimepicker({ 35 | inline: true 36 | }); 37 | var dtp = $(input).data('xdsoft_datetimepicker'); 38 | expect(dtp.is(':hidden')).to.be.false; 39 | expect($(input).is(':hidden')).to.be.true; 40 | }); 41 | }); 42 | }); 43 | describe('Set locale', function () { 44 | describe('Change locale', function () { 45 | it('Should create different pickers fro all locales', function (done) { 46 | $.datetimepicker.setLocale('en'); 47 | var $input = $(getInput()); 48 | $input.datetimepicker({inline: true}); 49 | setTimeout(function () { 50 | var text = $input.data('xdsoft_datetimepicker').text(); 51 | $input.datetimepicker('destroy'); 52 | $.datetimepicker.setLocale('ru'); 53 | $input.datetimepicker({inline: true}); 54 | setTimeout(function () { 55 | expect($input.data('xdsoft_datetimepicker').text()).to.be.not.equal(text); 56 | done(); 57 | }, 100) 58 | }, 100) 59 | }); 60 | }); 61 | }); 62 | describe('Select day', function () { 63 | it('Should set selected date to input by format', function (done) { 64 | var input= $(getInput()).val('2011/04/15'); 65 | 66 | var picker = input.datetimepicker({ 67 | format: 'Y/m/d' 68 | }).trigger('mousedown').data(PICKER); 69 | 70 | setTimeout(function () { 71 | var start = picker.find('td[data-date="15"][data-month="3"][data-year="2011"]'); 72 | expect(start.length).to.be.equal(1); 73 | expect(start.hasClass('xdsoft_disabled')).to.be.false; 74 | expect(start.hasClass('xdsoft_current')).to.be.true; 75 | var select = picker.find('td[data-date="17"][data-month="3"][data-year="2011"]'); 76 | expect(start.length).to.be.equal(1); 77 | select.trigger('click'); 78 | expect(input.val()).to.be.equal('2011/04/17') 79 | done(); 80 | }, 100); 81 | }); 82 | }); 83 | }); -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/tests/methods.js: -------------------------------------------------------------------------------- 1 | describe('Test methods', function () { 2 | describe('Show', function () { 3 | it('Should show picker', function () { 4 | var input= $(getInput()); 5 | var picker = input 6 | .datetimepicker() 7 | .datetimepicker('show') 8 | .data(PICKER); 9 | 10 | expect(picker.is(':hidden')).to.be.false; 11 | }); 12 | }); 13 | describe('Hide', function () { 14 | it('Should hide picker', function () { 15 | var input= $(getInput()); 16 | var picker = input 17 | .datetimepicker() 18 | .datetimepicker('show') 19 | .data(PICKER); 20 | 21 | expect(picker.is(':hidden')).to.be.false; 22 | input.datetimepicker('hide') 23 | expect(picker.is(':hidden')).to.be.true; 24 | }); 25 | }); 26 | describe('Toggle', function () { 27 | it('Should hide/show picker', function () { 28 | var input= $(getInput()); 29 | var picker = input 30 | .datetimepicker() 31 | .datetimepicker('show') 32 | .data(PICKER); 33 | 34 | expect(picker.is(':hidden')).to.be.false; 35 | input.datetimepicker('toggle') 36 | expect(picker.is(':hidden')).to.be.true; 37 | input.datetimepicker('toggle') 38 | expect(picker.is(':hidden')).to.be.false; 39 | }); 40 | }); 41 | describe('Reset', function () { 42 | it('Should restore default value', function (done) { 43 | var input= $('').appendTo(document.body); 44 | 45 | var picker = input 46 | .datetimepicker({format: 'd.m.Y'}) 47 | .datetimepicker('show') 48 | .data(PICKER); 49 | 50 | setTimeout(function () { 51 | var select = picker.find('td[data-date="16"][data-month="11"][data-year="2008"]'); 52 | expect(select.length).to.be.equal(1); 53 | select.trigger('click'); 54 | expect(input.val()).to.be.equal('16.12.2008'); 55 | input.datetimepicker('reset'); 56 | expect(input.val()).to.be.equal('15.12.2008'); 57 | input.datetimepicker('destroy').remove(); 58 | done(); 59 | }, 100) 60 | }); 61 | }); 62 | }); -------------------------------------------------------------------------------- /static/plugins/datetimepicker-master/tests/tests/options.js: -------------------------------------------------------------------------------- 1 | describe('Test options', function () { 2 | describe('dayOfWeekStart', function () { 3 | it('Should change default start of week', function (done) { 4 | $.datetimepicker.setLocale('en'); 5 | var input = $(getInput()); 6 | var picker = input.datetimepicker({inline: true}).trigger('mousedown').data('xdsoft_datetimepicker'); 7 | setTimeout(function () { 8 | var day = picker.find('th').eq(0).text(); 9 | var date = picker.find('td').eq(0).text(); 10 | input.datetimepicker('destroy'); 11 | 12 | var picker2 = $(getInput()).datetimepicker({ 13 | inline: true, 14 | dayOfWeekStart: 2 15 | }).trigger('mousedown').data(PICKER); 16 | 17 | setTimeout(function () { 18 | expect(picker2.find('th').eq(0).text()).to.be.not.equal(day); 19 | expect(picker2.find('td').eq(0).text()).to.be.not.equal(date); 20 | done(); 21 | }, 100); 22 | }, 100); 23 | }); 24 | }); 25 | describe('disabledDates and startDate', function () { 26 | it('Should disable some dates in picker and picker should be open on startDate', function (done) { 27 | var input= $(getInput()); 28 | var picker = input.datetimepicker({ 29 | disabledDates:['1986/01/08','1986/01/09','1986/01/10'], 30 | startDate: '1986/01/05' 31 | }).trigger('mousedown').data(PICKER); 32 | 33 | setTimeout(function () { 34 | var day = picker.find('td[data-date="8"][data-month="0"][data-year="1986"]'); 35 | expect(day.hasClass('xdsoft_disabled')).to.be.true; 36 | var start = picker.find('td[data-date="5"][data-month="0"][data-year="1986"]'); 37 | expect(start.length).to.be.equal(1); 38 | expect(start.hasClass('xdsoft_disabled')).to.be.false; 39 | done(); 40 | }, 100); 41 | }); 42 | }); 43 | describe('defaultDate', function () { 44 | it('Should open picker on some date', function (done) { 45 | var input= $(getInput()); 46 | var picker = input.datetimepicker({formatDate:'d.m.Y', defaultDate: '+03.01.1970'}).trigger('mousedown').data(PICKER); 47 | 48 | setTimeout(function () { 49 | var now = new Date(); 50 | now.setDate(now.getDate() + 2) 51 | var start = picker.find('td[data-date="' + now.getDate() + '"][data-month="' + now.getMonth() + '"][data-year="' + now.getFullYear() + '"]'); 52 | expect(start.length).to.be.equal(1); 53 | expect(start.hasClass('xdsoft_disabled')).to.be.false; 54 | expect(start.hasClass('xdsoft_current xdsoft_today')).to.be.true; 55 | done(); 56 | }, 100); 57 | }); 58 | }); 59 | describe('Value', function () { 60 | it('Should set value to plugin', function (done) { 61 | var input= $(getInput()); 62 | var picker = input.datetimepicker({value: '2011/04/15 05:03'}).trigger('mousedown').data(PICKER); 63 | 64 | setTimeout(function () { 65 | var start = picker.find('td[data-date="15"][data-month="3"][data-year="2011"]'); 66 | expect(start.length).to.be.equal(1); 67 | expect(start.hasClass('xdsoft_disabled')).to.be.false; 68 | done(); 69 | }, 100); 70 | }); 71 | }); 72 | describe('timepicker = false', function () { 73 | it('Should create only datepicker', function (done) { 74 | var input= $(getInput()); 75 | var picker = input.datetimepicker({ 76 | timepicker: false 77 | }).trigger('mousedown').data(PICKER); 78 | 79 | setTimeout(function () { 80 | var timepicker = picker.find('.xdsoft_timepicker'); 81 | expect(timepicker.length).to.be.equal(1); 82 | expect(timepicker.is(':hidden')).to.be.true; 83 | done(); 84 | }, 100); 85 | }); 86 | }); 87 | describe('datepicker = false', function () { 88 | it('Should create only timepicker', function (done) { 89 | var input= $(getInput()); 90 | var picker = input.datetimepicker({ 91 | datepicker: false 92 | }).trigger('mousedown').data(PICKER); 93 | 94 | setTimeout(function () { 95 | var datepicker = picker.find('.xdsoft_datepicker'); 96 | expect(datepicker.length).to.be.equal(1); 97 | expect(datepicker.is(':hidden')).to.be.true; 98 | done(); 99 | }, 100); 100 | }); 101 | }); 102 | }); -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/mobile/layer.js: -------------------------------------------------------------------------------- 1 | /*! layer mobile-v2.0.0 Web 通用弹出层组件 MIT License */ 2 | ;!function(e){"use strict";var t=document,n="querySelectorAll",i="getElementsByClassName",a=function(e){return t[n](e)},s={type:0,shade:!0,shadeClose:!0,fixed:!0,anim:"scale"},l={extend:function(e){var t=JSON.parse(JSON.stringify(s));for(var n in e)t[n]=e[n];return t},timer:{},end:{}};l.touch=function(e,t){e.addEventListener("click",function(e){t.call(this,e)},!1)};var r=0,o=["layui-m-layer"],c=function(e){var t=this;t.config=l.extend(e),t.view()};c.prototype.view=function(){var e=this,n=e.config,s=t.createElement("div");e.id=s.id=o[0]+r,s.setAttribute("class",o[0]+" "+o[0]+(n.type||0)),s.setAttribute("index",r);var l=function(){var e="object"==typeof n.title;return n.title?'

'+(e?n.title[0]:n.title)+"

":""}(),c=function(){"string"==typeof n.btn&&(n.btn=[n.btn]);var e,t=(n.btn||[]).length;return 0!==t&&n.btn?(e=''+n.btn[0]+"",2===t&&(e=''+n.btn[1]+""+e),'
'+e+"
"):""}();if(n.fixed||(n.top=n.hasOwnProperty("top")?n.top:100,n.style=n.style||"",n.style+=" top:"+(t.body.scrollTop+n.top)+"px"),2===n.type&&(n.content='

'+(n.content||"")+"

"),n.skin&&(n.anim="up"),"msg"===n.skin&&(n.shade=!1),s.innerHTML=(n.shade?"
':"")+'
"+l+'
'+n.content+"
"+c+"
",!n.type||2===n.type){var d=t[i](o[0]+n.type),y=d.length;y>=1&&layer.close(d[0].getAttribute("index"))}document.body.appendChild(s);var u=e.elem=a("#"+e.id)[0];n.success&&n.success(u),e.index=r++,e.action(n,u)},c.prototype.action=function(e,t){var n=this;e.time&&(l.timer[n.index]=setTimeout(function(){layer.close(n.index)},1e3*e.time));var a=function(){var t=this.getAttribute("type");0==t?(e.no&&e.no(),layer.close(n.index)):e.yes?e.yes(n.index):layer.close(n.index)};if(e.btn)for(var s=t[i]("layui-m-layerbtn")[0].children,r=s.length,o=0;odiv{line-height:22px;padding-top:7px;margin-bottom:20px;font-size:14px}.layui-m-layerbtn{display:box;display:-moz-box;display:-webkit-box;width:100%;height:50px;line-height:50px;font-size:0;border-top:1px solid #D0D0D0;background-color:#F2F2F2}.layui-m-layerbtn span{display:block;-moz-box-flex:1;box-flex:1;-webkit-box-flex:1;font-size:14px;cursor:pointer}.layui-m-layerbtn span[yes]{color:#40AFFE}.layui-m-layerbtn span[no]{border-right:1px solid #D0D0D0;border-radius:0 0 0 5px}.layui-m-layerbtn span:active{background-color:#F6F6F6}.layui-m-layerend{position:absolute;right:7px;top:10px;width:30px;height:30px;border:0;font-weight:400;background:0 0;cursor:pointer;-webkit-appearance:none;font-size:30px}.layui-m-layerend::after,.layui-m-layerend::before{position:absolute;left:5px;top:15px;content:'';width:18px;height:1px;background-color:#999;transform:rotate(45deg);-webkit-transform:rotate(45deg);border-radius:3px}.layui-m-layerend::after{transform:rotate(-45deg);-webkit-transform:rotate(-45deg)}body .layui-m-layer .layui-m-layer-footer{position:fixed;width:95%;max-width:100%;margin:0 auto;left:0;right:0;bottom:10px;background:0 0}.layui-m-layer-footer .layui-m-layercont{padding:20px;border-radius:5px 5px 0 0;background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn{display:block;height:auto;background:0 0;border-top:none}.layui-m-layer-footer .layui-m-layerbtn span{background-color:rgba(255,255,255,.8)}.layui-m-layer-footer .layui-m-layerbtn span[no]{color:#FD482C;border-top:1px solid #c2c2c2;border-radius:0 0 5px 5px}.layui-m-layer-footer .layui-m-layerbtn span[yes]{margin-top:10px;border-radius:5px}body .layui-m-layer .layui-m-layer-msg{width:auto;max-width:90%;margin:0 auto;bottom:-150px;background-color:rgba(0,0,0,.7);color:#fff}.layui-m-layer-msg .layui-m-layercont{padding:10px 20px} -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layer_2021_12_23/theme/default/icon-ext.png -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layer_2021_12_23/theme/default/icon.png -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layer_2021_12_23/theme/default/loading-0.gif -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layer_2021_12_23/theme/default/loading-1.gif -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layer_2021_12_23/theme/default/loading-2.gif -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/moon/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layer_2021_12_23/theme/moon/default.png -------------------------------------------------------------------------------- /static/plugins/layer_2021_12_23/theme/moon/style.css: -------------------------------------------------------------------------------- 1 | html #layui_layer_skinmoonstylecss{display:none;position:absolute;width:1989px}body .layer-ext-moon[type=dialog]{min-width:320px}body .layer-ext-moon-msg[type=dialog]{min-width:200px}body .layer-ext-moon .layui-layer-title{background:#f6f6f6;color:#212a31;font-size:16px;font-weight:700;height:46px;line-height:46px;border-bottom:1px solid #D5D5D5}body .layer-ext-moon .layui-layer-content .layui-layer-ico{height:32px;width:32px;top:18.5px}body .layer-ext-moon .layui-layer-ico0{background:url(default.png) -96px 0 no-repeat}body .layer-ext-moon .layui-layer-ico1{background:url(default.png) -224px 0 no-repeat}body .layer-ext-moon .layui-layer-ico2{background:url(default.png) -192px 0 no-repeat}body .layer-ext-moon .layui-layer-ico3{background:url(default.png) -160px 0 no-repeat}body .layer-ext-moon .layui-layer-ico4{background:url(default.png) -320px 0 no-repeat}body .layer-ext-moon .layui-layer-ico5{background:url(default.png) -288px 0 no-repeat}body .layer-ext-moon .layui-layer-ico6{background:url(default.png) -256px 0}body .layer-ext-moon .layui-layer-ico7{background:url(default.png) -128px 0 no-repeat}body .layer-ext-moon .layui-layer-setwin{top:15px;right:15px}body .layer-ext-moon .layui-layer-setwin a{width:16px;height:16px}body .layer-ext-moon .layui-layer-setwin .layui-layer-min cite:hover{background-color:#56abe4}body .layer-ext-moon .layui-layer-setwin .layui-layer-max{background:url(default.png) -80px 0 no-repeat}body .layer-ext-moon .layui-layer-setwin .layui-layer-max:hover{background:url(default.png) -64px 0 no-repeat}body .layer-ext-moon .layui-layer-setwin .layui-layer-maxmin{background:url(default.png) -32px 0 no-repeat}body .layer-ext-moon .layui-layer-setwin .layui-layer-maxmin:hover{background:url(default.png) -16px 0 no-repeat}body .layer-ext-moon .layui-layer-setwin .layui-layer-close1,body .layer-ext-moon .layui-layer-setwin .layui-layer-close2{background:url(default.png)}body .layer-ext-moon .layui-layer-setwin .layui-layer-close1:hover,body .layer-ext-moon .layui-layer-setwin .layui-layer-close2:hover{background:url(default.png) -48px 0}body .layer-ext-moon .layui-layer-padding{padding-top:24px}body .layer-ext-moon .layui-layer-btn{text-align:center;padding-top:15px;padding-bottom:15px;background:#f0f4f7;border-top:1px #c7c7c7 solid}body .layer-ext-moon .layui-layer-btn a{font-size:12px;font-weight:400;margin:0 7px;padding:6px 20px;color:#fff;border:1px solid #0064b6;background:no-repeat #0071ce;border-radius:3px;display:inline-block;height:20px;line-height:20px;text-align:center;vertical-align:middle;text-decoration:none;outline:0}body .layer-ext-moon .layui-layer-btn .layui-layer-btn0{background:#0071ce}body .layer-ext-moon .layui-layer-btn .layui-layer-btn1{background:#fff;color:#404a58;border:1px solid #c0c4cd;border-radius:3px}body .layer-ext-moon .layui-layer-btn .layui-layer-btn2{background:#f60;color:#fff;border:1px solid #f60;border-radius:3px}body .layer-ext-moon .layui-layer-btn .layui-layer-btn3{background:red;color:#fff;border:1px solid red;border-radius:3px}body .layer-ext-moon .layui-layer-title span.layui-layer-tabnow{height:47px} -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/code.css: -------------------------------------------------------------------------------- 1 | html #layuicss-skincodecss{display:none;position:absolute;width:1989px}.layui-code-h3,.layui-code-view{position:relative;font-size:12px}.layui-code-view{display:block;margin:10px 0;padding:0;border:1px solid #eee;border-left-width:6px;background-color:#FAFAFA;color:#333;font-family:Courier New}.layui-code-h3{padding:0 10px;height:40px;line-height:40px;border-bottom:1px solid #eee}.layui-code-h3 a{position:absolute;right:10px;top:0;color:#999}.layui-code-view .layui-code-ol{position:relative;overflow:auto}.layui-code-view .layui-code-ol li{position:relative;margin-left:45px;line-height:20px;padding:0 10px;border-left:1px solid #e2e2e2;list-style-type:decimal-leading-zero;*list-style-type:decimal;background-color:#fff}.layui-code-view .layui-code-ol li:first-child{padding-top:10px}.layui-code-view .layui-code-ol li:last-child{padding-bottom:10px}.layui-code-view pre{margin:0}.layui-code-notepad{border:1px solid #0C0C0C;border-left-color:#3F3F3F;background-color:#0C0C0C;color:#C2BE9E}.layui-code-notepad .layui-code-h3{border-bottom:none}.layui-code-notepad .layui-code-ol li{background-color:#3F3F3F;border-left:none}.layui-code-demo .layui-code{visibility:visible!important;margin:-15px;border-top:none;border-right:none;border-bottom:none}.layui-code-demo .layui-tab-content{padding:15px;border-top:none} -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/laydate/default/laydate.css: -------------------------------------------------------------------------------- 1 | .laydate-set-ym,.layui-laydate,.layui-laydate *,.layui-laydate-list{box-sizing:border-box}html #layuicss-laydate{display:none;position:absolute;width:1989px}.layui-laydate *{margin:0;padding:0}.layui-laydate{position:absolute;z-index:66666666;margin:5px 0;border-radius:2px;font-size:14px;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-fill-mode:both;animation-fill-mode:both;animation-name:laydate-downbit}.layui-laydate-main{width:272px}.layui-laydate-content td,.layui-laydate-header *,.layui-laydate-list li{transition-duration:.3s;-webkit-transition-duration:.3s}@keyframes laydate-downbit{0%{opacity:.3;transform:translate3d(0,-5px,0)}100%{opacity:1;transform:translate3d(0,0,0)}}.layui-laydate-static{position:relative;z-index:0;display:inline-block;margin:0;-webkit-animation:none;animation:none}.laydate-ym-show .laydate-next-m,.laydate-ym-show .laydate-prev-m{display:none!important}.laydate-ym-show .laydate-next-y,.laydate-ym-show .laydate-prev-y{display:inline-block!important}.laydate-time-show .laydate-set-ym span[lay-type=month],.laydate-time-show .laydate-set-ym span[lay-type=year],.laydate-time-show .layui-laydate-header .layui-icon,.laydate-ym-show .laydate-set-ym span[lay-type=month]{display:none!important}.layui-laydate-header{position:relative;line-height:30px;padding:10px 70px 5px}.layui-laydate-header *{display:inline-block;vertical-align:bottom}.layui-laydate-header i{position:absolute;top:10px;padding:0 5px;color:#999;font-size:18px;cursor:pointer}.layui-laydate-header i.laydate-prev-y{left:15px}.layui-laydate-header i.laydate-prev-m{left:45px}.layui-laydate-header i.laydate-next-y{right:15px}.layui-laydate-header i.laydate-next-m{right:45px}.laydate-set-ym{width:100%;text-align:center;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.laydate-set-ym span{padding:0 10px;cursor:pointer}.laydate-time-text{cursor:default!important}.layui-laydate-content{position:relative;padding:10px;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.layui-laydate-content table{border-collapse:collapse;border-spacing:0}.layui-laydate-content td,.layui-laydate-content th{width:36px;height:30px;padding:5px;text-align:center}.layui-laydate-content td{position:relative;cursor:pointer}.laydate-day-mark{position:absolute;left:0;top:0;width:100%;line-height:30px;font-size:12px;overflow:hidden}.laydate-day-mark::after{position:absolute;content:'';right:2px;top:2px;width:5px;height:5px;border-radius:50%}.layui-laydate-footer{position:relative;height:46px;line-height:26px;padding:10px}.layui-laydate-footer span{display:inline-block;vertical-align:top;height:26px;line-height:24px;padding:0 10px;border:1px solid #C9C9C9;border-radius:2px;background-color:#fff;font-size:12px;cursor:pointer;white-space:nowrap;transition:all .3s}.layui-laydate-list>li,.layui-laydate-range .layui-laydate-main{display:inline-block;vertical-align:middle}.layui-laydate-footer span:hover{color:#5FB878}.layui-laydate-footer span.layui-laydate-preview{cursor:default;border-color:transparent!important}.layui-laydate-footer span.layui-laydate-preview:hover{color:#666}.layui-laydate-footer span:first-child.layui-laydate-preview{padding-left:0}.laydate-footer-btns{position:absolute;right:10px;top:10px}.laydate-footer-btns span{margin:0 0 0 -1px}.layui-laydate-list{position:absolute;left:0;top:0;width:100%;height:100%;padding:10px;background-color:#fff}.layui-laydate-list>li{position:relative;width:33.3%;height:36px;line-height:36px;margin:3px 0;text-align:center;cursor:pointer}.laydate-month-list>li{width:25%;margin:17px 0}.laydate-time-list>li{height:100%;margin:0;line-height:normal;cursor:default}.laydate-time-list p{position:relative;top:-4px;line-height:29px}.laydate-time-list ol{height:181px;overflow:hidden}.laydate-time-list>li:hover ol{overflow-y:auto}.laydate-time-list ol li{width:130%;padding-left:33px;height:30px;line-height:30px;text-align:left;cursor:pointer}.layui-laydate-hint{position:absolute;top:115px;left:50%;width:250px;margin-left:-125px;line-height:20px;padding:15px;text-align:center;font-size:12px}.layui-laydate-range{width:546px}.layui-laydate-range .laydate-main-list-1 .layui-laydate-content,.layui-laydate-range .laydate-main-list-1 .layui-laydate-header{border-left:1px solid #e2e2e2}.layui-laydate,.layui-laydate-hint{border:1px solid #d2d2d2;box-shadow:0 2px 4px rgba(0,0,0,.12);background-color:#fff;color:#666}.layui-laydate-header{border-bottom:1px solid #e2e2e2}.layui-laydate-header i:hover,.layui-laydate-header span:hover{color:#5FB878}.layui-laydate-content{border-top:none 0;border-bottom:none 0}.layui-laydate-content th{font-weight:400;color:#333}.layui-laydate-content td{color:#666}.layui-laydate-content td.laydate-selected{background-color:#B5FFF8}.laydate-selected:hover{background-color:#00F7DE!important}.layui-laydate-content td:hover,.layui-laydate-list li:hover{background-color:#eee;color:#333}.laydate-time-list li ol{margin:0;padding:0;border:1px solid #e2e2e2;border-left-width:0}.laydate-time-list li:first-child ol{border-left-width:1px}.laydate-time-list>li:hover{background:0 0}.layui-laydate-content .laydate-day-next,.layui-laydate-content .laydate-day-prev{color:#d2d2d2}.laydate-selected.laydate-day-next,.laydate-selected.laydate-day-prev{background-color:#f8f8f8!important}.layui-laydate-footer{border-top:1px solid #e2e2e2}.layui-laydate-hint{color:#FF5722}.laydate-day-mark::after{background-color:#5FB878}.layui-laydate-content td.layui-this .laydate-day-mark::after{display:none}.layui-laydate-footer span[lay-type=date]{color:#5FB878}.layui-laydate .layui-this{background-color:#009688!important;color:#fff!important}.layui-laydate .laydate-disabled,.layui-laydate .laydate-disabled:hover{background:0 0!important;color:#d2d2d2!important;cursor:not-allowed!important;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none}.laydate-theme-molv{border:none}.laydate-theme-molv.layui-laydate-range{width:548px}.laydate-theme-molv .layui-laydate-main{width:274px}.laydate-theme-molv .layui-laydate-header{border:none;background-color:#009688}.laydate-theme-molv .layui-laydate-header i,.laydate-theme-molv .layui-laydate-header span{color:#f6f6f6}.laydate-theme-molv .layui-laydate-header i:hover,.laydate-theme-molv .layui-laydate-header span:hover{color:#fff}.laydate-theme-molv .layui-laydate-content{border:1px solid #e2e2e2;border-top:none;border-bottom:none}.laydate-theme-molv .laydate-main-list-1 .layui-laydate-content{border-left:none}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li,.laydate-theme-grid .layui-laydate-content td,.laydate-theme-grid .layui-laydate-content thead,.laydate-theme-molv .layui-laydate-footer{border:1px solid #e2e2e2}.laydate-theme-grid .laydate-selected,.laydate-theme-grid .laydate-selected:hover{background-color:#f2f2f2!important;color:#009688!important}.laydate-theme-grid .laydate-selected.laydate-day-next,.laydate-theme-grid .laydate-selected.laydate-day-prev{color:#d2d2d2!important}.laydate-theme-grid .laydate-month-list,.laydate-theme-grid .laydate-year-list{margin:1px 0 0 1px}.laydate-theme-grid .laydate-month-list>li,.laydate-theme-grid .laydate-year-list>li{margin:0 -1px -1px 0}.laydate-theme-grid .laydate-year-list>li{height:43px;line-height:43px}.laydate-theme-grid .laydate-month-list>li{height:71px;line-height:71px} -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/layer/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/css/modules/layer/default/icon-ext.png -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/layer/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/css/modules/layer/default/icon.png -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/layer/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/css/modules/layer/default/loading-0.gif -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/layer/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/css/modules/layer/default/loading-1.gif -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/css/modules/layer/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/css/modules/layer/default/loading-2.gif -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/font/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/font/iconfont.eot -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/font/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/font/iconfont.ttf -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/font/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/font/iconfont.woff -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/layui/font/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nicholas-cool/P4_MoneyArrangeModule/4dc287c788dd5ab0247928ab7947ab038dc3fd4e/static/plugins/layui-v2.6.8/layui/font/iconfont.woff2 -------------------------------------------------------------------------------- /static/plugins/layui-v2.6.8/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 测试 - layui 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 17 | 18 |
19 | 20 | 21 |
22 | 23 |
24 |
25 |
    26 |
  • 你当前预览的是:layui-v
  • 27 |
  • layui 是一套开源的 Web UI(界面)组件库。这是一个极其简洁的演示页面
  • 28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /templates/04_money_arrange_module/04_money_arrange_update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 资金管理模块-升级功能 6 | 7 | 8 | {% include 'common/head_import.html' %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
21 |
22 |
23 | 24 | 25 |
27 |
28 |
导入旧版数据库:
29 | 30 | 32 |
33 | 注意:导入旧版数据库,会覆盖当前版本所有数据,请确保当前版本数据库中无有用数据! 34 |
35 |
36 |
37 | 38 | 39 | -------------------------------------------------------------------------------- /templates/04_money_arrange_module/04_money_record_auto_complete_data.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/04_money_arrange_module/JS01_load_money_page.html: -------------------------------------------------------------------------------- 1 | 80 | 81 | -------------------------------------------------------------------------------- /templates/04_money_arrange_module/Layer04_MoneyCategoryChartLayer.html: -------------------------------------------------------------------------------- 1 | 2 | {% include 'common/custome_functions/decimal_functions.html' %} 3 | 4 | 41 | 42 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /templates/04_money_arrange_module/Layer05_MoneyHistoryLayer.html: -------------------------------------------------------------------------------- 1 | 2 | {% include "common/custome_functions/date_functions.html" %} 3 | 4 | 40 | 41 | 140 | 141 | -------------------------------------------------------------------------------- /templates/common/custome_functions/date_functions.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/common/custome_functions/decimal_functions.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/common/custome_functions/get_select_list_functions.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/common/custome_functions/login_functions.html: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /templates/common/custome_functions/time_format_functions.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/common/custome_functions/timespan_functions.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/common/head_import.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {##} 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /templates/common/login_page.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Login Page 6 | 7 | 8 | {% include 'common/head_import.html' %} 9 | 10 | 11 | 12 |
13 | 45 |
46 | 47 | 48 | 49 | 82 | --------------------------------------------------------------------------------