├── view.PNG
├── im-chat
├── static
│ └── img
│ │ ├── homeHL.png
│ │ └── customerHL.png
├── main.js
├── pages.json
├── App.vue
├── store
│ └── index.js
├── components
│ ├── chatinput.vue
│ └── messageshow.vue
├── manifest.json
├── pages
│ └── index
│ │ └── index.vue
└── common
│ └── icon.css
├── .gitignore
└── README.md
/view.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/felony/uniapp-chat/HEAD/view.PNG
--------------------------------------------------------------------------------
/im-chat/static/img/homeHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/felony/uniapp-chat/HEAD/im-chat/static/img/homeHL.png
--------------------------------------------------------------------------------
/im-chat/static/img/customerHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/felony/uniapp-chat/HEAD/im-chat/static/img/customerHL.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ##############################
2 | ## Folders ##
3 | ##############################
4 | im-chat/unpackage
--------------------------------------------------------------------------------
/im-chat/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | import store from './store'
5 |
6 | Vue.config.productionTip = false
7 |
8 | Vue.prototype.$store = store
9 |
10 | App.mpType = 'app'
11 |
12 | const app = new Vue({
13 | store,
14 | ...App
15 | })
16 | app.$mount()
17 |
--------------------------------------------------------------------------------
/im-chat/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [ //pages数组中第一项表示应用启动页
3 | {
4 | "path": "pages/index/index",
5 | "style": {
6 | "navigationBarTitleText": "im-chat"
7 | }
8 | }
9 | ],
10 | "globalStyle": {
11 | "navigationBarTextStyle": "white",
12 | "navigationBarTitleText": "im-chat",
13 | "navigationBarBackgroundColor": "#1482d1",
14 | "backgroundColor": "#1482d1"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/im-chat/App.vue:
--------------------------------------------------------------------------------
1 |
14 |
15 |
28 |
--------------------------------------------------------------------------------
/im-chat/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 |
4 | Vue.use(Vuex)
5 |
6 | const store = new Vuex.Store({
7 | state: {
8 | user: {
9 | home: {
10 | id: 1,
11 | name: 'tax',
12 | img: 'static/img/homeHL.png'
13 | },
14 | customer: {
15 | id: 2,
16 | name: 'customer',
17 | img: 'static/img/customerHL.png'
18 | }
19 | }
20 | },
21 | updated:function(){
22 | console.log('message update:'+ this.scrollTop);
23 | }
24 | });
25 |
26 | export default store
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # uniapp-chat
2 | 在mui中有chat界面的例子,升级到uni-app后,没有类似的模板,因此模仿写了一个。遇到了一些坑,在此一一记录下来。当然,由于是新手,可能有些坑可以避开。
3 | ---
4 | ## scroll-view高度的设置
5 | 输入内容后,必然要在对话界面的底部显示内容,但是在uni-app下不知道如何能操作DOM来显示和定位,有说需要通过uni.pageScrollTo的方式,但是这个页面刷新的太厉害,输入框都刷新了,没法使用。所以只能使用scroll-view组件。但是通过scroll-view使用竖向滚动时,需要给 一个固定高度。为了适配屏幕的大小,则需要通过计算来确定scroll-view的高度。
6 |
7 | ```
8 |
9 |
10 | ```
11 | > style.contentViewHeight 需要在加载前通过计算获得
12 |
13 | ```
14 | created: function () {
15 | const res = uni.getSystemInfoSync();
16 | this.style.pageHeight = res.windowHeight;
17 | this.style.contentViewHeight = res.windowHeight - uni.getSystemInfoSync().screenWidth / 750 * (100); //像素
18 | }
19 | ```
20 | > 由于给出的是像素高度,所以需要换算一下 res.windowHeight - uni.getSystemInfoSync().screenWidth / 750 * (100); 其中100是底部输入框的像素高度
21 |
22 | ## scroll-top的使用
23 | 每次发送内容后,需要滚动到底部,可以通过把最后一个元素id赋值给scroll-into-view的方式来实现,但是效果也不是很理想,所以采用了scroll-top的方式。
24 | ```
25 | var that = this;
26 | var query = uni.createSelectorQuery();
27 | query.selectAll('.m-item').boundingClientRect();
28 | query.select('#scrollview').boundingClientRect();
29 | query.exec(function (res) {
30 | that.style.mitemHeight = 0;
31 | res[0].forEach(function (rect) {
32 | that.style.mitemHeight = that.style.mitemHeight + rect.height + 20;});
33 |
34 | if (that.style.mitemHeight > that.style.contentViewHeight) {
35 | that.scrollTop = that.style.mitemHeight - that.style.contentViewHeight;
36 | }
37 | });
38 | ```
39 | > 方法就是先获取所有内部子元素的高度,然后用子元素的高度和-显示高度,就得到了scroll-top的滚动位置。
40 |
41 | ## 其他
42 | 1. uni-app的img组件地址有点问题,小程序版本能用绝对地址,但是app版本就需要使用相对路径。
43 | 2. 虽然现在每次发送信息后,能滚动到底部,但是输入的时候,由于弹出键盘,就可能覆盖了,没法看到最后一条信息。
--------------------------------------------------------------------------------
/im-chat/components/chatinput.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
54 |
55 |
103 |
--------------------------------------------------------------------------------
/im-chat/components/messageshow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{message.content}}
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
32 |
33 |
103 |
--------------------------------------------------------------------------------
/im-chat/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "im-chat",
3 | "appid" : "__UNI__39DE137",
4 | "description": "",
5 | "versionName": "1.0.0",
6 | "versionCode": "100",
7 | "app-plus": { /* 5+App特有相关 */
8 | "modules": { /* 模块配置 */
9 |
10 | },
11 | "distribute": { /* 应用发布信息 */
12 | "android": { /* android打包配置 */
13 | "permissions": ["",
14 | "",
15 | "",
16 | "",
17 | "",
18 | "",
19 | "",
20 | "",
21 | "",
22 | "",
23 | "",
24 | "",
25 | "",
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | ""
35 | ]
36 | },
37 | "ios": { /* ios打包配置 */
38 |
39 | },
40 | "sdkConfigs": { /* SDK配置 */
41 |
42 | }
43 | }
44 | },
45 | "quickapp": { /* 快应用特有相关 */
46 |
47 | },
48 | "mp-weixin": { /* 小程序特有相关 */
49 | "appid": ""
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/im-chat/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
92 |
93 |
94 |
95 |
97 |
98 |
99 |
100 |
101 |
104 |
105 |
106 |
107 |
129 |
--------------------------------------------------------------------------------
/im-chat/common/icon.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: uniicons;
3 | font-weight: normal;
4 | font-style: normal;
5 | src: url('https://img-cdn-qiniu.dcloud.net.cn/fonts/uni.ttf') format('truetype');
6 | }
7 |
8 | .uni-icon {
9 | font-family: uniicons;
10 | font-size: 48px;
11 | font-weight: normal;
12 | font-style: normal;
13 | line-height: 1;
14 | display: inline-block;
15 | text-decoration: none;
16 | -webkit-font-smoothing: antialiased;
17 | }
18 |
19 | .uni-icon.uni-active {
20 | color: #007aff;
21 | }
22 |
23 | .uni-icon-contact:before {
24 | content: '\e100';
25 | }
26 |
27 | .uni-icon-person:before {
28 | content: '\e101';
29 | }
30 |
31 | .uni-icon-personadd:before {
32 | content: '\e102';
33 | }
34 |
35 | .uni-icon-contact-filled:before {
36 | content: '\e130';
37 | }
38 |
39 | .uni-icon-person-filled:before {
40 | content: '\e131';
41 | }
42 |
43 | .uni-icon-personadd-filled:before {
44 | content: '\e132';
45 | }
46 |
47 | .uni-icon-phone:before {
48 | content: '\e200';
49 | }
50 |
51 | .uni-icon-email:before {
52 | content: '\e201';
53 | }
54 |
55 | .uni-icon-chatbubble:before {
56 | content: '\e202';
57 | }
58 |
59 | .uni-icon-chatboxes:before {
60 | content: '\e203';
61 | }
62 |
63 | .uni-icon-phone-filled:before {
64 | content: '\e230';
65 | }
66 |
67 | .uni-icon-email-filled:before {
68 | content: '\e231';
69 | }
70 |
71 | .uni-icon-chatbubble-filled:before {
72 | content: '\e232';
73 | }
74 |
75 | .uni-icon-chatboxes-filled:before {
76 | content: '\e233';
77 | }
78 |
79 | .uni-icon-weibo:before {
80 | content: '\e260';
81 | }
82 |
83 | .uni-icon-weixin:before {
84 | content: '\e261';
85 | }
86 |
87 | .uni-icon-pengyouquan:before {
88 | content: '\e262';
89 | }
90 |
91 | .uni-icon-chat:before {
92 | content: '\e263';
93 | }
94 |
95 | .uni-icon-qq:before {
96 | content: '\e264';
97 | }
98 |
99 | .uni-icon-videocam:before {
100 | content: '\e300';
101 | }
102 |
103 | .uni-icon-camera:before {
104 | content: '\e301';
105 | }
106 |
107 | .uni-icon-mic:before {
108 | content: '\e302';
109 | }
110 |
111 | .uni-icon-location:before {
112 | content: '\e303';
113 | }
114 |
115 | .uni-icon-mic-filled:before,
116 | .uni-icon-speech:before {
117 | content: '\e332';
118 | }
119 |
120 | .uni-icon-location-filled:before {
121 | content: '\e333';
122 | }
123 |
124 | .uni-icon-micoff:before {
125 | content: '\e360';
126 | }
127 |
128 | .uni-icon-image:before {
129 | content: '\e363';
130 | }
131 |
132 | .uni-icon-map:before {
133 | content: '\e364';
134 | }
135 |
136 | .uni-icon-compose:before {
137 | content: '\e400';
138 | }
139 |
140 | .uni-icon-trash:before {
141 | content: '\e401';
142 | }
143 |
144 | .uni-icon-upload:before {
145 | content: '\e402';
146 | }
147 |
148 | .uni-icon-download:before {
149 | content: '\e403';
150 | }
151 |
152 | .uni-icon-close:before {
153 | content: '\e404';
154 | }
155 |
156 | .uni-icon-redo:before {
157 | content: '\e405';
158 | }
159 |
160 | .uni-icon-undo:before {
161 | content: '\e406';
162 | }
163 |
164 | .uni-icon-refresh:before {
165 | content: '\e407';
166 | }
167 |
168 | .uni-icon-star:before {
169 | content: '\e408';
170 | }
171 |
172 | .uni-icon-plus:before {
173 | content: '\e409';
174 | }
175 |
176 | .uni-icon-minus:before {
177 | content: '\e410';
178 | }
179 |
180 | .uni-icon-circle:before,
181 | .uni-icon-checkbox:before {
182 | content: '\e411';
183 | }
184 |
185 | .uni-icon-close-filled:before,
186 | .uni-icon-clear:before {
187 | content: '\e434';
188 | }
189 |
190 | .uni-icon-refresh-filled:before {
191 | content: '\e437';
192 | }
193 |
194 | .uni-icon-star-filled:before {
195 | content: '\e438';
196 | }
197 |
198 | .uni-icon-plus-filled:before {
199 | content: '\e439';
200 | }
201 |
202 | .uni-icon-minus-filled:before {
203 | content: '\e440';
204 | }
205 |
206 | .uni-icon-circle-filled:before {
207 | content: '\e441';
208 | }
209 |
210 | .uni-icon-checkbox-filled:before {
211 | content: '\e442';
212 | }
213 |
214 | .uni-icon-closeempty:before {
215 | content: '\e460';
216 | }
217 |
218 | .uni-icon-refreshempty:before {
219 | content: '\e461';
220 | }
221 |
222 | .uni-icon-reload:before {
223 | content: '\e462';
224 | }
225 |
226 | .uni-icon-starhalf:before {
227 | content: '\e463';
228 | }
229 |
230 | .uni-icon-spinner:before {
231 | content: '\e464';
232 | }
233 |
234 | .uni-icon-spinner-cycle:before {
235 | content: '\e465';
236 | }
237 |
238 | .uni-icon-search:before {
239 | content: '\e466';
240 | }
241 |
242 | .uni-icon-plusempty:before {
243 | content: '\e468';
244 | }
245 |
246 | .uni-icon-forward:before {
247 | content: '\e470';
248 | }
249 |
250 | .uni-icon-back:before,
251 | .uni-icon-left-nav:before {
252 | content: '\e471';
253 | }
254 |
255 | .uni-icon-checkmarkempty:before {
256 | content: '\e472';
257 | }
258 |
259 | .uni-icon-home:before {
260 | content: '\e500';
261 | }
262 |
263 | .uni-icon-navigate:before {
264 | content: '\e501';
265 | }
266 |
267 | .uni-icon-gear:before {
268 | content: '\e502';
269 | }
270 |
271 | .uni-icon-paperplane:before {
272 | content: '\e503';
273 | }
274 |
275 | .uni-icon-info:before {
276 | content: '\e504';
277 | }
278 |
279 | .uni-icon-help:before {
280 | content: '\e505';
281 | }
282 |
283 | .uni-icon-locked:before {
284 | content: '\e506';
285 | }
286 |
287 | .uni-icon-more:before {
288 | content: '\e507';
289 | }
290 |
291 | .uni-icon-flag:before {
292 | content: '\e508';
293 | }
294 |
295 | .uni-icon-home-filled:before {
296 | content: '\e530';
297 | }
298 |
299 | .uni-icon-gear-filled:before {
300 | content: '\e532';
301 | }
302 |
303 | .uni-icon-info-filled:before {
304 | content: '\e534';
305 | }
306 |
307 | .uni-icon-help-filled:before {
308 | content: '\e535';
309 | }
310 |
311 | .uni-icon-more-filled:before {
312 | content: '\e537';
313 | }
314 |
315 | .uni-icon-settings:before {
316 | content: '\e560';
317 | }
318 |
319 | .uni-icon-list:before {
320 | content: '\e562';
321 | }
322 |
323 | .uni-icon-bars:before {
324 | content: '\e563';
325 | }
326 |
327 | .uni-icon-loop:before {
328 | content: '\e565';
329 | }
330 |
331 | .uni-icon-paperclip:before {
332 | content: '\e567';
333 | }
334 |
335 | .uni-icon-eye:before {
336 | content: '\e568';
337 | }
338 |
339 | .uni-icon-arrowup:before {
340 | content: '\e580';
341 | }
342 |
343 | .uni-icon-arrowdown:before {
344 | content: '\e581';
345 | }
346 |
347 | .uni-icon-arrowleft:before {
348 | content: '\e582';
349 | }
350 |
351 | .uni-icon-arrowright:before {
352 | content: '\e583';
353 | }
354 |
355 | .uni-icon-arrowthinup:before {
356 | content: '\e584';
357 | }
358 |
359 | .uni-icon-arrowthindown:before {
360 | content: '\e585';
361 | }
362 |
363 | .uni-icon-arrowthinleft:before {
364 | content: '\e586';
365 | }
366 |
367 | .uni-icon-arrowthinright:before {
368 | content: '\e587';
369 | }
370 |
371 | .uni-icon-pulldown:before {
372 | content: '\e588';
373 | }
374 |
--------------------------------------------------------------------------------