sync: 对齐小程序 v1.0.2 生产代码并脱敏

This commit is contained in:
MiFaZhan
2026-10-29 15:15:12 +08:00
parent a7abd476f0
commit 5fcf74deb0
423 changed files with 15001 additions and 6382 deletions
@@ -2,9 +2,8 @@
import { createWorkOrder } from '../api/workOrder'
import RouterPath from '@/constant/RouterPath'
import { useUserStore } from '@/store/user'
import { useTokenStore } from '@/store/token'
import WorkOrderAttachments from '@/components/WorkOrderAttachments.vue'
import { hasAnyFeature } from '@/utils/permission'
import { getEnvBaseUrl } from '@/utils'
definePage({
style: {
@@ -24,14 +23,27 @@ const problemTypes = [
const userStore = useUserStore()
const noPermission = ref(false)
const submitting = ref(false)
const submitted = ref(false)
const uploading = ref(false)
const attachmentUrls = ref<string[]>([])
const attachmentNames = 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(() => {
@@ -39,107 +51,27 @@ const typeText = computed(() => {
return problemTypes.find(item => item.value === value)?.label || '卡片问题'
})
function chooseAttachment() {
// #ifdef H5
const input = document.createElement('input')
input.type = 'file'
input.accept = '*/*'
input.onchange = (event) => {
const file = (event.target as HTMLInputElement).files?.[0]
if (file)
void uploadAttachment(file, file.name)
}
input.click()
return
// #endif
const choose = (uni as any).chooseFile || (uni as any).chooseMessageFile
if (typeof choose !== 'function') {
uni.showToast({ title: '当前平台暂不支持选择附件', icon: 'none' })
function openTypePicker() {
if (relatedId.value || submitting.value || submitted.value)
return
}
choose({
count: 1,
type: 'all',
success: (res: any) => {
const file = res.tempFiles?.[0] || res.tempFilePaths?.[0]
const filePath = file?.path || file?.tempFilePath || file
const fileName = file?.name || String(filePath || '').split('/').pop() || '附件'
if (filePath)
void uploadAttachment(filePath, fileName)
},
})
}
async function uploadAttachment(filePath: string | File, fileName: string) {
uploading.value = true
try {
if (typeof filePath !== 'string') {
const token = useTokenStore().updateNowTime().validToken
const form = new FormData()
form.append('file', filePath, fileName)
form.append('fileType', 'file')
const response = await fetch(`${getEnvBaseUrl()}/work-order/upload`, {
method: 'POST',
headers: token ? { Authorization: `Bearer ${token}` } : undefined,
body: form,
})
const body = await response.json()
if (!response.ok || Number(body.code) !== 0)
throw new Error(body.message || body.msg || '附件上传失败')
const data = body.data || body
const url = data.url || data.path
if (!url)
throw new Error('上传接口未返回文件地址')
attachmentUrls.value.push(url)
attachmentNames.value.push(fileName)
return
}
await new Promise<void>((resolve, reject) => {
uni.uploadFile({
url: `${getEnvBaseUrl()}/work-order/upload`,
filePath,
name: 'file',
formData: { fileType: 'file' },
success: (res) => {
try {
const body = JSON.parse(res.data)
const data = body.data || body
const url = data.url || data.path
if (!url)
throw new Error('上传接口未返回文件地址')
attachmentUrls.value.push(url)
attachmentNames.value.push(fileName)
resolve()
}
catch (error) {
reject(error)
}
},
fail: reject,
})
})
}
catch (error) {
uni.showToast({ title: getErrorMessage(error, '附件上传失败'), icon: 'none' })
}
finally {
uploading.value = false
}
}
function removeAttachment(index: number) {
attachmentUrls.value.splice(index, 1)
attachmentNames.value.splice(index, 1)
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
@@ -149,24 +81,33 @@ async function handleSubmit() {
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: Number(formData.woType[0] ?? 0),
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) {
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()
uni.navigateBack({ fail: () => uni.switchTab({ url: RouterPath.Business }) })
}
}, 500)
}
@@ -195,6 +136,8 @@ function getErrorMessage(error: unknown, fallback: string) {
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' })
@@ -203,91 +146,107 @@ onMounted(async () => {
</script>
<template>
<view class="min-h-screen p-24rpx pb-40rpx">
<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 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>
<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-900">提交工单</text>
<text class="mt-8rpx block text-24rpx text-#64748b leading-36rpx">
工单会分派到平台服务部门处理
</text>
</view>
</view>
</wd-card>
<wd-form :model="formData" error-type="toast">
<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-30rpx text-#111827 font-900">基础信息</text>
<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-picker
v-model="formData.woType"
label="问题类型"
label-width="96px"
:columns="problemTypes"
value-key="value"
label-key="label"
/>
<wd-input
v-model="formData.title"
label="工单标题"
label-width="96px"
placeholder="请输入简短标题"
clearable
<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-30rpx text-#111827 font-900">问题描述</text>
<text class="text-28rpx text-#0f172a font-600">问题描述</text>
</view>
</template>
<wd-textarea
v-model="formData.content"
:disabled="submitting || submitted"
placeholder="请描述问题现象、发生时间和已尝试的处理方式"
:maxlength="500"
:maxlength="2000"
show-word-limit
/>
<view class="mt-20rpx flex items-center justify-between">
<text class="text-24rpx text-#64748b">附件(可选)</text>
<wd-button size="small" type="info" :loading="uploading" @click="chooseAttachment">选择附件</wd-button>
</view>
<view v-if="attachmentNames.length" class="mt-16rpx flex flex-col gap-12rpx">
<view
v-for="(name, index) in attachmentNames"
:key="name + index"
class="flex items-center justify-between rounded-12rpx bg-#f8fafc px-16rpx py-12rpx"
>
<text class="min-w-0 flex-1 truncate text-24rpx text-#334155">{{ name }}</text>
<wd-button size="small" type="info" plain @click="removeAttachment(index)">移除</wd-button>
</view>
<view class="mt-20rpx">
<WorkOrderAttachments v-model="attachmentUrls" :disabled="submitting || submitted" @uploading="uploading = $event" />
</view>
</wd-card>
</wd-form>
</view>
</wd-form>
<wd-button block type="primary" :loading="submitting" :disabled="!canCreateWorkOrder || uploading" @click="handleSubmit">
提交工单
</wd-button>
</template>
</view>
<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>