index.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. <script>
  2. export default {
  3. name: "PopoverSelect",
  4. props: {
  5. // 参照类型 ,对应后端
  6. type: {
  7. type: String,
  8. require: true,
  9. },
  10. // v-model
  11. value: {
  12. type: [Array, String],
  13. require: true,
  14. },
  15. // 参照弹窗标题
  16. title: {
  17. type: String,
  18. default: "TITLE",
  19. },
  20. // 作为 value 唯一标识的键名,绑定值为对象类型时必填
  21. valueKey: {
  22. type: String,
  23. default: "id",
  24. },
  25. // 默认查询参数
  26. queryParams: {
  27. type: Object,
  28. default: () => ({}),
  29. },
  30. // 组件大小
  31. size: {
  32. type: String,
  33. default: "mini",
  34. },
  35. // 提示
  36. placeholder: {
  37. type: String,
  38. default: "",
  39. },
  40. // 是否可读
  41. readonly: {
  42. type: Boolean,
  43. default: false,
  44. },
  45. // 是否禁止
  46. disabled: {
  47. type: Boolean,
  48. default: false,
  49. },
  50. // 是否清除
  51. clearable: {
  52. type: Boolean,
  53. default: false,
  54. },
  55. // 是否多选
  56. multiple: {
  57. type: Boolean,
  58. default: false,
  59. },
  60. // 需映射源数据
  61. source: Object,
  62. // 参照内外映射
  63. dataMapping: Object,
  64. },
  65. emits: ["hide"],
  66. components: {
  67. TableDialog: () => import("./components/index.vue"),
  68. },
  69. data() {
  70. return {
  71. // popover宽度
  72. width: "",
  73. // 显示value
  74. showValue: "",
  75. // 选中data
  76. data: [],
  77. };
  78. },
  79. computed: {},
  80. watch: {
  81. "$props.value": {
  82. handler: function (newProp, oldProp) {
  83. if (!newProp) {
  84. this.data = [];
  85. this.showValue = "";
  86. } else {
  87. this.showValue = newProp;
  88. }
  89. },
  90. immediate: true,
  91. },
  92. },
  93. methods: {
  94. // 打开弹窗
  95. handleAsyncOpenDialog() {
  96. this.$nextTick(() => {
  97. const { setVisible } = this.$refs.TableDialogFef;
  98. setVisible(true);
  99. });
  100. },
  101. // 新增操作
  102. handleAdd(prop) {
  103. this.data = prop;
  104. this.handleUpdate(this.data);
  105. this.$emit("hide", prop);
  106. },
  107. // 删除操作
  108. handleDelete(prop) {
  109. this.data.splice(prop, 1);
  110. this.handleUpdate(this.data);
  111. },
  112. // 更新操作
  113. handleUpdate(prop) {
  114. const { source, multiple, valueKey, dataMapping } = this.$props;
  115. // 多选
  116. if (multiple) {
  117. this.showValue = "";
  118. this.$emit(
  119. "input",
  120. prop.map((item) => item[valueKey])
  121. );
  122. }
  123. // 单选
  124. if (!multiple) {
  125. this.showValue = prop[0].name;
  126. this.$emit("input", prop[0][valueKey]);
  127. // 更新映射数据
  128. for (let key in dataMapping) {
  129. source[key] = prop[0][dataMapping[key]];
  130. }
  131. this.$emit("update:source", source);
  132. }
  133. //
  134. this.$emit("change", prop, source);
  135. },
  136. },
  137. created() {
  138. this.$nextTick(() => {
  139. const { clientWidth } = this.$refs.PopoverSelect;
  140. this.width = clientWidth;
  141. });
  142. },
  143. mounted() {},
  144. destroyed() {},
  145. };
  146. </script>
  147. <template>
  148. <div ref="PopoverSelect" style="position: relative">
  149. <el-input
  150. v-model="showValue"
  151. :size="size"
  152. :disabled="disabled"
  153. :clearable="clearable"
  154. :placeholder="placeholder"
  155. readonly
  156. style="width: 100%; cursor: pointer"
  157. @click.native.stop="handleAsyncOpenDialog"
  158. >
  159. <template #suffix>
  160. <el-icon class="el-icon-more"></el-icon>
  161. </template>
  162. </el-input>
  163. <div
  164. v-if="multiple && data.length"
  165. style="
  166. position: absolute;
  167. left: 10px;
  168. top: 50%;
  169. transform: translateY(-50%);
  170. padding-right: 30px;
  171. overflow: hidden;
  172. "
  173. >
  174. <div style="width: 150px; display: flex">
  175. <el-popover
  176. :offset="-10"
  177. :width="width"
  178. :visible-arrow="false"
  179. title=""
  180. content=""
  181. trigger="click"
  182. placement="bottom-start"
  183. >
  184. <el-tag slot="reference" :size="size" style="margin-right: 10px">
  185. + {{ data.length }}
  186. </el-tag>
  187. <el-tag
  188. v-for="(item, index) in data"
  189. :size="size"
  190. hit
  191. closable
  192. style="
  193. display: flex;
  194. justify-content: space-between;
  195. align-items: center;
  196. margin: 0 0 5px 0;
  197. "
  198. @close="handleDelete(index)"
  199. >
  200. {{ item.name }}
  201. </el-tag>
  202. </el-popover>
  203. </div>
  204. </div>
  205. <table-dialog
  206. v-model="data"
  207. ref="TableDialogFef"
  208. :type="type"
  209. :title="title"
  210. :multiple="multiple"
  211. :queryParams="queryParams"
  212. @confirm="handleAdd"
  213. ></table-dialog>
  214. </div>
  215. </template>
  216. <style scoped></style>