ExtTreeSelect.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <template>
  2. <div style="display: inline-block;width: 100%">
  3. <div class="ext-tree-select-display" v-if="!state.init&&state.displays.length>0" @click="handleDisplayClick">
  4. <template v-if="readonly&&!state.displays">{{ placeholder }}</template>
  5. <el-tag :key="idx" v-for="(item,idx) in state.displays">{{ item.name || item }}</el-tag>
  6. </div>
  7. <el-tree-select
  8. style="width: 100%"
  9. v-show="state.init||(state.displays.length===0&&!readonly)"
  10. ref="extTreeSelectRef"
  11. v-model="state.modelValue"
  12. :data="state.treeData"
  13. :default-check-keys="state.defaultCheckKeys"
  14. :filter-node-method="filterNodeMethod"
  15. filterable clearable
  16. highlight-current
  17. :placeholder="placeholder"
  18. :disabled="readonly"
  19. show-checkbox
  20. :loading="state.loading"
  21. :check-on-click-node="state.checkOnClickNode"
  22. :check-strictly="state.checkStrictly"
  23. :multiple="multiple"
  24. :node-key="nodeKey"
  25. :props="{children:children,label:label}"
  26. @check="handleTreeCheck"
  27. @clear="handleSelectClear"
  28. @visible-change="handleVisibleChange">
  29. <template #empty>
  30. <el-empty :image-size="50"/>
  31. </template>
  32. </el-tree-select>
  33. </div>
  34. </template>
  35. <script lang="ts" setup name="ExtTreeSelect">
  36. import {reactive, onMounted, ref, watch} from 'vue';
  37. import {ElMessage} from 'element-plus';
  38. import {$body} from "/@/utils/request";
  39. import u from "/@/utils/u";
  40. const extTreeSelectRef = ref();
  41. const props = defineProps({
  42. modelValue: {
  43. type: [Number, Array<Number>]
  44. },
  45. multiple: {
  46. type: Boolean,
  47. default: false
  48. },
  49. mode: {
  50. type: String,
  51. default: 'dept' //dept user custom
  52. },
  53. children: {
  54. type: String,
  55. default: 'children'
  56. },
  57. label: {
  58. type: String,
  59. default: 'name'
  60. },
  61. nodeKey: {
  62. type: String,
  63. default: 'id'
  64. },
  65. showCheckbox: {
  66. type: Boolean,
  67. default: true
  68. },
  69. placeholder: {
  70. type: String,
  71. default: '请选择'
  72. },
  73. readonly: {
  74. type: Boolean,
  75. default: false
  76. },
  77. displayList: {
  78. type: [String, Array]
  79. }
  80. });
  81. const state = reactive({
  82. treeData: [] as Array<SelectTreeType>,
  83. checkStrictly: true, //任何节点都可被选择,否则只有子节点可被选择
  84. showCheckBox: false, // 复选框
  85. checkOnClickNode: false, //只有点击当前节点才可被选择
  86. modelValue: null,
  87. loading: false,
  88. init: false,
  89. displays: [] as Array<any>,
  90. defaultCheckKeys: []
  91. })
  92. const emit = defineEmits(['update:modelValue', 'on-change']);
  93. watch(() => props.modelValue, (val) => {
  94. if (state.init) {
  95. // console.log(val)
  96. // extTreeSelectRef.value.setCheckNodes(null)
  97. reactiveModel()
  98. }
  99. })
  100. // const modelVal = computed(() => props.value);
  101. // const defaultCheckKeys = computed(() => props.value);
  102. const reactiveModel = () => {
  103. let val = props.modelValue;
  104. let keys = [];
  105. if (!props.multiple) {
  106. keys = val ? [val] : []
  107. } else {
  108. keys = val || [];
  109. }
  110. console.log("reactiveModel>>>", keys)
  111. extTreeSelectRef.value.setCheckedKeys(keys);
  112. if (keys) {
  113. let nodes = setupCheckNodes(state.treeData, keys)
  114. extTreeSelectRef.value.setCheckedNodes(nodes);
  115. console.log(nodes)
  116. }
  117. }
  118. const setupCheckNodes = (tree: Array<any>, keys: Array<any>) => {
  119. let result: Array<any> = [];
  120. tree.forEach((item: any) => {
  121. if (keys.includes(item.id)) {
  122. result.push(item)
  123. }
  124. if (item.children) {
  125. let ret = setupCheckNodes(item.children, keys)
  126. if (ret) {
  127. result = result.concat(ret);
  128. }
  129. }
  130. })
  131. return result;
  132. }
  133. const handleDisplayClick = () => {
  134. state.init = true;
  135. handleVisibleChange(true)
  136. extTreeSelectRef.value.focus();
  137. }
  138. const handleVisibleChange = (visible: boolean) => {
  139. // console.log("visible>>>", visible)
  140. if (visible) {
  141. loadData();
  142. }
  143. }
  144. const handleSelectClear = () => {
  145. extTreeSelectRef.value.setCheckedNodes([])
  146. emitData();
  147. }
  148. const handleTreeCheck = () => {
  149. // console.log(JSON.stringify(val))
  150. if (!extTreeSelectRef.value) {
  151. console.error("null select ref")
  152. }
  153. emitData();
  154. }
  155. const emitData = () => {
  156. // console.error(state.modelValue)
  157. let data = extTreeSelectRef.value.getCheckedNodes();
  158. // console.error(data)
  159. if (props.multiple) {
  160. if (data && data.length > 0) {
  161. emit("update:modelValue", data.map((k: any) => k[props.nodeKey]));
  162. emit('on-change', data)
  163. } else {
  164. emit("update:modelValue", [])
  165. emit('on-change', [])
  166. }
  167. } else {
  168. if (data && data.length > 0) {
  169. emit("update:modelValue", data[0][props.nodeKey])
  170. emit('on-change', data[0])
  171. } else {
  172. emit("update:modelValue", null)
  173. emit('on-change', null)
  174. }
  175. }
  176. }
  177. // const handleCurrentChange = (val,event)=>{
  178. // console.log(val)
  179. // console.log(event)
  180. // }
  181. const filterNodeMethod = (value: string, data: any) => {
  182. // console.log(data, value)
  183. return data[props.label].includes(value)
  184. }
  185. const recursiveArrayV2 = (treeData: Array<any>) => {
  186. // let ret: Array<SelectTreeType> = [];
  187. treeData.forEach(item => {
  188. if (item.id < 0) {
  189. item.disabled = true;
  190. }
  191. if (!u.isEmptyOrNull(item.children)) {
  192. recursiveArrayV2(item.children)
  193. }
  194. })
  195. }
  196. const loadDeptUserData = () => {
  197. $body(`/department/treeV2`).then((res: any) => {
  198. if (!u.isEmptyOrNull(res)) {
  199. recursiveArrayV2(res)
  200. // console.log(res)
  201. state.treeData = res;
  202. state.loading = false;
  203. reactiveModel()
  204. } else {
  205. ElMessage.info("暂无部门信息");
  206. }
  207. })
  208. }
  209. const loadDeptData = () => {
  210. $body(`/department/tree`).then((res: any) => {
  211. if (!u.isEmptyOrNull(res)) {
  212. // let result: Array<SelectTreeType> = recursiveArray([res])
  213. // console.log(res)
  214. state.treeData = res;
  215. } else {
  216. ElMessage.info("暂无部门信息");
  217. }
  218. state.loading = false;
  219. reactiveModel()
  220. })
  221. }
  222. const loadData = () => {
  223. state.init = true;
  224. //不重复请求接口
  225. if (state.treeData && state.treeData.length > 0) {
  226. return;
  227. }
  228. state.loading = true;
  229. if (props.mode === 'user') {
  230. loadDeptUserData();
  231. state.checkStrictly = false;
  232. state.showCheckBox = true;
  233. // state.checkOnClickNode = true;
  234. } else if (props.mode === 'dept') {
  235. loadDeptData();
  236. state.showCheckBox = true;
  237. // state.checkStrictly = props.multiple;
  238. }
  239. }
  240. watch(() => props.displayList, (val) => {
  241. console.log("displayList",val)
  242. if (props.displayList) {
  243. if (props.multiple) {
  244. state.displays = [...props.displayList]
  245. } else {
  246. state.displays = [props.displayList]
  247. }
  248. }
  249. console.log(JSON.stringify(state.displays))
  250. },{deep:true,immediate:true})
  251. onMounted(() => {
  252. });
  253. </script>
  254. <style scoped lang="scss">
  255. .ext-tree-select-display {
  256. cursor: pointer;
  257. width: 100%;
  258. line-height: 32px;
  259. height: 32px;
  260. display: inline-flex;
  261. flex-grow: 1;
  262. align-items: center;
  263. //justify-content: center;
  264. padding: 1px 11px;
  265. background-color: var(--el-input-bg-color, var(--el-fill-color-blank));
  266. background-image: none;
  267. border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
  268. //transition: var(--el-transition-box-shadow);
  269. //transform: translate3d(0, 0, 0);
  270. box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
  271. &:hover {
  272. border: 1px solid var(--el-border-color-hover);
  273. }
  274. }
  275. </style>