feat: 目标和问题统一分类体系(综合/乐理相关/演奏能力/其他),添加数据库迁移
This commit is contained in:
@@ -131,6 +131,29 @@ def create_app():
|
||||
if "level" not in goal_columns:
|
||||
db.session.execute(text("ALTER TABLE goals ADD COLUMN level VARCHAR(20) DEFAULT '入门'"))
|
||||
db.session.commit()
|
||||
|
||||
# 检查goals表是否有category字段
|
||||
if "category" not in goal_columns:
|
||||
db.session.execute(text("ALTER TABLE goals ADD COLUMN category VARCHAR(20) DEFAULT '综合'"))
|
||||
db.session.commit()
|
||||
|
||||
# 迁移problems表分类:旧分类 -> 新分类
|
||||
# 技术类/技术类(手型)/技术类(生理限制) -> 演奏能力
|
||||
# 识谱类 -> 乐理相关
|
||||
# 综合类 -> 综合类 (保持不变)
|
||||
# 其他 -> 其他 (保持不变)
|
||||
category_mapping = {
|
||||
'技术类': '演奏能力',
|
||||
'技术类(手型)': '演奏能力',
|
||||
'技术类(生理限制)': '演奏能力',
|
||||
'识谱类': '乐理相关',
|
||||
}
|
||||
for old_cat, new_cat in category_mapping.items():
|
||||
db.session.execute(
|
||||
text("UPDATE problems SET category = :new_cat WHERE category = :old_cat"),
|
||||
{"new_cat": new_cat, "old_cat": old_cat}
|
||||
)
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
print(f"数据库迁移: {e}")
|
||||
|
||||
|
||||
+7
-2
@@ -6,6 +6,9 @@ import re
|
||||
|
||||
db = SQLAlchemy()
|
||||
|
||||
# 问题和目标的统一分类体系
|
||||
ITEM_CATEGORIES = ['综合', '乐理相关', '演奏能力', '其他']
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
"""管理员用户表"""
|
||||
@@ -149,7 +152,7 @@ class Problem(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
no = db.Column(db.String(10), unique=True, nullable=False) # 编号:01, 02...
|
||||
name = db.Column(db.String(100), nullable=False) # 问题名称
|
||||
category = db.Column(db.String(50), default="技术类") # 分类
|
||||
category = db.Column(db.String(20), default="综合") # 分类:综合/乐理相关/演奏能力/其他
|
||||
content = db.Column(db.Text) # 问题详细内容
|
||||
created_at = db.Column(db.DateTime, default=datetime.now)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
@@ -159,7 +162,7 @@ class Problem(db.Model):
|
||||
"id": self.id,
|
||||
"no": self.no,
|
||||
"name": self.name,
|
||||
"category": self.category,
|
||||
"category": self.category or "综合",
|
||||
}
|
||||
|
||||
|
||||
@@ -198,6 +201,7 @@ class Goal(db.Model):
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
content = db.Column(db.Text)
|
||||
level = db.Column(db.String(20), default="入门") # 启蒙/入门/进阶/熟练/精通
|
||||
category = db.Column(db.String(20), default="综合") # 分类:综合/乐理相关/演奏能力/其他
|
||||
created_at = db.Column(db.DateTime, default=datetime.now)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
|
||||
|
||||
@@ -207,6 +211,7 @@ class Goal(db.Model):
|
||||
"name": self.name,
|
||||
"content": self.content,
|
||||
"level": self.level,
|
||||
"category": self.category or "综合",
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
|
||||
+4
-1
@@ -25,7 +25,8 @@ def create_goal():
|
||||
goal = Goal(
|
||||
name=data["name"],
|
||||
content=data.get("content", ""),
|
||||
level=data.get("level", "入门")
|
||||
level=data.get("level", "入门"),
|
||||
category=data.get("category", "综合")
|
||||
)
|
||||
db.session.add(goal)
|
||||
db.session.commit()
|
||||
@@ -48,6 +49,8 @@ def update_goal(goal_id):
|
||||
goal.content = data["content"]
|
||||
if "level" in data:
|
||||
goal.level = data["level"]
|
||||
if "category" in data:
|
||||
goal.category = data["category"]
|
||||
db.session.commit()
|
||||
return jsonify(goal.to_dict())
|
||||
|
||||
|
||||
@@ -46,6 +46,15 @@
|
||||
<option value="精通">精通</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">分类</label>
|
||||
<select class="form-select" id="goal-category">
|
||||
<option value="综合">综合(涉及多方面)</option>
|
||||
<option value="乐理相关">乐理相关</option>
|
||||
<option value="演奏能力">演奏能力</option>
|
||||
<option value="其他">其他</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">目标内容 (Markdown)</label>
|
||||
<textarea class="form-control" id="goal-content" rows="8"></textarea>
|
||||
@@ -111,6 +120,7 @@ async function loadGoals() {
|
||||
|
||||
grid.innerHTML = goalsWithChildren.map(g => {
|
||||
const level = g.level || '入门';
|
||||
const category = g.category || '综合';
|
||||
const childNames = g.children && g.children.length > 0
|
||||
? g.children.map(c => escapeHtml(c.name)).join(', ')
|
||||
: '';
|
||||
@@ -120,6 +130,7 @@ async function loadGoals() {
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">${escapeHtml(g.name)}</h6>
|
||||
<div class="mb-1">
|
||||
<span class="badge bg-primary">${category}</span>
|
||||
<span class="badge bg-secondary">${level}</span>
|
||||
</div>
|
||||
${childNames ? `<div class="small text-muted mb-2">子目标: ${childNames}</div>` : ''}
|
||||
@@ -140,14 +151,14 @@ async function saveGoal() {
|
||||
const id = document.getElementById('goal-id').value;
|
||||
const name = document.getElementById('goal-name').value;
|
||||
const level = document.getElementById('goal-level').value;
|
||||
const category = document.getElementById('goal-category').value;
|
||||
const content = document.getElementById('goal-content').value;
|
||||
|
||||
if (!name) { alert('请输入目标名称'); return; }
|
||||
|
||||
const method = id ? 'PUT' : 'POST';
|
||||
const url = id ? `${API_BASE}/${id}` : API_BASE;
|
||||
const payload = {name, level, content};
|
||||
console.log('Saving goal:', payload);
|
||||
const payload = {name, level, category, content};
|
||||
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
@@ -174,10 +185,10 @@ function editGoal(id) {
|
||||
fetch(`${API_BASE}/${id}`)
|
||||
.then(r => r.json())
|
||||
.then(g => {
|
||||
console.log('Editing goal:', g);
|
||||
document.getElementById('goal-id').value = g.id;
|
||||
document.getElementById('goal-name').value = g.name;
|
||||
document.getElementById('goal-level').value = g.level || '入门';
|
||||
document.getElementById('goal-category').value = g.category || '综合';
|
||||
document.getElementById('goal-content').value = g.content || '';
|
||||
document.getElementById('goalModalTitle').textContent = '编辑目标';
|
||||
new bootstrap.Modal(document.getElementById('goalModal')).show();
|
||||
@@ -298,6 +309,7 @@ document.getElementById('goalModal').addEventListener('hidden.bs.modal', () => {
|
||||
document.getElementById('goal-id').value = '';
|
||||
document.getElementById('goal-name').value = '';
|
||||
document.getElementById('goal-level').value = '入门';
|
||||
document.getElementById('goal-category').value = '综合';
|
||||
document.getElementById('goal-content').value = '';
|
||||
document.getElementById('goalModalTitle').textContent = '新建目标';
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user