Zimo hai 11 horas
pai
achega
1655b56e74

+ 24 - 4
api/index.js

@@ -39,7 +39,7 @@ export const getAllCostCenterOrFactoryOrStorageLocationList = (type) =>
   });
 /**
  * 获取工厂、成本中心列表 - 新
- * @param type 1-工厂 2-成本中心 
+ * @param type 1-工厂 2-成本中心
  */
 export const filteredSimpleSapOrgList = (type) =>
   request({
@@ -99,6 +99,19 @@ export const getUserList = (userId) =>
     },
   });
 
+export const selectedDeptsEmployee = (params) =>
+  request({
+    url: "/system/user/selectedDeptsEmployee",
+    method: "GET",
+    params,
+  });
+
+export const getDept = (deptId) =>
+  request({
+    url: "/system/dept/get?id=" + deptId,
+    method: "GET",
+  });
+
 // 获取用户信息
 export const getUserInfo = (params) =>
   request({
@@ -121,7 +134,14 @@ export const getUserDeptInfo = (params) =>
  */
 export const getUserListByDeptId = (deptId) =>
   request({
-    url: '/system/user/simpleUserList',
-    method: 'GET',
+    url: "/system/user/simpleUserList",
+    method: "GET",
+    params: { deptId },
+  });
+
+export const specifiedSimpleDepts = (deptId) =>
+  request({
+    url: "/system/dept/specifiedSimpleDepts",
+    method: "GET",
     params: { deptId },
-  })
+  });

+ 25 - 17
api/ruiDu.js

@@ -1,4 +1,4 @@
-import { request, upload } from '@/utils/request';
+import { request, upload } from "@/utils/request";
 
 /**
  * 获取瑞都日报列表
@@ -8,12 +8,20 @@ import { request, upload } from '@/utils/request';
  */
 export function getRuiDuReportPage(params) {
   return request({
-    url: '/pms/iot-rd-daily-report/page',
-    method: 'get',
+    url: "/pms/iot-rd-daily-report/page",
+    method: "get",
     params,
   });
 }
 
+export function createIotRdDailyReport(data) {
+  return request({
+    url: "/pms/iot-rd-daily-report/create",
+    method: "post",
+    data,
+  });
+}
+
 /**
  * 获取瑞都日报详情 - 任务信息
  * @param params 查询参数
@@ -21,8 +29,8 @@ export function getRuiDuReportPage(params) {
  */
 export function getRuiDuReportDetail(params) {
   return request({
-    url: '/pms/iot-rd-daily-report/get',
-    method: 'get',
+    url: "/pms/iot-rd-daily-report/get",
+    method: "get",
     params,
   });
 }
@@ -33,8 +41,8 @@ export function getRuiDuReportDetail(params) {
  */
 export function getRuiDuReportAttrs(params) {
   return request({
-    url: '/rq/iot-daily-report-attrs/dailyReportAttrs',
-    method: 'get',
+    url: "/rq/iot-daily-report-attrs/dailyReportAttrs",
+    method: "get",
     params,
   });
 }
@@ -44,14 +52,14 @@ export function getRuiDuReportAttrs(params) {
  * @param filePath
  */
 export const uploadAttachmentsFile = (filePath, deviceId = undefined) =>
-  upload('/rq/file/upload', {
+  upload("/rq/file/upload", {
     // #ifdef MP-ALIPAY
-    fileType: 'image/video/audio', // 仅支付宝小程序,且必填。
+    fileType: "image/video/audio", // 仅支付宝小程序,且必填。
     // #endif
     filePath: filePath, // 要上传文件资源的路径。
-    name: 'files', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
+    name: "files", // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
     header: {
-      'device-id': deviceId,
+      "device-id": deviceId,
     } /* 会与全局header合并,如有同名属性,局部覆盖全局 */,
   });
 
@@ -61,24 +69,24 @@ export const uploadAttachmentsFile = (filePath, deviceId = undefined) =>
  */
 export function updateRuiDuReport(data) {
   return request({
-    url: '/pms/iot-rd-daily-report/update',
-    method: 'put',
+    url: "/pms/iot-rd-daily-report/update",
+    method: "put",
     data,
   });
 }
 
 export function updateRuiDuReportBatch(data) {
   return request({
-    url: '/pms/iot-rd-daily-report/saveBatch',
-    method: 'post',
+    url: "/pms/iot-rd-daily-report/saveBatch",
+    method: "post",
     data,
   });
 }
 
 export function approveRdDailyReport(data) {
   return request({
-    url: '/pms/iot-rd-daily-report/approval',
-    method: 'put',
+    url: "/pms/iot-rd-daily-report/approval",
+    method: "put",
     data,
   });
 }

+ 196 - 0
components/da-tree/changelog.md

@@ -0,0 +1,196 @@
+# 1.4.2
+
+新增
+
+1. 新增`filterValue`属性,支持通过此关键词来搜索并筛选树结构的内容
+
+# 1.4.1
+
+修复
+
+1. 修复单选 onlyRadioLeaf 时末级节点无法选中的 bug
+
+# 1.4.0
+
+版本调整
+
+建议更新,但需要注意,异步数据的时候,后台需返回 leaf 字段来判断是否末项数据
+
+1. **调整数据项格式,新增 `leaf` 字段,来判断是否为末节点**
+2. **调整数据项格式,新增 `sort` 字段,来排序节点位置**
+3. **注意:异步加载数据,当为末项的时候,需要服务端数据返回 `leaf` 字段**
+4. 新增 `alwaysFirstLoad` ,即异步数据总会在第一次展开节点时,拉取一次后台数据,来比对是否一致
+5. 拆分 `field` 属性,**注意: 1.5.0 版本后将移除 `field` 属性**
+6. 新增 `labelField` 同 `field.label`,指定节点对象中某个属性为**标签**字段,默认`label`
+7. 新增 `valueField` 同 `field.key`,指定节点对象中某个属性为**值**字段,默认`value`
+8. 新增 `childrenField` 同 `field.children`,指定节点对象中某个属性为**子树节点**字段,默认`children`
+9. 新增 `disabledField` 同 `field.disabled`,指定节点对象中某个属性为**禁用**字段,默认`disabled`
+10. 新增 `appendField` 同 `field.append`,指定节点对象中某个属性为**副标签**字段,默认`append`
+11. 新增 `leafField` 同 `field.label`,指定节点对象中某个属性为**末级节点**字段,默认`leaf`
+12. 新增 `sortField` 同 `field.label`,指定节点对象中某个属性为**排序**字段,默认`sort`
+13. 新增 `isLeafFn` ,用来自定义控制数据项的末项
+14. 更多的项目示例
+15. 支持单选取消选中
+16. 修复节点展开时可能存在的 bug
+17. 修复节点选择可能存在的 bug
+18. 调整为子节点默认继承父节点禁用属性
+19. `setExpandedKeys` 添加参数一为 `all` 即可支持一键展开/收起全部节点
+20. 其它更多优化
+
+# 1.3.4
+
+优化
+
+1. 优化图标字体命名
+
+# 1.3.3
+
+优化
+
+1. 新增方法调用
+   > - 新增`getUncheckedKeys`,返回未选的 key
+   > - 新增`getUncheckedNodes`,返回未选的节点
+   > - 新增`getUnexpandedKeys`,返回未展开的 key
+   > - 新增`getUnexpandedNodes`,返回未展开的节点
+2. 优化示例项目
+
+# 1.3.2
+
+修复
+
+1. 修复在 APP 真机环境中的报错
+
+# 1.3.1
+
+修复
+
+1. 修复方法`setExpandedKeys`没联动展开上级父子节点
+
+# 1.3.0
+
+优化
+
+1. `field`新增字段 `append` 用于在标签后面显示小提示
+2. 新增支持点击标签也能选中节点
+3. 方法`setExpandedKeys`支持加载动态数据
+4. 修复父节点禁用,则不能展开及图标展开显示
+5. 修复动态加载数据时,末级节点的 `children` 为 `null` 时仍显示展开图标
+
+# 1.2.6
+
+新增
+
+1. 新增支持主题换色
+2. 支持单选的`onlyRadioLeaf`为`true`时可点父节点展开/收起
+3. 优化`expandChecked`调整为不展开无子节点的节点
+
+# 1.2.5
+
+新增
+
+1. 新增 `expandChecked`,控制选择时是否展开当前已选的所有下级节点
+
+# 1.2.4
+
+修复
+
+1. 修复动态数据展开状态异常问题
+
+# 1.2.3
+
+新增
+
+1. 新增 `checkedDisabled`,是否渲染禁用值
+2. 新增 `packDisabledkey`,是否返回已禁用并选中的 key
+3. 修复选择父级时,子级已禁用但仍被选中的问题
+
+# 1.2.2
+
+优化
+
+1. 调整动态数据载入处理方式
+2. 修复节点数据因动态数据引起的状态异常
+3. 修复初始节点数据默认选中
+
+# 1.2.1
+
+修复
+
+1. 修复切换`选中状态`被重复选中问题
+2. 修复动态数据引起的重复选择问题
+
+# 1.2.0
+
+新增
+
+1. 新增方法调用
+   > - 新增`setCheckedKeys`,方法设置指定 key 的节点选中状态
+   > - 新增`setExpandedKeys`,方法设置指定 key 的节点展开状态
+2. 修复小程序重复插槽一直刷报错问题
+3. 优化展开时,会展开子级所以下级节点
+
+# 1.1.1
+
+新增
+
+1. 新增`data`的`disabled`,支持节点禁用状态
+2. 新增`field`的`disabled`,可自定`disabled`字段值
+
+# 1.1.0
+
+新增
+
+1. 新增`loadMode`、`loadApi`,支持展开时加载异步数据
+2. 新增方法调用
+   > - 新增`getCheckedKeys`,方法返回已选的 key
+   > - 新增`getHalfCheckedKeys`,方法返回半选的 key
+   > - 新增`getExpandedKeys`,方法返回已展开的 key
+   > - 新增`getCheckedNodes`,方法返回已选的节点
+   > - 新增`getHalfCheckedNodes`,方法返回半选的节点
+   > - 新增`getExpandedNodes`,方法返回已展开的节点
+3. 对代码进行重构,更易于后期拓展
+4. 此次更新后,页面多个的 DaTee 组件间的数据不再关联
+
+# 1.0.6
+
+新增
+
+1. 新增`checkStrictly`,多选模式下选中时是否父子不关联
+
+# 1.0.5
+
+修复
+
+1. 修复多选时已选数据重复问题
+
+# 1.0.4
+
+修复
+
+1. 修复 `change` 事件回调数据的问题
+
+# 1.0.3
+
+优化
+
+1. 优化文档及示例说明
+
+# 1.0.2
+
+新增
+
+1. 新增 `onlyRadioLeaf` ,单选时只允许选中末级
+2. 优化默认展开及默认选择的展开问题
+
+# 1.0.1
+
+新增
+
+1. 支持展开/收起回调事件`@expand`
+
+# 1.0.0
+
+初始版本 1.0.0,基于 Vue3 进行开发,支持单选、多选,兼容各大平台
+
+1. 支持单选
+2. 支持多选

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 1087 - 0
components/da-tree/index.vue


+ 197 - 0
components/da-tree/props.ts

@@ -0,0 +1,197 @@
+export default {
+  /**
+   * 树的数据
+   */
+  data: {
+    type: Array,
+    default: () => [],
+  },
+  /**
+   * 主题色
+   */
+  themeColor: {
+    type: String,
+    default: '#007aff',
+  },
+  /**
+   * 是否开启多选,默认单选
+   */
+  showCheckbox: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 默认选中的节点,注意单选时为单个key,多选时为key的数组
+   */
+  defaultCheckedKeys: {
+    type: [Array, String, Number],
+    default: null,
+  },
+  /**
+   * 是否默认展开全部
+   */
+  defaultExpandAll: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 默认展开的节点
+   */
+  defaultExpandedKeys: {
+    type: Array,
+    default: null,
+  },
+  /**
+   * 筛选关键词
+   */
+  filterValue: {
+    type: String,
+    default: '',
+  },
+  /**
+   * 是否自动展开到选中的节点,默认不展开
+   */
+  expandChecked: {
+    type: Boolean,
+    default: false,
+  },
+
+  /**
+   * (旧)字段对应内容,默认为 {label: 'label',key: 'key', children: 'children', disabled: 'disabled', append: 'append'}
+   * 注意:1.5.0版本后不再兼容
+   */
+  field: {
+    type: Object,
+    default: null,
+  },
+  /**
+   * 标签字段(新,拆分了)
+   */
+  labelField: {
+    type: String,
+    default: 'label',
+  },
+  /**
+   * 值字段(新,拆分了)
+   */
+  valueField: {
+    type: String,
+    default: 'value',
+  },
+  /**
+   * 下级字段(新,拆分了)
+   */
+  childrenField: {
+    type: String,
+    default: 'children',
+  },
+  /**
+   * 禁用字段(新,拆分了)
+   */
+  disabledField: {
+    type: String,
+    default: 'disabled',
+  },
+  /**
+   * 末级节点字段(新,拆分了)
+   */
+  leafField: {
+    type: String,
+    default: 'leaf',
+  },
+  /**
+   * 副标签字段(新,拆分了)
+   */
+  appendField: {
+    type: String,
+    default: 'append',
+  },
+  /**
+   * 排序字段(新,拆分了)
+   */
+  sortField: {
+    type: String,
+    default: 'sort',
+  },
+  /**
+   * Api数据返回后的结果路径,支持嵌套如`data.list`
+   */
+  resultField: {
+    type: String,
+    default: '',
+  },
+  isLeafFn: {
+    type: Function,
+    default: null,
+  },
+  /**
+   * 是否显示单选图标,默认显示
+   */
+  showRadioIcon: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   * 单选时只允许选中末级,默认可随意选中
+   */
+  onlyRadioLeaf: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 多选时,是否执行父子不关联的任意勾选,默认父子关联
+   */
+  checkStrictly: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 为 true 时,空的 children 数组会显示展开图标
+   */
+  loadMode: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 异步加载接口
+   */
+  loadApi: {
+    type: Function,
+    default: null,
+  },
+  /**
+   * 是否总在首次的时候加载一下内容,来比对是否一致
+   */
+  alwaysFirstLoad: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 是否渲染(操作)禁用值
+   */
+  checkedDisabled: {
+    type: Boolean,
+    default: false,
+  },
+  /**
+   * 是否返回已禁用的但已选中的key
+   */
+  packDisabledkey: {
+    type: Boolean,
+    default: true,
+  },
+  /**
+   * 选择框的位置,可选 left/right
+   */
+  checkboxPlacement: {
+    type: String,
+    default: 'left',
+  },
+  /**
+   * 子项缩进距离,默认40,单位rpx
+   */
+  indent: {
+    type: Number,
+    default: 40,
+  },
+}

+ 310 - 0
components/da-tree/readme.md

@@ -0,0 +1,310 @@
+# da-tree
+
+一个基于 Vue3 的 tree(树)组件,同时支持主题换色,可能是最适合你的 tree(树)组件
+
+组件一直在更新,遇到问题可在下方讨论。
+
+`同时更新 Vue2 版本,在此查看 ===>` **[Vue2 版](https://ext.dcloud.net.cn/plugin?id=12692)**
+
+### 关于使用
+
+可在右侧的`使用 HBuilderX 导入插件`或`下载示例项目ZIP`,方便快速上手。
+
+可通过下方的示例及文档说明,进一步了解使用组件相关细节参数。
+
+插件地址:https://ext.dcloud.net.cn/plugin?id=12384
+
+### 组件示例
+
+```jsx
+<template>
+  <view>多选</view>
+  <view><button @click="doCheckedTree(['2'],true)">全选</button></view>
+  <view><button @click="doCheckedTree(['2'],false)">取消全选</button></view>
+  <view><button @click="doCheckedTree(['211','222'],true)">选中指定节点</button></view>
+  <view><button @click="doCheckedTree(['211','222'],false)">取消选中指定节点</button></view>
+  <view><button @click="doExpandTree('all',true)">展开全部节点</button></view>
+  <view><button @click="doExpandTree('all',false)">收起全部节点</button></view>
+  <view><button @click="doExpandTree(['22','23'],true)">展开节点</button></view>
+  <view><button @click="doExpandTree(['22','23'],false)">收起节点</button></view>
+  <DaTree
+    ref="DaTreeRef"
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    defaultExpandAll
+    showCheckbox
+    :defaultCheckedKeys="defaultCheckedKeysValue"
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+  <view>单选</view>
+  <DaTree
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    defaultExpandAll
+    :defaultCheckedKeys="defaultCheckedKeysValue2"
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+  <view>默认展开指定节点</view>
+  <DaTree
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    showCheckbox
+    :defaultExpandedKeys="defaultExpandKeysValue3"
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+  <view>异步加载数据</view>
+  <DaTree
+    :data="roomTreeData"
+    labelField="name"
+    valueField="id"
+    showCheckbox
+    loadMode
+    :loadApi="GetApiData"
+    defaultExpandAll
+    @change="handleTreeChange"
+    @expand="handleExpandChange"></DaTree>
+</template>
+```
+
+```js
+import { defineComponent, ref } from 'vue'
+
+/**
+ * 模拟创建一个接口数据
+ */
+function GetApiData(currentNode) {
+  const { key } = currentNode
+
+  return new Promise((resolve) => {
+    setTimeout(() => {
+      // 模拟返回空数据
+      if (key.indexOf('-') > -1) {
+        return resolve(null)
+        // return resolve([])
+      }
+
+      return resolve([
+        {
+          id: `${key}-1`,
+          name: `行政部X${key}-1`,
+        },
+        {
+          id: `${key}-2`,
+          name: `财务部X${key}-2`,
+          append: '定义了末项数据',
+          leaf: true,
+        },
+        {
+          id: `${key}-3`,
+          name: `资源部X${key}-3`,
+        },
+        {
+          id: `${key}-4`,
+          name: `资源部X${key}-3`,
+          append: '被禁用,无展开图标',
+          disabled: true,
+        },
+      ])
+    }, 2000)
+  })
+}
+
+import DaTree from '@/components/da-tree/index.vue'
+export default defineComponent({
+  components: { DaTree },
+  setup() {
+    const DaTreeRef = ref()
+    // key的类型必须对应树数据key的类型
+    const defaultCheckedKeysValue = ref(['211', '222'])
+    const defaultCheckedKeysValue2 = ref('222')
+    const defaultExpandKeysValue3 = ref(['212', '231'])
+    const roomTreeData = ref([
+      {
+        id: '2',
+        name: '行政中心',
+        children: [
+          {
+            id: '21',
+            name: '行政部',
+            children: [
+              {
+                id: '211',
+                name: '行政一部',
+                children: null,
+              },
+              {
+                id: '212',
+                name: '行政二部',
+                children: [],
+                disabled: true,
+              },
+            ],
+          },
+          {
+            id: '22',
+            name: '财务部',
+            children: [
+              {
+                id: '221',
+                name: '财务一部',
+                children: [],
+                disabled: true,
+              },
+              {
+                id: '222',
+                name: '财务二部',
+                children: [],
+              },
+            ],
+          },
+          {
+            id: '23',
+            name: '人力资源部',
+            children: [
+              {
+                id: '231',
+                name: '人力一部',
+                children: [],
+              },
+              {
+                id: '232',
+                name: '人力二部',
+                append: '更多示例,请下载示例项目查看',
+              },
+            ],
+          },
+        ],
+      },
+    ])
+    function doExpandTree(keys, expand) {
+      DaTreeRef.value?.setExpandedKeys(keys, expand)
+
+      const gek = DaTreeRef.value?.getExpandedKeys()
+      console.log('当前已展开的KEY ==>', gek)
+    }
+    function doCheckedTree(keys, checked) {
+      DaTreeRef.value?.setCheckedKeys(keys, checked)
+
+      const gek = DaTreeRef.value?.getCheckedKeys()
+      console.log('当前已选中的KEY ==>', gek)
+    }
+    function handleTreeChange(allSelectedKeys, currentItem) {
+      console.log('handleTreeChange ==>', allSelectedKeys, currentItem)
+    }
+    function handleExpandChange(expand, currentItem) {
+      console.log('handleExpandChange ==>', expand, currentItem)
+    }
+    return {
+      DaTreeRef,
+      roomTreeData,
+      defaultCheckedKeysValue,
+      defaultCheckedKeysValue2,
+      defaultExpandKeysValue3,
+      handleTreeChange,
+      handleExpandChange,
+      GetApiData,
+      doExpandTree,
+      doCheckedTree,
+    }
+  },
+})
+```
+
+** 更多示例请下载/导入示例项目 ZIP 查看 **
+
+### 组件参数
+
+| 属性                | 类型                            | 默认值     | 必填 | 说明                                                                         |
+| :------------------ | :------------------------------ | :--------- | :--- | :--------------------------------------------------------------------------- |
+| data                | `Array`                         | -          | 是   | 树的数据                                                                     |
+| themeColor          | `String`                        | `#007aff`  | 否   | 主题色,十六进制                                                             |
+| defaultCheckedKeys  | `Array` \| `Number` \| `String` | -          | 否   | 默认选中的节点,单选为单个 key,多选为 key 的数组                            |
+| showCheckbox        | `Boolean`                       | `false`    | 否   | 是否开启多选,默认单选                                                       |
+| checkStrictly       | `Boolean`                       | `false`    | 否   | 多选时,是否执行父子不关联的任意勾选,默认父子关联                           |
+| showRadioIcon       | `Boolean`                       | `true`     | 否   | 是否显示单选图标,默认显示                                                   |
+| onlyRadioLeaf       | `Boolean`                       | `true`     | 否   | 单选时只允许选中末级,默认可随意选中                                         |
+| defaultExpandAll    | `Boolean`                       | `false`    | 否   | 是否默认展开全部                                                             |
+| defaultExpandedKeys | `Array`                         | -          | 否   | 默认展开的节点                                                               |
+| indent              | `Number`                        | `40`       | 否   | 子项缩进距离,单位 rpx                                                       |
+| checkboxPlacement   | `String`                        | `left`     | 否   | 选择框的位置,可选 left/right                                                |
+| loadMode            | `Boolean`                       | `false`    | 否   | 为 true 时,空的 children 数组会显示展开图标                                 |
+| loadApi             | `Function`                      | -          | 否   | 选择框的位置,可选 left/right                                                |
+| checkedDisabled     | `Boolean`                       | `false`    | 否   | 是否渲染禁用值,默认不渲染                                                   |
+| packDisabledkey     | `Boolean`                       | `true`     | 否   | 是否返回已禁用的但已选中的 key,默认返回禁用已选值                           |
+| expandChecked       | `Boolean`                       | `false`    | 否   | 是否自动展开到选中的节点,默认不展开                                         |
+| alwaysFirstLoad     | `Boolean`                       | `false`    | 否   | 是否总在首次的时候加载一下内容,默认不加载,否则只有展开末级节点才会加载数据 |
+| isLeafFn            | `Function`                      | -          | 否   | 自定义函数返回来控制数据项的末项                                             |
+| field               | `Object`                        | -          | 否   | 字段对应内容,格式参考下方(1.5.0 后移除,请用单独的字段匹配)                 |
+| labelField          | `String`                        | `label`    | 否   | 指定节点对象中某个属性为标签字段,默认`label`                                |
+| valueField          | `String`                        | `value`    | 否   | 指定节点对象中某个属性为值字段,默认`value`                                  |
+| childrenField       | `String`                        | `children` | 否   | 指定节点对象中某个属性为子树节点字段,默认`children`                         |
+| disabledField       | `String`                        | `disabled` | 否   | 指定节点对象中某个属性为禁用字段,默认`disabled`                             |
+| appendField         | `String`                        | `append`   | 否   | 指定节点对象中某个属性为副标签字段,默认`append`                             |
+| leafField           | `String`                        | `leaf`     | 否   | 指定节点对象中某个属性为末级节点字段,默认`leaf`                             |
+| sortField           | `String`                        | `sort`     | 否   | 指定节点对象中某个属性为排序字段,默认`sort`                                 |
+| filterValue         | `String`                        | -          | 否   | 搜索筛选的关键词,通过输入关键词筛选内容                                     |
+
+**field 格式(1.5.0 后移除,请用单独的字段匹配)**
+
+```js
+{
+  label: 'label',
+  key: 'key',
+  children: 'children',
+  disabled: 'disabled',
+  append: 'append'
+}
+```
+
+### 组件事件
+
+| 事件名称 | 回调参数                                | 说明            |
+| :------- | :-------------------------------------- | :-------------- |
+| change   | `(allCheckedKeys, currentItem) => void` | 选中时回调      |
+| expand   | `(expandState, currentItem) => void`    | 展开/收起时回调 |
+
+### 组件方法
+
+| 方法名称            | 参数             | 说明                                                                                              |
+| :------------------ | :--------------- | :------------------------------------------------------------------------------------------------ |
+| setCheckedKeys      | `(keys,checked)` | 设置指定 key 的节点选中/取消选中的状态。注: keys 单选时为 key,多选时为 key 的数组                |
+| setExpandedKeys     | `(keys,expand)`  | 设置指定 key 的节点展开/收起的状态,当 keys 为 all 时即代表展开/收起全部。注:keys 为数组或 `all` |
+| getCheckedKeys      | -                | 返回已选的 key                                                                                    |
+| getHalfCheckedKeys  | -                | 返回半选的 key                                                                                    |
+| getUncheckedKeys    | -                | 返回未选的 key                                                                                    |
+| getCheckedNodes     | -                | 返回已选的节点                                                                                    |
+| getUncheckedNodes   | -                | 返回未选的节点                                                                                    |
+| getHalfCheckedNodes | -                | 返回半选的节点                                                                                    |
+| getExpandedKeys     | -                | 返回已展开的 key                                                                                  |
+| getUnexpandedKeys   | -                | 返回未展开的 key                                                                                  |
+| getExpandedNodes    | -                | 返回已展开的节点                                                                                  |
+| getUnexpandedNodes  | -                | 返回未展开的节点                                                                                  |
+
+### 组件版本
+
+v1.4.2
+
+### 差异化
+
+已通过测试
+
+> - H5 页面
+> - 微信小程序
+> - 支付宝、钉钉小程序
+> - 字节跳动、抖音、今日头条小程序
+> - 百度小程序
+> - 飞书小程序
+> - QQ 小程序
+> - 京东小程序
+
+未测试
+
+> - 快手小程序由于非企业用户暂无演示
+> - 快应用、360 小程序因 Vue3 支持的原因暂无演示
+
+### 开发组
+
+[@CRLANG](https://crlang.com)

+ 150 - 0
components/da-tree/utils.ts

@@ -0,0 +1,150 @@
+/** 未选 */
+export const unCheckedStatus = 0
+/** 半选 */
+export const halfCheckedStatus = 1
+/** 选中 */
+export const isCheckedStatus = 2
+
+/**
+ * 深拷贝内容
+ * @param originData 拷贝对象
+ * @author crlang(https://crlang.com)
+ */
+export function deepClone(originData) {
+  const type = Object.prototype.toString.call(originData)
+  let data
+  if (type === '[object Array]') {
+    data = []
+    for (let i = 0; i < originData.length; i++) {
+      data.push(deepClone(originData[i]))
+    }
+  } else if (type === '[object Object]') {
+    data = {}
+    for (const prop in originData) {
+      // eslint-disable-next-line no-prototype-builtins
+      if (originData.hasOwnProperty(prop)) { // 非继承属性
+        data[prop] = deepClone(originData[prop])
+      }
+    }
+  } else {
+    data = originData
+  }
+  return data
+}
+
+/**
+ * 获取所有指定的节点
+ * @param type
+ * @param value
+ * @author crlang(https://crlang.com)
+ */
+export function getAllNodes(list, type, value, packDisabledkey = true) {
+  if (!list || list.length === 0) {
+    return []
+  }
+
+  const res = []
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i]
+    if (item[type] === value) {
+      if ((packDisabledkey && item.disabled) || !item.disabled) {
+        res.push(item)
+      }
+    }
+  }
+
+  return res
+}
+
+/**
+ * 获取所有指定的key值
+ * @param type
+ * @param value
+ * @author crlang(https://crlang.com)
+ */
+export function getAllNodeKeys(list, type, value, packDisabledkey = true) {
+  if (!list || list.length === 0) {
+    return null
+  }
+
+  const res = []
+  for (let i = 0; i < list.length; i++) {
+    const item = list[i]
+    if (item[type] === value) {
+      if ((packDisabledkey && item.disabled) || !item.disabled) {
+        res.push(item.key)
+      }
+    }
+  }
+
+  return res.length ? res : null
+}
+
+/**
+ * 错误输出
+ *
+ * @param msg
+ */
+export function logError(msg, ...args) {
+  console.error(`DaTree: ${msg}`, ...args)
+}
+
+const toString = Object.prototype.toString
+
+export function is(val, type) {
+  return toString.call(val) === `[object ${type}]`
+}
+
+/**
+ * 是否对象(Object)
+ * @param val
+
+ */
+export function isObject(val) {
+  return val !== null && is(val, 'Object')
+}
+
+/**
+ * 是否数字(Number)
+ * @param val
+
+ */
+export function isNumber(val) {
+  return is(val, 'Number')
+}
+
+/**
+ * 是否字符串(String)
+ * @param val
+
+ */
+export function isString(val) {
+  return is(val, 'String')
+}
+
+/**
+ * 是否函数方法(Function)
+ * @param val
+
+ */
+export function isFunction(val) {
+  return typeof val === 'function'
+}
+
+/**
+ * 是否布尔(Boolean)
+ * @param val
+
+ */
+export function isBoolean(val) {
+  return is(val, 'Boolean')
+}
+
+/**
+ * 是否数组(Array)
+ * @param val
+
+ */
+export function isArray(val) {
+  return val && Array.isArray(val)
+}

+ 1 - 0
locale/zh-Hans.json

@@ -410,6 +410,7 @@
   "ruiDu.detailTitle": "日报详情",
   "ruiDu.editTitle": "日报填报",
   "ruiDu.approvalTitle": "日报填报",
+  "ruiDu.createTitle": "新增日报",
   "ruiDu.shiftLeader": "带班干部",
   "ruiDu.reportName": "日报名称",
   "ruiDu.project": "项目",

+ 7 - 0
pages.json

@@ -249,6 +249,13 @@
         "navigationBarTitleText": "%ruiDu.indexTitle%"
       }
     },
+    {
+      // 瑞都日报-列表
+      "path": "pages/ruiDu/create",
+      "style": {
+        "navigationBarTitleText": "%ruiDu.createTitle%"
+      }
+    },
     {
       // 瑞都日报-编辑
       "path": "pages/ruiDu/approval",

+ 1 - 1
pages/home/index.vue

@@ -391,7 +391,7 @@ import { useDeviceStore } from "@/store/modules/device";
 import { messageNavigate } from "@/utils/navigate";
 import Upgrade from "@/components/upgrade.vue";
 
-const navigatorTo = url => {
+const navigatorTo = (url) => {
   uni.navigateTo({
     url: url,
   });

+ 74 - 59
pages/recordFilling/detail.vue

@@ -92,9 +92,9 @@
           class="item-content flex-col align-center justify-between"
           :class="{ 'bottom-bold': item.nonSumList.length > 0 }"
           v-for="nosum in item.nonSumList.filter(
-            nosum =>
+            (nosum) =>
               !keys.includes(nosum.description) &&
-              (companyName === 'ry'
+              (deptName === 'ry'
                 ? attrItem.description !== 'productionStatus'
                 : true)
           )">
@@ -197,7 +197,7 @@
                 :clearable="false"
                 :disabled="!isView"
                 v-model.number="nosum.fillContent"
-                @input="val => onInputChange(val, nosum)" />
+                @input="(val) => onInputChange(val, nosum)" />
               <uni-easyinput
                 v-else
                 type="textarea"
@@ -281,7 +281,7 @@
                   v-bind="defaultProps"
                   :disabled="!isView"
                   v-model.number="detail.currentDepth"
-                  @input="val => inputCurrentDepth(item.nonSumList)" />
+                  @input="(val) => inputCurrentDepth(item.nonSumList)" />
               </uni-forms-item>
               <uni-forms-item
                 label="详情"
@@ -385,7 +385,7 @@ const handleClickTimeRangeItem = (index, list) => {
   reportDetailsTimeRangeRef.value.open();
 };
 
-const calculateDuration = row => {
+const calculateDuration = (row) => {
   if (!row.startTime || !row.endTime) {
     row.duration = 0;
     return;
@@ -404,7 +404,7 @@ const calculateDuration = row => {
   row.duration = Number((diffMinutes / 60).toFixed(2));
 };
 
-const reportDetailsTimeRange = data => {
+const reportDetailsTimeRange = (data) => {
   reportDetails.value[reportDetailIndex.value].startTime = data[0];
   reportDetails.value[reportDetailIndex.value].endTime = data[1];
 
@@ -431,7 +431,7 @@ const addProductionStatusRow = () => {
   });
 };
 
-const removeReportDetailRow = index => {
+const removeReportDetailRow = (index) => {
   if (index === 0) {
     uni.showToast({ title: "至少填写一条生产动态", icon: "none" });
     return;
@@ -462,14 +462,14 @@ const inputCurrentDepth = useDebounceFn(function inputCurrentDepth(list) {
     });
 
     const currentDepth = list.find(
-      item => item?.description === "currentDepth"
+      (item) => item?.description === "currentDepth"
     );
     if (currentDepth) currentDepth.fillContent = latestDetail.currentDepth;
   }
 }, 300);
 
-const showDepth = computed(() => list => {
-  return list.some(item => item.description === "currentDepth");
+const showDepth = computed(() => (list) => {
+  return list.some((item) => item.description === "currentDepth");
 });
 
 const NON_KEYS = [
@@ -508,12 +508,12 @@ const keys = [
   "otherNptReason",
 ];
 
-const sumNonProdTimes = index => {
+const sumNonProdTimes = (index) => {
   let sum = 0;
   const { nonSumList } = dataList.value[index];
-  NON_KEYS.forEach(field => {
+  NON_KEYS.forEach((field) => {
     sum += Number(
-      (nonSumList || []).find(item => item.description === field)
+      (nonSumList || []).find((item) => item.description === field)
         ?.fillContent || 0
     );
   });
@@ -532,17 +532,19 @@ const handleInputRaw = (val, item) => {
 const onInputChange = debounce(handleInputRaw, 500);
 
 // 校验函数:总时间必须为 24
-const ryValidateTotalTime = index => (rule, value, data, callback) => {
+const ryValidateTotalTime = (index) => (rule, value, data, callback) => {
   const { nonSumList } = dataList.value[index];
 
   const drillingTime = Number(
-    (nonSumList || []).find(item => item.description === "drillingWorkingTime")
-      ?.fillContent || 0
+    (nonSumList || []).find(
+      (item) => item.description === "drillingWorkingTime"
+    )?.fillContent || 0
   );
 
   const otherTime = Number(
-    (nonSumList || []).find(item => item.description === "otherProductionTime")
-      ?.fillContent || 0
+    (nonSumList || []).find(
+      (item) => item.description === "otherProductionTime"
+    )?.fillContent || 0
   );
 
   const nonProdSum = sumNonProdTimes(index);
@@ -559,11 +561,11 @@ const ryValidateTotalTime = index => (rule, value, data, callback) => {
   return true;
 };
 
-const rhValidateTotalTime = index => (rule, value, data, callback) => {
+const rhValidateTotalTime = (index) => (rule, value, data, callback) => {
   const { nonSumList } = dataList.value[index];
 
   const gasTime = Number(
-    (nonSumList || []).find(item => item.description === "dailyInjectGasTime")
+    (nonSumList || []).find((item) => item.description === "dailyInjectGasTime")
       ?.fillContent || 0
   );
 
@@ -584,11 +586,11 @@ const rhValidateTotalTime = index => (rule, value, data, callback) => {
 };
 
 const rules = reactive({
-  drillingWorkingTime: index => [
+  drillingWorkingTime: (index) => [
     { required: true, errorMessage: "请输入进尺工作时间" },
     { validateFunction: ryValidateTotalTime(index) },
   ],
-  dailyInjectGasTime: index => [
+  dailyInjectGasTime: (index) => [
     { required: true, errorMessage: "请输入当日运转时间" },
     { validateFunction: rhValidateTotalTime(index) },
   ],
@@ -612,7 +614,7 @@ onReady(() => {
   console.log("onReady");
 });
 
-onLoad(async option => {
+onLoad(async (option) => {
   console.log("onLoad", option);
   await reloginByUserId(option.reloginUserId);
   isFromMsg.value = !!option.reloginUserId;
@@ -630,7 +632,7 @@ onLoad(async option => {
       wellNamePair: {},
     };
 
-    taskOptions.value = Object.keys(daily.wellNamePair ?? {}).map(key => ({
+    taskOptions.value = Object.keys(daily.wellNamePair ?? {}).map((key) => ({
       text: daily.wellNamePair[key],
       value: Number(key),
     }));
@@ -669,7 +671,7 @@ const totalNum = ref(0);
 // 监听dataList变化,初始化时计算一次总和
 watch(
   dataList,
-  newVal => {
+  (newVal) => {
     // calculateTotalRunTime();
   },
   { deep: true }
@@ -725,9 +727,9 @@ const debouncedApplyAccumulatedToReport = debounce(
  */
 const updateAccumulatedValue = (fieldName, deviceType) => {
   let total = 0;
-  dataList.value.forEach(item => {
+  dataList.value.forEach((item) => {
     if (item.deviceName.includes(deviceType) && item.nonSumList) {
-      item.nonSumList.forEach(nonSum => {
+      item.nonSumList.forEach((nonSum) => {
         if (
           nonSum.type === "double" &&
           nonSum.name === fieldName &&
@@ -767,7 +769,7 @@ function applyAccumulatedToReport() {
   recalculateAllAccumulatedValues();
 
   const reportItem = dataList.value.find(
-    item => item.deviceName === "生产日报"
+    (item) => item.deviceName === "生产日报"
   );
 
   if (!reportItem) {
@@ -775,9 +777,9 @@ function applyAccumulatedToReport() {
     return;
   }
 
-  Object.keys(accumulatedValues).forEach(fieldName => {
+  Object.keys(accumulatedValues).forEach((fieldName) => {
     const targetItem = reportItem.nonSumList.find(
-      item => item.name === fieldName
+      (item) => item.name === fieldName
     );
     if (targetItem) {
       console.log(
@@ -850,7 +852,7 @@ function calculateTotalRunTime(
   targetFieldName
 ) {
   const reportItem = dataList.value.find(
-    item => item.deviceName === "生产日报"
+    (item) => item.deviceName === "生产日报"
   );
 
   if (!reportItem) {
@@ -859,7 +861,7 @@ function calculateTotalRunTime(
   }
 
   const targetItem = reportItem.nonSumList.find(
-    item => item.name === targetFieldName
+    (item) => item.name === targetFieldName
   );
 
   if (!targetItem) {
@@ -869,9 +871,9 @@ function calculateTotalRunTime(
 
   let total = 0;
 
-  dataList.value.forEach(item => {
+  dataList.value.forEach((item) => {
     if (item.deviceName.includes(deviceNameToMatch) && item.nonSumList) {
-      item.nonSumList.forEach(nonSum => {
+      item.nonSumList.forEach((nonSum) => {
         if (
           nonSum.type === "double" &&
           nonSum.name === sourceFieldName &&
@@ -889,7 +891,7 @@ function calculateTotalRunTime(
   targetItem.fillContent = toFixed(total);
 }
 
-const formatT = arr =>
+const formatT = (arr) =>
   `${arr[0].toString().padStart(2, "0")}:${arr[1].toString().padStart(2, "0")}`;
 
 // @query所绑定的方法不要自己调用!!需要刷新列表数据时,只需要调用paging.value.reload()即可
@@ -907,14 +909,14 @@ const queryList = (pageNo, pageSize) => {
     orderId: params.value.orderId,
     // deviceCategoryId: 1,
   })
-    .then(async res => {
+    .then(async (res) => {
       console.log("🚀 ~ res:", res);
       const { data } = res;
       const resList = [].concat(data.list);
       // 列表总数
       totalNum.value = data.total;
       // 遍历列表,处理attrsDetail
-      resList.map(async item => {
+      resList.map(async (item) => {
         const attrParams = {
           deviceCode: item.deviceCode,
           deviceName: item.deviceName,
@@ -938,14 +940,14 @@ const queryList = (pageNo, pageSize) => {
           attrParams.id = attrParams.orderId;
           delete attrParams.orderId;
           delete attrParams.deviceName;
-          resAttrs.map(rtem => {
+          resAttrs.map((rtem) => {
             // 将rtem中sumList和nonSumList两个数组中的
             // fillContent字段判断是否为null, 如果为null,则赋值为0 不为null则保留两位小数
             // 将attrParams合并到两个数组的每个对象中
             // 然后将sumList和nonSumList分别赋值给item的sumList和nonSumList
 
             if (rtem.sumList) {
-              rtem.sumList.map(sumItem => {
+              rtem.sumList.map((sumItem) => {
                 if (sumItem.fillContent == null || sumItem.fillContent == "") {
                   // console.log("🚀 ~ rtem.sumList.map ~ sumItem:", sumItem);
                   // sumItem.fillContent = 0;
@@ -965,7 +967,7 @@ const queryList = (pageNo, pageSize) => {
             }
             if (rtem.nonSumList) {
               //
-              rtem.nonSumList.map(nonSumItem => {
+              rtem.nonSumList.map((nonSumItem) => {
                 if (
                   nonSumItem.fillContent == null ||
                   nonSumItem.fillContent == ""
@@ -994,7 +996,7 @@ const queryList = (pageNo, pageSize) => {
                       ? getIntDictOptions(nonSumItem.description)
                       : getStrDictOptions(nonSumItem.description);
 
-                  nonSumItem.enumList = dictOptions.map(dict => {
+                  nonSumItem.enumList = dictOptions.map((dict) => {
                     return {
                       ...dict,
                       text: dict.label,
@@ -1018,11 +1020,11 @@ const queryList = (pageNo, pageSize) => {
             }
             item.sumList = rtem.sumList;
 
-            const timeKeys = keys.filter(k => k !== "otherNptReason");
+            const timeKeys = keys.filter((k) => k !== "otherNptReason");
 
             item.nonSumList = rtem.nonSumList
               .sort((a, b) => a.modelId - b.modelId)
-              .map(item => {
+              .map((item) => {
                 if (timeKeys.includes(item.description)) {
                   item.fillContent = Number(item.fillContent || 0);
                 }
@@ -1043,13 +1045,13 @@ const queryList = (pageNo, pageSize) => {
       );
 
       // 如果加载的数据中包含生产日报,应用累加值
-      const hasReport = resList.some(item => item.deviceName === "生产日报");
+      const hasReport = resList.some((item) => item.deviceName === "生产日报");
 
       if (hasReport) {
         applyAccumulatedToReport();
 
-        reportDetailsGet(params.value.orderId).then(res => {
-          reportDetails.value = (res.data ?? []).map(item => ({
+        reportDetailsGet(params.value.orderId).then((res) => {
+          reportDetails.value = (res.data ?? []).map((item) => ({
             reportDate: item.reportDate ?? dayjs(params.createTime).valueOf(),
             startTime: formatT(item.startTime),
             endTime: formatT(item.endTime),
@@ -1067,7 +1069,7 @@ const queryList = (pageNo, pageSize) => {
         });
       }
     })
-    .catch(res => {
+    .catch((res) => {
       // 如果请求失败写paging.value.complete(false);
       // 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
       // 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
@@ -1112,7 +1114,7 @@ const checkRdThreshold = (item, totalValue, maxIncrement, itemName) => {
 };
 
 // 判断是否小于阈值 (<0)
-const checkLessThreshold = item => {
+const checkLessThreshold = (item) => {
   if (item.fillContent < 0) {
     uni.showToast({
       title:
@@ -1126,7 +1128,7 @@ const checkLessThreshold = item => {
   }
 };
 // 判断是否大于阈值
-const checkThreshold = item => {
+const checkThreshold = (item) => {
   checkLessThreshold(item);
   // 如果threshold > 0,则判断fillContent是否大于threshold,如果大于则提示用户填写小于等于threshold的值
   if (item.fillContent > item.threshold) {
@@ -1143,7 +1145,7 @@ const checkThreshold = item => {
 };
 
 // 保留两位小数
-const toFixed = num => {
+const toFixed = (num) => {
   if (num) {
     num = Number(num);
     num = num.toFixed(2);
@@ -1437,13 +1439,26 @@ const onSubmit = async () => {
 
     // 查找当日运转时间H项目
 
-    const otherNptTime = nonSumList.find(i => i.name === "其他非生产时间H");
-    const runtimeItem = nonSumList.find(i => i.name === "当日运转时间H");
+    const otherNptTime = nonSumList.find((i) => i.name === "其他非生产时间H");
+    const runtimeItem = nonSumList.find((i) => i.name === "当日运转时间H");
     const isRuntime24 =
       runtimeItem &&
       (runtimeItem.fillContent == 24 || runtimeItem.fillContent == "24");
     for (const nonSumItem of nonSumList) {
       // 增加判断条件:如果当日运转时间H等于24,则非生产原因和非生产时间H为非必填,否则为必填
+      if (
+        nonSumItem.description === "dailyGasInjection" &&
+        deptName.value === "rh"
+      ) {
+        if (runtimeItem.fillContent > 0 && nonSumItem.fillContent > 0) {
+        } else {
+          uni.showToast({
+            title: "当日运转时间大于0,注气量也需要大于0",
+            icon: "none",
+          });
+          return;
+        }
+      }
 
       if (
         nonSumItem.name === "其他非生产时间原因" &&
@@ -1515,7 +1530,7 @@ const onSubmit = async () => {
         // 新增:针对rd公司的特殊阈值检查
         // 检查包含"累计公里数填报"的字段不能超过对应"累计公里数"字段 + 3000
         if (nonSumItem.name.includes("累计公里数填报")) {
-          const correspondingSumItem = item.sumList.find(sumItem =>
+          const correspondingSumItem = item.sumList.find((sumItem) =>
             sumItem.name.includes("累计公里数")
           );
           if (correspondingSumItem) {
@@ -1525,7 +1540,7 @@ const onSubmit = async () => {
         }
         // 检查包含"累计运转时长填报"的字段不能超过对应"累计运转时长"字段 + 100
         else if (nonSumItem.name.includes("累计运转时长填报")) {
-          const correspondingSumItem = item.sumList.find(sumItem =>
+          const correspondingSumItem = item.sumList.find((sumItem) =>
             sumItem.name.includes("累计运转时长")
           );
           if (correspondingSumItem) {
@@ -1553,7 +1568,7 @@ const onSubmit = async () => {
       showCancel: true,
       cancelText: "取消",
       confirmText: "继续",
-      success: res => {
+      success: (res) => {
         if (res.confirm) {
           // 用户选择继续,执行保存操作,此时才设置按钮禁用
           submitDataWithDisable();
@@ -1610,7 +1625,7 @@ const submitDataWithDisable = async () => {
           // 新增:针对rd公司的特殊阈值检查
           // 检查包含"累计公里数填报"的字段不能超过对应"累计公里数"字段 + 3000
           if (nonSumItem.name.includes("累计公里数填报")) {
-            const correspondingSumItem = item.sumList.find(sumItem =>
+            const correspondingSumItem = item.sumList.find((sumItem) =>
               sumItem.name.includes("累计公里数")
             );
             if (correspondingSumItem) {
@@ -1621,7 +1636,7 @@ const submitDataWithDisable = async () => {
           }
           // 检查包含"累计运转时长填报"的字段不能超过对应"累计运转时长"字段 + 100
           else if (nonSumItem.name.includes("累计运转时长填报")) {
-            const correspondingSumItem = item.sumList.find(sumItem =>
+            const correspondingSumItem = item.sumList.find((sumItem) =>
               sumItem.name.includes("累计运转时长")
             );
             if (correspondingSumItem) {
@@ -1666,14 +1681,14 @@ const submitData = async () => {
 
     console.log("处理提交用的副本数据:subDataList", subDataList);
     // 2. 处理提交数据:将nonSumList和sumList合并为新数组并赋值给deviceInfoList对象,将所有的deviceInfoList合并为submitList
-    const submitList = subDataList.map(item => ({
+    const submitList = subDataList.map((item) => ({
       deviceInfoList: [].concat(item.sumList).concat(item.nonSumList),
     }));
     console.log("提交用的数据:submitList", submitList);
     // 3. 提交所有填写记录
     const reqData = {
       createReqVO: submitList,
-      reportDetails: reportDetails.value.map(item => ({
+      reportDetails: reportDetails.value.map((item) => ({
         ...item,
         taskId: taskId.value,
       })),

+ 784 - 0
pages/ruiDu/create.vue

@@ -0,0 +1,784 @@
+<script setup>
+import { ref, reactive, getCurrentInstance, computed } from "vue";
+import tpfTimeRange from "@/components/tpf-time-range/tpf-time-range.vue";
+import { getDeptId, getUserInfo } from "@/utils/auth.js";
+import { selectedDeptsEmployee, specifiedSimpleDepts } from "@/api/index.js";
+import { onLoad } from "@dcloudio/uni-app";
+import { useDataDictStore } from "@/store/modules/dataDict";
+import { createIotRdDailyReport, getRuiDuReportDetail } from "@/api/ruiDu.js";
+import DaTree from "@/components/da-tree/index.vue";
+
+const { appContext } = getCurrentInstance();
+const t = appContext.config.globalProperties.$t;
+
+const NON_PROD_FIELDS = [
+  { key: "repairTime", label: "设备故障" },
+  { key: "selfStopTime", label: "设备保养" },
+  { key: "accidentTime", label: "工程质量" },
+  { key: "complexityTime", label: "技术受限" },
+  { key: "rectificationTime", label: "生产组织" },
+  { key: "waitingStopTime", label: "不可抗力" },
+  { key: "partyaDesign", label: "甲方设计" },
+  { key: "partyaPrepare", label: "甲方准备" },
+  { key: "partyaResource", label: "甲方资源" },
+  { key: "relocationTime", label: "生产配合" },
+  { key: "winterBreakTime", label: "待命" },
+  { key: "otherNptTime", label: "其他非生产时间" },
+];
+
+const defaultProps = {
+  inputBorder: false,
+  clearable: false,
+  placeholder: "请输入",
+  style: {
+    "text-align": "right",
+  },
+  styles: {
+    disableColor: "#fff",
+  },
+};
+
+const original = {
+  deptId: getDeptId(),
+  startTime: "08:00",
+  endTime: "08:00",
+  ...NON_PROD_FIELDS.reduce((acc, field) => ({ ...acc, [field.key]: 0 }), {}),
+  winterBreakTime: 24,
+};
+
+const formRef = ref();
+
+const formDataBaseRules = reactive({
+  deptId: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请选择施工部门",
+      },
+    ],
+  },
+  constructionBrief: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请输入施工简报",
+      },
+    ],
+  },
+  createTime: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请选择施工时间",
+      },
+    ],
+  },
+  location: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请输入施工地点",
+      },
+    ],
+  },
+  responsiblePerson: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请选择带班干部",
+      },
+    ],
+  },
+  rdStatus: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请选择施工状态",
+      },
+    ],
+  },
+  dailyFuel: {
+    rules: [
+      {
+        required: true,
+        errorMessage: "请输入当日油耗",
+      },
+    ],
+  },
+  startTime: {
+    rules: [
+      {
+        required: true,
+        errorMessage: `${t("operation.PleaseSelect")}${t("ruiDu.timeNode")}`,
+      },
+    ],
+  },
+  endTime: {
+    rules: [
+      {
+        required: true,
+        errorMessage: `${t("operation.PleaseSelect")}${t("ruiDu.timeNode")}`,
+      },
+    ],
+  },
+  productionStatus: {
+    rules: [
+      {
+        required: true,
+        errorMessage: `${t("operation.PleaseFillIn")}${t(
+          "ruiDu.dailyProductionDynamic"
+        )}`,
+      },
+    ],
+  },
+  nextPlan: {
+    rules: [
+      {
+        required: true,
+        errorMessage: `请输入下步工作计划`,
+      },
+    ],
+  },
+});
+
+function debounce(func, delay) {
+  let timer;
+  return function (...args) {
+    if (timer) clearTimeout(timer);
+    timer = setTimeout(() => func.apply(this, args), delay);
+  };
+}
+
+const handleInputRaw = (val, field) => {
+  let num = Number(val);
+  if (val === "" || isNaN(num) || num < 0) {
+    num = 0;
+  } else if (num > 24) {
+    num = 24;
+  }
+  form.value[field] = num;
+};
+
+const onInputChange = debounce(handleInputRaw, 500);
+
+const form = ref({ ...original });
+
+const userOptions = ref([]);
+
+const submitterNames = ref("");
+
+async function loadUserOptions() {
+  const res = await selectedDeptsEmployee({ deptIds: [getDeptId()] });
+  userOptions.value = res.data.map((item) => ({
+    text: item.nickname,
+    value: item.id,
+    raw: item,
+  }));
+}
+
+const deptOptions = ref([]);
+const treeData = ref([]);
+const deptLoading = ref(false);
+
+const handleTree = (data, id, parentId, children) => {
+  if (!Array.isArray(data)) {
+    console.warn("data must be an array");
+    return [];
+  }
+  const config = {
+    id: id || "id",
+    parentId: parentId || "parentId",
+    childrenList: children || "children",
+  };
+
+  const childrenListMap = {};
+  const nodeIds = {};
+  const tree = [];
+
+  for (const d of data) {
+    const parentId = d[config.parentId];
+    if (childrenListMap[parentId] == null) {
+      childrenListMap[parentId] = [];
+    }
+    nodeIds[d[config.id]] = d;
+    childrenListMap[parentId].push(d);
+  }
+
+  for (const d of data) {
+    const parentId = d[config.parentId];
+    if (nodeIds[parentId] == null) {
+      tree.push(d);
+    }
+  }
+
+  for (const t of tree) {
+    adaptToChildrenList(t);
+  }
+
+  function adaptToChildrenList(o) {
+    if (childrenListMap[o[config.id]] !== null) {
+      o[config.childrenList] = childrenListMap[o[config.id]];
+    }
+    if (o[config.childrenList]) {
+      for (const c of o[config.childrenList]) {
+        adaptToChildrenList(c);
+      }
+    }
+  }
+
+  return tree;
+};
+
+async function loadDept() {
+  deptLoading.value = true;
+  try {
+    function sortTreeBySort(treeNodes) {
+      if (!treeNodes || !Array.isArray(treeNodes)) return treeNodes;
+      const sortedNodes = [...treeNodes].sort((a, b) => {
+        const sortA = a.sort != null ? a.sort : 999999;
+        const sortB = b.sort != null ? b.sort : 999999;
+        return sortA - sortB;
+      });
+
+      sortedNodes.forEach((node) => {
+        node.disabled = node.type !== "3";
+        if (node.children && Array.isArray(node.children)) {
+          node.children = sortTreeBySort(node.children);
+        }
+      });
+      return sortedNodes;
+    }
+
+    const depts = await specifiedSimpleDepts(getDeptId());
+
+    deptOptions.value = depts.data.map((item) => ({
+      text: item.name,
+      value: item.id,
+      raw: item,
+    }));
+    treeData.value = sortTreeBySort(handleTree(depts.data));
+  } catch (error) {
+  } finally {
+    deptLoading.value = false;
+  }
+}
+
+const popup = ref();
+
+const openPopup = () => {
+  popup.value.open();
+};
+
+const selectDeptId = ref("");
+
+function handleTreeChange(values) {
+  selectDeptId.value = values;
+}
+
+function handleConfirm() {
+  form.value.deptId = selectDeptId.value;
+  popup.value.close();
+}
+
+const rdStatusOptions = ref([]);
+
+const dictStore = useDataDictStore();
+
+function loadRdStatusOptions() {
+  rdStatusOptions.value = dictStore.getStrDictOptions("rdStatus").map((v) => ({
+    text: v.label,
+    value: v.value,
+  }));
+}
+
+const formatT = (arr) =>
+  `${arr[0].toString().padStart(2, "0")}:${arr[1].toString().padStart(2, "0")}`;
+
+const id = ref();
+const isview = ref("create");
+
+const detail = ref();
+
+onLoad(async (query) => {
+  if (dictStore.dataDict.length <= 0) {
+    dictStore.loadDataDictList().then(() => {
+      loadRdStatusOptions();
+    });
+  } else loadRdStatusOptions();
+  await loadUserOptions();
+
+  id.value = query.id;
+  isview.value = query.isview ?? "create";
+
+  await loadDept();
+
+  const info = JSON.parse(JSON.parse(getUserInfo()));
+
+  submitterNames.value = info.user.nickname;
+
+  if (query.id && query.isview) {
+    const res = await getRuiDuReportDetail({ id: query.id });
+    detail.value = res.data;
+    submitterNames.value = res.data.submitterNames;
+    form.value = {
+      deptId: res.data.deptId,
+      location: res.data.location,
+      responsiblePerson: res.data.responsiblePerson,
+      startTime: formatT(res.data.startTime),
+      endTime: formatT(res.data.endTime),
+      rdStatus: res.data.rdStatus,
+      dailyFuel: res.data.dailyFuel,
+      productionStatus: res.data.productionStatus,
+      nextPlan: res.data.nextPlan,
+      externalRental: res.data.externalRental,
+      malfunction: res.data.malfunction,
+      otherNptReason: res.data.otherNptReason,
+      createTime: res.data.createTime,
+      constructionBrief: res.data.constructionBrief,
+      ...NON_PROD_FIELDS.reduce(
+        (acc, field) => ({ ...acc, [field.key]: res.data[field.key] }),
+        {}
+      ),
+    };
+  }
+});
+
+const reportDetailsTimeRangeRef = ref(null);
+const startTime = ref("00:00");
+const startDefaultTime = ref("08:00");
+const endTime = ref("24:00");
+const endDefaultTime = ref("08:00");
+
+const handleClickTimeRangeItem = () => {
+  reportDetailsTimeRangeRef.value.open();
+};
+
+const reportDetailsTimeRange = (data) => {
+  form.value.startTime = data[0];
+  form.value.endTime = data[1];
+};
+
+const formLoading = ref(false);
+
+async function submitForm() {
+  try {
+    await formRef.value.validate();
+
+    const totalNonProdTime = NON_PROD_FIELDS.reduce((sum, field) => {
+      return sum + (Number(form.value[field.key]) || 0);
+    }, 0);
+
+    if (totalNonProdTime > 24) {
+      uni.showToast({
+        title: "非生产时间总和不能大于24小时",
+        icon: "none",
+      });
+      return;
+    }
+
+    const otherTime = Number(form.value.otherNptTime) || 0;
+
+    if (otherTime >= 1) {
+      if (
+        !form.value.otherNptReason ||
+        form.value.otherNptReason.trim() === ""
+      ) {
+        uni.showToast({
+          title: "其他非生产时间大于0小时,请填写其他非生产原因",
+          icon: "none",
+          duration: 2000,
+        });
+        return;
+      }
+    }
+
+    await createIotRdDailyReport(form.value);
+
+    uni.showToast({ title: t("operation.success"), icon: "none" });
+    uni.navigateBack();
+  } catch (error) {
+    console.error("提交表单失败", error);
+  } finally {
+    formLoading.value = false;
+  }
+}
+
+const disabled = computed(() => {
+  if (isview.value !== "create") {
+    if (isview.value === "time") {
+      return !(
+        detail.value?.contractName === null && detail.value?.taskName === null
+      );
+    }
+
+    return true;
+  }
+
+  return false;
+});
+</script>
+<template>
+  <view class="page">
+    <scroll-view scroll-y="true" class="segmented-content">
+      <view class="content">
+        <uni-forms
+          ref="formRef"
+          labelWidth="auto"
+          :rules="formDataBaseRules"
+          :model="form"
+          validateTrigger="submit"
+          err-show-type="toast">
+          <uni-forms-item label="施工队伍" name="deptId" required>
+            <view class="deptName">
+              <uni-data-select
+                :clear="true"
+                align="right"
+                placeholder="请选择"
+                :localdata="deptOptions"
+                hideRight
+                placement="bottom"
+                :disabled="true"
+                v-model="form.deptId" />
+              <button
+                class="popup-button"
+                type="primary"
+                size="mini"
+                :disabled="deptLoading || disabled"
+                @click="openPopup">
+                选择
+              </button>
+            </view>
+          </uni-forms-item>
+          <uni-forms-item label="施工日期" name="createTime" required>
+            <uni-datetime-picker
+              type="date"
+              return-type="timestamp"
+              :clear-icon="true"
+              v-model="form.createTime"
+              :border="false"
+              :style="{
+                'text-align': 'right',
+                'float': 'right',
+              }"
+              :styles="{ disableColor: '#fff' }"
+              :disabled="disabled" />
+          </uni-forms-item>
+          <uni-forms-item label="施工地点" name="location" required>
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              v-model="form.location"
+              :disabled="disabled"
+              :maxlength="1000" />
+          </uni-forms-item>
+          <uni-forms-item label="带班干部" name="responsiblePerson" required>
+            <uni-data-select
+              :clear="true"
+              multiple
+              align="right"
+              placeholder="请选择"
+              :localdata="userOptions"
+              placement="bottom"
+              :disabled="disabled"
+              v-model="form.responsiblePerson" />
+          </uni-forms-item>
+          <uni-forms-item label="填报人" name="submitterNames">
+            <span class="readOnly">{{ submitterNames }}</span>
+          </uni-forms-item>
+          <uni-forms-item :label="`${$t('ruiDu.timeNode')}:`" required>
+            <view
+              class="item-content"
+              @click="!disabled && handleClickTimeRangeItem()">
+              <view
+                class="time-range-item"
+                v-if="form.startTime && form.endTime">
+                {{ form.startTime }} 至 {{ form.endTime }}
+              </view>
+              <view class="time-range-item" v-else> '请选择' </view>
+            </view>
+          </uni-forms-item>
+          <uni-forms-item label="施工状态" name="rdStatus" required>
+            <uni-data-select
+              :clear="true"
+              align="right"
+              placeholder="请选择"
+              :localdata="rdStatusOptions"
+              placement="bottom"
+              :disabled="disabled"
+              v-model="form.rdStatus" />
+          </uni-forms-item>
+          <uni-forms-item label="当日油耗(L)" name="dailyFuel" required>
+            <uni-easyinput
+              type="number"
+              v-bind="defaultProps"
+              :disabled="disabled"
+              v-model.number="form.dailyFuel" />
+          </uni-forms-item>
+          <uni-forms-item label="当日生产动态" name="productionStatus" required>
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              :disabled="disabled"
+              v-model="form.productionStatus"
+              :maxlength="1000" />
+          </uni-forms-item>
+          <uni-forms-item label="下步工作计划" name="nextPlan" required>
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              :disabled="disabled"
+              v-model="form.nextPlan"
+              :maxlength="1000" />
+          </uni-forms-item>
+          <uni-forms-item label="外租设备" name="externalRental">
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              :disabled="disabled"
+              v-model="form.externalRental"
+              :maxlength="1000" />
+          </uni-forms-item>
+          <uni-forms-item label="故障情况" name="malfunction">
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              :disabled="disabled"
+              v-model="form.malfunction"
+              :maxlength="1000" />
+          </uni-forms-item>
+          <uni-forms-item label="施工简报" name="constructionBrief" required>
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              v-model="form.constructionBrief"
+              :disabled="disabled"
+              :maxlength="1000" />
+          </uni-forms-item>
+
+          <uv-divider text="非生产时间" textPosition="left"></uv-divider>
+
+          <uni-forms-item
+            v-for="field in NON_PROD_FIELDS"
+            :key="field.key"
+            :label="field.label + '(H)'"
+            :name="field.key">
+            <uni-easyinput
+              type="number"
+              v-bind="defaultProps"
+              v-model.number="form[field.key]"
+              :disabled="isview === 'detail'"
+              @input="(val) => onInputChange(val, field.key)" />
+          </uni-forms-item>
+
+          <uni-forms-item label="其他非生产原因" name="otherNptReason">
+            <uni-easyinput
+              type="textarea"
+              autoHeight
+              v-bind="defaultProps"
+              v-model="form.otherNptReason"
+              :disabled="isview === 'detail'"
+              :maxlength="1000" />
+          </uni-forms-item>
+        </uni-forms>
+      </view>
+    </scroll-view>
+    <view class="segmented-footer">
+      <view class="footer-btn">
+        <button
+          :loading="formLoading"
+          class="confirm-btn"
+          type="primary"
+          :disabled="isview === 'detail'"
+          @click="submitForm()">
+          确定
+        </button>
+      </view>
+    </view>
+  </view>
+
+  <tpf-time-range
+    ref="reportDetailsTimeRangeRef"
+    :startTime="startTime"
+    :startDefaultTime="startDefaultTime"
+    :endTime="endTime"
+    :endDefaultTime="endDefaultTime"
+    @timeRange="reportDetailsTimeRange"></tpf-time-range>
+
+  <uni-popup ref="popup" type="bottom">
+    <view class="popup-content">
+      <view class="tree">
+        <DaTree
+          :data="treeData"
+          labelField="name"
+          valueField="id"
+          disabledField="disabled"
+          defaultExpandAll
+          checkedDisabled
+          :defaultCheckedKeys="form.deptId"
+          @change="handleTreeChange"></DaTree>
+      </view>
+      <button class="mini-btn" type="primary" @click="handleConfirm">
+        确定
+      </button>
+    </view>
+  </uni-popup>
+</template>
+<style lang="scss" scoped>
+@import "@/style/work-order-segmented.scss";
+.page {
+  padding-bottom: 0;
+}
+.content {
+  background-color: white;
+  padding: 16px 16px;
+  border-radius: 8px;
+  box-sizing: border-box;
+}
+
+.uni-forms {
+  margin-top: 10px;
+  height: 100%;
+
+  .uni-form {
+    height: 100%;
+  }
+
+  .uni-forms-item {
+    display: flex;
+    align-items: center;
+    flex: 1;
+    margin-bottom: 6px;
+    border-bottom: 1px dashed #cacccf;
+  }
+
+  :deep(.uni-forms-item__content) {
+    text-align: right;
+    .readOnly {
+      padding-right: 10px;
+    }
+  }
+
+  :deep(.uni-forms-item__label) {
+    height: 44px;
+    font-weight: 500;
+    font-size: 14px;
+    color: #333333 !important;
+    width: max-content !important;
+  }
+
+  :deep(.uni-select) {
+    border: none;
+    text-align: right;
+    padding-right: 0;
+    .uniui-bottom:before {
+      content: "\e6b5" !important;
+      font-size: 16px !important;
+    }
+  }
+
+  :deep(.uni-easyinput__content-textarea) {
+    min-height: inherit;
+    margin: 10px;
+  }
+
+  :deep(.is-disabled) {
+    color: #333333 !important;
+  }
+
+  :deep(.uni-date-editor--x__disabled) {
+    opacity: 1;
+    .uni-date__x-input {
+      color: #333333 !important;
+    }
+  }
+
+  :deep(.icon-calendar) {
+    display: none;
+  }
+
+  :deep(.uni-select--disabled) {
+    background-color: #fff;
+  }
+}
+
+.item-content {
+  display: flex;
+  align-items: center;
+  justify-content: end;
+}
+
+.time-range-item {
+  margin: 10px;
+}
+
+.footer-btn {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  padding: 0 16px;
+  height: 100%;
+
+  gap: 0 32px;
+
+  & > uni-button {
+    margin: 0;
+  }
+}
+
+:deep(.confirm-btn) {
+  height: 38px !important;
+  font-size: 16px !important;
+}
+
+:deep(.popup-button) {
+  width: 62px;
+  margin: 0;
+}
+
+:deep(.deptName) {
+  display: flex;
+  justify-content: end;
+  align-items: center;
+  gap: 0 10px;
+}
+
+@mixin flex {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: column;
+}
+
+@mixin height {
+  /* #ifndef APP-NVUE */
+  height: 100%;
+  /* #endif */
+  /* #ifdef APP-NVUE */
+  flex: 1;
+  /* #endif */
+}
+
+.popup-content {
+  @include flex;
+  justify-content: space-between;
+  padding: 15px;
+  height: 500px;
+  background-color: #fff;
+
+  .tree {
+    flex: 1;
+    max-height: 420px;
+  }
+
+  button {
+    width: 100%;
+    height: 44px;
+  }
+}
+</style>

+ 42 - 6
pages/ruiDu/index.vue

@@ -22,7 +22,15 @@
           </button>
         </uni-col>
       </uni-row>
+      <button
+        class="create-btn"
+        type="primary"
+        size="mini"
+        @click="navigatorCreate">
+        新增日报
+      </button>
     </template>
+
     <view class="list">
       <view class="item" v-for="(item, index) in dataList" :key="index">
         <view
@@ -154,7 +162,7 @@ import { getLoginUserInfo } from "@/api/login";
 const { getStrDictOptions } = useDataDictStore();
 // 填写状态
 const fillStatusDict = reactive({});
-getStrDictOptions("operation_fill_order_status").map(item => {
+getStrDictOptions("operation_fill_order_status").map((item) => {
   fillStatusDict[item.value] = item.label;
 });
 console.log("🚀 ~ getDataDictList ~ fillStatusDict:", fillStatusDict);
@@ -176,11 +184,11 @@ const queryList = (pageNo, pageSize) => {
     pageNo,
     pageSize,
   })
-    .then(res => {
+    .then((res) => {
       // 将请求结果通过complete传给z-paging处理,同时也代表请求结束,这一行必须调用
       paging.value.complete(res.data.list);
     })
-    .catch(res => {
+    .catch((res) => {
       // 如果请求失败写paging.value.complete(false);
       // 注意,每次都需要在catch中写这句话很麻烦,z-paging提供了方案可以全局统一处理
       // 在底层的网络请求抛出异常时,写uni.$emit('z-paging-error-emit');即可
@@ -190,20 +198,43 @@ const queryList = (pageNo, pageSize) => {
 const searchList = () => {
   paging.value.reload();
 };
-const navigatorDetail = item => {
+
+const navigatorCreate = () => {
+  uni.navigateTo({
+    url: "/pages/ruiDu/create",
+  });
+};
+
+const navigatorDetail = (item) => {
+  if (item.projectId === null && item.taskId === null) {
+    uni.navigateTo({
+      url: "/pages/ruiDu/create?id=" + item.id + "&isview=detail",
+    });
+    return;
+  }
   uni.navigateTo({
     url: "/pages/ruiDu/detail?id=" + item.id,
   });
 };
 const navigatorEdit = (item, istime = "false") => {
+  if (item.projectId === null && item.taskId === null) {
+    uni.navigateTo({
+      url:
+        "/pages/ruiDu/create?id=" +
+        item.id +
+        "&isview=" +
+        (istime === "true" ? "time" : "detail"),
+    });
+    return;
+  }
   uni.navigateTo({
     url: "/pages/ruiDu/edit?id=" + item.id + "&istime=" + istime,
   });
 };
-const formatDate = time => {
+const formatDate = (time) => {
   return dayjs(time).format("YYYY-MM-DD");
 };
-const formatTime = time => {
+const formatTime = (time) => {
   return dayjs(time).format("YYYY-MM-DD HH:mm:ss");
 };
 
@@ -288,4 +319,9 @@ getLoginUser();
   border-color: #e9e9eb;
   color: #909399;
 }
+
+.create-btn {
+  margin-top: 10px;
+  width: 100%;
+}
 </style>

+ 372 - 331
pages/ruihen/components/form.vue

@@ -1,238 +1,258 @@
 <script setup>
-  import { ref, computed, watch, nextTick } from 'vue';
-  import { onLoad } from '@dcloudio/uni-app';
-  import { getRuiHenReportDetail } from '@/api/ruihen';
-  import { useDataDictStore } from '@/store/modules/dataDict';
-
-  const props = defineProps({
-    type: {
-      type: String,
-      default: 'edit',
-    },
-  });
-
-  const NON_PROD_FIELDS = [
-    { key: 'repairTime', label: '设备故障' },
-    { key: 'selfStopTime', label: '设备保养' },
-    { key: 'accidentTime', label: '工程质量' },
-    { key: 'complexityTime', label: '技术受限' },
-    { key: 'rectificationTime', label: '生产组织' },
-    { key: 'waitingStopTime', label: '不可抗力' },
-    { key: 'partyaDesign', label: '甲方设计' },
-    { key: 'partyaPrepare', label: '甲方准备' },
-    { key: 'partyaResource', label: '甲方资源' },
-    { key: 'relocationTime', label: '生产配合' },
-    { key: 'winterBreakTime', label: '待命' },
-    { key: 'otherNptTime', label: '其他非生产时间' },
-  ];
-
-  const FORM_KEYS = [
-    'id',
-    'deptId',
-    'projectId',
-    'taskId',
-    'deptName',
-    'contractName',
-    'taskName',
-    'dailyGasInjection',
-    'dailyWaterInjection',
-    'dailyInjectGasTime',
-    'dailyInjectWaterTime',
-    'productionStatus',
-    'remark',
-    'relocationDays',
-    'capacity',
-    'createTime',
-    'opinion',
-    'repairTime',
-    'selfStopTime',
-    'accidentTime',
-    'complexityTime',
-    'rectificationTime',
-    'waitingStopTime',
-    'partyaDesign',
-    'partyaPrepare',
-    'partyaResource',
-    'relocationTime',
-    'winterBreakTime',
-    'otherNptTime',
-    'otherNptReason',
-    'status',
-    'auditStatus',
-  ];
-
-  const formType = ref('edit');
-
-  const initFormData = () => {
-    const base = {
-      dailyGasInjection: 0,
-      dailyWaterInjection: 0,
-      dailyInjectGasTime: 0,
-      dailyInjectWaterTime: 0,
-      relocationDays: 0,
-      capacity: 0,
-    };
-    // 初始化所有非生产时间字段为 0
-    NON_PROD_FIELDS.forEach(field => {
-      base[field.key] = 0;
-    });
-    return base;
+import { ref, computed, watch, nextTick } from "vue";
+import { onLoad } from "@dcloudio/uni-app";
+import { getRuiHenReportDetail } from "@/api/ruihen";
+import { useDataDictStore } from "@/store/modules/dataDict";
+
+const props = defineProps({
+  type: {
+    type: String,
+    default: "edit",
+  },
+});
+
+const NON_PROD_FIELDS = [
+  { key: "repairTime", label: "设备故障" },
+  { key: "selfStopTime", label: "设备保养" },
+  { key: "accidentTime", label: "工程质量" },
+  { key: "complexityTime", label: "技术受限" },
+  { key: "rectificationTime", label: "生产组织" },
+  { key: "waitingStopTime", label: "不可抗力" },
+  { key: "partyaDesign", label: "甲方设计" },
+  { key: "partyaPrepare", label: "甲方准备" },
+  { key: "partyaResource", label: "甲方资源" },
+  { key: "relocationTime", label: "生产配合" },
+  { key: "winterBreakTime", label: "待命" },
+  { key: "otherNptTime", label: "其他非生产时间" },
+];
+
+const FORM_KEYS = [
+  "id",
+  "deptId",
+  "projectId",
+  "taskId",
+  "deptName",
+  "contractName",
+  "taskName",
+  "dailyGasInjection",
+  "dailyWaterInjection",
+  "dailyInjectGasTime",
+  "dailyInjectWaterTime",
+  "productionStatus",
+  "remark",
+  "relocationDays",
+  "capacity",
+  "createTime",
+  "opinion",
+  "repairTime",
+  "selfStopTime",
+  "accidentTime",
+  "complexityTime",
+  "rectificationTime",
+  "waitingStopTime",
+  "partyaDesign",
+  "partyaPrepare",
+  "partyaResource",
+  "relocationTime",
+  "winterBreakTime",
+  "otherNptTime",
+  "otherNptReason",
+  "status",
+  "auditStatus",
+  "location",
+];
+
+const formType = ref("edit");
+
+const initFormData = () => {
+  const base = {
+    dailyGasInjection: 0,
+    dailyWaterInjection: 0,
+    dailyInjectGasTime: 0,
+    dailyInjectWaterTime: 0,
+    relocationDays: 0,
+    capacity: 0,
   };
+  // 初始化所有非生产时间字段为 0
+  NON_PROD_FIELDS.forEach((field) => {
+    base[field.key] = 0;
+  });
+  return base;
+};
 
-  const form = ref(initFormData());
-
-  async function loadDetail(id) {
-    try {
-      const { data } = await getRuiHenReportDetail({ id });
+const form = ref(initFormData());
 
-      form.value = initFormData();
+async function loadDetail(id) {
+  try {
+    const { data } = await getRuiHenReportDetail({ id });
 
-      FORM_KEYS.forEach(key => {
-        if (Object.prototype.hasOwnProperty.call(data, key) && data[key] !== null && data[key] !== undefined) {
-          form.value[key] = data[key];
-        }
-      });
-      form.value.id = id;
+    form.value = initFormData();
 
-      if (props.type.includes('approval') && data.auditStatus !== 10) {
-        formType.value = 'readonly';
+    FORM_KEYS.forEach((key) => {
+      if (
+        Object.prototype.hasOwnProperty.call(data, key) &&
+        data[key] !== null &&
+        data[key] !== undefined
+      ) {
+        form.value[key] = data[key];
       }
+    });
+    form.value.id = id;
 
-      if (props.type.includes('edit') && data.status !== 0) {
-        formType.value = 'readonly';
-      }
+    if (props.type.includes("approval") && data.auditStatus !== 10) {
+      formType.value = "readonly";
+    }
 
-      if (props.type.includes('detail')) {
-        formType.value = 'readonly';
-      }
+    if (props.type.includes("edit") && data.status !== 0) {
+      formType.value = "readonly";
+    }
 
-      if (!form.value.capacity) {
-        uni.showToast({ title: '请维护增压机产能', icon: 'none' });
-      }
-    } finally {
+    if (props.type.includes("detail")) {
+      formType.value = "readonly";
     }
+
+    if (!form.value.capacity) {
+      uni.showToast({ title: "请维护增压机产能", icon: "none" });
+    }
+  } finally {
   }
+}
 
-  const dictStore = useDataDictStore();
+const dictStore = useDataDictStore();
 
-  const nptReasonOptions = ref([]);
-  const constructionStatusOptions = ref([]);
+const nptReasonOptions = ref([]);
+const constructionStatusOptions = ref([]);
 
-  const loadOptions = () => {
-    nptReasonOptions.value = dictStore.getStrDictOptions('nptReason').map(v => ({
+const loadOptions = () => {
+  nptReasonOptions.value = dictStore
+    .getStrDictOptions("nptReason")
+    .map((v) => ({
       text: v.label,
       value: v.value,
     }));
-    constructionStatusOptions.value = dictStore.getStrDictOptions('constructionStatus').map(v => ({
+  constructionStatusOptions.value = dictStore
+    .getStrDictOptions("constructionStatus")
+    .map((v) => ({
       text: v.label,
       value: v.value,
     }));
-  };
-
-  onLoad(options => {
-    if (dictStore.dataDict.length <= 0) {
-      dictStore.loadDataDictList().then(() => {
-        loadOptions();
-      });
-    } else loadOptions();
-    loadDetail(options.id);
-  });
-
-  const defaultProps = computed(() => ({
-    inputBorder: false,
-    clearable: false,
-    placeholder: '请输入',
-    style: {
-      'text-align': 'right',
-    },
-    styles: {
-      disableColor: '#fff',
-    },
-  }));
-
-  const disabled = computed(() => field => {
-    if (field === 'edit')
-      return formType.value === 'readonly' || props.type.includes('approval') || props.type.includes('detail');
-    else return formType.value === 'readonly';
-  });
-
-  const transitTime = computed(() => {
-    const cap = form.value.capacity;
-    const gas = form.value.dailyGasInjection ?? 0;
-
-    if (!cap) return { original: 0, value: '0%' };
-
-    const original = gas / cap;
-    return { original, value: (original * 100).toFixed(2) + '%' };
-  });
-
-  const formRef = ref(null);
+};
 
-  // 辅助函数:计算总时间
-  const sumNonProdTimes = () => {
-    let sum = 0;
-    NON_PROD_FIELDS.forEach(field => {
-      sum += Number(form.value[field.key] || 0);
+onLoad((options) => {
+  if (dictStore.dataDict.length <= 0) {
+    dictStore.loadDataDictList().then(() => {
+      loadOptions();
     });
-    return sum;
-  };
-
-  const validateTotalTime = (rule, value, data, callback) => {
-    const gasTime = Number(form.value.dailyInjectGasTime || 0);
-    const waterTime = Number(form.value.dailyInjectWaterTime || 0);
-    const nonProdSum = sumNonProdTimes();
-
-    let total = 0;
-    let msg = '';
-
-    if (gasTime === 0 && waterTime > 0) {
-      total = parseFloat((waterTime + nonProdSum).toFixed(2));
-      msg = `注水(${waterTime})+非生产(${nonProdSum})=${total}H,必须为24H`;
-    } else {
-      total = parseFloat((gasTime + nonProdSum).toFixed(2));
-      msg = `注气(${gasTime})+非生产(${nonProdSum})=${total}H,必须为24H`;
-    }
-
-    if (Math.abs(total - 24) > 0.01) {
-      callback(msg);
-    }
-    return true;
-  };
-
-  // uni-forms 规则定义
-  const rules = ref({
-    dailyGasInjection: {
-      rules: [{ required: true, errorMessage: '请输入当日注气量' }],
-    },
-    dailyWaterInjection: {
-      rules: [{ required: true, errorMessage: '请输入当日注水量' }],
-    },
-    productionStatus: {
-      rules: [{ required: true, errorMessage: '请输入生产动态' }],
-    },
-    constructionStatus: {
-      rules: [{ required: true, errorMessage: '请选择施工状态' }],
-    },
-    dailyInjectGasTime: {
-      rules: [{ required: true, errorMessage: '请输入当日注气量时间' }, { validateFunction: validateTotalTime }],
-    },
+  } else loadOptions();
+  loadDetail(options.id);
+});
+
+const defaultProps = computed(() => ({
+  inputBorder: false,
+  clearable: false,
+  placeholder: "请输入",
+  style: {
+    "text-align": "right",
+  },
+  styles: {
+    disableColor: "#fff",
+  },
+}));
+
+const disabled = computed(() => (field) => {
+  if (field === "edit")
+    return (
+      formType.value === "readonly" ||
+      props.type.includes("approval") ||
+      props.type.includes("detail")
+    );
+  else return formType.value === "readonly";
+});
+
+const transitTime = computed(() => {
+  const cap = form.value.capacity;
+  const gas = form.value.dailyGasInjection ?? 0;
+
+  if (!cap) return { original: 0, value: "0%" };
+
+  const original = gas / cap;
+  return { original, value: (original * 100).toFixed(2) + "%" };
+});
+
+const formRef = ref(null);
+
+// 辅助函数:计算总时间
+const sumNonProdTimes = () => {
+  let sum = 0;
+  NON_PROD_FIELDS.forEach((field) => {
+    sum += Number(form.value[field.key] || 0);
   });
+  return sum;
+};
+
+const validateTotalTime = (rule, value, data, callback) => {
+  const gasTime = Number(form.value.dailyInjectGasTime || 0);
+  const waterTime = Number(form.value.dailyInjectWaterTime || 0);
+  const nonProdSum = sumNonProdTimes();
+
+  let total = 0;
+  let msg = "";
+
+  if (gasTime === 0 && waterTime > 0) {
+    total = parseFloat((waterTime + nonProdSum).toFixed(2));
+    msg = `注水(${waterTime})+非生产(${nonProdSum})=${total}H,必须为24H`;
+  } else {
+    total = parseFloat((gasTime + nonProdSum).toFixed(2));
+    msg = `注气(${gasTime})+非生产(${nonProdSum})=${total}H,必须为24H`;
+  }
 
-  const allTimeKeys = ['dailyInjectGasTime', 'dailyInjectWaterTime', ...NON_PROD_FIELDS.map(f => f.key)];
-
-  watch(
-    () => allTimeKeys.map(key => form.value[key]),
-    () => {
-      nextTick(() => {
-        formRef.value?.validateField('dailyInjectGasTime');
-      });
-    },
-    {
-      deep: true,
-    }
-  );
+  if (Math.abs(total - 24) > 0.01) {
+    callback(msg);
+  }
+  return true;
+};
+
+// uni-forms 规则定义
+const rules = ref({
+  dailyGasInjection: {
+    rules: [{ required: true, errorMessage: "请输入当日注气量" }],
+  },
+  dailyWaterInjection: {
+    rules: [{ required: true, errorMessage: "请输入当日注水量" }],
+  },
+  productionStatus: {
+    rules: [{ required: true, errorMessage: "请输入生产动态" }],
+  },
+  constructionStatus: {
+    rules: [{ required: true, errorMessage: "请选择施工状态" }],
+  },
+  dailyInjectGasTime: {
+    rules: [
+      { required: true, errorMessage: "请输入当日注气量时间" },
+      { validateFunction: validateTotalTime },
+    ],
+  },
+});
+
+const allTimeKeys = [
+  "dailyInjectGasTime",
+  "dailyInjectWaterTime",
+  ...NON_PROD_FIELDS.map((f) => f.key),
+];
+
+watch(
+  () => allTimeKeys.map((key) => form.value[key]),
+  () => {
+    nextTick(() => {
+      formRef.value?.validateField("dailyInjectGasTime");
+    });
+  },
+  {
+    deep: true,
+  }
+);
 
-  defineExpose({ formRef, form, loadDetail });
+defineExpose({ formRef, form, loadDetail });
 </script>
 
 <template>
@@ -273,11 +293,18 @@
       <uni-forms-item label="任务" name="taskName">
         <span class="readOnly">{{ form.taskName }}</span>
       </uni-forms-item>
+      <uni-forms-item label="施工区域" name="location">
+        <span class="readOnly">{{ form.location }}</span>
+      </uni-forms-item>
       <uni-forms-item label="搬迁安装天数(D)" name="relocationDays">
         <span class="readOnly">{{ form.relocationDays }}</span>
       </uni-forms-item>
       <uni-forms-item label="运行时效" name="transitTime">
-        <span class="readOnly" :class="{ 'red-text': transitTime.original > 1.2 }">{{ transitTime.value }}</span>
+        <span
+          class="readOnly"
+          :class="{ 'red-text': transitTime.original > 1.2 }"
+          >{{ transitTime.value }}</span
+        >
       </uni-forms-item>
       <uni-forms-item label="施工状态" name="constructionStatus" required>
         <uni-data-select
@@ -296,7 +323,10 @@
           :disabled="disabled('edit')"
           v-model="form.dailyGasInjection" />
       </uni-forms-item>
-      <uni-forms-item label="当日注水量(方)" name="dailyWaterInjection" required>
+      <uni-forms-item
+        label="当日注水量(方)"
+        name="dailyWaterInjection"
+        required>
         <uni-easyinput
           type="number"
           v-bind="defaultProps"
@@ -337,8 +367,16 @@
           v-model="form.dailyInjectWaterTime" />
       </uni-forms-item>
       <uv-divider text="非生产时间" textPosition="left"></uv-divider>
-      <uni-forms-item v-for="field in NON_PROD_FIELDS" :key="field.key" :label="field.label + '(H)'" :name="field.key">
-        <uni-easyinput type="number" v-bind="defaultProps" :disabled="disabled('edit')" v-model="form[field.key]" />
+      <uni-forms-item
+        v-for="field in NON_PROD_FIELDS"
+        :key="field.key"
+        :label="field.label + '(H)'"
+        :name="field.key">
+        <uni-easyinput
+          type="number"
+          v-bind="defaultProps"
+          :disabled="disabled('edit')"
+          v-model="form[field.key]" />
       </uni-forms-item>
 
       <uni-forms-item label="其他非生产原因" name="otherNptReason">
@@ -351,7 +389,10 @@
           :maxlength="1000" />
       </uni-forms-item>
 
-      <uni-forms-item v-if="type.includes('approval')" label="审批意见" name="opinion">
+      <uni-forms-item
+        v-if="type.includes('approval')"
+        label="审批意见"
+        name="opinion">
         <uni-easyinput
           type="textarea"
           autoHeight
@@ -365,151 +406,151 @@
 </template>
 
 <style lang="scss" scoped>
-  .content {
-    background-color: white;
-    padding: 16px 16px;
-    border-radius: 8px;
-    box-sizing: border-box;
-  }
-
-  .uni-forms {
-    margin-top: 10px;
+.content {
+  background-color: white;
+  padding: 16px 16px;
+  border-radius: 8px;
+  box-sizing: border-box;
+}
+
+.uni-forms {
+  margin-top: 10px;
+  height: 100%;
+
+  .uni-form {
     height: 100%;
+  }
 
-    .uni-form {
-      height: 100%;
-    }
-
-    .uni-forms-item {
-      display: flex;
-      align-items: center;
-      flex: 1;
-      margin-bottom: 6px;
-      border-bottom: 1px dashed #cacccf;
-    }
-
-    :deep(.uni-forms-item__content) {
-      text-align: right;
-      .readOnly {
-        padding-right: 10px;
-      }
-    }
-
-    :deep(.uni-forms-item__label) {
-      height: 44px;
-      font-weight: 500;
-      font-size: 14px;
-      color: #333333 !important;
-      width: max-content !important;
-    }
-
-    :deep(.uni-select) {
-      border: none;
-      text-align: right;
-      padding-right: 0;
-      .uniui-bottom:before {
-        content: '\e6b5' !important;
-        font-size: 16px !important;
-      }
-    }
-
-    :deep(.uni-easyinput__content-textarea) {
-      min-height: inherit;
-      margin: 10px;
-    }
-
-    :deep(.is-disabled) {
-      color: #333333 !important;
-    }
+  .uni-forms-item {
+    display: flex;
+    align-items: center;
+    flex: 1;
+    margin-bottom: 6px;
+    border-bottom: 1px dashed #cacccf;
+  }
 
-    :deep(.red-text > .is-disabled) {
-      color: rgb(220 38 38 / 0.8) !important;
+  :deep(.uni-forms-item__content) {
+    text-align: right;
+    .readOnly {
+      padding-right: 10px;
     }
+  }
 
-    :deep(.orange-text > .is-disabled) {
-      color: rgb(234 88 12 / 0.8) !important;
-    }
+  :deep(.uni-forms-item__label) {
+    height: 44px;
+    font-weight: 500;
+    font-size: 14px;
+    color: #333333 !important;
+    width: max-content !important;
+  }
 
-    :deep(.uni-select--disabled) {
-      background-color: #fff;
+  :deep(.uni-select) {
+    border: none;
+    text-align: right;
+    padding-right: 0;
+    .uniui-bottom:before {
+      content: "\e6b5" !important;
+      font-size: 16px !important;
     }
   }
 
-  .red-text {
-    color: rgb(220 38 38 / 0.8) !important;
+  :deep(.uni-easyinput__content-textarea) {
+    min-height: inherit;
+    margin: 10px;
   }
 
-  .orange-text {
-    color: rgb(234 88 12 / 0.8) !important;
+  :deep(.is-disabled) {
+    color: #333333 !important;
   }
 
-  .red {
-    border: 1px solid rgb(254 226 226);
-    color: rgb(220 38 38 / 0.8);
-    background-color: rgb(254 226 226);
+  :deep(.red-text > .is-disabled) {
+    color: rgb(220 38 38 / 0.8) !important;
   }
 
-  .orange {
-    border: 1px solid rgb(254 215 170);
-    color: rgb(234 88 12 / 0.8);
-    background-color: rgb(254 215 170);
+  :deep(.orange-text > .is-disabled) {
+    color: rgb(234 88 12 / 0.8) !important;
   }
 
-  .tip {
-    border-radius: 8px;
-    border: 1px solid #e5e5e5;
-    background-color: rgba(239, 246, 255, 0.8);
-    box-sizing: border-box;
-    padding: 10px;
-    display: flex;
-    flex-direction: column;
-    gap: 6px;
-
-    .item {
-      display: flex;
-      align-items: center;
-      justify-content: space-between;
-      font-size: 12px;
-
-      .left {
-        color: rgb(75 85 99);
-
-        span {
-          color: rgb(31 41 55);
-          font-weight: 600;
-        }
-      }
-
-      .right {
-        display: inline-flex;
-        align-items: center;
-        border-radius: 4px;
-        padding: 2px 4px;
-        font-weight: 500;
-      }
-    }
+  :deep(.uni-select--disabled) {
+    background-color: #fff;
   }
-
-  .opinion {
-    border-radius: 8px;
-    border: 1px solid rgb(254 240 138);
-    background-color: rgb(254 252 232);
-    box-sizing: border-box;
-    padding: 10px;
+}
+
+.red-text {
+  color: rgb(220 38 38 / 0.8) !important;
+}
+
+.orange-text {
+  color: rgb(234 88 12 / 0.8) !important;
+}
+
+.red {
+  border: 1px solid rgb(254 226 226);
+  color: rgb(220 38 38 / 0.8);
+  background-color: rgb(254 226 226);
+}
+
+.orange {
+  border: 1px solid rgb(254 215 170);
+  color: rgb(234 88 12 / 0.8);
+  background-color: rgb(254 215 170);
+}
+
+.tip {
+  border-radius: 8px;
+  border: 1px solid #e5e5e5;
+  background-color: rgba(239, 246, 255, 0.8);
+  box-sizing: border-box;
+  padding: 10px;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+
+  .item {
     display: flex;
     align-items: center;
     justify-content: space-between;
     font-size: 12px;
-    margin-top: 10px;
 
     .left {
-      font-weight: 600;
-      color: rgb(133 77 14);
+      color: rgb(75 85 99);
+
+      span {
+        color: rgb(31 41 55);
+        font-weight: 600;
+      }
     }
 
     .right {
+      display: inline-flex;
+      align-items: center;
+      border-radius: 4px;
+      padding: 2px 4px;
       font-weight: 500;
-      color: rgb(75 85 99);
     }
   }
+}
+
+.opinion {
+  border-radius: 8px;
+  border: 1px solid rgb(254 240 138);
+  background-color: rgb(254 252 232);
+  box-sizing: border-box;
+  padding: 10px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 12px;
+  margin-top: 10px;
+
+  .left {
+    font-weight: 600;
+    color: rgb(133 77 14);
+  }
+
+  .right {
+    font-weight: 500;
+    color: rgb(75 85 99);
+  }
+}
 </style>

+ 30 - 22
pages/ruiying/components/form.vue

@@ -75,6 +75,7 @@ const FORM_KEYS = [
 
   "status",
   "auditStatus",
+  "location",
 ];
 
 const formType = ref("edit");
@@ -87,7 +88,7 @@ const initFormData = () => {
     reportDetails: [],
   };
   // 初始化所有非生产时间字段为 0
-  NON_PROD_FIELDS.forEach(field => {
+  NON_PROD_FIELDS.forEach((field) => {
     base[field.key] = 0;
   });
   return base;
@@ -95,7 +96,7 @@ const initFormData = () => {
 
 const form = ref(initFormData());
 
-const formatT = arr =>
+const formatT = (arr) =>
   `${arr[0].toString().padStart(2, "0")}:${arr[1].toString().padStart(2, "0")}`;
 
 async function loadDetail(id) {
@@ -104,7 +105,7 @@ async function loadDetail(id) {
 
     form.value = initFormData();
 
-    FORM_KEYS.forEach(key => {
+    FORM_KEYS.forEach((key) => {
       if (
         Object.prototype.hasOwnProperty.call(data, key) &&
         data[key] !== null &&
@@ -114,7 +115,7 @@ async function loadDetail(id) {
       }
     });
 
-    form.value.reportDetails = form.value.reportDetails.map(item => ({
+    form.value.reportDetails = form.value.reportDetails.map((item) => ({
       reportDate: item.reportDate || form.value.createTime,
       duration: item.duration || 0,
       constructionDetail: item.constructionDetail || "",
@@ -160,7 +161,7 @@ const addReportDetailRow = () => {
   });
 };
 
-const removeReportDetailRow = index => {
+const removeReportDetailRow = (index) => {
   if (index === 0) {
     uni.showToast({ title: "至少填写一条生产动态", icon: "none" });
     return;
@@ -175,17 +176,21 @@ const nptReasonOptions = ref([]);
 const rigStatusOptions = ref([]);
 
 const loadOptions = () => {
-  nptReasonOptions.value = dictStore.getStrDictOptions("nptReason").map(v => ({
-    text: v.label,
-    value: v.value,
-  }));
-  rigStatusOptions.value = dictStore.getStrDictOptions("rigStatus").map(v => ({
-    text: v.label,
-    value: v.value,
-  }));
+  nptReasonOptions.value = dictStore
+    .getStrDictOptions("nptReason")
+    .map((v) => ({
+      text: v.label,
+      value: v.value,
+    }));
+  rigStatusOptions.value = dictStore
+    .getStrDictOptions("rigStatus")
+    .map((v) => ({
+      text: v.label,
+      value: v.value,
+    }));
 };
 
-onLoad(options => {
+onLoad((options) => {
   if (dictStore.dataDict.length <= 0) {
     dictStore.loadDataDictList().then(() => {
       loadOptions();
@@ -206,7 +211,7 @@ const defaultProps = computed(() => ({
   },
 }));
 
-const disabled = computed(() => field => {
+const disabled = computed(() => (field) => {
   if (field === "edit")
     return (
       formType.value === "readonly" ||
@@ -221,7 +226,7 @@ const formRef = ref(null);
 // 辅助函数:计算总时间
 const sumNonProdTimes = () => {
   let sum = 0;
-  NON_PROD_FIELDS.forEach(field => {
+  NON_PROD_FIELDS.forEach((field) => {
     sum += Number(form.value[field.key] || 0);
   });
   return sum;
@@ -282,11 +287,11 @@ const rules = reactive({
 const allTimeKeys = [
   "drillingWorkingTime",
   "otherProductionTime",
-  ...NON_PROD_FIELDS.map(f => f.key),
+  ...NON_PROD_FIELDS.map((f) => f.key),
 ];
 
 watch(
-  () => allTimeKeys.map(key => form.value[key]),
+  () => allTimeKeys.map((key) => form.value[key]),
   () => {
     nextTick(() => {
       formRef.value?.validateField("drillingWorkingTime");
@@ -319,12 +324,12 @@ const endDefaultTime = ref("08:00");
 
 const reportDetailIndex = ref(0);
 
-const handleClickTimeRangeItem = index => {
+const handleClickTimeRangeItem = (index) => {
   reportDetailIndex.value = index;
   reportDetailsTimeRangeRef.value.open();
 };
 
-const calculateDuration = row => {
+const calculateDuration = (row) => {
   if (!row.startTime || !row.endTime) {
     row.duration = 0;
     return;
@@ -343,7 +348,7 @@ const calculateDuration = row => {
   row.duration = Number((diffMinutes / 60).toFixed(2));
 };
 
-const reportDetailsTimeRange = data => {
+const reportDetailsTimeRange = (data) => {
   form.value.reportDetails[reportDetailIndex.value].startTime = data[0];
   form.value.reportDetails[reportDetailIndex.value].endTime = data[1];
 
@@ -412,6 +417,9 @@ const inputCurrentDepth = useDebounceFn(function inputCurrentDepth() {
       <uni-forms-item label="任务" name="taskName">
         <span class="readOnly">{{ form.taskName }}</span>
       </uni-forms-item>
+      <uni-forms-item label="施工区域" name="location">
+        <span class="readOnly">{{ form.location }}</span>
+      </uni-forms-item>
       <uni-forms-item label="施工状态" name="rigStatus">
         <uni-data-select
           :clear="true"
@@ -596,7 +604,7 @@ const inputCurrentDepth = useDebounceFn(function inputCurrentDepth() {
             v-bind="defaultProps"
             :disabled="disabled('edit')"
             v-model.number="item.currentDepth"
-            @input="val => inputCurrentDepth(val, index)" />
+            @input="(val) => inputCurrentDepth(val, index)" />
         </uni-forms-item>
         <uni-forms-item
           label="详情"

+ 20 - 16
pages/ruiyingx/components/form.vue

@@ -69,6 +69,7 @@ const FORM_KEYS = [
   "otherNptReason",
   "status",
   "auditStatus",
+  "location",
 ];
 
 const formType = ref("edit");
@@ -81,7 +82,7 @@ const initFormData = () => {
     reportDetails: [],
   };
   // 初始化所有非生产时间字段为 0
-  NON_PROD_FIELDS.forEach(field => {
+  NON_PROD_FIELDS.forEach((field) => {
     base[field.key] = 0;
   });
   return base;
@@ -89,7 +90,7 @@ const initFormData = () => {
 
 const form = ref(initFormData());
 
-const formatT = arr =>
+const formatT = (arr) =>
   `${arr[0].toString().padStart(2, "0")}:${arr[1].toString().padStart(2, "0")}`;
 
 async function loadDetail(id) {
@@ -98,7 +99,7 @@ async function loadDetail(id) {
 
     form.value = initFormData();
 
-    FORM_KEYS.forEach(key => {
+    FORM_KEYS.forEach((key) => {
       if (
         Object.prototype.hasOwnProperty.call(data, key) &&
         data[key] !== null &&
@@ -108,7 +109,7 @@ async function loadDetail(id) {
       }
     });
 
-    form.value.reportDetails = form.value.reportDetails.map(item => ({
+    form.value.reportDetails = form.value.reportDetails.map((item) => ({
       reportDate: item.reportDate || form.value.createTime,
       duration: item.duration || 0,
       constructionDetail: item.constructionDetail || "",
@@ -152,7 +153,7 @@ const addReportDetailRow = () => {
   });
 };
 
-const removeReportDetailRow = index => {
+const removeReportDetailRow = (index) => {
   if (index === 0) {
     uni.showToast({ title: "至少填写一条生产动态", icon: "none" });
     return;
@@ -170,25 +171,25 @@ const techniqueOptions = ref([]);
 const loadOptions = () => {
   nptReasonOptions.value = dictStore
     .getStrDictOptions("ryNptReason")
-    .map(v => ({
+    .map((v) => ({
       text: v.label,
       value: v.value,
     }));
   rigStatusOptions.value = dictStore
     .getStrDictOptions("repairStatus")
-    .map(v => ({
+    .map((v) => ({
       text: v.label,
       value: v.value,
     }));
   techniqueOptions.value = dictStore
     .getStrDictOptions("rq_iot_project_technology_ry")
-    .map(v => ({
+    .map((v) => ({
       text: v.label,
       value: v.value,
     }));
 };
 
-onLoad(options => {
+onLoad((options) => {
   if (dictStore.dataDict.length <= 0) {
     dictStore.loadDataDictList().then(() => {
       loadOptions();
@@ -209,7 +210,7 @@ const defaultProps = computed(() => ({
   },
 }));
 
-const disabled = computed(() => field => {
+const disabled = computed(() => (field) => {
   if (field === "edit")
     return (
       formType.value === "readonly" ||
@@ -238,7 +239,7 @@ const onDutyStaffNum = computed(() => {
 // 辅助函数:计算总时间
 const sumNonProdTimes = () => {
   let sum = 0;
-  NON_PROD_FIELDS.forEach(field => {
+  NON_PROD_FIELDS.forEach((field) => {
     sum += Number(form.value[field.key] || 0);
   });
   return sum;
@@ -293,11 +294,11 @@ const rules = reactive({
 const allTimeKeys = [
   "ratedProductionTime",
   "productionTime",
-  ...NON_PROD_FIELDS.map(f => f.key),
+  ...NON_PROD_FIELDS.map((f) => f.key),
 ];
 
 watch(
-  () => allTimeKeys.map(key => form.value[key]),
+  () => allTimeKeys.map((key) => form.value[key]),
   () => {
     nextTick(() => {
       formRef.value?.validateField(["ratedProductionTime", "productionTime"]);
@@ -332,12 +333,12 @@ const endDefaultTime = ref("08:00");
 
 const reportDetailIndex = ref(0);
 
-const handleClickTimeRangeItem = index => {
+const handleClickTimeRangeItem = (index) => {
   reportDetailIndex.value = index;
   reportDetailsTimeRangeRef.value.open();
 };
 
-const calculateDuration = row => {
+const calculateDuration = (row) => {
   if (!row.startTime || !row.endTime) {
     row.duration = 0;
     return;
@@ -356,7 +357,7 @@ const calculateDuration = row => {
   row.duration = Number((diffMinutes / 60).toFixed(2));
 };
 
-const reportDetailsTimeRange = data => {
+const reportDetailsTimeRange = (data) => {
   form.value.reportDetails[reportDetailIndex.value].startTime = data[0];
   form.value.reportDetails[reportDetailIndex.value].endTime = data[1];
 
@@ -409,6 +410,9 @@ const reportDetailsTimeRange = data => {
       <uni-forms-item label="任务" name="taskName">
         <span class="readOnly">{{ form.taskName }}</span>
       </uni-forms-item>
+      <uni-forms-item label="施工区域" name="location">
+        <span class="readOnly">{{ form.location }}</span>
+      </uni-forms-item>
       <uni-forms-item label="施工状态" name="repairStatus" required>
         <uni-data-select
           :clear="true"

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio