|
@@ -1,82 +1,55 @@
|
|
|
<script>
|
|
|
import { REFER } from "./api/index";
|
|
|
-import deepCopy from "@gby/deep-copy";
|
|
|
+
|
|
|
export default {
|
|
|
name: "PopoverTreeSelect",
|
|
|
props: {
|
|
|
- // 参照类型 ,对应后端
|
|
|
- type: {
|
|
|
- type: String,
|
|
|
- require: true,
|
|
|
- },
|
|
|
// v-model
|
|
|
value: {
|
|
|
type: [Array, String],
|
|
|
require: true,
|
|
|
},
|
|
|
- // 参照弹窗标题
|
|
|
- title: {
|
|
|
+ // 参照类型 ,对应后端
|
|
|
+ referName: {
|
|
|
type: String,
|
|
|
- default: "TITLE",
|
|
|
+ require: true,
|
|
|
},
|
|
|
// 作为 value 唯一标识的键名,绑定值
|
|
|
valueKey: {
|
|
|
type: String,
|
|
|
- default: "code",
|
|
|
+ dafault: () => {
|
|
|
+ return "code";
|
|
|
+ },
|
|
|
},
|
|
|
// 默认查询参数
|
|
|
queryParams: {
|
|
|
type: Function,
|
|
|
default: () => {},
|
|
|
},
|
|
|
- // 组件大小
|
|
|
- size: {
|
|
|
- type: String,
|
|
|
- default: "mini",
|
|
|
- },
|
|
|
- // 提示
|
|
|
- placeholder: {
|
|
|
- type: String,
|
|
|
- default: "",
|
|
|
- },
|
|
|
- // 是否可读
|
|
|
- readonly: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
- // 是否禁止
|
|
|
- disabled: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
- // 是否清除
|
|
|
- clearable: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
- // 是否多选
|
|
|
- multiple: {
|
|
|
- type: Boolean,
|
|
|
- default: false,
|
|
|
- },
|
|
|
// 需映射源数据
|
|
|
- source: Object,
|
|
|
+ source: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
// 参照内外映射
|
|
|
- dataMapping: Object,
|
|
|
+ dataMapping: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({}),
|
|
|
+ },
|
|
|
},
|
|
|
components: {},
|
|
|
data() {
|
|
|
return {
|
|
|
+ size: "mini",
|
|
|
width: "50%",
|
|
|
visible: false,
|
|
|
loading: false,
|
|
|
- params: {
|
|
|
+ model: {
|
|
|
search: "",
|
|
|
- isPage: true,
|
|
|
+ isPage: false,
|
|
|
},
|
|
|
data: [],
|
|
|
selectData: [],
|
|
|
- lastSelectData: [],
|
|
|
defaultProps: {
|
|
|
label: "name",
|
|
|
children: "children",
|
|
@@ -84,30 +57,36 @@ export default {
|
|
|
};
|
|
|
},
|
|
|
computed: {
|
|
|
- innerValue() {
|
|
|
- const { value, multiple } = this.$props;
|
|
|
- return multiple ? "" : value;
|
|
|
- },
|
|
|
- TableColumnTemp() {
|
|
|
- const { type } = this.$props;
|
|
|
- const documents = require(`./components/${type}`).default;
|
|
|
- return documents.filter((document) => document.key);
|
|
|
+ innerValue: {
|
|
|
+ get() {
|
|
|
+ return this.value;
|
|
|
+ },
|
|
|
+ set(value) {
|
|
|
+ this.$emit("input", value);
|
|
|
+ },
|
|
|
},
|
|
|
},
|
|
|
watch: {
|
|
|
- "$props.value": {
|
|
|
- handler: function (newProp) {
|
|
|
- if (!newProp) this.lastSelectData = [];
|
|
|
+ innerValue: {
|
|
|
+ handler: function (newValue) {
|
|
|
+ if (!newValue) {
|
|
|
+ const {
|
|
|
+ $props: { source, dataMapping },
|
|
|
+ } = this;
|
|
|
+ for (let key in dataMapping) {
|
|
|
+ source[key] = undefined;
|
|
|
+ }
|
|
|
+ this.$emit("update:source", source);
|
|
|
+ }
|
|
|
},
|
|
|
- immediate: true,
|
|
|
},
|
|
|
+ // "model.search": {
|
|
|
+ // handler: function (newValue) {
|
|
|
+ // this.$refs.tree.filter(newValue);
|
|
|
+ // },
|
|
|
+ // },
|
|
|
},
|
|
|
methods: {
|
|
|
- //
|
|
|
- emitChange(prop) {
|
|
|
- const { type, source, multiple } = this.$props;
|
|
|
- this.$emit("change", multiple ? prop : prop[0], source, type);
|
|
|
- },
|
|
|
// open dialog
|
|
|
async open() {
|
|
|
this.visible = true;
|
|
@@ -121,7 +100,12 @@ export default {
|
|
|
async fetchList(prop) {
|
|
|
try {
|
|
|
this.loading = true;
|
|
|
- const { code, rows } = await REFER(prop);
|
|
|
+ const { referName: type, source, queryParams } = this.$props;
|
|
|
+ const { code, rows } = await REFER({
|
|
|
+ ...prop,
|
|
|
+ ...queryParams(source),
|
|
|
+ type: type,
|
|
|
+ });
|
|
|
if (code === 200) {
|
|
|
this.data = rows;
|
|
|
}
|
|
@@ -135,73 +119,46 @@ export default {
|
|
|
},
|
|
|
// reset list
|
|
|
async useReset() {
|
|
|
- const { type, source, queryParams } = this.$props;
|
|
|
- this.model = {
|
|
|
- type,
|
|
|
- search: "",
|
|
|
- ...this.model,
|
|
|
- ...queryParams(source),
|
|
|
- };
|
|
|
+ this.data = [];
|
|
|
+ this.model.search = null;
|
|
|
await this.fetchList(this.model);
|
|
|
},
|
|
|
// query list
|
|
|
- async useQuery() {
|
|
|
- await this.fetchList(this.model);
|
|
|
- },
|
|
|
- // cancel
|
|
|
- useCancel(prop) {
|
|
|
- const { multiple } = this.$props;
|
|
|
- this.useUpdate(multiple ? prop : prop[0]);
|
|
|
- this.hide();
|
|
|
+ async useQuery(value) {
|
|
|
+ await this.$refs.tree.filter(value);
|
|
|
+ // await this.fetchList(this.model);
|
|
|
},
|
|
|
- // confirm
|
|
|
- useConfirm(prop) {
|
|
|
- const { multiple } = this.$props;
|
|
|
- this.useUpdate(multiple ? prop : prop[0]);
|
|
|
- this.emitChange(this.selectData);
|
|
|
- this.lastSelectData = deepCopy(this.selectData);
|
|
|
- this.hide();
|
|
|
- },
|
|
|
- // delete tag
|
|
|
- useDelete(prop) {
|
|
|
- this.selectData.splice(prop, 1);
|
|
|
- this.useUpdate(this.selectData);
|
|
|
- this.emitChange(this.selectData);
|
|
|
- this.lastSelectData = deepCopy(this.selectData);
|
|
|
- },
|
|
|
- // update
|
|
|
- useUpdate(prop) {
|
|
|
- const { source, multiple, valueKey, dataMapping, type } = this.$props;
|
|
|
- // update data mapping
|
|
|
- if (multiple) {
|
|
|
- const vModel = prop.map((item) => item[valueKey]);
|
|
|
- this.$emit("input", vModel);
|
|
|
+ // auto
|
|
|
+ async useAutocomplete(prop, cb) {
|
|
|
+ if (prop) {
|
|
|
+ this.model.search = prop;
|
|
|
+ await this.fetchList(this.model);
|
|
|
+ await cb(this.data);
|
|
|
} else {
|
|
|
- const vModel = prop[valueKey];
|
|
|
- this.$emit("input", vModel);
|
|
|
- for (let key in dataMapping) {
|
|
|
- source[key] = prop[dataMapping[key]];
|
|
|
- }
|
|
|
- this.$emit("update:source", source);
|
|
|
+ cb([]);
|
|
|
}
|
|
|
},
|
|
|
- // click select row data
|
|
|
- onceSelect(prop) {
|
|
|
- if (prop) this.selectData = [prop];
|
|
|
+ // select
|
|
|
+ useSelect(prop) {
|
|
|
+ this.selectData = [prop];
|
|
|
},
|
|
|
- // double click select row data and confirm
|
|
|
- doubleSelect(prop) {
|
|
|
- this.useConfirm([prop]);
|
|
|
+ // filter
|
|
|
+ useTreeFilter(value, data) {
|
|
|
+ if (!value) return true;
|
|
|
+ return data.name.indexOf(value) !== -1;
|
|
|
},
|
|
|
- // multiple select row data
|
|
|
- selectionChange(prop, data) {
|
|
|
- if (prop) {
|
|
|
- this.selectData.push(data);
|
|
|
- } else {
|
|
|
- const { code } = data;
|
|
|
- const index = this.selectData.findIndex((item) => item.code === code);
|
|
|
- this.selectData.splice(index, 1);
|
|
|
+ // confirm
|
|
|
+ useConfirm(prop) {
|
|
|
+ this.hide();
|
|
|
+ const {
|
|
|
+ $props: { source, valueKey, dataMapping },
|
|
|
+ } = this;
|
|
|
+ for (let key in dataMapping) {
|
|
|
+ source[key] = prop[dataMapping[key]];
|
|
|
}
|
|
|
+ this.innerValue = prop[valueKey];
|
|
|
+ this.$emit("update:source", source);
|
|
|
+ this.$emit("change", prop, this.$props);
|
|
|
},
|
|
|
},
|
|
|
created() {},
|
|
@@ -211,136 +168,102 @@ export default {
|
|
|
</script>
|
|
|
<template>
|
|
|
<div class="popover-tree-select">
|
|
|
- <el-input
|
|
|
+ <el-autocomplete
|
|
|
+ v-bind="$attrs"
|
|
|
v-model="innerValue"
|
|
|
- :size="size"
|
|
|
- :disabled="disabled"
|
|
|
- :readonly="readonly"
|
|
|
- :clearable="clearable"
|
|
|
- :placeholder="placeholder"
|
|
|
+ :value-key="valueKey"
|
|
|
+ :fetch-suggestions="useAutocomplete"
|
|
|
+ @select="useConfirm"
|
|
|
+ style="width: 100%"
|
|
|
>
|
|
|
- <el-button
|
|
|
- :disabled="disabled"
|
|
|
- slot="append"
|
|
|
- icon="el-icon-search"
|
|
|
- @click="open"
|
|
|
- ></el-button>
|
|
|
- </el-input>
|
|
|
+ <i class="el-icon-search" slot="suffix" @click="open"> </i>
|
|
|
+ <template slot-scope="{ item }">
|
|
|
+ <p
|
|
|
+ style="
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ overflow: hidden;
|
|
|
+ line-height: 15px;
|
|
|
+ margin: 5px 0;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ {{ item.name }}
|
|
|
+ </p>
|
|
|
+ <p
|
|
|
+ style="
|
|
|
+ font-size: 12px;
|
|
|
+ color: #b4b4b4;
|
|
|
+ line-height: 15px;
|
|
|
+ margin: 5px 0;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ {{ item.code }}
|
|
|
+ </p>
|
|
|
+ </template>
|
|
|
+ </el-autocomplete>
|
|
|
|
|
|
<el-dialog
|
|
|
- :title="`${title}(${multiple ? '多选' : '单选'})`"
|
|
|
:width="width"
|
|
|
+ :show-close="false"
|
|
|
:visible.sync="visible"
|
|
|
:close-on-click-modal="false"
|
|
|
:close-on-press-escape="false"
|
|
|
append-to-body
|
|
|
- @close="hide"
|
|
|
>
|
|
|
+ <template slot="title">
|
|
|
+ <div
|
|
|
+ style="
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ "
|
|
|
+ >
|
|
|
+ <span>选 择</span>
|
|
|
+ <span>
|
|
|
+ <el-button
|
|
|
+ :size="size"
|
|
|
+ circle
|
|
|
+ icon="el-icon-check"
|
|
|
+ @click="useConfirm(selectData[0])"
|
|
|
+ >
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ :size="size"
|
|
|
+ circle
|
|
|
+ type="danger"
|
|
|
+ icon="el-icon-close"
|
|
|
+ @click="hide"
|
|
|
+ ></el-button>
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
<el-form
|
|
|
v-loading="loading"
|
|
|
:size="size"
|
|
|
:inline="true"
|
|
|
- :model="params"
|
|
|
+ :model="model"
|
|
|
@submit.native.prevent
|
|
|
>
|
|
|
<el-form-item prop="search">
|
|
|
- <el-input
|
|
|
- v-model="params.search"
|
|
|
- @keydown.enter="useQuery"
|
|
|
- @change="useQuery"
|
|
|
- >
|
|
|
- </el-input>
|
|
|
+ <!-- @change="useQuery"
|
|
|
+ @keydown.enter="useQuery" -->
|
|
|
+ <el-input v-model="model.search" @change="useQuery"> </el-input>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button icon="el-icon-refresh" @click="useReset"></el-button>
|
|
|
</el-form-item>
|
|
|
- </el-form>
|
|
|
- <el-tree
|
|
|
- v-if="multiple"
|
|
|
- :data="data"
|
|
|
- :props="defaultProps"
|
|
|
- accordion
|
|
|
- node-key="id"
|
|
|
- >
|
|
|
- <div slot-scope="{ node, data }">
|
|
|
- <el-checkbox
|
|
|
- v-model="data.checked"
|
|
|
- @click.native.stop
|
|
|
- @change="selectionChange($event, data)"
|
|
|
- style="margin: 0 5px 0 0"
|
|
|
- >
|
|
|
- </el-checkbox>
|
|
|
- <span> {{ data.name }}</span>
|
|
|
- </div>
|
|
|
- </el-tree>
|
|
|
- <el-radio-group v-else v-model="radio" style="width: 100%">
|
|
|
<el-tree
|
|
|
v-loading="loading"
|
|
|
:data="data"
|
|
|
:props="defaultProps"
|
|
|
+ :filter-node-method="useTreeFilter"
|
|
|
+ ref="tree"
|
|
|
accordion
|
|
|
node-key="id"
|
|
|
+ @node-click="useSelect"
|
|
|
>
|
|
|
- <div slot-scope="{ node, data }">
|
|
|
- <el-radio
|
|
|
- :label="data.code"
|
|
|
- @click.native.stop="onceSelect(data)"
|
|
|
- @dblclick.native.stop="doubleSelect(data)"
|
|
|
- style="margin: 0 5px 0 0"
|
|
|
- >
|
|
|
- {{ data.name }}
|
|
|
- </el-radio>
|
|
|
- </div>
|
|
|
</el-tree>
|
|
|
- </el-radio-group>
|
|
|
- <div style="margin-top: 20px; text-align: right">
|
|
|
- <el-button :size="size" @click="visible = false">取 消</el-button>
|
|
|
- <el-button :size="size" type="primary" @click="useConfirm(selectData)"
|
|
|
- >确 定</el-button
|
|
|
- >
|
|
|
- </div>
|
|
|
+ </el-form>
|
|
|
</el-dialog>
|
|
|
- <div
|
|
|
- style="
|
|
|
- position: absolute;
|
|
|
- left: 10px;
|
|
|
- top: 50%;
|
|
|
- transform: translateY(-50%);
|
|
|
- padding-right: 30px;
|
|
|
- overflow: hidden;
|
|
|
- "
|
|
|
- >
|
|
|
- <div v-if="multiple && lastSelectData.length">
|
|
|
- <el-popover
|
|
|
- :offset="-10"
|
|
|
- :width="width"
|
|
|
- :visible-arrow="false"
|
|
|
- title=""
|
|
|
- content=""
|
|
|
- trigger="click"
|
|
|
- placement="bottom-start"
|
|
|
- >
|
|
|
- <el-tag slot="reference" :size="size" style="margin-right: 10px">
|
|
|
- + {{ lastSelectData.length }}
|
|
|
- </el-tag>
|
|
|
- <el-tag
|
|
|
- v-for="(tag, index) in lastSelectData"
|
|
|
- :size="size"
|
|
|
- hit
|
|
|
- closable
|
|
|
- :style="{
|
|
|
- display: 'flex',
|
|
|
- justifyContent: 'space-between',
|
|
|
- alignItems: 'center',
|
|
|
- margin: lastSelectData.length - 1 === index ? '0' : '0 0 5px 0',
|
|
|
- }"
|
|
|
- @close="useDelete(index)"
|
|
|
- >
|
|
|
- {{ tag.name }}
|
|
|
- </el-tag>
|
|
|
- </el-popover>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<style scoped></style>
|