| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- import { type Ref, ref } from 'vue'
- import { draftTaskContentApi, saveTaskContentApi } from '@/services/modules/task/taskFrom'
- import type {
- SaveTaskContentBody,
- SaveTaskContentQuery,
- SaveTaskContentSuccessData,
- } from '@/services/modules/task/taskFrom/type'
- import type { DynamicFormState, TaskFieldConfigViewItem } from './useTaskForm'
- interface UseTaskFormActionOptions {
- taskTypeId: Ref<string>
- form: Ref<DynamicFormState>
- taskFieldConfigList: Ref<TaskFieldConfigViewItem[]>
- }
- type FormAction = 'save' | 'submit'
- const PACKAGE_FIELD_ALIASES = new Set(['approvalResult', 'scorePackage'])
- const IMAGE_FIELD_TYPE = 'img'
- const SUCCESS_TOAST_DURATION = 3000
- const ACTION_API_MAP = {
- save: draftTaskContentApi,
- submit: saveTaskContentApi,
- } satisfies Record<FormAction, typeof saveTaskContentApi>
- const ACTION_FAILURE_TEXT = {
- save: '保存失败',
- submit: '提交失败',
- } satisfies Record<FormAction, string>
- const isEmptyValue = (value: unknown): boolean => {
- return value === undefined || value === null || String(value).trim() === ''
- }
- const normalizeSizeLimit = (value: unknown): number | null => {
- if (value === undefined || value === null || value === '') {
- return null
- }
- const size = Number(value)
- if (!Number.isFinite(size) || size <= 0) {
- return null
- }
- return Math.floor(size)
- }
- const getContentLength = (value: unknown): number => {
- if (value === undefined || value === null) {
- return 0
- }
- return Array.from(String(value).trim()).length
- }
- const getImageCount = (value: unknown): number => {
- if (typeof value !== 'string') {
- return 0
- }
- return value
- .split(',')
- .map((item) => item.trim())
- .filter(Boolean).length
- }
- const showMessage = (title: string, duration?: number): void => {
- uni.showToast({
- title,
- icon: 'none',
- ...(duration === undefined ? {} : { duration }),
- })
- }
- /**
- * 从接口 data 中提取有效图片地址。
- */
- const extractImageUrls = (data: unknown): string[] => {
- if (!Array.isArray(data)) {
- return []
- }
- const imageUrls = data
- .filter((item): item is string => typeof item === 'string' && item.trim().length > 0)
- .map((item) => item.trim())
- return [...new Set(imageUrls)]
- }
- /**
- * 判断是否为提交成功数据。
- */
- const isSaveTaskContentSuccessData = (data: unknown): data is SaveTaskContentSuccessData => {
- if (!data || typeof data !== 'object' || Array.isArray(data)) {
- return false
- }
- const record = data as Record<string, unknown>
- return typeof record.score === 'string' && typeof record.type === 'string'
- }
- export const useTaskFormAction = ({
- taskTypeId,
- form,
- taskFieldConfigList,
- }: UseTaskFormActionOptions) => {
- let actionLocked = false
- const duplicateImageVisible = ref(false)
- const duplicateImageList = ref<string[]>([])
- const findPackageField = (): TaskFieldConfigViewItem | undefined => {
- return taskFieldConfigList.value.find((field) => PACKAGE_FIELD_ALIASES.has(field.alias ?? ''))
- }
- const getPackageId = (): string => {
- const packageField = findPackageField()
- if (!packageField) {
- return ''
- }
- return String(form.value.value[packageField.id]?.value ?? '').trim()
- }
- const validateImageField = (field: TaskFieldConfigViewItem, value: unknown): boolean => {
- const imageCount = getImageCount(value)
- const configuredMinSize = normalizeSizeLimit(field.taskFiledMinsize)
- const maxSize = normalizeSizeLimit(field.taskFiledMaxsize)
- const minSize = field.isMustfill === '1' ? (configuredMinSize ?? 1) : configuredMinSize
- if (imageCount === 0 && field.isMustfill !== '1') {
- return true
- }
- if (minSize !== null && imageCount < minSize) {
- showMessage(`${field.taskFiledValue}最少需要${minSize}张`)
- return false
- }
- if (maxSize !== null && imageCount > maxSize) {
- showMessage(`${field.taskFiledValue}最多上传${maxSize}张`)
- return false
- }
- return true
- }
- const validateContentField = (field: TaskFieldConfigViewItem, value: unknown): boolean => {
- if (isEmptyValue(value)) {
- if (field.isMustfill === '1') {
- showMessage(`请完善${field.taskFiledValue}`)
- return false
- }
- return true
- }
- const contentLength = getContentLength(value)
- const minSize = normalizeSizeLimit(field.taskFiledMinsize)
- const maxSize = normalizeSizeLimit(field.taskFiledMaxsize)
- if (minSize !== null && contentLength < minSize) {
- showMessage(`${field.taskFiledValue}最少输入${minSize}个字符`)
- return false
- }
- if (maxSize !== null && contentLength > maxSize) {
- showMessage(`${field.taskFiledValue}最多输入${maxSize}个字符`)
- return false
- }
- return true
- }
- const validateField = (field: TaskFieldConfigViewItem): boolean => {
- const value = form.value.value[field.id]?.value
- if (field.taskFiledType === IMAGE_FIELD_TYPE) {
- return validateImageField(field, value)
- }
- return validateContentField(field, value)
- }
- const validateSubmitForm = (): boolean => {
- for (const field of taskFieldConfigList.value) {
- if (!validateField(field)) {
- return false
- }
- }
- return true
- }
- const buildActionQuery = (action: FormAction): SaveTaskContentQuery | null => {
- const normalizedTaskTypeId = taskTypeId.value.trim()
- if (!normalizedTaskTypeId) {
- showMessage('缺少 taskTypeId')
- return null
- }
- const packageId = getPackageId()
- if (action === 'submit' && !packageId) {
- const packageField = findPackageField()
- showMessage(
- packageField ? `请完善${packageField.taskFiledValue}` : '表单配置中未找到关联积分包字段'
- )
- return null
- }
- return {
- taskTypeId: normalizedTaskTypeId,
- packageId,
- }
- }
- const buildSubmitPayload = (): SaveTaskContentBody => {
- const payload: SaveTaskContentBody = {}
- for (const field of taskFieldConfigList.value) {
- const fieldValue = form.value.value[field.id]
- payload[field.taskFiledValue] = {
- seq: field.seq ?? 0,
- ...(field.isMustfill === '1'
- ? {
- required: true,
- }
- : {}),
- items: [
- {
- label: String(fieldValue?.label ?? ''),
- type: field.taskFiledType,
- value: String(fieldValue?.value ?? ''),
- },
- ],
- }
- }
- return payload
- }
- const clearDuplicateImages = (): void => {
- duplicateImageList.value = []
- }
- const showDuplicateImages = (images: string[]): void => {
- duplicateImageList.value = images
- duplicateImageVisible.value = true
- }
- const handleActionSuccess = (action: FormAction, data: unknown): void => {
- if (action === 'save') {
- showMessage('保存成功', SUCCESS_TOAST_DURATION)
- } else {
- if (!isSaveTaskContentSuccessData(data)) {
- showMessage('提交结果数据异常')
- return
- }
- showMessage(`提交成功,若审核通过可获得${data.score}积分`, SUCCESS_TOAST_DURATION)
- }
- setTimeout(() => {
- uni.navigateBack()
- }, SUCCESS_TOAST_DURATION)
- }
- const executeAction = async (action: FormAction): Promise<void> => {
- if (actionLocked) {
- return
- }
- actionLocked = true
- try {
- if (action === 'submit' && !validateSubmitForm()) {
- return
- }
- const query = buildActionQuery(action)
- if (!query) {
- return
- }
- const payload = buildSubmitPayload()
- const requestApi = ACTION_API_MAP[action]
- const response = await requestApi(query, payload)
- /**
- * 提交时:
- * code === 1 且 data 为非空图片数组,
- * 视为图片重复,不能进入成功逻辑。
- */
- if (action === 'submit' && response.code === 1) {
- const duplicateImages = extractImageUrls(response.data)
- if (duplicateImages.length > 0) {
- showDuplicateImages(duplicateImages)
- return
- }
- }
- /**
- * 所有非 0 状态码均不能视为成功。
- */
- if (response.code !== 0) {
- showMessage(response.msg || ACTION_FAILURE_TEXT[action])
- return
- }
- handleActionSuccess(action, response.data)
- } catch (error: unknown) {
- console.error(`[useTaskFormAction] ${action} 失败`, error)
- showMessage(ACTION_FAILURE_TEXT[action])
- } finally {
- actionLocked = false
- }
- }
- const handleSave = (): Promise<void> => {
- return executeAction('save')
- }
- const handleSubmit = (): Promise<void> => {
- return executeAction('submit')
- }
- return {
- duplicateImageVisible,
- duplicateImageList,
- clearDuplicateImages,
- handleSave,
- handleSubmit,
- }
- }
|