Parcourir la source

移动端项目案例库

liuzhuo il y a 1 semaine
Parent
révision
b38fc7599f

+ 10
- 0
src/router/index.ts Voir le fichier

@@ -614,6 +614,16 @@ const router = createRouter({
614 614
 			name: '管理案例编辑',
615 615
 			component: () => import('@/view/knowledge/managerList.vue')
616 616
 		},
617
+		{
618
+			path: '/knowledge/project',
619
+			name: '项目案例库',
620
+			component: () => import('@/view/knowledge/project.vue')
621
+		},
622
+		{
623
+			path: '/projectList',
624
+			name: '项目案例库编辑',
625
+			component: () => import('@/view/knowledge/projectList.vue')
626
+		},
617 627
 	]
618 628
 })
619 629
 

+ 4
- 0
src/view/Home2.vue Voir le fichier

@@ -244,6 +244,10 @@
244 244
           <img src="../../public/images/zd.png" width="45rpx" />
245 245
           <span class="vanicon_text">管理案例</span>
246 246
         </van-grid-item>
247
+        <van-grid-item to="/knowledge/project">
248
+          <img src="../../public/images/zd.png" width="45rpx" />
249
+          <span class="vanicon_text">项目案例库</span>
250
+        </van-grid-item>
247 251
       </van-grid>
248 252
     </div>
249 253
 

+ 1
- 1
src/view/knowledge/accidentList.vue Voir le fichier

@@ -163,7 +163,7 @@ const showRule=ref(false);
163 163
 const distestType=ref(false)
164 164
 if (planInfo==1) {
165 165
   console.log(planInfo);
166
-  title = '修改事故案例'
166
+  title = '查看事故案例'
167 167
   fromVue.value= JSON.parse(route.query.data)
168 168
   if (!fromVue.value.fileId) {
169 169
     const newFileId = guid();

+ 363
- 0
src/view/knowledge/project.vue Voir le fichier

@@ -0,0 +1,363 @@
1
+<template>
2
+  <div class="h5-container">
3
+    <van-nav-bar title="项目案例" @click-left="onClickLeft" @click-right="handAdd">
4
+      <template #right>
5
+        <van-icon name="add" size="25" color="#000" />
6
+      </template>
7
+    </van-nav-bar>
8
+    
9
+    <van-search 
10
+      v-model="query.projectName" 
11
+      show-action 
12
+      placeholder="请输入项目名称" 
13
+      @search="onRefresh"
14
+      @cancel="handleClearSearch" 
15
+    />
16
+
17
+    <!-- 项目列表 -->
18
+    <van-pull-refresh v-model="isRefreshing" success-text="刷新成功" @refresh="onRefresh">
19
+      <van-list 
20
+        v-model:loading="isLoading" 
21
+        :finished="isFinished" 
22
+        finished-text="没有更多了" 
23
+        offset="200" 
24
+        @load="onLoad"
25
+      >
26
+        <div v-for="(item, idx) in resultData" :key="item.id">
27
+          <van-swipe-cell 
28
+            title-style="color: #007aff" 
29
+            style="height: 80px;" 
30
+            :ref="el => getSwipeCellRef(el, idx)"
31
+          >
32
+            <template #default>
33
+              <div class="swipe-cell-default">
34
+                <van-cell 
35
+                  style="min-height: 120px; padding: 0 0 0 0; display: flex; align-items: flex-start;" 
36
+                  @click="edits(item)"
37
+                >
38
+                  <template #title>
39
+                    <div class="cell-title">
40
+                      {{ item.projectName }}
41
+                    </div>
42
+                  </template>
43
+                  <template #label>
44
+                    <div>案例编号:{{ item.caseNumber }}</div>
45
+                    <div>案例类型:{{ item.caseType }}</div>
46
+                    <div>浏览量:{{ item.viewCount }}</div>
47
+                  </template>
48
+                </van-cell>
49
+                <div class="swipe-cell-default-icon">
50
+                  <van-icon 
51
+                    v-if="openStatus[idx]" 
52
+                    name="arrow-double-left" 
53
+                    @click.stop="openSwipe(idx)" 
54
+                  />
55
+                  <van-icon 
56
+                    v-else 
57
+                    name="arrow-double-right" 
58
+                    @click.stop="closeSwipe(idx)" 
59
+                  />
60
+                </div>
61
+              </div>
62
+            </template>
63
+
64
+            <template #right>
65
+              <van-button 
66
+                v-if="item.canDelete" 
67
+                square 
68
+                class="delete-button" 
69
+                text="删除" 
70
+                @click="handleDelete(item)" 
71
+              />
72
+            </template>
73
+          </van-swipe-cell>
74
+        </div>
75
+      </van-list>
76
+    </van-pull-refresh>
77
+  </div>
78
+</template>
79
+
80
+<script setup>
81
+import { ref, getCurrentInstance } from 'vue';
82
+import { useRouter } from 'vue-router';
83
+import { showDialog, showSuccessToast, showToast } from 'vant';
84
+
85
+const { proxy } = getCurrentInstance();
86
+const router = useRouter();
87
+
88
+const onClickLeft = () => {
89
+  history.back();
90
+};
91
+
92
+const query = ref({
93
+  caseNumber: '',
94
+  projectName: ''
95
+});
96
+
97
+const isRefreshing = ref(false);
98
+const isLoading = ref(false);
99
+const isFinished = ref(false);
100
+const currentPage = ref(1);
101
+const pageSize = ref(10);
102
+const totalRows = ref(0);
103
+const resultData = ref([]);
104
+const tableData = ref([]);
105
+
106
+const currentUserId = String(localStorage.getItem('userId'));
107
+
108
+// 获取列表数据
109
+const getTableData = async () => {
110
+  const url = '/sgsafe/Manager/queryProject';
111
+  const param = {
112
+    page: currentPage.value,
113
+    rows: pageSize.value,
114
+    params: JSON.stringify(query.value)
115
+  };
116
+  
117
+  const response = await proxy.$axios.get(url, param);
118
+  if (response.data.code == 0) {
119
+    tableData.value = response.data.data.records.map(item => ({
120
+      ...item,
121
+      canDelete: String(item.addId) === currentUserId
122
+    }));
123
+    console.log('列表数据', tableData.value);
124
+    totalRows.value = response.data.data.total;
125
+  } else {
126
+    showToast({
127
+      type: 'error',
128
+      message: '操作失败!' + response.data.msg
129
+    });
130
+  }
131
+};
132
+
133
+// 刷新
134
+const onRefresh = () => {
135
+  basicReset();
136
+  onLoad();
137
+};
138
+
139
+// 加载数据
140
+const onLoad = async () => {
141
+  if (isRefreshing.value) {
142
+    resultData.value = [];
143
+    currentPage.value = 1;
144
+    isRefreshing.value = false;
145
+  }
146
+
147
+  try {
148
+    await getTableData();
149
+
150
+    if (pageSize.value * currentPage.value < totalRows.value) {
151
+      resultData.value = [...resultData.value, ...tableData.value];
152
+      openStatus.value = new Array(resultData.value.length).fill(true);
153
+      currentPage.value++;
154
+    } else {
155
+      resultData.value = [...resultData.value, ...tableData.value];
156
+      openStatus.value = new Array(resultData.value.length).fill(true);
157
+      isFinished.value = true;
158
+    }
159
+
160
+    console.log('resultData', resultData.value);
161
+  } catch (error) {
162
+    console.log(error);
163
+    isFinished.value = true;
164
+  } finally {
165
+    isLoading.value = false;
166
+  }
167
+};
168
+
169
+// 重置列表
170
+const basicReset = () => {
171
+  isFinished.value = false;
172
+  isLoading.value = true;
173
+  currentPage.value = 1;
174
+  resultData.value = [];
175
+};
176
+
177
+// 清空搜索
178
+const handleClearSearch = () => {
179
+  query.value.projectName = '';
180
+  onRefresh();
181
+};
182
+
183
+// 新增
184
+const handAdd = () => {
185
+  router.push({ 
186
+    path: "/projectList",
187
+    query: { mark: -1 } 
188
+  });
189
+};
190
+
191
+// 编辑/查看
192
+const edits = async (row) => {
193
+  const currentUserId = localStorage.getItem('userId');
194
+  const addId = row.addId;
195
+  const isOwner = String(addId).trim().toLowerCase() === String(currentUserId).trim().toLowerCase();
196
+
197
+  // 更新浏览量
198
+  await updateViewCount(row);
199
+
200
+  router.push({ 
201
+    path: "/projectList",
202
+    query: {
203
+      mark: 1,
204
+      data: JSON.stringify(row),
205
+      readOnly: !isOwner ? 'true' : undefined
206
+    } 
207
+  });
208
+};
209
+
210
+// 更新浏览量
211
+const updateViewCount = async (item) => {
212
+  try {
213
+    const payload = { ...item };
214
+    payload.viewCount = String((Number(payload.viewCount) || 0) + 1);
215
+
216
+    const url = '/sgsafe/Manager/saveProject';
217
+    const param = {
218
+      json: JSON.stringify(payload)
219
+    };
220
+
221
+    const response = await proxy.$axios.post(url, param);
222
+    if (response.data.code === '0' || response.data.code === 0) {
223
+      const index = resultData.value.findIndex(data => data.id === item.id);
224
+      if (index !== -1) {
225
+        resultData.value[index].viewCount = payload.viewCount;
226
+      }
227
+    }
228
+  } catch (error) {
229
+    console.error('更新浏览量失败:', error);
230
+  }
231
+};
232
+
233
+// 删除
234
+const handleDelete = (item) => {
235
+  const currentUserId = localStorage.getItem('userId');
236
+  const addId = item.addId;
237
+  
238
+  if (!currentUserId || !addId || String(addId).trim() !== String(currentUserId).trim()) {
239
+    showToast({
240
+      type: 'warning',
241
+      message: '无权限删除!只能删除自己添加的案例。'
242
+    });
243
+    return;
244
+  }
245
+  
246
+  showDialog({
247
+    title: '删除确认',
248
+    message: `确定要删除项目"${item.projectName}"吗?删除后将无法恢复。`,
249
+    showCancelButton: true,
250
+    confirmButtonText: '确定删除',
251
+    cancelButtonText: '取消',
252
+  }).then(() => {
253
+    executeDelete(item);
254
+  }).catch(() => {
255
+    console.log('取消删除');
256
+  });
257
+};
258
+
259
+// 执行删除
260
+const executeDelete = (item) => {
261
+  const deleteData = { ...item };
262
+  const now = new Date();
263
+  const year = now.getFullYear();
264
+  const month = String(now.getMonth() + 1).padStart(2, '0');
265
+  const day = String(now.getDate()).padStart(2, '0');
266
+  const hours = String(now.getHours()).padStart(2, '0');
267
+  const minutes = String(now.getMinutes()).padStart(2, '0');
268
+  const seconds = String(now.getSeconds()).padStart(2, '0');
269
+  deleteData.cancelTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
270
+  deleteData.cancelFlag = '1';
271
+  
272
+  const url = '/sgsafe/Manager/saveProject';
273
+  const param = {
274
+    json: JSON.stringify(deleteData)
275
+  };
276
+  
277
+  proxy.$axios.post(url, param).then(response => {
278
+    if (response.data.code == '0' || response.data.code === 0) {
279
+      showSuccessToast("删除成功");
280
+      onRefresh();
281
+    } else {
282
+      showToast({
283
+        type: 'fail',
284
+        message: '删除失败:' + (response.data.msg || '未知错误')
285
+      });
286
+    }
287
+  }).catch(error => {
288
+    showToast({
289
+      type: 'fail',
290
+      message: '删除失败:网络错误'
291
+    });
292
+    console.error('删除失败:', error);
293
+  });
294
+};
295
+
296
+// 滑动单元格控制
297
+const openStatus = ref([]);
298
+const swipeCellRefs = ref([]);
299
+
300
+const getSwipeCellRef = (el, index) => {
301
+  if (el) {
302
+    swipeCellRefs.value[index] = el;
303
+  }
304
+};
305
+
306
+const openSwipe = (idx) => {
307
+  openStatus.value = new Array(resultData.value.length).fill(true);
308
+  if (idx >= 0 && idx < swipeCellRefs.value.length) {
309
+    openStatus.value[idx] = false;
310
+    swipeCellRefs.value[idx].open('right');
311
+  }
312
+};
313
+
314
+const closeSwipe = (idx) => {
315
+  if (idx >= 0 && idx < swipeCellRefs.value.length) {
316
+    openStatus.value[idx] = true;
317
+    swipeCellRefs.value[idx].close();
318
+  }
319
+};
320
+</script>
321
+
322
+<style scoped>
323
+.h5-container {
324
+  width: 100%;
325
+  padding: 5px;
326
+  box-sizing: border-box;
327
+}
328
+
329
+.cell-title {
330
+  display: -webkit-box;
331
+  -webkit-box-orient: vertical;
332
+  -webkit-line-clamp: 2;
333
+  overflow: hidden;
334
+  text-overflow: ellipsis;
335
+  line-height: 1.5;
336
+  max-height: calc(1.5em * 2);
337
+  font-size: 16px;
338
+  font-weight: bold;
339
+  color: #333;
340
+}
341
+
342
+.swipe-cell-default {
343
+  display: flex;
344
+  background-color: #ffffff;
345
+  justify-content: center;
346
+  align-items: center;
347
+}
348
+
349
+.swipe-cell-default-icon {
350
+  width: 60px;
351
+  display: flex;
352
+  justify-content: center;
353
+}
354
+
355
+.delete-button {
356
+  height: 100%;
357
+  border: none;
358
+  color: #ff0000;
359
+  background-image: url('@/assets/img/del.png');
360
+  background-size: auto 100%;
361
+  background-repeat: no-repeat;
362
+}
363
+</style>

+ 563
- 0
src/view/knowledge/projectList.vue Voir le fichier

@@ -0,0 +1,563 @@
1
+<script setup>
2
+import { getCurrentInstance, onMounted, ref, computed } from 'vue';
3
+import { useRoute, useRouter } from 'vue-router';
4
+import tools from '@/tools'
5
+const {
6
+  proxy
7
+} = getCurrentInstance()
8
+
9
+const projectDictList = ref([])
10
+const caseTypeColumns = ref([])
11
+const caseSourceColumns = ref([])
12
+
13
+// 定义生成编号的函数
14
+const generateCode = () => {
15
+  const now = new Date();
16
+  const year = now.getFullYear();
17
+  const month = String(now.getMonth() + 1).padStart(2, '0');
18
+  const day = String(now.getDate()).padStart(2, '0');
19
+  const formattedDate = `${year}${month}${day}`;
20
+  const hours = String(now.getHours()).padStart(2, '0');
21
+  const minutes = String(now.getMinutes()).padStart(2, '0');
22
+  const seconds = String(now.getSeconds()).padStart(2, '0');
23
+  const formattedTime = `${hours}${minutes}${seconds}`;
24
+  const sequenceNumber = Math.floor(Math.random() * 1000);
25
+  const paddedSequence = String(sequenceNumber).padStart(3, '0');
26
+  return `XMAL${formattedDate}${formattedTime}${paddedSequence}`;
27
+};
28
+
29
+// 获取字典数据
30
+const getProjectDicList = () => {
31
+  tools.dic.getDicList(['sgsafe_project_case_type', 'sgsafe_project_case_source']).then((response => {
32
+    console.log(JSON.stringify(response.data.data))
33
+    projectDictList.value = response.data.data
34
+    
35
+    caseTypeColumns.value = projectDictList.value.sgsafe_project_case_type?.map(item => ({
36
+      text: item.dicName,
37
+      value: item.dicCode
38
+    })) || [];
39
+    
40
+    caseSourceColumns.value = projectDictList.value.sgsafe_project_case_source?.map(item => ({
41
+      text: item.dicName,
42
+      value: item.dicCode
43
+    })) || [];
44
+    
45
+    console.log('案例类型:', caseTypeColumns.value)
46
+  }))
47
+}
48
+
49
+const caseTypeFlag = ref(false)
50
+const caseSourceFlag = ref(false)
51
+let title = '新增项目案例'
52
+
53
+/* 返回上一级页面 */
54
+const router = useRouter()
55
+const onClickLeft = () => {
56
+  router.go(-1)
57
+}
58
+
59
+const guid = () => {
60
+  function S4() {
61
+    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
62
+  }
63
+  return (S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4())
64
+}
65
+
66
+const route = useRoute()
67
+let planInfo = {}
68
+const isEdit = ref(route.query.mark === '1');
69
+const isReadOnly = ref(route.query.readOnly === 'true');
70
+const isCaseSubmitted = computed(() => isReadOnly.value && isEdit.value);
71
+const result = ref('')
72
+const fromVue = ref({})
73
+
74
+if (route.query.mark) {
75
+  planInfo = JSON.parse(route.query.mark)
76
+}
77
+
78
+
79
+// 新增模式
80
+if (planInfo == -1) {
81
+  const caseNumber = generateCode();
82
+  result.value = caseNumber;
83
+  
84
+  fromVue.value = {
85
+    caseNumber: caseNumber,
86
+    projectName: '',
87
+    caseType: '',
88
+    caseSource: '',
89
+    startTime: '',
90
+    endTime: '',
91
+    tags: '',
92
+    caseSummary: '',
93
+    highLights: '',
94
+    resultsValue: '',
95
+    fileId: caseNumber,
96
+    viewCount: '0',
97
+    downloadCount: '0'
98
+  };
99
+  
100
+}
101
+
102
+// 编辑模式
103
+if (planInfo == 1) {
104
+  title = '查看项目案例'
105
+  fromVue.value = JSON.parse(route.query.data)
106
+  
107
+  // 清理微秒格式
108
+  if (fromVue.value.startTime && String(fromVue.value.startTime).includes('.')) {
109
+    fromVue.value.startTime = String(fromVue.value.startTime).split('.')[0];
110
+  }
111
+  if (fromVue.value.endTime && String(fromVue.value.endTime).includes('.')) {
112
+    fromVue.value.endTime = String(fromVue.value.endTime).split('.')[0];
113
+  }
114
+  
115
+  // fileId 与 caseNumber 保持一致
116
+  if (!fromVue.value.fileId || fromVue.value.fileId !== fromVue.value.caseNumber) {
117
+    fromVue.value.fileId = fromVue.value.caseNumber;
118
+  }
119
+  result.value = fromVue.value.fileId;
120
+}
121
+
122
+// 时间选择器
123
+const showStartTimePicker = ref(false);
124
+const showEndTimePicker = ref(false);
125
+const currentStartDate = ref([2025, 1, 1])
126
+const currentEndDate = ref([2025, 1, 1])
127
+const currentStartTime = ref([0, 0, 0]);
128
+const currentEndTime = ref([0, 0, 0]);
129
+const minDate = ref(new Date(1900, 0, 1));
130
+const maxDate = ref(new Date(2100, 11, 31));
131
+
132
+const startDateOrTime = ref(false);
133
+const endDateOrTime = ref(false);
134
+
135
+// 开始时间选择
136
+const onStartDateConfirm = () => {
137
+  startDateOrTime.value = true;
138
+};
139
+
140
+const onConfirmStartDatetime = () => {
141
+  const year = currentStartDate.value[0];
142
+  const month = currentStartDate.value[1].toString().padStart(2, '0');
143
+  const day = currentStartDate.value[2].toString().padStart(2, '0');
144
+  const hours = currentStartTime.value[0].toString().padStart(2, '0');
145
+  const minutes = currentStartTime.value[1].toString().padStart(2, '0');
146
+  const seconds = currentStartTime.value[2].toString().padStart(2, '0');
147
+
148
+  fromVue.value.startTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
149
+  showStartTimePicker.value = false;
150
+  startDateOrTime.value = false;
151
+};
152
+
153
+const cancelStartDatePicker = () => {
154
+  showStartTimePicker.value = false;
155
+  startDateOrTime.value = false;
156
+};
157
+
158
+const cancelStartTimePicker = () => {
159
+  showStartTimePicker.value = false;
160
+  startDateOrTime.value = false;
161
+};
162
+
163
+// 结束时间选择
164
+const onEndDateConfirm = () => {
165
+  endDateOrTime.value = true;
166
+};
167
+
168
+const onConfirmEndDatetime = () => {
169
+  const year = currentEndDate.value[0];
170
+  const month = currentEndDate.value[1].toString().padStart(2, '0');
171
+  const day = currentEndDate.value[2].toString().padStart(2, '0');
172
+  const hours = currentEndTime.value[0].toString().padStart(2, '0');
173
+  const minutes = currentEndTime.value[1].toString().padStart(2, '0');
174
+  const seconds = currentEndTime.value[2].toString().padStart(2, '0');
175
+
176
+  fromVue.value.endTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
177
+  showEndTimePicker.value = false;
178
+  endDateOrTime.value = false;
179
+};
180
+
181
+const cancelEndDatePicker = () => {
182
+  showEndTimePicker.value = false;
183
+  endDateOrTime.value = false;
184
+};
185
+
186
+const cancelEndTimePicker = () => {
187
+  showEndTimePicker.value = false;
188
+  endDateOrTime.value = false;
189
+};
190
+
191
+// 标签选择
192
+const addTag = (item) => {
193
+  if (isCaseSubmitted.value) return;
194
+  
195
+  const valueToStore = item.value?.trim() || '';
196
+  if (!valueToStore) return;
197
+
198
+  const currentTags = fromVue.value.tags
199
+    ? fromVue.value.tags.split(',').map(t => t.trim()).filter(Boolean)
200
+    : [];
201
+
202
+  if (currentTags.includes(valueToStore)) return;
203
+
204
+  currentTags.push(valueToStore);
205
+  fromVue.value.tags = currentTags.join(', ');
206
+};
207
+
208
+/* 组织树选择 */
209
+import { showFailToast, showLoadingToast, showSuccessToast } from 'vant';
210
+
211
+// 保存
212
+const addEmergencyDrillPlan = async () => {
213
+  const loadingToast = showLoadingToast({
214
+    duration: 0,
215
+    message: '加载中',
216
+    forbidClick: true
217
+  })
218
+  
219
+  fromVue.value.fileId = result.value
220
+
221
+  var url = '/sgsafe/Manager/saveProject';
222
+  const params = {
223
+    json: JSON.stringify(fromVue.value)
224
+  }
225
+  proxy.$axios.post(url, params).then(res => {
226
+    if (res.data.code === 0 || res.data.code === '0') {
227
+      loadingToast.close()
228
+      showSuccessToast('保存成功')
229
+      onClickLeft()
230
+    } else {
231
+      loadingToast.close()
232
+      showFailToast('操作失败!' + res.data.msg)
233
+    }
234
+  }).catch(error => {
235
+    loadingToast.close()
236
+    showFailToast('保存失败: 网络错误')
237
+    console.error('保存失败:', error)
238
+  })
239
+}
240
+
241
+onMounted(() => {
242
+  getProjectDicList()
243
+  const today = new Date()
244
+  const year = today.getFullYear()
245
+  const month = today.getMonth() + 1
246
+  const day = today.getDate()
247
+  currentStartDate.value = [year, month, day]
248
+  currentEndDate.value = [year, month, day]
249
+  currentStartTime.value = [today.getHours(), today.getMinutes(), today.getSeconds()];
250
+  currentEndTime.value = [today.getHours(), today.getMinutes(), today.getSeconds()];
251
+  
252
+  // 如果是编辑模式且有已有的时间,解析并初始化
253
+  if (isEdit.value) {
254
+    if (fromVue.value.startTime) {
255
+      try {
256
+        const timeStr = fromVue.value.startTime;
257
+        const [datePart, timePart] = timeStr.split(' ');
258
+        if (datePart && timePart) {
259
+          const [year, month, day] = datePart.split('-').map(Number);
260
+          const [hours, minutes, seconds] = timePart.split(':').map(Number);
261
+          currentStartDate.value = [year, month, day];
262
+          currentStartTime.value = [hours || 0, minutes || 0, seconds || 0];
263
+        }
264
+      } catch (error) {
265
+        // 解析失败,使用默认值
266
+      }
267
+    }
268
+    
269
+    if (fromVue.value.endTime) {
270
+      try {
271
+        const timeStr = fromVue.value.endTime;
272
+        const [datePart, timePart] = timeStr.split(' ');
273
+        if (datePart && timePart) {
274
+          const [year, month, day] = datePart.split('-').map(Number);
275
+          const [hours, minutes, seconds] = timePart.split(':').map(Number);
276
+          currentEndDate.value = [year, month, day];
277
+          currentEndTime.value = [hours || 0, minutes || 0, seconds || 0];
278
+        }
279
+      } catch (error) {
280
+        // 解析失败,使用默认值
281
+      }
282
+    }
283
+  }
284
+})
285
+
286
+/* 文件上传 */
287
+import AttachmentS3 from '@/components/AttachmentS3.vue';
288
+
289
+const onSubmit = (values) => {
290
+  addEmergencyDrillPlan()
291
+}
292
+
293
+const onCaseTypeConfirm = ({ selectedOptions }) => {
294
+  caseTypeFlag.value = false;
295
+  fromVue.value.caseType = selectedOptions[0].text;
296
+};
297
+
298
+const onCaseSourceConfirm = ({ selectedOptions }) => {
299
+  caseSourceFlag.value = false;
300
+  fromVue.value.caseSource = selectedOptions[0].text;
301
+};
302
+
303
+</script>
304
+
305
+<template>
306
+
307
+  <div class="page-container">
308
+    <van-sticky class="header">
309
+      <van-nav-bar
310
+        :title="title"
311
+        left-text="返回"
312
+        left-arrow
313
+        @click-left="onClickLeft" >
314
+      </van-nav-bar>
315
+    </van-sticky>
316
+    <div class="scroll-container">
317
+      <van-form @submit="onSubmit">
318
+        <van-field
319
+            v-model="fromVue.caseNumber"
320
+            label="案例编号"
321
+            name="caseNumber"
322
+            readonly
323
+            :rules="[{required: true, message: '编号生成失败'}]"
324
+        />
325
+
326
+        <van-field
327
+          v-model="fromVue.projectName"
328
+          label="项目名称"
329
+          name="projectName"
330
+          :readonly="isCaseSubmitted"
331
+          required
332
+          placeholder="请输入项目名称"
333
+          :rules="[{required: true, message: '请输入项目名称'}]"
334
+        />
335
+
336
+        <van-field
337
+          v-model="fromVue.caseType"
338
+          readonly
339
+          label="案例类型"
340
+          name="caseType"
341
+          required
342
+          placeholder="请选择案例类型"
343
+          :rules="[{required: true, message: '请选择案例类型'}]"
344
+          @click="!isCaseSubmitted && (caseTypeFlag = true)"
345
+        />
346
+        
347
+        <van-field
348
+          v-model="fromVue.caseSource"
349
+          readonly
350
+          label="案例来源"
351
+          name="caseSource"
352
+          placeholder="请选择案例来源"
353
+          @click="!isCaseSubmitted && (caseSourceFlag = true)"
354
+        />
355
+
356
+        <!-- 开始时间 -->
357
+        <van-field
358
+            v-model="fromVue.startTime"
359
+            is-link
360
+            readonly
361
+            name="startTime"
362
+            label="开始时间"
363
+            :colon="true"
364
+            placeholder="点击选择开始时间"
365
+            @click="!isCaseSubmitted && (showStartTimePicker = true)"
366
+        />
367
+
368
+        <!-- 结束时间 -->
369
+        <van-field
370
+            v-model="fromVue.endTime"
371
+            is-link
372
+            readonly
373
+            name="endTime"
374
+            label="结束时间"
375
+            :colon="true"
376
+            placeholder="点击选择结束时间"
377
+            @click="!isCaseSubmitted && (showEndTimePicker = true)"
378
+        />
379
+
380
+        <!-- 关键词/标签 -->
381
+        <van-field
382
+            v-model="fromVue.tags"
383
+            label="关键词/标签"
384
+            name="tags"
385
+            placeholder="请手动输入标签,多个标签用逗号分隔"
386
+            :readonly="isCaseSubmitted"
387
+        />
388
+        
389
+        <!-- 标签按钮 -->
390
+        <!-- 整个 van-cell 删除 -->
391
+
392
+        <van-field
393
+            v-model="fromVue.caseSummary"
394
+            label="案例摘要"
395
+            name="caseSummary"
396
+            rows="3"
397
+            autosize
398
+            type="textarea"
399
+            placeholder="请输入案例摘要"
400
+            :readonly="isCaseSubmitted"
401
+        />
402
+        
403
+        <van-field
404
+            v-model="fromVue.highLights"
405
+            label="创新点与亮点"
406
+            name="highLights"
407
+            rows="3"
408
+            autosize
409
+            type="textarea"
410
+            placeholder="请输入创新点与亮点"
411
+            :readonly="isCaseSubmitted"
412
+        />
413
+        
414
+        <van-field
415
+            v-model="fromVue.resultsValue"
416
+            label="应用成效与价值"
417
+            name="resultsValue"
418
+            rows="3"
419
+            autosize
420
+            type="textarea"
421
+            placeholder="请输入应用成效与价值"
422
+            :readonly="isCaseSubmitted"
423
+        />
424
+
425
+        <van-field label="附件上传" >
426
+          <template #input>
427
+            <AttachmentS3 :f-id="result" :readonly="isCaseSubmitted" />
428
+          </template>
429
+        </van-field>
430
+        
431
+        <div style="margin: 16px;">
432
+          <van-button v-if="!isReadOnly" round block type="primary" native-type="submit">
433
+            {{ isEdit ? '保存' : '提交' }}
434
+          </van-button>
435
+        </div>
436
+      </van-form>
437
+
438
+      <!-- 案例类型选择器 -->
439
+      <van-popup v-model:show="caseTypeFlag" round position="bottom">
440
+        <van-picker
441
+            :columns="caseTypeColumns"
442
+            @cancel="caseTypeFlag = false"
443
+            @confirm="onCaseTypeConfirm"
444
+        />
445
+      </van-popup>
446
+
447
+      <!-- 案例来源选择器 -->
448
+      <van-popup v-model:show="caseSourceFlag" round position="bottom">
449
+        <van-picker
450
+            :columns="caseSourceColumns"
451
+            @cancel="caseSourceFlag = false"
452
+            @confirm="onCaseSourceConfirm"
453
+        />
454
+      </van-popup>
455
+
456
+      <!-- 开始时间选择器 -->
457
+      <van-popup
458
+          v-model:show="showStartTimePicker"
459
+          position="bottom"
460
+          round
461
+          style="max-height: 50vh;"
462
+      >
463
+        <van-date-picker
464
+            v-if="!startDateOrTime"
465
+            v-model="currentStartDate"
466
+            title="选择开始日期"
467
+            :min-date="minDate"
468
+            :max-date="maxDate"
469
+            @confirm="onStartDateConfirm"
470
+            @cancel="cancelStartDatePicker"
471
+        >
472
+          <template #confirm>
473
+            <van-button type="primary" @click="onStartDateConfirm">下一步</van-button>
474
+          </template>
475
+          <template #cancel>
476
+            <van-button type="danger" @click="cancelStartDatePicker">取消</van-button>
477
+          </template>
478
+        </van-date-picker>
479
+
480
+        <van-time-picker
481
+            v-if="startDateOrTime"
482
+            v-model="currentStartTime"
483
+            title="选择开始时间"
484
+            :columns-type="['hour', 'minute', 'second']"
485
+            @confirm="onConfirmStartDatetime"
486
+            @cancel="cancelStartTimePicker"
487
+        >
488
+          <template #confirm>
489
+            <van-button type="primary" @click="onConfirmStartDatetime">确定</van-button>
490
+          </template>
491
+          <template #cancel>
492
+            <van-button type="danger" @click="cancelStartTimePicker">取消</van-button>
493
+          </template>
494
+        </van-time-picker>
495
+      </van-popup>
496
+
497
+      <!-- 结束时间选择器 -->
498
+      <van-popup
499
+          v-model:show="showEndTimePicker"
500
+          position="bottom"
501
+          round
502
+          style="max-height: 50vh;"
503
+      >
504
+        <van-date-picker
505
+            v-if="!endDateOrTime"
506
+            v-model="currentEndDate"
507
+            title="选择结束日期"
508
+            :min-date="minDate"
509
+            :max-date="maxDate"
510
+            @confirm="onEndDateConfirm"
511
+            @cancel="cancelEndDatePicker"
512
+        >
513
+          <template #confirm>
514
+            <van-button type="primary" @click="onEndDateConfirm">下一步</van-button>
515
+          </template>
516
+          <template #cancel>
517
+            <van-button type="danger" @click="cancelEndDatePicker">取消</van-button>
518
+          </template>
519
+        </van-date-picker>
520
+
521
+        <van-time-picker
522
+            v-if="endDateOrTime"
523
+            v-model="currentEndTime"
524
+            title="选择结束时间"
525
+            :columns-type="['hour', 'minute', 'second']"
526
+            @confirm="onConfirmEndDatetime"
527
+            @cancel="cancelEndTimePicker"
528
+        >
529
+          <template #confirm>
530
+            <van-button type="primary" @click="onConfirmEndDatetime">确定</van-button>
531
+          </template>
532
+          <template #cancel>
533
+            <van-button type="danger" @click="cancelEndTimePicker">取消</van-button>
534
+          </template>
535
+        </van-time-picker>
536
+      </van-popup>
537
+    </div>
538
+  </div>
539
+</template>
540
+
541
+<style scoped>
542
+.page-container {
543
+  height: 100vh;
544
+  display: flex;
545
+  flex-direction: column;
546
+}
547
+
548
+.scroll-container {
549
+  flex: 1;
550
+  overflow: auto;
551
+  -webkit-overflow-scrolling: touch;
552
+}
553
+
554
+.scroll-container::-webkit-scrollbar {
555
+  display: none;
556
+}
557
+
558
+.header, .footer {
559
+  flex-shrink: 0;
560
+  background: #f5f5f5;
561
+  padding: 12px;
562
+}
563
+</style>

Loading…
Annuler
Enregistrer