产品迭代模块:卡片改表格 + 日期内联编辑 + 后端日期校验
- 卡片列表改为表格列表(10列),参考用户运营中心产品台账 - 数据库新增 priority + 5 个日期字段(start/plan/dev_done/test/launch) - 删除 owner/platform/feature_list 字段(migrate_drop_product_fields) - 日期内联编辑:5个日期列直接渲染 date input - 后端日期校验:4个时间不能早于启动时间;启动时间必填 - 详情页新增耗时统计区块(总/产品/研发/测试耗时) - 优先级和状态合并同一行 - 新增'未开始'状态 - 表格垂直居中对齐 - renderProducts 后重新初始化 lucide 图标
This commit is contained in:
@@ -856,7 +856,7 @@ TABLES = {
|
||||
"sales": ("sales_leads", ["target_customer", "priority", "status", "tenant"]),
|
||||
"proposals": ("business_proposals", ["customer_or_project_name", "version", "description", "status", "created_date", "proposal_type", "notes", "tenant"]),
|
||||
"operations": ("operation_projects", ["project_name", "project_version", "project_type", "project_status", "current_stage", "owner", "target_customer", "customer_need", "expected_contract_amount", "expected_sign_date", "sign_probability", "next_action", "sop_stage", "execution_progress", "current_deliverable", "risks", "notes", "tenant"]),
|
||||
"products": ("product_versions", ["product_name", "version", "version_goal", "feature_list", "launch_date", "status", "platform", "notes", "tenant"]),
|
||||
"products": ("product_versions", ["product_name", "version", "version_goal", "priority", "start_date", "plan_date", "dev_done_date", "test_date", "launch_date", "status", "notes", "tenant"]),
|
||||
"finance": ("finance_records", ["month", "project_name", "record_type", "category", "amount", "occurred_date", "notes", "tenant"]),
|
||||
"tasks": ("project_tasks", ["project_id", "phase", "milestone", "task", "owner", "due_date", "blockers", "notes", "status", "sort_order", "priority", "tenant"]),
|
||||
"projectFinances": ("project_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "status", "sales_person", "owner", "total_rev", "total_gross", "budget_data"]),
|
||||
@@ -913,6 +913,22 @@ def update_resource(resource, item_id):
|
||||
valid_statuses = ["未开始", "进行中", "已结束"]
|
||||
if not payload["status"] or payload["status"] not in valid_statuses:
|
||||
payload["status"] = "未开始"
|
||||
# 产品日期约束:4 个时间不能早于启动时间;启动时间不能清空
|
||||
if resource == "products":
|
||||
# 查当前记录的 start_date
|
||||
cur = _exec(conn, f"SELECT start_date FROM {table} WHERE id=?", (item_id,))
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
current_start = (row or {}).get("start_date", "") or ""
|
||||
new_start = payload.get("start_date", current_start)
|
||||
# 启动时间必填
|
||||
if "start_date" in payload and not new_start:
|
||||
return jsonify({"error": "启动时间为必填项"}), 400
|
||||
date_fields = ["plan_date", "dev_done_date", "test_date", "launch_date"]
|
||||
for f in date_fields:
|
||||
if f in payload and payload[f] and new_start and payload[f] < new_start:
|
||||
labels = {"plan_date": "产品方案", "dev_done_date": "研发完成", "test_date": "测试完成", "launch_date": "上线时间"}
|
||||
return jsonify({"error": f"{labels[f]}不能早于启动时间({new_start})"}), 400
|
||||
update_cols = [col for col in cols if col in payload]
|
||||
if update_cols:
|
||||
_exec(conn,
|
||||
|
||||
@@ -17,12 +17,13 @@ def run_migrations():
|
||||
"""
|
||||
from migrations.tables import migrate_create_tables
|
||||
from migrations.columns import migrate_add_columns
|
||||
from migrations.data_fixes import migrate_fix_task_status, migrate_rename_tenant
|
||||
from migrations.data_fixes import migrate_fix_task_status, migrate_rename_tenant, migrate_drop_product_fields
|
||||
from migrations.seed import migrate_seed_users, migrate_seed_demo_data
|
||||
|
||||
migrate_create_tables()
|
||||
migrate_add_columns()
|
||||
migrate_fix_task_status()
|
||||
migrate_rename_tenant()
|
||||
migrate_drop_product_fields()
|
||||
migrate_seed_users()
|
||||
migrate_seed_demo_data()
|
||||
|
||||
@@ -37,8 +37,20 @@ def migrate_add_columns():
|
||||
"ALTER TABLE business_proposals ADD COLUMN notes VARCHAR(2000) NOT NULL DEFAULT ''")
|
||||
|
||||
# product_versions 扩展字段
|
||||
_add_column_if_missing(conn, "product_versions", "platform",
|
||||
"ALTER TABLE product_versions ADD COLUMN platform VARCHAR(100) NOT NULL DEFAULT ''")
|
||||
_add_column_if_missing(conn, "product_versions", "priority",
|
||||
"ALTER TABLE product_versions ADD COLUMN priority VARCHAR(10) NOT NULL DEFAULT 'P2'")
|
||||
_add_column_if_missing(conn, "product_versions", "start_date",
|
||||
"ALTER TABLE product_versions ADD COLUMN start_date VARCHAR(30) NOT NULL DEFAULT ''")
|
||||
_add_column_if_missing(conn, "product_versions", "plan_date",
|
||||
"ALTER TABLE product_versions ADD COLUMN plan_date VARCHAR(30) NOT NULL DEFAULT ''")
|
||||
_add_column_if_missing(conn, "product_versions", "dev_done_date",
|
||||
"ALTER TABLE product_versions ADD COLUMN dev_done_date VARCHAR(30) NOT NULL DEFAULT ''")
|
||||
_add_column_if_missing(conn, "product_versions", "test_date",
|
||||
"ALTER TABLE product_versions ADD COLUMN test_date VARCHAR(30) NOT NULL DEFAULT ''")
|
||||
_add_column_if_missing(conn, "product_versions", "devs",
|
||||
"ALTER TABLE product_versions ADD COLUMN devs VARCHAR(500) NOT NULL DEFAULT ''")
|
||||
_add_column_if_missing(conn, "product_versions", "testers",
|
||||
"ALTER TABLE product_versions ADD COLUMN testers VARCHAR(500) NOT NULL DEFAULT ''")
|
||||
|
||||
# project_tasks 扩展字段
|
||||
_add_column_if_missing(conn, "project_tasks", "status",
|
||||
|
||||
@@ -47,3 +47,27 @@ def migrate_rename_tenant():
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def migrate_drop_product_fields():
|
||||
"""删除 product_versions 表的 owner / platform / feature_list 字段"""
|
||||
from flask_app import db, mysql
|
||||
|
||||
conn = db()
|
||||
try:
|
||||
for col in ["owner", "platform", "feature_list"]:
|
||||
cur = conn.cursor(dictionary=True)
|
||||
cur.execute("SHOW COLUMNS FROM product_versions LIKE %s", (col,))
|
||||
exists = cur.fetchone()
|
||||
cur.close()
|
||||
if exists:
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute(f"ALTER TABLE product_versions DROP COLUMN {col}")
|
||||
cur.close()
|
||||
conn.commit()
|
||||
print(f"[migrate] product_versions.{col} 列已删除")
|
||||
except mysql.connector.Error as e:
|
||||
print(f"[migrate] 删除 {col} 失败: {e}")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
Reference in New Issue
Block a user