anji-select.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <!--element下拉列表封装
  2. 使用:
  3. <hyb-select ref="hybSelect"
  4. url="/v1/orgs"
  5. v-model="search.orgId" option="orgId" label="orgName"
  6. placeholder multiple localOptions></hyb-select>
  7. 1.url:要显示的下拉列表数据来源
  8. 规范:返回的数据要符合(JSON中对应的字段必须:label和value,不能其他)
  9. [
  10. {label:'显示1',value:'值1'},
  11. {label:'显示2',value:'值2'},
  12. {label:'显示3',value:'值3'},
  13. ...
  14. ]
  15. 2.v-model数据的双向绑定,绑定父组件中的字段
  16. 3.option自定义select的value对应的字段
  17. 4.label自定义select的label对应的字段
  18. 5.placeholder下拉列表提示
  19. 6.multiple是否是多选
  20. 7.localOptions使用本地的数据,不用请求远程数据,注意:使用该属性时Url属性不能要,不然无效
  21. -->
  22. <template>
  23. <div>
  24. <el-select
  25. v-model="selectValue"
  26. :clearable="clearable"
  27. :allow-create="!!allowCreate"
  28. :default-first-option="!!allowCreate"
  29. :collapse-tags="collapseTags"
  30. filterable
  31. class="filter-item"
  32. :placeholder="placeholder"
  33. :disabled="disabled"
  34. :multiple="multiple == null ? false : true"
  35. :remote="remoteFilter"
  36. :remote-method="remoteQuery"
  37. @change="change"
  38. >
  39. <el-option
  40. v-for="(item, index) in options"
  41. :key="index"
  42. :label="getItemLabel(item, label)"
  43. :value="item[option] + ''"
  44. :disabled="isDisabledOption(item)"
  45. >
  46. <template v-if="mergeLabel">
  47. <span style="float: left">{{ getItemLabel(item, label) }}</span>
  48. <span
  49. v-if="!dictCode && !localOptions"
  50. style="float: right; color: #8492a6; font-size: 13px"
  51. >{{ item[option] }}</span
  52. >
  53. </template>
  54. </el-option>
  55. <el-option v-if="totalPage >= 1" value="" disabled
  56. >输入关键词搜索更多</el-option
  57. >
  58. </el-select>
  59. </div>
  60. </template>
  61. <script>
  62. import request from "@/utils/request";
  63. export default {
  64. props: {
  65. dictCode: null, // 当传入dictCode时,可以不用传递url
  66. url: null,
  67. allowCreate: {
  68. type: Boolean,
  69. default: false
  70. },
  71. method: {
  72. type: String,
  73. default: "get"
  74. },
  75. queryParam: {
  76. type: Object,
  77. default: () => {
  78. return {};
  79. }
  80. },
  81. value: null,
  82. placeholder: null,
  83. label: {
  84. type: String,
  85. default: "text"
  86. },
  87. option: {
  88. type: String,
  89. default: "id"
  90. },
  91. multiple: null,
  92. localOptions: null,
  93. disabled: null,
  94. clearable: {
  95. type: Boolean,
  96. default: true
  97. },
  98. collapseTags: {
  99. type: Boolean,
  100. default: false
  101. },
  102. mergeLabel: {
  103. type: Boolean,
  104. default: true
  105. },
  106. // 禁用的下拉选项
  107. disabledOptions: {
  108. type: String,
  109. default: () => {
  110. return "";
  111. }
  112. },
  113. // 使用远程搜索
  114. remoteFilter: {
  115. type: Boolean,
  116. default: false
  117. }
  118. },
  119. data() {
  120. return {
  121. options: null,
  122. selectValue: null,
  123. // 如果是分页的,
  124. totalPage: 0
  125. };
  126. },
  127. computed: {
  128. // 根据dictCode和url拼出最终的请求url
  129. requestUrl() {
  130. if (this.localOptions) {
  131. return null;
  132. }
  133. if (this.url != null && this.url.trim() != "") {
  134. if (this.url.indexOf("?") > 0) {
  135. if (this.option == null) {
  136. // console.log('url-' + this.url.substring(this.url.indexOf('?')))
  137. }
  138. if (this.label == null) {
  139. }
  140. }
  141. return this.url;
  142. }
  143. if (this.dictCode != null && this.dictCode.trim() != "") {
  144. return `/meta/gaeaDict/select/${this.dictCode}`;
  145. }
  146. return null;
  147. }
  148. },
  149. watch: {
  150. dictCode(val) {
  151. if (val) {
  152. this.queryData();
  153. }
  154. },
  155. // 监听接口地址变化时,触发刷新请求
  156. localOptions(val) {
  157. this.options = val;
  158. },
  159. value: {
  160. handler(val) {
  161. if (
  162. typeof val == "string" &&
  163. this.url != null &&
  164. this.url.trim() != ""
  165. ) {
  166. this.remoteQuery(val);
  167. }
  168. if (this.multiple != null) {
  169. if (!this.value) {
  170. this.selectValue = [];
  171. } else {
  172. this.selectValue = this.value;
  173. }
  174. } else {
  175. if (this.value != null && this.value != undefined) {
  176. this.selectValue = this.value + "";
  177. } else {
  178. this.selectValue = "";
  179. }
  180. }
  181. },
  182. immediate: true
  183. },
  184. url() {
  185. setTimeout(() => {
  186. this.queryData();
  187. }, 500);
  188. }
  189. },
  190. created() {
  191. if (this.multiple != null) {
  192. this.selectValue = this.value;
  193. } else {
  194. if (this.value != null) {
  195. this.selectValue = this.value + "";
  196. }
  197. }
  198. },
  199. mounted() {
  200. if (this.requestUrl == null) {
  201. this.options = this.localOptions;
  202. return;
  203. }
  204. this.queryData();
  205. },
  206. methods: {
  207. // 判断选择是否已经禁用
  208. isDisabledOption(option) {
  209. if (
  210. option == null ||
  211. this.disabledOptions == null ||
  212. this.disabledOptions.length == 0
  213. ) {
  214. return false;
  215. }
  216. let currentOptionVal = option[this.option];
  217. return this.disabledOptions.indexOf(currentOptionVal) >= 0;
  218. },
  219. change(value) {
  220. if (value === "") {
  221. value = "";
  222. }
  223. this.$emit("input", value);
  224. // 根据当前值,找出对应的选项
  225. let optionItem = this.options.find(item => item[this.option] == value);
  226. this.$emit("change", value, optionItem);
  227. },
  228. // 根据用户配置的label,生成对应的标签
  229. getItemLabel(item, label) {
  230. if (label.indexOf("${") < 0 && label.indexOf("}" < 0)) {
  231. return item[label];
  232. }
  233. let reg = /\$\{[a-zA-Z0-9]*\}/g;
  234. let list = label.match(reg);
  235. // ["${id}", "${text}"]
  236. let result = label;
  237. for (let i = 0; i < list.length; i++) {
  238. let sub = list[i];
  239. let key = sub.replace("${", "").replace("}", "");
  240. result = result.replace(sub, item[key]);
  241. }
  242. return result;
  243. },
  244. // 从本地localStorage取 gaeaDict
  245. getOptionsFromLocalStorage() {
  246. let dicts = JSON.parse(localStorage.getItem("gaeaDict"));
  247. let options = [];
  248. if (!dicts.hasOwnProperty(this.dictCode)) {
  249. return [];
  250. }
  251. let dictItems = dicts[this.dictCode];
  252. for (let i = 0; i < dictItems.length; i++) {
  253. let dictItem = dictItems[i];
  254. options.push({
  255. id: dictItem.id.toString(),
  256. text: dictItem.text,
  257. extend: dictItem.extend
  258. });
  259. }
  260. return options;
  261. },
  262. queryData() {
  263. // 所有从本地localStorage取,因为在App.vue中已经请求远程保存到本地了
  264. let options = this.getOptionsFromLocalStorage();
  265. if (this.isNotBlank(options)) {
  266. this.options = options;
  267. return;
  268. }
  269. // 本地localStorage取不到,再从远程接口取
  270. if (this.requestUrl == null) {
  271. return;
  272. }
  273. if (
  274. this.method != null &&
  275. this.method.toLocaleLowerCase().trim() == "post"
  276. ) {
  277. this.queryDataByPost();
  278. } else {
  279. this.queryDataByGet();
  280. }
  281. },
  282. queryDataByGet(keyword) {
  283. let param = this.deepClone(this.queryParam);
  284. if (this.isNotBlank(keyword)) {
  285. param["keyword"] = keyword;
  286. }
  287. param["multiple"] = this.multiple == null ? null : 1;
  288. request({
  289. url: this.requestUrl,
  290. headers: { noPrompt: true },
  291. params: param
  292. }).then(response => {
  293. // console.log(response)
  294. this.setOptions(response.data);
  295. });
  296. },
  297. queryDataByPost(keyword) {
  298. let param = this.deepClone(this.queryParam);
  299. if (this.isNotBlank(keyword)) {
  300. param["keyword"] = keyword;
  301. }
  302. request({
  303. url: this.requestUrl,
  304. method: "post",
  305. headers: { noPrompt: true },
  306. data: param
  307. }).then(response => {
  308. this.setOptions(response.data);
  309. });
  310. },
  311. setOptions(resData) {
  312. if (resData == null || resData.length == 0) {
  313. this.options = [];
  314. this.totalPage = 0;
  315. return;
  316. }
  317. if (this.isArray(resData)) {
  318. this.options = resData;
  319. this.totalPage = 1;
  320. return;
  321. }
  322. if (
  323. resData.records == null ||
  324. resData.total == null ||
  325. resData.pages == null
  326. ) {
  327. this.options = [];
  328. return;
  329. }
  330. this.totalPage = resData.pages;
  331. // resData.records
  332. // resData.total
  333. // resData.size
  334. // resData.current
  335. this.options = resData.records;
  336. },
  337. remoteQuery(keyword) {
  338. // if (this.isBlank(keyword)) {
  339. // return
  340. // }
  341. setTimeout(() => {
  342. if (
  343. this.method != null &&
  344. this.method.toLocaleLowerCase().trim() == "post"
  345. ) {
  346. this.queryDataByPost(keyword);
  347. } else {
  348. this.queryDataByGet(keyword);
  349. }
  350. }, 200);
  351. }
  352. }
  353. };
  354. </script>
  355. <style lang="scss" scoped>
  356. .el-select {
  357. width: 100%;
  358. }
  359. .el-select-dropdown__item.selected {
  360. text-align: center;
  361. }
  362. .el-select-dropdown__item.is-disabled {
  363. text-align: center;
  364. }
  365. </style>