index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. import { NPopover, NScrollbar, NTooltip } from 'naive-ui';
  2. import {
  3. PropType,
  4. computed,
  5. defineComponent,
  6. onMounted,
  7. reactive,
  8. ref,
  9. toRefs,
  10. watch
  11. } from 'vue';
  12. import styles from './index.module.less';
  13. import arrowDown from './images/icon-arrow-down.png';
  14. import arrowUp from './images/icon-arrow-up.png';
  15. export default defineComponent({
  16. name: 'c-cascader',
  17. props: {
  18. value: {
  19. type: String,
  20. default: ''
  21. },
  22. options: {
  23. type: Array as PropType<any[]>,
  24. default: () => []
  25. },
  26. placeholder: {
  27. type: String,
  28. default: '请选择'
  29. },
  30. placement: {
  31. type: String,
  32. default: 'bottom-start'
  33. },
  34. showPath: {
  35. type: Boolean,
  36. default: false
  37. }
  38. },
  39. emits: ['update:value'],
  40. setup(props, { emit }) {
  41. const state = reactive({
  42. popoverShow: false,
  43. selectParents: {}, // 选中的数据
  44. tagActiveId: '' as any,
  45. tagActive: {} as any,
  46. childSelectId: null as any,
  47. x: 0,
  48. y: 0
  49. });
  50. // const formatParentCurrentValue = (ids: any, list: any) => {
  51. // for (const item of list) {
  52. // if (ids.includes(item.id)) {
  53. // if (item.children && item.children.length > 0) {
  54. // let lastId: any;
  55. // item.children.forEach((child: any) => {
  56. // if (ids.includes(child.id)) {
  57. // lastId = child.id;
  58. // }
  59. // });
  60. // item.activeIndex = lastId;
  61. // }
  62. // }
  63. // if (item.children && item.children.length > 0) {
  64. // formatParentCurrentValue(ids, item.children);
  65. // }
  66. // }
  67. // };
  68. const initParentSelect = (subject: any) => {
  69. let children: any;
  70. let columnName = '';
  71. if (subject.children) {
  72. children = [
  73. {
  74. columnName: subject.children[0].columnName,
  75. name: '全部' + subject.children[0].columnName || '',
  76. id: ''
  77. },
  78. ...subject.children
  79. ];
  80. columnName = subject.children[0].columnName;
  81. state.selectParents = {
  82. ...subject,
  83. columnName,
  84. activeIndex: '',
  85. children
  86. };
  87. } else {
  88. state.selectParents = {};
  89. }
  90. };
  91. // watch(
  92. // () => state.popoverShow,
  93. // () => {
  94. // if (!state.popoverShow || !props.value) return;
  95. // let ids = formatParentId(props.value, props.options);
  96. // state.tagActiveId = ids[0].id;
  97. // props.options.forEach((item: any) => {
  98. // if (item.id === state.tagActiveId) {
  99. // initParentSelect(item);
  100. // }
  101. // });
  102. // //
  103. // const index = ids.findIndex((child: any) => child.id === props.value);
  104. // ids = ids.slice(0, index + 1);
  105. // const values = ids.map((item: any) => {
  106. // return item.id;
  107. // });
  108. // console.log(values, 'values');
  109. // formatParentCurrentValue(values, props.options);
  110. // }
  111. // );
  112. const valueText = computed(() => {
  113. const id = props.value;
  114. const values = getValues(id);
  115. const names: any = [];
  116. values.forEach((item: any) => {
  117. names.push(item.name);
  118. });
  119. if (props.showPath) {
  120. return names.join(' / ');
  121. } else {
  122. const lastName = names[names.length - 1];
  123. // console.log(lastName, 'last names');
  124. return lastName;
  125. }
  126. });
  127. // 递归获取数据
  128. const formatParentId = (id: any, list: any, ids = [] as any) => {
  129. for (const item of list) {
  130. if (item.children && item.children.length > 0) {
  131. const cIds: any = formatParentId(id, item.children, [
  132. ...ids,
  133. {
  134. name: item.name,
  135. id: item.id
  136. }
  137. ]);
  138. const index = cIds.findIndex((c: any) => c.id === id);
  139. if (index > -1) {
  140. return cIds;
  141. }
  142. }
  143. if (item.id === id) {
  144. return [
  145. ...ids,
  146. {
  147. name: item.name,
  148. id
  149. }
  150. ];
  151. }
  152. }
  153. return ids;
  154. };
  155. const getValues = (value: any) => {
  156. let ids = formatParentId(value, props.options);
  157. const index = ids.findIndex((child: any) => child.id === value);
  158. ids = ids.slice(0, index + 1);
  159. return ids;
  160. };
  161. // 重置
  162. const onReset = () => {
  163. state.childSelectId = null;
  164. state.tagActiveId = '';
  165. state.selectParents = {};
  166. emit('update:value', '');
  167. state.popoverShow = false;
  168. };
  169. // 提交
  170. const onConfirm = () => {
  171. emit('update:value', state.childSelectId || state.tagActiveId);
  172. state.popoverShow = false;
  173. };
  174. return () => (
  175. <>
  176. <NPopover
  177. placement={props.placement as any}
  178. v-model:show={state.popoverShow}
  179. showArrow={false}
  180. trigger="click"
  181. displayDirective="show"
  182. class={[styles.cascaderPopover, 'c-cascaderPopover']}>
  183. {{
  184. trigger: () => (
  185. <div
  186. class={[
  187. styles.nBaseCascaser,
  188. state.popoverShow ? styles.nBaseCascaserActive : ''
  189. ]}
  190. title={valueText.value}>
  191. <div class={styles['n-base-selection-tags']}>
  192. <div class={styles['n-base-selection-input']}>
  193. <div class={styles['n-base-selection-input__content']}>
  194. {valueText.value}
  195. </div>
  196. </div>
  197. <div class={[styles['n-base-suffix']]}>
  198. <div class={[styles.arrow]}>
  199. <img src={state.popoverShow ? arrowUp : arrowDown} />
  200. </div>
  201. </div>
  202. </div>
  203. <div
  204. class={[
  205. styles['n-base-selection-placeholder'],
  206. styles['n-base-selection-overlay']
  207. ]}>
  208. {!valueText.value && (
  209. <div class={styles.inner}>{props.placeholder}</div>
  210. )}
  211. </div>
  212. <div class={styles['n-base-selection__border']}></div>
  213. <div class={styles['n-base-selection__state-border']}></div>
  214. </div>
  215. ),
  216. default: () => (
  217. <div class={styles.baseContent}>
  218. <NScrollbar
  219. class={styles.baseScrollBar}
  220. style={{ maxHeight: '400px' }}>
  221. <div class={styles.baseContentTitle}>
  222. {props.options[0].columnName}
  223. </div>
  224. <div class={styles.baseContentWrap}>
  225. {props.options.map((subject: any) => (
  226. <span
  227. class={[
  228. styles.tag,
  229. (state.tagActiveId || '') == subject.id &&
  230. styles.tagActive
  231. ]}
  232. onClick={() => {
  233. if (state.tagActiveId !== subject.id) {
  234. state.childSelectId = null;
  235. }
  236. state.tagActiveId = subject.id;
  237. initParentSelect(subject);
  238. }}>
  239. {subject.name}
  240. </span>
  241. ))}
  242. </div>
  243. <ChildNodeSearch
  244. activeRow={state.selectParents}
  245. onSelectChildTag={(val: any) => {
  246. state.childSelectId = val;
  247. }}
  248. />
  249. </NScrollbar>
  250. <div class={styles.btnGroup}>
  251. <div class={[styles.btn, styles.btnCancel]} onClick={onReset}>
  252. 重置
  253. </div>
  254. <div
  255. class={[styles.btn, styles.btnConfirm]}
  256. onClick={onConfirm}>
  257. 确认
  258. </div>
  259. </div>
  260. </div>
  261. )
  262. }}
  263. </NPopover>
  264. </>
  265. );
  266. }
  267. });
  268. const ChildNodeSearch = defineComponent({
  269. name: 'ChildNodeSearch',
  270. props: {
  271. activeRow: {
  272. type: Object,
  273. default: () => ({})
  274. },
  275. list: {
  276. type: Array,
  277. default: () => []
  278. },
  279. loading: {
  280. type: Boolean,
  281. default: false
  282. }
  283. },
  284. emits: ['selectChildTag'],
  285. setup(props, { emit }) {
  286. const { activeRow } = toRefs(props);
  287. const selectItem = ref({});
  288. watch(
  289. () => props.activeRow,
  290. () => {
  291. activeRow.value = props.activeRow;
  292. initActiveRow();
  293. }
  294. );
  295. const initActiveRow = () => {
  296. if (activeRow.value.activeIndex) {
  297. const childList = activeRow.value.children || [];
  298. childList.forEach((subject: any) => {
  299. if (subject.id === activeRow.value.activeIndex) {
  300. let children: any;
  301. let columnName = '';
  302. if (subject.children) {
  303. children = [
  304. {
  305. columnName: subject.children[0].columnName,
  306. name: '全部' + subject.children[0].columnName || '',
  307. id: ''
  308. },
  309. ...subject.children
  310. ];
  311. columnName = subject.children[0].columnName;
  312. selectItem.value = {
  313. ...subject,
  314. columnName,
  315. activeIndex: subject.activeIndex || '',
  316. children
  317. };
  318. }
  319. }
  320. });
  321. } else {
  322. selectItem.value = {};
  323. }
  324. };
  325. onMounted(() => {
  326. initActiveRow();
  327. });
  328. return () => (
  329. <>
  330. {activeRow.value?.id && (
  331. <>
  332. <div class={styles.baseContentTitle}>
  333. {activeRow.value.columnName}
  334. </div>
  335. <div class={styles.baseContentWrap}>
  336. {activeRow.value?.children.map((subject: any) => (
  337. <span
  338. class={[
  339. styles.tag,
  340. (activeRow.value.activeIndex || '') == subject.id &&
  341. styles.tagActive
  342. ]}
  343. onClick={() => {
  344. if (props.loading) return;
  345. activeRow.value.activeIndex = subject.id;
  346. let children: any;
  347. let columnName = '';
  348. if (subject.children) {
  349. children = [
  350. {
  351. columnName: subject.children[0].columnName,
  352. name: '全部' + subject.children[0].columnName || '',
  353. id: ''
  354. },
  355. ...subject.children
  356. ];
  357. columnName = subject.children[0].columnName;
  358. selectItem.value = {
  359. ...subject,
  360. columnName,
  361. activeIndex: '',
  362. children
  363. };
  364. } else {
  365. selectItem.value = {};
  366. }
  367. emit('selectChildTag', activeRow.value.activeIndex);
  368. }}>
  369. {subject.name}
  370. </span>
  371. ))}
  372. </div>
  373. <ChildNodeSearch
  374. activeRow={selectItem.value}
  375. onSelectChildTag={(item: any) => {
  376. emit('selectChildTag', item || activeRow.value.activeIndex);
  377. }}
  378. />
  379. </>
  380. )}
  381. </>
  382. );
  383. }
  384. });