| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464 |
- <template>
- <view class="app-container pb-24">
- <TopBar title="发布任务" show-back />
- <view class="px-4 py-4">
- <!-- 板块一:项目基本信息 -->
- <view class="card p-4 mb-4">
- <view class="detail-section-title mb-4">
- <view class="w-8 h-8 rounded-lg bg-blue-100 flex items-center justify-center mr-3">
- <text class="uni-icons uniui-folder-add-filled text-primary"></text>
- </view>
- <text>项目基本信息</text>
- </view>
- <!-- 项目选择提示 -->
- <view class="bg-blue-50 rounded-lg px-3 py-2 mb-4 flex items-center">
- <text class="uni-icons uniui-info-filled text-blue-500 mr-2 text-sm"></text>
- <text class="text-blue-600 text-sm">请从项目库选择或新增项目</text>
- </view>
- <!-- 项目选择 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>选择项目
- </text>
- <picker mode="selector" :range="projectNames" :value="projectIndex" @change="onProjectSelectChange">
- <view class="form-select flex justify-between items-center">
- <text :class="selectedProject ? 'text-gray-800' : 'text-gray-400'">{{ selectedProject?.name || '请选择项目' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 已选项目信息 -->
- <view v-if="selectedProject" class="bg-green-50 rounded-xl p-3 mb-4">
- <view class="flex items-center justify-between mb-3">
- <text class="text-green-600 text-sm font-medium">已选择:{{ selectedProject.name }}</text>
- <text class="text-green-500 text-xs" @click="resetProjectSelection">重新选择</text>
- </view>
- <view class="gap-y-2">
- <view class="flex justify-between text-sm">
- <text class="text-gray-500">合作类型:</text>
- <text class="text-gray-700">{{ selectedProject.type }}</text>
- </view>
- <view class="flex justify-between text-sm">
- <text class="text-gray-500">设施类型:</text>
- <text class="text-gray-700">油污管、化粪池</text>
- </view>
- <view class="flex justify-between text-sm">
- <text class="text-gray-500">项目地址:</text>
- <text class="text-gray-700 flex-1 text-right">{{ selectedProject.address }}</text>
- </view>
- </view>
- </view>
- <!-- 联系人列表 -->
- <view class="form-group mb-0">
- <text class="form-label">联系人</text>
- <view v-if="selectedProject" class="gap-y-2 mb-2">
- <view
- v-for="(contact, index) in selectedProject.contacts"
- :key="index"
- class="bg-gray-50 rounded-lg p-3 flex justify-between items-center"
- >
- <view>
- <text class="text-sm text-gray-800">{{ contact.name }}</text>
- <text class="text-xs text-gray-400 ml-2">{{ contact.role }}</text>
- </view>
- <text class="text-sm text-primary">{{ contact.phone }}</text>
- </view>
- </view>
- </view>
- </view>
- <!-- 板块二:服务信息 -->
- <view class="card p-4">
- <view class="detail-section-title mb-4">
- <view class="w-8 h-8 rounded-lg bg-purple-100 flex items-center justify-center mr-3">
- <text class="uni-icons uniui-gear-filled text-purple-500"></text>
- </view>
- <text>服务信息</text>
- </view>
- <!-- 服务时间 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>服务时间
- </text>
- <view class="flex gap-2">
- <picker mode="date" :value="serviceDate" @change="onServiceDateChange" class="flex-1">
- <view class="form-select flex justify-between items-center">
- <text :class="serviceDate ? 'text-gray-800' : 'text-gray-400'">{{ serviceDate || '选择日期' }}</text>
- <text class="uni-icons uniui-calendar text-gray-400"></text>
- </view>
- </picker>
- <picker mode="time" :value="serviceTime" @change="onServiceTimeChange" style="flex: 0.6;">
- <view class="form-select flex justify-between items-center">
- <text :class="serviceTime ? 'text-gray-800' : 'text-gray-400'">{{ serviceTime || '选择时间' }}</text>
- <text class="uni-icons uniui-clock text-gray-400"></text>
- </view>
- </picker>
- </view>
- </view>
- <!-- 故障位置 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>故障位置
- </text>
- <uni-easyinput class="form-input" v-model="faultLocation" placeholder="请输入故障位置" />
- </view>
- <!-- 地理位置 -->
- <view class="form-group">
- <text class="form-label">地理位置</text>
- <view
- class="bg-gray-100 rounded-xl h-24 flex items-center justify-center border border-gray-200"
- @click="showToast('地图定位功能开发中')"
- >
- <view class="text-center">
- <text class="uni-icons uniui-location text-gray-400 text-2xl block mb-1"></text>
- <text class="text-gray-400 text-xs">点击获取当前位置</text>
- </view>
- </view>
- </view>
- <!-- 故障类型 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>故障类型
- </text>
- <view class="flex flex-wrap gap-2">
- <view
- v-for="type in faultTypes"
- :key="type"
- class="px-3 py-1 rounded-full text-sm border"
- :class="selectedFaultTypes.includes(type) ? 'bg-primary text-white border-primary' : 'bg-gray-100 text-gray-600 border-gray-200'"
- @click="toggleFaultType(type)"
- >
- {{ type }}
- </view>
- </view>
- </view>
- <!-- 客户原话 -->
- <view class="form-group">
- <text class="form-label">客户原话</text>
- <textarea class="form-textarea" v-model="customerWords" placeholder="请记录客户原话描述..."></textarea>
- </view>
- <!-- 故障描述 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>故障描述
- </text>
- <textarea class="form-textarea" v-model="faultDesc" placeholder="请详细描述故障情况..."></textarea>
- </view>
- <!-- 施工类型 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>施工类型
- </text>
- <picker mode="selector" :range="serviceTypes" :value="serviceTypeIndex" @change="onServiceTypeChange">
- <view class="form-select flex justify-between items-center">
- <text :class="serviceType ? 'text-gray-800' : 'text-gray-400'">{{ serviceType || '请选择施工类型' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 施工设施 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>施工设施
- </text>
- <picker mode="selector" :range="facilityOptions" :value="facilityIndex" @change="onFacilityChange">
- <view class="form-select flex justify-between items-center">
- <text :class="facility ? 'text-gray-800' : 'text-gray-400'">{{ facility || '请选择施工设施' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 服务方式 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>服务方式
- </text>
- <picker mode="selector" :range="serviceModes" :value="serviceModeIndex" @change="onServiceModeChange">
- <view class="form-select flex justify-between items-center">
- <text :class="serviceMode ? 'text-gray-800' : 'text-gray-400'">{{ serviceMode || '请选择服务方式' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 建议出车 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>建议出车
- </text>
- <picker mode="selector" :range="vehicleOptions" :value="vehicleIndex" @change="onVehicleChange">
- <view class="form-select flex justify-between items-center">
- <text :class="vehicle ? 'text-gray-800' : 'text-gray-400'">{{ vehicle || '请选择车辆' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 取水距离 -->
- <view class="form-group">
- <text class="form-label">取水距离</text>
- <picker mode="selector" :range="waterOptions" :value="waterIndex" @change="onWaterChange">
- <view class="form-select flex justify-between items-center">
- <text :class="waterSource ? 'text-gray-800' : 'text-gray-400'">{{ waterSource || '请选择' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 施工助力信息 -->
- <view class="form-group">
- <text class="form-label">施工助力信息</text>
- <textarea class="form-textarea" v-model="constructionHelp" placeholder="如地库限高、需搭脚手架、登高作业等"></textarea>
- </view>
- <!-- 预计作业量 -->
- <view class="form-group">
- <text class="form-label">预计作业量</text>
- <uni-easyinput class="form-input" v-model="estimatedQuantity" placeholder="如 2个堵点" />
- </view>
- <!-- 预计工时 -->
- <view class="form-group">
- <text class="form-label">预计工时</text>
- <uni-easyinput class="form-input" v-model="estimatedHours" placeholder="如 2小时" />
- </view>
- <!-- 验收要求 -->
- <view class="form-group">
- <text class="form-label">验收要求</text>
- <uni-easyinput class="form-input" v-model="acceptanceReq" placeholder="如 正常走水" />
- </view>
- <!-- 验收方式 -->
- <view class="form-group">
- <text class="form-label">
- <text class="text-red-500">*</text>验收方式
- </text>
- <picker mode="selector" :range="acceptTypes" :value="acceptTypeIndex" @change="onAcceptTypeChange">
- <view class="form-select flex justify-between items-center">
- <text :class="acceptType ? 'text-gray-800' : 'text-gray-400'">{{ acceptType || '请选择验收方式' }}</text>
- <text class="uni-icons uniui-arrowdown text-gray-400"></text>
- </view>
- </picker>
- </view>
- <!-- 故障图片 -->
- <view class="form-group">
- <text class="form-label">故障图片/视频</text>
- <PhotoUploader v-model="photos" :max-count="9" />
- </view>
- </view>
- <!-- 提交按钮 -->
- <view class="pt-4 pb-6">
- <button class="w-full py-4 btn-primary rounded-2xl text-base font-semibold" @click="submitTask">
- 发布任务
- </button>
- </view>
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import { ref, computed } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import TopBar from '@/components/common/TopBar.vue'
- import PhotoUploader from '@/components/common/PhotoUploader.vue'
- import { useProjectStore } from '@/stores/project'
- import { useTaskStore } from '@/stores/task'
- import { showToast, generateId, navigateTo } from '@/utils'
- import type { Task } from '@/types'
- const projectStore = useProjectStore()
- const taskStore = useTaskStore()
- const projectNames = computed(() => [...projectStore.projects.map(p => p.name), '+ 新增项目'])
- const projects = computed(() => projectStore.projects)
- const selectedProject = ref<typeof projects.value[0] | null>(null)
- const projectIndex = computed(() => {
- if (!selectedProject.value) return -1
- return projects.value.findIndex(p => p.id === selectedProject.value?.id)
- })
- const serviceDate = ref('')
- const serviceTime = ref('')
- const faultLocation = ref('')
- const faultTypes = ['堵塞', '满溢', '排水慢', '异味', '其他']
- const selectedFaultTypes = ref<string[]>([])
- const customerWords = ref('')
- const faultDesc = ref('')
- const serviceTypes = ['疏通', '清淤', '排查']
- const serviceType = ref('')
- const serviceTypeIndex = computed(() => serviceTypes.indexOf(serviceType.value))
- const facilityOptions = ['PVC', '直抽直排', '油污', '排污', '油污管', '化粪池']
- const facility = ref('')
- const facilityIndex = computed(() => facilityOptions.indexOf(facility.value))
- const serviceModes = ['应急', '月计划']
- const serviceMode = ref('')
- const serviceModeIndex = computed(() => serviceModes.indexOf(serviceMode.value))
- const vehicleOptions = ['疏通车', '吸污车', '高压清洗车']
- const vehicle = ref('')
- const vehicleIndex = computed(() => vehicleOptions.indexOf(vehicle.value))
- const waterOptions = ['有消防水', '无消防水']
- const waterSource = ref('')
- const waterIndex = computed(() => waterOptions.indexOf(waterSource.value))
- const constructionHelp = ref('')
- const estimatedQuantity = ref('')
- const estimatedHours = ref('')
- const acceptanceReq = ref('')
- const acceptTypes = ['签署验收计量单', '累计自动计费']
- const acceptType = ref('')
- const acceptTypeIndex = computed(() => acceptTypes.indexOf(acceptType.value))
- const photos = ref<string[]>([])
- onLoad((query) => {
- if (query?.projectId) {
- const project = projectStore.projects.find(p => p.id === query.projectId)
- if (project) {
- selectedProject.value = project
- }
- }
- })
- function onProjectSelectChange(e: any) {
- const index = e.detail.value
- if (index >= projects.value.length) {
- navigateTo('/subPackages/pages-common/addProject')
- return
- }
- selectedProject.value = projects.value[index]
- }
- function resetProjectSelection() {
- selectedProject.value = null
- }
- function toggleFaultType(type: string) {
- const idx = selectedFaultTypes.value.indexOf(type)
- if (idx > -1) {
- selectedFaultTypes.value.splice(idx, 1)
- } else {
- selectedFaultTypes.value.push(type)
- }
- }
- function onServiceDateChange(e: any) {
- serviceDate.value = e.detail.value
- }
- function onServiceTimeChange(e: any) {
- serviceTime.value = e.detail.value
- }
- function onServiceTypeChange(e: any) {
- serviceType.value = serviceTypes[e.detail.value]
- }
- function onFacilityChange(e: any) {
- facility.value = facilityOptions[e.detail.value]
- }
- function onServiceModeChange(e: any) {
- serviceMode.value = serviceModes[e.detail.value]
- }
- function onVehicleChange(e: any) {
- vehicle.value = vehicleOptions[e.detail.value]
- }
- function onWaterChange(e: any) {
- waterSource.value = waterOptions[e.detail.value]
- }
- function onAcceptTypeChange(e: any) {
- acceptType.value = acceptTypes[e.detail.value]
- }
- function validateForm(): boolean {
- if (!selectedProject.value) {
- showToast('请选择项目')
- return false
- }
- if (!serviceDate.value) {
- showToast('请选择服务时间')
- return false
- }
- if (!faultLocation.value.trim()) {
- showToast('请输入故障位置')
- return false
- }
- if (selectedFaultTypes.value.length === 0) {
- showToast('请选择故障类型')
- return false
- }
- if (!faultDesc.value.trim()) {
- showToast('请输入故障描述')
- return false
- }
- if (!serviceType.value) {
- showToast('请选择施工类型')
- return false
- }
- if (!facility.value) {
- showToast('请选择施工设施')
- return false
- }
- if (!serviceMode.value) {
- showToast('请选择服务方式')
- return false
- }
- if (!vehicle.value) {
- showToast('请选择建议出车')
- return false
- }
- if (!acceptType.value) {
- showToast('请选择验收方式')
- return false
- }
- return true
- }
- function submitTask() {
- if (!validateForm()) return
- const newTask: Task = {
- id: generateId('t'),
- projectId: selectedProject.value!.id,
- projectName: selectedProject.value!.name,
- type: serviceMode.value === '应急' ? 'emergency' : 'regular',
- status: 'pending',
- urgent: serviceMode.value === '应急' ? 'high' : 'normal',
- createTime: new Date().toISOString(),
- timeline: [
- {
- time: new Date().toISOString(),
- action: '任务创建',
- operator: '张销售',
- role: 'sales'
- }
- ]
- }
- taskStore.tasks.push(newTask)
- showToast({ title: '任务发布成功', icon: 'success' })
- setTimeout(() => {
- navigateTo('/pages/task/task')
- }, 1500)
- }
- </script>
|