| src/api/tickets/orderLog.js | ●●●●● patch | view | raw | blame | history | |
| src/views/tickets/component/AddEditDetails.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/tickets/component/SearchBox.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/tickets/orderLog-copy1.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/tickets/orderLog.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/tickets/orderLog1.vue | ●●●●● patch | view | raw | blame | history | |
| src/views/tickets/orderLogCopy.vue | ●●●●● patch | view | raw | blame | history |
src/api/tickets/orderLog.js
@@ -68,6 +68,14 @@ }); }; // 智飞工单 统计数替换上面接口 // export const jobStatusNum = (data) => { // return request({ // url: '/drone-device-core/wayline/orderLog/jobStatusNum', // method: 'post', // data // }); // }; // 草稿转发布 export const userPublish = (id) => { src/views/tickets/component/AddEditDetails.vue
New file @@ -0,0 +1,548 @@ <template> <el-dialog class="ztzf-dialog-mange" modal-class="add-edit-details" v-model="isShowAddEditDetails" :title="txtTitle" width="70%" :close-on-click-modal="false" :destroy-on-close="true" @close="cancel" :before-close="handleBeforeClose" > <div class="add-edit-details"> <div class="process" v-if="txtTitle !=='新建工单'"> <div class="order_title">{{ formParams.name }}</div> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles"> <div class="step-title" v-for="(record, index) in formParams.record_list" :class="{ active: record.user_id >= 0 }" :key="index"> {{ record.status_str }} </div> </div> <!-- Element Steps 组件 --> <el-steps :active="formParams.active" align-center class="custom-steps"> <el-step v-for="(record, index) in formParams.record_list" :key="index"> <template #description> <span class="step-description" style="position: relative; display: inline-block"> {{ record.user_name }} </span> <span class="step-timer"> {{ record.interval_time_str }} </span> <div class="step-description"> {{ record.create_time_str }} </div> </template> </el-step> </el-steps> </div> </div> <el-form ref="orderFormRef" :model="formParams" :rules="rules" label-width="100px"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="formParams.name" placeholder="请输入工单名称" maxlength="100" show-word-limit :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="formParams.file_id" placeholder="请选择航线" filterable @change="getFlyingNest" :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"> <el-option v-for="item in lineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number v-model="formParams.rth_altitude" :min="50" :max="500" :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="formParams.device_sns" placeholder="请选择机巢" multiple :disabled="(txtTitle === '工单审核' || txtTitle === '工单详情')"> <el-option v-for="item in drone_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="formParams.ai_types" placeholder="请选择关联算法" multiple :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"> <el-option v-for="item in aiTypeList" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12" v-if="txtTitle === '新建工单'"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="formParams.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"></el-input> </el-form-item> </el-col> <el-col :span="12" prop="statusStr" v-else> <el-form-item label="当前状态"> {{ getOrderStatus(formParams.status) }} </el-form-item> </el-col> <el-col :span="7"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'" v-model="formParams.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" /> </el-form-item> </el-col> <el-col :span="2"> <el-select v-model="formParams.rep_fre_type" placeholder="请选择频次" :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"> <el-option v-for="item in weeks" :key="item" :label="item" :value="item" /> </el-select> </el-col> <el-col :span="3"> <el-time-picker :style="{ width: pxToRem(140) }" v-model="formParams.deal_time" :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59'}" /> </el-col> </el-row> <el-row :gutter="20" v-if="txtTitle === '工单审核' || txtTitle === '工单详情' || txtTitle === '工单编辑'"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="formParams.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit :disabled="txtTitle === '工单审核' || txtTitle === '工单详情'"></el-input> </el-form-item> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="isShowAddEditDetails" ref="mapContainerRef"></map-container> </el-col> </el-row> </el-form> </div> <template #footer v-if="txtTitle === '新建工单'"> <div class="dialog-footer"> <el-button type="danger" @click="submitForm(1)" icon="el-icon-position">发起</el-button> <!-- <el-button type="primary" @click="submitForm(0)" icon="el-icon-document-add">存草稿</el-button>--> <el-button @click="cancel" icon="el-icon-circle-close">取消</el-button> </div> </template> <template #footer v-if="txtTitle === '工单审核'"> <div class="dialog-footer"> <el-button type="primary" v-if="formParams.status == 1 && permission.orderLogpass" @click="orderPass(formParams.id)">通过 </el-button> <el-button type="danger" v-if="formParams.status == 1 && permission.rejection_btn" @click="orderReject(formParams.id)">驳回 </el-button> <el-button @click="cancel" icon="el-icon-circle-close">取消</el-button> </div> </template> <template #footer v-if="txtTitle === '工单编辑'"> <div class="dialog-footer"> <el-button type="danger" v-if="formParams.status == 0 || (formParams.status == 2 && userInfo.user_id === formParams.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <el-button @click="cancel" icon="el-icon-circle-close">取消</el-button> </div> </template> </el-dialog> </template> <script setup> import { pxToRem, pxToRemNum } from '@/utils/rem' import { ref, shallowRef, onMounted, computed } from 'vue'; import { newGetWorkspacesPage } from '@/api/resource/wayline' import { getDictionaryByCode } from '@/api/system/dictbiz' import { getFlyingNestBy } from '@/api/device/device' import { saveUpdateOrderLog, orderLogDetails, orderLogPass, orderLogReject, orderLogRecall, getWaylineMaxTerrainHeight } from '@/api/tickets/orderLog'; import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' // 导入中文语言包 import _ from 'lodash' import { ElMessage, ElMessageBox } from 'element-plus'; import weekday from 'dayjs/plugin/weekday' dayjs.extend(weekday) dayjs.locale('zh-cn') import { useStore } from 'vuex' const emit = defineEmits(['refresh']); const store = useStore() const userInfo = computed(() => store.state.user.userInfo) const permission = computed(() => store.state.user.permission); // safeHeight 计算属性 const safeHeight = computed(() => { const height = store.state.common.safeHeight return Number(height) || 0 // 确保是数字类型 }) // positionsArr 计算属性 const positionsArr = computed(() => { const arr = store.state.common.positionsArr return arr || [] // 确保是数组类型 }) const txtTitle = inject('txtTitle') const rowObj = inject('rowObj') const isShowAddEditDetails = defineModel('show') const mapContainerRef = ref(null) const orderFormRef = ref(null) const formParams = ref({}) const rules = ref({ name: [ { required: true, message: '请输入工单名称', trigger: 'blur' }, { max: 100, message: '工单名称不能超过100个字', trigger: 'blur' } ], file_id: [{ required: true, message: '需要选择航线', trigger: 'change' }], device_sns: [{ required: true, message: '请选择机巢', trigger: 'change' }], content: [ { required: true, message: '请输入工单内容', trigger: 'blur' }, { max: 255, message: '工单内容不能超过255个字', trigger: 'blur' } ], date_range: [{ required: true, message: '请选择时间不能为空', trigger: 'blur' }], rep_fre_type: [{ required: true, message: '请选择周期', trigger: 'blur' }], deal_time: [{ required: true, message: '请选择执行时间', trigger: 'blur' }] }) //周期 const weeks = ref(['每天', '周一', '周二', '周三', '周四', '周五', '周六', '周末', '周天', '工作日']) let lineList = inject('lineList') let aiTypeList = inject('aiTypeList') // 获取航线 // let lineList = ref([]) // function getLines() { // const formData = { // name: '', // waylineType: undefined, // current: 1, // size: 99999999, // descs: 'update_time' // } // newGetWorkspacesPage(formData).then(res => { // lineList.value = res.data.data.records // }) // } function getOrderStatus (status) { const statusTextMap = { 0: '草稿', 1: '待审核', 2: '已驳回', 3: '已通过' } return statusTextMap[status] || '未知状态' } function disabledDate (time) { return time.getTime() < Date.now() - 8.64e7 // 86400000 = 24 * 60 * 60 * 1000 } // 关联机巢 const drone_sns = ref([]) function getFlyingNest (waylineId) { console.log('选择航线', lineList.value) // 选中航线对象 initMapLine({}, polygon => { const currentLine = lineList.value.find(item => item.wayline_id === waylineId) const params = { type: ['2', '4', '5'].includes(currentLine.wayline_type) ? 2 : 0, wayline_id: waylineId, polygon } // 重置 formParams.value.device_sns = [] drone_sns.value = [] getFlyingNestBy(params).then(res => { drone_sns.value = res.data.data initMapLine() }) }) } // 获取算法 // let aiTypeList = ref([]) // function aitypeList() { // getDictionaryByCode('SF').then(res => { // aiTypeList.value = res.data.data['SF'] // }) // } // initMapLine 方法 const initMapLine = async (infos = {}, cb = () => {}) => { const currentLine = lineList.value.find(item => item.wayline_id == formParams.value.file_id) if (!currentLine) return await nextTick() if (mapContainerRef.value?.initAddEntity) { mapContainerRef.value.initAddEntity('polyline', { url: `${import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key}?_t=${new Date().getTime()}`, type: currentLine.wayline_type, cb, infos }) } } function formatDate (date) { if (!date) return undefined const d = new Date(date) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String( d.getDate() ).padStart(2, '0')} 00:00:00` } async function returnHeight() { let checkedList = drone_sns.value.filter(item => formParams.value.device_sns.includes(item.device_sn)); const maxItem = checkedList.reduce((max, item) => { return item.drone_height > max.drone_height ? item : max; }); // 返航绝对高度 let backHeight = formParams.value.rth_altitude // + maxItem.drone_height console.log(backHeight,'backHeight') // 安全起飞高度 let positions = positionsArr.value positions.unshift({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height }) positions.push({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height}) let resultHeight = 0 await getWaylineMaxTerrainHeight(positions).then(res => { resultHeight = safeHeight.value > (res.data.data + 30) ? safeHeight.value : (res.data.data + 30) }) let valueHeight = _.round(backHeight - resultHeight, 2) return _.round(backHeight - resultHeight, 2) } function submitForm(status) { orderFormRef.value?.validate(async (valid, fields) => { if (valid) { console.log('表单验证通过') formParams.value.begin_time = formatDate(formParams.value.date_range[0]) formParams.value.end_time = formatDate(formParams.value.date_range[1]) // 如果选中日期包含当前天,那么选中的时间点不能小于当前时间 if (formParams.value.deal_time) { const selectedDate = dayjs(formParams.value.date_range[0]).format('YYYY-MM-DD') const selectedTime = dayjs(selectedDate + ' ' + formParams.value.deal_time).toDate() const now = new Date() if (selectedDate === dayjs().format('YYYY-MM-DD') && selectedTime < now) { return ElMessage({message: '任务时间不能小于当前时间', type: 'warning'}) } } let checkedList = drone_sns.value.filter(item => formParams.value.device_sns.includes(item.device_sn)); const maxItem = checkedList.reduce((max, item) => { return item.drone_height > max.drone_height ? item : max; }); // 返航绝对高度 let backHeight = formParams.value.rth_altitude // + maxItem.drone_height console.log(backHeight,'backHeight') // 安全起飞高度 let positions = positionsArr.value positions.unshift({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height }) positions.push({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height}) let resultHeight = 0 await getWaylineMaxTerrainHeight(positions).then(res => { resultHeight = safeHeight.value > (res.data.data + 30) ? safeHeight.value : (res.data.data + 30) }) let valueHeight = _.round(backHeight - resultHeight, 2) if (valueHeight < 0) { await ElMessageBox.confirm(`当前返航高度存在安全隐患,建议调整为${resultHeight}米以上后进行发布`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) } const submitData = { ...formParams.value, status: status, ai_types: formParams.value.ai_types?.length ? formParams.value.ai_types : [] } await saveUpdateOrderLog(submitData) if (txtTitle === '新建工单') { ElMessage({message: '工单创建成功', type: 'success'}) } else { ElMessage({message: '工单发布成功', type: 'success'}) } cancel() } else { console.log('表单验证失败', fields) } }) } // 审核通过 function orderPass(id) { orderLogPass(id).then(res => { ElMessage.success('审核通过') cancel() }) } // 驳回 const orderReject = async (id) => { try { const { value } = await ElMessageBox.prompt('', '驳回原因', { confirmButtonText: '确定', cancelButtonText: '取消', inputPlaceholder: '请输入驳回原因', inputValidator: (value) => { if (!value) { return '驳回原因不能为空' } } }) // 用户输入了内容并点击确定 await orderLogReject(id, value) ElMessage.success('驳回成功') cancel() } catch (error) { // 用户点击取消或关闭弹窗 if (error !== 'cancel' && error !== 'close') { ElMessage.error('驳回失败') console.error('驳回错误:', error) } } } const initMapPosition = async () => { await nextTick() if (mapContainerRef.value?.initAddEntity) { mapContainerRef.value.initAddEntity('initPosition') } } // 监听txtTitle watch(isShowAddEditDetails, async (newVal, oldVal) => { console.log('txtTitle 发生变化:', newVal, oldVal) formParams.value = {rth_altitude:120} if (txtTitle.value === '工单审核' || txtTitle.value === '工单详情' || txtTitle.value === '工单编辑') { const response = await orderLogDetails(rowObj.value.id) const data = response.data.data formParams.value = { ...data } console.log(formParams.value, '898789') drone_sns.value = data.device_list initMapLine(data.device_map_infos) } else { initMapPosition() } }, { immediate: true // 立即执行一次 }) function handleBeforeClose() { cancel() } function cancel() { formParams.value = {} isShowAddEditDetails.value = false emit('refresh'); } // onMounted(async () => { // aitypeList() // getLines() // }); </script> <style lang="scss" scoped> .add-edit-details { .safe-height { :deep(.el-form-item__label) { width: 120px !important; } :deep(.el-input-number) { width: 180px !important; } } .week_pc_one { flex: 1; .week-r { margin-right: 4px; } &> ::v-deep(div) { width: 100% !important; } } .process { .order_title { height: 30px; line-height: 30px; text-align: center; font-size: 20px; font-weight: bold; color: #333; } .custom-steps-container { width: 100%; margin: 7px 0; } .steps-titles { display: flex; justify-content: space-between; margin-bottom: 16px; position: relative; } .step-title { text-align: center; flex: 1; font-size: 16px; color: #999; position: relative; padding-bottom: 10px; } .step-title.active { color: #409eff; font-weight: bold; } .custom-steps { margin-top: -20px; /* 向上移动与标题重叠 */ } .el-step__head { margin-top: 0; } .el-step__description { margin-top: 8px; padding: 0 20px; } .step-description { font-size: 14px; color: #666; line-height: 2.2; } } } </style> src/views/tickets/component/SearchBox.vue
New file @@ -0,0 +1,305 @@ <template> <div class="search-box-test"> <el-form :model="searchForm" inline> <div class="search-first"> <el-form-item label="关键字:"> <el-input v-model="searchForm.key_word" placeholder="编号/名称/内容/姓名" clearable /> </el-form-item> <el-form-item label="选择日期:" :style="{ width: pxToRem(460) }"> <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" @change="handleSearch" /> </el-form-item> <el-form-item label="关联航线:"> <el-select :teleported="false" v-model="searchForm.file_id" placeholder="请选择关联航线" clearable @change="handleSearch" > <el-option v-for="item in lineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> <el-form-item label="关联算法:"> <el-select :teleported="false" v-model="searchForm.ai_typesValue" placeholder="请选择关联算法" clearable @change="handleSearch"> <el-option v-for="item in aiTypeList" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> <el-form-item label="工单状态:"> <el-select :teleported="false" :disabled="activeTab === 'WAIT_AUDIT' || activeTab === 'REJECTED' || activeTab === 'PASS'" v-model="searchForm.status" placeholder="请选择单状态" @change="handleSearch" clearable> <el-option v-for="item in orderStatus" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> <el-form-item label="工单周期:" :style="{ width: pxToRem(460) }"> <el-date-picker v-model="cycleDateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" @change="handleSearch"/> </el-form-item> <el-form-item label="选择频次:"> <el-select v-model="searchForm.rep_fre_type" placeholder="请选择频次" clearable @change="handleSearch"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </el-form-item> <el-form-item label="执行时间:"> <el-time-picker @change="handleSearch" v-model="searchForm.deal_time" placeholder="请选择执行时间" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59'}" /> </el-form-item> <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button> <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button> </div> </el-form> </div> </template> <script setup> import { pxToRem } from '@/utils/rem'; import { calculateDefaultRange } from '@/utils/util' import { newGetWorkspacesPage } from '@/api/resource/wayline' import { getDictionaryByCode } from '@/api/system/dictbiz' import dayjs from 'dayjs'; // 选择日期 const dateRange = ref('') let datePickerDefaultVal = calculateDefaultRange() // 选择周期 const cycleDateRange = ref('') const searchForm = reactive({ ai_types: [], ai_typesValue: '', deal_time: "", file_id: "", key_word: "", status: '', rep_fre_type: '', }) const emit = defineEmits(['search']); const rowObj = inject('rowObj') const activeTab = inject('activeTab') const lineList = inject('lineList') const aiTypeList = inject('aiTypeList') watch(activeTab, (newTab, oldTab) => { if(newTab === 'WAIT_AUDIT') { searchForm.status = '1' } if(newTab === 'REJECTED') { searchForm.status = '2' } if(newTab === 'PASS') { searchForm.status = '3' } }, { immediate: true }) // 周期 const cycles = ref(['每天', '周一', '周二', '周三', '周四', '周五', '周六', '周末', '周天', '工作日']) // 工单状态 const orderStatus = ref([ { label: '待审核', value: '1' }, { label: '已驳回', value: '2' }, { label: '已通过', value: '3' } ]) const timeFormat = 'YYYY-MM-DD HH:mm:ss'; // 搜索 const handleSearch = () => { if (!dateRange.value) { dateRange.value = []; } if (!cycleDateRange.value) { cycleDateRange.value = []; } // if (dateRange.value && dateRange.value.length) { // checked.value = ''; // searchForm.date_enum = ''; // } // 提交至store let params = { ...searchForm, create_start_date: dateRange.value.length ? dayjs(dateRange?.value[0]).startOf('day').format(timeFormat) : null, create_end_date: dateRange?.value.length ? dayjs(dateRange?.value[1]).endOf('day').format(timeFormat) : null, start_date: cycleDateRange?.value.length ? dayjs(cycleDateRange?.value[0]).startOf('day').format(timeFormat) : null, end_date: cycleDateRange?.value.length ? dayjs(cycleDateRange?.value[1]).startOf('day').format(timeFormat) : null, }; emit('search', params); }; function handleReset () { searchForm.ai_types = [] searchForm.ai_typesValue = '' searchForm.deal_time = "" searchForm.file_id = "" searchForm.key_word = "" searchForm.status = '' searchForm.rep_fre_type = '' searchForm.create_start_date = null searchForm.create_end_date = null searchForm.start_date = null searchForm.emd_date = null emit('search', searchForm); } // onMounted(() => { // aitypeList() // getLines() // }); </script> <style lang="scss" scoped> // .taskAlgorithm { // :deep() { // .task-algorithm > div { // width: 233px !important; // } // } // } .search-box-test { margin-bottom: 16px; transition: all 0.3s; .search-first { position: relative; display: flex; flex-wrap: wrap; align-items: center; gap: 20px 15px; // 设置行间距和列间距 .more { cursor: pointer; width: 32px; height: 32px; color: #1c5cff; line-height: 32px; font-size: 16px; margin: 0 28px; } .search-btn { // position: absolute; // right: 0; display: flex; justify-content: space-between; cursor: pointer; font-size: 14px; .btn { width: 80px; height: 32px; border-radius: 4px 4px 4px 4px; line-height: 32px; display: flex; justify-content: center; align-items: center; // margin-left: px; img { width: 14px; height: 14px; } } .search { background: #409eff; color: #fff; margin-right: 10px; } .clear { border: 1px solid #d2d1d1; color: #676767; margin-right: 10px; } .add { background: #ff9243; color: #fff; } } .time-card { text-align: center; background: #ffffff; border-radius: 4px 0px 0px 4px; border: 1px solid #e5e5e5; font-family: Source Han Sans CN, Source Han Sans CN; font-weight: 400; font-size: 14px; color: #7c8091; display: flex; height: 32px; .card-item { width: 60px; height: 100%; line-height: 32px; cursor: pointer; } .active { background: #ffffff; border-radius: 0px 0px 0px 0px; border: 1px solid #1c5cff; color: #1441ff; } } } :deep(.el-form) { :deep(.el-input__wrapper.is-disabled) { box-shadow: 0 0 0 1px #026ad6; } .el-form-item__label { width: 80px; } .el-form-item { margin-bottom: 0; width: 280px; margin-right: 20px; .el-form-item__label { color: #363636; line-height: 32px; } } } } </style> src/views/tickets/orderLog-copy1.vue
File was deleted src/views/tickets/orderLog.vue
@@ -1,585 +1,97 @@ <!-- 任务统计表格 --> <template> <basic-container> <div class="order-log-update"> <el-tabs v-model="activeTab" @tab-click="handleTabChange"> <el-tab-pane v-for="tab in filteredTabs" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name"> <basic-main-content> <!-- 查询条件筛选栏 --> <div class="ztzf-form-search"> <el-form :model="filters" inline> <el-row :gutter="24"> <el-col :span="4"> <el-form-item label="关键字:"> <el-input v-model="filters.key_word" placeholder="编号/名称/内容/姓名" clearable @keyup.enter="handleSearch" /> </el-form-item> <!-- <div class="search-bar-box-item">--> <!-- <el-select placeholder="请选择所属单位" v-model="filters.create_dept" clearable>--> <!-- <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" />--> <!-- </el-select>--> <!-- </div>--> </el-col> <el-col :span="8"> <el-form-item label="选择日期:"> <el-date-picker @change="handleSearch" v-model="filters.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="关联航线:"> <el-select @change="handleSearch" v-model="filters.file_id" placeholder="请选择关联航线" filterable clearable> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="关联算法:"> <el-select @change="handleSearch" v-model="filters.ai_types" placeholder="关联算法" clearable> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> <!-- <div class="search-bar-box-item"> <el-select v-model="filters.type" placeholder="请选择工单类型" clearable> <el-option v-for="item in types" :key="item.dictValue" :label="item.dictValue" :value="item.dictKey" /> </el-select> </div> --> </el-col> <el-col :span="4"> <el-form-item label="工单状态:"> <el-select @change="handleSearch" v-model="filters.status" placeholder="请选择工单状态" clearable :disabled="activeTab !== 'all'"> <el-option v-for="item in statuses" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="8"> <el-form-item label="工单周期:"> <el-date-picker @change="handleSearch" v-model="filters.cycleDateRange" type="daterange" range-separator="至" start-placeholder="工单周期开始日期" end-placeholder="工单周期结束日期" :default-value="datePickerDefaultVal" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="选择频次:"> <el-select @change="handleSearch" v-model="filters.rep_fre_type" placeholder="请选择频次" clearable> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="执行时间:"> <el-time-picker @change="handleSearch" v-model="filters.deal_time" placeholder="请选择执行时间" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </el-form-item> </el-col> <el-col :span="4"> <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索 </el-button> <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button> </el-col> </el-row> </el-form> </div> <!-- 表格部分 --> <avue-crud class="ztzf-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page" ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange" @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name"> <template #job_info_num="{ row }"> <el-tooltip-copy :content="row.job_info_num" :showCopyText="true" textAlign="left"> {{ row.job_info_num }} </el-tooltip-copy> </template> <template #name="{ row }"> <el-tooltip-copy :content="row.name" :showCopyText="true" textAlign="left"> {{ row.name }} </el-tooltip-copy> </template> <template #menu-left> <el-button v-if="hasAddBtnPermission() && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus" @click="handleAdd">新建工单 </el-button> <el-button type="success" plain icon="el-icon-download" @click="exportData">导出 </el-button> </template> <template #menu="{ row }"> <div class="menu-custom-box"> <template v-if="row.status == 1"> <el-button v-if="hasPaddingBtnPermission()" type="text" icon="el-icon-view" @click="handleCheckDetail(row)">审核 </el-button> </template> <!-- v-if=" (userInfo.user_id == row.create_user || hasRecallPaddingBtnPermission()) && row.status == 1 " --> <template v-if="row.status == 1"> <el-tab-pane v-for="tab in tabsList" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name"> <SearchBox @search="searchClick"></SearchBox> <el-button v-if="permission?.order_log_add && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus" @click="handleAddOrder">新建工单 </el-button> <el-button type="success" plain icon="el-icon-download" @click="exportData">导出 </el-button> <div class="task-table"> <el-table border :data="orderListTable" class="custom-header"> <el-table-column label="序号" type="index" width="60"> <template #default="{ $index }"> {{ ($index + 1 + (orderListParams.current - 1) * orderListParams.size).toString().padStart(2, '0') }} </template> </el-table-column> <el-table-column prop="job_info_num" label="工单编号" width="150" show-overflow-tooltip> <template #default="scope"> <el-tooltip-copy :content="scope.row.job_info_num" :showCopyText="true"> {{scope.row.job_info_num}} </el-tooltip-copy> </template> </el-table-column> <el-table-column prop="name" label="工单名称" width="100" show-overflow-tooltip> <template #default="scope"> <el-tooltip-copy :content="scope.row.name" :showCopyText="true"> {{scope.row.name}} </el-tooltip-copy> </template> </el-table-column> <el-table-column prop="dept_name" label="工单状态" width="88"> <template #default="scope"> <el-tag :type="getStatusTagType(scope.row.status)">{{ getOrderStatus(scope.row.status) }}</el-tag> </template> </el-table-column> <el-table-column prop="dept_name" label="所属单位" width="88" show-overflow-tooltip/> <el-table-column prop="create_time" label="创建时间" width="120" show-overflow-tooltip/> <el-table-column prop="job_num" label="已执行次数" width="100" align="center" /> <el-table-column prop="content" label="工单内容" show-overflow-tooltip align="center"/> <el-table-column prop="wayline_name" label="关联航线" show-overflow-tooltip /> <el-table-column prop="ai_type_str" label="关联算法" width="100" align="center" show-overflow-tooltip/> <el-table-column prop="device_names" label="关联机巢" width="100" align="center" show-overflow-tooltip/> <el-table-column prop="creator_name" label="创建人" width="100" align="center" show-overflow-tooltip /> <el-table-column prop="cycle_time_value" label="工单周期频次" width="110" align="center" show-overflow-tooltip /> <el-table-column label="操作" width="200" fixed="right" align="center"> <template #default="scope"> <!-- {{scope.row.status}}--> <template v-if="scope.row.status === 1"> <el-button type="text" icon="el-icon-view" v-if="permission.order_log_review" @click="handleCheckDetail(scope.row, '工单审核')">审核</el-button> <!--待审核状态--> <el-button type="text" icon="el-icon-warning" v-if="hasRecallPaddingBtnPermission()" @click="orderLogRecall(row.id)">撤回 </el-button> <!-- <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button> --> <el-button type="text" icon="el-icon-warning" v-if="permission.order_log_recall" @click="revokeOrder(scope.row.id)">撤回</el-button> <el-button type="text" icon="el-icon-view" @click="handleViewDetail(scope.row, '工单详情')">详情</el-button> </template> <!--已驳回--> <template v-if="row.status == 2"> <el-button type="text" icon="el-icon-warning" @click="rejectDetail(row.id)">驳回原因 </el-button> <el-button v-if="userInfo.user_id == row.create_user" type="text" icon="el-icon-view" @click="handleViewDetail(row)">编辑 </el-button> <el-button v-else type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情 </el-button> <template v-if="scope.row.status === 2"> <el-button type="text" icon="el-icon-warning" @click="rejectReason(scope.row.id, scope.row)">驳回原因</el-button> <el-button type="text" icon="el-icon-view" @click="handleViewDetail(scope.row, '工单详情')">详情</el-button> <!-- <el-button type="text" icon="el-icon-view" v-if="userInfo.user_id === scope.row.create_user" @click="handleViewDetail(scope.row, '工单编辑')">编辑</el-button>--> <!-- <el-button type="text" icon="el-icon-view" v-else @click="handleViewDetail(scope.row, '工单详情')">详情</el-button>--> </template> <!-- 已通过 --> <template v-if="row.status == 3"> <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情 </el-button> <template v-if="scope.row.status === 3"> <el-button type="text" icon="el-icon-view" @click="handleViewDetail(scope.row, '工单详情')">详情</el-button> </template> <!--草稿--> <template v-if="row.status == 0"> <el-button type="text" icon="el-icon-edit" @click="handleViewDetail(row)">编辑 </el-button> <el-button type="text" icon="el-icon-position" @click="userPublishPush(row.id)">发布 </el-button> <el-button type="text" icon="el-icon-delete" @click="deleteOrderLog(row.id)">删除 </el-button> </template> </div> </template> <template #status="{ row }"> <el-tag :type="getStatusTagType(row.status)">{{ mapStatus(row.status) }}</el-tag> </template> <template #keyData="{ row }"> <el-tooltip :content="row.address" placement="top" effect="light"> <span>{{ row.keyData }}</span> </el-tooltip> </template> </avue-crud> </basic-main-content> <!-- <template v-if="scope.row.status === 0">--> <!-- <el-button type="text" icon="el-icon-edit" @click="handleViewDetail(scope.row, '工单编辑')">编辑</el-button>--> <!-- <el-button type="text" icon="el-icon-position" @click="userPublishPush(scope.row.id)">发布</el-button>--> <!-- <el-button type="text" icon="el-icon-delete" @click="deleteOrder(scope.row.id)">删除</el-button>--> <!-- </template>--> </template> </el-table-column> </el-table> </div> <div class="pagination"> <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background :page-sizes="[10, 20, 30, 40, 50, 100]" v-model:current-page="orderListParams.current" v-model:page-size="orderListParams.size" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </el-tab-pane> </el-tabs> <!-- 新建工单对话框 --> <el-dialog class="ztzf-dialog-mange" v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false" @close="resetForm"> <el-form :model="form" :rules="rules" ref="testform" label-width="100px"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="form.name" placeholder="请输入工单名称" maxlength="100" show-word-limit></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" filterable @change="getFlyingNestBy"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="!device_sns.length"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="自定义识别区" prop="enable_custom_area" class="safe-height"> <el-checkbox v-model="form.enable_custom_area"></el-checkbox> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit></el-input> </el-form-item> </el-col> <el-col :span="7"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" /> </el-form-item> </el-col> <el-col :span="5"> <div class="flex"> <div class="flex-1 flex-3"> <el-select v-model="form.rep_fre_type" placeholder="请选择频次"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </div> <div class="flex-1"> <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </div> </div> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="dialogVisible" ref="MapContainer"></map-container> </el-col> </el-row> <!-- <el-row>--> <!-- <div class="add-box-btns">--> <!-- <el-button type="danger" @click="submitForm(1)">发起</el-button>--> <!-- <el-button type="primary" @click="submitForm(0)">存草稿</el-button>--> <!-- <el-button @click="dialogVisible = false">取消</el-button>--> <!-- </div>--> <!-- </el-row>--> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="danger" @click="submitForm(1)" icon="el-icon-position">发起</el-button> <!-- <el-button type="primary" @click="submitForm(0)" icon="el-icon-document-add">存草稿</el-button> --> <el-button @click="dialogVisible = false" icon="el-icon-circle-close">取消</el-button> </div> </template> </el-dialog> <!-- 工单详情对话框 --> <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisible" :title="detailTitle" width="70%" :close-on-click-modal="false" @close="resetForm"> <div class="event-title-center">{{ form.name }}</div> <el-form :model="form" :rules="rules" ref="testform" label-width="100px"> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles" v-if="filters.status !== '0'"> <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }" :key="index" class="step-title"> {{ record.status_str }} </div> </div> <!-- Element Steps 组件 --> <el-steps :active="form.active" align-center class="custom-steps" v-if="filters.status !== '0'"> <el-step v-for="(record, index) in form.record_list" :key="index"> <template #description> <span class="step-description" style="position: relative; display: inline-block"> {{ record.user_name }} </span> <span class="step-timer"> {{ record.interval_time_str }} </span> <div class="step-description"> {{ record.create_time_str }} </div> </template> </el-step> </el-steps> </div> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="form.name" placeholder="请输入工单名称" :disabled="detailTitle === '工单详情'"></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy" filterable :disabled="detailTitle === '工单详情'"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number :disabled="detailTitle === '工单详情'" v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="detailTitle === '工单详情'"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple :disabled="detailTitle === '工单详情'"> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="自定义识别区" prop="enable_custom_area" class="safe-height"> <el-checkbox v-model="form.enable_custom_area" :disabled="detailTitle === '工单详情'"></el-checkbox> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12" prop="statusStr"> <el-form-item label="当前状态"> {{ mapStatus(form.status) }} </el-form-item> </el-col> <el-col :span="7"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" :disabled="detailTitle === '工单详情'" /> </el-form-item> </el-col> <el-col :span="5"> <div class="flex"> <div class="flex-1 flex-3"> <el-select v-model="form.rep_fre_type" placeholder="请选择频次" :disabled="detailTitle === '工单详情'"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </div> <div class="flex-1"> <el-time-picker v-model="form.deal_time" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" :disabled="detailTitle === '工单详情'" /> </div> </div> </el-col> <!-- <el-col :span="3">--> <!-- <el-time-picker class="timeStyle" :style="{ width: pxToRem(140) }" v-model="form.deal_time" prop="deal_time"--> <!-- value-format="HH:mm" :picker-options="{--> <!-- selectableRange: '00:00 - 23:59',--> <!-- }" :disabled="detailTitle === '工单详情'" />--> <!-- </el-col>--> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit :disabled="detailTitle === '工单详情'"></el-input> </el-form-item> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="detailVisible" ref="MapContainer"></map-container> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="danger" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <el-button type="primary" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(0)" icon="el-icon-plus">保存 </el-button> <!-- <el-button type="primary" v-if="form.status == 0 || userInfo.user_id == form.create_user" @click="submitForm(0)">保存</el-button> --> <el-button type="primary" v-if="form.status == 1 && hasPaddingBtnPermission()" @click="orderLogPass(form.id)">通过 </el-button> <el-button type="danger" v-if="form.status == 1 && hasRejectionBtnPermission()" @click="orderLogReject(form.id)">驳回 </el-button> </div> </template> </el-dialog> <!-- 工单详情 --> <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisibleCopy" title="工单详情" width="70%" :close-on-click-modal="false" @close="resetForm"> <div class="event-title-center">{{ form.name }}</div> <el-form :model="form" ref="testform" label-width="100px"> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles" v-if="filters.status !== '0'"> <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }" :key="index" class="step-title"> {{ record.status_str }} </div> </div> <!-- Element Steps 组件 --> <el-steps :active="form.active" align-center class="custom-steps" v-if="filters.status !== '0'"> <el-step v-for="(record, index) in form.record_list" :key="index"> <template #description> <span class="step-description" style="position: relative; display: inline-block"> {{ record.user_name }} </span> <span class="step-timer"> {{ record.interval_time_str }} </span> <div class="step-description"> {{ record.create_time_str }} </div> </template> </el-step> </el-steps> </div> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="form.name" placeholder="请输入工单名称" :disabled="true"></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" :disabled="true"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number :disabled="true" v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="true"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple :disabled="true"> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="自定义识别区" prop="enable_custom_area" class="safe-height"> <el-checkbox v-model="form.enable_custom_area" :disabled="true"></el-checkbox> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12" prop="statusStr"> <el-form-item label="当前状态"> {{ mapStatus(form.status) }} </el-form-item> </el-col> <el-col :span="7"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled="true" /> </el-form-item> </el-col> <el-col :span="5"> <div class="flex"> <div class="flex-1 flex-3"> <el-select v-model="form.rep_fre_type" placeholder="请选择频次" :disabled="true"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </div> <div class="flex-1"> <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" :disabled="true" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </div> </div> </el-col> <!-- <el-col :span="3">--> <!-- <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" :disabled="true"--> <!-- value-format="HH:mm" :picker-options="{--> <!-- selectableRange: '00:00 - 23:59',--> <!-- }" />--> <!-- </el-col>--> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="2" placeholder="请输入工单内容" maxlength="255" show-word-limit :readonly="true" :disabled="true"></el-input> </el-form-item> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="detailVisibleCopy" ref="MapContainer"></map-container> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="danger" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <!-- <el-button type="primary" v-if="form.status == 0 || userInfo.user_id == form.create_user" @click="submitForm(0)">保存</el-button> --> <el-button type="primary" v-if="form.status == 1 && hasPaddingBtnPermission()" @click="orderLogPass(form.id)" icon="el-icon-check">通过 </el-button> <el-button type="danger" v-if="form.status == 1 && hasRejectionBtnPermission()" @click="orderLogReject(form.id)" icon="el-icon-close">驳回 </el-button> <el-button @click="detailVisibleCopy = false" icon="el-icon-circle-close">取消</el-button> </div> </template> </el-dialog> </basic-container> </div> <AddEditDetails v-model:show="isShowAddEditDetails" @refresh="refreshGetOrderList" /> </template> <script> import { pxToRem, pxToRemNum } from '@/utils/rem' import { calculateDefaultRange } from '@/utils/util' <script setup> import SearchBox from './component/SearchBox.vue' import AddEditDetails from './component/AddEditDetails.vue' import NProgress from 'nprogress' import { useStore } from 'vuex' import { downloadXls } from '@/utils/util' import { getList, saveUpdateOrderLog, @@ -593,1114 +105,389 @@ deleteOrderLog, getWaylineMaxTerrainHeight } from '@/api/tickets/orderLog' import { getTicketInfo } from '@/api/tickets/ticket' import { newGetWorkspacesPage } from '@/api/resource/wayline' import { getDictionaryByCode } from '@/api/system/dictbiz' import { getWaylineFileListByArea, newGetWorkspacesPage } from '@/api/resource/wayline' import { export_json_to_excel } from '@/utils/exportExcel' import { getFlyingNestBy } from '@/api/device/device' import { mapGetters } from 'vuex' import NProgress from 'nprogress' import { downloadXls } from '@/utils/util' import 'nprogress/nprogress.css' import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz' import { cloneDeep } from 'lodash'; import { computed, onMounted } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus' import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' // 导入中文语言包 import weekday from 'dayjs/plugin/weekday' import elTooltipCopy from '@/components/ElTooltipCopy.vue' import { CircleClose, Promotion, Select } from '@element-plus/icons-vue' import { useStore } from 'vuex' import _ from 'lodash' // const store = useStore() dayjs.extend(weekday) dayjs.locale('zh-cn') import { aiImagesPage } from '@/api/dataCenter/dataCenter'; // const lastHeight = computed(() => store.state.common.lastHeight) const store = useStore() export default { components: { elTooltipCopy }, name: 'TicketPage', data () { return { activeTab: 'all', getlastHeight: 0, tabs: [ { label: '全部工单', name: 'all', value: null, count: 0 }, { label: '待审核', name: 'WAIT_AUDIT', value: 1, count: 0 }, { label: '已驳回', name: 'REJECTED', value: 2, count: 0 }, { label: '已通过', name: 'PASS', value: 3, count: 0 }, // { label: '草稿', name: 'DRAFT', value: 0, count: 0 } ], filters: { key_word: '', create_dept: '', start_date: null, end_date: null, file_id: '', ai_types: [], status: '', dateRange: [] }, departments: [], types: [], device_sns: [], ai_types: [], wayLineList: [], detailTitle: '编辑工单', detailVisibleCopy: false, const userInfo = computed(() => store.state.user.userInfo) const permission = computed(() => store.state.user.permission); statuses: [ // { label: '草稿', value: '0' }, { label: '待审核', value: '1' }, { label: '已驳回', value: '2' }, { label: '已通过', value: '3' } ], //周期 cycles: ['每天', '周一', '周二', '周三', '周四', '周五', '周六', '周末', '周天', '工作日'], console.log(userInfo,permission, '8888') tableData: [], option: { index: true, indexWidth: 66, indexLabel: '序号', indexFixed: true, const isShowAddEditDetails = ref(false) align: 'left', border: true, stripe: false, searchShow: true, searchBtnText: '搜索', searchMenuSpan: 6, addBtnText: '新建工单', addBtn: false, viewBtn: false, editBtn: false, delBtn: false, cancelBtn: true, cancelBtnText: '取消', const activeTab = ref('all') provide('activeTab', activeTab) let tabsList = ref([ // { label: '我的工单', name: 'my_order', value: null, count: 0 }, // { label: '全部工单', name: 'all', value: null, count: 0 }, // { label: '待审核', name: 'WAIT_AUDIT', value: 1, count: 0 }, // { label: '已驳回', name: 'REJECTED', value: 2, count: 0 }, // { label: '已通过', name: 'PASS', value: 3, count: 0 }, // { label: '草稿', name: 'DRAFT', value: 0, count: 0 } ]) function filteredTabs () { if (permission?.value.orderLog_tab_allOrders) { tabsList.value.push({ label: '全部工单', name: 'all', value: null, count: 0 }) } if (permission?.value.orderLog_tab_pending) { tabsList.value.push({ label: '待审核', name: 'WAIT_AUDIT', value: 1, count: 0 }) } if (permission?.value.orderLog_tab_reject) { tabsList.value.push({ label: '已驳回', name: 'REJECTED', value: 2, count: 0 }) } if (permission?.value.orderLog_tab_pass) { tabsList.value.push({ label: '已通过', name: 'PASS', value: 3, count: 0 }) } activeTab.value = tabsList.value[0].name // tab值传 orderListParams.status = tabsList.value[0].value console.log('8878797', tabsList.value) // tabsList.value // .map(tab => { // if (tab.name === 'all') { // return { ...tab, isShow: permission?.value.orderLog_tab_allOrders } // } // if (tab.name === 'WAIT_AUDIT') { // return { ...tab, isShow: permission?.value.orderLog_tab_pending } // } // if (tab.name === 'REJECTED') { // return { ...tab, isShow: permission?.value.orderLog_tab_reject } // } // if (tab.name === 'PASS') { // return { ...tab, isShow: permission?.value.orderLog_tab_pass } // } // // return { ...tab, isShow: true } // }) // .filter(tab => tab.isShow) } menu: true, menuWidth: 210, menuClassName: 'cur-menu', // tab切换 function handleTabChange (tab) { activeTab.value = tab.paneName orderListParams.status = '' if (tab.paneName === 'WAIT_AUDIT') { orderListParams.status = '1' } else if (tab.paneName === 'REJECTED') { orderListParams.status = '2' } else if (tab.paneName === 'PASS') { orderListParams.status = '3' } else if (tab.paneName === 'DRAFT') { orderListParams.status = '0' } refreshGetOrderList() } height: 'auto', calcHeight: 196, column: [ { label: '工单编号', prop: 'job_info_num', width: 150 }, { label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '工单状态', prop: 'status', width: 88, showOverflowTooltip: true }, { label: '所属单位', prop: 'dept_name', ellipsis: true, showOverflowTooltip: true }, { label: '创建时间', prop: 'create_time', width: 144, ellipsis: true, showOverflowTooltip: true }, { label: '已执行次数', prop: 'job_num', width: 70, ellipsis: true, showOverflowTooltip: true }, { label: '工单内容', prop: 'content', ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '关联航线', prop: 'wayline_name', ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '关联算法', prop: 'ai_type_str', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '关联机巢', prop: 'device_names', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '创建人', prop: 'creator_name', width: 96, ellipsis: true, overHidden: true, showOverflowTooltip: true }, // { // label: '关联机巢', prop: 'device_names', width: 112, ellipsis: true, overHidden: true, // showOverflowTooltip: true, // }, { label: '工单周期频次', prop: '', width: 110, formatter: row => this.formatCycleTime(row), html: true, ellipsis: true, showOverflowTooltip: true // overHidden: true } ] }, page: { pageSize: 10, currentPage: 1, total: 0 }, dialogVisible: false, detailVisible: false, currentDetail: {}, form: {}, rules: { name: [ { required: true, message: '请输入工单名称', trigger: 'blur' }, { max: 100, message: '工单名称不能超过100个字', trigger: 'blur' } ], file_id: [{ required: true, message: '需要选择航线', trigger: 'change' }], device_sns: [{ required: true, message: '请选择机巢', trigger: 'change' }], content: [ { required: true, message: '请输入工单内容', trigger: 'blur' }, { max: 255, message: '工单内容不能超过255个字', trigger: 'blur' } ], date_range: [{ required: true, message: '请选择时间不能为空', trigger: 'blur' }], rep_fre_type: [{ required: true, message: '请选择周期', trigger: 'blur' }], deal_time: [{ required: true, message: '请选择执行时间', trigger: 'blur' }] }, loading: false, globalCounts: {}, mapLoaded: false, // 配置时间选择器默认配置 datePickerDefaultVal: calculateDefaultRange() // 更新统计数 async function updateGlobalCounts() { let res = await jobStatusNum() res.data.data.forEach(item => { const tab = tabsList.value.find(t => t.name === item.dict_key) if (tab) { tab.count = item.num } }, async created () { var response = await getDictionaryByCode('SF') var word_order_typeResponse = await getDictionaryByCode('WORK_ORDER_TYPE') this.ai_types = response.data.data['SF'] this.types = word_order_typeResponse.data.data['WORK_ORDER_TYPE'] //获取航线 this.asyncgetWaylineFileListByArea() const response2 = await getTicketInfo() const { dept_data, event_type, ai_type } = response2.data.data this.departments = dept_data.map(item => ({ label: item.dept_name, value: item.id })) }, mounted () { this.fetchTableData() const id = this.$route.query.id console.log('idddddd', id) }) } if (id) { // 确保 id 存在 this.handleViewDetail({ id }) const find = this.$store.state.tags.bsTagList.find(i => i.path === '/tickets/orderLog') find && (find.query = {}) } else { console.error('工单ID不存在!') // 获取航线 let lineList = ref([]) provide('lineList', lineList) function getLines() { const formData = { name: '', waylineType: undefined, current: 1, size: 99999999, descs: 'update_time' } newGetWorkspacesPage(formData).then(res => { lineList.value = res.data.data.records }) } // 获取算法 let aiTypeList = ref([]) provide('aiTypeList', aiTypeList) function aitypeList() { getDictionaryByCode('SF').then(res => { aiTypeList.value = res.data.data['SF'] }) } const total = ref(0) const orderListParams = reactive({ current: 1, size: 10, searchParams: {}, status: '' }) const orderListTable = ref([]) function getTableList() { orderListParams.searchParams.ai_types = orderListParams.searchParams.ai_typesValue ? [ orderListParams.searchParams.ai_typesValue] : null const apiParams = { ...cloneDeep(orderListParams), ...cloneDeep(orderListParams.searchParams) } getList(apiParams, orderListParams.current, orderListParams.size).then(res => { orderListTable.value = res.data.data.records total.value = res.data.data.total // updateGlobalCounts() }) } // 分页大小改变 const handleSizeChange = val => { orderListParams.size = val getTableList() } // 页码改变 const handleCurrentChange = val => { orderListParams.current = val getTableList() } // 传参查询条件 const searchClick = params => { orderListParams.current = 1 orderListParams.size = 10 console.log('333', params) orderListParams.searchParams = params getTableList() } const refreshGetOrderList = () => { orderListParams.current = 1 orderListParams.size = 10 getTableList() } function getOrderStatus (status) { const statusTextMap = { // 0: '草稿', 1: '待审核', 2: '已驳回', 3: '已通过' } return statusTextMap[status] || '未知状态' } function getStatusTagType (status) { const statusMap = { 1: 'warning', 2: 'info', 3: 'primary', 4: 'success', 5: 'danger' } return statusMap[status] || 'info' } // 新增按钮 和 导出按钮 // function hasAddBtnPermission () { // return permission && permission?.value.order_log_add // } // 新建工单、编辑、详情 const txtTitle = ref('新建工单') const rowObj = ref({}) provide('txtTitle', txtTitle) provide('rowObj', rowObj) function handleAddOrder() { txtTitle.value = '新建工单' isShowAddEditDetails.value = true } // 详情、编辑 function handleViewDetail(row, txt) { rowObj.value = row txtTitle.value = txt isShowAddEditDetails.value = true } // 审核 function handleCheckDetail(row, txt) { rowObj.value = row txtTitle.value = txt isShowAddEditDetails.value = true } // 导出 async function exportData() { try { await ElMessageBox.confirm('是否导出智飞工单数据?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', }) NProgress.start() orderListParams.searchParams.ai_types = orderListParams.searchParams.ai_typesValue ? [ orderListParams.searchParams.ai_typesValue] : null const apiParams = { ...cloneDeep(orderListParams), ...cloneDeep(orderListParams.searchParams) } }, computed: { CircleClose () { return CircleClose }, ...mapGetters(['userInfo', 'permission']), safeHeight () { const safeHeight = this.$store.state.common.safeHeight return Number(safeHeight) || 0 // 确保是数字类型 }, positionsArr () { const positionsArr = this.$store.state.common.positionsArr return positionsArr || [] // 确保是数字类型 }, filteredTabs () { // rejection_and_draft 权限控制“已驳回”和“草稿”tab // console.log(this.permission, '权限信息') // console.log(this.userInfo, '权限信息22') const canShowRejectAndDraft = this.permission?.rejection_and_draft === true return this.tabs .map(tab => { if (tab.name === 'DRAFT') { return { ...tab, isShow: canShowRejectAndDraft } } if (tab.name === 'REJECTED') { return { ...tab, isShow: canShowRejectAndDraft } } return { ...tab, isShow: true } }) .filter(tab => tab.isShow) const res = await orderLogExport(apiParams) console.log(res, '导出') downloadXls(res.data, `智飞工单${dayjs().format('YYYY-MM-DD')}.xlsx`) ElMessage.success('导出成功') } catch (error) { if (error !== 'cancel') { ElMessage.error('导出失败') console.error('导出错误:', error) } }, } finally { NProgress.done() } } methods: { handleCellClick (row, column) { if (column.no === 1) { navigator.clipboard.writeText(row.job_info_num).then(() => { this.$message.success('复制工单编号成功') }) } else if (column.no === 2) { navigator.clipboard.writeText(row.name).then(() => { this.$message.success('复制工单名称成功') }) } else if (column.no === 4) { navigator.clipboard.writeText(row.dept_name).then(() => { this.$message.success('复制所属单位成功') }) } else if (column.no === 8) { navigator.clipboard.writeText(row.wayline_name).then(() => { this.$message.success('复制关联航线成功') }) } else if (column.no === 9) { navigator.clipboard.writeText(row.ai_type_str).then(() => { this.$message.success('复制关联算法成功') }) } }, disabledDate (time) { return time.getTime() < Date.now() - 8.64e7 // 86400000 = 24 * 60 * 60 * 1000 }, searchChange (params, done) { // console.log('searchChange') this.query = params this.parentId = '' this.page.currentPage = 1 this.onLoad(this.page, params) done() }, async onLoad (page, params = {}) { this.loading = true getList( null, this.page.currentPage, this.page.pageSize, Object.assign(params, this.query) ).then(res => { this.tableData = res.data.data this.loading = false this.selectionClear() }) }, selectionClear () { this.selectionList = [] this.$refs.crud.toggleSelection() }, async loadAMapScripts () { try { // await loadAMap(); // await loadAMapUI(); this.mapLoaded = true } catch (error) { console.error('Failed to load AMap scripts:', error) this.$message.error('地图加载失败,请检查网络或API Key配置') } }, formatCycleTime (row) { return `${row.cycle_time_value}` }, async fetchTableData () { this.loading = true try { let params = this.getQueryParam() // console.log('发送的参数:', params) const response = await getList(params, this.page.currentPage, this.page.pageSize) if (!response?.data?.data?.records) { throw new Error('接口返回数据格式不正确') } const { total, records } = response.data.data this.tableData = records.map(item => { return item }) // console.log('权限检查:', this.permission) this.page.total = total || 0 this.updateGlobalCounts() } catch (error) { // console.error('获取数据失败:', error) this.$message.error(error.message || '获取数据失败') this.tableData = [] this.page.total = 0 } finally { this.loading = false } }, getQueryParam () { const currentTab = this.tabs.find(tab => tab.name === this.activeTab) if (this.filters.dateRange) { // console.log( // 'this.formatDate(this.filters.dateRange[0])', // this.formatDate(this.filters.dateRange[0]) // ) } const params = { key_word: this.filters.key_word || undefined, create_dept: this.filters.create_dept || undefined, ai_types: this.filters.ai_types && this.filters.ai_types.length > 0 ? [this.filters.ai_types] : null, file_id: this.filters.file_id || undefined, status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value, dept_id: this.filters.department || undefined, create_start_date: this.filters.dateRange?.[0] ? this.formatDate(this.filters.dateRange[0]) : undefined, create_end_date: this.filters.dateRange?.[1] ? this.formatDate(this.filters.dateRange[1]).replace('00:00:00', '23:59:59') : undefined, start_date: this.filters.cycleDateRange?.[0] ? this.formatDate(this.filters.cycleDateRange[0]) : undefined, end_date: this.filters.cycleDateRange?.[1] ? this.formatDate(this.filters.cycleDateRange[1]).replace('00:00:00', '23:59:59') : undefined, rep_fre_type: this.filters.rep_fre_type || undefined, deal_time: this.filters.deal_time || undefined, current: this.page.currentPage, size: this.page.pageSize } return params }, sizeChange (pageSize) { this.page.pageSize = pageSize }, async submitForm (status) { this.$refs.testform.validate(async valid => { if (valid) { let dateRange = this.form.date_range // console.log('dateRange' + dateRange) this.form.begin_time = this.formatDate(dateRange[0]) this.form.end_time = this.formatDate(dateRange[1]) // 如果选中日期包含当前天,那么选中的时间点不能小于当前时间 if (this.form.deal_time) { const selectedDate = dayjs(this.form.date_range[0]).format('YYYY-MM-DD') const selectedTime = dayjs(selectedDate + ' ' + this.form.deal_time).toDate() const now = new Date() if (selectedDate === dayjs().format('YYYY-MM-DD') && selectedTime < now) { return this.$message.warning('任务时间不能小于当前时间') } } let checkedList = this.device_sns.filter(item => this.form.device_sns.includes(item.device_sn)); const maxItem = checkedList.reduce((max, item) => { return item.drone_height > max.drone_height ? item : max; }); // 返航绝对高度 let backHeight = this.form.rth_altitude // + maxItem.drone_height console.log(backHeight,'backHeight') // 安全起飞高度 let positions = this.positionsArr positions.unshift({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height }) positions.push({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height}) let resultHeight = 0 await getWaylineMaxTerrainHeight(positions).then(res => { resultHeight = this.safeHeight > (res.data.data + 30) ? this.safeHeight : (res.data.data + 30) }) let valueHeight = _.round(backHeight - resultHeight, 2) console.log('打印比较值', valueHeight) if (valueHeight < 0) { this.$confirm(`当前返航高度存在安全隐患,建议调整为${resultHeight}米以上后进行发布`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { const submitData = { ...this.form, status: status, ai_types: this.form.ai_types?.length ? this.form.ai_types : [] } await saveUpdateOrderLog(submitData) let id = this.form.id if (id) { this.$message.success('工单发布成功') } else { this.$message.success('工单创建成功') } this.dialogVisible = false this.detailVisible = false; (this.device_sns = []), (this.wayLineList = []), this.fetchTableData() }).catch(() => { }) } else { const submitData = { ...this.form, status: status, ai_types: this.form.ai_types?.length ? this.form.ai_types : [] } await saveUpdateOrderLog(submitData) let id = this.form.id if (id) { this.$message.success('工单发布成功') } else { this.$message.success('工单创建成功') } this.dialogVisible = false this.detailVisible = false; (this.device_sns = []), (this.wayLineList = []), this.fetchTableData() } } }) }, //驳回原因显示 async rejectDetail (id) { const response = await orderLogDetails(id) let data = response.data.data this.$confirm(data.remark, '驳回原因', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.form = { ...response.data.data } // this.detailVisible = true this.handleViewDetail(data) }) }, formatDate (date) { if (!date) return undefined const d = new Date(date) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String( d.getDate() ).padStart(2, '0')} 00:00:00` }, mapStatus (status) { const statusTextMap = { 0: '草稿', 1: '待审核', 2: '已驳回', 3: '已通过' } return statusTextMap[status] || '未知状态' }, getStatusTagType (status) { const statusMap = { 1: 'warning', 2: 'info', 3: 'primary', 4: 'success', 5: 'danger' } return statusMap[status] || 'info' }, handleTabChange (tab) { this.activeTab = tab.props?.name || tab.name this.filters.status = '' if (tab.props?.name === 'WAIT_AUDIT') { this.filters.status = '1' } else if (tab.props?.name === 'REJECTED') { this.filters.status = '2' } else if (tab.props?.name === 'PASS') { this.filters.status = '3' } else if (tab.props?.name === 'DRAFT') { this.filters.status = '0' } this.page.currentPage = 1 this.fetchTableData() }, handleSearch () { this.page.currentPage = 1 this.fetchTableData() }, handleReset () { this.filters = { keyword: '', department: '', type: '', dateRange: [], status: '' } this.page.currentPage = 1 this.fetchTableData() }, currentChange (currentPage) { this.page.currentPage = currentPage }, async updateGlobalCounts () { const counts = { all: 0, DRAFT: 0, WAIT_AUDIT: 0, REJECTED: 0, PASS: 0 } var reponse = await jobStatusNum() // console.log('统计' + reponse.data.data) reponse.data.data.forEach(item => { const tab = this.tabs.find(t => t.name === item.dict_key) if (tab) { tab.count = item.num } }) }, handleAdd () { this.form = {} this.form.rth_altitude = 120 this.dialogVisible = true this.$nextTick(() => { if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { this.$refs.MapContainer.initAddEntity('initPosition') } }) //航线列表 this.asyncgetWaylineFileListByArea() }, resetForm () { this.form = { name: '', type: '', handler: '', algorithm: '', location: '', address: '', content: '', photos: [] } if (this.$refs.testform) { this.$refs.testform.resetFields() } }, formatLocation (location) { if (!Array.isArray(location)) { return '未知位置' } return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}` }, async handleViewDetail (row) { const response = await orderLogDetails(row.id) const data = response.data.data this.form = { ...data } // 更新机巢列表 this.device_sns = data.device_map_infos;// data.device_list; this.permission && (this.permission.order_log_review || this.permission.order_log_recall) && (data.status == 1 || data.status == 3 || (data.status == 2 && this.userInfo.user_id !== this.form.create_user)) ? (this.detailTitle = '工单详情') : (this.detailTitle = '编辑工单') this.detailVisible = true // 更新航线列表,追加 wayline_file_region_vo 数据 if (data.wayline_file_region_vo) { const newWayline = data.wayline_file_region_vo // 检查是否已经存在于 this.wayLineList 中 const isDuplicate = this.wayLineList.some( item => item.wayline_id === newWayline.wayline_id ) if (!isDuplicate) { this.wayLineList.push(newWayline) } } this.initMapLine(data.device_map_infos) const find = this.$store.state.tags.bsTagList.find(i => i.path === '/tickets/orderLog') find && (find.query = {}) }, async handleCheckDetail (row) { const response = await orderLogDetails(row.id) const data = response.data.data this.form = { ...data } // 更新航线列表,追加 wayline_file_region_vo 数据 // 更新航线列表,追加 wayline_file_region_vo 数据 if (data.wayline_file_region_vo) { const newWayline = data.wayline_file_region_vo // 检查是否已经存在于 this.wayLineList 中 const isDuplicate = this.wayLineList.some( item => item.wayline_id === newWayline.wayline_id ) if (!isDuplicate) { this.wayLineList.push(newWayline) } } // 更新机巢列表 this.device_sns = data.device_list this.detailVisibleCopy = true this.initMapLine(data.device_map_infos) }, //导出 async exportData () { this.$confirm('是否智飞工单数据?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { NProgress.start() let params = this.getQueryParam() orderLogExport(params).then(res => { downloadXls(res.data, `智飞工单${this.$dayjs().format('YYYY-MM-DD')}.xlsx`) NProgress.done() }) }) }, hasAddBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('this.permission.order_log_add :', this.permission.order_log_add) return this.permission && this.permission.order_log_add === true }, hasPaddingBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('权限检查:', this.permission) return this.permission && this.permission.order_log_review === true }, hasRecallPaddingBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('权限检查:', this.permission) // 智飞工单撤回 return this.permission && this.permission.order_log_recall === true }, //驳回按钮权限 hasRejectionBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('权限检查:', this.permission) return this.permission && this.permission.rejection_btn === true }, //自己点发布 userPublishPush (id) { this.$confirm('确定发布吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { let response = userPublish(id) this.$message.success('发布成功') this.fetchTableData() }) }, //删除 deleteOrderLog (id) { this.$confirm('确定删除吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { let response = deleteOrderLog(id) this.$message.success('删除') this.fetchTableData() }) }, refreshChange () { this.fetchTableData() }, //获取航线列表 async asyncgetWaylineFileListByArea (name) { const formData = { name: '', waylineType: undefined, current: 1, size: 99999999, descs: 'update_time' } var wayLineListResponse = await newGetWorkspacesPage(formData) this.wayLineList = wayLineListResponse.data.data.records this.initMapLine() }, initMapLine (infos = {}, cb = () => { }) { let currentLine = this.wayLineList.find(item => item.wayline_id == this.form.file_id) if (!currentLine) return this.$nextTick(() => { if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { this.$refs.MapContainer.initAddEntity('polyline', { url: `${import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key }?_t=${new Date().getTime()}`, type: currentLine.wayline_type, cb, infos }) } }) }, //可飞行机巢列表 async getFlyingNestBy (waylineId) { const that = this // 先清空 that.form.device_sns = [] that.device_sns = [] this.initMapLine({}, async polygon => { const currentLine = that.wayLineList.find(item => item.wayline_id === waylineId) //按照航线来 const params = { type: ['2', '4', '5'].includes(currentLine.wayline_type) ? 2 : 0, wayline_id: waylineId, polygon } var wayLineListResponse = await getFlyingNestBy(params) // 再赋值 that.device_sns = wayLineListResponse.data.data }) }, //撤回 async orderLogRecall (id) { this.$confirm('确定撤回则到草稿箱。', '是否撤回?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { let reposne = await orderLogRecall(id) this.handleSearch() }) }, onLoad () { this.fetchTableData() }, /** * 通过 */ async orderLogPass (id) { let response = await orderLogPass(id) let data = response.data.data this.$message.success('审核通过') this.detailVisibleCopy = false this.detailVisible = false this.page.currentPage = 1 this.onLoad(this.page, this.query) // this.handleViewDetail() }, /** * 驳回 */ async orderLogReject (id) { this.$prompt('', '驳回原因', { confirmButtonText: '确定', cancelButtonText: '取消' }).then(async ({ value }) => { let response = await orderLogReject(id, value) let data = response.data.data this.$message.success('驳回成功') this.detailVisibleCopy = false // this.detailVisible = false this.page.currentPage = 1 this.onLoad(this.page, this.query) }) } }, watch: { // lastHeight(newVal, oldVal) { // console.log('lastHeight', newVal, oldVal); // this.getlastHeight = newVal; // }, tableData: { handler () { // this.updateTabCounts() }, deep: true // 撤回工单 async function revokeOrder(id) { try { await ElMessageBox.confirm('撤回会删除本工单,需重新提交?', '是否撤回', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) // 用户点击确定,执行撤回操作 // await orderLogRecall(id) await deleteOrderLog(id) // 显示成功消息 ElMessage.success('撤回成功') // 触发搜索刷新 refreshGetOrderList() } catch (error) { // 用户点击取消,不执行任何操作 if (error !== 'cancel') { ElMessage.error('撤回失败') console.error('撤回错误:', error) } } } // 驳回原因 async function rejectReason(id, row) { try { const res = await orderLogDetails(id) const data = res.data.data await ElMessageBox.confirm(data.remark, '驳回原因', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) // 用户点击确定后的逻辑 TODO // form.value = { ...res.data.data } handleViewDetail(row, userInfo.value.user_id === row.create_user ? '工单编辑':'工单详情') } catch (error) { // 用户点击取消,不执行任何操作 if (error !== 'cancel') { ElMessage.error('操作失败') console.error('错误:', error) } } } // 删除 async function deleteOrder(id) { try { await ElMessageBox.confirm('确定删除吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }) deleteOrderLog(id) ElMessage.success('删除成功') // 触发搜索刷新 refreshGetOrderList() } catch (error) { if (error !== 'cancel') { ElMessage.error('删除失败') console.error('删除错误:', error) } } } onMounted(() => { getLines() aitypeList() filteredTabs() getTableList() updateGlobalCounts() }) </script> <style lang="scss" scoped> //:deep(.el-input-number) { // width: 200px; //} //:deep(.el-form-item__label) { // width: 100px !important; //} ::v-deep(.el-tabs) { height: 100%; .order-log-update { height: 0; flex: 1; margin: 0 10px 10px 10px; background-color: #ffffff; padding: 10px 20px; border-radius: 5px; display: flex; flex-direction: column; .el-tabs__header { order: 1; // 表格 .task-table { //height: 0; //flex: 1; height: 600px; margin-top: 18px; overflow: auto; } .el-tabs__content { order: 2; .btnItem { height: 27px; line-height: 27px; border-radius: 0px 0px 0px 0px; border: 1px solid #51a8ff; font-family: Segoe UI, Segoe UI; font-weight: 400; font-size: 14px; color: #1C5CFF; padding: 0 10px; display: inline-block; margin-right: 10px; cursor: pointer; &.turnBack { border: 1px solid #ffa500; color: #ffa500; } &.cancelTask { border: 1px solid #00AA2D; color: #00AA2D; } } // 分页 :deep(.custom-header th.el-table__cell) { color: rgba(0, 0, 0, 0.85); } .el-tabs__content { height: 0; flex: 1; :deep(.el-table td.el-table__cell) { color: #606266; } :deep(.el-pagination) { display: flex; flex-direction: column; .el-tab-pane { height: 0; flex: 1; display: flex; flex-direction: column; } } } .filter-bar { // align-items: center; .search-bar-box { margin-bottom: 15px; display: flex; justify-content: space-between; gap: 8px; &-item { flex: 1; &> ::v-deep(.el-date-editor) { width: 100%; box-sizing: border-box; } } .search-btn { flex: 1; display: flex; justify-content: flex-end; } .flex-2 { min-width: 454px; } } } :deep(.el-pagination .el-select) { width: 128px !important; } .step-timer { position: absolute; left: 80%; top: 50%; transform: translateY(-50%); width: 100px; margin-left: 4px; color: #666; font-size: 12px; } :deep(.el-textarea__inner) { padding: 8px 12px; } :deep(.el-input__inner), :deep(.el-select), :deep(.el-select .el-input) { width: 100%; // 确保所有输入框和选择框宽度一致 } :deep(.el-textarea__inner) { resize: none; } .action-bar { margin-bottom: 16px; } .el-tabs { :deep(.el-tabs__content) { overflow: visible; } } .el-dialog { .el-form-item { margin-bottom: 10px; } } .el-upload { width: 100%; display: flex; flex-wrap: wrap; :deep(.el-upload-list__item) { transition: all 0.3s ease; justify-content: right; } :deep(.el-upload-list__item:hover) { background-color: #f5f7fa; :deep(.el-pagination button) { background: center center no-repeat none !important; color: #8eb8ea !important; } } .el-upload__tip { font-size: 12px; color: #909399; margin-top: 7px; } .custom-steps-container { width: 100%; margin: 20px 0; } .steps-titles { display: flex; justify-content: space-between; margin-bottom: 16px; position: relative; } .step-title { text-align: center; flex: 1; font-size: 16px; color: #999; position: relative; padding-bottom: 10px; } .step-title.active { color: #409eff; font-weight: bold; } .custom-steps { margin-top: -20px; /* 向上移动与标题重叠 */ } .el-step__head { margin-top: 0; } .el-step__description { margin-top: 8px; padding: 0 20px; } .step-description { font-size: 14px; color: #666; line-height: 1.5; } .event-title-center { text-align: center; font-size: 20px; font-weight: bold; margin-bottom: 12px; color: #333; } .cur-menu { .menu-custom-box { display: flex; justify-content: center; flex-wrap: wrap; &> ::v-deep(.el-button) { flex: 1; margin-left: 0; margin-right: 10px; } } } .add-box-btns { width: 100%; display: flex; justify-content: center; } .safe-height { :deep(.el-form-item__label) { width: 120px !important; } :deep(.el-input-number) { width: 180px !important; } } .flex { display: flex; } .flex-1 { flex: 1; &> ::v-deep(div) { width: 100% !important; } } .flex-3 { margin-right: 4px; } </style> src/views/tickets/orderLog1.vue
New file @@ -0,0 +1,191 @@ <!-- 任务统计表格 --> <template> <div class="order-log-update"> <el-tabs v-model="activeTab" @tab-click="handleTabChange"> <el-tab-pane v-for="tab in tabsList" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name"> <SearchBox @search="searchClick"></SearchBox> <div class="task-table"> <el-table border :data="orderListTable" class="custom-header"> <el-table-column label="序号" type="index" width="60"> <template #default="{ $index }"> {{ ($index + 1 + (orderListParams.current - 1) * orderListParams.size).toString().padStart(2, '0') }} </template> </el-table-column> <el-table-column prop="job_info_num" label="工单编号" width="160"> <template #default="scope"> <el-tooltip-copy :content="scope.row.job_info_num" :showCopyText="true"> {{scope.row.job_info_num}} </el-tooltip-copy> </template> </el-table-column> <el-table-column prop="name" label="工单名称" width="160"> <template #default="scope"> <el-tooltip-copy :content="scope.row.name" :showCopyText="true"> {{scope.row.name}} </el-tooltip-copy> </template> </el-table-column> <el-table-column prop="dept_name" label="所属单位" show-overflow-tooltip/> <el-table-column prop="create_time" label="创建时间" show-overflow-tooltip/> <el-table-column prop="job_num" label="已执行次数" show-overflow-tooltip align="center" /> <el-table-column prop="content" label="工单内容" show-overflow-tooltip align="center" /> <el-table-column prop="wayline_name" label="关联航线" show-overflow-tooltip /> <el-table-column prop="ai_type_str" label="关联算法" width="200" align="center" /> <el-table-column prop="device_names" label="关联机巢" width="200" align="center" /> <el-table-column prop="creator_name" label="创建人" align="center" show-overflow-tooltip /> <el-table-column prop="cycle_time_value" label="工单周期频次" align="center" show-overflow-tooltip /> <el-table-column label="操作" width="200" align="center"> <template #default="scope"> <el-button icon="el-icon-back" v-if="scope.row.status === 2" type="text"@click="turnBack(scope.row)">返航</el-button> <el-button icon="el-icon-close" v-if="scope.row.status === 1" type="text" @click="cancelTask(scope.row)">取消任务</el-button> <el-button icon="el-icon-view" type="text" @click="handleDetail(scope.row)">查看</el-button> </template> </el-table-column> </el-table> </div> <div class="pagination"> <el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background :page-sizes="[10, 20, 30, 40, 50, 100]" :size="size" v-model:current-page="orderListParams.current" v-model:page-size="orderListParams.size" layout="total, sizes, prev, pager, next, jumper" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </el-tab-pane> </el-tabs> </div> </template> <script setup> import SearchBox from './component/SearchBox.vue' import { getList, saveUpdateOrderLog, orderLogDetails, orderLogRecall, orderLogReject, orderLogPass, orderLogExport, jobStatusNum, userPublish, deleteOrderLog, getWaylineMaxTerrainHeight } from '@/api/tickets/orderLog' import { cloneDeep } from 'lodash'; let tabsList = ref([ { label: '全部工单', name: 'all', value: null, count: 0 }, { label: '待审核', name: 'WAIT_AUDIT', value: 1, count: 0 }, { label: '已驳回', name: 'REJECTED', value: 2, count: 0 }, { label: '已通过', name: 'PASS', value: 3, count: 0 }, { label: '草稿', name: 'DRAFT', value: 0, count: 0 } ]) const orderListParams = reactive({ current: 1, size: 10, searchParams: {}, }) const orderListTable = ref([]) function getTableList() { const apiParams = { ...cloneDeep(orderListParams), searchParams: { ...cloneDeep(orderListParams.searchParams) }, } getList(apiParams).then(res => { orderListTable.value = res.data.data }) } // 分页大小改变 const handleSizeChange = val => { orderListParams.size = val getTableList() } // 页码改变 const handleCurrentChange = val => { orderListParams.current = val getTableList() } // 传参查询条件 const searchClick = params => { orderListParams.current = 1 orderListParams.size = 10 orderListParams.searchParams = params getTableList() } const refreshGetJobList = () => { orderListParams.current = 1 orderListParams.size = 10 getTableList() } </script> <style lang="scss" scoped> .order-log-update { height: 0; flex: 1; margin: 0 10px 10px 10px; background-color: #ffffff; padding: 10px 20px; border-radius: 5px; display: flex; flex-direction: column; // 表格 .task-table { height: 0; flex: 1; margin-top: 18px; overflow: auto; } .btnItem { height: 27px; line-height: 27px; border-radius: 0px 0px 0px 0px; border: 1px solid #51a8ff; font-family: Segoe UI, Segoe UI; font-weight: 400; font-size: 14px; color: #1C5CFF; padding: 0 10px; display: inline-block; margin-right: 10px; cursor: pointer; &.turnBack { border: 1px solid #ffa500; color: #ffa500; } &.cancelTask { border: 1px solid #00AA2D; color: #00AA2D; } } // 分页 :deep(.custom-header th.el-table__cell) { color: rgba(0, 0, 0, 0.85); } :deep(.el-table td.el-table__cell) { color: #606266; } :deep(.el-pagination) { display: flex; justify-content: right; } :deep(.el-pagination button) { background: center center no-repeat none !important; color: #8eb8ea !important; } } </style> src/views/tickets/orderLogCopy.vue
New file @@ -0,0 +1,1666 @@ <template> <basic-container> <el-tabs v-model="activeTab" @tab-click="handleTabChange"> <el-tab-pane v-for="tab in filteredTabs" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name"> <basic-main-content> <!-- 查询条件筛选栏 --> <div class="ztzf-form-search"> <el-form :model="filters" inline> <el-row :gutter="24"> <el-col :span="4"> <el-form-item label="关键字:"> <el-input v-model="filters.key_word" placeholder="编号/名称/内容/姓名" clearable @keyup.enter="handleSearch" /> </el-form-item> <!-- <div class="search-bar-box-item">--> <!-- <el-select placeholder="请选择所属单位" v-model="filters.create_dept" clearable>--> <!-- <el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" />--> <!-- </el-select>--> <!-- </div>--> </el-col> <el-col :span="8"> <el-form-item label="选择日期:"> <el-date-picker @change="handleSearch" v-model="filters.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="关联航线:"> <el-select @change="handleSearch" v-model="filters.file_id" placeholder="请选择关联航线" filterable clearable> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="关联算法:"> <el-select @change="handleSearch" v-model="filters.ai_types" placeholder="关联算法" clearable> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> <!-- <div class="search-bar-box-item"> <el-select v-model="filters.type" placeholder="请选择工单类型" clearable> <el-option v-for="item in types" :key="item.dictValue" :label="item.dictValue" :value="item.dictKey" /> </el-select> </div> --> </el-col> <el-col :span="4"> <el-form-item label="工单状态:"> <el-select @change="handleSearch" v-model="filters.status" placeholder="请选择工单状态" clearable :disabled="activeTab !== 'all'"> <el-option v-for="item in statuses" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="24"> <el-col :span="8"> <el-form-item label="工单周期:"> <el-date-picker @change="handleSearch" v-model="filters.cycleDateRange" type="daterange" range-separator="至" start-placeholder="工单周期开始日期" end-placeholder="工单周期结束日期" :default-value="datePickerDefaultVal" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="选择频次:"> <el-select @change="handleSearch" v-model="filters.rep_fre_type" placeholder="请选择频次" clearable> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="执行时间:"> <el-time-picker @change="handleSearch" v-model="filters.deal_time" placeholder="请选择执行时间" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </el-form-item> </el-col> <el-col :span="4"> <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索 </el-button> <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button> </el-col> </el-row> </el-form> </div> <!-- 表格部分 --> <avue-crud class="ztzf-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page" ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange" @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name"> <template #job_info_num="{ row }"> <el-tooltip-copy :content="row.job_info_num" :showCopyText="true" textAlign="left"> {{ row.job_info_num }} </el-tooltip-copy> </template> <template #name="{ row }"> <el-tooltip-copy :content="row.name" :showCopyText="true" textAlign="left"> {{ row.name }} </el-tooltip-copy> </template> <template #menu-left> <el-button v-if="hasAddBtnPermission() && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus" @click="handleAdd">新建工单 </el-button> <el-button type="success" plain icon="el-icon-download" @click="exportData">导出 </el-button> </template> <template #menu="{ row }"> <div class="menu-custom-box"> <template v-if="row.status == 1"> <el-button v-if="hasPaddingBtnPermission()" type="text" icon="el-icon-view" @click="handleCheckDetail(row)">审核 </el-button> </template> <!-- v-if=" (userInfo.user_id == row.create_user || hasRecallPaddingBtnPermission()) && row.status == 1 " --> <template v-if="row.status == 1"> <!--待审核状态--> <el-button type="text" icon="el-icon-warning" v-if="hasRecallPaddingBtnPermission()" @click="orderLogRecall(row.id)">撤回 </el-button> <!-- <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button> --> </template> <!--已驳回--> <template v-if="row.status == 2"> <el-button type="text" icon="el-icon-warning" @click="rejectDetail(row.id)">驳回原因 </el-button> <el-button v-if="userInfo.user_id == row.create_user" type="text" icon="el-icon-view" @click="handleViewDetail(row)">编辑 </el-button> <el-button v-else type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情 </el-button> </template> <!-- 已通过 --> <template v-if="row.status == 3"> <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情 </el-button> </template> <!--草稿--> <template v-if="row.status == 0"> <el-button type="text" icon="el-icon-edit" @click="handleViewDetail(row)">编辑 </el-button> <el-button type="text" icon="el-icon-position" @click="userPublishPush(row.id)">发布 </el-button> <el-button type="text" icon="el-icon-delete" @click="deleteOrderLog(row.id)">删除 </el-button> </template> </div> </template> <template #status="{ row }"> <el-tag :type="getStatusTagType(row.status)">{{ mapStatus(row.status) }}</el-tag> </template> <template #keyData="{ row }"> <el-tooltip :content="row.address" placement="top" effect="light"> <span>{{ row.keyData }}</span> </el-tooltip> </template> </avue-crud> </basic-main-content> </el-tab-pane> </el-tabs> <!-- 新建工单对话框 --> <el-dialog class="ztzf-dialog-mange" v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false" @close="resetForm"> <el-form :model="form" :rules="rules" ref="testform" label-width="100px"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="form.name" placeholder="请输入工单名称" maxlength="100" show-word-limit></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" filterable @change="getFlyingNestBy"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="!device_sns.length"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" /> </el-form-item> </el-col> <el-col :span="4"> <div class="flex"> <div class="flex-1"> <el-select v-model="form.rep_fre_type" placeholder="请选择频次"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </div> <div class="flex-1"> <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </div> </div> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="dialogVisible" ref="MapContainer"></map-container> </el-col> </el-row> <!-- <el-row>--> <!-- <div class="add-box-btns">--> <!-- <el-button type="danger" @click="submitForm(1)">发起</el-button>--> <!-- <el-button type="primary" @click="submitForm(0)">存草稿</el-button>--> <!-- <el-button @click="dialogVisible = false">取消</el-button>--> <!-- </div>--> <!-- </el-row>--> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="danger" @click="submitForm(1)" icon="el-icon-position">发起</el-button> <el-button type="primary" @click="submitForm(0)" icon="el-icon-document-add">存草稿</el-button> <el-button @click="dialogVisible = false" icon="el-icon-circle-close">取消</el-button> </div> </template> </el-dialog> <!-- 工单详情对话框 --> <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisible" :title="detailTitle" width="70%" :close-on-click-modal="false" @close="resetForm"> <div class="event-title-center">{{ form.name }}</div> <el-form :model="form" :rules="rules" ref="testform" label-width="100px"> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles" v-if="filters.status !== '0'"> <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }" :key="index" class="step-title"> {{ record.status_str }} </div> </div> <!-- Element Steps 组件 --> <el-steps :active="form.active" align-center class="custom-steps" v-if="filters.status !== '0'"> <el-step v-for="(record, index) in form.record_list" :key="index"> <template #description> <span class="step-description" style="position: relative; display: inline-block"> {{ record.user_name }} </span> <span class="step-timer"> {{ record.interval_time_str }} </span> <div class="step-description"> {{ record.create_time_str }} </div> </template> </el-step> </el-steps> </div> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="form.name" placeholder="请输入工单名称" :disabled="detailTitle === '工单详情'"></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy" filterable :disabled="detailTitle === '工单详情'"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number :disabled="detailTitle === '工单详情'" v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="detailTitle === '工单详情'"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple :disabled="detailTitle === '工单详情'"> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12" prop="statusStr"> <el-form-item label="当前状态"> {{ mapStatus(form.status) }} </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" :disabled="detailTitle === '工单详情'" /> </el-form-item> </el-col> <el-col :span="3"> <el-select v-model="form.rep_fre_type" placeholder="请选择频次" :disabled="detailTitle === '工单详情'"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </el-col> <el-col :span="3"> <el-time-picker class="timeStyle" :style="{ width: pxToRem(140) }" v-model="form.deal_time" prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" :disabled="detailTitle === '工单详情'" /> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit :disabled="detailTitle === '工单详情'"></el-input> </el-form-item> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="detailVisible" ref="MapContainer"></map-container> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="danger" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <el-button type="primary" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(0)" icon="el-icon-plus">保存 </el-button> <!-- <el-button type="primary" v-if="form.status == 0 || userInfo.user_id == form.create_user" @click="submitForm(0)">保存</el-button> --> <el-button type="primary" v-if="form.status == 1 && hasPaddingBtnPermission()" @click="orderLogPass(form.id)">通过 </el-button> <el-button type="danger" v-if="form.status == 1 && hasRejectionBtnPermission()" @click="orderLogReject(form.id)">驳回 </el-button> </div> </template> </el-dialog> <!-- 工单详情 --> <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisibleCopy" title="工单详情" width="70%" :close-on-click-modal="false" @close="resetForm"> <div class="event-title-center">{{ form.name }}</div> <el-form :model="form" ref="testform" label-width="100px"> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles" v-if="filters.status !== '0'"> <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }" :key="index" class="step-title"> {{ record.status_str }} </div> </div> <!-- Element Steps 组件 --> <el-steps :active="form.active" align-center class="custom-steps" v-if="filters.status !== '0'"> <el-step v-for="(record, index) in form.record_list" :key="index"> <template #description> <span class="step-description" style="position: relative; display: inline-block"> {{ record.user_name }} </span> <span class="step-timer"> {{ record.interval_time_str }} </span> <div class="step-description"> {{ record.create_time_str }} </div> </template> </el-step> </el-steps> </div> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单名称" prop="name"> <el-input v-model="form.name" placeholder="请输入工单名称" :disabled="true"></el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" :disabled="true"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number :disabled="true" v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="true"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple :disabled="true"> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12" prop="statusStr"> <el-form-item label="当前状态"> {{ mapStatus(form.status) }} </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled="true" /> </el-form-item> </el-col> <el-col :span="3"> <el-select v-model="form.rep_fre_type" placeholder="请选择频次" :disabled="true"> <el-option v-for="item in cycles" :key="item" :label="item" :value="item" /> </el-select> </el-col> <el-col :span="3"> <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" :disabled="true" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="2" placeholder="请输入工单内容" maxlength="255" show-word-limit :readonly="true" :disabled="true"></el-input> </el-form-item> </el-col> </el-row> <el-row :gutter="20" style="height: 400px"> <el-col :span="24"> <map-container v-if="detailVisibleCopy" ref="MapContainer"></map-container> </el-col> </el-row> </el-form> <template #footer> <div class="dialog-footer"> <el-button type="danger" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <!-- <el-button type="primary" v-if="form.status == 0 || userInfo.user_id == form.create_user" @click="submitForm(0)">保存</el-button> --> <el-button type="primary" v-if="form.status == 1 && hasPaddingBtnPermission()" @click="orderLogPass(form.id)" icon="el-icon-check">通过 </el-button> <el-button type="danger" v-if="form.status == 1 && hasRejectionBtnPermission()" @click="orderLogReject(form.id)" icon="el-icon-close">驳回 </el-button> <el-button @click="detailVisibleCopy = false" icon="el-icon-circle-close">取消</el-button> </div> </template> </el-dialog> </basic-container> </template> <script> import { pxToRem, pxToRemNum } from '@/utils/rem' import { calculateDefaultRange } from '@/utils/util' import { getList, saveUpdateOrderLog, orderLogDetails, orderLogRecall, orderLogReject, orderLogPass, orderLogExport, jobStatusNum, userPublish, deleteOrderLog, getWaylineMaxTerrainHeight } from '@/api/tickets/orderLog' import { getTicketInfo } from '@/api/tickets/ticket' import { getDictionaryByCode } from '@/api/system/dictbiz' import { getWaylineFileListByArea, newGetWorkspacesPage } from '@/api/resource/wayline' import { export_json_to_excel } from '@/utils/exportExcel' import { getFlyingNestBy } from '@/api/device/device' import { mapGetters } from 'vuex' import NProgress from 'nprogress' import { downloadXls } from '@/utils/util' import 'nprogress/nprogress.css' import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz' import dayjs from 'dayjs' import 'dayjs/locale/zh-cn' // 导入中文语言包 import weekday from 'dayjs/plugin/weekday' import elTooltipCopy from '@/components/ElTooltipCopy.vue' import { CircleClose, Promotion, Select } from '@element-plus/icons-vue' import { useStore } from 'vuex' import _ from 'lodash' // const store = useStore() dayjs.extend(weekday) dayjs.locale('zh-cn') // const lastHeight = computed(() => store.state.common.lastHeight) export default { components: { elTooltipCopy }, name: 'TicketPage', data () { return { activeTab: 'all', getlastHeight: 0, tabs: [ { label: '全部工单', name: 'all', value: null, count: 0 }, { label: '待审核', name: 'WAIT_AUDIT', value: 1, count: 0 }, { label: '已驳回', name: 'REJECTED', value: 2, count: 0 }, { label: '已通过', name: 'PASS', value: 3, count: 0 }, { label: '草稿', name: 'DRAFT', value: 0, count: 0 } ], filters: { key_word: '', create_dept: '', start_date: null, end_date: null, file_id: '', ai_types: [], status: '', dateRange: [] }, departments: [], types: [], device_sns: [], ai_types: [], wayLineList: [], detailTitle: '编辑工单', detailVisibleCopy: false, statuses: [ { label: '草稿', value: '0' }, { label: '待审核', value: '1' }, { label: '已驳回', value: '2' }, { label: '已通过', value: '3' } ], //周期 cycles: ['每天', '周一', '周二', '周三', '周四', '周五', '周六', '周末', '周天', '工作日'], tableData: [], option: { index: true, indexWidth: 66, indexLabel: '序号', indexFixed: true, align: 'left', border: true, stripe: false, searchShow: true, searchBtnText: '搜索', searchMenuSpan: 6, addBtnText: '新建工单', addBtn: false, viewBtn: false, editBtn: false, delBtn: false, cancelBtn: true, cancelBtnText: '取消', menu: true, menuWidth: 210, menuClassName: 'cur-menu', height: 'auto', calcHeight: 196, column: [ { label: '工单编号', prop: 'job_info_num', width: 150 }, { label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '工单状态', prop: 'status', width: 88, showOverflowTooltip: true }, { label: '所属单位', prop: 'dept_name', ellipsis: true, showOverflowTooltip: true }, { label: '创建时间', prop: 'create_time', width: 144, ellipsis: true, showOverflowTooltip: true }, { label: '已执行次数', prop: 'job_num', width: 70, ellipsis: true, showOverflowTooltip: true }, { label: '工单内容', prop: 'content', ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '关联航线', prop: 'wayline_name', ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '关联算法', prop: 'ai_type_str', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '关联机巢', prop: 'device_names', width: 100, ellipsis: true, overHidden: true, showOverflowTooltip: true }, { label: '创建人', prop: 'creator_name', width: 96, ellipsis: true, overHidden: true, showOverflowTooltip: true }, // { // label: '关联机巢', prop: 'device_names', width: 112, ellipsis: true, overHidden: true, // showOverflowTooltip: true, // }, { label: '工单周期频次', prop: '', width: 110, formatter: row => this.formatCycleTime(row), html: true, ellipsis: true, showOverflowTooltip: true // overHidden: true } ] }, page: { pageSize: 10, currentPage: 1, total: 0 }, dialogVisible: false, detailVisible: false, currentDetail: {}, form: {}, rules: { name: [ { required: true, message: '请输入工单名称', trigger: 'blur' }, { max: 100, message: '工单名称不能超过100个字', trigger: 'blur' } ], file_id: [{ required: true, message: '需要选择航线', trigger: 'change' }], device_sns: [{ required: true, message: '请选择机巢', trigger: 'change' }], content: [ { required: true, message: '请输入工单内容', trigger: 'blur' }, { max: 255, message: '工单内容不能超过255个字', trigger: 'blur' } ], date_range: [{ required: true, message: '请选择时间不能为空', trigger: 'blur' }], rep_fre_type: [{ required: true, message: '请选择周期', trigger: 'blur' }], deal_time: [{ required: true, message: '请选择执行时间', trigger: 'blur' }] }, loading: false, globalCounts: {}, mapLoaded: false, // 配置时间选择器默认配置 datePickerDefaultVal: calculateDefaultRange() } }, async created () { var response = await getDictionaryByCode('SF') var word_order_typeResponse = await getDictionaryByCode('WORK_ORDER_TYPE') this.ai_types = response.data.data['SF'] this.types = word_order_typeResponse.data.data['WORK_ORDER_TYPE'] //获取航线 this.asyncgetWaylineFileListByArea() const response2 = await getTicketInfo() const { dept_data, event_type, ai_type } = response2.data.data this.departments = dept_data.map(item => ({ label: item.dept_name, value: item.id })) }, mounted () { this.fetchTableData() const id = this.$route.query.id console.log('idddddd', id) if (id) { // 确保 id 存在 this.handleViewDetail({ id }) const find = this.$store.state.tags.bsTagList.find(i => i.path === '/tickets/orderLog') find && (find.query = {}) } else { console.error('工单ID不存在!') } }, computed: { CircleClose () { return CircleClose }, ...mapGetters(['userInfo', 'permission']), safeHeight () { const safeHeight = this.$store.state.common.safeHeight return Number(safeHeight) || 0 // 确保是数字类型 }, positionsArr () { const positionsArr = this.$store.state.common.positionsArr return positionsArr || [] // 确保是数字类型 }, filteredTabs () { // rejection_and_draft 权限控制“已驳回”和“草稿”tab // console.log(this.permission, '权限信息') // console.log(this.userInfo, '权限信息22') const canShowRejectAndDraft = this.permission?.rejection_and_draft === true return this.tabs .map(tab => { if (tab.name === 'DRAFT') { return { ...tab, isShow: canShowRejectAndDraft } } if (tab.name === 'REJECTED') { return { ...tab, isShow: canShowRejectAndDraft } } return { ...tab, isShow: true } }) .filter(tab => tab.isShow) } }, methods: { handleCellClick (row, column) { if (column.no === 1) { navigator.clipboard.writeText(row.job_info_num).then(() => { this.$message.success('复制工单编号成功') }) } else if (column.no === 2) { navigator.clipboard.writeText(row.name).then(() => { this.$message.success('复制工单名称成功') }) } else if (column.no === 4) { navigator.clipboard.writeText(row.dept_name).then(() => { this.$message.success('复制所属单位成功') }) } else if (column.no === 8) { navigator.clipboard.writeText(row.wayline_name).then(() => { this.$message.success('复制关联航线成功') }) } else if (column.no === 9) { navigator.clipboard.writeText(row.ai_type_str).then(() => { this.$message.success('复制关联算法成功') }) } }, disabledDate (time) { return time.getTime() < Date.now() - 8.64e7 // 86400000 = 24 * 60 * 60 * 1000 }, searchChange (params, done) { // console.log('searchChange') this.query = params this.parentId = '' this.page.currentPage = 1 this.onLoad(this.page, params) done() }, async onLoad (page, params = {}) { this.loading = true getList( null, this.page.currentPage, this.page.pageSize, Object.assign(params, this.query) ).then(res => { this.tableData = res.data.data this.loading = false this.selectionClear() }) }, selectionClear () { this.selectionList = [] this.$refs.crud.toggleSelection() }, async loadAMapScripts () { try { // await loadAMap(); // await loadAMapUI(); this.mapLoaded = true } catch (error) { console.error('Failed to load AMap scripts:', error) this.$message.error('地图加载失败,请检查网络或API Key配置') } }, formatCycleTime (row) { return `${row.cycle_time_value}` }, async fetchTableData () { this.loading = true try { let params = this.getQueryParam() // console.log('发送的参数:', params) const response = await getList(params, this.page.currentPage, this.page.pageSize) if (!response?.data?.data?.records) { throw new Error('接口返回数据格式不正确') } const { total, records } = response.data.data this.tableData = records.map(item => { return item }) // console.log('权限检查:', this.permission) this.page.total = total || 0 this.updateGlobalCounts() } catch (error) { // console.error('获取数据失败:', error) this.$message.error(error.message || '获取数据失败') this.tableData = [] this.page.total = 0 } finally { this.loading = false } }, getQueryParam () { const currentTab = this.tabs.find(tab => tab.name === this.activeTab) if (this.filters.dateRange) { // console.log( // 'this.formatDate(this.filters.dateRange[0])', // this.formatDate(this.filters.dateRange[0]) // ) } const params = { key_word: this.filters.key_word || undefined, create_dept: this.filters.create_dept || undefined, ai_types: this.filters.ai_types && this.filters.ai_types.length > 0 ? [this.filters.ai_types] : null, file_id: this.filters.file_id || undefined, status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value, dept_id: this.filters.department || undefined, create_start_date: this.filters.dateRange?.[0] ? this.formatDate(this.filters.dateRange[0]) : undefined, create_end_date: this.filters.dateRange?.[1] ? this.formatDate(this.filters.dateRange[1]).replace('00:00:00', '23:59:59') : undefined, start_date: this.filters.cycleDateRange?.[0] ? this.formatDate(this.filters.cycleDateRange[0]) : undefined, end_date: this.filters.cycleDateRange?.[1] ? this.formatDate(this.filters.cycleDateRange[1]).replace('00:00:00', '23:59:59') : undefined, rep_fre_type: this.filters.rep_fre_type || undefined, deal_time: this.filters.deal_time || undefined, current: this.page.currentPage, size: this.page.pageSize } return params }, sizeChange (pageSize) { this.page.pageSize = pageSize }, async submitForm (status) { this.$refs.testform.validate(async valid => { if (valid) { let dateRange = this.form.date_range // console.log('dateRange' + dateRange) this.form.begin_time = this.formatDate(dateRange[0]) this.form.end_time = this.formatDate(dateRange[1]) // 如果选中日期包含当前天,那么选中的时间点不能小于当前时间 if (this.form.deal_time) { const selectedDate = dayjs(this.form.date_range[0]).format('YYYY-MM-DD') const selectedTime = dayjs(selectedDate + ' ' + this.form.deal_time).toDate() const now = new Date() if (selectedDate === dayjs().format('YYYY-MM-DD') && selectedTime < now) { return this.$message.warning('任务时间不能小于当前时间') } } let checkedList = this.device_sns.filter(item => this.form.device_sns.includes(item.device_sn)); const maxItem = checkedList.reduce((max, item) => { return item.drone_height > max.drone_height ? item : max; }); // 返航绝对高度 let backHeight = this.form.rth_altitude // + maxItem.drone_height console.log(backHeight,'backHeight') // 安全起飞高度 let positions = this.positionsArr positions.unshift({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height }) positions.push({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height}) let resultHeight = 0 await getWaylineMaxTerrainHeight(positions).then(res => { resultHeight = this.safeHeight > (res.data.data + 30) ? this.safeHeight : (res.data.data + 30) }) let valueHeight = _.round(backHeight - resultHeight, 2) console.log('打印比较值', valueHeight) if (valueHeight < 0) { this.$confirm(`当前返航高度存在安全隐患,建议调整为${resultHeight}米以上后进行发布`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { const submitData = { ...this.form, status: status, ai_types: this.form.ai_types?.length ? this.form.ai_types : [] } await saveUpdateOrderLog(submitData) let id = this.form.id if (id) { this.$message.success('工单发布成功') } else { this.$message.success('工单创建成功') } this.dialogVisible = false this.detailVisible = false; (this.device_sns = []), (this.wayLineList = []), this.fetchTableData() }).catch(() => { }) } else { const submitData = { ...this.form, status: status, ai_types: this.form.ai_types?.length ? this.form.ai_types : [] } await saveUpdateOrderLog(submitData) let id = this.form.id if (id) { this.$message.success('工单发布成功') } else { this.$message.success('工单创建成功') } this.dialogVisible = false this.detailVisible = false; (this.device_sns = []), (this.wayLineList = []), this.fetchTableData() } } }) }, //驳回原因显示 async rejectDetail (id) { const response = await orderLogDetails(id) let data = response.data.data this.$confirm(data.remark, '驳回原因', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.form = { ...response.data.data } // this.detailVisible = true this.handleViewDetail(data) }) }, formatDate (date) { if (!date) return undefined const d = new Date(date) return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String( d.getDate() ).padStart(2, '0')} 00:00:00` }, mapStatus (status) { const statusTextMap = { 0: '草稿', 1: '待审核', 2: '已驳回', 3: '已通过' } return statusTextMap[status] || '未知状态' }, getStatusTagType (status) { const statusMap = { 1: 'warning', 2: 'info', 3: 'primary', 4: 'success', 5: 'danger' } return statusMap[status] || 'info' }, handleTabChange (tab) { this.activeTab = tab.props?.name || tab.name this.filters.status = '' if (tab.props?.name === 'WAIT_AUDIT') { this.filters.status = '1' } else if (tab.props?.name === 'REJECTED') { this.filters.status = '2' } else if (tab.props?.name === 'PASS') { this.filters.status = '3' } else if (tab.props?.name === 'DRAFT') { this.filters.status = '0' } this.page.currentPage = 1 this.fetchTableData() }, handleSearch () { this.page.currentPage = 1 this.fetchTableData() }, handleReset () { this.filters = { keyword: '', department: '', type: '', dateRange: [], status: '' } this.page.currentPage = 1 this.fetchTableData() }, currentChange (currentPage) { this.page.currentPage = currentPage }, async updateGlobalCounts () { const counts = { all: 0, DRAFT: 0, WAIT_AUDIT: 0, REJECTED: 0, PASS: 0 } var reponse = await jobStatusNum() // console.log('统计' + reponse.data.data) reponse.data.data.forEach(item => { const tab = this.tabs.find(t => t.name === item.dict_key) if (tab) { tab.count = item.num } }) }, handleAdd () { this.form = {} this.form.rth_altitude = 120 this.dialogVisible = true this.$nextTick(() => { if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { this.$refs.MapContainer.initAddEntity('initPosition') } }) //航线列表 this.asyncgetWaylineFileListByArea() }, resetForm () { this.form = { name: '', type: '', handler: '', algorithm: '', location: '', address: '', content: '', photos: [] } if (this.$refs.testform) { this.$refs.testform.resetFields() } }, formatLocation (location) { if (!Array.isArray(location)) { return '未知位置' } return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}` }, async handleViewDetail (row) { const response = await orderLogDetails(row.id) const data = response.data.data this.form = { ...data } // 更新机巢列表 this.device_sns = data.device_map_infos;// data.device_list; this.permission && (this.permission.order_log_review || this.permission.order_log_recall) && (data.status == 1 || data.status == 3 || (data.status == 2 && this.userInfo.user_id !== this.form.create_user)) ? (this.detailTitle = '工单详情') : (this.detailTitle = '编辑工单') this.detailVisible = true // 更新航线列表,追加 wayline_file_region_vo 数据 if (data.wayline_file_region_vo) { const newWayline = data.wayline_file_region_vo // 检查是否已经存在于 this.wayLineList 中 const isDuplicate = this.wayLineList.some( item => item.wayline_id === newWayline.wayline_id ) if (!isDuplicate) { this.wayLineList.push(newWayline) } } this.initMapLine(data.device_map_infos) const find = this.$store.state.tags.bsTagList.find(i => i.path === '/tickets/orderLog') find && (find.query = {}) }, async handleCheckDetail (row) { const response = await orderLogDetails(row.id) const data = response.data.data this.form = { ...data } // 更新航线列表,追加 wayline_file_region_vo 数据 // 更新航线列表,追加 wayline_file_region_vo 数据 if (data.wayline_file_region_vo) { const newWayline = data.wayline_file_region_vo // 检查是否已经存在于 this.wayLineList 中 const isDuplicate = this.wayLineList.some( item => item.wayline_id === newWayline.wayline_id ) if (!isDuplicate) { this.wayLineList.push(newWayline) } } // 更新机巢列表 this.device_sns = data.device_list this.detailVisibleCopy = true this.initMapLine(data.device_map_infos) }, //导出 async exportData () { this.$confirm('是否智飞工单数据?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { NProgress.start() let params = this.getQueryParam() orderLogExport(params).then(res => { downloadXls(res.data, `智飞工单${this.$dayjs().format('YYYY-MM-DD')}.xlsx`) NProgress.done() }) }) }, hasAddBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('this.permission.order_log_add :', this.permission.order_log_add) return this.permission && this.permission.order_log_add === true }, hasPaddingBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('权限检查:', this.permission) return this.permission && this.permission.order_log_review === true }, hasRecallPaddingBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('权限检查:', this.permission) // 智飞工单撤回 return this.permission && this.permission.order_log_recall === true }, //驳回按钮权限 hasRejectionBtnPermission () { // undefined 或 false 都返回 false,只有 true 返回 true // console.log('权限检查:', this.permission) return this.permission && this.permission.rejection_btn === true }, //自己点发布 userPublishPush (id) { this.$confirm('确定发布吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { let response = userPublish(id) this.$message.success('发布成功') this.fetchTableData() }) }, //删除 deleteOrderLog (id) { this.$confirm('确定删除吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { let response = deleteOrderLog(id) this.$message.success('删除') this.fetchTableData() }) }, refreshChange () { this.fetchTableData() }, //获取航线列表 async asyncgetWaylineFileListByArea (name) { const formData = { name: '', waylineType: undefined, current: 1, size: 99999999, descs: 'update_time' } var wayLineListResponse = await newGetWorkspacesPage(formData) this.wayLineList = wayLineListResponse.data.data.records this.initMapLine() }, initMapLine (infos = {}, cb = () => { }) { let currentLine = this.wayLineList.find(item => item.wayline_id == this.form.file_id) if (!currentLine) return this.$nextTick(() => { if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { this.$refs.MapContainer.initAddEntity('polyline', { url: `${import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key }?_t=${new Date().getTime()}`, type: currentLine.wayline_type, cb, infos }) } }) }, //可飞行机巢列表 async getFlyingNestBy (waylineId) { const that = this // 先清空 that.form.device_sns = [] that.device_sns = [] this.initMapLine({}, async polygon => { const currentLine = that.wayLineList.find(item => item.wayline_id === waylineId) //按照航线来 const params = { type: ['2', '4', '5'].includes(currentLine.wayline_type) ? 2 : 0, wayline_id: waylineId, polygon } var wayLineListResponse = await getFlyingNestBy(params) // 再赋值 that.device_sns = wayLineListResponse.data.data }) }, //撤回 async orderLogRecall (id) { this.$confirm('确定撤回则到草稿箱。', '是否撤回?', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { let reposne = await orderLogRecall(id) this.handleSearch() }) }, onLoad () { this.fetchTableData() }, /** * 通过 */ async orderLogPass (id) { let response = await orderLogPass(id) let data = response.data.data this.$message.success('审核通过') this.detailVisibleCopy = false this.detailVisible = false this.page.currentPage = 1 this.onLoad(this.page, this.query) // this.handleViewDetail() }, /** * 驳回 */ async orderLogReject (id) { this.$prompt('', '驳回原因', { confirmButtonText: '确定', cancelButtonText: '取消' }).then(async ({ value }) => { let response = await orderLogReject(id, value) let data = response.data.data this.$message.success('驳回成功') this.detailVisibleCopy = false // this.detailVisible = false this.page.currentPage = 1 this.onLoad(this.page, this.query) }) } }, watch: { // lastHeight(newVal, oldVal) { // console.log('lastHeight', newVal, oldVal); // this.getlastHeight = newVal; // }, tableData: { handler () { // this.updateTabCounts() }, deep: true } } } </script> <style lang="scss" scoped> //:deep(.el-input-number) { // width: 200px; //} //:deep(.el-form-item__label) { // width: 100px !important; //} ::v-deep(.el-tabs) { height: 100%; display: flex; flex-direction: column; .el-tabs__header { order: 1; } .el-tabs__content { order: 2; } .el-tabs__content { height: 0; flex: 1; display: flex; flex-direction: column; .el-tab-pane { height: 0; flex: 1; display: flex; flex-direction: column; } } } .filter-bar { // align-items: center; .search-bar-box { margin-bottom: 15px; display: flex; justify-content: space-between; gap: 8px; &-item { flex: 1; &> ::v-deep(.el-date-editor) { width: 100%; box-sizing: border-box; } } .search-btn { flex: 1; display: flex; justify-content: flex-end; } .flex-2 { min-width: 454px; } } } :deep(.el-pagination .el-select) { width: 128px !important; } .step-timer { position: absolute; left: 80%; top: 50%; transform: translateY(-50%); width: 100px; margin-left: 4px; color: #666; font-size: 12px; } :deep(.el-textarea__inner) { padding: 8px 12px; } :deep(.el-input__inner), :deep(.el-select), :deep(.el-select .el-input) { width: 100%; // 确保所有输入框和选择框宽度一致 } :deep(.el-textarea__inner) { resize: none; } .action-bar { margin-bottom: 16px; } .el-tabs { :deep(.el-tabs__content) { overflow: visible; } } .el-dialog { .el-form-item { margin-bottom: 10px; } } .el-upload { width: 100%; display: flex; flex-wrap: wrap; :deep(.el-upload-list__item) { transition: all 0.3s ease; } :deep(.el-upload-list__item:hover) { background-color: #f5f7fa; } } .el-upload__tip { font-size: 12px; color: #909399; margin-top: 7px; } .custom-steps-container { width: 100%; margin: 20px 0; } .steps-titles { display: flex; justify-content: space-between; margin-bottom: 16px; position: relative; } .step-title { text-align: center; flex: 1; font-size: 16px; color: #999; position: relative; padding-bottom: 10px; } .step-title.active { color: #409eff; font-weight: bold; } .custom-steps { margin-top: -20px; /* 向上移动与标题重叠 */ } .el-step__head { margin-top: 0; } .el-step__description { margin-top: 8px; padding: 0 20px; } .step-description { font-size: 14px; color: #666; line-height: 1.5; } .event-title-center { text-align: center; font-size: 20px; font-weight: bold; margin-bottom: 12px; color: #333; } .cur-menu { .menu-custom-box { display: flex; justify-content: center; flex-wrap: wrap; &> ::v-deep(.el-button) { flex: 1; margin-left: 0; margin-right: 10px; } } } .add-box-btns { width: 100%; display: flex; justify-content: center; } .safe-height { :deep(.el-form-item__label) { width: 120px !important; } :deep(.el-input-number) { width: 180px !important; } } .flex { display: flex; } .flex-1 { flex: 1; &> ::v-deep(div) { width: 100% !important; } } </style>