前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[PYTHON] 使用python实现一个miniWorkNote来记录工作内容

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

原创
作者头像
大大刺猬
发布2024-06-26 17:58:59
880
发布2024-06-26 17:58:59
举报
文章被收录于专栏:大大刺猬大大刺猬

导读&需求

刚才做了啥事情? 忘了..

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

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

要求:

  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参考如下

代码语言:sql
复制
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里面的....

代码语言:shell
复制
python miniWorkNote.py

然后浏览器打开那个地址, 就能看到如下信息(我这里有部分测试数据)

点击本/上周周报, 会自动复制相关内容到粘贴板

点击修改可以修改相关信息

添加也是可以的

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

总结

不使用框架/第三方包之类的, 写起来还是蛮舒服的(如果偶尔写写的话).

工具刚写完不多久, 目前用起来还不错(边写边用,边用边写), 后续好用的话再改进改进(感觉刷新按钮没啥用, 一直都是自动刷新的...).

一些中间遇到的坑, 前端,数据库的都有...

前端: Date的月份得是从0开始的异世界. tr在元素附设置的背景颜色会被td(class)覆盖掉

后端: sqlite3不支持多线程, 也不支持comment

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

附源码

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

代码语言:python
代码运行次数:0
复制
#!/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() # 润

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 导读&需求
  • 设计&实现
  • 演示
  • 总结
  • 附源码
相关产品与服务
数据保险箱
数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档