Переглянути джерело

安全费用预算移动端

liuzhuo 1 тиждень тому
джерело
коміт
b741201061

+ 11
- 1
src/router/index.ts Переглянути файл

@@ -643,7 +643,17 @@ const router = createRouter({
643 643
 			path: '/safeMoneyManagementList',
644 644
 			name: '安环费用管理编辑',
645 645
 			component: () => import('@/view/moneySafe/safeMoneyManagementList.vue')
646
-		}
646
+		},
647
+		{
648
+			path: '/moneySafe/safeMoneyBudget',
649
+			name: '安全费用预算',
650
+			component: () => import('@/view/moneySafe/safeMoneyBudget.vue')
651
+		},
652
+		{
653
+			path: '/safeMoneyBudgetList',
654
+			name: '安全费用预算编辑',
655
+			component: () => import('@/view/moneySafe/safeMoneyBudgetList.vue')
656
+		},
647 657
 	]
648 658
 })
649 659
 

+ 4
- 0
src/view/Home2.vue Переглянути файл

@@ -261,6 +261,10 @@
261 261
           <img src="../../public/images/aq.png" width="45rpx" />
262 262
           <span class="vanicon_text">安环费用管理</span>
263 263
         </van-grid-item>
264
+        <van-grid-item to="/moneySafe/safeMoneyBudget">
265
+          <img src="../../public/images/aq.png" width="45rpx" />
266
+          <span class="vanicon_text">安全费用预算</span>
267
+        </van-grid-item>
264 268
       </van-grid>
265 269
     </div>
266 270
 

+ 493
- 0
src/view/moneySafe/safeMoneyBudget.vue Переглянути файл

@@ -0,0 +1,493 @@
1
+<script setup>
2
+import { getCurrentInstance, onMounted, ref, computed } from 'vue';
3
+import { useRouter } from 'vue-router';
4
+import { showFailToast, showSuccessToast, showDialog, showLoadingToast } from 'vant';
5
+import tools from "@/tools/index.js";
6
+
7
+const { proxy } = getCurrentInstance()
8
+const router = useRouter()
9
+
10
+/* 通用方法: 重置list数据 */
11
+const basicReset = () => {
12
+  finished.value = false;
13
+  loading.value = true;
14
+  pageNum.value = 1
15
+  resultData.value = []
16
+}
17
+
18
+/* 查询数据 */
19
+const pageNum = ref(1)
20
+const pageSize = ref(10)
21
+const total = ref(0)
22
+const resultData = ref([])
23
+const queryList = ref([])
24
+
25
+// 年份选择
26
+const currentYear = new Date().getFullYear();
27
+const yearOptions = Array.from({ length: 10 }, (_, i) => {
28
+  const year = currentYear - 5 + i;
29
+  return {
30
+    text: String(year),
31
+    value: String(year)
32
+  };
33
+}).reverse();
34
+const showYearPicker = ref(false);
35
+
36
+const formData = ref({
37
+  year: String(currentYear),
38
+  quarter: null
39
+})
40
+
41
+// 获取用户部门信息
42
+const jsonArray = localStorage.getItem('dept')
43
+const deptInformation = ref([])
44
+try {
45
+  deptInformation.value = jsonArray ? JSON.parse(jsonArray) : [];
46
+} catch (error) {
47
+  deptInformation.value = [];
48
+}
49
+
50
+const jsonArrayTree = localStorage.getItem('deptTree')
51
+const deptInformationTree = ref([])
52
+try {
53
+  deptInformationTree.value = jsonArrayTree ? JSON.parse(jsonArrayTree) : [];
54
+} catch (error) {
55
+  deptInformationTree.value = [];
56
+}
57
+
58
+const deptNameTree = deptInformationTree.value[0]?.deptName || ''
59
+const deptCode = deptInformation.value[0]?.deptCode?.substring(0, 5) || ''
60
+
61
+const tableData = ref([])
62
+const loading = ref(false)
63
+
64
+// 格式化数字显示(0 显示为空)
65
+const formatterZero = (value) => {
66
+  return value === 0 || value === '0' || value === null || value === undefined ? '' : value
67
+}
68
+
69
+// 计算 1-9 月合计
70
+const formatterSum1To9 = (row) => {
71
+  const months = [
72
+    row.oneMonth,
73
+    row.twoMonth,
74
+    row.threeMonth,
75
+    row.fourMonth,
76
+    row.fiveMonth,
77
+    row.sixMonth,
78
+    row.sevenMonth,
79
+    row.eightMonth,
80
+    row.nineMonth
81
+  ]
82
+  const sum = months
83
+    .map(val => {
84
+      const num = Number(val)
85
+      return isNaN(num) ? 0 : num
86
+    })
87
+    .reduce((a, b) => a + b, 0)
88
+  return sum === 0 && months.every(v => !v) ? '' : sum
89
+}
90
+
91
+// 年份选择确认
92
+const onConfirmYear = (value) => {
93
+  let selectedYear = '';
94
+  if (value && value.selectedOptions && value.selectedOptions.length > 0) {
95
+    const selectedOption = value.selectedOptions[0];
96
+    selectedYear = typeof selectedOption === 'string' ? selectedOption : selectedOption.value || selectedOption.text;
97
+  } else if (typeof value === 'string') {
98
+    selectedYear = value;
99
+  }
100
+  
101
+  formData.value.year = selectedYear;
102
+  showYearPicker.value = false;
103
+  search();
104
+}
105
+
106
+// 查询
107
+const search = async () => {
108
+  const now = new Date()
109
+  const currentYear = now.getFullYear()
110
+  const currentMonth = now.getMonth()
111
+  const currentQuarter = Math.ceil((currentMonth + 1) / 3)
112
+
113
+  if (!formData.value.year) {
114
+    formData.value.year = String(currentYear)
115
+  }
116
+  if (!formData.value.quarter) {
117
+    formData.value.quarter = currentQuarter
118
+  }
119
+
120
+  loading.value = true
121
+  await getTableData()
122
+}
123
+
124
+// 重置
125
+const reset = () => {
126
+  formData.value.year = String(currentYear)
127
+  formData.value.quarter = null
128
+  search()
129
+}
130
+
131
+// 获取表格数据
132
+const getTableData = async () => {
133
+  try {
134
+    const res = await proxy.$axios.post('/sgsafe/expect/query', {
135
+      params: JSON.stringify(formData.value)
136
+    })
137
+
138
+    if (res.data.code == '0') {
139
+      tableData.value = res.data.data || []
140
+      resultData.value = tableData.value
141
+      total.value = tableData.value.length
142
+    } else {
143
+      showFailToast('加载失败:' + res.data.msg)
144
+    }
145
+  } catch (error) {
146
+    console.error('请求失败:', error)
147
+    showFailToast('网络错误,请重试')
148
+  } finally {
149
+    loading.value = false
150
+    finished.value = true
151
+  }
152
+}
153
+
154
+// 新增行
155
+const addRow = () => {
156
+  router.push({ 
157
+    path: "/safeMoneyBudgetList",
158
+    query: {
159
+      mark: -1,
160
+      year: formData.value.year
161
+    }
162
+  });
163
+}
164
+
165
+// 编辑行
166
+const editRow = (row) => {
167
+  router.push({ 
168
+    path: "/safeMoneyBudgetList",
169
+    query: {
170
+      mark: 1,
171
+      data: JSON.stringify(row),
172
+      year: formData.value.year
173
+    }
174
+  });
175
+}
176
+
177
+// 删除行
178
+const deleteRow = async (row) => {
179
+  try {
180
+    await showDialog({
181
+      title: '删除确认',
182
+      message: '确定要删除该条记录吗?此操作不可恢复。',
183
+      showCancelButton: true,
184
+      confirmButtonText: '确定',
185
+      cancelButtonText: '取消',
186
+    })
187
+
188
+    loading.value = true
189
+    const deleteDataItem = { ...row }
190
+    deleteDataItem.cancelFlag = '1'
191
+    
192
+    const res = await proxy.$axios.post('/sgsafe/expect/save', {
193
+      json: JSON.stringify(deleteDataItem)
194
+    })
195
+    
196
+    if (res.data.code == '0') {
197
+      showSuccessToast('删除成功')
198
+      tableData.value = tableData.value.filter(r => r.id !== row.id)
199
+      resultData.value = tableData.value
200
+    } else {
201
+      showFailToast('删除失败:' + res.data.msg)
202
+    }
203
+  } catch (error) {
204
+    if (error !== 'cancel') {
205
+      showFailToast('删除操作异常')
206
+    }
207
+  } finally {
208
+    loading.value = false
209
+  }
210
+}
211
+
212
+// 列表加载与下拉刷新
213
+const refreshing = ref(false)
214
+const finished = ref(false)
215
+
216
+const onRefresh = async () => {
217
+  refreshing.value = true
218
+  basicReset()
219
+  await search()
220
+  refreshing.value = false
221
+};
222
+
223
+const onLoad = async () => {
224
+  // 如果是下拉刷新,已经在 onRefresh 中处理了
225
+  if (refreshing.value) {
226
+    return
227
+  }
228
+  
229
+  // 上拉加载更多(如果需要分页的话)
230
+  if (finished.value) {
231
+    return
232
+  }
233
+  
234
+  await search()
235
+};
236
+
237
+// 获取费用类型字典
238
+const moneyTypeMap = ref({})
239
+const getMoneyTypeName = (value) => {
240
+  if (!value) return ''
241
+  if (moneyTypeMap.value[value]) {
242
+    return moneyTypeMap.value[value]
243
+  }
244
+  const strValue = String(value)
245
+  if (moneyTypeMap.value[strValue]) {
246
+    return moneyTypeMap.value[strValue]
247
+  }
248
+  return value
249
+}
250
+
251
+const getDicList = () => {
252
+  tools.dic.getDicList(['MONEY_TYPE']).then((response => {
253
+    if (response.data && response.data.code === 0 && response.data.data) {
254
+      const moneyTypeList = response.data.data.MONEY_TYPE || []
255
+      moneyTypeMap.value = {}
256
+      moneyTypeList.forEach(item => {
257
+        if (item.dicCode && item.dicName) {
258
+          moneyTypeMap.value[item.dicCode] = item.dicName
259
+          moneyTypeMap.value[String(item.dicCode)] = item.dicName
260
+        }
261
+        if (item.dicName) {
262
+          moneyTypeMap.value[item.dicName] = item.dicName
263
+        }
264
+      })
265
+    }
266
+  })).catch(error => {
267
+    console.error('获取字典数据出错:', error)
268
+  })
269
+}
270
+
271
+onMounted(() => {
272
+  getDicList()
273
+  search()
274
+})
275
+</script>
276
+
277
+<template>
278
+  <div class="page-container">
279
+    <van-sticky>
280
+      <van-nav-bar title="安全费用预算" @click-right="addRow">
281
+        <template #right>
282
+          <van-icon name="add" size="25" color="#000" />
283
+        </template>
284
+      </van-nav-bar>
285
+    </van-sticky>
286
+
287
+    <!-- 查询区域 -->
288
+    <div class="query-area">
289
+      <van-field
290
+        :model-value="formData.year"
291
+        is-link
292
+        readonly
293
+        name="year"
294
+        label="年份"
295
+        placeholder="点击选择年份"
296
+        @click="showYearPicker = true"
297
+      />
298
+      <div class="query-buttons">
299
+        <van-button type="primary" size="small" @click="search">查询</van-button>
300
+        <van-button size="small" @click="reset">重置</van-button>
301
+      </div>
302
+    </div>
303
+
304
+    <van-popup v-model:show="showYearPicker" position="bottom">
305
+      <van-picker
306
+        :columns="yearOptions"
307
+        @confirm="onConfirmYear"
308
+        @cancel="showYearPicker = false"
309
+      />
310
+    </van-popup>
311
+
312
+    <div class="scroll-container">
313
+      <van-pull-refresh
314
+        v-model="refreshing"
315
+        success-text="刷新成功"
316
+        @refresh="onRefresh"
317
+      >
318
+        <van-list
319
+          v-model:loading="loading"
320
+          :finished="finished"
321
+          :immediate-check="false"
322
+          finished-text="没有更多了"
323
+          @load="onLoad"
324
+        >
325
+          <div v-for="item in resultData" :key="item.id || item.tempId" class="card">
326
+            <van-swipe-cell>
327
+              <template #default>
328
+                <van-cell @click="editRow(item)" :border="false" is-link>
329
+                  <template #title>
330
+                    <div class="card-title">
331
+                      <div class="title-row">
332
+                        <span class="label">费用类型:</span>
333
+                        <span class="value">{{ getMoneyTypeName(item.moneyType) || '未设置' }}</span>
334
+                      </div>
335
+                    </div>
336
+                  </template>
337
+                  <template #label>
338
+                    <div class="card-content">
339
+                      <div class="content-row">
340
+                        <span class="label">预计投入费用:</span>
341
+                        <span class="value">{{ formatterZero(item.moneyNumber) || '0' }}</span>
342
+                      </div>
343
+                      <div class="content-row">
344
+                        <span class="label">1-9月合计:</span>
345
+                        <span class="value">{{ formatterSum1To9(item) || '0' }}</span>
346
+                      </div>
347
+<!--                      <div class="months-grid">-->
348
+<!--                        <div class="month-item" v-for="(month, index) in [-->
349
+<!--                          { key: 'oneMonth', label: '1月' },-->
350
+<!--                          { key: 'twoMonth', label: '2月' },-->
351
+<!--                          { key: 'threeMonth', label: '3月' },-->
352
+<!--                          { key: 'fourMonth', label: '4月' },-->
353
+<!--                          { key: 'fiveMonth', label: '5月' },-->
354
+<!--                          { key: 'sixMonth', label: '6月' },-->
355
+<!--                          { key: 'sevenMonth', label: '7月' },-->
356
+<!--                          { key: 'eightMonth', label: '8月' },-->
357
+<!--                          { key: 'nineMonth', label: '9月' },-->
358
+<!--                          { key: 'tenMonth', label: '10月' },-->
359
+<!--                          { key: 'elevenMonth', label: '11月' },-->
360
+<!--                          { key: 'twelveMonth', label: '12月' }-->
361
+<!--                        ]" :key="month.key">-->
362
+<!--                          <span class="month-label">{{ month.label }}:</span>-->
363
+<!--                          <span class="month-value">{{ formatterZero(item[month.key]) || '0' }}</span>-->
364
+<!--                        </div>-->
365
+<!--                      </div>-->
366
+                    </div>
367
+                  </template>
368
+                </van-cell>
369
+              </template>
370
+              <template #right>
371
+                <van-button 
372
+                  square 
373
+                  class="delete-button" 
374
+                  text="删除" 
375
+                  @click="deleteRow(item)" 
376
+                />
377
+              </template>
378
+            </van-swipe-cell>
379
+          </div>
380
+        </van-list>
381
+      </van-pull-refresh>
382
+    </div>
383
+  </div>
384
+</template>
385
+
386
+<style scoped>
387
+.page-container {
388
+  height: 100vh;
389
+  display: flex;
390
+  flex-direction: column;
391
+}
392
+
393
+.query-area {
394
+  background-color: #f8f9fa;
395
+  padding: 12px;
396
+  border-bottom: 1px solid #ebedf0;
397
+}
398
+
399
+.query-buttons {
400
+  display: flex;
401
+  gap: 8px;
402
+  margin-top: 12px;
403
+}
404
+
405
+.query-buttons .van-button {
406
+  flex: 1;
407
+}
408
+
409
+.scroll-container {
410
+  flex: 1;
411
+  overflow: auto;
412
+  -webkit-overflow-scrolling: touch;
413
+}
414
+
415
+.scroll-container::-webkit-scrollbar {
416
+  display: none;
417
+}
418
+
419
+.card {
420
+  margin: 10px;
421
+  border-radius: 8px;
422
+  overflow: hidden;
423
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
424
+  background-color: #fff;
425
+}
426
+
427
+.card-title {
428
+  font-weight: bold;
429
+  color: #333;
430
+}
431
+
432
+.title-row {
433
+  display: flex;
434
+  align-items: center;
435
+  margin-bottom: 8px;
436
+}
437
+
438
+.card-content {
439
+  margin-top: 8px;
440
+}
441
+
442
+.content-row {
443
+  display: flex;
444
+  align-items: center;
445
+  margin-bottom: 8px;
446
+  font-size: 14px;
447
+}
448
+
449
+.label {
450
+  color: #969799;
451
+  margin-right: 8px;
452
+  min-width: 80px;
453
+}
454
+
455
+.value {
456
+  color: #323233;
457
+  font-weight: 500;
458
+}
459
+
460
+.months-grid {
461
+  display: grid;
462
+  grid-template-columns: repeat(3, 1fr);
463
+  gap: 8px;
464
+  margin-top: 12px;
465
+  padding-top: 12px;
466
+  border-top: 1px solid #ebedf0;
467
+}
468
+
469
+.month-item {
470
+  display: flex;
471
+  flex-direction: column;
472
+  font-size: 12px;
473
+}
474
+
475
+.month-label {
476
+  color: #969799;
477
+  margin-bottom: 4px;
478
+}
479
+
480
+.month-value {
481
+  color: #323233;
482
+  font-weight: 500;
483
+}
484
+
485
+.delete-button {
486
+  height: 100%;
487
+  border: none;
488
+  color: #fff;
489
+  background-color: #ee0a24;
490
+  font-size: 16px;
491
+  padding: 0 20px;
492
+}
493
+</style>

+ 409
- 0
src/view/moneySafe/safeMoneyBudgetList.vue Переглянути файл

@@ -0,0 +1,409 @@
1
+<script setup>
2
+import { getCurrentInstance, onMounted, ref, computed, nextTick,reactive } from 'vue';
3
+import { useRoute, useRouter } from 'vue-router';
4
+import { showFailToast, showLoadingToast, showSuccessToast } from 'vant';
5
+import tools from '@/tools'
6
+
7
+const { proxy } = getCurrentInstance()
8
+const router = useRouter()
9
+const route = useRoute()
10
+
11
+const onClickLeft = () => {
12
+  router.go(-1)
13
+}
14
+
15
+// 获取用户部门信息
16
+const jsonArray = localStorage.getItem('dept')
17
+const deptInformation = ref([])
18
+try {
19
+  deptInformation.value = jsonArray ? JSON.parse(jsonArray) : [];
20
+} catch (error) {
21
+  deptInformation.value = [];
22
+}
23
+
24
+const jsonArrayTree = localStorage.getItem('deptTree')
25
+const deptInformationTree = ref([])
26
+try {
27
+  deptInformationTree.value = jsonArrayTree ? JSON.parse(jsonArrayTree) : [];
28
+} catch (error) {
29
+  deptInformationTree.value = [];
30
+}
31
+
32
+const deptNameTree = deptInformationTree.value[0]?.deptName || ''
33
+const deptCode = deptInformation.value[0]?.deptCode?.substring(0, 5) || ''
34
+
35
+// 编辑模式判断
36
+const isEdit = ref(route.query.mark === '1')
37
+const title = computed(() => isEdit.value ? '编辑费用预算' : '新增费用预算')
38
+
39
+// 费用类型选择器
40
+//const moneyTypeFlag = ref(false)
41
+const columnsLevel = ref([])
42
+const moneyTypeMap = ref({})
43
+
44
+const currentYear = new Date().getFullYear()
45
+const yearOptions = Array.from({ length: 10 }, (_, i) => {
46
+  const year = currentYear - 5 + i;
47
+  return {
48
+    text: String(year),
49
+    value: String(year)
50
+  };
51
+}).reverse();
52
+
53
+// 表单数据
54
+const budgetForm = reactive({
55
+  id: null,
56
+  tempId: null,
57
+  moneyType: '',
58
+  oneMonth: '',
59
+  twoMonth: '',
60
+  threeMonth: '',
61
+  fourMonth: '',
62
+  fiveMonth: '',
63
+  sixMonth: '',
64
+  sevenMonth: '',
65
+  eightMonth: '',
66
+  nineMonth: '',
67
+  tenMonth: '',
68
+  elevenMonth: '',
69
+  twelveMonth: '',
70
+  moneyNumber: '',
71
+  yearTime: route.query.year || String(currentYear),
72
+  moneyDept: deptNameTree,
73
+  addDeptCode: deptCode
74
+})
75
+
76
+// 初始化表单数据
77
+// if (isEdit.value && route.query.data) {
78
+//   try {
79
+//     const editData = JSON.parse(route.query.data)
80
+//     budgetForm.value = {
81
+//       ...budgetForm.value,
82
+//       ...editData
83
+//     }
84
+//   } catch (error) {
85
+//     console.error('解析编辑数据失败:', error)
86
+//   }
87
+// } else {
88
+//   // 新增模式,生成临时ID
89
+//   budgetForm.value.tempId = 'temp_' + Date.now()
90
+// }
91
+
92
+// 初始化表单数据
93
+if (isEdit.value && route.query.data) {
94
+  try {
95
+    const editData = JSON.parse(route.query.data)
96
+    // 只更新已有字段,避免破坏 reactive 响应性
97
+    for (const key in budgetForm) {
98
+      if (key in editData) {
99
+        budgetForm[key] = editData[key]
100
+      }
101
+    }
102
+  } catch (error) {
103
+    console.error('解析编辑数据失败:', error)
104
+  }
105
+} else {
106
+  budgetForm.tempId = 'temp_' + Date.now()
107
+}
108
+
109
+// 费用类型选择确认
110
+// const moneyTypeLevelConfirm = ({ selectedOptions }) => {
111
+//   moneyTypeFlag.value = false;
112
+//   budgetForm.value.moneyType = selectedOptions[0].text;
113
+// };
114
+
115
+// 获取费用类型字典
116
+const getDicList = () => {
117
+  tools.dic.getDicList(['MONEY_TYPE']).then((response => {
118
+    if (response.data && response.data.code === 0 && response.data.data) {
119
+      const moneyTypeList = response.data.data.MONEY_TYPE || []
120
+      columnsLevel.value = moneyTypeList.map(item => ({
121
+        text: item.dicName,
122
+        value: item.dicName
123
+      }))
124
+      
125
+      moneyTypeMap.value = {}
126
+      moneyTypeList.forEach(item => {
127
+        if (item.dicCode && item.dicName) {
128
+          moneyTypeMap.value[item.dicCode] = item.dicName
129
+          moneyTypeMap.value[String(item.dicCode)] = item.dicName
130
+        }
131
+        if (item.dicName) {
132
+          moneyTypeMap.value[item.dicName] = item.dicName
133
+        }
134
+      })
135
+    }
136
+  })).catch(error => {
137
+    console.error('获取字典数据出错:', error)
138
+    columnsLevel.value = []
139
+  })
140
+}
141
+
142
+// 格式化数字输入
143
+const formatNumberInput = (value) => {
144
+  if (value === '' || value === null || value === undefined) {
145
+    return null
146
+  }
147
+  const num = Number(value)
148
+  return isNaN(num) ? null : num
149
+}
150
+
151
+// 保存数据
152
+const saveBudget = async () => {
153
+  // 验证必填字段
154
+  if (!budgetForm.moneyType) {
155
+    showFailToast('请选择费用类型')
156
+    return
157
+  }
158
+
159
+  const loadingToast = showLoadingToast({
160
+    duration: 0,
161
+    message: '保存中...',
162
+    forbidClick: true
163
+  })
164
+
165
+  try {
166
+    const submitData = { ...budgetForm }
167
+    delete submitData.tempId
168
+
169
+    const response = await proxy.$axios.post('/sgsafe/expect/save', {
170
+      json: JSON.stringify(submitData)
171
+    })
172
+
173
+    if (response.data.code == '0') {
174
+      loadingToast.close()
175
+      showSuccessToast('保存成功')
176
+      
177
+      // 如果是新增且返回了ID,更新表单ID
178
+      if (!budgetForm.id && response.data.data?.id) {
179
+        budgetForm.id = response.data.data.id
180
+      }
181
+      
182
+      // 延迟返回,让用户看到成功提示
183
+      setTimeout(() => {
184
+        onClickLeft()
185
+      }, 500)
186
+    } else {
187
+      loadingToast.close()
188
+      showFailToast('保存失败:' + response.data.msg)
189
+    }
190
+  } catch (error) {
191
+    loadingToast.close()
192
+    showFailToast('保存失败,请检查网络')
193
+    console.error('保存错误:', error)
194
+  }
195
+}
196
+
197
+// 月份字段列表
198
+const monthFields = [
199
+  { key: 'oneMonth', label: '1月' },
200
+  { key: 'twoMonth', label: '2月' },
201
+  { key: 'threeMonth', label: '3月' },
202
+  { key: 'fourMonth', label: '4月' },
203
+  { key: 'fiveMonth', label: '5月' },
204
+  { key: 'sixMonth', label: '6月' },
205
+  { key: 'sevenMonth', label: '7月' },
206
+  { key: 'eightMonth', label: '8月' },
207
+  { key: 'nineMonth', label: '9月' },
208
+  { key: 'tenMonth', label: '10月' },
209
+  { key: 'elevenMonth', label: '11月' },
210
+  { key: 'twelveMonth', label: '12月' }
211
+]
212
+const showYearPicker = ref(false)
213
+const onYearConfirm = ({ selectedOptions }) => {
214
+  // 处理对象数组格式的选中值
215
+  let selectedYear = ''
216
+  if (selectedOptions && selectedOptions.length > 0) {
217
+    const selectedOption = selectedOptions[0]
218
+    selectedYear = typeof selectedOption === 'string' 
219
+      ? selectedOption 
220
+      : selectedOption.value || selectedOption.text
221
+  }
222
+  budgetForm.yearTime = selectedYear
223
+  showYearPicker.value = false
224
+}
225
+// 计算 1-9 月合计
226
+const sum1To9 = computed(() => {
227
+  const months = [
228
+    budgetForm.oneMonth,
229
+    budgetForm.twoMonth,
230
+    budgetForm.threeMonth,
231
+    budgetForm.fourMonth,
232
+    budgetForm.fiveMonth,
233
+    budgetForm.sixMonth,
234
+    budgetForm.sevenMonth,
235
+    budgetForm.eightMonth,
236
+    budgetForm.nineMonth
237
+  ]
238
+  const sum = months
239
+    .map(val => {
240
+      const num = Number(val)
241
+      return isNaN(num) ? 0 : num
242
+    })
243
+    .reduce((a, b) => a + b, 0)
244
+  return sum === 0 && months.every(v => !v) ? '' : sum
245
+})
246
+
247
+onMounted(() => {
248
+  getDicList()
249
+})
250
+</script>
251
+
252
+<template>
253
+  <div class="page-container">
254
+    <van-sticky class="header">
255
+      <van-nav-bar
256
+        :title="title"
257
+        left-text="返回"
258
+        left-arrow
259
+        @click-left="onClickLeft"
260
+      />
261
+    </van-sticky>
262
+
263
+    <div class="scroll-container">
264
+      <van-form @submit="saveBudget">
265
+        <!-- 费用类型 -->
266
+        <van-field
267
+          v-model="budgetForm.moneyType"
268
+          label="费用类型"
269
+          name="moneyType"
270
+          placeholder="请输入费用类型"
271
+          type="textarea"
272
+          autosize="ture"
273
+          :rules="[{required: true, message: '请输入费用类型'}]"
274
+          required
275
+
276
+        />
277
+
278
+        <!-- 1-12月输入 -->
279
+        <div class="months-section">
280
+          <div class="section-title">月度预算</div>
281
+          <div class="months-grid">
282
+            <van-field
283
+                v-for="month in monthFields"
284
+                :key="month.key"
285
+                v-model.number="budgetForm[month.key]"
286
+                :label="month.label"
287
+                :name="month.key"
288
+                type="number"
289
+                placeholder="0"
290
+            />
291
+          </div>
292
+        </div>
293
+
294
+        <!-- 1-9月合计(只读显示) -->
295
+        <van-field
296
+          :model-value="sum1To9"
297
+          label="1-9月合计"
298
+          name="sum1To9"
299
+          readonly
300
+          placeholder="自动计算"
301
+        />
302
+
303
+        <!-- 预计投入费用 -->
304
+        <van-field
305
+          v-model.number="budgetForm.moneyNumber"
306
+          label="预计投入费用"
307
+          name="moneyNumber"
308
+          type="number"
309
+          placeholder="请输入预计投入费用"
310
+          :rules="[{ pattern: /^\d+(\.\d+)?$/, message: '请输入有效数字' }]"
311
+        />
312
+
313
+        <!-- 年份(只读显示) -->
314
+        <van-field
315
+          :model-value="budgetForm.yearTime"
316
+          label="年份"
317
+          name="yearTime"
318
+          readonly
319
+          is-link
320
+          placeholder="请选择年份"
321
+          @click="showYearPicker = true"
322
+        />
323
+        <van-popup v-model:show="showYearPicker" position="bottom">
324
+          <van-picker
325
+              :columns="yearOptions"
326
+              @confirm="onYearConfirm"
327
+              @cancel="showYearPicker = false"
328
+          />
329
+        </van-popup>
330
+
331
+        <!-- 所属部门(只读显示) -->
332
+        <van-field
333
+            v-if="false"
334
+          :model-value="budgetForm.moneyDept"
335
+          label="所属部门"
336
+          name="moneyDept"
337
+          readonly
338
+        />
339
+
340
+        <!-- 提交按钮 -->
341
+        <div style="margin: 16px;">
342
+          <van-button round block type="primary" native-type="submit">
343
+            保存
344
+          </van-button>
345
+        </div>
346
+      </van-form>
347
+
348
+      <!-- 费用类型选择器 -->
349
+<!--      <van-popup v-model:show="moneyTypeFlag" round position="bottom">-->
350
+<!--        <van-picker-->
351
+<!--          :columns="columnsLevel"-->
352
+<!--          @cancel="moneyTypeFlag = false"-->
353
+<!--          @confirm="moneyTypeLevelConfirm"-->
354
+<!--        />-->
355
+<!--      </van-popup>-->
356
+    </div>
357
+  </div>
358
+</template>
359
+
360
+<style scoped>
361
+.page-container {
362
+  height: 100vh;
363
+  display: flex;
364
+  flex-direction: column;
365
+}
366
+
367
+.scroll-container {
368
+  flex: 1;
369
+  overflow: auto;
370
+  -webkit-overflow-scrolling: touch;
371
+}
372
+
373
+.scroll-container::-webkit-scrollbar {
374
+  display: none;
375
+}
376
+
377
+.months-section {
378
+  margin: 16px 0;
379
+  background-color: #fff;
380
+}
381
+
382
+.section-title {
383
+  padding: 12px 16px;
384
+  font-size: 16px;
385
+  font-weight: 500;
386
+  color: #323233;
387
+  border-bottom: 1px solid #ebedf0;
388
+}
389
+
390
+.months-grid {
391
+  display: grid;
392
+  grid-template-columns: repeat(3, 1fr);
393
+  gap: 0;
394
+}
395
+
396
+.months-grid .van-field {
397
+  border-bottom: 1px solid #ebedf0;
398
+  border-right: 1px solid #ebedf0;
399
+}
400
+
401
+.months-grid .van-field:nth-child(3n) {
402
+  border-right: none;
403
+}
404
+
405
+.header {
406
+  flex-shrink: 0;
407
+  background: #fff;
408
+}
409
+</style>

+ 1
- 1
src/view/moneySafe/safeMoneyManagementList.vue Переглянути файл

@@ -381,7 +381,7 @@ const getDicList = () => {
381 381
     dictList.value = response.data.data
382 382
     columnsLevel.value = dictList.value.MONEY_TYPE.map(item => ({
383 383
       text: item.dicName,
384
-      value: item.dicName // Web端保存的是 dicCode
384
+      value: item.dicName
385 385
     }))
386 386
   })).catch(error => {
387 387
     console.error('获取字典数据出错:', error)

Завантаження…
Відмінити
Зберегти