fc-contacts.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. <template>
  2. <view class="yt-txl-container">
  3. <view class="search">
  4. <input type="text" @input="inputFunc" class="s-input" />
  5. </view>
  6. <view class="toLine"></view>
  7. <view class="scroll" :style="showflag ? '' :'right:60rpx'">
  8. <scroll-view :scroll-into-view="toNameK" scroll-y class="scrollview" :scroll-with-animation="true">
  9. <view :id="item.NameK" v-for="(item,index) in datas" :key="index" class="scrollarea">
  10. <view class="item-unit" :id="item.NameK">
  11. <view class="nameK">{{item.NameK}}</view>
  12. <view v-for="(_item,_index) in item.Items" class="info" :key="_index" @click="checkItem(_item)">
  13. <image :src="_item.HeadImg|headImg" class="headImg" mode="scaleToFill"></image>
  14. <view class="item">
  15. <text class="name">{{_item.name}}</text>
  16. <text class="tel">{{_item.updateTime||_item.createTime}}</text>
  17. <text class="post">{{_item.Desc}}</text>
  18. </view>
  19. <!-- <image @click="calltel(_item.Tel)" src="/static/components/calltel.png" class="itemTel" mode="scaleToFill"></image> -->
  20. </view>
  21. </view>
  22. </view>
  23. </scroll-view>
  24. </view>
  25. <view class="flag" v-if="showflag">
  26. <scroll-view scroll-y="true" :show-scrollbar="false" style="width: 100%;height: 100%;">
  27. <view v-for="(item,index) in kList" :key="item.id"
  28. @click="toFunc(item)"
  29. :class="(item.IsSelect?'flag-active':'')+' flag-key'">
  30. {{item.Name}}
  31. </view>
  32. </scroll-view>
  33. </view>
  34. </view>
  35. </template>
  36. <script>
  37. import { objectIsValid } from '@/libs/components';
  38. let observer = null;
  39. export default {
  40. filters:{
  41. headImg(val){
  42. if(!val){
  43. return "/static/components/avatar.png"
  44. }
  45. }
  46. },
  47. props: {
  48. contacts: {
  49. type: Array,
  50. default: null
  51. },
  52. showflag: {
  53. type: Boolean,
  54. default: true
  55. }
  56. },
  57. data() {
  58. return {
  59. kList: [{ id: 1, Name: 'A', IsSelect: true }, { id: 2, Name: 'B', IsSelect: false }, { id: 3, Name: 'C', IsSelect: false },
  60. { id: 4, Name: 'D', IsSelect: false }, { id: 5, Name: 'E', IsSelect: false }, { id: 6, Name: 'F', IsSelect: false },
  61. { id: 7, Name: 'G', IsSelect: false }, { id: 8, Name: 'H', IsSelect: false }, { id: 9, Name: 'I', IsSelect: false },
  62. { id: 10, Name: 'J', IsSelect: false }, { id: 11, Name: 'K', IsSelect: false }, { id: 12, Name: 'L', IsSelect: false },
  63. { id: 13, Name: 'M', IsSelect: false }, { id: 14, Name: 'N', IsSelect: false }, { id: 15, Name: 'O', IsSelect: false },
  64. { id: 16, Name: 'P', IsSelect: false }, { id: 17, Name: 'Q', IsSelect: false }, { id: 18, Name: 'R', IsSelect: false },
  65. { id: 19, Name: 'S', IsSelect: false }, { id: 20, Name: 'T', IsSelect: false }, { id: 21, Name: 'U', IsSelect: false },
  66. { id: 22, Name: 'V', IsSelect: false }, { id: 23, Name: 'W', IsSelect: false }, { id: 24, Name: 'X', IsSelect: false },
  67. { id: 25, Name: 'Y', IsSelect: false }, { id: 26, Name: 'Z', IsSelect: false }
  68. ],
  69. datas: [],
  70. toNameK: ""
  71. };
  72. },
  73. watch: {
  74. contacts: { //watch监听不到属性解决方案。前端传普通list数据,这里重新重新组织数据结构
  75. immediate: true, // 很重要!!!
  76. deep: true,
  77. handler(newval, oldval) {
  78. var _this = this
  79. if (objectIsValid(newval)) {
  80. var data = newval
  81. _this.kList.forEach(el => {
  82. var filterItems = newval.filter(s => s.NameK == el.Name)
  83. if (objectIsValid(filterItems)) {
  84. _this.datas.push({ NameK: el.Name, Items: filterItems })
  85. }
  86. })
  87. }
  88. }
  89. }
  90. },
  91. methods: {
  92. toFunc(item) { //右侧点击对应关键词滑动到对应的锚点
  93. this.toNameK = item.name;
  94. this.kList.forEach(el => {
  95. if (el.id == item.id) {
  96. el.IsSelect = true;
  97. } else {
  98. el.IsSelect = false;
  99. }
  100. })
  101. },
  102. inputFunc(event) { //搜索功能
  103. var _this = this
  104. var value = event.target.value;
  105. _this.datas = []
  106. _this.kList.forEach(el => {
  107. var filterItems = this.contacts.filter(s => s.NameK == el.Name && (s.name.search(value) != -1 || !objectIsValid(value)))
  108. if (objectIsValid(filterItems)) {
  109. _this.datas.push({ NameK: el.Name, Items: filterItems })
  110. }
  111. })
  112. _this.watchflag()
  113. },
  114. calltel(tel) {
  115. uni.makePhoneCall({
  116. phoneNumber: tel
  117. })
  118. },
  119. checkItem(item){
  120. this.$emit('click',item)
  121. },
  122. watchflag() {
  123. var _this = this //监听锚点与参照区重叠事件
  124. _this.observer = uni.createIntersectionObserver(_this, { observeAll: true })
  125. _this.observer.relativeTo('.toLine', { bottom: 1 }) //参照区
  126. .observe('.scrollarea', (res) => {
  127. if (res.intersectionRatio > 0) {
  128. _this.kList.forEach(el => {
  129. if (el.Name == res.id) {
  130. el.IsSelect = true;
  131. } else {
  132. el.IsSelect = false;
  133. }
  134. })
  135. }
  136. })
  137. }
  138. },
  139. mounted() {
  140. this.watchflag()
  141. },
  142. destroyed() {
  143. this.observer.disconnect()
  144. }
  145. };
  146. </script>
  147. <style lang="scss" scoped>
  148. .yt-txl-container{
  149. width: 100%;
  150. height: calc(100vh - 300upx);
  151. .search {
  152. width: 730upx;
  153. height: 90upx;
  154. display: flex;
  155. justify-content: center;
  156. align-items: center;
  157. padding: 10upx;
  158. .s-input {
  159. width: 680rpx;
  160. height: 70rpx;
  161. border-radius: 40rpx;
  162. background: url('data:image/png;base64,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');
  163. background-position: 20upx center;
  164. background-repeat: no-repeat;
  165. background-color: #fff;
  166. background-size: 50rpx;
  167. text-indent: 60rpx;
  168. padding:0 60rpx;
  169. }
  170. }
  171. .item-unit{
  172. position: relative;
  173. .nameK {
  174. // position: sticky;
  175. // top: 0;
  176. // left: 0;
  177. // background: #f4f4f4;
  178. // background-color: red;
  179. font-size: 36rpx;
  180. text-indent: 10rpx;
  181. z-index: 99998;
  182. padding: 8upx 0;
  183. }
  184. .info {
  185. display: flex;
  186. justify-content: flex-start;
  187. align-items: center;
  188. // margin-bottom: 10upx;
  189. padding: 20upx;
  190. background: #fff;
  191. border-bottom: 1px solid #eaeaea;
  192. .item {
  193. height: 90upx;
  194. display: flex;
  195. font-size: 34rpx;
  196. flex-direction: column;
  197. justify-content: space-around;
  198. .name{
  199. font-size: 36upx;
  200. line-height: 1;
  201. letter-spacing: 2px;
  202. }
  203. .tel{
  204. font-size: 30upx;
  205. line-height: 1;
  206. padding-top: 14upx;
  207. }
  208. }
  209. }
  210. }
  211. .scroll {
  212. position: absolute;
  213. left: 25rpx;
  214. top: 240rpx;
  215. right: 100rpx;
  216. bottom: 25rpx;
  217. }
  218. .scrollview {
  219. width: 100%;
  220. height: 100%;
  221. }
  222. .toLine {
  223. width: 100%;
  224. height: 5rpx;
  225. display: flex;
  226. }
  227. .scrollarea {
  228. margin-top: 5rpx;
  229. }
  230. .headImg {
  231. width: 80rpx;
  232. height: 80rpx;
  233. border-radius: 50%;
  234. margin-right: 15rpx;
  235. display: flex;
  236. }
  237. .flag {
  238. width: 60rpx;
  239. position: absolute;
  240. top: 240rpx;
  241. right: 25rpx;
  242. bottom: 25rpx;
  243. .flag-key {
  244. padding: 0;
  245. margin: 0 auto 20rpx auto;
  246. width: 60rpx;
  247. height: 60rpx;
  248. border-radius: 50%;
  249. color: #000;
  250. display: flex;
  251. justify-content: center;
  252. align-items: center;
  253. font-size: 40rpx;
  254. }
  255. .flag-active {
  256. // background-image: linear-gradient(to right bottom, #FF9002, #FF5100);
  257. background-color: #007AFF;
  258. color: #fff;
  259. }
  260. }
  261. .itemTel {
  262. width: 60rpx;
  263. height: 60rpx;
  264. border-radius: 50%;
  265. position: absolute;
  266. right: 10rpx;
  267. }
  268. }
  269. </style>