WorkShopTree.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <template>
  2. <div class="select-tree">
  3. <div class="left">
  4. <el-form-item>
  5. <el-input
  6. v-model="queryStr"
  7. :style="{ width: '300px', height: '30px' }"
  8. placeholder="请输入相机名称/设备ID"
  9. :prefix-icon="Search"
  10. @change="getCameraData({ queryString: queryStr })"
  11. clearable
  12. /></el-form-item>
  13. <el-tree
  14. ref="treeRef"
  15. :data="cameraTreeTemp"
  16. show-checkbox
  17. node-key="code"
  18. :props="defaultProps"
  19. :render-after-expand="false"
  20. :filter-node-method="filterNode"
  21. :default-checked-keys="defaultSelectedByCode"
  22. accordion
  23. @node-click="handleNodeClick"
  24. @check-change="handleCheckChange"
  25. />
  26. </div>
  27. <div class="right" style="margin-left: 16px">
  28. <div class="head" style="margin-bottom: 22px">
  29. <span style="font-weight: 400; font-size: 16px; color: rgba(0, 0, 0, 0.88); line-height: 22px"
  30. >已选择({{ selected }})</span
  31. >
  32. </div>
  33. <div class="selected">
  34. <el-tag v-for="(person, index) in selectedPeople" :key="index" closable @close="handleTagClose(person.code)">
  35. {{ person.name }}
  36. </el-tag>
  37. </div>
  38. <div class="footer">
  39. <el-button @click="handleCancle"> 取消 </el-button>
  40. <el-button type="primary" @click="handleSubmit">提交</el-button>
  41. </div>
  42. </div>
  43. </div>
  44. </template>
  45. <script lang="ts" setup>
  46. import { onMounted, ref } from 'vue';
  47. import { Search } from '@element-plus/icons-vue';
  48. import { ElTree } from 'element-plus';
  49. import { ElMessage } from 'element-plus';
  50. import { getPushRange, updatePushRange } from '@/api/message/question-notifications';
  51. import { treeSelected } from '../type';
  52. import { CameraTree, getCameraTree } from '@/api/camera/camera-preview';
  53. const props = defineProps<{
  54. selectedUser: treeSelected[];
  55. refreshCamera: (arr: treeSelected[]) => unknown;
  56. }>();
  57. //相机树信息
  58. const cameraTreeTemp = ref<CameraTree[]>([]); // 保存修改name之后的树
  59. // 把树节点中所有 nodeType = camera 的 name 替换成 name + code
  60. function getCameraNameCode(data) {
  61. const cameraNameCode = data;
  62. for (let i = 0; i < data.length; i++) {
  63. const node = cameraNameCode[i];
  64. if (node.nodeType === 'camera') {
  65. node.name = node.name + ` [${node.code}] `;
  66. }
  67. if (node.children && node.children.length > 0) {
  68. getCameraNameCode(node.children);
  69. }
  70. }
  71. return cameraNameCode;
  72. }
  73. const getCameraData = async (tempQuery) => {
  74. await getCameraTree(tempQuery).then((res) => {
  75. cameraTreeTemp.value = getCameraNameCode(res);
  76. });
  77. };
  78. const queryStr = ref<string>('');
  79. const defaultProps = { label: 'name' };
  80. const filterNode = (query, nodeData, node) => {
  81. if (!query) return true;
  82. nodeData.filter = nodeData.name!.includes(query);
  83. if (!nodeData.filter && node.level > 1) {
  84. nodeData.filter = node.parent.data.filter;
  85. }
  86. return nodeData.filter!;
  87. };
  88. const treeRef = ref<InstanceType<typeof ElTree>>();
  89. const total = ref<number>(0);
  90. const selected = ref<number>(0);
  91. const defaultSelectedByCode = ref<(string | number)[]>([]);
  92. const selectedPeople = ref<treeSelected[]>([]);
  93. const handleTagClose = (code) => {
  94. const index = selectedPeople.value.findIndex((item) => item.code === code);
  95. if (index !== -1) {
  96. selectedPeople.value.splice(index, 1);
  97. selected.value = selectedPeople.value.length;
  98. treeRef.value!.setChecked(code, false, true);
  99. }
  100. };
  101. const emit = defineEmits(['cancel', 'submit']);
  102. const handleCancle = () => {
  103. emit('cancel');
  104. };
  105. const handleSubmit = () => {
  106. const arr = selectedPeople.value.map((selectedPeople) => selectedPeople.id);
  107. updatePushRange({ cameraIdList: arr })
  108. .then(() => {
  109. ElMessage({
  110. message: '修改成功',
  111. type: 'success',
  112. plain: true,
  113. });
  114. props.refreshCamera(selectedPeople.value);
  115. })
  116. .catch((error) => {
  117. console.error(error);
  118. handleCancle();
  119. });
  120. };
  121. const handleCheckChange = (node, checked) => {
  122. if (!node.children) {
  123. if (checked) {
  124. const exists = selectedPeople.value.some((item) => item.code === node.code);
  125. if (!exists) {
  126. selectedPeople.value.push({
  127. id: node.id,
  128. code: node.code,
  129. name: node.name,
  130. });
  131. }
  132. } else {
  133. const index = selectedPeople.value.findIndex((item) => item.code === node.code);
  134. if (index !== -1) {
  135. selectedPeople.value.splice(index, 1);
  136. }
  137. }
  138. selected.value = selectedPeople.value.length;
  139. }
  140. };
  141. const handleNodeClick = (node, checked) => {
  142. if (!node.children) {
  143. treeRef.value!.setChecked(node.code, !checked.checked, true);
  144. }
  145. };
  146. onMounted(() => {
  147. getPushRange().then((res) => {
  148. selectedPeople.value = res;
  149. selected.value = selectedPeople.value.length;
  150. defaultSelectedByCode.value = selectedPeople.value.map((item) => item.code);
  151. });
  152. getCameraData({});
  153. });
  154. </script>
  155. <style lang="scss" scoped>
  156. .select-tree {
  157. display: flex;
  158. width: 100%;
  159. height: 100%;
  160. .left {
  161. display: flex;
  162. flex-direction: column;
  163. width: 50%;
  164. height: 100%;
  165. border-right: 1px solid rgba(0, 0, 0, 0.06);
  166. .el-tree {
  167. width: 100%;
  168. margin-top: 20px;
  169. font-weight: 500;
  170. font-size: 14px;
  171. color: #303133;
  172. line-height: 22px;
  173. overflow-y: auto;
  174. max-height: calc(100% - 80px);
  175. }
  176. }
  177. .right {
  178. display: flex;
  179. flex-direction: column;
  180. flex: 1;
  181. height: 100%;
  182. position: relative;
  183. .head {
  184. display: flex;
  185. align-items: center;
  186. font-weight: 400;
  187. font-size: 16px;
  188. color: rgba(0, 0, 0, 0.88);
  189. line-height: 22px;
  190. }
  191. .selected {
  192. display: flex;
  193. flex-wrap: wrap;
  194. gap: 8px;
  195. overflow-y: auto;
  196. max-height: calc(100% - 120px);
  197. }
  198. .footer {
  199. display: flex;
  200. gap: 6px;
  201. position: absolute;
  202. right: 24px;
  203. bottom: 20px;
  204. }
  205. }
  206. }
  207. </style>