| | |
| | | </van-swipe> |
| | | |
| | | <van-image-preview v-model:show="previewShow" :images="getImageList" :initial-index="previewIndex" /> |
| | | <div class="detailTitle"> |
| | | <div class="titleText"> |
| | | <div class="itemStatus"> |
| | | <span v-if="currentDetail.status === 0" style="background-color: #ff7411"></span> |
| | | <span v-else-if="currentDetail.status === 2" style="background-color: #ff472f"></span> |
| | | <span v-else-if="currentDetail.status === 3" style="background-color: #ffc300"></span> |
| | | <span v-else-if="currentDetail.status === 4" style="background-color: #06d957"></span> |
| | | <div>{{ currentDetail.event_name }}</div> |
| | | </div> |
| | | <div class="timeNavigation"> |
| | | <p>{{ formatDate(currentDetail.create_time) }}</p> |
| | | <img @click="jumpMap(currentDetail)" src="/src/appDataSource/appwork/navigation.svg" |
| | | alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 步骤条 --> |
| | | <div class="stepBox"> |
| | | <div class="stepContainer"> |
| | | <van-steps direction="vertical" :active="currentStep"> |
| | | <van-step v-for="(step, index) in displayedSteps" :key="step.status"> |
| | | <div class="horizontal-step"> |
| | | <span class="step-title" :class="getStatusClass(index)">{{ step.title }}</span> |
| | | <!-- 工单内容 --> |
| | | <div class="worderContainer"> |
| | | <div class="workOrderContent"> |
| | | <div class="workOrderTitle">工单内容</div> |
| | | <div class="workOrderContainer"> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">工单编号</div> |
| | | <div>{{ workDetailData.eventNum }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">工单处置人</div> |
| | | <div>{{ workDetailData.disposeUserName }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">处置部门</div> |
| | | <div>{{ workDetailData.disposeDeptName }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">拍摄时间</div> |
| | | <div>{{ workDetailData.shootTime }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">分发人员</div> |
| | | <div>{{ workDetailData.distributeUserName }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">分发部门</div> |
| | | <div>{{ workDetailData.distributeDeptName }}</div> |
| | | </div> |
| | | |
| | | <div class="step-desc" v-if="stepResponse[index]"> |
| | | <span>{{ stepResponse[index].name || '' }}</span> |
| | | <span>{{ stepResponse[index].create_time || '' }}</span> |
| | | </div> |
| | | </div> |
| | | </van-step> |
| | | </van-steps> |
| | | </div> |
| | | </div> |
| | | <!-- 工单内容 --> |
| | | <div class="worderContainer"> |
| | | <div class="workOrderContent"> |
| | | <div class="workOrderTitle">工单内容</div> |
| | | <div class="workOrderContainer"> |
| | | <div class="orderRow"> |
| | | <span class="required-mark" v-if="isEditable">*</span> |
| | | <div class="rowTitle">工单名称</div> |
| | | <div v-if="!isEditable">{{ currentDetail.event_name }}</div> |
| | | <van-field v-else v-model="currentDetail.event_name" name="event_name" required |
| | | input-align="right" placeholder="请输入" class="custom-field" /> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">工单编号</div> |
| | | <div>{{ currentDetail.event_num }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">工单类型</div> |
| | | <div>{{ workOrderTypeName }}</div> |
| | | </div> |
| | | <div class="orderRow" v-if="currentDetail.work_type == '0'"> |
| | | <div class="rowTitle">关联任务</div> |
| | | <div>{{ currentDetail.job_name }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">工单创建人</div> |
| | | <div>{{ currentDetail.event_num?.slice(0, 2) === 'AI' ? '智飞agent' : currentDetail.create_user }} |
| | | </div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">事件地址</div> |
| | | <div class="addressBox" @click="jumpMap(currentDetail)"> |
| | | <div class="rowAddress">{{ currentDetail.address }}</div> |
| | | <img class="pointing" src="/src/appDataSource/appwork/pointing.svg" alt="" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="orderRow"> |
| | | <div class="guanlian"> |
| | | <span class="required-mark" v-if="isEditable">*</span> |
| | | <div class="rowTitle">关联算法</div> |
| | | </div> |
| | | <div v-if="!isEditable">{{ currentDetail.ai_types }}</div> |
| | | <div v-else> |
| | | <span @click="openselect" class="selectTrigger"> |
| | | {{ currentDetail.ai_types }} |
| | | <img class="downpointing" src="/src/appDataSource/appwork/downpointing.svg" alt="" /> |
| | | </span> |
| | | |
| | | <van-popup v-model:show="showPicker" destroy-on-close position="bottom" |
| | | :close-on-click-overlay="true"> |
| | | <van-picker v-model="selectedValues" title="选择关联算法" :columns="columns" |
| | | @confirm="onConfirm" @cancel="onCancel" /> |
| | | </van-popup> |
| | | </div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">发起部门</div> |
| | | <div>{{ currentDetail.dept_name }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">发起任务时间</div> |
| | | <div>{{ currentDetail.create_time }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">所属机巢</div> |
| | | <div>{{ currentDetail.device_names }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <span class="required-mark" v-if="isEditable">*</span> |
| | | <div class="rowTitle">工单内容</div> |
| | | <div v-if="!isEditable">{{ currentDetail.content }}</div> |
| | | <van-field v-else class="custom-field" input-align="right" v-model="currentDetail.content" |
| | | name="remark" required placeholder="请输入" /> |
| | | </div> |
| | | <div class="rowCard" v-if="isEditableProcess"> |
| | | <div class="guanlian"> |
| | | <span class="required-mark">*</span> |
| | | <div class="rowTitle"> |
| | | 上传图片 |
| | | <span>(只能上传jpg、jpeg、png照片,且不超过5M)</span> |
| | | </div> |
| | | </div> |
| | | <div> |
| | | <van-field name="uploader"> |
| | | <template #input> |
| | | <van-uploader :disabled="!isCurrentUserAssigned" v-model="fileList" |
| | | :after-read="afterRead" :max-count="1" :max-size="5 * 1024 * 1024" |
| | | @oversize="onOversize" :before-read="beforeRead" @delete="handleDelete" |
| | | accept="image/jpeg,image/jpg,image/png" /> |
| | | </template> |
| | | </van-field> |
| | | </div> |
| | | </div> |
| | | <div class="orderRow" v-if="isEditableProcess"> |
| | | <span class="required-mark">*</span> |
| | | <div class="rowTitle">事件处理详情</div> |
| | | <van-field class="custom-field" input-align="right" v-model="currentDetail.processingDetail" |
| | | name="event_name" required placeholder="请输入" :disabled="!isCurrentUserAssigned" /> |
| | | </div> |
| | | <div class="orderRow" v-if="isComplent"> |
| | | <div class="rowTitle">事件处理详情</div> |
| | | <div>{{ currentDetail.processingDetail }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 操作按钮 --> |
| | | <div class="actionButton"> |
| | | <div class="floatingBtn leftFloatingBtn" @click="leftClick"> |
| | | <img src="/src/appDataSource/appwork/leftBtn1.svg" alt="" /> |
| | | </div> |
| | | <div class="paginationInfo"> |
| | | <span>{{ currentPage }} / {{ totalRecords }}</span> |
| | | </div> |
| | | <div class="btngroups" v-if="currentDetail.status === 2"> |
| | | <van-button type="primary" color="#FF452C" round text="不通过" @click="rejectTicket"></van-button> |
| | | <van-button type="primary" color="#1D6FE9" round text="通过" @click="approveTicket"></van-button> |
| | | </div> |
| | | <div class="btngroups" v-else-if="currentDetail.status === 0"> |
| | | <van-button type="primary" color="#FF452C" round text="不受理" @click="rejectTicket"></van-button> |
| | | <van-button type="primary" color="#1D6FE9" round text="受理" @click="approveAndDispatch"></van-button> |
| | | </div> |
| | | <div class="btngroups" v-else-if="currentDetail.status === 3"> |
| | | <van-button type="primary" color="#FF452C" round text="取消" @click="cancellation"></van-button> |
| | | <van-button :disabled="!isCurrentUserAssigned" type="primary" color="#1D6FE9" round text="完成工单" |
| | | @click="completeTicket"></van-button> |
| | | </div> |
| | | <div class="btngroups back-btn" v-else> |
| | | <van-button type="primary" round color="#FF730F" text="返回" @click="cancellation"></van-button> |
| | | </div> |
| | | <div class="floatingBtn rightFloatingBtn" @click="rightClick"> |
| | | <img src="/src/appDataSource/appwork/rightBtn1.svg" alt="" /> |
| | | </div> |
| | | </div> |
| | | |
| | | <!--派发工单对话框--> |
| | | <van-dialog v-model:show="isshowDispatch" title="派发工单" show-cancel-button @confirm="submitDispatch" |
| | | @cancel="dispatchCancel"> |
| | | <van-field v-model="dispatchForm.departmentName" is-link readonly label="选择部门" placeholder="请选择部门" |
| | | @click="openPicker('department')" /> |
| | | <van-field v-model="dispatchForm.handlerName" is-link readonly label="选择处理人" placeholder="请选择处理人" |
| | | @click="openPicker('processor')" :disabled="!dispatchForm.department" /> |
| | | </van-dialog> |
| | | |
| | | <teleport to="body"> |
| | | <van-popup v-model:show="showGlobalPicker" position="bottom"> |
| | | <!-- 部门选择器 --> |
| | | <van-picker v-if="currentPickerType === 'department'" v-model="selectedDepartment" title="选择部门" |
| | | :columns="departments" @confirm="onDepartmentConfirm" @cancel="onPickerCancel" /> |
| | | |
| | | <!-- 处理人选择器 --> |
| | | <van-picker v-if="currentPickerType === 'processor'" v-model="selectedProcessor" title="选择处理人" |
| | | :columns="availableDispatchHandlers" @confirm="onProcessorConfirm" @cancel="onPickerCancel" /> |
| | | </van-popup> |
| | | </teleport> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">分发时间</div> |
| | | <div>{{ workDetailData.distributeTime }}</div> |
| | | </div> |
| | | <div class="orderRow"> |
| | | <div class="rowTitle">工单位置</div> |
| | | <div class="rowAddress" @click="jumpMap(workDetailData)">{{ workDetailData.eventLocation }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import {ALL_WORK_ORDER_TYPE_OPTIONS} from '@ztzf/constants' |
| | | |
| | | import { getDeviceRegion } from '@/api/home/aggregation' |
| | | import { usePermission } from '@/appPages/work/usePermission' |
| | | import { showToast, showNotify, showImagePreview } from 'vant' |
| | | import { getShowImg, getSmallImg } from '@/utils/util' |
| | | import { useRoute } from 'vue-router' |
| | | import { getStepInfo, getList, flowEvent, getTicketInfo } from '/src/api/work/index.js' |
| | | import { showToast, showNotify, showImagePreview } from 'vant' |
| | | import dayjs from 'dayjs' |
| | | import _ from 'lodash' |
| | | import { useStore } from 'vuex' |
| | | const store = useStore() |
| | | const userInfo = computed(() => store?.state?.user?.userInfo) |
| | | const { permission, getButtonsApi } = usePermission() |
| | | const showPicker = ref(false) |
| | | const selectedValues = ref([]) |
| | | const columns = ref([]) |
| | | const allAlgorithms = ref([]) |
| | | const algorithms = ref([]) |
| | | const types = ref([]) //工单类型 |
| | | const eventNum = ref('') |
| | | const currentStatus = ref(0) |
| | | const stepResponse = ref([]) |
| | | const currentStep = ref(0) |
| | | |
| | | const route = useRoute() |
| | | const currentDetail = ref({ status: 0 }) |
| | | const currentIndex = ref(null) //当前显示数据索引 |
| | | const departments = ref([]) //部门 |
| | | const departmentUsers = ref({}) |
| | | const fileList = ref([]) |
| | | // 机巢数据 |
| | | let machineData = ref([]) |
| | | // 处理人选择相关 |
| | | const processorColumns = ref([]) |
| | | // 可编辑状态 |
| | | const isEditable = computed(() => currentDetail.value.status === 0) |
| | | const isEditableProcess = computed(() => currentDetail.value.status === 3) |
| | | const isComplent = computed(() => currentDetail.value.status === 4) |
| | | // 工单内容 |
| | | |
| | | const workDetailData = ref({}) |
| | | // 分页相关 |
| | | const currentPage = ref(1) |
| | | const pageSize = ref(1) |
| | | const totalRecords = ref(0) |
| | | const formatDate = dateString => { |
| | | return dayjs(dateString).format('MM/DD HH:mm') |
| | | } |
| | | const allStepConfigs = ref([ |
| | | { title: '待审核', status: 2 }, |
| | | { title: '待处理', status: 0 }, |
| | | { title: '处理中', status: 3 }, |
| | | { title: '已完成', status: 4 }, |
| | | ]) |
| | | const hasProcessingBtnPermission = () => { |
| | | return permission.value && permission.value.tickets_processing_btn === true |
| | | } |
| | | // 完成工单 |
| | | const hasProcessedAndOverBtnPermission = () => { |
| | | return permission.value && permission.value.tickets_view_processedAndOver === true |
| | | } |
| | | // 通过不通过 |
| | | const hasReviewBtnPermission = () => { |
| | | return permission.value && permission.value.tickets_review_btn === true |
| | | } |
| | | // 是否有权限完成工单 |
| | | const isCurrentUserAssigned = computed(() => { |
| | | const handleUserId = currentDetail.value?.handle_user_id |
| | | const currentUserId = userInfo.value?.user_id |
| | | return !!handleUserId && !!currentUserId && String(handleUserId) === String(currentUserId) |
| | | |
| | | // 预览图片 |
| | | const previewShow = ref(false) |
| | | const previewIndex = ref(0) |
| | | const getImageList = computed(() => { |
| | | const imageArr = [] |
| | | const detail = workDetailData.value |
| | | if (detail.eventImageUrl) { |
| | | const smallUrl = getSmallImg(detail.eventImageUrl) |
| | | imageArr.push(smallUrl) |
| | | } |
| | | return imageArr |
| | | }) |
| | | const getStatusClass = index => { |
| | | if (index <= currentStep.value) { |
| | | const status = Number(stepResponse.value[index]?.status) || 0 |
| | | const statusClasses = { |
| | | 2: 'status-pending', // 待审核 |
| | | 0: 'status-waiting', // 待处理 |
| | | 3: 'status-processing', // 处理中 |
| | | 4: 'status-completed', // 已完成 |
| | | } |
| | | return statusClasses[status] || '' |
| | | } |
| | | |
| | | return 'status-default' |
| | | } |
| | | // 关联算法 |
| | | const getTicketInfoData = async () => { |
| | | const response = await getTicketInfo() |
| | | const { dept_data, event_type, ai_type, info } = response.data.data |
| | | allAlgorithms.value = info |
| | | |
| | | types.value = Object.entries(event_type).map(([key, value]) => ({ |
| | | label: value, |
| | | value: key, |
| | | })) |
| | | departments.value = dept_data.map(item => ({ |
| | | text: item.dept_name, |
| | | value: item.id, |
| | | })) |
| | | departmentUsers.value = dept_data.reduce((acc, dept) => { |
| | | acc[dept.id] = dept.user_data || [] |
| | | return acc |
| | | }, {}) |
| | | } |
| | | // 处理人 |
| | | const availableDispatchHandlers = computed(() => { |
| | | if (!dispatchForm.value.department) return [] |
| | | const users = departmentUsers.value[dispatchForm.value.department] |
| | | if (!users) return [] |
| | | return users.map(user => ({ |
| | | text: user.name, |
| | | value: user.id, |
| | | })) |
| | | }) |
| | | |
| | | // 关联算法选择 |
| | | const handleTypeChange = typeValue => { |
| | | const matchedCategory = allAlgorithms.value.find(category => category.dict_key === typeValue) |
| | | if (!matchedCategory || !matchedCategory.algorithms || matchedCategory.algorithms.length === 0) { |
| | | // 无匹配的算法时清空 |
| | | algorithms.value = [] |
| | | return |
| | | } |
| | | algorithms.value = matchedCategory.algorithms.map(algo => ({ |
| | | label: algo.dict_value, |
| | | value: algo.dict_key, |
| | | dict_key: algo.dict_key, |
| | | dict_value: algo.dict_value, |
| | | })) |
| | | columns.value = algorithms.value.map(item => ({ |
| | | text: item.label, |
| | | value: item.value, |
| | | })) |
| | | } |
| | | // 机巢数据 |
| | | const handleNodeClick = async data => { |
| | | const droneList = await getDeviceRegion({ areaCode: userInfo.value.detail.areaCode }) |
| | | machineData.value = droneList?.data?.data |
| | | } |
| | | const tableData = ref([]) |
| | | |
| | | const fetchWorkData = async (val) => { |
| | | const params = { |
| | | current: Number(currentPage.value) || 1, |
| | | size: pageSize.value, |
| | | source: 1, |
| | | // event_name: val.keyword || val.eventNum, |
| | | ai_types: val.aiType, |
| | | way_line_job_info_id: val.wLJobInfoId, |
| | | work_types: ALL_WORK_ORDER_TYPE_OPTIONS |
| | | } |
| | | if (val.status !== 'undefined') { |
| | | params.status = val.status |
| | | } |
| | | |
| | | const res = await getList(params) |
| | | const response = res.data.data.records |
| | | |
| | | totalRecords.value = res.data.data.total || 0 |
| | | console.log(totalRecords.value,res.data.data.total,2222) |
| | | |
| | | if (response && response.length > 0) { |
| | | const item = response[0] |
| | | const matchedMachine = machineData.value.find(m => m.device_sn === item.device_sn) |
| | | const deviceNickname = matchedMachine?.nickname || '' |
| | | |
| | | currentDetail.value = { |
| | | ...item, |
| | | processingDetail: item.processing_details, |
| | | update_photo_url: item.update_photo_url, |
| | | photos: [], |
| | | aiType: item.ai_type_key_list?.join(',') || '', |
| | | status: Number(item.status) || 0, |
| | | device_names: deviceNickname ? deviceNickname : '' |
| | | } |
| | | currentStatus.value = currentDetail.value.status |
| | | handleTypeChange(currentDetail.value.work_order_type_dict_key) |
| | | |
| | | await getStepInfoData(currentDetail.value.event_num) |
| | | } |
| | | } |
| | | // 计算要显示的步骤 |
| | | const displayedSteps = computed(() => { |
| | | return allStepConfigs.value.filter(stepConfig => { |
| | | return stepResponse.value.some(stepData => Number(stepData?.status) === stepConfig.status) |
| | | const openPreview = index => { |
| | | const detail = workDetailData.value |
| | | const showUrl = getShowImg(detail.eventImageUrl) |
| | | showImagePreview({ |
| | | images: [showUrl], |
| | | startPosition: 0, |
| | | }) |
| | | }) |
| | | // 计算工单类型显示名称 |
| | | const workOrderTypeName = computed(() => { |
| | | const type = types.value.find(item => item.value === currentDetail.value.work_order_type_dict_key) |
| | | return type ? type.label : currentDetail.value.work_order_type_dict_key |
| | | }) |
| | | // 步骤条 |
| | | const calculateCurrentStep = status => { |
| | | const stepIndex = displayedSteps.value.findIndex(step => step.status === status) |
| | | return stepIndex !== -1 ? stepIndex : 0 |
| | | } |
| | | const getStepInfoData = async val => { |
| | | const res = await getStepInfo(val) |
| | | stepResponse.value = res.data.data.map(item => ({ |
| | | ...item, |
| | | status: Number(item.status) || 0, |
| | | })) |
| | | currentStep.value = calculateCurrentStep(currentStatus.value) |
| | | } |
| | | // 通过 |
| | | const approveTicket = async () => { |
| | | const data = { |
| | | id: currentDetail.value.id, |
| | | status: currentDetail.value.status, |
| | | isPass: 0, // 0 表示通过 |
| | | eventNum: currentDetail.value.event_num, |
| | | } |
| | | const file = currentDetail.value.file || null |
| | | const response = await flowEvent(data, file) |
| | | if (response.data.code === 0) { |
| | | showToast('工单已通过') |
| | | } |
| | | |
| | | const transmitData = { data: { type: 'workback', fun: 'add' } } |
| | | wx.miniProgram.switchTab({ url: `/pages/work/index?addLog=111` }) |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | } |
| | | // 不通过/不受理 |
| | | const rejectTicket = async () => { |
| | | const data = { |
| | | id: currentDetail.value.id, |
| | | status: currentDetail.value.status, |
| | | isPass: 1, |
| | | } |
| | | const response = await flowEvent(data) |
| | | if (response.data.code === 0) { |
| | | showToast('工单未通过') |
| | | } |
| | | const transmitData = { data: { type: 'workback', fun: 'add' } } |
| | | wx.miniProgram.switchTab({ url: `/pages/work/index?addLog=111` }) |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | } |
| | | const isshowDispatch = ref(false) |
| | | // 受理 |
| | | const approveAndDispatch = () => { |
| | | if (!currentDetail.value.event_name) { |
| | | showToast('请填写工单名称') |
| | | return |
| | | } |
| | | if (!currentDetail.value.ai_types) { |
| | | showToast('请选择关联算法') |
| | | return |
| | | } |
| | | if (!currentDetail.value.content) { |
| | | showToast('请填写工单内容') |
| | | return |
| | | } |
| | | isshowDispatch.value = true |
| | | } |
| | | const dispatchCancel = () => { |
| | | // 重置表单 |
| | | dispatchForm.value = { |
| | | department: '', |
| | | departmentName: '', |
| | | handler: '', |
| | | handlerName: '', |
| | | } |
| | | isshowDispatch.value = false |
| | | } |
| | | const dispatchForm = ref({ |
| | | department: '', // 存储部门ID (value) |
| | | departmentName: '', // 显示部门名称 (text) |
| | | handler: '', // 存储处理人ID (value) |
| | | handlerName: '', // 显示处理人名称 (text) |
| | | }) |
| | | |
| | | const selectedDepartment = ref([]) |
| | | const selectedProcessor = ref([]) |
| | | const showGlobalPicker = ref(false) |
| | | const currentPickerType = ref('') |
| | | |
| | | const openPicker = type => { |
| | | currentPickerType.value = type |
| | | showGlobalPicker.value = true |
| | | } |
| | | // 部门选择确认 |
| | | const onDepartmentConfirm = value => { |
| | | dispatchForm.value.department = value.selectedValues[0] |
| | | dispatchForm.value.departmentName = value.selectedOptions[0].text |
| | | showGlobalPicker.value = false |
| | | } |
| | | // 处理人选择确认 |
| | | const onProcessorConfirm = value => { |
| | | if (!dispatchForm.value.department) { |
| | | showToast('请先选择部门') |
| | | return |
| | | } |
| | | dispatchForm.value.handler = value.selectedValues[0] |
| | | dispatchForm.value.handlerName = value.selectedOptions[0].text |
| | | showGlobalPicker.value = false |
| | | } |
| | | |
| | | // 取消选择 |
| | | const onPickerCancel = () => { |
| | | showGlobalPicker.value = false |
| | | } |
| | | // 派发工单 |
| | | const submitDispatch = async () => { |
| | | if (!dispatchForm.value.department) { |
| | | showToast('请选择部门') |
| | | return |
| | | } |
| | | |
| | | if (!dispatchForm.value.handler) { |
| | | showToast('请选择处理人') |
| | | return |
| | | } |
| | | |
| | | try { |
| | | const data = { |
| | | id: currentDetail.value.id, |
| | | status: currentDetail.value.status, |
| | | isPass: 0, |
| | | eventName: currentDetail.value.event_name, |
| | | eventNum: currentDetail.value.event_num, |
| | | workOrderTypeDictKey: currentDetail.value.work_order_type_dict_key, |
| | | content: currentDetail.value.content, |
| | | createDept: dispatchForm.value.department, |
| | | updateUser: dispatchForm.value.handler, |
| | | aiType: currentDetail.value.aiType, |
| | | } |
| | | const file = currentDetail.value.file || null |
| | | const response = await flowEvent(data, file) |
| | | if (response.data.code === 0) { |
| | | showToast('工单派发成功') |
| | | const transmitData = { data: { type: 'workback', fun: 'add' } } |
| | | wx.miniProgram.switchTab({ url: `/pages/work/index?addLog=111` }) |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | isshowDispatch.value = false |
| | | } |
| | | } catch (error) { |
| | | showToast('工单派发失败') |
| | | } |
| | | } |
| | | |
| | | // 完成工单 |
| | | const completeTicket = async () => { |
| | | if (!currentDetail.value.processingDetail) { |
| | | showToast('请先填写事件处理详情') |
| | | return |
| | | } |
| | | if (!currentDetail.value.photos || currentDetail.value.photos.length === 0) { |
| | | showToast('请选择上传图片') |
| | | return |
| | | } |
| | | const data = { |
| | | id: currentDetail.value.id, |
| | | status: currentDetail.value.status, |
| | | processingDetails: currentDetail.value.processingDetail, |
| | | eventNum: currentDetail.value.event_num, |
| | | } |
| | | const file = currentDetail.value.photos?.[0] || null |
| | | const response = await flowEvent(data, file) |
| | | if (response.data.code === 0) { |
| | | showToast('工单已完成') |
| | | } |
| | | const transmitData = { data: { type: 'workback', fun: 'add' } } |
| | | wx.miniProgram.switchTab({ url: `/pages/work/index?addLog=111` }) |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | } |
| | | // 取消 |
| | | const cancellation = () => { |
| | | const transmitData = { data: { type: 'workback', fun: 'add' } } |
| | | wx.miniProgram.switchTab({ url: `/pages/work/index?addLog=111` }) |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | } |
| | | |
| | | // 下拉选择 |
| | | const openselect = () => { |
| | | showPicker.value = true |
| | | } |
| | | // 选择器确认方法 |
| | | const onConfirm = val => { |
| | | currentDetail.value.ai_types = val.selectedOptions.map(option => option.text).join(',') |
| | | currentDetail.value.aiType = val.selectedValues.join(',') |
| | | showPicker.value = false |
| | | } |
| | | // 选择器取消方法 |
| | | const onCancel = () => { |
| | | showPicker.value = false |
| | | } |
| | | // 文件上传前的校验 |
| | | const beforeRead = file => { |
| | | const ext = file.name.split('.').pop().toLowerCase() |
| | | const allowedExts = ['jpg', 'jpeg', 'png'] |
| | | if (!allowedExts.includes(ext)) { |
| | | showToast('请上传 jpg/jpeg/png 格式图片') |
| | | return false |
| | | } |
| | | return true |
| | | } |
| | | // 文件大小超过限制时的回调 |
| | | const onOversize = file => { |
| | | showToast('文件大小不能超过 5MB') |
| | | } |
| | | // 文件读取完成后的回调 |
| | | const afterRead = file => { |
| | | currentDetail.value.photos = [file.file] |
| | | } |
| | | // 处理图片删除 |
| | | const handleDelete = () => { |
| | | currentDetail.value.photos = [] |
| | | } |
| | | // 更新当前显示的工单详情 |
| | | const updateCurrentDetail = () => { |
| | | currentDetail.value = tableData.value[currentIndex.value] |
| | | if (currentDetail.value.status !== undefined) { |
| | | currentDetail.value.status = Number(currentDetail.value.status) || 0 |
| | | } |
| | | currentStatus.value = currentDetail.value.status |
| | | getStepInfoData(currentDetail.value.event_num) |
| | | } |
| | | // 上一页 |
| | | const leftClick = () => { |
| | | if (currentPage.value > 1) { |
| | | currentPage.value-- |
| | | fetchWorkData(route.query) |
| | | } else { |
| | | showToast('已经是第一页') |
| | | } |
| | | } |
| | | |
| | | // 下一页 |
| | | const rightClick = () => { |
| | | if (currentPage.value < totalRecords.value) { |
| | | currentPage.value++ |
| | | fetchWorkData(route.query) |
| | | } else { |
| | | showToast('已经是最后一页') |
| | | } |
| | | previewIndex.value = index |
| | | previewShow.value = true |
| | | } |
| | | // 跳转地图 |
| | | const jumpMap = item => { |
| | |
| | | wx.miniProgram.postMessage(transmitData) |
| | | uni.postMessage(transmitData) |
| | | } |
| | | // 预览图片 |
| | | const previewShow = ref(false) |
| | | const previewIndex = ref(0) |
| | | |
| | | const getImageList = computed(() => { |
| | | const imageArr = [] |
| | | const detail = currentDetail.value |
| | | |
| | | if (detail.photo_url) { |
| | | const smallUrl = getSmallImg(detail.photo_url) |
| | | imageArr.push(smallUrl) |
| | | } |
| | | |
| | | if (detail.update_photo_url) { |
| | | const smallUrl = getSmallImg(detail.update_photo_url) |
| | | imageArr.push(smallUrl) |
| | | } |
| | | |
| | | return imageArr |
| | | }) |
| | | |
| | | const openPreview = index => { |
| | | const detail = currentDetail.value |
| | | const showUrl = getShowImg(detail.photo_url || detail.update_photo_url) |
| | | showImagePreview({ |
| | | images: [showUrl], |
| | | startPosition: 0, |
| | | }) |
| | | previewIndex.value = index |
| | | previewShow.value = true |
| | | } |
| | | onMounted(async () => { |
| | | handleNodeClick() |
| | | eventNum.value = route.query.eventNum || '' |
| | | await getTicketInfoData() |
| | | currentPage.value = route.query.current |
| | | await fetchWorkData(route.query) |
| | | await getStepInfoData(eventNum.value) |
| | | currentIndex.value = tableData.value.findIndex(item => item.event_num === currentDetail.value.event_num) |
| | | |
| | | workDetailData.value = JSON.parse(route.query.workDetailData) |
| | | console.log('route.query',workDetailData.value); |
| | | }) |
| | | </script> |
| | | <style lang="scss" scoped> |
| | | .workDetailContainer { |
| | | position: relative; |
| | | |
| | | .required-mark { |
| | | color: #ff4d4f; // 红色 |
| | | margin-left: 4px; |
| | | } |
| | | |
| | | padding-top: 44px; |
| | | .detailTop { |
| | | .image-container { |
| | | position: relative; |
| | |
| | | display: block; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .detailTitle { |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | width: 100%; |
| | | padding: 5px; |
| | | height: 30px; |
| | | background: rgba(7, 7, 7, 0.4); |
| | | } |
| | | |
| | | .titleText { |
| | | display: flex; |
| | | width: 100%; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: #ffffff; |
| | | |
| | | .itemStatus { |
| | | display: flex; |
| | | align-items: center; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: #ffffff; |
| | | |
| | | span { |
| | | display: inline-block; |
| | | width: 10px; |
| | | height: 10px; |
| | | border-radius: 50%; |
| | | margin-right: 7px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .timeNavigation { |
| | | display: flex; |
| | | align-items: center; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 13px; |
| | | color: #ffffff; |
| | | |
| | | img { |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-left: 5px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .stepBox { |
| | | padding: 0 12px; |
| | | } |
| | | |
| | | .stepContainer { |
| | | margin-top: 10px; |
| | | display: flex; |
| | | |
| | | border-radius: 5px; |
| | | padding: 10px; |
| | | background: #fff; |
| | | |
| | | :deep() { |
| | | .van-step__circle-container { |
| | | font-size: 20px; |
| | | } |
| | | |
| | | .van-step__circle { |
| | | width: 15px; |
| | | height: 15px; |
| | | } |
| | | |
| | | .van-step--vertical { |
| | | padding: 20px 20px 20px 0; |
| | | } |
| | | |
| | | .van-step__circle-container, |
| | | .van-step__line { |
| | | top: 50%; |
| | | } |
| | | |
| | | .van-step--vertical:not(:last-child):after { |
| | | border-bottom-width: 0; |
| | | } |
| | | } |
| | | |
| | | .horizontal-step { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 12px; |
| | | |
| | | .step-title { |
| | | min-width: 60px; |
| | | background: #e8e8e8; |
| | | padding: 5px; |
| | | border-radius: 5px; |
| | | text-align: center; |
| | | |
| | | &.status-pending { |
| | | background-color: #ffebeb; // 待审核 |
| | | color: #ff1414; |
| | | } |
| | | |
| | | &.status-waiting { |
| | | background-color: #fff1e6; // 待处理 |
| | | color: #ff7411; |
| | | } |
| | | |
| | | &.status-processing { |
| | | background-color: #fff6d8; // 处理中 |
| | | color: #ffbb00; |
| | | } |
| | | |
| | | &.status-completed { |
| | | background-color: #e5fcff; // 已完成 |
| | | color: #0291a1; |
| | | } |
| | | |
| | | &.status-default { |
| | | background-color: #e8e8e8; // 默认背景色 |
| | | color: #191919; |
| | | } |
| | | } |
| | | |
| | | .step-desc { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | gap: 28px; |
| | | color: #222324; |
| | | font-size: 14px; |
| | | } |
| | | |
| | | .step-desc span:first-child { |
| | | width: 60px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | span:last-child { |
| | | margin-left: auto; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | } |
| | | } |
| | | } |
| | | |
| | | :deep(.van-step__circle) { |
| | | width: 8px; |
| | | height: 8px; |
| | | } |
| | | |
| | | /* 悬浮左右按钮样式 */ |
| | | .floatingBtn { |
| | | position: fixed; |
| | | top: 50%; |
| | | width: 46px; |
| | | height: 46px; |
| | | z-index: 9; |
| | | transform: translateY(-50%); |
| | | background: rgba(17, 17, 17, 0.28); |
| | | backdrop-filter: blur(3px); |
| | | border-radius: 50%; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .leftFloatingBtn { |
| | | left: 5px; |
| | | /* 左边距离 */ |
| | | } |
| | | |
| | | .rightFloatingBtn { |
| | | right: 5px; |
| | | /* 右边距离 */ |
| | | } |
| | | |
| | | .floatingBtn img { |
| | | width: 10px; |
| | | height: 18px; |
| | | } |
| | | |
| | | .paginationInfo { |
| | | position: absolute; |
| | | left: 50%; |
| | | transform: translateX(-50%); |
| | | font-size: 14px; |
| | | color: #666; |
| | | background: rgba(255, 255, 255, 0.9); |
| | | padding: 4px 12px; |
| | | border-radius: 12px; |
| | | } |
| | | |
| | | .actionButton { |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | width: 100%; |
| | | height: 61px; |
| | | background: #ffffff; |
| | | border-radius: 6px 6px 6px 6px; |
| | | padding: 0 18px; |
| | | |
| | | .btngroups { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .van-button { |
| | | padding: 9px 20px; |
| | | height: 38px; |
| | | width: 156px; |
| | | |
| | | &:last-child { |
| | | margin-left: 12px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .back-btn :deep(.van-button) { |
| | | width: 100%; |
| | | width: 324px; |
| | | margin-left: 0px !important; |
| | | } |
| | | |
| | | .leftBtn { |
| | | width: 27px; |
| | | height: 27px; |
| | | cursor: pointer; |
| | | |
| | | img { |
| | | width: 27px; |
| | | height: 27px; |
| | | } |
| | | } |
| | | |
| | | .disableds { |
| | | background: #999 !important; |
| | | cursor: not-allowed !important; |
| | | pointer-events: none; |
| | | opacity: 0.3 !important; |
| | | } |
| | | } |
| | | |
| | |
| | | font-weight: bold; |
| | | font-size: 16px; |
| | | color: #222324; |
| | | // margin-bottom: 16px; |
| | | } |
| | | |
| | | .workOrderContainer { |
| | | .orderRow { |
| | | // margin-bottom: 10px; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | |
| | | } |
| | | |
| | | .rowAddress { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #1d6fe9; |
| | | white-space: nowrap; |
| | | /* 禁止换行 */ |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | // max-width: 74%; |
| | | text-decoration: underline; |
| | | text-align: right; |
| | | padding-top: 1px; |
| | | padding-left: 5px; |
| | | padding-right: 2px; |
| | | } |
| | | |
| | | .selectTrigger { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #222324; |
| | | } |
| | | |
| | | .pointing { |
| | | width: 8px; |
| | | height: 12px; |
| | | } |
| | | |
| | | .downpointing { |
| | | width: 12px; |
| | | height: 7px; |
| | | } |
| | | |
| | | :deep(.custom-field) { |
| | | padding: 0 !important; |
| | | padding-left: 10px !important; |
| | |
| | | padding: 0 !important; |
| | | } |
| | | |
| | | .titketName { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .upload-link { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 12px; |
| | | color: #1d6fe9; |
| | | cursor: pointer; |
| | | text-decoration: underline; |
| | | } |
| | | } |
| | | </style> |