feat: 修复 H5 底部导航覆盖 + 更新项目进度文档

## H5 底部导航修复 (Bug #10)
- 精简 App.vue,移除重复 tabbar,仅保留全局样式
- uni-page 设置 height: calc(100% - 50px) + overflow-y: auto
- 内容区域精确停在底部导航上方,独立滚动不再叠加
- 恢复 custom-tab-bar 组件

## 项目进度文档
- PROGRESS.md 更新至 10 个 Bug 修复
- 新增 H5 底部导航修复记录
- 新增历史变更条目
This commit is contained in:
TradeMate Dev
2026-05-12 20:24:42 +08:00
parent 69e164dcae
commit 7b62c2f8b4
125 changed files with 19725 additions and 728 deletions
+16
View File
@@ -2,10 +2,26 @@ from .user import User, Product
from .customer import Customer, Conversation, Message
from .quotation import Quotation, QuotationItem
from .corpus import CorpusEntry
from .team import Team, TeamMember
from .analytics import UsageLog
from .notification import Notification
from .feedback import Feedback
from .subscription import Subscription
from .preference import PreferenceAnalysis, MarketingEffect
from .device import Device
from .followup import FollowupStrategy, FollowupLog
__all__ = [
"User", "Product",
"Customer", "Conversation", "Message",
"Quotation", "QuotationItem",
"CorpusEntry",
"Team", "TeamMember",
"UsageLog",
"Notification",
"Feedback",
"Subscription",
"PreferenceAnalysis", "MarketingEffect",
"Device",
"FollowupStrategy", "FollowupLog",
]
+18
View File
@@ -0,0 +1,18 @@
from sqlalchemy import Column, String, Integer, DateTime, Text, Float
from sqlalchemy.dialects.postgresql import UUID, JSONB
from datetime import datetime
from app.database import Base
import uuid
class UsageLog(Base):
__tablename__ = "usage_logs"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
team_id = Column(UUID(as_uuid=True), nullable=True, index=True)
action = Column(String(100), nullable=False)
detail = Column(JSONB, default={})
ip_address = Column(String(50))
user_agent = Column(String(255))
created_at = Column(DateTime, default=datetime.utcnow)
+3 -4
View File
@@ -1,6 +1,5 @@
from sqlalchemy import Column, String, Integer, DateTime, Text, Float
from sqlalchemy import Column, String, Integer, DateTime, Text, Float, Boolean
from sqlalchemy.dialects.postgresql import UUID, JSONB
from pgvector.sqlalchemy import Vector
from datetime import datetime
from app.database import Base
import uuid
@@ -21,6 +20,6 @@ class CorpusEntry(Base):
user_edited = Column(Boolean, default=False)
user_rating = Column(Integer)
usage_count = Column(Integer, default=0)
embedding = Column(Vector(768))
metadata = Column(JSONB, default={})
embedding = Column(JSONB)
entry_metadata = Column("metadata", JSONB, default={})
created_at = Column(DateTime, default=datetime.utcnow)
+3 -3
View File
@@ -10,7 +10,7 @@ class Customer(Base):
__tablename__ = "customers"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, index=True)
name = Column(String(255), nullable=False)
company = Column(String(255))
country = Column(String(100))
@@ -38,7 +38,7 @@ class Conversation(Base):
__tablename__ = "conversations"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, index=True)
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False, index=True)
channel = Column(String(50), default="whatsapp")
topic = Column(String(255))
@@ -66,7 +66,7 @@ class Message(Base):
selected_suggestion = Column(Integer)
user_edited = Column(Text)
status = Column(String(50), default="sent")
metadata = Column(JSONB, default={})
msg_metadata = Column("metadata", JSONB, default={})
created_at = Column(DateTime, default=datetime.utcnow)
conversation = relationship("Conversation", back_populates="messages")
+19
View File
@@ -0,0 +1,19 @@
from sqlalchemy import Column, String, Boolean, DateTime, Text
from sqlalchemy.dialects.postgresql import UUID, JSONB
from datetime import datetime
from app.database import Base
import uuid
class Device(Base):
__tablename__ = "devices"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
platform = Column(String(50), default="weapp")
push_token = Column(String(500))
client_id = Column(String(255), nullable=False)
device_info = Column(JSONB, default={})
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+17
View File
@@ -0,0 +1,17 @@
from sqlalchemy import Column, String, Integer, DateTime, Text
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
from app.database import Base
import uuid
class Feedback(Base):
__tablename__ = "feedbacks"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
category = Column(String(50), default="general")
content = Column(Text, nullable=False)
contact = Column(String(100))
status = Column(String(20), default="pending")
created_at = Column(DateTime, default=datetime.utcnow)
+51
View File
@@ -0,0 +1,51 @@
from sqlalchemy import Column, String, Boolean, Integer, DateTime, Text, ForeignKey
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.orm import relationship
from datetime import datetime
from app.database import Base
import uuid
class FollowupStrategy(Base):
__tablename__ = "followup_strategies"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(255), nullable=False)
description = Column(Text)
trigger_condition = Column(JSONB, default={
"min_silence_days": 3,
"max_silence_days": 999,
"min_health_score": 0,
"max_health_score": 100,
"status_filter": ["lead", "negotiating"],
})
channel = Column(String(50), default="whatsapp")
ai_prompt_template = Column(Text)
priority = Column(Integer, default=0)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class FollowupLog(Base):
__tablename__ = "followup_logs"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False, index=True)
strategy_id = Column(UUID(as_uuid=True), ForeignKey("followup_strategies.id"), nullable=True)
status = Column(String(50), default="pending")
channel = Column(String(50), default="whatsapp")
content = Column(Text)
ai_generated_content = Column(Text)
user_edited_content = Column(Text)
health_score_at_time = Column(Integer)
silence_days_at_time = Column(Integer)
sent_at = Column(DateTime)
replied_at = Column(DateTime)
response_status = Column(String(50))
log_metadata = Column("metadata", JSONB, default={})
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
customer = relationship("Customer", backref="followup_logs")
+20
View File
@@ -0,0 +1,20 @@
from sqlalchemy import Column, String, Boolean, DateTime, Text, ForeignKey
from sqlalchemy.dialects.postgresql import UUID, JSONB
from datetime import datetime
from app.database import Base
import uuid
class Notification(Base):
__tablename__ = "notifications"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
title = Column(String(255), nullable=False)
content = Column(Text, nullable=False)
notification_type = Column(String(50), default="system")
reference_type = Column(String(50))
reference_id = Column(String(255))
is_read = Column(Boolean, default=False)
notify_metadata = Column("metadata", JSONB, default={})
created_at = Column(DateTime, default=datetime.utcnow)
+40
View File
@@ -0,0 +1,40 @@
from sqlalchemy import Column, String, Boolean, DateTime, Text, Integer, Float
from sqlalchemy.dialects.postgresql import UUID, JSONB
from datetime import datetime
from app.database import Base
import uuid
class PreferenceAnalysis(Base):
__tablename__ = "preference_analyses"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True, unique=True)
task_type = Column(String(50), nullable=False, default="reply")
preferred_tone = Column(String(50))
preferred_style = Column(String(50))
common_replacements = Column(JSONB, default=[])
avg_formality_score = Column(Float, default=0.5)
greeting_style = Column(String(100))
sign_off_style = Column(String(100))
analysis_data = Column(JSONB, default={})
confidence = Column(Float, default=0.0)
interaction_count = Column(Integer, default=0)
last_analysis_at = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
class MarketingEffect(Base):
__tablename__ = "marketing_effects"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
content_hash = Column(String(64), nullable=False, index=True)
product_id = Column(UUID(as_uuid=True))
product_name = Column(String(255))
channel = Column(String(50), default="copy")
event_type = Column(String(50), nullable=False)
target_audience = Column(String(255))
effect_metadata = Column("metadata", JSONB, default={})
created_at = Column(DateTime, default=datetime.utcnow)
+1 -1
View File
@@ -10,7 +10,7 @@ class Quotation(Base):
__tablename__ = "quotations"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, index=True)
customer_id = Column(UUID(as_uuid=True), ForeignKey("customers.id"), nullable=False)
title = Column(String(255))
status = Column(String(50), default="draft")
+23
View File
@@ -0,0 +1,23 @@
from sqlalchemy import Column, String, Integer, DateTime, Float, Boolean, ForeignKey
from sqlalchemy.dialects.postgresql import UUID
from datetime import datetime
from app.database import Base
import uuid
class Subscription(Base):
__tablename__ = "subscriptions"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
plan = Column(String(50), nullable=False)
status = Column(String(20), default="active")
started_at = Column(DateTime, default=datetime.utcnow)
expires_at = Column(DateTime)
auto_renew = Column(Boolean, default=True)
payment_provider = Column(String(50), default="wechat")
payment_id = Column(String(255))
amount = Column(Float)
currency = Column(String(10), default="CNY")
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
+38
View File
@@ -0,0 +1,38 @@
from sqlalchemy import Column, String, Boolean, DateTime, ForeignKey, Text, Integer
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.orm import relationship
from datetime import datetime
from app.database import Base
import uuid
class Team(Base):
__tablename__ = "teams"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(255), nullable=False)
owner_id = Column(UUID(as_uuid=True), nullable=False, index=True)
description = Column(Text)
member_count = Column(Integer, default=0)
max_members = Column(Integer, default=5)
tier = Column(String(50), default="free")
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
members = relationship("TeamMember", back_populates="team", cascade="all, delete-orphan")
class TeamMember(Base):
__tablename__ = "team_members"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
team_id = Column(UUID(as_uuid=True), ForeignKey("teams.id"), nullable=False, index=True)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
role = Column(String(50), default="member")
invited_by = Column(UUID(as_uuid=True))
status = Column(String(50), default="active")
joined_at = Column(DateTime, default=datetime.utcnow)
created_at = Column(DateTime, default=datetime.utcnow)
team = relationship("Team", back_populates="members")
+3 -2
View File
@@ -1,4 +1,4 @@
from sqlalchemy import Column, String, Boolean, Integer, DateTime, Text
from sqlalchemy import Column, String, Boolean, Integer, DateTime, Text, ForeignKey
from sqlalchemy.dialects.postgresql import UUID, JSONB
from sqlalchemy.orm import relationship
from datetime import datetime
@@ -15,6 +15,7 @@ class User(Base):
username = Column(String(100))
password_hash = Column(String(255))
tier = Column(String(50), default="free")
role = Column(String(20), default="user")
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
@@ -35,7 +36,7 @@ class Product(Base):
__tablename__ = "products"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), nullable=False, index=True)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False, index=True)
name = Column(String(255), nullable=False)
name_en = Column(String(255))
description = Column(Text)