|
@@ -0,0 +1,384 @@
|
|
|
+import { NPopover, NScrollbar } from 'naive-ui';
|
|
|
+import {
|
|
|
+ PropType,
|
|
|
+ computed,
|
|
|
+ defineComponent,
|
|
|
+ onMounted,
|
|
|
+ reactive,
|
|
|
+ ref,
|
|
|
+ toRefs,
|
|
|
+ watch
|
|
|
+} from 'vue';
|
|
|
+import styles from './index.module.less';
|
|
|
+import arrowDown from './images/icon-arrow-down.png';
|
|
|
+import arrowUp from './images/icon-arrow-up.png';
|
|
|
+
|
|
|
+export default defineComponent({
|
|
|
+ name: 'c-cascader',
|
|
|
+ props: {
|
|
|
+ value: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ options: {
|
|
|
+ type: Array as PropType<any[]>,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ placeholder: {
|
|
|
+ type: String,
|
|
|
+ default: '请选择'
|
|
|
+ },
|
|
|
+ placement: {
|
|
|
+ type: String,
|
|
|
+ default: 'bottom-start'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ emits: ['update:value'],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const state = reactive({
|
|
|
+ popoverShow: false,
|
|
|
+ selectParents: {}, // 选中的数据
|
|
|
+ tagActiveId: '' as any,
|
|
|
+ tagActive: {} as any,
|
|
|
+ childSelectId: null as any
|
|
|
+ });
|
|
|
+
|
|
|
+ // const formatParentCurrentValue = (ids: any, list: any) => {
|
|
|
+ // for (const item of list) {
|
|
|
+ // if (ids.includes(item.id)) {
|
|
|
+ // if (item.children && item.children.length > 0) {
|
|
|
+ // let lastId: any;
|
|
|
+ // item.children.forEach((child: any) => {
|
|
|
+ // if (ids.includes(child.id)) {
|
|
|
+ // lastId = child.id;
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+ // item.activeIndex = lastId;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // if (item.children && item.children.length > 0) {
|
|
|
+ // formatParentCurrentValue(ids, item.children);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // };
|
|
|
+
|
|
|
+ const initParentSelect = (subject: any) => {
|
|
|
+ let children: any;
|
|
|
+ let columnName = '';
|
|
|
+ if (subject.children) {
|
|
|
+ children = [
|
|
|
+ {
|
|
|
+ columnName: subject.children[0].columnName,
|
|
|
+ name: '全部' + subject.children[0].columnName || '',
|
|
|
+ id: ''
|
|
|
+ },
|
|
|
+ ...subject.children
|
|
|
+ ];
|
|
|
+ columnName = subject.children[0].columnName;
|
|
|
+
|
|
|
+ state.selectParents = {
|
|
|
+ ...subject,
|
|
|
+ columnName,
|
|
|
+ activeIndex: '',
|
|
|
+ children
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ state.selectParents = {};
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // watch(
|
|
|
+ // () => state.popoverShow,
|
|
|
+ // () => {
|
|
|
+ // if (!state.popoverShow || !props.value) return;
|
|
|
+ // let ids = formatParentId(props.value, props.options);
|
|
|
+ // state.tagActiveId = ids[0].id;
|
|
|
+ // props.options.forEach((item: any) => {
|
|
|
+ // if (item.id === state.tagActiveId) {
|
|
|
+ // initParentSelect(item);
|
|
|
+ // }
|
|
|
+ // });
|
|
|
+
|
|
|
+ // //
|
|
|
+ // const index = ids.findIndex((child: any) => child.id === props.value);
|
|
|
+ // ids = ids.slice(0, index + 1);
|
|
|
+ // const values = ids.map((item: any) => {
|
|
|
+ // return item.id;
|
|
|
+ // });
|
|
|
+ // console.log(values, 'values');
|
|
|
+ // formatParentCurrentValue(values, props.options);
|
|
|
+ // }
|
|
|
+ // );
|
|
|
+
|
|
|
+ const valueText = computed(() => {
|
|
|
+ const id = props.value;
|
|
|
+ const values = getValues(id);
|
|
|
+ const names: any = [];
|
|
|
+ values.forEach((item: any) => {
|
|
|
+ names.push(item.name);
|
|
|
+ });
|
|
|
+ return names.join(' / ');
|
|
|
+ });
|
|
|
+
|
|
|
+ // 递归获取数据
|
|
|
+ const formatParentId = (id: any, list: any, ids = [] as any) => {
|
|
|
+ for (const item of list) {
|
|
|
+ if (item.children && item.children.length > 0) {
|
|
|
+ const cIds: any = formatParentId(id, item.children, [
|
|
|
+ ...ids,
|
|
|
+ {
|
|
|
+ name: item.name,
|
|
|
+ id: item.id
|
|
|
+ }
|
|
|
+ ]);
|
|
|
+ const index = cIds.findIndex((c: any) => c.id === id);
|
|
|
+ if (index > -1) {
|
|
|
+ return cIds;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (item.id === id) {
|
|
|
+ return [
|
|
|
+ ...ids,
|
|
|
+ {
|
|
|
+ name: item.name,
|
|
|
+ id
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return ids;
|
|
|
+ };
|
|
|
+
|
|
|
+ const getValues = (value: any) => {
|
|
|
+ let ids = formatParentId(value, props.options);
|
|
|
+ const index = ids.findIndex((child: any) => child.id === value);
|
|
|
+ ids = ids.slice(0, index + 1);
|
|
|
+ return ids;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 重置
|
|
|
+ const onReset = () => {
|
|
|
+ state.childSelectId = null;
|
|
|
+ state.tagActiveId = '';
|
|
|
+ state.selectParents = {};
|
|
|
+ emit('update:value', '');
|
|
|
+ state.popoverShow = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 提交
|
|
|
+ const onConfirm = () => {
|
|
|
+ emit('update:value', state.childSelectId || state.tagActiveId);
|
|
|
+ state.popoverShow = false;
|
|
|
+ };
|
|
|
+ return () => (
|
|
|
+ <NPopover
|
|
|
+ placement={props.placement as any}
|
|
|
+ v-model:show={state.popoverShow}
|
|
|
+ showArrow={false}
|
|
|
+ trigger="click"
|
|
|
+ displayDirective="show"
|
|
|
+ class={[styles.cascaderPopover, 'c-cascaderPopover']}>
|
|
|
+ {{
|
|
|
+ trigger: () => (
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles.nBaseCascaser,
|
|
|
+ state.popoverShow ? styles.nBaseCascaserActive : ''
|
|
|
+ ]}>
|
|
|
+ <div class={styles['n-base-selection-tags']}>
|
|
|
+ <div class={styles['n-base-selection-input']}>
|
|
|
+ <div class={styles['n-base-selection-input__content']}>
|
|
|
+ {valueText.value}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class={[styles['n-base-suffix']]}>
|
|
|
+ <div class={[styles.arrow]}>
|
|
|
+ <img src={state.popoverShow ? arrowUp : arrowDown} />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ styles['n-base-selection-placeholder'],
|
|
|
+ styles['n-base-selection-overlay']
|
|
|
+ ]}>
|
|
|
+ {!valueText.value && (
|
|
|
+ <div class={styles.inner}>{props.placeholder}</div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div class={styles['n-base-selection__border']}></div>
|
|
|
+ <div class={styles['n-base-selection__state-border']}></div>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ default: () => (
|
|
|
+ <div class={styles.baseContent}>
|
|
|
+ <NScrollbar
|
|
|
+ class={styles.baseScrollBar}
|
|
|
+ style={{ maxHeight: '400px' }}>
|
|
|
+ <div class={styles.baseContentTitle}>
|
|
|
+ {props.options[0].columnName}
|
|
|
+ </div>
|
|
|
+ <div class={styles.baseContentWrap}>
|
|
|
+ {props.options.map((subject: any) => (
|
|
|
+ <span
|
|
|
+ class={[
|
|
|
+ styles.tag,
|
|
|
+ (state.tagActiveId || '') == subject.id &&
|
|
|
+ styles.tagActive
|
|
|
+ ]}
|
|
|
+ onClick={() => {
|
|
|
+ state.tagActiveId = subject.id;
|
|
|
+ initParentSelect(subject);
|
|
|
+ }}>
|
|
|
+ {subject.name}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+ <ChildNodeSearch
|
|
|
+ activeRow={state.selectParents}
|
|
|
+ onSelectChildTag={(val: any) => {
|
|
|
+ state.childSelectId = val;
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </NScrollbar>
|
|
|
+ <div class={styles.btnGroup}>
|
|
|
+ <div class={[styles.btn, styles.btnCancel]} onClick={onReset}>
|
|
|
+ 重置
|
|
|
+ </div>
|
|
|
+ <div
|
|
|
+ class={[styles.btn, styles.btnConfirm]}
|
|
|
+ onClick={onConfirm}>
|
|
|
+ 确认
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+ }}
|
|
|
+ </NPopover>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+const ChildNodeSearch = defineComponent({
|
|
|
+ name: 'ChildNodeSearch',
|
|
|
+ props: {
|
|
|
+ activeRow: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({})
|
|
|
+ },
|
|
|
+ list: {
|
|
|
+ type: Array,
|
|
|
+ default: () => []
|
|
|
+ },
|
|
|
+ loading: {
|
|
|
+ type: Boolean,
|
|
|
+ default: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ emits: ['selectChildTag'],
|
|
|
+ setup(props, { emit }) {
|
|
|
+ const { activeRow } = toRefs(props);
|
|
|
+ const selectItem = ref({});
|
|
|
+
|
|
|
+ watch(
|
|
|
+ () => props.activeRow,
|
|
|
+ () => {
|
|
|
+ activeRow.value = props.activeRow;
|
|
|
+ initActiveRow();
|
|
|
+ }
|
|
|
+ );
|
|
|
+
|
|
|
+ const initActiveRow = () => {
|
|
|
+ if (activeRow.value.activeIndex) {
|
|
|
+ const childList = activeRow.value.children || [];
|
|
|
+ childList.forEach((subject: any) => {
|
|
|
+ if (subject.id === activeRow.value.activeIndex) {
|
|
|
+ let children: any;
|
|
|
+ let columnName = '';
|
|
|
+ if (subject.children) {
|
|
|
+ children = [
|
|
|
+ {
|
|
|
+ columnName: subject.children[0].columnName,
|
|
|
+ name: '全部' + subject.children[0].columnName || '',
|
|
|
+ id: ''
|
|
|
+ },
|
|
|
+ ...subject.children
|
|
|
+ ];
|
|
|
+ columnName = subject.children[0].columnName;
|
|
|
+
|
|
|
+ selectItem.value = {
|
|
|
+ ...subject,
|
|
|
+ columnName,
|
|
|
+ activeIndex: subject.activeIndex || '',
|
|
|
+ children
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ selectItem.value = {};
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ onMounted(() => {
|
|
|
+ initActiveRow();
|
|
|
+ });
|
|
|
+ return () => (
|
|
|
+ <>
|
|
|
+ {activeRow.value?.id && (
|
|
|
+ <>
|
|
|
+ <div class={styles.baseContentTitle}>
|
|
|
+ {activeRow.value.columnName}
|
|
|
+ </div>
|
|
|
+ <div class={styles.baseContentWrap}>
|
|
|
+ {activeRow.value?.children.map((subject: any) => (
|
|
|
+ <span
|
|
|
+ class={[
|
|
|
+ styles.tag,
|
|
|
+ (activeRow.value.activeIndex || '') == subject.id &&
|
|
|
+ styles.tagActive
|
|
|
+ ]}
|
|
|
+ onClick={() => {
|
|
|
+ if (props.loading) return;
|
|
|
+ activeRow.value.activeIndex = subject.id;
|
|
|
+ let children: any;
|
|
|
+ let columnName = '';
|
|
|
+ if (subject.children) {
|
|
|
+ children = [
|
|
|
+ {
|
|
|
+ columnName: subject.children[0].columnName,
|
|
|
+ name: '全部' + subject.children[0].columnName || '',
|
|
|
+ id: ''
|
|
|
+ },
|
|
|
+ ...subject.children
|
|
|
+ ];
|
|
|
+ columnName = subject.children[0].columnName;
|
|
|
+
|
|
|
+ selectItem.value = {
|
|
|
+ ...subject,
|
|
|
+ columnName,
|
|
|
+ activeIndex: '',
|
|
|
+ children
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ selectItem.value = {};
|
|
|
+ }
|
|
|
+ emit('selectChildTag', activeRow.value.activeIndex);
|
|
|
+ }}>
|
|
|
+ {subject.name}
|
|
|
+ </span>
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <ChildNodeSearch
|
|
|
+ activeRow={selectItem.value}
|
|
|
+ onSelectChildTag={(item: any) => {
|
|
|
+ emit('selectChildTag', item || activeRow.value.activeIndex);
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ </>
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ );
|
|
|
+ }
|
|
|
+});
|