暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

[PYTHON] 使用python实现一个miniWorkNote来记录工作内容

原创 大大刺猬 2024-06-26
195

导读&需求

刚才做了啥事情? 忘了…

今天有变更吗? 不记得了…

对于我这种记忆力不好的人来说, 是需要一个工具来记录 已完成待完成 的事情的. 比如一个记事本就行, 但我没法一眼就能找到还有哪些事情未做(都一个色儿). 那么就需要一个工具来帮我了.

要求:

  1. 离线: 出于安全考虑
  2. 使用简单: 不能比记事本复杂
  3. 方便: 能一眼看出来还有哪些事情要做
  4. 最好是有源码: 能简单的修改以实现更多的需求.
  5. 免费: 穷! 这种软件花钱感觉不值得…
  6. 安装简单: 不能有一大堆依赖包. 不能点下一步点到怀疑人生

好像/貌似没找到类似的软件. 这要求也太TM刁钻了. 谁提的需求? 原来是我自己, 那没事了.

这种情况, 作为一个MYSQL-DBA应该怎么办呢? 那当然是自己搓一个了卅!

设计&实现

语言: 直接选python, 成本最低. 改起来也简单, win/linux都兼容.
前端: 可以使用TK写界面, 但太丑了. 而且不熟. 那就使用 html吧
存储: 可以直接将py对象写入文件, 但兼容性不好, 也可以自己定义数据结构, 但对于修改操作太麻烦了. 所以直接使用sqlite3 (py也自带该接口)
交互: jq/ajax之类的太麻烦了, 直接XMLHttpRequest. 同理界面也使用bootstrap之类的, 稍微排版下即可(没得审美要求). 后端也不是有flask之类的, 直接走http server

为了方便使用, 这些功能就放一个文件就行.

作为一个完整的程序, 还得做到正常退出, 即处理SIGTERM之类的信号量(退出前要保存数据)

考虑到兼容性和数据恢复之类的, 我们也会记录DML语句到日志文件, 就相当于REDO LOG

本来打算做用户验证的, 但没必要, 就自己使用就行, 还安全点. 而且sqlite3也不支持多线程

我们的需求主要是记录已做/将做的事情 和 某个时间(确定)的变更. 所以可以设计两张表 workplan.
work 记录已完成的工作和未完成的工作. 这个通常是没得时间限制的. 但涉及到状态变化, 即某件事情可能已经完成了, 也可能我先记录下, 但还没有完成. 工作内容可能也是会变化的. 所以要有编辑/修改功能
plan 是计划安排, 比如周五晚上做切换演练. 没有那么多状态, 完成后, 只需要反馈下即可.
DDL参考如下

create table work( work_id bigint primary key, -- 使用时间戳作为ID吧(为了兼容性,就不使用comment了) work_author varchar(255) default 'ddcw', -- 相关人员 work_dt datetime, -- 修改时间 work_status varchar(20), -- 完成/未完成/异常/已删除 work_si varchar(255), -- 简述 work_detail text, -- 详情 work_other varchar(255) -- 保留字段 ); create table plan( plan_id bigint primary key, plan_author varchar(255) default 'ddcw', plan_dt datetime, -- 变更时间 plan_update datetime, -- 这行数据的修改时间, 可以当作是完成时间 plan_status varchar(20), -- 状态, 完成之后要手动设置为已完成. (已完成/未完成/异常) plan_contact, -- 对接人 plan_si varchar(255), -- 任务内容 plan_detail varchar(255), -- 任务内容 plan_other varchar(255) -- 保留字段 );

最开始是设计登录选项的, 所以有author.

演示

原神启动:
我这里就用win环境演示吧. 除了要求python3, 没得其它任何要求的. 嘎嘎好用.
为了方便使用, html也是直接放在py里面的…

python miniWorkNote.py

image.png

然后浏览器打开那个地址, 就能看到如下信息(我这里有部分测试数据)
image.png
点击本/上周周报, 会自动复制相关内容到粘贴板
image.png

点击修改可以修改相关信息
image.png

添加也是可以的
image.png
image.png

其它的我就不测试了. 感兴趣的自己试一试.

总结

不使用框架/第三方包之类的, 写起来还是蛮舒服的(如果偶尔写写的话).
工具刚写完不多久, 目前用起来还不错(边写边用,边用边写), 后续好用的话再改进改进(感觉刷新按钮没啥用, 一直都是自动刷新的…).

一些中间遇到的坑, 前端,数据库的都有…
前端: Date的月份得是从0开始的异世界. tr在元素附设置的背景颜色会被td(class)覆盖掉
后端: sqlite3不支持多线程, 也不支持comment

生成的.sql文件也是可以在mysql等数据库回放的. SQL版的REDO LOG

附源码

源码地址: https://github.com/ddcw/ddcw/tree/master/python/miniworknote

#!/usr/bin/env python # write by ddcw https://github.com/ddcw # 一个脚本, 用来记录做过的事情, 和安排. # 只使用内置Python模块 # 理由: 外包,没得专门的系统来管理, 全靠记忆力.... # 只支持本地访问, 不支持远程访问(简单一点) # 使用方法: python3 miniWorkNote.py 然后浏览器访问即可 # 数据存储在 .db (sqlite3) 文件, 最开始打算不使用数据库的, 但修改不方便, 就还是整了一个 # 还会保留一份SQL文件. 方便写入其它数据库 # 界面设计 """ ----NOTE------------------------PLAN---------- | worktime | 变更时间(计划时间) | | 工作内存简述 | 变更内容 | | 详情 | 对接人 | | | | | ADD FLUSH | ADD FLUSH | ----------------------------------------------- | 本周周报 上周周报 | ----------------------------------------------- | WORK 1 改|详|删 | PLAN 1 详|完成 | | WORK 2 改|详|删 | PLAN 2 详|完成 | | WORK 3 改|详|删 | PLAN 3 详|完成 | | WORK 4 改|详|删 | | """ # CHAGE LOG # at 2024.06.21 init # at 2024.06.26 finish # 变量名. BIND_HOST = '127.0.0.1' # 监听的ip BIND_PORT = 80 # 监听的端口 DB_FILE = 'miniWorkNote.db' # 保存数据的db文件, sqlite3 SQL_FILE = 'miniWorkNote.sql' # 保存数据的sql, 含DDL. like REDO SAVE_DAYS = 3650000 # 保存天数. (还没实现...) # 表结构定义 MINIWN_DDL = [""" create table work( work_id bigint primary key, -- 使用时间戳作为ID吧(为了兼容性,就不使用comment了) work_author varchar(255) default 'ddcw', -- 相关人员 work_dt datetime, -- 修改时间 work_status varchar(20), -- 完成/未完成/异常/已删除 work_si varchar(255), -- 简述 work_detail text, -- 详情 work_other varchar(255) -- 保留字段 ); """,""" create table plan( plan_id bigint primary key, plan_author varchar(255) default 'ddcw', plan_dt datetime, -- 变更时间 plan_update datetime, -- 这行数据的修改时间, 可以当作是完成时间 plan_status varchar(20), -- 状态, 完成之后要手动设置为已完成. (已完成/未完成/异常) plan_contact, -- 对接人 plan_si varchar(255), -- 任务内容 plan_detail varchar(255), -- 任务内容 plan_other varchar(255) -- 保留字段 ); """ ] import os,sys,signal import datetime import time import sqlite3 from http.server import HTTPServer, BaseHTTPRequestHandler from socketserver import ThreadingMixIn import threading import urllib.parse import json # 做全局初始化 if os.path.exists(SQL_FILE): SQL_FD = open(SQL_FILE,'a') else: SQL_FD = open(SQL_FILE,'w') # 更新下基础DDL for ddl in MINIWN_DDL: SQL_FD.write(ddl) SQL_FD.write('\n') # 再来个换行, 好看点 SQL_FD.flush() if os.path.exists(DB_FILE): CONN = sqlite3.connect(DB_FILE) else: # 不存在的话, 就初始化表结构 CONN = sqlite3.connect(DB_FILE) for ddl in MINIWN_DDL: cursor = CONN.cursor() cursor.execute(ddl) CONN.commit() # 捕获15信号量, 做conn和fd的关闭 def signal_15_handler(sig,frame): print("将自动退出") CONN.commit() CONN.close() SQL_FD.flush() SQL_FD.close() print("已保存退出") sys.exit(1) signal.signal(signal.SIGTERM, signal_15_handler) # kill -15 signal.signal(signal.SIGINT, signal_15_handler) # ctrl+c def runsql(sql): try: cursor = CONN.cursor() cursor.execute(sql) data = cursor.fetchall() status = True sql += f"{';' if data[-1:] != ';' else ''}" + "\n" if sql[:7].upper() != "SELECT ": SQL_FD.write(sql) except Exception as e: status = False data = str(e) sql = "-- /* MAY BE ERROR " + data + " */" + sql + f"{';' if data[-1:] != ';' else ''}" + "\n" SQL_FD.write(sql) SQL_FD.flush() return status,data # 啊哈! sqlite3不支持多线程 #class ThreadingSimpleServer(ThreadingMixIn, HTTPServer): # pass class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): url_components = urllib.parse.urlparse(self.path) query = urllib.parse.parse_qs(url_components.query) path = url_components.path if path == '/work': #self.handle_work_request(query) self.handle_html_request() elif path == '/plan': #self.handle_work_plan(query) self.handle_html_request() else: self.handle_html_request() def do_POST(self): content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) data = json.loads(post_data) print('PATH',self.path,"收到的数据:",data) if self.path == '/work': # 判断action if data['action'] == 'add': # 新增 if data['work_dt'] == '': data['work_dt'] = str(datetime.datetime.now()).split('.')[0] sql = f"insert into work values({int(time.time()*1000)},'ddcw',\'{data['work_dt']}\',\'{data['work_status']}\', \'{data['work_si']}\', \'{data['work_detail']}\', '')" elif data['action'] == 'delete': # 删除 # sql = f"delete from work where work_id={data['work_id']}" sql = f"update work set work_status='已删除' where work_id={data['work_id']}" elif data['action'] == 'update': # 更新 sql = f"update work set work_status=\'{data['work_status']}\', work_si=\'{data['work_si']}\', work_dt='{str(datetime.datetime.now()).split('.')[0]}' where work_id = {data['work_id']}" elif data['action'] == 'select': # 查询的, 默认就最近50条 sql = 'select work_id,work_dt,work_status,work_si,work_detail from work where work_status !="已删除" order by work_dt desc limit 50' else: sql = "select '不知道你在干什么, 但我只管记录... {data}'" #self.wfile.write(post_data) # 慕容复 elif self.path == '/plan': # 判断action if data['action'] == 'add': if data['plan_dt'] == '': data['plan_dt'] = str(datetime.datetime.now()).split('.')[0] sql = f"insert into plan values({int(time.time()*1000)}, 'ddcw', \'{data['plan_dt']}\', \'{str(datetime.datetime.now()).split('.')[0]}\', '未完成', \'{data['plan_contact']}\', \'{data['plan_si']}\', \'{data['plan_detail']}\', '' )" elif data['action'] == 'status': sql = f"update plan set plan_status=\'{data['plan_status']}\',plan_update=\'{str(datetime.datetime.now()).split('.')[0]}\' where plan_id={data['plan_id']}" elif data['action'] == 'select': sql = f"select plan_id,plan_dt,plan_contact,plan_status,plan_si,plan_detail from plan where plan_status='未完成' order by plan_dt" # 就不要limit了, 全TM查出来 else: sql = "select '不知道你在干什么, 但我只管记录... {data}'" elif self.path == '/report': if data['action'] == 1: # 上周周报 sql = f"select work_id,work_dt,work_si from work where work_dt >= date('now', 'weekday 0', '-13 days') and work_dt < date('now', 'weekday 0', '-6 day') and work_status!='已删除'" else : # 本周周报 sql = f"select work_id,work_dt,work_si from work where work_dt >= date('now', 'weekday 0', '-6 days') and work_dt < date('now', 'weekday 0', '+1 day') and work_status!='已删除'" else: sql = "select '不知道你在干什么, 但我只管记录... {data}'" status,data = runsql(sql) self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() rbdata = json.dumps({'data':data,'status':status}).encode('utf-8') print('返回数据',{'data':data,'status':status}) self.wfile.write(rbdata) def handle_html_request(self): #pass # 就返回首页html就行. 其实就这一个page... self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() html_content = ''' <html> <head> <title>DDCW miniWorkNote</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body { font-family: Arial, sans-serif; } .container { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 10px; } .note, .plan { border: 1px solid #ccc; padding: 10px; } .header { background-color: #f0f0f0; padding: 5px; font-weight: bold; text-align: center; } button { margin-right: 5px; } .footer { display: flex; justify-content: space-between; margin-top: 10px; } .note>div:last-child, .plan>div:last-child { border-top: 1px solid #ccc; margin-top: 10px; padding-top: 10px; } .week-report { border-top: 1px solid #ccc; padding-top: 10px; } td { padding: 15px; } tr {background-color:LightGray;} tbody tr:hover {background-color: yellow;} </style> <script> // 周报 function work_report(n){ data = {'action':n} var xhr = new XMLHttpRequest(); xhr.open('POST', '/report', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); vv = "" if (rdata.data.length == 0){alert('无周报内容, 你在摸鱼?')} else{ for (var row in rdata.data){ vv += rdata.data[row][1] + " " + rdata.data[row][2] + "\\n" } document.getElementById('hidden_report').style.display = 'block'; document.getElementById('hidden_report').value = vv; document.getElementById('hidden_report').select(); document.execCommand('copy'); document.getElementById('hidden_report').style.display = 'none'; avv = "已复制\\n" + vv alert(avv); } } } data1 = JSON.stringify(data); xhr.send(data1) } function modify_work(n){ work_status = document.getElementById("work_status_"+n).innerHTML work_si = document.getElementById("work_si_"+n).innerHTML // select work_status_select = "<select id='work_status_op_"+n+"'><option value='已完成'>已完成</option><option value='未完成'>未完成</option><option value='异常'>异常</option></select>" document.getElementById("work_status_"+n).innerHTML = work_status_select // 简述 work_si_input = "<textarea cols=30 id='work_si_op_"+n+"'>"+work_si+"</textarea>" document.getElementById("work_si_"+n).innerHTML = work_si_input // 操作: 确认:提交数据. 取消:重新刷新即可... work_ctl_button = "<input value='确认' type='button' onclick='modify_work_op("+n+")'><input value='取消' type='button' onclick='flush_work()'>" document.getElementById("work_ctl_"+n).innerHTML = work_ctl_button } // 提交确认的数据 function modify_work_op(n){ work_status_idx = document.getElementById('work_status_op_'+n).selectedIndex; data = { 'action':'update', 'work_id':n, 'work_status':document.getElementById('work_status_op_'+n).options[work_status_idx].value, 'work_si':document.getElementById("work_si_op_"+n).value } var xhr = new XMLHttpRequest(); xhr.open('POST', '/work', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); if (rdata.status){ flush_work() } else{ alert(rdata.data) } } } data1 = JSON.stringify(data); xhr.send(data1) } function flush_work(){ // 刷新的. data = {'action':'select'} var xhr = new XMLHttpRequest(); xhr.open('POST', '/work', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); if (rdata.status){ tbodyhtml = "" document.getElementById('work_tbody').innerHTML="看不见的"; for (var row in rdata['data']){ tbodyhtml += "<tr>" tbodyhtml += "<td>"+ rdata['data'][row][0] +"</td>" tbodyhtml += "<td >"+ rdata['data'][row][1] +"</td>" tbodyhtml += "<td id='work_status_"+rdata['data'][row][0]+"' " if (rdata['data'][row][2] == "异常"){ tbodyhtml += " style='background-color:red'>"+ rdata['data'][row][2] +"</td>" } else if (rdata['data'][row][2] == "未完成"){ tbodyhtml += " style='background-color:yellow;'>"+ rdata['data'][row][2] +"</td>" } else{ tbodyhtml += ">"+ rdata['data'][row][2] +"</td>" } tbodyhtml += "<td id='work_si_"+rdata['data'][row][0]+"'>"+ rdata['data'][row][3] +"</td>" tbodyhtml += "<td id='work_ctl_"+rdata['data'][row][0]+"'>" tbodyhtml += "<input type='button' value='修改' onclick='modify_work("+rdata['data'][row][0]+")'>" tbodyhtml += "<input type='button' value='详情' onclick='alert(\\""+rdata['data'][row][4]+"\\")'>" tbodyhtml += "<input type='button' value='删除' onclick='delete_work("+rdata['data'][row][0]+")'>" tbodyhtml += "</td>" tbodyhtml += "</tr>" } document.getElementById('work_tbody').innerHTML=tbodyhtml } } } data1 = JSON.stringify(data); xhr.send(data1) } function delete_work(n){ data = {"action":'delete','work_id':n} var xhr = new XMLHttpRequest(); xhr.open('POST', '/work', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); if (rdata.status){ //alert("删除成功") //setTimeout(function(){flush_work()},500) flush_work() } else { alert(rdata.data) } } } data1 = JSON.stringify(data); xhr.send(data1) } function add_work(){ formdata = new FormData(); // dio用没得, 还不如直接json work_status_idx = document.getElementById('work_status').selectedIndex; data = { "action":"add", "work_dt":document.getElementById('work_dt').value, "work_status":document.getElementById('work_status').options[work_status_idx].value, "work_si":document.getElementById('work_si').value, "work_detail":document.getElementById('work_detail').value, }; var xhr = new XMLHttpRequest(); xhr.open('POST', '/work', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { //alert(xhr.responseText) var rdata = JSON.parse(xhr.responseText); if (rdata.status) { //flush_work() setTimeout(function(){flush_work()},100) } } } data1 = JSON.stringify(data); xhr.send(data1) // setTimeout(function(){flush_work()},500) /* send 完之后刷新 */ } // 判断字符串是否是今天 function is_today(dt){ today = new Date(); y = today.getFullYear(); m = today.getMonth() + 1; d = today.getDate(); dt2 = dt.split('-'); return y == dt2[0] && m == dt2[1] && d == dt2[2]; } // 判断字符串是否是这周 function is_week(dt){ today = new Date(); dt2 = new Date(dt); fstweek = new Date(today.getTime() - today.getDay() * 86400000); lstweek = new Date(fstweek.getTime() + 6 * 86400000); return dt2 >= fstweek && dt2 <= lstweek; } // plan相关的了 function flush_plan(){ data = {'action':'select'} var xhr = new XMLHttpRequest(); xhr.open('POST', '/plan', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); document.getElementById('plan_tbody').innerHTML="看不见的"; tbodyhtml = "" // 如果是今天的变更, 就红色. 本周的就黄色, 其它的不管 for (var row in rdata['data']){ tbodyhtml += "<tr>" tbodyhtml += "<td>"+ rdata['data'][row][0] +"</td>" tbodyhtml += "<td>"+ rdata['data'][row][1] +"</td>" tbodyhtml += "<td>"+ rdata['data'][row][2] +"</td>" tbodyhtml += "<td " if (is_today(rdata['data'][row][1])){ tbodyhtml += "style='background-color:red'" } else if (is_week(rdata['data'][row][1])){ tbodyhtml += "style='background-color:yellow'" } tbodyhtml += " >" + rdata['data'][row][3] +"</td>" //tbodyhtml += "<td>"+ rdata['data'][row][3] +"</td>" tbodyhtml += "<td>"+ rdata['data'][row][4] +"</td>" tbodyhtml += "<td>" tbodyhtml += "<input type='button' value='详情' onclick='alert(\\""+rdata['data'][row][5]+"\\")'>" tbodyhtml += "<input type='button' value='设置为已完成' onclick='plan_commit("+rdata['data'][row][0]+")'>" tbodyhtml += "</td>" tbodyhtml += "</tr>" } document.getElementById('plan_tbody').innerHTML=tbodyhtml } } data1 = JSON.stringify(data); xhr.send(data1) } function add_plan(){ data = { 'action':'add', "plan_dt":document.getElementById('plan_dt').value, "plan_contact":document.getElementById('plan_contact').value, "plan_si":document.getElementById('plan_si').value, "plan_detail":document.getElementById('plan_detail').value, } var xhr = new XMLHttpRequest(); xhr.open('POST', '/plan', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); if (rdata.status) {setTimeout(function(){flush_plan()},100)} } } data1 = JSON.stringify(data); xhr.send(data1) } function plan_commit(n){ data = {'action':'status','plan_status':'已完成','plan_id':n} var xhr = new XMLHttpRequest(); xhr.open('POST', '/plan', true); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var rdata = JSON.parse(xhr.responseText); if (rdata.status) {setTimeout(function(){flush_plan()},100)} } } data1 = JSON.stringify(data); xhr.send(data1) } </script> </head> <body> <div class="container"> <div class="note"> <div class="header">NOTE</div> <div> <label>任务时间:</label> <input type='datetime-local', id='work_dt'> <label>任务状态</label> <select id='work_status'><option value='未完成'>未完成</option><option value='已完成'>已完成</option><option value='异常'>异常</option></select> </div> <div align="left"><label>工作内容简述:</label> <textarea id='work_si' cols="50" ></textarea></div> <div align="left">工作内容详情: <textarea id='work_detail' rows="3" cols="50"></textarea></div> <div align="center"> <input style="width: 80px; height: 40px; padding: 8px; margin: 4px;" type='button' value='添加' class='btn' onclick="add_work()"> <input style="width: 80px; height: 40px; padding: 8px; margin: 4px;" type='button' value='刷新' class='btn' onclick="flush_work()"> </div> <div class="week-report"> <input type='button' value='本周周报' class='btn' onclick="work_report(0)"> <input type='button' value='上周周报' class='btn' onclick="work_report(1)"> <textarea id='hidden_report' style='display:none'></textarea> <!-- 不显示的话, 就无法复制 . 所以得打开显示,然后复制, 然后取消显示 --> </div> <table> <thead> <tr> <td>任务ID</td> <td>修改时间</td> <td>任务状态</td> <td>任务简述</td> <td>操作</td> </tr> </thead> <tbody id='work_tbody'> </tbody> </table> </div> <div class="plan"> <div class="header">PLAN</div> <div> <label>任务时间:</label> <input type='date', id='plan_dt'> <label>对接人:</label> <input id='plan_contact' value='不知道'> </div> <div align="left"><label>工作内容简述:</label> <textarea id='plan_si' cols="50" ></textarea></div> <div align="left"><label>工作内容详情:</label> <textarea id='plan_detail' cols="50" rows="3" ></textarea></div> <div align="center"> <input style="width: 80px; height: 40px; padding: 8px; margin: 4px;" type='button' value='添加' class='btn' onclick="add_plan()"> <input style="width: 80px; height: 40px; padding: 8px; margin: 4px;" type='button' value='刷新' class='btn' onclick="flush_plan()"> </div> <div> <table> <thead> <tr> <td>ID</td> <td>时间</td> <td>对接人</td> <td>状态</td> <td>简述</td> <td>操作</td> </tr> </thead> <tbody id='plan_tbody'> </tbody> </table> </div> </div> </div> </body> <script> window.onload = function() { flush_work(); flush_plan(); // 设置默认时间. 前端获取时间太麻烦了(还得考虑月份+1就离了个大谱). 还是后端整吧... var default_dt = "''' + str(datetime.datetime.now()).split('.')[0] + '''" var default_d = "''' + str(datetime.datetime.now()).split()[0] + '''" document.getElementById('work_dt').value = default_dt; document.getElementById('plan_dt').value = default_d; }; </script> </html> ''' self.wfile.write(html_content.encode('utf-8')) def handle_work_request(self,query): pass def handle_work_plan(self,query): pass #def run(server_class=ThreadingSimpleServer, handler_class=SimpleHTTPRequestHandler): def run(server_class=HTTPServer, handler_class=SimpleHTTPRequestHandler): server_address = (BIND_HOST,BIND_PORT) httpd = server_class(server_address, handler_class) print(f'http://{BIND_HOST}:{BIND_PORT}') httpd.serve_forever() if __name__ == '__main__': run() # 润
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论