diff --git a/backend/migrations/seed_data.py b/backend/migrations/seed_data.py index 131dc19..8d5da9b 100644 --- a/backend/migrations/seed_data.py +++ b/backend/migrations/seed_data.py @@ -1,127 +1,284 @@ -# seed_data.py — 初始示例数据填充(仅在空库时执行一次) -# 从 flask_app.py 搬迁,被 migrations/seed.py 调用 +# seed_data.py — 丰富示例数据填充(仅在空库时执行一次) +# 为每个工作台生成 > 1 亿的总规模,覆盖万/十万/百万/千万量级 + +import json +import random +random.seed(42) from datetime import date -from pathlib import Path -from db import db, _exec, one, WEIXIN_BASE -from helpers import add_file_index +from db import db, _exec, one + +TENANTS = ["科普·无界", "科研·无界", "医患·无界", "MCN·无界", "学会·无界"] + +# 每个工作台的差异化客户和项目模板 +TENANT_CUSTOMERS = { + "科普·无界": [ + ("信达生物", ["肿瘤免疫科普年度框架", "信迪利单抗医生教育", "CSCO卫星会项目", "患者随访管理系统"]), + ("百利天恒", ["BL-B01D1上市前教育", "双抗药物科普专区", "数字人患教视频"]), + ("齐鲁制药", ["多产品线医生教育", "安可达学术推广", "基层医疗科普巡讲"]), + ("天广实生物", ["血液肿瘤科普项目", "CD20单抗学术推广"]), + ("复星医药", ["复宏汉霖学术年会", "CAR-T患者教育"]), + ], + "科研·无界": [ + ("北京协和医院", ["临床研究数据采集系统", "罕见病队列研究", "AI辅助诊断验证"]), + ("中国人民解放军总医院", ["战场创伤数据平台", "远程会诊系统"]), + ("复旦大学附属华山医院", ["脑卒中AI预警系统", "神经内科临床研究"]), + ("中山大学肿瘤防治中心", ["鼻咽癌早筛项目", "真实世界研究平台"]), + ("四川大学华西医院", ["精准医学大数据平台", "基层科研能力提升"]), + ], + "医患·无界": [ + ("平安好医生", ["在线问诊科普内容", "慢病管理专区", "随访SOP标准化"]), + ("微医集团", ["互联网医院患教系统", "云药房科普推送"]), + ("丁香园", ["医生社区运营升级", "用药助手科普专区"]), + ("圆心科技", ["药房患者教育", "DTP药房随访管理", "罕见病用药指导"]), + ("国药控股", ["国药患者管理平台", "肿瘤药房随访系统"]), + ], + "MCN·无界": [ + ("抖音健康", ["医学IP孵化矩阵", "短视频科普内容制作", "健康直播运营"]), + ("快手健康", ["医生个人IP打造", "快手健康科普专区"]), + ("小红书医美", ["皮肤健康科普矩阵", "医美科普合规审核"]), + ("B站知识区", ["深度科普纪录片", "医学动画制作"]), + ("微信视频号", ["医生科普直播运营", "企业微信科普推送"]), + ], + "学会·无界": [ + ("中华医学会", ["学术年会数字化平台", "继续教育学分系统", "基层医生培训"]), + ("中国医师协会", ["医师定考管理系统", "专科医师培训平台"]), + ("中国抗癌协会", ["肿瘤科普万里行", "CACA指南推广"]), + ("中国药学会", ["合理用药科普平台", "药师继续教育"]), + ("中华护理学会", ["护理科研能力提升", "专科护士培训系统"]), + ], +} + +# 签约金额模板:每个工作台的千万/百万/十万/万级别项目的签约金额 +def generate_sign_amounts(): + amounts = [] + # 千万级:2个(4000-7000万) + amounts.append(random.randint(4500, 7000) * 10000) + amounts.append(random.randint(4000, 6500) * 10000) + # 百万级:4个(400-980万) + for _ in range(4): + amounts.append(random.randint(400, 980) * 10000) + # 十万级:4个(30-98万) + for _ in range(4): + amounts.append(random.randint(30, 98) * 10000) + # 万级:3个(3-10万) + for _ in range(3): + amounts.append(random.randint(3, 10) * 10000) + return sorted(amounts, reverse=True) + + +# 生成 budget_data JSON(每月确收/毛利/回款) +def make_budget_data(sign_amount, signed_month, status): + """生成12个月的预算数据""" + months = [] + start_m = int(signed_month.split("-")[1]) if signed_month else 1 + rev_total = sign_amount if status == "已签约" else int(sign_amount * random.uniform(0.3, 0.7)) + gross_rate = random.uniform(0.25, 0.55) + payment_rate = random.uniform(0.7, 0.95) + + for i in range(12): + m = (start_m + i - 1) % 12 + 1 + yr = 2026 + (start_m + i - 1) // 12 + month_key = f"{yr}-{m:02d}" + + # 越往后确收越多 + progress = min(1.0, (i + 1) / max(1, random.randint(3, 8))) + rev = round(rev_total * progress * random.uniform(0.85, 1.15)) + gross = round(rev * gross_rate * random.uniform(0.9, 1.1)) + payment = round(rev * payment_rate * random.uniform(0.85, 1.15)) + cost = round(rev * random.uniform(0.3, 0.5)) + paid = round(cost * random.uniform(0.8, 1.0)) + + months.append({ + "month": month_key, + "rev": rev, + "gross": gross, + "payment": payment, + "cost": cost, + "paid": paid, + }) + return json.dumps(months) + + +# 生成 expense_data(平台费用) +def make_expense_data(): + """生成若干条费用记录""" + items = [] + expense_types = ["平台采购", "人力成本", "研发费用", "通用费用", "奖金"] + for q in range(1, 7): + for et in random.sample(expense_types, random.randint(2, 4)): + month_str = f"2026-{q:02d}" + amount = random.randint(5000, 150000) + incurred = int(amount * random.uniform(0.6, 1.0)) + items.append({"month": month_str, "expense_type": et, "amount": amount, "incurred": incurred}) + return json.dumps(items) def seed_db(): - """填充初始示例数据(仅在空库时执行一次)""" + """填充丰富示例数据""" conn = db() try: if one(conn, "SELECT id FROM sales_leads LIMIT 1"): - return # 已有数据,跳过 + return - sales = [ - ("齐鲁制药", "P0", "跟进中", "多产品线科普年度框架,需推进高层沟通。"), - ("百利天恒", "P0", "方案中", "BL-B01D1 上市前医生教育机会,准备方案。"), - ("信达生物", "P0", "已签约", "现有科普项目升级/续约,重点保障执行。"), - ("三生制药", "P1", "待跟进", "多科室医生教育+患者科普机会。"), - ("天广实生物", "P1", "待跟进", "血液肿瘤医生教育机会。"), + today = date.today().isoformat() + pf_ids = {} # tenant -> [project_finance_ids] + + for tenant in TENANTS: + customers = TENANT_CUSTOMERS[tenant] + amounts = generate_sign_amounts() + cust_idx = 0 + + for i, sign_amount in enumerate(amounts): + cust_name, projects = customers[cust_idx % len(customers)] + proj_name = projects[i % len(projects)] if i < len(projects) else f"{cust_name} 项目{i+1}" + cust_idx += i % 3 + + # 70% 已签约, 30% 待签约 + status = "已签约" if random.random() < 0.7 else "待签约" + sign_month = f"2026-{random.randint(1, 6):02d}" if status == "已签约" else "" + project_code = f"{tenant[:2]}-2026-{i+1:03d}" + business_type = random.choice(["学术推广", "医生教育", "患者管理", "科研合作", "科普内容"]) + + # 如果签约,总额 = 签约额;否则为0 + total_rev = int(sign_amount * random.uniform(0.8, 1.0)) if status == "已签约" else 0 + total_gross = int(total_rev * random.uniform(0.25, 0.5)) if status == "已签约" else 0 + total_payment = int(total_rev * random.uniform(0.7, 0.9)) if status == "已签约" else 0 + total_cost = int(total_rev * random.uniform(0.3, 0.5)) if status == "已签约" else 0 + total_paid = int(total_cost * random.uniform(0.7, 0.95)) if status == "已签约" else 0 + + budget_data = make_budget_data(sign_amount, sign_month, status) + expense_data = make_expense_data() if status == "已签约" else "[]" + task_data = json.dumps([ + {"task": f"{proj_name} 项目启动", "due": f"2026-{min(12, 1+i//2):02d}-01", "status": "已完成" if i < 8 else "进行中"}, + {"task": f"方案评审与确认", "due": f"2026-{min(12, 2+i//2):02d}-15", "status": "已完成" if i < 6 else "进行中"}, + {"task": f"内容生产阶段", "due": f"2026-{min(12, 3+i//2):02d}-30", "status": "进行中"}, + ]) + + cur = _exec(conn, """ + INSERT INTO project_finances + (tenant, project_id, business_type, customer_name, sign_amount, sign_month, status, + sales_person, total_rev, total_gross, total_payment, total_cost, total_paid, + budget_data, expense_data, task_data, + project_code, start_date, end_date, task_type, task_count, + service_fee_standard, project_manager, contact_name, contact_phone, other_info, + created_at, updated_at) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) + """, ( + tenant, f"PF-{tenant[:2]}-{i+1:04d}", business_type, cust_name, + sign_amount, sign_month, status, + f"销售{random.choice(['A','B','C','D'])}", total_rev, total_gross, + total_payment, total_cost, total_paid, + budget_data, expense_data, task_data, + project_code, f"2026-{random.randint(1,3):02d}-01", f"2026-{random.randint(9,12):02d}-30", + random.choice(["内容生产", "视频制作", "专家访谈", "学术会议", "患者教育"]), + random.randint(10, 100), random.choice([800, 1000, 1200, 1500, 2000]), + f"经理{random.choice(['A','B','C'])}", f"联系人{random.randint(1,20)}", + f"1380000{random.randint(1000,9999)}", f"备注信息 {i+1}", + today, today, + )) + # Store for later use + pf_ids.setdefault(tenant, []).append((cur.lastrowid, proj_name, cust_name, status)) + + # operation_projects: 每个工作台 3-5 个 + op_count = random.randint(3, 5) + for j in range(op_count): + pf_row = random.choice(pf_ids[tenant]) + cur2 = _exec(conn, """ + INSERT INTO operation_projects + (tenant, project_name, project_version, project_type, project_status, + current_stage, owner, start_date, end_date, target_customer, customer_need, + expected_contract_amount, expected_sign_date, sign_probability, next_action, + sop_stage, execution_progress, current_deliverable, notes) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) + """, ( + tenant, f"{pf_row[1]} 运营方案", "v1.0", + "execution" if random.random() < 0.6 else "opportunity", + "SOP 执行中" if random.random() < 0.6 else "方案已提交", + random.choice(["商务推进", "内容生产", "渠道分发"]), + f"OPC{random.randint(1,5)}", "2026-06-01", "2026-12-31", + pf_row[2], "科普内容项目执行与管理", + pf_row[0] if pf_row[3] == "已签约" else random.randint(50, 300) * 10000, + "2026-06", 70 + random.randint(0, 30), + "补齐版本要求文件并更新下一节点", + "SOP 执行", random.randint(30, 80), "当前交付物 v1.0", + "运营备注", + )) + _exec(conn, """ + INSERT INTO follow_up_records + (target_type, target_id, followed_at, content, next_action, follower) + VALUES (?,?,?,?,?,?) + """, ( + "operation", cur2.lastrowid, today, + f"运营项目跟进:{pf_row[1]},当前进度正常", "继续推进下一里程碑", + f"销售{random.choice(['A','B'])}", + )) + + # expense_records: 每个工作台 15-20 条 + expense_count = random.randint(15, 20) + expense_types = ["通用费用", "平台采购", "人力成本", "研发费用", "奖金", "其他"] + for j in range(expense_count): + et = random.choice(expense_types) + month_str = f"2026-{random.randint(1, 6):02d}" + amount = random.randint(5000, 500000) + incurred = int(amount * random.uniform(0.5, 1.0)) + _exec(conn, """ + INSERT INTO expense_records + (tenant, expense_type, expense_month, amount, incurred_amount, notes) + VALUES (?,?,?,?,?,?) + """, ( + tenant, et, month_str, amount, incurred, + f"{et} - {random.choice(['季度预算', '紧急支出', '常规费用', '项目费用'])}", + )) + + print(f"[seed] {tenant}: {len(amounts)} 项目, {op_count} 运营项目, {expense_count} 费用记录") + + conn.commit() # 提交所有工作台的核心数据 + + # 全域数据:sales_leads 对所有 tenant 可见 + global_sales = [ + ("齐鲁制药", "P0", "跟进中", "多产品线科普年度框架"), + ("百利天恒", "P0", "方案中", "BL-B01D1 上市前医生教育"), + ("信达生物", "P0", "已签约", "现有科普项目升级/续约"), + ("三生制药", "P1", "待跟进", "多科室医生教育+患者科普"), + ("天广实生物", "P1", "待跟进", "血液肿瘤医生教育机会"), ] - for customer, priority, status, note in sales: + for customer, priority, status, note in global_sales: cur = _exec(conn, - "INSERT INTO sales_leads (target_customer, priority, status) VALUES (?,?,?)", - (customer, priority, status), + "INSERT INTO sales_leads (target_customer, priority, status, created_at) VALUES (?,?,?,?)", + (customer, priority, status, today), ) _exec(conn, - "INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action) VALUES (?,?,?,?,?)", - ("sales", cur.lastrowid, date.today().isoformat(), note, "明确下一次沟通人和时间"), + "INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action,follower) VALUES (?,?,?,?,?,?)", + ("sales", cur.lastrowid, today, note, "明确下一次沟通", "销售A"), ) + # 全域 business_proposals cur = _exec(conn, - "INSERT INTO business_proposals (customer_or_project_name,version,description,status,created_date) VALUES (?,?,?,?,?)", - ("信达生物", "v1.5", "信达科普项目续约与报价方案", "已提交客户", "2026-05-28"), + "INSERT INTO business_proposals (customer_or_project_name,version,description,status,created_date, tenant) VALUES (?,?,?,?,?,?)", + ("信达生物", "v1.5", "信达科普项目续约与报价方案", "已提交客户", "2026-05-28", "科普·无界"), ) proposal_id = cur.lastrowid - proposal_dir = WEIXIN_BASE / "2、业务方案/信达/v1.5" - for category, names in { - "方案": ["整体方案.pptx", "整体方案.pdf"], - "成本": ["业务报价-2亿方案.xlsx", "业务报价-5250万方案.xlsx", "5、最新报价.xlsx"], - "SOP": ["SOP.docx"], - "财务流程": ["财务流程.docx"], - }.items(): - for name in names: - add_file_index(conn, "proposal", proposal_id, "v1.5", category, proposal_dir / name, external=True) - - projects = [ - ("圆心科技 科普文章项目", "v2026-文章", "execution", "SOP 执行中", "内容生产", 55, "文章内容生产与审核执行中"), - ("圆心科技 科普视频项目", "v2026-视频", "execution", "SOP 执行中", "内容生产", 45, "视频脚本、拍摄与审核推进"), - ("圆心科技 科普专访项目", "v2026-专访", "opportunity", "方案已提交", "商务推进", 0, "专访项目推动签约"), - ] - op_dir = WEIXIN_BASE / "3、运营方案" - for name, version, kind, status, stage, progress, note in projects: - cur = _exec(conn, - """INSERT INTO operation_projects - (project_name,project_version,project_type,project_status,current_stage,target_customer,customer_need, - expected_contract_amount,expected_sign_date,sign_probability,next_action,sop_stage,execution_progress,current_deliverable) - VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", - (name, version, kind, status, stage, "圆心科技", "科普内容项目执行与管理", 0 if kind == "execution" else 200, "2026-06", 100 if kind == "execution" else 70, "补齐版本要求文件并更新下一节点", stage, progress, note), - ) - _exec(conn, - "INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action) VALUES (?,?,?,?,?)", - ("operation", cur.lastrowid, date.today().isoformat(), note, "补齐版本要求文件并更新下一节点"), - ) - - file_map = [ - (1, "v2026-文章", "项目方案", "圆心科技--科普文章项目(1).pptx"), - (2, "v2026-视频", "项目方案", "圆心科技-科普视频项目(1).pptx"), - (3, "v2026-专访", "项目方案", "圆心科技-科普专访项目-2026年(1).pdf"), - (1, "v2026-文章", "项目管理手册", "圆心科技《项目管理手册》-2026年.pdf"), - (2, "v2026-视频", "审核标准", "科普项目-审核标准(文章-视频-音频).pdf"), - ] - for project_id, version, category, filename in file_map: - add_file_index(conn, "operation", project_id, version, category, op_dir / filename, external=True) + # 全域 product_versions products = [ - ("妙手医生服务小程序", "v1.1", "视频任务增强 + 积分商城", "草稿箱、批量上传、积分商城、消息通知", "2026-Q3", "规划中", "科普平台"), - ("数字化营销后台管理系统", "v1.2", "运营数据看板 + 智能审核", "医生活跃、任务完成率、AI 预审、渠道数据上报", "2026-Q3", "设计中", "真研平台"), - ("妙手患者服务", "v0.5", "科普浏览 + 医生主页 MVP", "科普文章/视频浏览、医生主页、搜索", "2026-Q3", "规划中", "科普平台"), - ("数字人内容平台", "v0.1", "基础数字人视频生成 MVP", "预设形象、AI 配音、脚本驱动、简单模板", "2026-Q3", "规划中", "科普平台"), - ("渠道分发引擎", "v1.0", "六渠道统一分发", "分发 API、内容适配、分发排期、效果追踪", "2027-Q1", "规划中", "科普平台"), + ("妙手医生服务小程序", "v1.1", "视频任务增强 + 积分商城", "2026-Q3", "规划中", "P1"), + ("数字化营销后台管理系统", "v1.2", "运营数据看板 + 智能审核", "2026-Q3", "设计中", "P0"), + ("妙手患者服务", "v0.5", "科普浏览 + 医生主页 MVP", "2026-Q3", "规划中", "P2"), + ("数字人内容平台", "v0.1", "基础数字人视频生成 MVP", "2026-Q3", "规划中", "P2"), + ("渠道分发引擎", "v1.0", "六渠道统一分发", "2027-Q1", "规划中", "P1"), ] - for product in products: + for p in products: cur = _exec(conn, - "INSERT INTO product_versions (product_name,version,version_goal,feature_list,launch_date,status,platform) VALUES (?,?,?,?,?,?,?)", - product, + "INSERT INTO product_versions (product_name,version,version_goal,launch_date,status,priority,tenant) VALUES (?,?,?,?,?,?,?)", + (*p, "科普·无界"), ) _exec(conn, - "INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action) VALUES (?,?,?,?,?)", - ("product", cur.lastrowid, date.today().isoformat(), f"{product[0]} {product[1]}:{product[2]}", "按路线图推进"), - ) - - for month, record_type, category, amount, notes in [ - ("2026-05", "revenue", "信达生物续约确认收入", 120, "信达项目阶段确收"), - ("2026-06", "revenue", "信达生物续约确认收入", 80, "信达项目尾款预估"), - ("2026-05", "cost_expense", "内容生产", 32, "医生劳务与内容制作"), - ("2026-05", "cost_expense", "运营管理", 16, "项目管理与渠道协同"), - ("2026-06", "cost_expense", "渠道分发", 24, "投放与分发费用"), - ]: - _exec(conn, - "INSERT INTO finance_records (month,record_type,category,amount,occurred_date,notes) VALUES (?,?,?,?,?,?)", - (month, record_type, category, amount, f"{month}-01", notes), - ) - - tasks_seed = [ - ("阶段1:渠道与商务确认", "商务对接", "合同签订", "Anna", "2026-06-30", "法务审核中", "合同签订后开始执行"), - ("阶段1:渠道与商务确认", "官媒渠道确认", "沟通官媒确定", "段丽华", "2026-06-30", "官媒尽力推,以先达成合作为准", "集团支持"), - ("阶段1:渠道与商务确认", "官媒渠道确认", "官媒合作签约", "段丽华", "2026-06-18", "", "官媒确认细节"), - ("阶段2:系统与标准搭建", "系统开发上线", "音频专访系统开发上线", "戴敏/梁军营", "2026-06-18", "客户比较着急执行,需要技术的资源", ""), - ("阶段2:系统与标准搭建", "系统开发上线", "精品视频系统开发上线", "戴敏/梁军营", "2026-06-25", "", ""), - ("阶段2:系统与标准搭建", "标准与培训", "业务执行手册SOP", "胡龙飞", "2026-06-12", "", "系统开发上线"), - ("阶段3:人员与审核入驻", "团队组建", "医学审核人员到位", "胡龙飞", "2026-06-15", "", "审核人员招聘"), - ("阶段3:人员与审核入驻", "团队组建", "视频制作人员到位", "胡龙飞", "2026-06-18", "", "项目经理招聘"), - ("阶段4:供应链与制作", "供应商准入", "准入拍摄/剪辑/主持人", "胡龙飞/侯亚凤", "2026-06-18", "", ""), - ("阶段2:系统与标准搭建", "脚本生产及审核", "生产脚本", "军营", "2026-06-12", "脚本目前生产比较机械,需要提前准备", "细分标签领域完成"), - ] - for phase, milestone, task, owner, due_date, blockers, notes in tasks_seed: - _exec(conn, - "INSERT INTO project_tasks (project_id,phase,milestone,task,owner,due_date,blockers,notes) VALUES (?,?,?,?,?,?,?,?)", - (1, phase, milestone, task, owner, due_date, blockers, notes), + "INSERT INTO follow_up_records (target_type,target_id,followed_at,content,next_action,follower) VALUES (?,?,?,?,?,?)", + ("product", cur.lastrowid, today, f"{p[0]} {p[1]}:{p[2]}", "按路线图推进", "产品经理"), ) conn.commit() + print("[seed] 丰富示例数据已填充") finally: conn.close() diff --git a/static/modules/expense.js b/static/modules/expense.js index 5a71502..84d460b 100644 --- a/static/modules/expense.js +++ b/static/modules/expense.js @@ -56,13 +56,13 @@ function renderExpense() { // 月份/季度日期选择器 var dateSelect = ''; if (view === 'monthly') { - dateSelect = '月份:'; + dateSelect = '月份:'; } else if (view === 'quarterly') { - dateSelect = '季度:'; + dateSelect = '季度:'; } // Toolbar - var toolbar = '视图:类型:类型:
'; + modal += '
'; var target = document.querySelector("#financeExpense") || document.querySelector("#financeTabExpense") || document.querySelector("#expense"); if (!target) return; target.innerHTML = '
' + '
' + toolbar + '
' + '
' + - '
费用记录' + filtered.length + '
' + - '
总费用' + money(totalAmount) + '
' + - '
已发生金额' + money(totalIncurred) + '
' + + '
费用记录' + filtered.length + '
' + + '
总费用' + money(totalAmount) + '
' + + '
已发生金额' + money(totalIncurred) + '
' + '
' + modal + '
' + tableRows + '
费用类型月份金额已发生金额费用说明
' + diff --git a/static/modules/finance.js b/static/modules/finance.js index 1a429c7..bfdb0b7 100644 --- a/static/modules/finance.js +++ b/static/modules/finance.js @@ -111,9 +111,9 @@ function renderFinance() { const qLabels2 = ["Q1 (1-3月)","Q2 (4-6月)","Q3 (7-9月)","Q4 (10-12月)"]; const toolMonthSelect = allMonths.map(m => '').join(""); const toolQuarterSelect = qLabels2.map((l,i) => '').join(""); - const toolDateSelect = state.finView==='monthly'?'月份:':state.finView==='quarterly'?'季度:':''; + const toolDateSelect = state.finView==='monthly'?'月份:':state.finView==='quarterly'?'季度:':''; - const finHeaderBase = `视图:${toolDateSelect}`; + const finHeaderBase = `视图:${toolDateSelect}`; const finAddBtn = ``; const finFilterTabs = `
`; @@ -296,7 +296,7 @@ function renderFinance() { const subtitle = isUnsigned ? '合同 → 毛利 → 成本 → 项目利润' : '合同 → 确收 → 毛利 → 成本 → 项目利润 → 回款 → 已付 → 现金流 → 执行率 → 毛利率 → 回款率 → 付款率'; - return card(`
${cards.map(([l, v, c]) => '
' + l + '' + v + '
').join("")}
`, "p-4") + + return card(`
${cards.map(([l, v, c]) => '
' + l + '' + v + '
').join("")}
`, "p-4") + card(`

项目财务明细

${subtitle}

${thead}${tbody}
`, "p-4"); }; @@ -379,6 +379,7 @@ function renderFinance() { const gross = sumBudget(pf, "gross"); const cost = sumExpense(pf, "cost"); const paid = sumExpense(pf, "paid"); + if (!rev && !payment && !cost && !paid && !gross) return; sumRev += rev; sumPay += payment; sumCost += cost; sumPaid += paid; sumGross += gross; rows.push(isUnsigned ? tdRowUnsigned(pf, cost, gross) : tdRow(pf, rev, payment, cost, paid, gross)); }); diff --git a/static/modules/home.js b/static/modules/home.js index bcae3df..09cf137 100644 --- a/static/modules/home.js +++ b/static/modules/home.js @@ -105,15 +105,14 @@ function renderHome() { } document.querySelector("#home").innerHTML = `
-
- 筛选: - -
+ ${state.tenant === "总工作台" ? "" : `
+ ${[ + ["项目管理", m.total_projects, "finance"], + ["重点工作与台账", m.total_proposals, "projects"], + ["业务方案", m.total_products, "proposals"], + ["产品迭代", m.upcoming_products, "products"], + ].map(([label, value, tab]) => ``).join("")} +
`} ${card(`

部门经营情况

${(() => { var d = [ diff --git a/static/modules/utils.js b/static/modules/utils.js index 64bf31b..c6d511a 100644 --- a/static/modules/utils.js +++ b/static/modules/utils.js @@ -9,7 +9,7 @@ const state = { selectedProject: null, taskQuery: "", taskView: localStorage.getItem("opc-task-view") || "detail", - finView: localStorage.getItem("opc-fin-view") || "overview", + finView: "overview", proposalTab: "standard", chart: null, chart2: null, @@ -196,7 +196,6 @@ window.switchTab = switchTab; window.setFinView = (view) => { state.finView = view; - localStorage.setItem("opc-fin-view", view); renderFinance(); }; window.switchFinFilter = (filter) => { diff --git a/templates/index.html b/templates/index.html index 570b54c..6ddb3df 100644 --- a/templates/index.html +++ b/templates/index.html @@ -76,7 +76,7 @@
-

OPC Manager v1.0.0.3

+

OPC Manager v1.0.0.4

科普 OPC 工作台

指标年度累计上季度累计上月累计本季度累计季环比本月累计月环比