feat(project): 项目编号支持新增编辑自由填写
项目管理弹窗增加项目编号输入;后端保存改为接收客户端编号并校验租户内唯一,不再自动生成。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
3bdbefb56e
commit
e895daf8fc
5 files changed
+24
-5
No files matched your search
@@ -45,6 +45,7 @@ class TeamBody(Body):
|
||||
|
||||
class ProjectBody(Body):
|
||||
projectName: str = Field(min_length=1, max_length=200)
|
||||
projectNo: str = Field(min_length=1, max_length=64)
|
||||
teamId: str
|
||||
leaderUserId: str
|
||||
bizTypes: List[str]
|
||||
|
||||
@@ -333,8 +333,17 @@ def save_entity(db, tenant, user, model, body, row_id=None):
|
||||
if status not in ('pending', 'active', 'completed', 'disabled'):
|
||||
raise HTTPException(400, '项目状态无效')
|
||||
row.project_name, row.team_id, row.biz_types, row.status = body[name_key].strip(), team.id, ','.join(dict.fromkeys(kinds)), status
|
||||
if not row_id:
|
||||
row.project_no = 'XM' + datetime.now().strftime('%Y%m%d') + generate_uuid().replace('-', '')[:16].upper()
|
||||
project_no = str(body.get('projectNo') or '').strip()
|
||||
if not project_no:
|
||||
raise HTTPException(400, '项目编号不能为空')
|
||||
if len(project_no) > 64:
|
||||
raise HTTPException(400, '项目编号不能超过 64 个字符')
|
||||
conflict = scoped(db, Project, tenant).filter(Project.project_no == project_no)
|
||||
if row_id:
|
||||
conflict = conflict.filter(Project.id != row.id)
|
||||
if conflict.with_for_update().first():
|
||||
raise HTTPException(400, '项目编号已存在')
|
||||
row.project_no = project_no
|
||||
try:
|
||||
row.start_date = date.fromisoformat(body['startDate']) if body.get('startDate') else None
|
||||
row.end_date = date.fromisoformat(body['endDate']) if body.get('endDate') else None
|
||||
|
||||
@@ -148,8 +148,8 @@ class ProjectManagementTest(unittest.TestCase):
|
||||
self.db.add(SysUser(id='u', tenant_no='t', user_no='u', nick_name='User'))
|
||||
self.db.commit()
|
||||
result = self.svc.save_entity(self.db, 't', self.user, self.Project,
|
||||
dict(projectName='New', leaderUserId='u', teamId='g', bizTypes=['basic_experiment'], startDate='', endDate=''))
|
||||
self.assertTrue(result['projectNo'].startswith('XM'))
|
||||
dict(projectName='New', projectNo='CUSTOM-001', leaderUserId='u', teamId='g', bizTypes=['basic_experiment'], startDate='', endDate=''))
|
||||
self.assertEqual(result['projectNo'], 'CUSTOM-001')
|
||||
self.assertIsNone(result['startDate'])
|
||||
|
||||
def test_conversion_table_reverse_graph_and_audit(self):
|
||||
|
||||
@@ -22,13 +22,14 @@ export function assignmentSnapshot(records: ProjectRecord[]) {
|
||||
}))
|
||||
}
|
||||
|
||||
/** Write bodies intentionally exclude response-only labels, timestamps and generated numbers. */
|
||||
/** Write bodies intentionally exclude response-only labels and timestamps. */
|
||||
export function managementPayload(kind: 'teams' | 'projects', form: ProjectRecord): ProjectRecord {
|
||||
const fields =
|
||||
kind === 'teams'
|
||||
? ['teamName', 'leaderUserId', 'enabled', 'remark']
|
||||
: [
|
||||
'projectName',
|
||||
'projectNo',
|
||||
'teamId',
|
||||
'leaderUserId',
|
||||
'bizTypes',
|
||||
|
||||
@@ -149,6 +149,13 @@
|
||||
v-else
|
||||
v-model="form.projectName"
|
||||
/></el-form-item>
|
||||
<el-form-item
|
||||
v-if="!isTeam"
|
||||
label="项目编号"
|
||||
prop="projectNo"
|
||||
:rules="[{ required: true, message: '请输入项目编号' }]"
|
||||
><el-input v-model="form.projectNo" maxlength="64" show-word-limit
|
||||
/></el-form-item>
|
||||
<el-form-item
|
||||
v-if="!isTeam"
|
||||
label="所属项目组"
|
||||
@@ -356,6 +363,7 @@
|
||||
: {
|
||||
teamName: '',
|
||||
projectName: '',
|
||||
projectNo: '',
|
||||
leaderUserId: '',
|
||||
enabled: true,
|
||||
status: 'pending',
|
||||
|
||||
Reference in new issue
Block a user