Selaa lähdekoodia

工单相关代码

miaofuhao 1 kuukausi sitten
vanhempi
commit
4bc9d5cc51

+ 234 - 0
src/components/jiayiList/index.vue

@@ -0,0 +1,234 @@
1
+<script setup lang="ts">
2
+import { ref } from 'vue'
3
+
4
+// 定义组件参数
5
+interface Props {
6
+  /** 列表数据 */
7
+  data: {
8
+    id: string
9
+    title: string
10
+    statusName: string
11
+    orderNo: string
12
+    userName: string
13
+    createTime: string
14
+  }[]
15
+}
16
+
17
+const props = defineProps<Props>()
18
+
19
+// 定义事件
20
+const emit = defineEmits<{
21
+  /** 刷新事件 */
22
+  (e: 'refresh'): void
23
+  /** 筛选事件 */
24
+  (e: 'filter'): void
25
+  /** 筛选确定事件 */
26
+  (e: 'filter-confirm'): void
27
+  /** 筛选取消事件 */
28
+  (e: 'filter-cancel'): void
29
+  /** 重置事件 */
30
+  (e: 'reset'): void
31
+  /** 滚动到底部事件 */
32
+  (e: 'load-more'): void
33
+}>()
34
+
35
+// 筛选框显示状态
36
+const showFilter = ref(false)
37
+// 下拉刷新状态
38
+const refresherTriggered = ref(false)
39
+
40
+// 处理刷新点击
41
+function handleRefresh() {
42
+  refresherTriggered.value = true
43
+  emit('refresh')
44
+  setTimeout(() => {
45
+    refresherTriggered.value = false
46
+  }, 1000)
47
+}
48
+
49
+// 处理筛选点击
50
+function handleFilter() {
51
+  showFilter.value = !showFilter.value
52
+  emit('filter')
53
+}
54
+
55
+// 处理筛选确定
56
+function handleFilterConfirm() {
57
+  showFilter.value = false
58
+  emit('filter-confirm')
59
+}
60
+
61
+// 处理筛选取消
62
+function handleFilterCancel() {
63
+  showFilter.value = false
64
+  emit('filter-cancel')
65
+}
66
+
67
+// 处理重置
68
+function handleReset() {
69
+  emit('reset')
70
+}
71
+
72
+// 跳转到工单详情页面
73
+function navigateToDetail(id: string) {
74
+  uni.navigateTo({
75
+    url: `/pages/archive/order/detail?id=${id}`
76
+  })
77
+}
78
+
79
+// 获取状态对应的样式
80
+function getStatusStyle(statusName: string) {
81
+  // 状态样式映射
82
+  const statusStyles: Record<string, { color: string, backgroundColor: string }> = {
83
+    '新工单': { color: '#4CAF50', backgroundColor: '#E8F5E9' },
84
+    '待处理': { color: '#FF9500', backgroundColor: '#FEF4EB' },
85
+    '已完结': { color: '#3498DB', backgroundColor: '#F0F7FF' },
86
+    '未审批': { color: '#FF4D4F', backgroundColor: '#FFF2F0' },
87
+    '已同意': { color: '#2ECC41', backgroundColor: '#E6F5E6' },
88
+    '已拒绝': { color: '#FF9800', backgroundColor: '#FFF5E6' },
89
+    '已废除': { color: '#9E9E9E', backgroundColor: '#F5F5F5' },
90
+    // 可以根据实际需求添加更多状态样式
91
+  }
92
+  
93
+  // 如果状态存在于映射中,返回对应的样式,否则返回默认样式
94
+  return statusStyles[statusName] || { color: '#666', backgroundColor: '#F5F5F5' }
95
+}
96
+</script>
97
+
98
+<template>
99
+  <view class="jiayi-list">
100
+    <!-- 列表卡片 -->
101
+    <scroll-view
102
+      scroll-y
103
+      class="scroll-container"
104
+      :refresher-enabled="true"
105
+      :refresher-triggered="refresherTriggered"
106
+      @refresherrefresh="handleRefresh"
107
+      @scrolltolower="emit('load-more')"
108
+    >
109
+      <wd-card
110
+        v-for="item in data"
111
+        :key="item.id"
112
+        :border="true"
113
+        :shadow="false"
114
+        style="margin: 16rpx 0;"
115
+        :style="{ backgroundColor: getStatusStyle(item.statusName).backgroundColor }"
116
+        @click="navigateToDetail(item.id)"
117
+      >
118
+        <view class="card-content">
119
+          <!-- 第一行:标题和状态 -->
120
+          <view class="flex justify-between items-center mb-3" style="height: 40rpx;">
121
+            <text class="font-bold text-lg text-black">{{ item.title }}</text>
122
+            <text :style="{ color: getStatusStyle(item.statusName).color }" class="text-sm">{{ item.statusName }}</text>
123
+          </view>
124
+          
125
+          <!-- 第二行:工单编号 -->
126
+          <view class="flex justify-between items-center mb-3">
127
+            <text class="text-sm text-gray-400">{{ item.orderNo }}</text>
128
+          </view>
129
+          
130
+          <!-- 第三行:用户名和创建时间 -->
131
+          <view class="flex justify-between items-center">
132
+            <text class="text-sm text-gray-600">{{ item.userName }}</text>
133
+            <text class="text-sm text-gray-600">{{ item.createTime }}</text>
134
+          </view>
135
+        </view>
136
+      </wd-card>
137
+      
138
+      <!-- 加载更多提示 -->
139
+      <view class="load-more" v-if="$parent.loadingMore">
140
+        <wd-loading size="small" type="ring" />
141
+        <text class="ml-2 text-sm text-gray-600">加载中...</text>
142
+      </view>
143
+      <view class="load-more" v-else-if="!$parent.hasMore && ($parent.listData && $parent.listData.length > 0)">
144
+        <text class="text-sm text-gray-600">没有更多数据了</text>
145
+      </view>
146
+    </scroll-view>
147
+    
148
+    <!-- 操作按钮 -->
149
+    <view class="action-buttons">
150
+      <view class="refresh-btn" @click="handleRefresh">
151
+        <wd-button type="icon" icon="refresh"  style="color: #fff;"></wd-button>
152
+      </view>
153
+      <view class="filter-btn" @click="handleFilter">
154
+        <wd-button type="icon" icon="filter" style="color: #fff;"></wd-button>
155
+      </view>
156
+    </view>
157
+    
158
+    <!-- 筛选框 -->
159
+     
160
+    <wd-popup
161
+      v-model="showFilter"
162
+      position="bottom"
163
+      round
164
+      :overlay="true"
165
+      :close-on-click-overlay="true"
166
+      overlay-class="popup-overlay"
167
+      class="filter-popup-container">
168
+      <view class="filter-popup" style="min-height: 400rpx;">
169
+        <slot name="filter-content" class="p-4">
170
+          <view class="text-lg font-medium mb-4">筛选条件</view>
171
+          <!-- 这里可以根据实际需求添加筛选条件 -->
172
+        </slot>
173
+        <view class="flex justify-end m-6">
174
+          <!-- <wd-button size="large" @click="handleFilterCancel" style="margin-right: 16rpx;">取消</wd-button> -->
175
+          <wd-button size="large" type="info" @click="handleReset" style="margin-right: 16rpx;">重置</wd-button>
176
+          <wd-button size="large" type="primary" @click="handleFilterConfirm">确定</wd-button>
177
+        </view>
178
+      </view>
179
+    </wd-popup>
180
+  </view>
181
+</template>
182
+
183
+<style scoped>
184
+.jiayi-list {
185
+  padding: 16rpx;
186
+  position: relative;
187
+  min-height: 100vh;
188
+  background-color: #ffffff;
189
+}
190
+
191
+.scroll-container {
192
+  height: 100vh;
193
+  padding-bottom: 200rpx;
194
+}
195
+
196
+.load-more {
197
+  display: flex;
198
+  align-items: center;
199
+  justify-content: center;
200
+  padding: 20rpx 0;
201
+  margin-bottom: 20rpx;
202
+}
203
+
204
+.card-content {
205
+  padding: 30rpx;
206
+}
207
+
208
+.action-buttons {
209
+  position: fixed;
210
+  bottom: 32rpx;
211
+  right: 32rpx;
212
+  display: flex;
213
+  flex-direction: column;
214
+  gap: 16rpx;
215
+  z-index: 9;
216
+}
217
+
218
+.refresh-btn,
219
+.filter-btn {
220
+  width: 80rpx;
221
+  height: 80rpx;
222
+  border-radius: 50%;
223
+  background-color: #3194F2;
224
+  display: flex;
225
+  align-items: center;
226
+  justify-content: center;
227
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
228
+}
229
+
230
+.filter-popup {
231
+  width: 100%;
232
+  max-height: 80vh;
233
+}
234
+</style>

+ 6 - 1
src/pages/archive/childMenu/index.vue

@@ -95,8 +95,13 @@ function handleMenuItemClick(menu: MenuItem) {
95 95
   }
96 96
   // 根据菜单ID执行相应操作
97 97
   if (menu.component) {
98
+    // 构建带参数的URL
99
+    let url = menu.component;
100
+    if (menu.perms) {
101
+      url += (url.includes('?') ? '&' : '?') + 'perms=' + encodeURIComponent(menu.perms);
102
+    }
98 103
     uni.navigateTo({
99
-      url: menu.component,
104
+      url: url,
100 105
     })
101 106
   } else {
102 107
     uni.showToast({

+ 247 - 5
src/pages/archive/order/comList.vue

@@ -1,14 +1,256 @@
1 1
 
2 2
 <script setup lang="ts">
3
+import { ref, onMounted, computed } from 'vue'
4
+import JiayiList from '@/components/jiayiList/index.vue'
5
+import { http } from '@/utils/request'
6
+
7
+// 接收参数
8
+const perms = ref('')
9
+
10
+// 动态页面标题
11
+const pageTitle = computed(() => {
12
+  switch (perms.value) {
13
+    case 'taskWaiting':
14
+      return '我的待办'
15
+    case 'taskFinish':
16
+      return '我的已办'
17
+    case 'taskCopyList':
18
+      return '我的抄送'
19
+    case 'delayedReview':
20
+      return '延时审核'
21
+    default:
22
+      return '公共订单列表'
23
+  }
24
+})
25
+
26
+// 定义页面配置
3 27
 definePage({
4
-  title: '公共订单列表'
28
+  title: '工单列表'
29
+})
30
+
31
+// 工单数据接口
32
+interface WorkOrder {
33
+  id: string
34
+  title: string
35
+  statusName: string
36
+  orderNo: string
37
+  userName: string
38
+  createTime: string
39
+}
40
+
41
+// 工单列表数据
42
+const listData = ref<WorkOrder[]>([])
43
+const loading = ref(false)
44
+const loadingMore = ref(false)
45
+const pageNum = ref(1)
46
+const hasMore = ref(true)
47
+
48
+// 筛选相关变量
49
+const filterStatus = ref<string[]>([])
50
+const filterTimeRange = ref<string[]>([])
51
+const filterOrderNo = ref('')
52
+
53
+// 使用 onLoad 生命周期接收参数
54
+function onLoad(options: any) {
55
+  if (options.perms) {
56
+    perms.value = options.perms as string
57
+    console.log('收到的 perms 参数:', perms.value)
58
+    // 设置页面标题
59
+    uni.setNavigationBarTitle({
60
+      title: pageTitle.value
61
+    })
62
+    // 加载数据
63
+    fetchWorkOrderList()
64
+  }
65
+}
66
+
67
+// 获取接口 URL 根据 perms 值
68
+function getApiUrl() {
69
+  switch (perms.value) {
70
+    case 'taskWaiting':
71
+      return '/workOrder/order/list/pending'
72
+    case 'taskFinish':
73
+      return '/workOrder/order/list/over'
74
+    case 'taskCopyList':
75
+      return '/workOrder/order/list/cc'
76
+    case 'delayedReview':
77
+      return '/workOrder/order/list/delay'
78
+    default:
79
+      return '/workOrder/order/list/pending'
80
+  }
81
+}
82
+
83
+// 获取工单列表数据
84
+async function fetchWorkOrderList(isLoadMore = false) {
85
+  if (isLoadMore) {
86
+    loadingMore.value = true
87
+  } else {
88
+    loading.value = true
89
+    pageNum.value = 1
90
+    hasMore.value = true
91
+  }
92
+  try {
93
+    // 构建请求参数
94
+    const params = {
95
+      pageSize: 10,
96
+      pageNum: pageNum.value,
97
+    }
98
+    // 添加筛选条件
99
+    if (filterStatus.value.length > 0) {
100
+      params.statusName = filterStatus.value.join(',')
101
+    }
102
+    
103
+    // 添加工单编号筛选
104
+    if (filterOrderNo.value) {
105
+      params.orderNo = filterOrderNo.value
106
+    }
107
+    
108
+    // 获取接口 URL
109
+    const apiUrl = getApiUrl()
110
+    
111
+    const response = await http.get<{ rows: any[], total: number }>(apiUrl, params)
112
+    const newData = response.rows || []   
113
+    
114
+    // 获取状态映射
115
+    function getStatusName(status: string | number, permsValue: string) {
116
+      if (permsValue === 'delayedReview') {
117
+        // 待办状态映射
118
+        const statusMap: Record<string | number, string> = {
119
+          0: '未审批',
120
+          1: '已同意',
121
+          2: '已拒绝',
122
+          3: '已废除'
123
+        }
124
+        return statusMap[status] || ''
125
+      } else {
126
+        // 其他状态映射
127
+        const statusMap: Record<string | number, string> = {
128
+          0: '待处理',
129
+          1: '处理中',
130
+          2: '已处理',
131
+          3: '已退回'
132
+        }
133
+        return statusMap[status] || ''
134
+      }
135
+    }
136
+    
137
+    // 转换数据格式为组件所需的格式
138
+    const formattedData = newData.map(item => ({
139
+      id: item.orderId,
140
+      title: item.ticketTypeName || '',
141
+      statusName: item.statusName || getStatusName(item.status, perms.value) || getStatusName(item.approveStatus, perms.value),
142
+      orderNo: item.orderNo || '',
143
+      userName:item.createBy ? item.createBy + "/" + (item.stationName || '') : '',
144
+      createTime: item.createTime || ''
145
+    }))
146
+    
147
+    if (isLoadMore) {
148
+      listData.value = [...(listData.value || []), ...formattedData]
149
+    } else {
150
+      listData.value = formattedData
151
+    }
152
+    // 检查是否还有更多数据
153
+    hasMore.value = (newData || []).length === 10
154
+    // 增加页码
155
+    pageNum.value++
156
+  }
157
+  catch (error) {
158
+    uni.showToast({
159
+      title: '获取工单列表失败',
160
+      icon: 'none',
161
+    })
162
+  }
163
+  finally {
164
+    loading.value = false
165
+    loadingMore.value = false
166
+  }
167
+}
168
+
169
+// 处理刷新
170
+function handleRefresh() {
171
+  console.log('刷新数据')
172
+  fetchWorkOrderList()
173
+}
174
+
175
+// 处理加载更多
176
+function handleLoadMore() {
177
+  console.log('加载更多数据')
178
+  if (!loadingMore.value && hasMore.value) {
179
+    fetchWorkOrderList(true)
180
+  }
181
+}
182
+
183
+// 处理筛选
184
+function handleFilter() {
185
+  console.log('筛选数据')
186
+  // 这里可以添加筛选数据的逻辑
187
+}
188
+
189
+// 处理筛选确定
190
+function handleFilterConfirm() {
191
+  console.log('筛选确定', filterStatus.value, filterTimeRange.value)
192
+  // 重新获取数据,应用筛选条件
193
+  fetchWorkOrderList()
194
+}
195
+
196
+// 处理筛选取消
197
+function handleFilterCancel() {
198
+  console.log('筛选取消')
199
+  // 这里可以添加筛选取消的逻辑
200
+}
201
+
202
+// 处理重置
203
+function handleReset() {
204
+  console.log('重置筛选条件')
205
+  // 清空所有筛选条件
206
+  filterStatus.value = []
207
+  filterTimeRange.value = []
208
+  filterOrderNo.value = ''
209
+}
210
+
211
+onMounted(() => {
212
+  // 页面加载时获取参数并加载数据
213
+  const pages = getCurrentPages()
214
+  const currentPage = pages[pages.length - 1]
215
+  if (currentPage.options.perms) {
216
+    perms.value = currentPage.options.perms as string
217
+    console.log('收到的 perms 参数:', perms.value)
218
+    // 设置页面标题
219
+    uni.setNavigationBarTitle({
220
+      title: pageTitle.value
221
+    })
222
+    fetchWorkOrderList()
223
+  }
5 224
 })
6 225
 </script>
7 226
 
8 227
 <template>
9
-  <view class="page">
10
-    <view class="flex items-center justify-center h-full">
11
-      <text class="text-lg">公共订单列表页面</text>
12
-    </view>
228
+  <view class="page min-h-screen bg-gray-100">
229
+    <JiayiList
230
+      :data="listData"
231
+      @refresh="handleRefresh"
232
+      @filter="handleFilter"
233
+      @filter-confirm="handleFilterConfirm"
234
+      @filter-cancel="handleFilterCancel"
235
+      @reset="handleReset"
236
+      @load-more="handleLoadMore"
237
+    >
238
+      <template #filter-content>
239
+        <view class="p-4">
240
+          <view class="text-lg font-medium mb-4">筛选条件</view>
241
+          <!-- 工单编号搜索 -->
242
+          <view class="mb-4">
243
+            <text class="block mb-2">工单编号</text>
244
+            <wd-input v-model="filterOrderNo" placeholder="请输入工单编号" />
245
+          </view>
246
+        </view>
247
+      </template>
248
+    </JiayiList>
13 249
   </view>
14 250
 </template>
251
+
252
+<style scoped>
253
+.page {
254
+  padding-bottom: 100rpx;
255
+}
256
+</style>

+ 549 - 0
src/pages/archive/order/detail.vue

@@ -0,0 +1,549 @@
1
+<script setup lang="ts">
2
+import { ref, onMounted, computed } from 'vue'
3
+import { http } from '@/utils/request'
4
+
5
+// 定义页面配置
6
+definePage({
7
+  title: '工单详情'
8
+})
9
+
10
+// 工单详情接口
11
+interface WorkOrderDetail {
12
+  id: string
13
+  orderNo: string
14
+  statusName: string
15
+  status: string
16
+  createBy: string
17
+  ticketTypeName: string
18
+  createTime: string
19
+  stationName?: string
20
+  contact?: string
21
+  phone?: string
22
+  content?: string
23
+  attachments?: string
24
+}
25
+
26
+// 流程信息接口
27
+interface FlowInfo {
28
+  instanceId: string
29
+}
30
+
31
+// 工单流程记录接口
32
+interface WorkOrderRecord {
33
+  id: string
34
+  orderId: string
35
+  operationContent: string
36
+  status: string
37
+  operatorName: string
38
+  operationTime: string
39
+  operationType: string
40
+  approveName?: string
41
+  workOrderProcessRecords?: Array<{
42
+    processContent: string
43
+    attachmentsUrl?: string
44
+  }>
45
+  workOrderDelay?: Array<{
46
+    delayReason: string
47
+    delayDuration: string
48
+    approveRemark: string
49
+    approveStatus: string
50
+    approveTime: string
51
+    attachmentsUrl?: string
52
+  }>
53
+  message?: string
54
+}
55
+
56
+// 页面数据
57
+const workOrderDetail = ref<WorkOrderDetail>({
58
+  id: '',
59
+  orderNo: '',
60
+  statusName: '',
61
+  status: '',
62
+  createBy: '',
63
+  ticketTypeName: '',
64
+  createTime: '',
65
+  stationName: '',
66
+  contact: '',
67
+  phone: '',
68
+  content: '',
69
+  attachments: ''
70
+})
71
+
72
+const workOrderRecords = ref<WorkOrderRecord[]>([])
73
+const loading = ref(true)
74
+const recordsLoading = ref(false)
75
+
76
+// 获取工单ID(从路由参数中获取)
77
+const getWorkOrderId = () => {
78
+  const pages = getCurrentPages()
79
+  const currentPage = pages[pages.length - 1]
80
+  return currentPage.options.id || ''
81
+}
82
+
83
+// 获取工单详情
84
+async function fetchWorkOrderDetail() {
85
+  const id = getWorkOrderId()
86
+  if (!id) return
87
+  
88
+  try {
89
+    loading.value = true
90
+    const response = await http.get<WorkOrderDetail>(`/workOrder/order/${id}`)
91
+    workOrderDetail.value = response || workOrderDetail.value
92
+    
93
+    // 获取工单流程记录
94
+    await fetchWorkOrderRecords(id)
95
+  } catch (error) {
96
+    console.error('获取工单详情失败:', error)
97
+    uni.showToast({
98
+      title: '获取工单详情失败',
99
+      icon: 'none'
100
+    })
101
+  } finally {
102
+    loading.value = false
103
+  }
104
+}
105
+
106
+// 获取工单流程记录
107
+async function fetchWorkOrderRecords(orderId: string) {
108
+  try {
109
+    recordsLoading.value = true
110
+    const response = await http.get<WorkOrderRecord[]>('/workOrderRecord/record/list', {
111
+      orderId: orderId
112
+    })
113
+    workOrderRecords.value = response.rows || []
114
+  } catch (error) {
115
+    console.error('获取工单流程记录失败:', error)
116
+    uni.showToast({
117
+      title: '获取工单流程记录失败',
118
+      icon: 'none'
119
+    })
120
+  } finally {
121
+    recordsLoading.value = false
122
+  }
123
+}
124
+
125
+// 复制工单编号
126
+function handleCopy(text: string) {
127
+  uni.setClipboardData({
128
+    data: text,
129
+    success: () => {
130
+      uni.showToast({
131
+        title: '复制成功',
132
+        icon: 'success'
133
+      })
134
+    }
135
+  })
136
+}
137
+
138
+// 获取状态样式
139
+function getStatusClass(statusName: string) {
140
+  switch (statusName) {
141
+    case '新工单':
142
+      return 'status-new'
143
+    case '待处理':
144
+      return 'status-pending'
145
+    case '已完结':
146
+      return 'status-completed'
147
+    default:
148
+      return 'status-default'
149
+  }
150
+}
151
+
152
+// 编辑按钮
153
+function handleEdit() {
154
+  console.log('编辑工单')
155
+}
156
+
157
+// 删除按钮
158
+function handleDelete() {
159
+  console.log('删除工单')
160
+}
161
+
162
+// 提交按钮
163
+function handleSubmit() {
164
+  console.log('提交工单')
165
+}
166
+
167
+// 页面加载时获取工单详情
168
+onMounted(() => {
169
+  fetchWorkOrderDetail()
170
+})
171
+</script>
172
+<template>
173
+  <view class="work-order-detail">
174
+    <!-- 标题区域 -->
175
+    <view class="detail-header">
176
+      <view class="flex justify-between items-center mb-3">
177
+        <view class="flex items-center gap-2">
178
+          <text class="text-2xl font-bold">{{ workOrderDetail.orderNo }}</text>
179
+          <wd-icon name="copy" size="20px" @click="handleCopy(workOrderDetail.orderNo)"></wd-icon>
180
+          <view :class="['status-tag', getStatusClass(workOrderDetail.statusName)]">
181
+            {{ workOrderDetail.statusName }}
182
+          </view>
183
+        </view>
184
+      </view>
185
+      
186
+      <view class="flex items-center gap-2 mb-4">
187
+        <view class="flex flex-col gap-2">
188
+          <view class="flex items-center gap-1 text-gray-500">
189
+            <wd-icon name="user" size="16px"></wd-icon>
190
+            创建人: {{ workOrderDetail.createBy + '/' + (workOrderDetail.stationName || '无') }}
191
+          </view>
192
+          <view class="flex items-center gap-1 text-gray-500">  
193
+            <wd-icon name="transfer" size="16px"></wd-icon>
194
+            流程分类: {{ workOrderDetail.ticketTypeName }}
195
+          </view>
196
+          <view class="flex items-center gap-1 text-gray-500">
197
+            <wd-icon name="time" size="16px"></wd-icon>
198
+            提交时间: {{ workOrderDetail.createTime }}
199
+          </view>
200
+        </view>
201
+      </view>
202
+    </view>
203
+    
204
+    <!-- 工单详情内容 -->
205
+    <view class="detail-content">
206
+      <view class="detail-title">
207
+        <text class="text-lg font-medium">工单详情</text>
208
+      </view>
209
+
210
+      <!-- 基本信息 -->
211
+      <wd-card :border="true" :shadow="false" style="margin-bottom: 16rpx;">
212
+        <view class="card-content">
213
+          <view class="info-item flex items-center py-2">
214
+            <text class="info-label w-40">工单类型:</text>
215
+            <text class="info-value flex-1">{{ workOrderDetail.ticketTypeName }}</text>
216
+          </view>
217
+          <view class="info-item flex items-center py-2">
218
+            <text class="info-label w-40">场站:</text>
219
+            <text class="info-value flex-1">{{ workOrderDetail.stationName || '无' }}</text>
220
+          </view>
221
+          <view class="info-item flex items-center py-2">
222
+            <text class="info-label w-40">联系人:</text>
223
+            <text class="info-value flex-1">{{ workOrderDetail.contact || '无' }}</text>
224
+          </view>
225
+          <view class="info-item flex items-center py-2">
226
+            <text class="info-label w-40">联系电话:</text>
227
+            <text class="info-value flex-1">{{ workOrderDetail.phone || '无' }}</text>
228
+          </view>
229
+          
230
+          <view class="info-item flex flex-col">
231
+            <text class="info-label w-40">提报内容:</text>
232
+            <text class="info-value flex-1">{{ workOrderDetail.content || '无' }}</text>
233
+          </view>
234
+          
235
+          <!-- 附件 -->
236
+          <view class="info-item flex flex-col mt-4">
237
+            <text class="info-label w-40">附件:</text>
238
+            <view v-if="workOrderDetail.attachmentsUrl.length > 0" class="flex flex-wrap gap-2">
239
+              <view 
240
+                v-for="(url, index) in workOrderDetail.attachmentsUrl" 
241
+                :key="index" 
242
+                class="w-20 h-20 cursor-pointer rounded overflow-hidden"
243
+              >
244
+                <wd-img :width="100" :height="100" :src="url" :enable-preview="true" />
245
+              </view>
246
+            </view>
247
+            <text v-else class="info-value">无</text>
248
+          </view>
249
+        </view>
250
+      </wd-card>
251
+       <view class="detail-title">
252
+        <text class="text-lg font-medium">工单流程</text>
253
+      </view>
254
+      <!-- 审批流程 -->
255
+      <wd-card :border="true" :shadow="false">
256
+        <view class="card-content">
257
+          <view v-if="workOrderRecords.length > 0">
258
+            <wd-steps :active="workOrderRecords.length - 1" vertical dot>
259
+              <wd-step 
260
+                v-for="(record, index) in workOrderRecords" 
261
+                :key="record.id"
262
+                :title="record.operationContent">
263
+                <template #description>
264
+                  <view class="mt-2 ml-4">
265
+                    <text class="text-sm font-medium">{{ `${record.operatorName} · ${record.operationTime}` }}</text>
266
+                    <!-- 处理内容 -->
267
+                    <view v-if="record.workOrderProcessRecords?.[0]?.processContent" class="mb-2 p-2 bg-gray-50 rounded">
268
+                      <text class="text-sm font-medium">处理内容:</text>
269
+                      <text class="text-sm">{{ record.workOrderProcessRecords[0].processContent }}</text>
270
+                      <!-- 处理内容附件 -->
271
+                      <view v-if="record.workOrderProcessRecords[0].attachmentsUrl" class="mt-2">
272
+                        <view class="flex flex-wrap gap-2">
273
+                          <view 
274
+                            v-for="(url, index) in record.workOrderProcessRecords[0].attachmentsUrl" 
275
+                            :key="index" 
276
+                            class="w-20 h-20 cursor-pointer rounded overflow-hidden">
277
+                            <wd-img :width="100" :height="100" :src="url" :enable-preview="true" />
278
+                          </view>
279
+                        </view>
280
+                      </view>
281
+                    </view>
282
+                    
283
+                    <!-- 延时申请 -->
284
+                    <view v-if="record.operationType === 'APPLY_DELAY' && record.workOrderDelay?.[0]" class="mb-2 p-2 bg-gray-50 rounded">
285
+                      <text class="text-sm font-medium">延时原因:</text>
286
+                      <text class="text-sm">{{ record.workOrderDelay[0].delayReason }}</text>
287
+                      <text v-if="record.workOrderDelay[0].delayDuration" class="text-sm block mt-1">
288
+                        延时时长:{{ record.workOrderDelay[0].delayDuration }} 小时
289
+                      </text>
290
+                      <!-- 延时申请附件 -->
291
+                      <view v-if="record.workOrderDelay[0].attachmentsUrl" class="mt-2">
292
+                        <view class="flex flex-wrap gap-2">
293
+                          <view 
294
+                            v-for="(url, index) in record.workOrderProcessRecords[0].attachmentsUrl" 
295
+                            :key="index" 
296
+                            class="w-20 h-20 cursor-pointer rounded overflow-hidden"
297
+                          >
298
+                            <wd-img :width="100" :height="100" :src="url" :enable-preview="true" />
299
+                          </view>
300
+                        </view>
301
+                      </view>
302
+                    </view>
303
+                    
304
+                    <!-- 延时审批 -->
305
+                    <view v-if="record.operationType === 'AUDIT_DELAY' && record.workOrderDelay?.[0]" class="mb-2 p-2 bg-gray-50 rounded">
306
+                      <text class="text-sm font-medium">审批意见:</text>
307
+                      <text class="text-sm">{{ record.workOrderDelay[0].approveRemark }}</text>
308
+                      <text v-if="record.workOrderDelay[0].approveStatus" class="text-sm block mt-1">
309
+                        审核状态:{{ record.workOrderDelay[0].approveStatus === '1' ? '同意' : '拒绝' }}
310
+                      </text>
311
+                      <text v-if="record.workOrderDelay[0].approveTime" class="text-sm block mt-1">
312
+                        新截止时间:{{ record.workOrderDelay[0].approveTime }}
313
+                      </text>
314
+                    </view>
315
+                    
316
+                    <!-- 其他消息 -->
317
+                    <view v-if="record.message && !['PROCESS', 'APPLY_DELAY', 'AUDIT_DELAY'].includes(record.operationType)" class="mb-2 p-2 bg-gray-50 rounded">
318
+                      <text class="text-sm">{{ record.message }}</text>
319
+                    </view>
320
+                  </view>
321
+                </template>
322
+              </wd-step>
323
+            </wd-steps>
324
+          </view>
325
+          <text v-else>暂无工单流程记录</text>
326
+        </view>
327
+      </wd-card>
328
+    </view>
329
+    
330
+    <!-- 底部悬浮按钮 -->
331
+    <view v-if="workOrderDetail.status === 'new'" class="bottom-actions">
332
+      <wd-button size="medium" custom-class="action-btn" @click="handleEdit">
333
+        编辑
334
+      </wd-button>
335
+      <wd-button type="error" size="medium" custom-class="action-btn" @click="handleDelete">
336
+        删除
337
+      </wd-button>
338
+      <wd-button size="medium" custom-class="action-btn" @click="handleSubmit">
339
+        提交
340
+      </wd-button>
341
+    </view>
342
+  </view>
343
+</template>
344
+
345
+
346
+
347
+<style scoped>
348
+.work-order-detail {
349
+  padding: 20rpx;
350
+  padding-bottom: 160rpx;
351
+  background-color: #ffffff;
352
+  min-height: 100vh;
353
+}
354
+
355
+.detail-header {
356
+  background-color: #fff;
357
+  padding: 20rpx;
358
+}
359
+
360
+.status-tag {
361
+  padding: 4rpx 12rpx;
362
+  border-radius: 16rpx;
363
+  font-size: 24rpx;
364
+}
365
+
366
+.status-new {
367
+  background-color: #E8F5E9;
368
+  color: #4CAF50;
369
+}
370
+
371
+.status-pending {
372
+  background-color: #FEF4EB;
373
+  color: #FF9500;
374
+}
375
+
376
+.status-completed {
377
+  background-color: #F0F7FF;
378
+  color: #3498DB;
379
+}
380
+
381
+.status-default {
382
+  background-color: #F5F5F5;
383
+  color: #666;
384
+}
385
+
386
+.avatar {
387
+  width: 56rpx;
388
+  height: 56rpx;
389
+  border-radius: 50%;
390
+  background-color: #3194F2;
391
+  display: flex;
392
+  align-items: center;
393
+  justify-content: center;
394
+  color: white;
395
+  font-size: 24rpx;
396
+}
397
+
398
+.detail-content {
399
+  background-color: #fff;
400
+  padding: 20rpx;
401
+}
402
+
403
+.card-content {
404
+  padding: 20rpx 0;
405
+}
406
+
407
+.grid {
408
+  display: grid;
409
+  grid-template-columns: 1fr 1fr;
410
+  gap: 20rpx;
411
+}
412
+
413
+.info-item {
414
+  display: flex;
415
+  gap: 16rpx;
416
+}
417
+
418
+.info-item.flex-col {
419
+  flex-direction: column;
420
+  align-items: flex-start;
421
+}
422
+
423
+.w-40 {
424
+  width: 160rpx;
425
+  flex-shrink: 0;
426
+}
427
+
428
+.info-label {
429
+  font-size: 26rpx;
430
+  color: #666;
431
+  font-weight: 500;
432
+}
433
+
434
+.info-value {
435
+  font-size: 28rpx;
436
+  color: #333;
437
+  line-height: 1.5;
438
+}
439
+
440
+.preview-container {
441
+  width: 100%;
442
+  height: 80vh;
443
+  display: flex;
444
+  align-items: center;
445
+  justify-content: center;
446
+}
447
+
448
+.flex {
449
+  display: flex;
450
+}
451
+
452
+.flex-wrap {
453
+  flex-wrap: wrap;
454
+}
455
+
456
+.justify-between {
457
+  justify-content: space-between;
458
+}
459
+
460
+.items-center {
461
+  align-items: center;
462
+}
463
+
464
+.gap-2 {
465
+  gap: 16rpx;
466
+}
467
+
468
+.gap-4 {
469
+  gap: 32rpx;
470
+}
471
+
472
+.mb-3 {
473
+  margin-bottom: 24rpx;
474
+}
475
+
476
+.mb-4 {
477
+  margin-bottom: 32rpx;
478
+}
479
+
480
+.mt-4 {
481
+  margin-top: 32rpx;
482
+}
483
+
484
+.text-2xl {
485
+  font-size: 36rpx;
486
+}
487
+
488
+.font-bold {
489
+  font-weight: bold;
490
+}
491
+
492
+.text-gray-500 {
493
+  color: #999;
494
+  font-size: 24rpx;
495
+}
496
+
497
+.detail-title {
498
+  margin: 0 0 16rpx 0;
499
+  padding-bottom: 16rpx;
500
+  border-bottom: 1rpx solid #e5e7eb;
501
+}
502
+
503
+.text-lg {
504
+  font-size: 32rpx;
505
+}
506
+
507
+.font-medium {
508
+  font-weight: 500;
509
+}
510
+
511
+.bg-primary {
512
+  background-color: #3194F2;
513
+}
514
+
515
+.size-20px {
516
+  width: 20px;
517
+  height: 20px;
518
+}
519
+
520
+.size-28px {
521
+  width: 28px;
522
+  height: 28px;
523
+}
524
+
525
+.rounded-full {
526
+  border-radius: 50%;
527
+}
528
+
529
+.text-white {
530
+  color: white;
531
+}
532
+
533
+.bottom-actions {
534
+  position: fixed;
535
+  bottom: 0;
536
+  left: 0;
537
+  right: 0;
538
+  background-color: #fff;
539
+  padding: 20rpx;
540
+  display: flex;
541
+  gap: 20rpx;
542
+  box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
543
+  z-index: 100;
544
+}
545
+
546
+.action-btn {
547
+  flex: 1;
548
+}
549
+</style>

+ 214 - 4
src/pages/archive/order/myDocument.vue

@@ -1,14 +1,224 @@
1 1
 
2 2
 <script setup lang="ts">
3
+import { ref, onMounted } from 'vue'
4
+import JiayiList from '@/components/jiayiList/index.vue'
5
+import { http } from '@/utils/request'
6
+
3 7
 definePage({
4 8
   title: '我的发起'
5 9
 })
10
+
11
+// 工单数据接口
12
+interface WorkOrder {
13
+  id: string
14
+  title: string
15
+  statusName: string
16
+  orderNo: string
17
+  userName: string
18
+  createTime: string
19
+}
20
+
21
+// 工单列表数据
22
+const listData = ref<WorkOrder[]>([])
23
+const loading = ref(false)
24
+const loadingMore = ref(false)
25
+const pageNum = ref(1)
26
+const hasMore = ref(true)
27
+
28
+// 获取工单列表数据
29
+async function fetchWorkOrderList(isLoadMore = false) {
30
+  if (isLoadMore) {
31
+    loadingMore.value = true
32
+  } else {
33
+    loading.value = true
34
+    pageNum.value = 1
35
+    hasMore.value = true
36
+  }
37
+  try {
38
+    // 构建请求参数
39
+    const params = {
40
+      pageSize: 10,
41
+      pageNum: pageNum.value,
42
+    }
43
+    
44
+    // 添加筛选条件
45
+    if (filterStatus.value.length > 0) {
46
+      params.statusName = filterStatus.value.join(',')
47
+    }
48
+    
49
+    // 添加新的筛选条件
50
+    console.log('filterStatusValue.value:', filterContent.value)
51
+    if (filterContent.value) {
52
+      params.content = filterContent.value
53
+    }
54
+    
55
+    if (filterStatusValue.value) {
56
+      params.status = filterStatusValue.value
57
+    }
58
+    
59
+    if (filterOrderNo.value) {
60
+      params.orderNo = filterOrderNo.value
61
+    }
62
+    
63
+    const response = await http.get<{ rows: any[], total: number }>('/workOrder/order/myList', params)
64
+    const newData = response.rows || []   
65
+    
66
+    // 转换数据格式为组件所需的格式
67
+    const formattedData = newData.map(item => ({
68
+      id: item.id,
69
+      title: item.ticketTypeName || '',
70
+      statusName: item.statusName || '',
71
+      orderNo: item.orderNo || '',
72
+      userName: item.createBy+"/"+item.stationName || '',
73
+      createTime: item.createTime || ''
74
+    }))
75
+    
76
+    if (isLoadMore) {
77
+      listData.value = [...(listData.value || []), ...formattedData]
78
+    } else {
79
+      listData.value = formattedData
80
+    }
81
+    // 检查是否还有更多数据
82
+    hasMore.value = (newData || []).length === 10
83
+    // 增加页码
84
+    pageNum.value++
85
+  }
86
+  catch (error) {
87
+    console.error('获取工单列表失败:', error)
88
+    uni.showToast({
89
+      title: '获取工单列表失败',
90
+      icon: 'none',
91
+    })
92
+  }
93
+  finally {
94
+    loading.value = false
95
+    loadingMore.value = false
96
+  }
97
+}
98
+
99
+// 处理刷新
100
+function handleRefresh() {
101
+  console.log('刷新数据')
102
+  fetchWorkOrderList()
103
+}
104
+
105
+// 处理加载更多
106
+function handleLoadMore() {
107
+  console.log('加载更多数据')
108
+  if (!loadingMore.value && hasMore.value) {
109
+    fetchWorkOrderList(true)
110
+  }
111
+}
112
+
113
+// 筛选相关变量
114
+const filterStatus = ref<string[]>([])
115
+const filterTimeRange = ref<string[]>([])
116
+const filterContent = ref('')
117
+const filterStatusValue = ref('')
118
+const filterOrderNo = ref('')
119
+
120
+// 工单状态字典数据
121
+const statusColumns = ref<Array<{ value: string; label: string }>>([])
122
+
123
+// 获取工单状态字典数据
124
+async function fetchStatusDict() {
125
+  try {
126
+    const response = await http.get<any[]>('/system/dict/data/type/ticket_status')
127
+    statusColumns.value = response.map(item => ({
128
+      value: item.dictValue,
129
+      label: item.dictLabel
130
+    }))
131
+  } catch (error) {
132
+    console.error('获取工单状态字典失败:', error)
133
+  }
134
+}
135
+
136
+// 处理状态选择确认
137
+function handleStatusConfirm({ value }: { value: string }) {
138
+  filterStatusValue.value = value
139
+}
140
+
141
+// 处理筛选
142
+function handleFilter() {
143
+  console.log('筛选数据')
144
+  // 这里可以添加筛选数据的逻辑
145
+}
146
+
147
+// 处理筛选确定
148
+function handleFilterConfirm() {
149
+  console.log('筛选确定', filterStatus.value, filterTimeRange.value)
150
+  // 重新获取数据,应用筛选条件
151
+  fetchWorkOrderList()
152
+}
153
+
154
+// 处理筛选取消
155
+function handleFilterCancel() {
156
+  console.log('筛选取消')
157
+  // 这里可以添加筛选取消的逻辑
158
+}
159
+
160
+// 处理重置
161
+function handleReset() {
162
+  console.log('重置筛选条件')
163
+  // 清空所有筛选条件
164
+  filterStatus.value = []
165
+  filterContent.value = ''
166
+  filterStatusValue.value = ''
167
+  filterOrderNo.value = ''
168
+  filterTimeRange.value = []
169
+}
170
+
171
+onMounted(() => {
172
+  fetchWorkOrderList()
173
+  fetchStatusDict()
174
+})
6 175
 </script>
7 176
 
8 177
 <template>
9
-  <view class="page">
10
-    <view class="flex items-center justify-center h-full">
11
-      <text class="text-lg">我的发起页面</text>
12
-    </view>
178
+  <view class="page min-h-screen bg-gray-100">
179
+    <JiayiList
180
+      :data="listData"
181
+      @refresh="handleRefresh"
182
+      @filter="handleFilter"
183
+      @filter-confirm="handleFilterConfirm"
184
+      @filter-cancel="handleFilterCancel"
185
+      @reset="handleReset"
186
+      @load-more="handleLoadMore"
187
+    >
188
+      <template #filter-content>
189
+        <view class="p-4">
190
+          <view class="text-lg font-medium mb-4">筛选条件</view>
191
+          
192
+          <!-- 工单内容搜索 -->
193
+          <view class="mb-4">
194
+            <text class="block mb-2">工单内容</text>
195
+            <wd-input v-model="filterContent" placeholder="请输入工单内容" />
196
+          </view>
197
+          
198
+          <!-- 工单状态下拉框 -->
199
+          <view class="mb-4">
200
+            <text class="block mb-2">工单状态</text>
201
+            <wd-picker 
202
+              :columns="[statusColumns]" 
203
+              v-model="filterStatusValue" 
204
+              @confirm="handleStatusConfirm"
205
+            />
206
+          </view>
207
+          
208
+          <!-- 工单编号搜索 -->
209
+          <view class="mb-4">
210
+            <text class="block mb-2">工单编号</text>
211
+            <wd-input v-model="filterOrderNo" placeholder="请输入工单编号" />
212
+          </view>
213
+          
214
+        </view>
215
+      </template>
216
+    </JiayiList>
13 217
   </view>
14 218
 </template>
219
+
220
+<style scoped>
221
+.page {
222
+  padding-bottom: 100rpx;
223
+}
224
+</style>