瀏覽代碼

+ 设备工单处置

chen.cheng 8 月之前
父節點
當前提交
08b31e3532

+ 196 - 0
src/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. 支持多选

文件差異過大導致無法顯示
+ 972 - 0
src/components/da-tree/index.vue


+ 197 - 0
src/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
src/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
src/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)
+}

+ 19 - 0
src/package.json

@@ -0,0 +1,19 @@
+{
+    "id": "da-tree",
+    "name": "da-tree 树组件(支持单选、多选、无限级、主题色,Vue3版) ",
+    "displayName": "da-tree 树组件(支持单选、多选、无限级、主题色,Vue3版) ",
+    "version": "1.4.2",
+    "description": "一个基于 Vue3 的tree(树)组件,支持主题换色,可能是最适合你的tree(树)组件",
+    "keywords": [
+        "tree",
+        "树",
+        "树组件",
+        "da系列"
+    ],
+    "dcloudext": {
+        "category": [
+            "前端组件",
+            "通用组件"
+        ]
+    }
+}

+ 155 - 37
src/pages/workbenchsub/workorder/detail.vue

@@ -189,7 +189,7 @@
     <up-action-sheet
         :show="state.handleForm"
         @close="state.handleForm = false"
-        title="报废设备"
+        title="处理工单"
         :round="10"
     >
       <view class="action-content common-form">
@@ -252,15 +252,64 @@
           >
             <up-switch v-model="state.model.order.rewrite"></up-switch>
           </up-form-item>
+          <up-form-item
+              label="报废时间"
+              prop="order.startDate"
+              borderBottom
+              :required="true"
+              @click="state.toUsrShow = true; hideKeyboard()"
+          >
+            <up-input
+                disabled
+                disabledColor="#ffffff"
+                placeholder="其请选择处理人"
+                border="none"
+            ></up-input>
+            <template #right>
+              <up-icon
+                  name="arrow-right"
+              ></up-icon>
+            </template>
+          </up-form-item>
+          <up-form-item>
+            <text class="u-demo-block__title">问题图片</text>
+            <up-upload
+                :fileList="state.model.order.files"
+                @afterRead="afterRead"
+                @delete="deletePic"
+                name="imags"
+                multiple
+                :maxCount="5"
+                :previewFullImage="true"
+            ></up-upload>
+          </up-form-item>
 
           <up-button @click="submit" style="margin-top: 20rpx;">提交</up-button>
         </up-form>
       </view>
     </up-action-sheet>
     <up-toast ref="uToastRef"></up-toast>
-    <up-popup :show="state.toUsrShow" mode="right" @close="state.toUsrShow=false" @open="open">
-      <dw-picker-1 :keywords="state.keywords" :data="state.pickerOptions" @on-select="actionSelect"></dw-picker-1>
-    </up-popup>
+    <up-action-sheet
+        :show="state.toUsrShow"
+        @close="state.toUsrShow = false"
+        title="选择指派对象"
+        :round="10"
+    >
+      <view class="action-content common-form">
+        <DaTree
+            ref="DaTreeRef"
+            :data="roomTreeData"
+            labelField="name"
+            valueField="id"
+            defaultExpandAll
+            showCheckbox
+            :defaultCheckedKeys="defaultCheckedKeysValue"
+            @change="handleTreeChange"
+            @expand="handleExpandChange"
+        />
+        <up-button @click="submit" style="margin-top: 20rpx;">指派</up-button>
+      </view>
+    </up-action-sheet>
   </view>
 </template>
 
@@ -272,43 +321,94 @@ import LabelText from "@/components/labeltext/index.vue";
 import Steps from "@/components/steps/index.vue";
 import {onMounted, reactive, ref} from 'vue';
 import {onLoad} from "@dcloudio/uni-app";
-import {getWorkOrderDetail, getWorkOrderSteps, handleWorkOrder, scrapWorkOrder} from "@/api/workorder.js";
+import {
+  getWorkOrderDetail,
+  getWorkOrderSteps,
+  handleWorkOrder,
+  scrapWorkOrder,
+  uploadOrderFile
+} from "@/api/workorder.js";
 import {valueToConst} from "@/common/consts/CommonConst.js";
 import {WorkOrderLevel, WorkOrderStat, WorkOrderTypes} from "@/common/consts/WorkOrderConst.js";
-import {formatTxt, getImgs, getStepInfo} from "@/util/index.js";
+import {formatTxt, getImgs, getStepInfo, hideKeyboard} from "@/util/index.js";
 import ImagesH from "@/components/imagesh/index.vue";
+import DaTree from '@/components/da-tree/index.vue';
 
 const order = ref('')
-const state = reactive({
-  showDisableModal: false,
-  showAcceptModal: false,
-  handleForm: false,
-  toUsrShow: false,
-  keywords: '',
-  pickerOptions : {
-    dataList: [
+const DaTreeRef = ref()
+// key的类型必须对应树数据key的类型
+const defaultCheckedKeysValue = ref(['211', '222'])
+const roomTreeData = ref([
+  {
+    id: '2',
+    name: '行政中心',
+    children: [
       {
-        "label": "北京市",
-        "value": "beijing",
-        "initial": "B"
+        id: '21',
+        name: '行政部',
+        children: [
+          {
+            id: '211',
+            name: '行政一部',
+            children: null,
+          },
+          {
+            id: '212',
+            name: '行政二部',
+            children: [],
+            disabled: true,
+          },
+        ],
       },
       {
-        "label": "上海市",
-        "value": "shanghai",
-        "initial": "S"
+        id: '22',
+        name: '财务部',
+        children: [
+          {
+            id: '221',
+            name: '财务一部',
+            children: [],
+            disabled: true,
+          },
+          {
+            id: '222',
+            name: '财务二部',
+            children: [],
+          },
+        ],
       },
       {
-        "label": "广州市",
-        "value": "guangzhou",
-        "initial": "G"
+        id: '23',
+        name: '人力资源部',
+        children: [
+          {
+            id: '231',
+            name: '人力一部',
+            children: [],
+          },
+          {
+            id: '232',
+            name: '人力二部',
+          },
+        ],
       },
-      {
-        "label": "深圳市",
-        "value": "shenzhen",
-        "initial": "S"
-      }
-    ]
+    ],
   },
+])
+
+function handleTreeChange(allSelectedKeys, currentItem) {
+  console.log('handleTreeChange ==>', allSelectedKeys, currentItem)
+}
+
+function handleExpandChange(expand, currentItem) {
+  console.log('handleExpandChange ==>', expand, currentItem)
+}
+
+const state = reactive({
+  showDisableModal: false,
+  showAcceptModal: false,
+  handleForm: false,
+  toUsrShow: false,
   orderInfo: {
     problemImg: [],
     solveImg: []
@@ -386,6 +486,32 @@ const onConfirmAccept = () => {
     });
   })
 }
+const deletePic = (event) => {
+  state.model.order.files.splice(event.index, 1)
+}
+const afterRead = async (event) => {
+  // 当设置 mutiple 为 true 时, file 为数组格式,否则为对象格式
+  let lists = [].concat(event.file)
+  let fileListLen = state.model.order.files.length
+  lists.map((item) => {
+    state.model.order.files.push({
+      ...item,
+      status: 'uploading',
+      message: '上传中'
+    })
+  })
+  for (let i = 0; i < lists.length; i++) {
+    const result = await uploadOrderFile(lists[i].url)
+    let item = state.model.order.files[fileListLen]
+    state.model.order.files.splice(fileListLen, 1, Object.assign(item, {
+      status: 'success',
+      name: result.fileName,
+      uid: result.md5,
+      url: result.url
+    }))
+    fileListLen++
+  }
+}
 </script>
 
 
@@ -456,14 +582,6 @@ const onConfirmAccept = () => {
     padding: 0 $uni-pop-padding;
   }
 
-  .u-popup {
-    .u-transition {
-      width: 90% !important;
-      height: 90% !important;
-    }
-
-  }
-
 }
 
 

+ 0 - 136
src/pages/workbenchsub/workorder/tousr.vue

@@ -1,136 +0,0 @@
-<template>
-  <up-index-list :indexList="state.indexList">
-    <template #header>
-      <view class="list">
-        <view class="list__item" @click="goNext">
-          <up-avatar shape="square" size="35" icon="man-add-fill" fontSize="26" randomBgColor></up-avatar>
-          <text class="list__item__user-name">新的朋友</text>
-        </view>
-        <up-line></up-line>
-        <view class="list__item">
-          <up-avatar shape="square" size="35" icon="tags-fill" fontSize="26" randomBgColor></up-avatar>
-          <text class="list__item__user-name">标签</text>
-        </view>
-        <up-line></up-line>
-        <view class="list__item">
-          <up-avatar shape="square" size="35" icon="chrome-circle-fill" fontSize="26" randomBgColor></up-avatar>
-          <text class="list__item__user-name">朋友圈</text>
-        </view>
-        <up-line></up-line>
-        <view class="list__item">
-          <up-avatar shape="square" size="35" icon="qq-fill" fontSize="26" randomBgColor></up-avatar>
-          <text class="list__item__user-name">QQ</text>
-        </view>
-        <up-line></up-line>
-      </view>
-    </template>
-    <template :key="index" v-for="(item, index) in itemArr">
-      <up-index-item>
-        <up-index-anchor :text="state.indexList[index]"></up-index-anchor>
-        <view class="list" v-for="(item1, index1) in item" :key="index1">
-          <view class="list__item">
-            <image class="list__item__avatar" :src="item1.url"></image>
-            <text class="list__item__user-name">{{ item1.name }}</text>
-          </view>
-          <up-line></up-line>
-        </view>
-      </up-index-item>
-    </template>
-    <template #footer>
-      <view class="u-safe-area-inset--bottom">
-        <text class="list__footer">共305位好友</text>
-      </view>
-    </template>
-  </up-index-list>
-</template>
-
-<script setup lang="ts">
-import {computed, reactive} from 'vue'
-
-const indexList = () => {
-  const indexList = []
-  const charCodeOfA = 'A'.charCodeAt(0)
-  indexList.push("↑")
-  indexList.push("☆")
-  for (let i = 0; i < 16; i++) {
-    indexList.push(String.fromCharCode(charCodeOfA + i))
-  }
-  indexList.push('#')
-  return indexList
-}
-const state = reactive({
-  indexList: indexList(),
-  urls: [
-    './static/uview/album/1.jpg',
-    './static/uview/album/2.jpg',
-    './static/uview/album/3.jpg',
-    './static/uview/album/4.jpg',
-    './static/uview/album/5.jpg',
-    './static/uview/album/6.jpg',
-    './static/uview/album/7.jpg',
-    './static/uview/album/8.jpg',
-    './static/uview/album/9.jpg',
-    './static/uview/album/10.jpg',
-  ],
-  names: ["勇往无敌", "疯狂的迪飙", "磊爱可", "梦幻梦幻梦", "枫中飘瓢", "飞翔天使",
-    "曾经第一", "追风幻影族长", "麦小姐", "胡格罗雅", "Red磊磊", "乐乐立立", "青龙爆风", "跑跑卡叮车", "山里狼", "supersonic超"
-  ]
-})
-const itemArr = computed(() => {
-  return state.indexList.map(item => {
-    const arr = []
-    for (let i = 0; i < 10; i++) {
-      arr.push({
-        name: state.names[uni.$u.random(0, state.names.length - 1)],
-        url: state.urls[uni.$u.random(0, state.urls.length - 1)]
-      })
-    }
-    return arr
-  });
-})
-
-</script>
-
-<script lang="ts">
-export default {
-  options: {
-    styleIsolation: 'shared',
-    name: 'to-usr'
-  }
-}
-</script>
-<style scoped lang="scss">
-.u-index-list {
-  .u-index-list__letter {
-    top: 200rpx;
-  }
-}
-
-.list {
-
-  &__item {
-    @include flex;
-    padding: 6px 12px;
-    align-items: center;
-
-    &__avatar {
-      height: 35px;
-      width: 35px;
-      border-radius: 3px;
-    }
-
-    &__user-name {
-      font-size: 16px;
-      margin-left: 10px;
-      color: $u-main-color;
-    }
-  }
-
-  &__footer {
-    color: $u-tips-color;
-    font-size: 14px;
-    text-align: center;
-    margin: 15px 0;
-  }
-}
-</style>

部分文件因文件數量過多而無法顯示