253 lines
8.7 KiB
Vue
253 lines
8.7 KiB
Vue
<script lang="ts" setup>
|
||
import { createWorkOrder } from '../api/workOrder'
|
||
import RouterPath from '@/constant/RouterPath'
|
||
import { useUserStore } from '@/store/user'
|
||
import WorkOrderAttachments from '@/components/WorkOrderAttachments.vue'
|
||
import { hasAnyFeature } from '@/utils/permission'
|
||
|
||
definePage({
|
||
style: {
|
||
navigationBarTitleText: '提交工单',
|
||
},
|
||
})
|
||
|
||
const problemTypes = [
|
||
{ label: '卡片问题', value: 0 },
|
||
{ label: '模组问题', value: 1 },
|
||
{ label: '设备问题', value: 2 },
|
||
{ label: '订单问题', value: 3 },
|
||
{ label: '余额问题', value: 4 },
|
||
{ label: '提现问题', value: 5 },
|
||
]
|
||
|
||
const userStore = useUserStore()
|
||
const noPermission = ref(false)
|
||
const submitting = ref(false)
|
||
const submitted = ref(false)
|
||
const uploading = ref(false)
|
||
const attachmentUrls = ref<string[]>([])
|
||
const relatedId = ref<number>()
|
||
const typePickerOpen = ref(false)
|
||
const canCreateWorkOrder = computed(() => hasAnyFeature(userStore.userInfo, 'workorder:create'))
|
||
const formData = reactive({
|
||
woType: [0],
|
||
title: '',
|
||
content: '',
|
||
creatorPhone: userStore.userInfo?.phone || '',
|
||
})
|
||
|
||
onLoad((query) => {
|
||
const type = Number(query?.woType)
|
||
const id = Number(query?.relatedId)
|
||
if ([0, 3].includes(type) && Number.isSafeInteger(id) && id > 0) {
|
||
formData.woType = [type]
|
||
relatedId.value = id
|
||
formData.content = String(query?.context || '').slice(0, 500)
|
||
}
|
||
})
|
||
|
||
const typeText = computed(() => {
|
||
const value = Number(formData.woType[0] ?? 0)
|
||
return problemTypes.find(item => item.value === value)?.label || '卡片问题'
|
||
})
|
||
|
||
function openTypePicker() {
|
||
if (relatedId.value || submitting.value || submitted.value)
|
||
return
|
||
typePickerOpen.value = true
|
||
}
|
||
|
||
async function handleSubmit() {
|
||
if (submitting.value || submitted.value || uploading.value)
|
||
return
|
||
if (!canCreateWorkOrder.value) {
|
||
uni.showToast({ title: '暂无提交工单权限', icon: 'none' })
|
||
return
|
||
}
|
||
const title = formData.title.trim()
|
||
const content = formData.content.trim()
|
||
const creatorPhone = formData.creatorPhone.trim()
|
||
const woType = Number(formData.woType[0])
|
||
if (!problemTypes.some(item => item.value === woType)) {
|
||
uni.showToast({ title: '请选择有效的工单类型', icon: 'none' })
|
||
return
|
||
}
|
||
if (!title) {
|
||
uni.showToast({ title: '请输入工单标题', icon: 'none' })
|
||
return
|
||
}
|
||
if (!content) {
|
||
uni.showToast({ title: '请输入问题描述', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
if (!/^1[3-9]\d{9}$/.test(creatorPhone)) {
|
||
uni.showToast({ title: creatorPhone ? '请输入正确的手机号' : '请输入创建人手机号', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
submitting.value = true
|
||
try {
|
||
const res = await createWorkOrder({
|
||
woType,
|
||
title,
|
||
content,
|
||
creatorPhone,
|
||
attachmentUrls: attachmentUrls.value,
|
||
relatedId: relatedId.value,
|
||
})
|
||
const woNo = res.workOrder?.woNo || ''
|
||
submitted.value = true
|
||
uni.showToast({ title: '工单已提交', icon: 'success' })
|
||
setTimeout(() => {
|
||
if (woNo && hasAnyFeature(userStore.userInfo, 'workorder:detail')) {
|
||
uni.redirectTo({
|
||
url: `${RouterPath.BusinessViewsWorkOrderDetail}?woNo=${encodeURIComponent(woNo)}`,
|
||
fail: () => uni.navigateBack({ fail: () => uni.switchTab({ url: RouterPath.Business }) }),
|
||
})
|
||
}
|
||
else {
|
||
uni.navigateBack({ fail: () => uni.switchTab({ url: RouterPath.Business }) })
|
||
}
|
||
}, 500)
|
||
}
|
||
catch (error) {
|
||
uni.showToast({ title: getErrorMessage(error, '工单提交失败'), icon: 'none' })
|
||
}
|
||
finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
function getErrorMessage(error: unknown, fallback: string) {
|
||
if (error && typeof error === 'object') {
|
||
const data = error as { msg?: unknown, message?: unknown, errMsg?: unknown }
|
||
if (typeof data.msg === 'string' && data.msg)
|
||
return data.msg
|
||
if (typeof data.message === 'string' && data.message)
|
||
return data.message
|
||
if (typeof data.errMsg === 'string' && data.errMsg)
|
||
return data.errMsg
|
||
}
|
||
if (error instanceof Error && error.message)
|
||
return error.message
|
||
return fallback
|
||
}
|
||
|
||
onMounted(async () => {
|
||
await userStore.fetchUserInfo().catch(() => undefined)
|
||
if (!formData.creatorPhone)
|
||
formData.creatorPhone = userStore.userInfo?.phone || ''
|
||
if (!canCreateWorkOrder.value) {
|
||
noPermission.value = true
|
||
uni.showToast({ title: '当前账号不能提交工单', icon: 'none' })
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view class="min-h-screen bg-#f5f7fa p-24rpx pb-48rpx">
|
||
<view class="mx-auto max-w-920rpx flex flex-col gap-20rpx">
|
||
<wd-card v-if="noPermission" custom-class="!mx-0 !mb-0">
|
||
<view class="text-26rpx text-#64748b leading-38rpx">
|
||
当前账号不能提交工单。
|
||
</view>
|
||
</wd-card>
|
||
|
||
<template v-else>
|
||
<wd-card custom-class="!mx-0 !mb-0">
|
||
<view class="flex items-start gap-16rpx">
|
||
<view class="h-64rpx w-64rpx center shrink-0 rounded-20rpx bg-#eff6ff text-#2563eb">
|
||
<view class="i-carbon-chat text-36rpx" />
|
||
</view>
|
||
<view class="min-w-0 flex-1">
|
||
<text class="block text-34rpx text-#111827 font-700">提交工单</text>
|
||
<text class="mt-8rpx block text-24rpx text-#64748b leading-36rpx">
|
||
提交后由服务部门跟进,可在工单详情查看进度
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</wd-card>
|
||
|
||
<wd-form :model="formData" error-type="toast">
|
||
<view class="flex flex-col gap-20rpx">
|
||
<wd-card custom-class="!mx-0 !mb-0" custom-content-class="!p-0">
|
||
<template #title>
|
||
<view class="h-88rpx flex items-center justify-between">
|
||
<text class="text-28rpx text-#0f172a font-600">基础信息</text>
|
||
<wd-tag type="primary" variant="plain">
|
||
{{ typeText }}
|
||
</wd-tag>
|
||
</view>
|
||
</template>
|
||
<wd-cell-group :border="false">
|
||
<wd-cell
|
||
title="工单类型"
|
||
:value="typeText"
|
||
:label="relatedId ? '已关联业务,类型不可更改' : '请选择本次工单的问题分类'"
|
||
:is-link="!relatedId && !submitting && !submitted"
|
||
center
|
||
custom-title-class="!text-28rpx !text-#334155"
|
||
custom-value-class="!text-26rpx !text-#2563eb"
|
||
@click="openTypePicker"
|
||
/>
|
||
<wd-form-item title="工单标题" prop="title" required title-width="96px" custom-title-class="!text-28rpx !text-#334155">
|
||
<wd-input
|
||
v-model="formData.title"
|
||
:disabled="submitting || submitted"
|
||
placeholder="请输入简短标题"
|
||
:maxlength="100"
|
||
compact
|
||
clearable
|
||
/>
|
||
</wd-form-item>
|
||
<wd-form-item title="创建人手机号" prop="creatorPhone" required title-width="96px" custom-title-class="!text-28rpx !text-#334155">
|
||
<wd-input
|
||
v-model="formData.creatorPhone"
|
||
:disabled="submitting || submitted"
|
||
type="number"
|
||
placeholder="请输入创建人手机号"
|
||
:maxlength="11"
|
||
compact
|
||
clearable
|
||
/>
|
||
</wd-form-item>
|
||
</wd-cell-group>
|
||
</wd-card>
|
||
|
||
<wd-card custom-class="!mx-0 !mb-0">
|
||
<template #title>
|
||
<view class="h-88rpx flex items-center">
|
||
<text class="text-28rpx text-#0f172a font-600">问题描述</text>
|
||
</view>
|
||
</template>
|
||
<wd-textarea
|
||
v-model="formData.content"
|
||
:disabled="submitting || submitted"
|
||
placeholder="请描述问题现象、发生时间和已尝试的处理方式"
|
||
:maxlength="2000"
|
||
show-word-limit
|
||
/>
|
||
<view class="mt-20rpx">
|
||
<WorkOrderAttachments v-model="attachmentUrls" :disabled="submitting || submitted" @uploading="uploading = $event" />
|
||
</view>
|
||
</wd-card>
|
||
</view>
|
||
</wd-form>
|
||
|
||
<wd-button block type="primary" :round="false" :loading="submitting" :disabled="!canCreateWorkOrder || submitting || uploading || submitted" @click="handleSubmit">
|
||
提交工单
|
||
</wd-button>
|
||
</template>
|
||
</view>
|
||
<wd-picker
|
||
v-model="formData.woType"
|
||
v-model:visible="typePickerOpen"
|
||
title="选择工单类型"
|
||
:columns="[problemTypes]"
|
||
value-key="value"
|
||
label-key="label"
|
||
/>
|
||
</view>
|
||
</template>
|