index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. <template>
  2. <div class="page-head">
  3. <van-search
  4. v-model="keywords"
  5. placeholder="请输入搜索关键词"
  6. :left-icon="searchImg"
  7. :right-icon="closeImg"
  8. @search="onSearch"
  9. />
  10. <div class="select-box">
  11. <div
  12. style="padding-top: 10px; width: 50%; margin-left: 20px"
  13. @click="showStructure"
  14. >
  15. {{adressContent}}
  16. </div>
  17. <div>
  18. <van-field
  19. v-model="fieldValue"
  20. is-link
  21. readonly
  22. placeholder="选择类型"
  23. @click="showPicker = true"
  24. />
  25. <van-popup
  26. v-model:show="showPicker"
  27. destroy-on-close
  28. round
  29. position="bottom"
  30. >
  31. <van-picker
  32. :columns="responsibility_type"
  33. title="类型选择"
  34. :columns-field-names="customFieldName"
  35. @cancel="showPicker = false"
  36. @confirm="onConfirm"
  37. />
  38. </van-popup>
  39. </div>
  40. </div>
  41. </div>
  42. <div
  43. style="
  44. margin-top: 90px;
  45. height: calc(100vh - 55px - 90px);
  46. overflow: scroll;
  47. "
  48. >
  49. <van-list
  50. v-model:loading="loading"
  51. :finished="finished"
  52. finished-text="没有更多了"
  53. @load="onLoad"
  54. >
  55. <div v-for="item in persons" :key="item.id">
  56. <div class="person-box" @click="showPersonalInformation(item)">
  57. <div class="circle">
  58. <img :src="item.profile_picture" alt="" />
  59. </div>
  60. <div class="information-card">
  61. <div class="one">
  62. <div>{{ item.name }}</div>
  63. <div class="isOnline" :class="{ notOnline: item.state }">
  64. {{ item.state === false ? "离线" : "在线" }}
  65. </div>
  66. </div>
  67. <div class="text-ellipsis">{{ item.position }}</div>
  68. <div style="margin-top: 5px">
  69. <span v-for="i in item.type" :key="i" class="type-name">
  70. {{ i }}
  71. </span>
  72. </div>
  73. </div>
  74. </div>
  75. </div>
  76. </van-list>
  77. </div>
  78. <div>
  79. <van-overlay :show="isShowStructure">
  80. <div class="block1">
  81. <van-search
  82. v-model="keywords"
  83. placeholder="请输入关键字"
  84. :left-icon="searchImg"
  85. :right-icon="closeImg"
  86. :clearable="false"
  87. @search="onSearchKeyword"
  88. @click-right-icon.stop="onSearchCancel"
  89. />
  90. <div class="head-card">
  91. <div class="structure-head">
  92. <div class="blue-rectangle" />
  93. <div style="margin-top: 5px; margin-left: 5px">组织架构</div>
  94. </div>
  95. <div style="color: #b7c0c4; padding: 8px 13px; font-size: 13px">
  96. [总人数]
  97. </div>
  98. </div>
  99. <hr />
  100. <div style="margin-top: 1px">
  101. <el-tree
  102. ref="treeRef"
  103. class="filter-tree"
  104. :data="treeData"
  105. show-checkbox
  106. :props="defaultProps"
  107. :filter-node-method="filterNode"
  108. node-key="uuid"
  109. default-expand-all
  110. @check-change="handleCheckChange"
  111. style="
  112. height: calc(-145px - 70px + 100vh);
  113. overflow: scroll;
  114. margin-left: 10px;
  115. "
  116. >
  117. <template #default="{ node, data }">
  118. <span class="custom-tree-node">
  119. <span>{{ node.label }}</span>
  120. <span>
  121. <a @click="append(data)" style="color: #d6d6d7"> [{{ data.personSum }}] </a>
  122. </span>
  123. </span>
  124. </template>
  125. </el-tree>
  126. </div>
  127. <div class="footer">
  128. <div class="cancel-btn" @click="handleCancel">取消</div>
  129. <div class="confirm-btn" @click="handleConfirm">确认</div>
  130. </div>
  131. </div>
  132. </van-overlay>
  133. </div>
  134. </template>
  135. <script setup lang="ts">
  136. import {reactive, ref, watch} from "vue";
  137. import profile from "@/assets/images/tempImage/用户.png";
  138. import { getDicts } from "@/api/system/dict/data";
  139. import { getEventList } from "@/api/event";
  140. import searchImg from "@/assets/images/search.png";
  141. import closeImg from "@/assets/images/close.png";
  142. import { ElTree } from "element-plus";
  143. import {showConfirmDialog, showFailToast, showToast} from "vant";
  144. import {create_by_dept_ids} from "@/api/onlineRollCall";
  145. const router = useRouter();
  146. const keywords = ref("");
  147. let responsibility_type = ref([]);
  148. const fieldValue = ref("");
  149. const showPicker = ref(false);
  150. const onConfirm = ({ selectedValues, selectedOptions }) => {
  151. showPicker.value = false; //用于控制 van-picker 组件的显示状态,将其设置为 false 表示隐藏选择器。
  152. requestParameters.value.type_content = selectedValues.toString(); //将用户选择的值(selectedValues)保存到 requestParameters 对象的 type_content 属性中
  153. fieldValue.value = selectedOptions[0].dictLabel; //将用户选择的第一个选项的 dictLabel 属性值保存到 fieldValue 中
  154. onLoad();
  155. };
  156. const loading = ref(false);
  157. const finished = ref(false);
  158. const customFieldName = {
  159. text: "dictLabel",
  160. value: "dictValue"
  161. };
  162. const requestParameters = ref({
  163. name: "",
  164. type: [],
  165. type_content: "",
  166. page: 1,
  167. page_size: 10,
  168. keyword: keywords
  169. });
  170. const persons = ref([]);
  171. const onLoad = () => {
  172. getEventList(requestParameters.value)
  173. .then(res => {
  174. res.data = [
  175. {
  176. id: 1,
  177. name: "张嘉佳",
  178. unit: "茂名市委员会",
  179. position: "茂名市市委常委 市人民政府副市长 党组副书记",
  180. type: ["三防指挥部", "应急部门"],
  181. profile_picture: profile,
  182. state: true
  183. },
  184. {
  185. id: 2,
  186. name: "王长青",
  187. unit: "茂名市委员会",
  188. position: "茂名市委员会 茂名市委常委 茂名市委常委",
  189. type: ["党委政府"],
  190. profile_picture: profile,
  191. state: false
  192. },
  193. {
  194. id: 3,
  195. name: "王长青",
  196. unit: "茂名市委员会",
  197. position: "茂名市委员会 茂名市委常委",
  198. type: ["重点部门"],
  199. profile_picture: profile,
  200. state: true
  201. },
  202. {
  203. id: 4,
  204. name: "王长青",
  205. unit: "茂名市委员会",
  206. position: "茂名市委员会 茂名市委常委",
  207. type: ["重点部门"],
  208. profile_picture: profile,
  209. state: true
  210. },
  211. {
  212. id: 5,
  213. name: "王长青",
  214. unit: "茂名市委员会",
  215. position: "茂名市委员会 茂名市委常委",
  216. type: ["重点部门"],
  217. profile_picture: profile,
  218. state: true
  219. },
  220. {
  221. id: 6,
  222. name: "王长青",
  223. unit: "茂名市委员会",
  224. position: "茂名市委员会 茂名市委常委",
  225. type: ["重点部门", "成员单位"],
  226. profile_picture: profile,
  227. state: true
  228. },
  229. {
  230. id: 7,
  231. name: "王长青",
  232. unit: "茂名市委员会",
  233. position: "茂名市委员会 茂名市委常委",
  234. type: ["重点部门"],
  235. profile_picture: profile,
  236. state: true
  237. },
  238. {
  239. id: 8,
  240. name: "王长青",
  241. unit: "茂名市委员会",
  242. position: "茂名市委员会 茂名市委常委",
  243. type: ["重点部门"],
  244. profile_picture: profile,
  245. state: true
  246. },
  247. {
  248. id: 9,
  249. name: "王长青",
  250. unit: "茂名市委员会",
  251. position: "茂名市委员会 茂名市委常委",
  252. type: ["重点部门"],
  253. profile_picture: profile,
  254. state: true
  255. },
  256. {
  257. id: 10,
  258. name: "王长青",
  259. unit: "茂名市委员会",
  260. position: "茂名市委员会 茂名市委常委",
  261. type: ["重点部门"],
  262. profile_picture: profile,
  263. state: true
  264. }
  265. ];
  266. var item = res.data || [];
  267. // 重置 persons 数组(如果是第一页)
  268. if (requestParameters.value.page == 1) {
  269. persons.value = [];
  270. }
  271. // 将新数据添加到 persons 数组中
  272. item.forEach(temp => {
  273. persons.value.push(temp);
  274. });
  275. // 检查是否加载了所有数据
  276. if (persons.value.length >= res.total) {
  277. finished.value = true;
  278. } else {
  279. finished.value = false;
  280. }
  281. requestParameters.value.page++;
  282. loading.value = false;
  283. })
  284. .catch(error => {
  285. // 处理错误(例如,显示错误消息或重置加载状态)
  286. console.error("加载事件列表时出错:", error);
  287. loading.value = false;
  288. });
  289. };
  290. onMounted(() => {
  291. getDicts("responsibility_type").then(res => {
  292. res.data.unshift({ dictLabel: "所有类型", dictValue: "" });
  293. responsibility_type.value = res.data;
  294. });
  295. });
  296. const onSearch = keywords => {
  297. requestParameters.value.page = 1;
  298. onLoad();
  299. };
  300. const showPersonalInformation = item => {
  301. router.push({ name: "PersonInformation", query: { id: item.id } });
  302. };
  303. let keyword = ref("");
  304. let treeData = ref([
  305. {
  306. id: 1,
  307. deptType: true,
  308. label: "茂南区",
  309. personSum: 34643,
  310. children: [
  311. {
  312. id: 2,
  313. deptType: true,
  314. label: "茂南区政府",
  315. personSum: 343,
  316. children: [
  317. {
  318. id: 3,
  319. deptType: true,
  320. label: "茂南区应急管理局",
  321. personSum: 5687,
  322. children: [
  323. {
  324. id: 3,
  325. deptType: true,
  326. label: "茂南区应急管理局",
  327. personSum: 5687
  328. }
  329. ]
  330. }
  331. ]
  332. }
  333. ]
  334. },
  335. {
  336. id: 1,
  337. deptType: true,
  338. label: "茂南区",
  339. personSum: 34643,
  340. children: [
  341. {
  342. id: 2,
  343. deptType: true,
  344. label: "茂南区政府",
  345. personSum: 343,
  346. children: [
  347. {
  348. id: 3,
  349. deptType: true,
  350. label: "茂南区应急管理局",
  351. personSum: 5687,
  352. children: [
  353. {
  354. id: 3,
  355. deptType: true,
  356. label: "茂南区应急管理局",
  357. personSum: 5687
  358. }
  359. ]
  360. }
  361. ]
  362. }
  363. ]
  364. },
  365. {
  366. id: 1,
  367. deptType: true,
  368. label: "茂南区",
  369. personSum: 34643,
  370. children: [
  371. {
  372. id: 2,
  373. deptType: true,
  374. label: "茂南区政府",
  375. personSum: 343,
  376. children: [
  377. {
  378. id: 3,
  379. deptType: true,
  380. label: "茂南区应急管理局",
  381. personSum: 5687,
  382. children: [
  383. {
  384. id: 3,
  385. deptType: true,
  386. label: "茂南区应急管理局",
  387. personSum: 5687
  388. }
  389. ]
  390. }
  391. ]
  392. }
  393. ]
  394. },
  395. {
  396. id: 1,
  397. deptType: true,
  398. label: "茂南区",
  399. personSum: 34643,
  400. children: [
  401. {
  402. id: 2,
  403. deptType: true,
  404. label: "茂南区政府",
  405. personSum: 343,
  406. children: [
  407. {
  408. id: 3,
  409. deptType: true,
  410. label: "茂南区应急管理局",
  411. personSum: 5687,
  412. children: [
  413. {
  414. id: 3,
  415. deptType: true,
  416. label: "茂南区应急管理局",
  417. personSum: 5687
  418. }
  419. ]
  420. }
  421. ]
  422. }
  423. ]
  424. },
  425. {
  426. id: 1,
  427. deptType: true,
  428. label: "茂南区",
  429. personSum: 34643,
  430. children: [
  431. {
  432. id: 2,
  433. deptType: true,
  434. label: "茂南区政府",
  435. personSum: 343,
  436. children: [
  437. {
  438. id: 3,
  439. deptType: true,
  440. label: "茂南区应急管理局",
  441. personSum: 5687,
  442. children: [
  443. {
  444. id: 3,
  445. deptType: true,
  446. label: "茂南区应急管理局",
  447. personSum: 5687
  448. }
  449. ]
  450. }
  451. ]
  452. }
  453. ]
  454. },
  455. {
  456. id: 1,
  457. deptType: true,
  458. label: "茂南区",
  459. personSum: 34643,
  460. children: [
  461. {
  462. id: 2,
  463. deptType: true,
  464. label: "茂南区政府",
  465. personSum: 343,
  466. children: [
  467. {
  468. id: 3,
  469. deptType: true,
  470. label: "茂南区应急管理局",
  471. personSum: 5687,
  472. children: [
  473. {
  474. id: 3,
  475. deptType: true,
  476. label: "茂南区应急管理局",
  477. personSum: 5687
  478. }
  479. ]
  480. }
  481. ]
  482. }
  483. ]
  484. },
  485. {
  486. id: 1,
  487. deptType: true,
  488. label: "茂南区",
  489. personSum: 34643,
  490. children: [
  491. {
  492. id: 2,
  493. deptType: true,
  494. label: "茂南区政府",
  495. personSum: 343,
  496. children: [
  497. {
  498. id: 3,
  499. deptType: true,
  500. label: "茂南区应急管理局",
  501. personSum: 5687,
  502. children: [
  503. {
  504. id: 3,
  505. deptType: true,
  506. label: "茂南区应急管理局",
  507. personSum: 5687
  508. }
  509. ]
  510. }
  511. ]
  512. }
  513. ]
  514. }
  515. ]);
  516. let treeRef = ref();
  517. let checkData = ref([]);
  518. const isShowStructure = ref(false);
  519. const showStructure = () => {
  520. isShowStructure.value = true;
  521. };
  522. const onSearchKeyword = () => {};
  523. const onSearchCancel = () => {};
  524. const handleCancel = () => {
  525. isShowStructure.value = false;
  526. };
  527. const adressContent = ref("");
  528. const handleConfirm = () => {
  529. if (checkData.value.length > 0) {
  530. showConfirmDialog({
  531. title: '提示',
  532. message: '确认选择?'
  533. })
  534. .then(() => {
  535. // create_by_dept_ids({depts: checkData.value, remark:''}).then((res) => {
  536. // showToast({type: 'success', message: res.msg, onClose:()=>{
  537. // router.push({ name : 'RollCallDetails', query: { id: res.data }})
  538. // }});
  539. // })
  540. adressContent.value = checkData.value;
  541. isShowStructure.value = false;
  542. });
  543. // router.push({ name : 'RollCallDetails', query: { id: 'test' }})
  544. } else {
  545. showFailToast('请先选择点名单位')
  546. }
  547. }
  548. let defaultProps = reactive({
  549. children: "children",
  550. label: "label",
  551. personSum: "personSum"
  552. });
  553. const filterNode = (value, data) => {
  554. if (!value) return true;
  555. return data.label.indexOf(value) !== -1;
  556. };
  557. watch(keyword, (val) => {
  558. treeRef.value!.filter(val);
  559. });
  560. const handleCheckChange = () => {
  561. const data = [];
  562. const treeData = treeRef.value.getCheckedNodes(false, false);
  563. treeData.forEach((item) => {
  564. data.push(item.id);
  565. });
  566. checkData.value = data;
  567. };
  568. </script>
  569. <style scoped lang="scss">
  570. .select-box {
  571. display: flex;
  572. flex-direction: row;
  573. margin-top: -2px;
  574. background: #fff;
  575. }
  576. .circle {
  577. width: 80px;
  578. height: 80px;
  579. border-radius: 50%;
  580. background-color: #eaeaea;
  581. display: flex;
  582. justify-content: center;
  583. align-items: center;
  584. color: black;
  585. font-size: 15px;
  586. margin: 15px 5px 15px;
  587. }
  588. .circle2 {
  589. width: 100px;
  590. height: 100px;
  591. border-radius: 50%;
  592. background-color: #eaeaea;
  593. display: flex;
  594. justify-content: center;
  595. align-items: center;
  596. color: black;
  597. font-size: 15px;
  598. margin: 15px 5px 15px;
  599. }
  600. .person-box {
  601. display: flex;
  602. flex-direction: row;
  603. }
  604. .information-card {
  605. display: flex;
  606. flex-direction: column;
  607. margin-left: 10px;
  608. }
  609. .type-name {
  610. background-color: #eff2fc;
  611. padding: 3px 15px;
  612. font-size: 13px;
  613. color: #8d9bc8;
  614. margin-left: 5px;
  615. &:nth-child(1) {
  616. margin-left: 0;
  617. }
  618. }
  619. .text-ellipsis {
  620. margin-top: 5px;
  621. width: 250px;
  622. white-space: nowrap;
  623. overflow: hidden;
  624. text-overflow: ellipsis;
  625. font-size: 14px;
  626. }
  627. .one {
  628. display: flex;
  629. justify-content: space-between;
  630. margin-top: 15px;
  631. align-items: center;
  632. }
  633. .isOnline {
  634. font-size: 12px;
  635. background-color: #b3b3b3;
  636. padding: 0px 10px;
  637. height: 15px;
  638. }
  639. .notOnline {
  640. background-color: limegreen;
  641. }
  642. .wrapper {
  643. display: flex;
  644. align-items: center;
  645. justify-content: center;
  646. height: 100%;
  647. }
  648. .block {
  649. width: 100%;
  650. height: 100%;
  651. background-color: #f2f2f2;
  652. }
  653. .block1 {
  654. width: 100%;
  655. height: 100%;
  656. background-color: #ffffff;
  657. }
  658. .information-box {
  659. display: flex;
  660. flex-direction: row;
  661. padding: 10px 10px;
  662. }
  663. .unit-box {
  664. display: flex;
  665. flex-direction: row;
  666. background-color: #ffffff;
  667. height: 35px;
  668. width: 100%;
  669. margin-top: 5px;
  670. }
  671. .unit1-box {
  672. display: flex;
  673. flex-direction: row;
  674. background-color: #ffffff;
  675. height: 35px;
  676. width: 100%;
  677. margin-top: 1px;
  678. }
  679. .info-content {
  680. margin: 5px 20px;
  681. }
  682. .text {
  683. color: grey;
  684. width: 84.04px;
  685. padding: 5px 10px;
  686. text-align-last: justify;
  687. }
  688. .page-head {
  689. position: fixed;
  690. top: 0;
  691. width: 100%;
  692. }
  693. .structure-head {
  694. display: flex;
  695. background-color: #ffffff;
  696. padding-left: 10px;
  697. padding-bottom: 5px;
  698. align-items: center;
  699. }
  700. .blue-rectangle {
  701. width: 6px;
  702. height: 17px;
  703. background-image: linear-gradient(180deg, #00fce7 0%, #2c81ff 100%);
  704. border-radius: 0 3px 0 0;
  705. }
  706. .head-card {
  707. display: flex;
  708. flex-direction: row;
  709. justify-content: space-between;
  710. background-color: white;
  711. }
  712. .footer {
  713. position: absolute;
  714. z-index: 9999;
  715. bottom: 0;
  716. left: 0;
  717. width: 100%;
  718. height: 70px;
  719. background: #ffffff;
  720. display: flex;
  721. justify-content: center;
  722. align-items: center;
  723. margin-bottom: 50px;
  724. .cancel-btn {
  725. width: 90px;
  726. height: 40px;
  727. border: 1px solid #d1d1d1;
  728. border-radius: 2px;
  729. font-size: 16px;
  730. color: #b3b3b3;
  731. display: flex;
  732. justify-content: center;
  733. align-items: center;
  734. }
  735. .confirm-btn {
  736. width: 90px;
  737. height: 40px;
  738. background: #2c81ff;
  739. border-radius: 2px;
  740. font-size: 16px;
  741. color: #ffffff;
  742. display: flex;
  743. justify-content: center;
  744. align-items: center;
  745. margin-left: 80px;
  746. }
  747. }
  748. .custom-tree-node {
  749. display: flex;
  750. width: 100%;
  751. justify-content: space-between;
  752. margin-right: 12px;
  753. }
  754. </style>