| | |
| | | <template #keyData="{ row }"> |
| | | <span>{{ row.address }}</span> |
| | | </template> |
| | | |
| | | <template #isReview="{ row }"> |
| | | <span>{{ showIsReviewText(row) }}</span> |
| | | </template> |
| | | </avue-crud> |
| | | </div> |
| | | </el-tab-pane> |
| | |
| | | import { getAdcodeObj } from '@/utils/disposeData' |
| | | export default { |
| | | name: "TicketPage", |
| | | data() { |
| | | data () { |
| | | return { |
| | | submitLoading: false, // 新增loading状态 |
| | | draftLoading: false, |
| | |
| | | // { label: "序号", prop: "id", width: 70 }, |
| | | { label: "工单编号", prop: "orderNumber", width: 120, overHidden: true, tooltip: true }, |
| | | { label: "工单名称", prop: "orderName", width: 150, overHidden: true, tooltip: true }, |
| | | { label: "所属单位", prop: "department", width: 100, overHidden: true, tooltip: true }, |
| | | { label: "所属单位", prop: "department", overHidden: true, tooltip: true }, |
| | | { label: "发起时间", prop: "startTime", width: 160 }, |
| | | { label: "关联算法", prop: "aiType", width: 150, overHidden: true, tooltip: true }, |
| | | { label: "工单类型", prop: "type", width: 130, overHidden: true, tooltip: true }, |
| | |
| | | { label: "创建人", prop: "creator", width: 100 }, |
| | | { label: "处理人", prop: "handler", width: 100 }, |
| | | { |
| | | slot: true, |
| | | hide: false, |
| | | label: "复核状态", |
| | | prop: "isReview", |
| | | width: 90, |
| | | formatter: (row) => { |
| | | return row.isReview === 1 ? '是' : '否' |
| | | } |
| | | }, |
| | | { label: "工单状态", prop: "status", slot: true, width: 90 }, |
| | | |
| | |
| | | datePickerDefaultVal: calculateDefaultRange(), |
| | | } |
| | | }, |
| | | created() { |
| | | created () { |
| | | let orderNumber = new URLSearchParams(window.location.search).get('orderNumber') |
| | | if (orderNumber) { |
| | | this.filters.keyword = orderNumber |
| | |
| | | console.log('permission.tickets_processing_btn', this.permission.tickets_processing_btn) |
| | | console.log('permission', this.permission.tickets_tab_pending) |
| | | }, |
| | | mounted() { |
| | | mounted () { |
| | | this.fetchTableData() |
| | | }, |
| | | computed: { |
| | | firstRowData() { |
| | | firstRowData () { |
| | | return this.tableData.length > 0 ? this.tableData[0] : null |
| | | }, |
| | | availableHandlers() { |
| | | availableHandlers () { |
| | | return this.form.department ? (this.departmentUsers[this.form.department] || []) : [] |
| | | }, |
| | | availableDispatchHandlers() { |
| | | availableDispatchHandlers () { |
| | | return this.dispatchForm.department ? (this.departmentUsers[this.dispatchForm.department] || []) : [] |
| | | }, |
| | | detailTableData() { |
| | | detailTableData () { |
| | | return [ |
| | | { |
| | | label: "工单名称", |
| | |
| | | }, |
| | | ] |
| | | }, |
| | | detailFields() { |
| | | detailFields () { |
| | | return [ |
| | | { label: "工单名称", value: this.currentDetail.orderName, editable: this.currentDetail.status === 0, type: "input" }, |
| | | { label: "关键任务", value: this.currentDetail.keyData, editable: false }, |
| | |
| | | { label: "工单内容", value: this.currentDetail.content, editable: this.currentDetail.status === 0, type: "textarea" }, |
| | | ] |
| | | }, |
| | | formattedDetailFields() { |
| | | formattedDetailFields () { |
| | | const fields = [ |
| | | { label: "工单名称", value: this.currentDetail.orderName }, |
| | | { label: "工单类型", value: this.currentDetail.type }, |
| | |
| | | } |
| | | return formattedFields |
| | | }, |
| | | dynamicFixedStatuses() { |
| | | dynamicFixedStatuses () { |
| | | // 直接使用接口返回的 stepInfos |
| | | return this.stepInfos.map(step => String(step.status)) |
| | | }, |
| | | ...mapGetters(['userInfo', 'permission']), |
| | | // 动态过滤tabs,保证isShow为true/false |
| | | filteredTabs() { |
| | | filteredTabs () { |
| | | // 统一处理权限,undefined视为false |
| | | const tabStatus = this.permission?.tickets_tab_status === true |
| | | const tabPending = this.permission?.tickets_tab_pending === true |
| | |
| | | return { ...tab, isShow: false } |
| | | }).filter(tab => tab.isShow) |
| | | }, |
| | | permissionList() { |
| | | permissionList () { |
| | | // 可根据实际后端权限key调整 |
| | | return { |
| | | addBtn: this.validData(this.permission.tickets_add, false), |
| | |
| | | reviewBtn: this.validData(this.permission.tickets_review, false), |
| | | } |
| | | }, |
| | | stepStatusList() { |
| | | stepStatusList () { |
| | | // “我发起的工单”tab用默认流程,其它tab用接口返回的stepInfos |
| | | if (this.activeTab === 'myTickets') { |
| | | if (this.workType === 1) { |
| | |
| | | // 其它tab直接用接口返回的stepInfos |
| | | return this.stepInfos.map(step => String(step.status)) |
| | | }, |
| | | |
| | | showIsReviewText () { |
| | | return (row) => { |
| | | if (['4', '5'].includes(row.status)) return row.isReview === 1 ? '是' : '否' |
| | | |
| | | return '/' |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | async loadAMapScripts() { |
| | | async loadAMapScripts () { |
| | | try { |
| | | const areaCode = this.userInfo.detail.areaCode |
| | | const subAreaCode = areaCode ? areaCode.substring(0, 6) : '' |
| | |
| | | this.$message.error('地图加载失败,请检查网络或API Key配置') |
| | | } |
| | | }, |
| | | async handleBatchApprove() { |
| | | async handleBatchApprove () { |
| | | try { |
| | | if (this.selections.length === 0) { |
| | | this.$message.warning('没有选中的工单') |
| | |
| | | this.$message.error(error.message || '审核失败,请稍后重试') |
| | | } |
| | | }, |
| | | async fetchDropdownData() { |
| | | async fetchDropdownData () { |
| | | try { |
| | | const response = await getTicketInfo() |
| | | const { dept_data, event_type, ai_type } = response.data.data |
| | |
| | | } |
| | | }, |
| | | |
| | | async fetchTableData() { |
| | | async fetchTableData () { |
| | | if (this.isFetching) return |
| | | this.isFetching = true |
| | | this.loading = true |
| | |
| | | } |
| | | }, |
| | | |
| | | async submitForm() { |
| | | async submitForm () { |
| | | if (this.submitLoading) return // 防止重复提交 |
| | | this.submitLoading = true |
| | | try { |
| | |
| | | } |
| | | }, |
| | | |
| | | async saveDraft() { |
| | | async saveDraft () { |
| | | if (this.draftLoading) return // 防止重复提交 |
| | | this.draftLoading = true |
| | | try { |
| | |
| | | } |
| | | }, |
| | | |
| | | handleLocationChange(val) { |
| | | handleLocationChange (val) { |
| | | let locationValue = val.value |
| | | console.log('handleLocationChange', locationValue) |
| | | if (locationValue && locationValue.length >= 2) { |
| | |
| | | } |
| | | }, |
| | | |
| | | formatDate(date) { |
| | | 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) { |
| | | mapStatus (status) { |
| | | const statusTextMap = { |
| | | "-1": "草稿", // 添加草稿状态 |
| | | "2": "待审核", |
| | |
| | | return statusTextMap[status] || "未知状态" |
| | | }, |
| | | |
| | | getStatusTagType(status) { |
| | | getStatusTagType (status) { |
| | | // 草稿不加颜色 |
| | | if (status === -1 || status === "-1") return '' |
| | | // 状态颜色映射 |
| | |
| | | return colorMap[String(status)] || '' |
| | | }, |
| | | |
| | | async fetchTabCounts() { |
| | | async fetchTabCounts () { |
| | | try { |
| | | // 判断是否有部门筛选 |
| | | let params = {} |
| | |
| | | } |
| | | }, |
| | | |
| | | handleTabChange(tab) { |
| | | handleTabChange (tab) { |
| | | this.activeTab = tab.props?.name || tab.name |
| | | |
| | | const isReview = this.findObject(this.option.column, 'isReview') |
| | | // 根据条件切换显隐 |
| | | isReview.hide = !['all', 'completed', 'myTickets'].includes(this.activeTab) |
| | | |
| | | this.handleReset() |
| | | this.page.currentPage = 1 |
| | | this.fetchTableData() |
| | | this.fetchTabCounts() // 切换 tab 时重新获取数据 |
| | | }, |
| | | |
| | | handleSearch() { |
| | | handleSearch () { |
| | | this.page.currentPage = 1 |
| | | this.fetchTableData() |
| | | this.fetchTabCounts() |
| | | }, |
| | | |
| | | handleReset() { |
| | | handleReset () { |
| | | this.filters = { |
| | | keyword: "", |
| | | department: "", |
| | |
| | | this.fetchTableData() |
| | | }, |
| | | |
| | | async handleCurrentChange(val) { |
| | | async handleCurrentChange (val) { |
| | | // 先更新页码 |
| | | this.page.currentPage = val |
| | | // 等待 DOM 更新后再请求数据 |
| | |
| | | await this.fetchTableData() |
| | | }, |
| | | |
| | | async sizeChange(val) { |
| | | async sizeChange (val) { |
| | | this.page.pageSize = val |
| | | this.page.currentPage = 1 // 重置到第一页 |
| | | await this.$nextTick() |
| | | await this.fetchTableData() |
| | | }, |
| | | |
| | | handleAdd() { |
| | | handleAdd () { |
| | | this.dialogVisible = true |
| | | }, |
| | | |
| | | resetForm() { |
| | | resetForm () { |
| | | this.form = { |
| | | name: '', |
| | | type: '', |
| | |
| | | } |
| | | }, |
| | | |
| | | formatLocation(location) { |
| | | formatLocation (location) { |
| | | if (!Array.isArray(location)) { |
| | | return '未知位置' |
| | | } |
| | | return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}` |
| | | }, |
| | | |
| | | async handleViewDetail(row) { |
| | | async handleViewDetail (row) { |
| | | |
| | | // 先设置workType,直接从row读取 |
| | | this.workType = row.work_type !== undefined ? Number(row.work_type) : 0 |
| | |
| | | }) |
| | | }, |
| | | |
| | | getStepHandler(status) { |
| | | getStepHandler (status) { |
| | | const step = this.stepInfos.find(step => step.status === status) |
| | | return step ? step.name : '' |
| | | }, |
| | | |
| | | getStepTime(status) { |
| | | getStepTime (status) { |
| | | const step = this.stepInfos.find(step => step.status === status) |
| | | // 如果 step 不存在或 step.time 为 0,返回 false |
| | | return step && step.time && step.time !== '0' ? step.time : false |
| | | }, |
| | | getStepCreateTime(status) { |
| | | getStepCreateTime (status) { |
| | | const step = this.stepInfos.find(step => step.status === status) |
| | | return step ? step.create_time : null |
| | | }, |
| | | getActiveStep() { |
| | | getActiveStep () { |
| | | // 步骤索引适配 |
| | | const arr = this.stepStatusList |
| | | const index = arr.indexOf(String(this.currentDetail.status)) |
| | | return index !== -1 ? index + 2 : 1 |
| | | }, |
| | | |
| | | openMap() { |
| | | openMap () { |
| | | const areaCode = this.userInfo.detail.areaCode |
| | | const subAreaCode = areaCode ? areaCode.substring(0, 6) : '' |
| | | getAdcodeObj(geoJson, 'adcode', subAreaCode) |
| | | this.$message.info("地图选址功能暂未实现") |
| | | }, |
| | | |
| | | handlePreview(file) { |
| | | handlePreview (file) { |
| | | this.$message.info(`预览图片:${file.name}`) |
| | | }, |
| | | |
| | | handleRemove(file) { |
| | | handleRemove (file) { |
| | | this.$message.info(`移除图片:${file.name}`) |
| | | }, |
| | | |
| | | refreshChange() { |
| | | refreshChange () { |
| | | if (this.isFetching) return |
| | | this.fetchTableData() |
| | | }, |
| | | |
| | | onLoad() { |
| | | onLoad () { |
| | | if (this.isFetching) return |
| | | this.fetchTableData() |
| | | }, |
| | | |
| | | async exportData() { |
| | | async exportData () { |
| | | // 修改导出方法,添加选中数据的处理 |
| | | try { |
| | | this.loading = true |
| | |
| | | } |
| | | }, |
| | | |
| | | handleDepartmentChange(deptId) { |
| | | handleDepartmentChange (deptId) { |
| | | this.form.handler = '' |
| | | }, |
| | | |
| | | handleDispatchDepartmentChange(deptId) { |
| | | handleDispatchDepartmentChange (deptId) { |
| | | this.dispatchForm.handler = '' // 清空处理人选择 |
| | | }, |
| | | |
| | | // 文件改变时的钩子 |
| | | handleFileChange(file, fileList) { |
| | | handleFileChange (file, fileList) { |
| | | // 保持最新的文件列表 |
| | | this.form.photos = fileList.map(item => ({ |
| | | ...item, |
| | |
| | | }, |
| | | |
| | | // 文件移除时的钩子 |
| | | handleUploadRemove(file, fileList) { |
| | | handleUploadRemove (file, fileList) { |
| | | this.form.photos = fileList |
| | | this.currentDetail.photos = fileList |
| | | }, |
| | | |
| | | // 上传前的验证 |
| | | beforeUpload(file) { |
| | | beforeUpload (file) { |
| | | const isImage = file.type.includes('image') |
| | | const isLt5M = file.size / 1024 / 1024 < 5 |
| | | |
| | |
| | | return true |
| | | }, |
| | | |
| | | async approveTicket() { |
| | | async approveTicket () { |
| | | if (this.approveLoading) return |
| | | this.approveLoading = true |
| | | try { |
| | |
| | | this.approveLoading = false |
| | | } |
| | | }, |
| | | async rejectTicket() { |
| | | async rejectTicket () { |
| | | if (this.rejectLoading) return |
| | | this.rejectLoading = true |
| | | try { |
| | |
| | | this.rejectLoading = false |
| | | } |
| | | }, |
| | | async submitProcessing() { |
| | | async submitProcessing () { |
| | | if (this.currentDetail.status !== 3) { |
| | | this.$message.warning('只有处理中状态的工单可以提交处理详情') |
| | | return |
| | |
| | | this.$message.error(error.message || '提交失败,请稍后重试') |
| | | } |
| | | }, |
| | | markAsCompleted() { |
| | | markAsCompleted () { |
| | | this.$message.success("工单已标记为完成") |
| | | }, |
| | | async completeTicket() { |
| | | async completeTicket () { |
| | | if (this.completeLoading) return |
| | | this.completeLoading = true |
| | | try { |
| | |
| | | this.completeLoading = false |
| | | } |
| | | }, |
| | | async approveAndDispatch() { |
| | | async approveAndDispatch () { |
| | | if (this.dispatchLoading) return |
| | | |
| | | // 添加必填项检查 |
| | |
| | | // 通过验证后,打开派发对话框 |
| | | this.dispatchDialogVisible = true |
| | | }, |
| | | hasProcessingBtnPermission() { |
| | | hasProcessingBtnPermission () { |
| | | // undefined 或 false 都返回 false,只有 true 返回 true |
| | | console.log('权限检查:', this.permission) |
| | | return this.permission && this.permission.tickets_processing_btn === true |
| | | }, |
| | | hasProcessedAndOverBtnPermission() { |
| | | hasProcessedAndOverBtnPermission () { |
| | | // undefined 或 false 都返回 false,只有 true 返回 true |
| | | console.log('权限检查:', this.permission) |
| | | return this.permission && this.permission.tickets_view_processedAndOver === true |
| | | }, |
| | | hasReviewBtnPermission() { |
| | | hasReviewBtnPermission () { |
| | | // undefined 或 false 都返回 false,只有 true 返回 true |
| | | console.log('权限检查:', this.permission) |
| | | return this.permission && this.permission.tickets_review_btn === true |
| | | }, |
| | | async submitDispatch() { |
| | | async submitDispatch () { |
| | | if (this.dispatchLoading) return |
| | | if (!this.currentDetail.orderName || !this.currentDetail.orderName.trim()) { |
| | | this.$message.warning('请填写工单名称') |
| | |
| | | } |
| | | }) |
| | | }, |
| | | async finalizeTicket() { |
| | | async finalizeTicket () { |
| | | if (this.finalizeLoading) return |
| | | this.finalizeLoading = true |
| | | try { |
| | |
| | | }, |
| | | |
| | | // 添加编辑方法 |
| | | handleEdit(row) { |
| | | handleEdit (row) { |
| | | console.log('编辑原始数据:', row) |
| | | |
| | | // 尝试从row.dept_id或通过部门名称查找对应的部门ID |
| | |
| | | }, |
| | | |
| | | // 添加删除方法 |
| | | handleDelete(row) { |
| | | handleDelete (row) { |
| | | this.$confirm('确认删除该工单?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | |
| | | }, |
| | | |
| | | // 添加选择变化处理方法 |
| | | handleSelectionChange(selection) { |
| | | handleSelectionChange (selection) { |
| | | this.selections = selection |
| | | console.log('已选择的行:', selection) |
| | | }, |
| | | |
| | | // 添加全选方法 |
| | | handleSelectAll(val) { |
| | | handleSelectAll (val) { |
| | | this.$refs.avueCrud.toggleSelection(val) |
| | | }, |
| | | |
| | | // 添加单行选择方法 |
| | | handleSelect(selection, row) { |
| | | handleSelect (selection, row) { |
| | | console.log('选中行变化:', selection, row) |
| | | }, |
| | | |
| | | // 如果需要手动选中某些行 |
| | | setSelection(rows) { |
| | | setSelection (rows) { |
| | | this.$nextTick(() => { |
| | | rows.forEach(row => { |
| | | this.$refs.avueCrud.toggleSelection(row, true) |
| | |
| | | }, |
| | | |
| | | // 清空选择 |
| | | clearSelection() { |
| | | clearSelection () { |
| | | this.$refs.avueCrud.clearSelection() |
| | | }, |
| | | |
| | | // 打开审核对话框 |
| | | openReviewDialog() { |
| | | openReviewDialog () { |
| | | if (this.selections.length === 0) { |
| | | this.$message.warning('请先选择要审核的工单') |
| | | return |
| | |
| | | }, |
| | | |
| | | // 更新当前审核图片 |
| | | updateCurrentReviewImage() { |
| | | updateCurrentReviewImage () { |
| | | // 修正索引范围 |
| | | if (!this.selections || this.selections.length === 0) { |
| | | this.currentReviewImage = '' |
| | |
| | | }, |
| | | |
| | | // 处理图片分页变化 |
| | | handleImagePageChange(page) { |
| | | handleImagePageChange (page) { |
| | | if (page > 0 && page <= this.selections.length) { |
| | | this.currentImageIndex = page |
| | | this.updateCurrentReviewImage() |
| | |
| | | }, |
| | | |
| | | // 批量审核通过 |
| | | async handleBatchApprove() { |
| | | async handleBatchApprove () { |
| | | try { |
| | | if (this.selections.length === 0) { |
| | | this.$message.warning('没有选中的工单') |
| | |
| | | } |
| | | }, |
| | | |
| | | cancleBatchReject() { |
| | | cancleBatchReject () { |
| | | this.reviewDialogVisible = false |
| | | this.selections = [] |
| | | this.currentImageIndex = 1 |
| | |
| | | this.fetchTableData() |
| | | }, |
| | | // 批量审核不通过 |
| | | async handleBatchReject() { |
| | | async handleBatchReject () { |
| | | try { |
| | | if (this.selections.length === 0) { |
| | | this.$message.warning('没有选中的工单') |
| | |
| | | }, |
| | | |
| | | // 获取所有图片列表用于预览 |
| | | getImageList() { |
| | | getImageList () { |
| | | return this.selections |
| | | .map(item => this.getPreviewUrl(item.photo_url)) |
| | | .filter(url => url) // 过滤掉空值 |
| | | }, |
| | | |
| | | // 处理图片点击 |
| | | handleImageClick() { |
| | | handleImageClick () { |
| | | // 图片点击事件由 el-image 的预览功能处理 |
| | | }, |
| | | |
| | | // 处理上一张图片 |
| | | handlePrevImage() { |
| | | handlePrevImage () { |
| | | if (this.currentImageIndex > 1) { |
| | | this.currentImageIndex-- |
| | | this.updateCurrentReviewImage() |
| | |
| | | }, |
| | | |
| | | // 处理下一张图片 |
| | | handleNextImage() { |
| | | handleNextImage () { |
| | | if (this.currentImageIndex < this.selections.length) { |
| | | this.currentImageIndex++ |
| | | this.updateCurrentReviewImage() |
| | |
| | | * @param {string} url 原图地址 |
| | | * @returns {string} 缩略图地址 |
| | | */ |
| | | getThumbUrl(url) { |
| | | getThumbUrl (url) { |
| | | if (!url) return '' |
| | | const lastDot = url.lastIndexOf('.') |
| | | if (lastDot === -1) return url |
| | |
| | | }, |
| | | |
| | | // 添加新方法:获取预览图地址 |
| | | getPreviewUrl(url) { |
| | | getPreviewUrl (url) { |
| | | if (!url) return '' |
| | | const lastDot = url.lastIndexOf('.') |
| | | if (lastDot === -1) return url |
| | |
| | | * 坐标转换方法处理 |
| | | * @param goWgs84 是否由国测转换到84,默认false----由84转换到国测 |
| | | */ |
| | | disposeLocation(goWgs84 = false, data) { |
| | | disposeLocation (goWgs84 = false, data) { |
| | | let lng, lat |
| | | if (goWgs84) { |
| | | lng = data.location?.[0] ? String(data.location[0]) : undefined |