hookehuyr

feat(签到): 添加多附件功能开关

添加环境变量 VITE_CHECKIN_MULTI_ATTACHMENT 控制是否开启多附件功能。
当开关关闭时,混合类型附件提交会提示“请分别提交”。
当开关开启时,允许混合类型附件提交,并使用新的接口字段。
更新测试用例以覆盖开关开启和关闭的场景。
......@@ -15,3 +15,6 @@ VITE_CONSOLE = 0
# appID相关
VITE_APPID=微信appID
# 是否开启多附件功能
VITE_CHECKIN_MULTI_ATTACHMENT = 0
......
import { describe, expect, it, vi, beforeEach } from 'vitest'
let mock_query = {}
vi.mock('vue-router', () => {
const router = {
push: vi.fn(),
......@@ -7,7 +9,7 @@ vi.mock('vue-router', () => {
}
return {
useRoute: () => ({
query: {}
query: mock_query
}),
useRouter: () => router
}
......@@ -58,6 +60,7 @@ import { addUploadTaskAPI } from '@/api/checkin'
describe('useCheckin 上传大小限制', () => {
beforeEach(() => {
vi.clearAllMocks()
mock_query = { enable_multi: '0' }
if (!globalThis.sessionStorage) {
let store = {}
globalThis.sessionStorage = {
......@@ -136,6 +139,7 @@ describe('useCheckin 上传大小限制', () => {
describe('useCheckin 提交兼容', () => {
beforeEach(() => {
vi.clearAllMocks()
mock_query = { enable_multi: '0' }
if (globalThis.sessionStorage?.clear) sessionStorage.clear()
})
......@@ -156,13 +160,31 @@ describe('useCheckin 提交兼容', () => {
expect(addUploadTaskAPI).toHaveBeenCalledTimes(2)
expect(addUploadTaskAPI.mock.calls[0][0]).toHaveProperty('files')
expect(addUploadTaskAPI.mock.calls[0][0]).toHaveProperty('meta_id')
expect(addUploadTaskAPI.mock.calls[1][0]).toHaveProperty('meta_id')
expect(addUploadTaskAPI.mock.calls[1][0]).not.toHaveProperty('files')
expect(showToast).toHaveBeenCalledWith('提交成功')
})
it('混合附件且新结构失败时会提示分别提交', async () => {
addUploadTaskAPI.mockResolvedValueOnce({ code: 0, msg: '参数错误' })
const { activeType, fileList, message, onSubmit } = useCheckin()
activeType.value = 'image'
message.value = '随便写点内容'
fileList.value = [
{ status: 'done', meta_id: 11, file_type: 'image' },
{ status: 'done', meta_id: 12, file_type: 'audio' },
]
await onSubmit({ subtask_id: 's1' })
expect(addUploadTaskAPI).toHaveBeenCalledTimes(0)
expect(showToast).toHaveBeenCalledWith('当前接口暂不支持多类型附件,请分别提交')
})
it('开启多附件开关时允许 mixed files 提交', async () => {
mock_query = { enable_multi: '1' }
addUploadTaskAPI.mockResolvedValueOnce({ code: 1, data: { id: 1 } })
const { activeType, fileList, message, onSubmit } = useCheckin()
......@@ -176,6 +198,9 @@ describe('useCheckin 提交兼容', () => {
await onSubmit({ subtask_id: 's1' })
expect(addUploadTaskAPI).toHaveBeenCalledTimes(1)
expect(showToast).toHaveBeenCalledWith('当前接口暂不支持多类型附件,请分别提交')
expect(addUploadTaskAPI.mock.calls[0][0]).toHaveProperty('files')
expect(addUploadTaskAPI.mock.calls[0][0]).not.toHaveProperty('meta_id')
expect(addUploadTaskAPI.mock.calls[0][0]).toHaveProperty('file_type', 'mixed')
expect(showToast).toHaveBeenCalledWith('提交成功')
})
})
......
......@@ -23,6 +23,16 @@ export function useCheckin() {
const router = useRouter()
const { currentUser } = useAuth()
// TAG: 多附件功能开关
const multiAttachmentEnabled = computed(() => {
const query_value = String(route?.query?.enable_multi ?? '')
if (query_value === '1') return true
if (query_value === '0') return false
const fromEnv = String(import.meta.env.VITE_CHECKIN_MULTI_ATTACHMENT || '') === '1'
return fromEnv
})
// ==================== 状态定义 ====================
/** @type {import('vue').Ref<boolean>} 上传中状态 */
......@@ -470,19 +480,25 @@ export function useCheckin() {
if (files.length === 0 && activeType.value === 'text') {
submitData.file_type = 'text'
} else if (files.length > 0) {
const types = new Set(files.map(f => f.file_type).filter(Boolean))
const hasMixedTypes = types.size > 1
console.warn(multiAttachmentEnabled.value);
if (hasMixedTypes && !multiAttachmentEnabled.value) {
showToast('当前接口暂不支持多类型附件,请分别提交')
return
}
// 如果有文件,file_type 可能是 'mixed' 或者取第一个文件的类型作为主类型
// 这里暂时保留 file_type 字段以防后端必须校验,取第一个文件的类型,或者传 'mixed'
// 如果后端已更新为只看 files 数组,则此字段可能无效
const types = new Set(files.map(f => f.file_type))
if (types.size > 1) {
submitData.file_type = 'mixed' // 假设后端支持 mixed
} else {
submitData.file_type = files[0].file_type
submitData.file_type = hasMixedTypes ? 'mixed' : files[0].file_type
if (!multiAttachmentEnabled.value) {
submitData.meta_id = files.map(f => f.meta_id)
}
}
// 如果有文件,添加文件ID (为了兼容可能还在用 meta_id 的旧接口逻辑,如果确定废弃可删除)
// submitData.meta_id = files.map(f => f.meta_id)
// 说明:submitData.meta_id 为旧接口兜底字段,后端完全支持 files 后再移除
let result
if (route.query.status === 'edit') {
......@@ -491,7 +507,7 @@ export function useCheckin() {
i: route.query.post_id,
subtask_id: submitData.subtask_id || route.query.subtask_id,
note: submitData.note,
// meta_id: submitData.meta_id,
...(submitData.meta_id ? { meta_id: submitData.meta_id } : {}),
files: submitData.files,
file_type: submitData.file_type,
}
......@@ -515,9 +531,13 @@ export function useCheckin() {
const hasMixedTypes = types.size > 1
if (hasMixedTypes) {
if (!multiAttachmentEnabled.value) {
showToast('当前接口暂不支持多类型附件,请分别提交')
return
}
showToast(result?.msg || '提交失败,请重试')
return
}
const legacy_type = files?.[0]?.file_type || submitData.file_type || activeType.value
const legacy_meta_id = (files || []).filter(f => f.file_type === legacy_type).map(f => f.meta_id)
......