123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- <template>
- <div v-if="showDisplayValue" class="display-value" @click="handleDisplayValueClick">
- <span>{{displayValue}}</span>
- </div>
- <a-tree-select
- v-else
- ref="selectRef"
- style="min-width: 150px"
- :defaultOpen="true"
- :value="valueV"
- :dropdown-style="dropdownStyle"
- :notFoundContent="notFoundContent"
- :tree-data="treeData"
- :load-data="handleLoadData"
- :treeDataSimpleMode="true"
- :placeholder="placeholder"
- :allow-clear="allowClear"
- :multiple="multiple"
- v-bind="customProps"
- @blur="handleSelectBlur"
- @update:value="handleChange"
- />
- </template>
- <script lang="ts">
- import { TreeDataItem } from "@/models/ui/TreeCommon";
- import { SelectProps } from "ant-design-vue";
- import { defineComponent, PropType } from "vue";
- export type LoadDataFun = (pid: string|number, level: number) => Promise<TreeDataItem[]>;
- export type CheckClickableFun = (item: TreeDataItem) => Promise<boolean>;
- export type GetDiaplayValue = (ref: IdAsValueTreeDropdownInterface) => string;
- export type GetRef = (ref: IdAsValueTreeDropdownInterface) => void;
- /**
- * IdAsValueTreeDropdown 的公共接口
- */
- export interface IdAsValueTreeDropdownInterface {
- /**
- * 获取某个ID的树(正排列)
- * @param value 要获取的ID
- */
- getTree(value: number) : Array<TreeDataItem>;
- /**
- * 获取某个ID的Lablel
- * @param value 要获取的ID
- */
- getLableByValue(value: number) : string;
- /**
- * 重新加载数据
- */
- reload(): void;
- }
- /**
- * IdAsValueTreeDropdown 的公共接口
- */
- export interface IdAsValueTreeDropdownProps {
- /**
- * 允许清除
- */
- allowClear?: boolean,
- /**
- * 多选?
- */
- multiple?: boolean,
- dropdownStyle?: Record<string, unknown>,
- disabled?: boolean,
- placeholder?: string,
- /**
- * 未找到数据时的文案
- */
- notFoundContent?: string,
- /**
- * 初始化时加载数据
- */
- loadAtStart?: boolean,
- /**
- * 加载数据
- */
- loadData?: LoadDataFun,
- /**
- * 自定义检查条目是否可点击回调
- */
- checkClickable?: CheckClickableFun,
- /**
- * 获取显示数据回调
- */
- getDisplayValue?: GetDiaplayValue,
- /**
- * 是否在非激活时显示临时字符串(防止树形数据没有加载,而无法显示当前值)
- */
- showDisplayValueBeforeEdit?: boolean,
- /**
- * 子数据最大层级
- */
- maxLevel?: number,
- /**
- * 是否只有最后一级可以点击
- */
- onlyLastLevelClickable?: boolean,
- /**
- * a-select 其他自定义参数
- */
- customProps?: SelectProps,
- }
- /**
- * 使用数据的ID作为value的下拉框包装
- */
- export default defineComponent({
- name: "IdAsValueTreeDropdown",
- emits: [
- 'update:value',
- 'change',
- 'blur',
- ],
- props: {
- allowClear: {
- default: true,
- type: Boolean
- },
- multiple: {
- default: false,
- type: Boolean
- },
- dropdownStyle: {
- type: Object,
- default: () => { return { maxHeight: '400px', overflow: 'auto' } }
- },
- disabled: {
- default: false,
- type: Boolean
- },
- placeholder: {
- default: '请选择,输入可进行搜索',
- type: String
- },
- notFoundContent: {
- default: '未找到数据,请换个搜索词再试',
- type: String
- },
- loadAtStart: {
- default: true,
- type: Boolean
- },
- value: {
- default: null,
- },
- loadData: {
- type: Function as PropType<LoadDataFun>,
- default: null,
- },
- checkClickable: {
- type: Function as PropType<CheckClickableFun>,
- default: null,
- },
- getDisplayValue: {
- type: Function as PropType<GetDiaplayValue>,
- default: null,
- },
- defaultDisplayValue: {
- type: String,
- default: '',
- },
- showDisplayValueBeforeEdit: {
- default: false,
- type: Boolean
- },
- maxLevel: {
- default: 0,
- type: Number,
- },
- onlyLastLevelClickable: {
- default: false,
- type: Boolean
- },
- /**
- * a-select 其他自定义参数
- */
- customProps: {
- type: Object as PropType<SelectProps>,
- default: null,
- },
- },
- computed: {
- displayValue() : string {
- if (this.valueV != null && this.valueV != 0 && this.defaultDisplayValue != '')
- return this.defaultDisplayValue;
- if (this.getDisplayValue)
- return (this.getDisplayValue as GetDiaplayValue)(this as IdAsValueTreeDropdownInterface);
- return '';
- },
- },
- methods: {
- handleChange(value: unknown) {
- this.$nextTick(() => {
- if(value != this.value) {
- this.$emit('update:value', value);
- this.$emit('change', value);
- }
- })
- },
- handleLoadData(treeNode: { dataRef: TreeDataItem }) {
- return new Promise((resolve: (value?: unknown) => void) => {
- const { id, level } = treeNode.dataRef;
- this.doLoadData(id, level as number).then(() => resolve()).catch(() => resolve());
- });
- },
- handleDisplayValueClick() {
- this.showDisplayValue = false;
- setTimeout(() => {
- (this.$refs.selectRef as {
- focus: () => void
- }).focus();
- }, 200);
- },
- handleSelectBlur() {
- if(this.showDisplayValueBeforeEdit) {
- if(this.valueV != null && this.valueV != 0 && this.defaultDisplayValue != '')
- this.showDisplayValue = true;
- else if (this.getLableByValue(this.valueV as number) === '') //只有没有在列表中搜索到数据时,才显示临时数据
- this.showDisplayValue = true;
- }
- },
- doLoadData(pid: string|number|null, level: number) {
- const loadData = this.loadData;
- if(typeof loadData === 'function') {
- return (loadData as LoadDataFun)(pid as string, level).then((d) => {
- for(let i = this.treeData.length - 1; i >= 0; i--)
- if(this.treeData[i].pId == pid)
- this.treeData.splice(i, 1);
- d.forEach(h => {
- h.level = level + 1;
- if(this.maxLevel > 0 && h.level >= this.maxLevel)
- h.isLeaf = true;
- if(typeof this.checkClickable === 'function')
- this.checkClickable(h).then((v: boolean) => h.selectable = v);
- else if(this.maxLevel > 0) {
- if(h.level >= this.maxLevel)
- h.selectable = false;
- if(this.onlyLastLevelClickable)
- h.selectable = (h.level == this.maxLevel);
- }
- this.treeData.push(h)
- });
- });
- } else
- return Promise.resolve();
- },
- /**
- * 获取某个ID的树(正排列)
- */
- getTree(value: number) {
- const result = new Array<TreeDataItem>();
- let child : TreeDataItem|null = this.treeData.find((v) => v.id == value) as TreeDataItem;
- while(child) {
- result.unshift(child);
- if(child.pId == 0) child = null;
- else child = this.treeData.find((v) => v.id == (child as TreeDataItem).pId) as TreeDataItem;
- }
- return result;
- },
- /**
- * 获取某个ID的Lablel
- */
- getLableByValue(value: number) {
- const data = this.treeData;
- for (let i = 0; i < data.length; i++) {
- if(data[i].value == value) {
- return data[i].title;
- }
- }
- return '';
- },
- /**
- * 重新加载数据
- */
- reload() {
- this.treeData = [];
- this.doLoadData(0, 0)
- },
- },
- watch: {
- value(v) {
- this.valueV = v;
- },
- showDisplayValueBeforeEdit(v, old) {
- if(!old && v) {
- this.showDisplayValue = true;
- }
- },
- },
- data() {
- return {
- showDisplayValue: false,
- valueV: null as null|number|string,
- treeData: [] as TreeDataItem[],
- }
- },
- mounted() {
- this.valueV = this.value;
- if(this.showDisplayValueBeforeEdit)
- this.showDisplayValue = true;
- setTimeout(() => {
- if(this.loadAtStart) {
- this.treeData = [];
- this.doLoadData(0, 0) ;
- }
- } , 300);
- }
- });
- </script>
- <style lang="scss" scoped>
- .display-value {
- min-width: 150px;
- padding: 4px 11px;
- color: rgba(0, 0, 0, 0.85);
- font-size: 14px;
- line-height: 1.5715;
- background-color: #fff;
- background-image: none;
- border: 1px solid #d9d9d9;
- border-radius: 2px;
- }
- //暗黑主题
- body[data-theme="dark"] {
- .display-value {
- color: #dedede;
- background-color: #1f1f1f;
- border: 1px solid #434343;
- }
- }
- </style>
|