index.vue 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  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. }
  87. },
  88. immediate: true,
  89. },
  90. },
  91. methods: {
  92. // 打开弹窗
  93. handleAsyncOpenDialog() {
  94. this.$nextTick(() => {
  95. const { setVisible } = this.$refs.TableDialogFef;
  96. setVisible(true);
  97. });
  98. },
  99. // 新增操作
  100. handleAdd(prop) {
  101. this.data = prop;
  102. this.handleUpdate(this.data);
  103. this.$emit("hide", prop);
  104. },
  105. // 删除操作
  106. handleDelete(prop) {
  107. this.data.splice(prop, 1);
  108. this.handleUpdate(this.data);
  109. },
  110. // 更新操作
  111. handleUpdate(prop) {
  112. const { source, multiple, valueKey, dataMapping } = this.$props;
  113. // 多选
  114. if (multiple) {
  115. this.showValue = "";
  116. this.$emit(
  117. "input",
  118. prop.map((item) => item[valueKey])
  119. );
  120. }
  121. // 单选
  122. if (!multiple) {
  123. this.showValue = prop[0].name;
  124. this.$emit("input", prop[0][valueKey]);
  125. // 更新映射数据
  126. for (let key in dataMapping) {
  127. source[key] = prop[0][dataMapping[key]];
  128. }
  129. this.$emit("update:source", source);
  130. }
  131. //
  132. this.$emit("change", prop, source);
  133. },
  134. },
  135. created() {
  136. this.$nextTick(() => {
  137. const { clientWidth } = this.$refs.PopoverSelect;
  138. this.width = clientWidth;
  139. });
  140. },
  141. mounted() {},
  142. destroyed() {},
  143. };
  144. </script>
  145. <template>
  146. <div ref="PopoverSelect" style="position: relative">
  147. <el-input
  148. v-model="showValue"
  149. :size="size"
  150. :disabled="disabled"
  151. :clearable="clearable"
  152. :placeholder="placeholder"
  153. readonly
  154. style="width: 100%; cursor: pointer"
  155. @click.native.stop="handleAsyncOpenDialog"
  156. >
  157. <template #suffix>
  158. <el-icon class="el-icon-more"></el-icon>
  159. </template>
  160. </el-input>
  161. <div
  162. v-if="multiple && data.length"
  163. style="
  164. position: absolute;
  165. left: 10px;
  166. top: 50%;
  167. transform: translateY(-50%);
  168. padding-right: 30px;
  169. overflow: hidden;
  170. "
  171. >
  172. <div style="width: 150px; display: flex">
  173. <el-popover
  174. :offset="-10"
  175. :width="width"
  176. :visible-arrow="false"
  177. title=""
  178. content=""
  179. trigger="click"
  180. placement="bottom-start"
  181. >
  182. <el-tag slot="reference" :size="size" style="margin-right: 10px">
  183. + {{ data.length }}
  184. </el-tag>
  185. <el-tag
  186. v-for="(item, index) in data"
  187. :size="size"
  188. hit
  189. closable
  190. style="
  191. display: flex;
  192. justify-content: space-between;
  193. align-items: center;
  194. margin: 0 0 5px 0;
  195. "
  196. @close="handleDelete(index)"
  197. >
  198. {{ item.name }}
  199. </el-tag>
  200. </el-popover>
  201. </div>
  202. </div>
  203. <table-dialog
  204. v-model="data"
  205. ref="TableDialogFef"
  206. :type="type"
  207. :title="title"
  208. :multiple="multiple"
  209. :queryParams="queryParams"
  210. @confirm="handleAdd"
  211. ></table-dialog>
  212. </div>
  213. </template>
  214. <style scoped></style>