index.tsx 11 KB

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