select-lay.vue 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. <template>
  2. <view class="uni-select-lay" :style="{ 'z-index': zindex }">
  3. <input type="text" :name="name" v-model="value" class="uni-select-input" readonly />
  4. <view class="uni-select-lay-select" :class="{ active: active }">
  5. <!-- 禁用mask -->
  6. <view class="uni-disabled" v-if="disabled"></view>
  7. <!-- 禁用mask -->
  8. <!-- 清空 -->
  9. <view class="uni-select-lay-input-close" v-if="changevalue != '' && this.active"><text @click.stop="removevalue"></text></view>
  10. <!-- 清空 -->
  11. <input
  12. type="text"
  13. readonly
  14. disabled="true"
  15. class="uni-select-lay-input"
  16. :class="{ active: changevalue != '' && changevalue != placeholder }"
  17. v-model="changevalue"
  18. :placeholder="placeholder"
  19. @focus="unifocus"
  20. @input="intchange"
  21. @blur="uniblur"
  22. @click.stop="select"
  23. />
  24. <view class="uni-select-lay-icon" :class="{ disabled: disabled }" @click.stop="select"><text></text></view>
  25. </view>
  26. <view class="uni-date-mask" v-show="active" @click.stop="select"></view>
  27. <scroll-view class="uni-select-lay-options" :scroll-y="true" v-show="active" @scroll="selectmove" @touchstart="movetouch">
  28. <template v-if="!changes">
  29. <view class="uni-select-lay-item" v-if="showplaceholder" :class="{ active: value == '' }" @click.stop="selectitem(-1, null)">{{ placeholder }}</view>
  30. <view
  31. class="uni-select-lay-item"
  32. :class="{ active: value == item[svalue], disabled: item.disabled }"
  33. v-for="(item, index) in options"
  34. :key="index"
  35. @click.stop="selectitem(index, item)"
  36. >
  37. {{ item[slabel] }}
  38. </view>
  39. </template>
  40. <!-- 搜索 -->
  41. <template v-else>
  42. <template v-if="vlist.length > 0">
  43. <view class="uni-select-lay-item" :class="{ active: value == item[svalue] }" v-for="(item, index) in vlist" :key="index" @click.stop="selectitem(index, item)">
  44. {{ item[slabel] }}
  45. </view>
  46. </template>
  47. <template v-else>
  48. <view class="nosearch">{{ changesValue }}</view>
  49. </template>
  50. </template>
  51. </scroll-view>
  52. </view>
  53. </template>
  54. <script>
  55. export default {
  56. name: 'select-lay',
  57. props: {
  58. disabled: {
  59. type: Boolean,
  60. default: false
  61. },
  62. zindex: {
  63. type: Number,
  64. default: 999
  65. },
  66. options: {
  67. type: Array,
  68. default() {
  69. return [];
  70. }
  71. },
  72. name: {
  73. type: String,
  74. default: ''
  75. },
  76. value: {
  77. type: String,
  78. default: ''
  79. },
  80. placeholder: {
  81. type: String,
  82. default: '请选择'
  83. },
  84. showplaceholder: {
  85. type: Boolean,
  86. default: true
  87. },
  88. slabel: {
  89. type: String,
  90. default: 'label'
  91. },
  92. svalue: {
  93. type: String,
  94. default: 'value'
  95. }
  96. },
  97. data() {
  98. return {
  99. active: false, //组件是否激活,
  100. isfocus: false, //是否有焦点
  101. isremove: false, //是否是因为点击清空才导致的失去焦点
  102. ismove: false, //是否是因为移动才失去焦点
  103. changevalue: '', //搜索框同步
  104. oldvalue: '', //数据回滚
  105. changes: false, //正在搜索
  106. changesValue: '',
  107. vlist: [], //搜索框查询的列表
  108. settimer: null //value改变定时器
  109. };
  110. },
  111. mounted() {
  112. this.itemcheck();
  113. },
  114. watch: {
  115. //value改变
  116. value() {
  117. this.itemcheck();
  118. },
  119. //初始化数组
  120. options() {
  121. // 此处判断是否有初始value,存在则判断显示文字
  122. this.itemcheck();
  123. }
  124. },
  125. methods: {
  126. //判断数组跟当前active值
  127. itemcheck() {
  128. // 此处判断是否有初始value,存在则判断显示文字
  129. if (this.value != '') {
  130. // 展示plachhoder
  131. //判断数组
  132. if (this.options.length > 0) {
  133. this.options.forEach(item => {
  134. if (this.value == item[this.svalue]) {
  135. this.oldvalue = this.changevalue = item[this.slabel];
  136. return;
  137. }
  138. });
  139. }
  140. } else {
  141. this.oldvalue = this.changevalue = '';
  142. }
  143. },
  144. //点击组件
  145. select() {
  146. if (this.disabled) return;
  147. this.active = !this.active;
  148. if (this.active) {
  149. this.changes = false;
  150. } else {
  151. this.changevalue = this.oldvalue;
  152. }
  153. },
  154. // 获得焦点
  155. unifocus() {
  156. if (this.disabled) return;
  157. this.active = true;
  158. this.changes = false;
  159. this.isfocus = true;
  160. },
  161. // 失去焦点
  162. uniblur() {
  163. this.isfocus = false;
  164. // bug 点击组件列会先触发失去焦点,此时组件列事件不执行
  165. setTimeout(() => {
  166. if (this.isremove || this.ismove) {
  167. this.isremove = false;
  168. this.ismove = false;
  169. } else {
  170. this.changevalue = this.oldvalue;
  171. this.isremove = false;
  172. this.active = false;
  173. }
  174. }, 153);
  175. },
  176. movetouch() {
  177. setTimeout(() => {
  178. if (this.isfocus) {
  179. this.ismove = false;
  180. return;
  181. }
  182. if (!this.ismove) this.ismove = true;
  183. }, 100);
  184. // this.changes = false;
  185. },
  186. selectmove() {
  187. setTimeout(() => {
  188. if (this.isfocus) {
  189. this.ismove = false;
  190. return;
  191. }
  192. if (!this.ismove) this.ismove = true;
  193. }, 100);
  194. // this.changes = false;
  195. },
  196. //移除数据
  197. removevalue() {
  198. this.isremove = true;
  199. this.changes = false;
  200. this.changevalue = '';
  201. },
  202. //value 改变
  203. intchange() {
  204. if (this.changevalue == '') {
  205. this.changes = false;
  206. return;
  207. }
  208. if (this.oldvalue == this.changevalue) {
  209. return;
  210. }
  211. this.vlist = [];
  212. this.changes = true;
  213. this.changesValue = '正在搜索...';
  214. if (this.settimer) {
  215. clearTimeout(this.settimer);
  216. }
  217. this.settimer = setTimeout(() => {
  218. this.vlist = this.options.filter(item => {
  219. return item[this.slabel].includes(this.changevalue);
  220. });
  221. if (this.vlist.length === 0) {
  222. this.changesValue = '暂无匹配内容!';
  223. }
  224. }, 600);
  225. },
  226. //点击组件列
  227. selectitem(index, item) {
  228. if (item && item.disabled) {
  229. return false;
  230. }
  231. this.changevalue = this.oldvalue;
  232. this.active = false;
  233. this.$emit('selectitem', index, item);
  234. }
  235. }
  236. };
  237. </script>
  238. <style lang="scss" scoped>
  239. .uni-select-lay {
  240. position: relative;
  241. z-index: 999;
  242. box-sizing: border-box;
  243. .uni-select-input {
  244. opacity: 0;
  245. position: absolute;
  246. z-index: -111;
  247. }
  248. // select部分
  249. .uni-select-lay-select {
  250. user-select: none;
  251. position: relative;
  252. z-index: 3;
  253. height: 0.32rem;
  254. padding: 0 0.3rem 0 0.1rem;
  255. box-sizing: border-box;
  256. border-radius: 0.02rem;
  257. border: 0.01rem solid rgb(229, 229, 229);
  258. display: flex;
  259. align-items: center;
  260. font-size: 0.14rem;
  261. color: #999;
  262. box-sizing: border-box;
  263. .uni-disabled {
  264. position: absolute;
  265. left: 0;
  266. width: 100%;
  267. height: 100%;
  268. z-index: 19;
  269. cursor: no-drop;
  270. background: rgba(255, 255, 255, 0.5);
  271. }
  272. // input 框的清除按钮
  273. .uni-select-lay-input-close {
  274. position: absolute;
  275. right: 0.35rem;
  276. top: 0;
  277. height: 100%;
  278. width: 0.15rem;
  279. display: flex;
  280. align-items: center;
  281. justify-content: center;
  282. z-index: 3;
  283. cursor: pointer;
  284. text {
  285. position: relative;
  286. background: #fff;
  287. width: 0.13rem;
  288. height: 0.13rem;
  289. border-radius: 50%;
  290. border: 0.01rem solid #bbb;
  291. &::before,
  292. &::after {
  293. content: '';
  294. position: absolute;
  295. left: 20%;
  296. top: 50%;
  297. height: 0.01rem;
  298. width: 60%;
  299. transform: rotate(45deg);
  300. background-color: #bbb;
  301. }
  302. &::after {
  303. transform: rotate(-45deg);
  304. }
  305. }
  306. }
  307. .uni-select-lay-input {
  308. font-size: 0.14rem;
  309. color: #999;
  310. display: block;
  311. width: 98%;
  312. overflow: hidden;
  313. text-overflow: ellipsis;
  314. white-space: nowrap;
  315. line-height: 0.3rem;
  316. box-sizing: border-box;
  317. &.active {
  318. color: #333;
  319. }
  320. }
  321. .uni-select-lay-icon {
  322. cursor: pointer;
  323. position: absolute;
  324. right: 0;
  325. top: 0;
  326. height: 100%;
  327. width: 0.3rem;
  328. display: flex;
  329. align-items: center;
  330. justify-content: center;
  331. &::before {
  332. content: '';
  333. width: 0.01rem;
  334. height: 100%;
  335. position: absolute;
  336. left: 0;
  337. top: 0;
  338. background-color: #e5e5e5;
  339. }
  340. text {
  341. display: block;
  342. width: 0;
  343. height: 0;
  344. border-width: 0.07rem 0.07rem 0;
  345. border-style: solid;
  346. border-color: #bbb transparent transparent;
  347. transition: 0.3s;
  348. }
  349. &.disabled {
  350. cursor: no-drop;
  351. text {
  352. width: 0.2rem;
  353. height: 0.2rem;
  354. border: 0.02rem solid #ff0000;
  355. border-radius: 50%;
  356. transition: 0.3s;
  357. position: relative;
  358. z-index: 999;
  359. &::after {
  360. content: '';
  361. position: absolute;
  362. top: 50%;
  363. left: 0;
  364. width: 100%;
  365. height: 0.02rem;
  366. margin-top: -0.01rem;
  367. background-color: #ff0000;
  368. transform: rotate(45deg);
  369. }
  370. }
  371. }
  372. }
  373. &.active .uni-select-lay-icon {
  374. text {
  375. transform: rotate(180deg);
  376. }
  377. }
  378. }
  379. // options部分
  380. .uni-select-lay-options {
  381. user-select: none;
  382. position: absolute;
  383. top: calc(100% + 0.05rem);
  384. left: 0;
  385. width: 100%;
  386. // height: 500rpx;
  387. max-height: 2.5rem;
  388. // overflow-y: auto;
  389. border-radius: 0.02rem;
  390. border: 1px solid rgb(229, 229, 229);
  391. background: #fff;
  392. padding: 0.05rem 0;
  393. box-sizing: border-box;
  394. z-index: 9;
  395. .uni-select-lay-item {
  396. padding: 0 0.1rem;
  397. box-sizing: border-box;
  398. cursor: pointer;
  399. line-height: 2.5;
  400. transition: 0.3s;
  401. font-size: 0.14rem;
  402. &.active {
  403. background: $primary-color;
  404. color: #fff;
  405. &:hover {
  406. background: $primary-color;
  407. color: #fff;
  408. }
  409. }
  410. &.disabled {
  411. color: #999;
  412. cursor: not-allowed;
  413. }
  414. &:hover {
  415. background-color: #f5f5f5;
  416. }
  417. }
  418. .nosearch {
  419. font-size: 0.16rem;
  420. line-height: 3;
  421. text-align: center;
  422. color: #666;
  423. }
  424. }
  425. }
  426. .uni-date-mask {
  427. position: fixed;
  428. bottom: 0rem;
  429. top: 0rem;
  430. left: 0rem;
  431. right: 0rem;
  432. background-color: rgba(0, 0, 0, 0);
  433. transition-duration: 0.3s;
  434. z-index: 8;
  435. }
  436. </style>