让凭证数组与套餐作废任务进入可联调状态
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 7m52s

Constraint: 后端订单套餐作废接口使用 /api/admin/order-package-invalidate-tasks,上传
  purpose 只能使用 attachment。
  Rejected: 继续提交逗号拼接凭证字符串 | 新提交路径必须使用 string[],历史字符串只做读取归一化。
  Confidence: high
  Scope-risk: broad
  Directive: 新增订单套餐作废权限时需同时配置菜单 URL 与
  order_package_invalidate_task:create/detail 按钮权限。
  Tested: bun run build;development 真实后端联调上传、创建、列表、详情通过;复机接口未触发。
  Not-tested: 未逐一手工覆盖所有历史凭证数据、全部角色权限组合和所有文件扩展名预览
This commit is contained in:
2026-06-23 09:50:12 +08:00
parent ebf9739f06
commit 0c3065f970
71 changed files with 4264 additions and 240 deletions

View File

@@ -50,7 +50,7 @@
</template>
<script setup lang="ts">
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { genFileId } from 'element-plus'
import { Close, Document, UploadFilled } from '@element-plus/icons-vue'
@@ -58,7 +58,7 @@
import { StorageService } from '@/api/modules'
interface Props {
modelValue?: string
modelValue?: string[] | string
voucherName?: string
maxCount?: number
}
@@ -69,9 +69,9 @@
})
const emit = defineEmits<{
'update:modelValue': [value: string | undefined]
'update:modelValue': [value: string[]]
'uploading-change': [value: boolean]
change: [value: string | undefined]
change: [value: string[]]
}>()
const rootRef = ref<HTMLElement>()
@@ -91,9 +91,8 @@
const emitVoucherKeys = () => {
const keys = Array.from(voucherFileKeyMap.values())
const value = keys.length ? keys.join(',') : undefined
emit('update:modelValue', value)
emit('change', value)
emit('update:modelValue', keys)
emit('change', keys)
}
const setUploadingCount = (count: number) => {
@@ -114,6 +113,26 @@
}
}
watch(
() => props.modelValue,
(value) => {
const keys = Array.isArray(value)
? value
: value
? value
.split(',')
.map((key) => key.trim())
.filter(Boolean)
: []
voucherFileKeyMap.clear()
keys.slice(0, getMaxCount()).forEach((key, index) => {
voucherFileKeyMap.set(index + 1, key)
})
},
{ immediate: true }
)
const revokeFileObjectUrl = (uid: number) => {
const url = fileObjectUrlMap.get(uid)
if (!url) return