Ver código fonte

fix: validate activity drafts during autosave

Developer 1 semana atrás
pai
commit
99aaf91b0f

BIN
dist.zip


+ 7 - 0
src/views/modules/activity/add.vue

@@ -238,6 +238,7 @@ const getInitialBasicForm = () => ({
   categoryIds: '',
   categoryIdsArr: [],
   activityName: '',
+  activeState: null,
   signupStartEnd: [],
   signupStartTime: '',
   signupEndTime: '',
@@ -662,6 +663,7 @@ export default {
     handleTempSave () {
       // 同步 categoryIdsArr 到 categoryId(主分类)/categoryIds(CSV)字段再校验
       this.syncCategoryIds()
+      if (!this.basicForm.activityName || !this.basicForm.activityName.trim()) return this.$message.error('请填写活动名称后再保存草稿')
       if (this.basicForm.signupStartTime && this.basicForm.signupEndTime && (Date.parse(this.basicForm.signupStartTime) > Date.parse(this.basicForm.signupEndTime))) return this.$message.error('报名结束时间不能小于报名开始时间')
       if (this.basicForm.signupEndTime && (Date.parse(this.basicForm.signupEndTime) < Date.parse(new Date()))) return this.$message.error('报名结束时间不能小于当前时间')
       if (this.basicForm.signupEndTime && this.basicForm.activityStartTime && (Date.parse(this.basicForm.signupEndTime) > Date.parse(this.basicForm.activityStartTime))) return this.$message.error('活动开始时间不能小于报名结束时间')
@@ -691,9 +693,14 @@ export default {
     autoSave () {
       // 只在编辑模式(有id)时自动保存
       if (!this.basicForm.id) return
+      const isDraft = Number(this.basicForm.activeState) === -2
+      if (isDraft && (!this.basicForm.activityName || !this.basicForm.activityName.trim())) {
+        return this.$message.error('请填写活动名称后再自动保存草稿')
+      }
       let { signupStartEnd, activityStartEnd, ...newBasic } = JSON.parse(JSON.stringify(this.basicForm))
       let { recruitmentFlag, userAgeFlag, userLevelFlag, ...newMore } = JSON.parse(JSON.stringify(this.moreForm))
       let dto = { ...newBasic, ...newMore }
+      if (isDraft) dto.activeState = -2
       dto.state = dto.state ? 1 : 0
       this.autoSaving = true
       this.$http.put('/core/activity', dto).then(res => {

+ 127 - 0
tests/activity-draft-validation.test.js

@@ -0,0 +1,127 @@
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import fs from 'node:fs'
+import path from 'node:path'
+import vm from 'node:vm'
+
+const addFile = path.resolve('src/views/modules/activity/add.vue')
+
+function loadHandleTempSave () {
+  const source = fs.readFileSync(addFile, 'utf8')
+  const start = source.indexOf('    handleTempSave () {')
+  const end = source.indexOf('\n    handlePrevious () {', start)
+  assert.notEqual(start, -1, '应找到保存草稿方法')
+  assert.notEqual(end, -1, '应找到保存草稿方法结束位置')
+
+  const method = source.slice(start, end)
+    .replace('    handleTempSave () {', 'function handleTempSave () {')
+    .replace(/\n    },\s*$/, '\n}')
+  const context = { Date, JSON }
+  vm.createContext(context)
+  vm.runInContext(`${method}\nthis.handleTempSave = handleTempSave`, context)
+  return context.handleTempSave
+}
+
+function loadAutoSave () {
+  const source = fs.readFileSync(addFile, 'utf8')
+  const start = source.indexOf('    autoSave () {')
+  const end = source.indexOf('\n    handleSave () {', start)
+  assert.notEqual(start, -1, '应找到自动保存方法')
+  assert.notEqual(end, -1, '应找到自动保存方法结束位置')
+
+  const method = source.slice(start, end)
+    .replace('    autoSave () {', 'function autoSave () {')
+    .replace(/\n    },\s*$/, '\n}')
+  const context = { JSON }
+  vm.createContext(context)
+  vm.runInContext(`${method}\nthis.autoSave = autoSave`, context)
+  return context.autoSave
+}
+
+test('空白活动名称不能提交保存草稿请求', () => {
+  const handleTempSave = loadHandleTempSave()
+  const errors = []
+  const requests = []
+  const component = {
+    basicForm: {
+      id: '',
+      activityName: '   ',
+      categoryIdsArr: [],
+      signupStartTime: '',
+      signupEndTime: '',
+      activityStartTime: '',
+      activityEndTime: ''
+    },
+    moreForm: {},
+    syncCategoryIds: () => {},
+    $message: {
+      error: message => errors.push(message),
+      success: () => {}
+    },
+    $http: {
+      post: (...args) => {
+        requests.push(args)
+        return Promise.resolve({ data: { code: 0 } })
+      }
+    },
+    $router: { push: () => {} }
+  }
+
+  handleTempSave.call(component)
+
+  assert.deepEqual(errors, ['请填写活动名称后再保存草稿'])
+  assert.equal(requests.length, 0)
+})
+
+test('编辑草稿自动保存时保留草稿状态', async () => {
+  const source = fs.readFileSync(addFile, 'utf8')
+  const initialForm = source.slice(
+    source.indexOf('const getInitialBasicForm = () => ({'),
+    source.indexOf('const getInitialMoreForm = () => ({')
+  )
+  assert.match(initialForm, /activeState:\s*null/, '表单必须接收详情接口返回的活动状态')
+
+  const autoSave = loadAutoSave()
+  const requests = []
+  const component = {
+    basicForm: { id: '1', activityName: '测试草稿', activeState: -2 },
+    moreForm: { state: false },
+    autoSaving: false,
+    $message: { error: () => {} },
+    $http: {
+      put: (...args) => {
+        requests.push(args)
+        return Promise.resolve({ data: { code: 0 } })
+      }
+    }
+  }
+
+  autoSave.call(component)
+  await Promise.resolve()
+
+  assert.equal(requests.length, 1)
+  assert.equal(requests[0][1].activeState, -2)
+})
+
+test('编辑草稿名称为空时不执行自动保存', () => {
+  const autoSave = loadAutoSave()
+  const errors = []
+  const requests = []
+  const component = {
+    basicForm: { id: '1', activityName: '  ', activeState: -2 },
+    moreForm: { state: false },
+    autoSaving: false,
+    $message: { error: message => errors.push(message) },
+    $http: {
+      put: (...args) => {
+        requests.push(args)
+        return Promise.resolve({ data: { code: 0 } })
+      }
+    }
+  }
+
+  autoSave.call(component)
+
+  assert.deepEqual(errors, ['请填写活动名称后再自动保存草稿'])
+  assert.equal(requests.length, 0)
+})