├── .gitignore
├── .hbuilderx
└── launch.json
├── App.vue
├── README.md
├── api
├── index.js
├── login.js
└── mock.js
├── common
├── common.scss
├── font
│ ├── demo.css
│ ├── demo_index.html
│ ├── iconfont.css
│ ├── iconfont.eot
│ ├── iconfont.js
│ ├── iconfont.json
│ ├── iconfont.svg
│ ├── iconfont.ttf
│ ├── iconfont.woff
│ └── iconfont.woff2
├── home.scss
├── index.scss
├── login.scss
└── password.scss
├── components
├── neil-modal
│ ├── neil-modal.vue
│ └── readme.md
├── uni-countdown
│ └── uni-countdown.vue
└── uni-popup
│ └── uni-popup.vue
├── lang
├── en.js
├── index.js
├── vue-i18n.js
└── zh.js
├── main.js
├── manifest.json
├── package-lock.json
├── package.json
├── pages.json
├── pages
└── login
│ └── login.vue
├── static
├── 1.gif
├── images
│ ├── bg_circle.png
│ ├── bg_login.png
│ ├── big_btn.png
│ ├── bottom_btn.png
│ ├── bottom_btn_tap.png
│ ├── circel_btn.png
│ ├── circel_btn_tap.png
│ ├── left-tap.png
│ ├── left.png
│ ├── left_btn.png
│ ├── left_btn_tap.png
│ ├── logo
│ │ ├── LOGO.png
│ │ └── LOGO1.png
│ ├── right-tap.png
│ ├── right.png
│ ├── right_btn.png
│ ├── right_btn_tap.png
│ ├── tap.png
│ ├── top_btn.png
│ └── top_btn_tap.png
└── neil-modal
│ └── logo.png
├── uni.scss
└── utils
├── request
├── index.js
├── readme.md
└── request.js
├── service.js
├── socket
└── BLEConn.js
└── storageTypes.js
/.gitignore:
--------------------------------------------------------------------------------
1 | unpackage
2 | .idea
3 | node_modules
--------------------------------------------------------------------------------
/.hbuilderx/launch.json:
--------------------------------------------------------------------------------
1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
3 | "version": "0.0",
4 | "configurations": [{
5 | "default" :
6 | {
7 | "launchtype" : "local"
8 | },
9 | "mp-weixin" :
10 | {
11 | "launchtype" : "local"
12 | },
13 | "type" : "uniCloud"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/App.vue:
--------------------------------------------------------------------------------
1 |
16 |
17 |
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
9 | # 启动
10 |
11 | 把项目下载后放到Hbuilder中 用微信小程序预览即可打开
12 |
13 | # 预览
14 |
15 |
16 | 
17 |
18 |
19 | # 项目介绍
20 |
21 | > 本项目是由uniapp开发的微信小程序连接低功耗蓝牙
22 | >
23 | > 主要功能有搜索低功耗蓝牙,连接低功耗蓝牙,给蓝牙发送命令,接收蓝牙回复的命令
24 |
25 | # 功能介绍以及使用说明
26 |
27 | > 用到的文件有`pages/login/login`, `utils/socket/BLEConn.js`两个文件
28 | >
29 | > `utils/socket/BLEConn.js`对蓝牙操作的封装(包含蓝牙列表搜索,蓝牙连接,蓝牙断开,蓝牙分包发送命令,蓝牙分包接收命令)
30 | >
31 | > `pages/login/login`对蓝牙的连接操作
32 |
33 | ## BLEConn.js方法说明
34 | > getBlooth 搜索蓝牙列表
35 | > createBLE 连接蓝牙
36 | > getCharacteristics 获取某个服务的特征值这里做了判断 有notify功能才会返回正确 可根据项目需要修改返回状态
37 | > writeBLE 写入命令-即给蓝牙发送命令(项目中是分包操作),如果不需要分包可参考hexToArrayBuffer方法转换命令
38 | > watchNotify 如果你项目里面有notify功能 这里会主动收到消息
39 |
40 | ## `pages/login/login`业务逻辑
41 |
42 | 1. 点击按钮调用BLEConn封装的方法进行搜索蓝牙
43 | 2. 搜索到蓝牙之后点击需要连接的蓝牙进行连接
44 | 3. 连接时调用createBLE方法 ,该方法会传入两个参数,一个是需要连接的蓝牙对象,另一个是发送命令时候的服务的uuid(此uuid可以问给蓝牙协议的人)
45 | 4. 连接不上会进行三次重连操作 ,三次都没有连接上提示连接失败
46 |
47 | # `utils/socket/BLEConn.js`业务逻辑
48 |
49 | 1. 初始化蓝牙
50 | 2. 搜索蓝牙列表,将搜索出的name值为空或者’未知设备‘并且包含有localName的蓝牙设备的name重新命名为localName的值,将搜索出来的deviceId重复的只保存一个
51 |
52 | # 分包接收访问下边的链接
53 |
54 | https://juejin.im/post/5e1b0bf25188252c4f2ba2bd
55 |
56 | # 分包发送访问下边的链接
57 |
58 | https://juejin.im/post/5e1b0c85e51d45588849581f
59 |
--------------------------------------------------------------------------------
/api/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/api/index.js
--------------------------------------------------------------------------------
/api/login.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/api/login.js
--------------------------------------------------------------------------------
/api/mock.js:
--------------------------------------------------------------------------------
1 | export default data = {
2 |
3 | }
--------------------------------------------------------------------------------
/common/common.scss:
--------------------------------------------------------------------------------
1 | page {
2 | font-size: 30rpx;
3 | font-family: '思源黑体', 'Segoe UI', 'Lucida Grande', Helvetica, Arial, 'Microsoft YaHei';
4 | color: #3a3a3a;
5 | width: 100vw;
6 | height: 100vh;
7 | background: #1b294b;
8 | }
9 |
10 | page,
11 | view,
12 | text,
13 | image,
14 | scroll-view,
15 | swiper,
16 | swiper-item {
17 | box-sizing: border-box;
18 | }
19 |
20 | .am-clear-native-style {
21 | /* 清除原生默认样式 */
22 | background: transparent;
23 | border: none;
24 | margin: 0;
25 | padding: 0;
26 | &:after {
27 | border: none;
28 | }
29 | }
30 | // 超出出现省略号 param {Number} $value - 行数,默认2
31 | @mixin amOverLineClamp($value: 2) {
32 | text-overflow: -o-ellipsis-lastline;
33 | overflow: hidden;
34 | text-overflow: ellipsis;
35 | display: -webkit-box;
36 | -webkit-line-clamp: $value;
37 | line-clamp: $value;
38 | -webkit-box-orient: vertical;
39 | }
40 | // 超出两行出现省略号
41 | .am-line-clamp-two {
42 | @include amOverLineClamp();
43 | }
44 |
45 | .am-text-eill {
46 | /*超出省略号*/
47 | overflow: hidden;
48 | text-overflow: ellipsis;
49 | white-space: nowrap;
50 | }
51 |
52 | .am-text-break {
53 | /*换行*/
54 | white-space: normal;
55 | word-break: break-all;
56 | }
57 |
58 | .am-align-center {
59 | /*文本居中*/
60 | text-align: center;
61 | }
62 |
63 | .am-block {
64 | display: block;
65 | }
66 |
67 | .am-inline {
68 | display: inline;
69 | }
70 |
71 | .am-inline-block {
72 | display: inline-block;
73 | }
--------------------------------------------------------------------------------
/common/font/demo.css:
--------------------------------------------------------------------------------
1 | /* Logo 字体 */
2 | @font-face {
3 | font-family: "iconfont logo";
4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
9 | }
10 |
11 | .logo {
12 | font-family: "iconfont logo";
13 | font-size: 160px;
14 | font-style: normal;
15 | -webkit-font-smoothing: antialiased;
16 | -moz-osx-font-smoothing: grayscale;
17 | }
18 |
19 | /* tabs */
20 | .nav-tabs {
21 | position: relative;
22 | }
23 |
24 | .nav-tabs .nav-more {
25 | position: absolute;
26 | right: 0;
27 | bottom: 0;
28 | height: 42px;
29 | line-height: 42px;
30 | color: #666;
31 | }
32 |
33 | #tabs {
34 | border-bottom: 1px solid #eee;
35 | }
36 |
37 | #tabs li {
38 | cursor: pointer;
39 | width: 100px;
40 | height: 40px;
41 | line-height: 40px;
42 | text-align: center;
43 | font-size: 16px;
44 | border-bottom: 2px solid transparent;
45 | position: relative;
46 | z-index: 1;
47 | margin-bottom: -1px;
48 | color: #666;
49 | }
50 |
51 |
52 | #tabs .active {
53 | border-bottom-color: #f00;
54 | color: #222;
55 | }
56 |
57 | .tab-container .content {
58 | display: none;
59 | }
60 |
61 | /* 页面布局 */
62 | .main {
63 | padding: 30px 100px;
64 | width: 960px;
65 | margin: 0 auto;
66 | }
67 |
68 | .main .logo {
69 | color: #333;
70 | text-align: left;
71 | margin-bottom: 30px;
72 | line-height: 1;
73 | height: 110px;
74 | margin-top: -50px;
75 | overflow: hidden;
76 | *zoom: 1;
77 | }
78 |
79 | .main .logo a {
80 | font-size: 160px;
81 | color: #333;
82 | }
83 |
84 | .helps {
85 | margin-top: 40px;
86 | }
87 |
88 | .helps pre {
89 | padding: 20px;
90 | margin: 10px 0;
91 | border: solid 1px #e7e1cd;
92 | background-color: #fffdef;
93 | overflow: auto;
94 | }
95 |
96 | .icon_lists {
97 | width: 100% !important;
98 | overflow: hidden;
99 | *zoom: 1;
100 | }
101 |
102 | .icon_lists li {
103 | width: 100px;
104 | margin-bottom: 10px;
105 | margin-right: 20px;
106 | text-align: center;
107 | list-style: none !important;
108 | cursor: default;
109 | }
110 |
111 | .icon_lists li .code-name {
112 | line-height: 1.2;
113 | }
114 |
115 | .icon_lists .icon {
116 | display: block;
117 | height: 100px;
118 | line-height: 100px;
119 | font-size: 42px;
120 | margin: 10px auto;
121 | color: #333;
122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear;
123 | -moz-transition: font-size 0.25s linear, width 0.25s linear;
124 | transition: font-size 0.25s linear, width 0.25s linear;
125 | }
126 |
127 | .icon_lists .icon:hover {
128 | font-size: 100px;
129 | }
130 |
131 | .icon_lists .svg-icon {
132 | /* 通过设置 font-size 来改变图标大小 */
133 | width: 1em;
134 | /* 图标和文字相邻时,垂直对齐 */
135 | vertical-align: -0.15em;
136 | /* 通过设置 color 来改变 SVG 的颜色/fill */
137 | fill: currentColor;
138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
139 | normalize.css 中也包含这行 */
140 | overflow: hidden;
141 | }
142 |
143 | .icon_lists li .name,
144 | .icon_lists li .code-name {
145 | color: #666;
146 | }
147 |
148 | /* markdown 样式 */
149 | .markdown {
150 | color: #666;
151 | font-size: 14px;
152 | line-height: 1.8;
153 | }
154 |
155 | .highlight {
156 | line-height: 1.5;
157 | }
158 |
159 | .markdown img {
160 | vertical-align: middle;
161 | max-width: 100%;
162 | }
163 |
164 | .markdown h1 {
165 | color: #404040;
166 | font-weight: 500;
167 | line-height: 40px;
168 | margin-bottom: 24px;
169 | }
170 |
171 | .markdown h2,
172 | .markdown h3,
173 | .markdown h4,
174 | .markdown h5,
175 | .markdown h6 {
176 | color: #404040;
177 | margin: 1.6em 0 0.6em 0;
178 | font-weight: 500;
179 | clear: both;
180 | }
181 |
182 | .markdown h1 {
183 | font-size: 28px;
184 | }
185 |
186 | .markdown h2 {
187 | font-size: 22px;
188 | }
189 |
190 | .markdown h3 {
191 | font-size: 16px;
192 | }
193 |
194 | .markdown h4 {
195 | font-size: 14px;
196 | }
197 |
198 | .markdown h5 {
199 | font-size: 12px;
200 | }
201 |
202 | .markdown h6 {
203 | font-size: 12px;
204 | }
205 |
206 | .markdown hr {
207 | height: 1px;
208 | border: 0;
209 | background: #e9e9e9;
210 | margin: 16px 0;
211 | clear: both;
212 | }
213 |
214 | .markdown p {
215 | margin: 1em 0;
216 | }
217 |
218 | .markdown>p,
219 | .markdown>blockquote,
220 | .markdown>.highlight,
221 | .markdown>ol,
222 | .markdown>ul {
223 | width: 80%;
224 | }
225 |
226 | .markdown ul>li {
227 | list-style: circle;
228 | }
229 |
230 | .markdown>ul li,
231 | .markdown blockquote ul>li {
232 | margin-left: 20px;
233 | padding-left: 4px;
234 | }
235 |
236 | .markdown>ul li p,
237 | .markdown>ol li p {
238 | margin: 0.6em 0;
239 | }
240 |
241 | .markdown ol>li {
242 | list-style: decimal;
243 | }
244 |
245 | .markdown>ol li,
246 | .markdown blockquote ol>li {
247 | margin-left: 20px;
248 | padding-left: 4px;
249 | }
250 |
251 | .markdown code {
252 | margin: 0 3px;
253 | padding: 0 5px;
254 | background: #eee;
255 | border-radius: 3px;
256 | }
257 |
258 | .markdown strong,
259 | .markdown b {
260 | font-weight: 600;
261 | }
262 |
263 | .markdown>table {
264 | border-collapse: collapse;
265 | border-spacing: 0px;
266 | empty-cells: show;
267 | border: 1px solid #e9e9e9;
268 | width: 95%;
269 | margin-bottom: 24px;
270 | }
271 |
272 | .markdown>table th {
273 | white-space: nowrap;
274 | color: #333;
275 | font-weight: 600;
276 | }
277 |
278 | .markdown>table th,
279 | .markdown>table td {
280 | border: 1px solid #e9e9e9;
281 | padding: 8px 16px;
282 | text-align: left;
283 | }
284 |
285 | .markdown>table th {
286 | background: #F7F7F7;
287 | }
288 |
289 | .markdown blockquote {
290 | font-size: 90%;
291 | color: #999;
292 | border-left: 4px solid #e9e9e9;
293 | padding-left: 0.8em;
294 | margin: 1em 0;
295 | }
296 |
297 | .markdown blockquote p {
298 | margin: 0;
299 | }
300 |
301 | .markdown .anchor {
302 | opacity: 0;
303 | transition: opacity 0.3s ease;
304 | margin-left: 8px;
305 | }
306 |
307 | .markdown .waiting {
308 | color: #ccc;
309 | }
310 |
311 | .markdown h1:hover .anchor,
312 | .markdown h2:hover .anchor,
313 | .markdown h3:hover .anchor,
314 | .markdown h4:hover .anchor,
315 | .markdown h5:hover .anchor,
316 | .markdown h6:hover .anchor {
317 | opacity: 1;
318 | display: inline-block;
319 | }
320 |
321 | .markdown>br,
322 | .markdown>p>br {
323 | clear: both;
324 | }
325 |
326 |
327 | .hljs {
328 | display: block;
329 | background: white;
330 | padding: 0.5em;
331 | color: #333333;
332 | overflow-x: auto;
333 | }
334 |
335 | .hljs-comment,
336 | .hljs-meta {
337 | color: #969896;
338 | }
339 |
340 | .hljs-string,
341 | .hljs-variable,
342 | .hljs-template-variable,
343 | .hljs-strong,
344 | .hljs-emphasis,
345 | .hljs-quote {
346 | color: #df5000;
347 | }
348 |
349 | .hljs-keyword,
350 | .hljs-selector-tag,
351 | .hljs-type {
352 | color: #a71d5d;
353 | }
354 |
355 | .hljs-literal,
356 | .hljs-symbol,
357 | .hljs-bullet,
358 | .hljs-attribute {
359 | color: #0086b3;
360 | }
361 |
362 | .hljs-section,
363 | .hljs-name {
364 | color: #63a35c;
365 | }
366 |
367 | .hljs-tag {
368 | color: #333333;
369 | }
370 |
371 | .hljs-title,
372 | .hljs-attr,
373 | .hljs-selector-id,
374 | .hljs-selector-class,
375 | .hljs-selector-attr,
376 | .hljs-selector-pseudo {
377 | color: #795da3;
378 | }
379 |
380 | .hljs-addition {
381 | color: #55a532;
382 | background-color: #eaffea;
383 | }
384 |
385 | .hljs-deletion {
386 | color: #bd2c00;
387 | background-color: #ffecec;
388 | }
389 |
390 | .hljs-link {
391 | text-decoration: underline;
392 | }
393 |
394 | /* 代码高亮 */
395 | /* PrismJS 1.15.0
396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
397 | /**
398 | * prism.js default theme for JavaScript, CSS and HTML
399 | * Based on dabblet (http://dabblet.com)
400 | * @author Lea Verou
401 | */
402 | code[class*="language-"],
403 | pre[class*="language-"] {
404 | color: black;
405 | background: none;
406 | text-shadow: 0 1px white;
407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
408 | text-align: left;
409 | white-space: pre;
410 | word-spacing: normal;
411 | word-break: normal;
412 | word-wrap: normal;
413 | line-height: 1.5;
414 |
415 | -moz-tab-size: 4;
416 | -o-tab-size: 4;
417 | tab-size: 4;
418 |
419 | -webkit-hyphens: none;
420 | -moz-hyphens: none;
421 | -ms-hyphens: none;
422 | hyphens: none;
423 | }
424 |
425 | pre[class*="language-"]::-moz-selection,
426 | pre[class*="language-"] ::-moz-selection,
427 | code[class*="language-"]::-moz-selection,
428 | code[class*="language-"] ::-moz-selection {
429 | text-shadow: none;
430 | background: #b3d4fc;
431 | }
432 |
433 | pre[class*="language-"]::selection,
434 | pre[class*="language-"] ::selection,
435 | code[class*="language-"]::selection,
436 | code[class*="language-"] ::selection {
437 | text-shadow: none;
438 | background: #b3d4fc;
439 | }
440 |
441 | @media print {
442 |
443 | code[class*="language-"],
444 | pre[class*="language-"] {
445 | text-shadow: none;
446 | }
447 | }
448 |
449 | /* Code blocks */
450 | pre[class*="language-"] {
451 | padding: 1em;
452 | margin: .5em 0;
453 | overflow: auto;
454 | }
455 |
456 | :not(pre)>code[class*="language-"],
457 | pre[class*="language-"] {
458 | background: #f5f2f0;
459 | }
460 |
461 | /* Inline code */
462 | :not(pre)>code[class*="language-"] {
463 | padding: .1em;
464 | border-radius: .3em;
465 | white-space: normal;
466 | }
467 |
468 | .token.comment,
469 | .token.prolog,
470 | .token.doctype,
471 | .token.cdata {
472 | color: slategray;
473 | }
474 |
475 | .token.punctuation {
476 | color: #999;
477 | }
478 |
479 | .namespace {
480 | opacity: .7;
481 | }
482 |
483 | .token.property,
484 | .token.tag,
485 | .token.boolean,
486 | .token.number,
487 | .token.constant,
488 | .token.symbol,
489 | .token.deleted {
490 | color: #905;
491 | }
492 |
493 | .token.selector,
494 | .token.attr-name,
495 | .token.string,
496 | .token.char,
497 | .token.builtin,
498 | .token.inserted {
499 | color: #690;
500 | }
501 |
502 | .token.operator,
503 | .token.entity,
504 | .token.url,
505 | .language-css .token.string,
506 | .style .token.string {
507 | color: #9a6e3a;
508 | background: hsla(0, 0%, 100%, .5);
509 | }
510 |
511 | .token.atrule,
512 | .token.attr-value,
513 | .token.keyword {
514 | color: #07a;
515 | }
516 |
517 | .token.function,
518 | .token.class-name {
519 | color: #DD4A68;
520 | }
521 |
522 | .token.regex,
523 | .token.important,
524 | .token.variable {
525 | color: #e90;
526 | }
527 |
528 | .token.important,
529 | .token.bold {
530 | font-weight: bold;
531 | }
532 |
533 | .token.italic {
534 | font-style: italic;
535 | }
536 |
537 | .token.entity {
538 | cursor: help;
539 | }
540 |
--------------------------------------------------------------------------------
/common/font/demo_index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IconFont Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | - Unicode
22 | - Font class
23 | - Symbol
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | -
32 |
33 |
箭头
34 | 
35 |
36 |
37 | -
38 |
39 |
输出
40 | 
41 |
42 |
43 | -
44 |
45 |
箭头
46 | 
47 |
48 |
49 |
50 |
51 |
Unicode 引用
52 |
53 |
54 |
Unicode 是字体在网页端最原始的应用方式,特点是:
55 |
56 | - 兼容性最好,支持 IE6+,及所有现代浏览器。
57 | - 支持按字体的方式去动态调整图标大小,颜色等等。
58 | - 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
59 |
60 |
61 | 注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式
62 |
63 |
Unicode 使用步骤如下:
64 |
第一步:拷贝项目下面生成的 @font-face
65 |
@font-face {
67 | font-family: 'iconfont';
68 | src: url('iconfont.eot');
69 | src: url('iconfont.eot?#iefix') format('embedded-opentype'),
70 | url('iconfont.woff2') format('woff2'),
71 | url('iconfont.woff') format('woff'),
72 | url('iconfont.ttf') format('truetype'),
73 | url('iconfont.svg#iconfont') format('svg');
74 | }
75 |
76 |
第二步:定义使用 iconfont 的样式
77 |
.iconfont {
79 | font-family: "iconfont" !important;
80 | font-size: 16px;
81 | font-style: normal;
82 | -webkit-font-smoothing: antialiased;
83 | -moz-osx-font-smoothing: grayscale;
84 | }
85 |
86 |
第三步:挑选相应图标并获取字体编码,应用于页面
87 |
88 | <span class="iconfont">3</span>
90 |
91 |
92 | "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
93 |
94 |
95 |
96 |
97 |
98 |
99 | -
100 |
101 |
102 | 箭头
103 |
104 | .icon-ai36
105 |
106 |
107 |
108 | -
109 |
110 |
111 | 输出
112 |
113 | .icon-shuchu
114 |
115 |
116 |
117 | -
118 |
119 |
120 | 箭头
121 |
122 | .icon-ai36-copy
123 |
124 |
125 |
126 |
127 |
128 |
font-class 引用
129 |
130 |
131 |
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
132 |
与 Unicode 使用方式相比,具有如下特点:
133 |
134 | - 兼容性良好,支持 IE8+,及所有现代浏览器。
135 | - 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
136 | - 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
137 | - 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
138 |
139 |
使用步骤如下:
140 |
第一步:引入项目下面生成的 fontclass 代码:
141 |
<link rel="stylesheet" href="./iconfont.css">
142 |
143 |
第二步:挑选相应图标并获取类名,应用于页面:
144 |
<span class="iconfont icon-xxx"></span>
145 |
146 |
147 | "
148 | iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
149 |
150 |
151 |
152 |
153 |
154 |
155 | -
156 |
159 |
箭头
160 | #icon-ai36
161 |
162 |
163 | -
164 |
167 |
输出
168 | #icon-shuchu
169 |
170 |
171 | -
172 |
175 |
箭头
176 | #icon-ai36-copy
177 |
178 |
179 |
180 |
181 |
Symbol 引用
182 |
183 |
184 |
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章
185 | 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
186 |
187 | - 支持多色图标了,不再受单色限制。
188 | - 通过一些技巧,支持像字体那样,通过
font-size
, color
来调整样式。
189 | - 兼容性较差,支持 IE9+,及现代浏览器。
190 | - 浏览器渲染 SVG 的性能一般,还不如 png。
191 |
192 |
使用步骤如下:
193 |
第一步:引入项目下面生成的 symbol 代码:
194 |
<script src="./iconfont.js"></script>
195 |
196 |
第二步:加入通用 CSS 代码(引入一次就行):
197 |
<style>
198 | .icon {
199 | width: 1em;
200 | height: 1em;
201 | vertical-align: -0.15em;
202 | fill: currentColor;
203 | overflow: hidden;
204 | }
205 | </style>
206 |
207 |
第三步:挑选相应图标并获取类名,应用于页面:
208 |
<svg class="icon" aria-hidden="true">
209 | <use xlink:href="#icon-xxx"></use>
210 | </svg>
211 |
212 |
213 |
214 |
215 |
216 |
217 |
236 |
237 |
238 |
--------------------------------------------------------------------------------
/common/font/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {font-family: "iconfont";
2 | src: url('iconfont.eot?t=1573099033531'); /* IE9 */
3 | src: url('iconfont.eot?t=1573099033531#iefix') format('embedded-opentype'), /* IE6-IE8 */
4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAANEAAsAAAAAB3QAAAL4AAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqCNIIaATYCJAMQCwoABCAFhG0HQxuHBhHVmyfIfh7YjqkTjqGcizyUo6tRDB0T/OfgefTv69wkD1+YyQpAJRSN0cHdljtva84BGxVhhqBbP9KlPBXyl08HAZAEQ/gHx4wXKZRns7lkUGXSACc40Kj8KOF/kH9B/kF3EZMiL22HAGGKUAlp1aZTL3wMoJ8AMmn82OH4FTsGE4R8BLdjqkG24eCr9eohHbbyn5dfbMQHhaMBQ7uNaT2KZl/tN6uXBcsYRJUs304LBLNAA5UAAzKuUz8CLf2V0ISzvzHUAXx8FHy1X/d/s0FgK+HCbPgvDxSCBnEByVEAajkBDV8tpkPB1/10CHyzSWiAF9LbB1xgEDDNwRQSK2jxvOJF3a9Y2IcoMSbFJCq9ei88fR4OPa2VvP68iHf0Rd3UtWeFWXZuwwWRpJuS5RcHLt+Ta2f12bPh3LKc7lFKOh+Jddv1VBtt3712796FvSIqoiODdViFe/dt0qLomzcn5k+af6LAsQUTF5x489pcxO1p5+S2rRXef6zWsf1hHmNvV71ga4Ej7YUONU2kprtm67q/5f+UyZtd1N6udNsWnZ1X5n/ZvJaiNW7NCLmZUesSjxVgeUybf8ed9Bd1sJToZRDs04v16B/yOxTuP6ri0ESjf37EAPDux438nD+d+1a5Gff1lxkgCEjIBwpjUgHmFEZDs8VZCggTJpIDzGk+byHLmZwMgk+OBoVHYdD4lEIYTCVwiFALXHyaQpiKtJ0dIcMMNGJCQAVWWyAkuQqKOA9Ak+QRwmA+gUMuf8AlKRrC9JHMkhHKCGxnMaaCBWR/sDk1CqlkJlLvqNsqZZPPSW/kYF2wXqzqoRgb5C1GhI/eiChQTDVE1m5YVQSeyWEuCyPiL8ul6nqlRU51Z9aJMRUsIPuDzalRtLXMGt9/R91WKQ/UzBjfyMG2D+vFagIiljSTal7llvDRGxEFiqmGyOqEVUXguzs5zGVhFnj9ZQmbqamSxfGy+gGygwbAUrpKtBhxkNrtYRRMmyPxVMfOc/L/Tgc=') format('woff2'),
5 | url('iconfont.woff?t=1573099033531') format('woff'),
6 | url('iconfont.ttf?t=1573099033531') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
7 | url('iconfont.svg?t=1573099033531#iconfont') format('svg'); /* iOS 4.1- */
8 | }
9 |
10 | .iconfont {
11 | font-family: "iconfont" !important;
12 | font-size: 16px;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .icon-ai36:before {
19 | content: "\e699";
20 | }
21 |
22 | .icon-shuchu:before {
23 | content: "\e617";
24 | }
25 |
26 | .icon-ai36-copy:before {
27 | content: "\e717";
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/common/font/iconfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/common/font/iconfont.eot
--------------------------------------------------------------------------------
/common/font/iconfont.js:
--------------------------------------------------------------------------------
1 | !function(d){var e,n='',t=(e=document.getElementsByTagName("script"))[e.length-1].getAttribute("data-injectcss");if(t&&!d.__iconfont__svg__cssinject__){d.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(e){console&&console.log(e)}}!function(e){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(e,0);else{var t=function(){document.removeEventListener("DOMContentLoaded",t,!1),e()};document.addEventListener("DOMContentLoaded",t,!1)}else document.attachEvent&&(c=e,o=d.document,i=!1,(l=function(){try{o.documentElement.doScroll("left")}catch(e){return void setTimeout(l,50)}n()})(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,n())});function n(){i||(i=!0,c())}var c,o,i,l}(function(){var e,t;(e=document.createElement("div")).innerHTML=n,n=null,(t=e.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",function(e,t){t.firstChild?function(e,t){t.parentNode.insertBefore(e,t)}(e,t.firstChild):t.appendChild(e)}(t,document.body))})}(window);
--------------------------------------------------------------------------------
/common/font/iconfont.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "",
3 | "name": "",
4 | "font_family": "iconfont",
5 | "css_prefix_text": "icon-",
6 | "description": "",
7 | "glyphs": [
8 | {
9 | "icon_id": "44945910",
10 | "name": "箭头",
11 | "font_class": "ai36",
12 | "unicode": "e699",
13 | "unicode_decimal": 59033
14 | },
15 | {
16 | "icon_id": "44945927",
17 | "name": "输出",
18 | "font_class": "shuchu",
19 | "unicode": "e617",
20 | "unicode_decimal": 58903
21 | },
22 | {
23 | "icon_id": "45432605",
24 | "name": "箭头",
25 | "font_class": "ai36-copy",
26 | "unicode": "e717",
27 | "unicode_decimal": 59159
28 | }
29 | ]
30 | }
31 |
--------------------------------------------------------------------------------
/common/font/iconfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
36 |
--------------------------------------------------------------------------------
/common/font/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/common/font/iconfont.ttf
--------------------------------------------------------------------------------
/common/font/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/common/font/iconfont.woff
--------------------------------------------------------------------------------
/common/font/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/common/font/iconfont.woff2
--------------------------------------------------------------------------------
/common/home.scss:
--------------------------------------------------------------------------------
1 | // 屏幕
2 | .screen {
3 | touch-action: none;
4 | display: flex;
5 | flex-direction: column;
6 | justify-content: space-between;
7 | // height:379rpx;
8 | height:350rpx;
9 | background:rgba(0,97,192,1);
10 | box-shadow:0rpx 8rpx 8rpx 0rpx rgba(1,90,176,0.66);
11 | border-radius:8rpx;
12 | margin: auto;
13 | color: #fff;
14 | font-size: 40rpx;
15 | font-weight: bold;
16 | padding: 22rpx;
17 | overflow: hidden;
18 | .line {
19 | display: flex;
20 | justify-content: space-between;
21 | }
22 | .center {
23 | display: flex;
24 | justify-content: center;
25 | }
26 | }
27 | // 居中的布局
28 | .screen_c {
29 | justify-content: center;
30 | }
31 | .margin_c {
32 | margin: 56rpx 0;
33 | }
34 | .en_l {
35 | margin-left: 30rpx;
36 | }
37 | .center {
38 | line-height: 45rpx;
39 | }
40 | .num {
41 | background: #000 !important;
42 | }
43 | .bg {
44 | background: #000 !important;
45 | // background: #0086B3;
46 | }
--------------------------------------------------------------------------------
/common/index.scss:
--------------------------------------------------------------------------------
1 |
2 | .box {
3 | touch-action: none;
4 | }
5 | .content {
6 | touch-action: none;
7 | padding: 24rpx;
8 | height: 100%;
9 | padding-bottom: 0;
10 | padding-top: 15rpx;
11 | // padding-bottom: 5vh;
12 |
13 |
14 |
15 | /* // 按钮公共样式 */
16 | .btns {
17 | padding-top: 26rpx;
18 | .btn {
19 | padding: 35rpx 0;
20 | width:160rpx;
21 | height:160rpx;
22 | background:linear-gradient(0deg,rgba(0,85,173,1) 0%,rgba(0,137,255,1) 100%);
23 | border-radius:50%;
24 | box-shadow:3rpx 5rpx 20rpx 0rpx rgba(0,93,187,0.48), 1rpx 1rpx 32rpx 0rpx rgba(0,93,182,0.64);
25 | display: flex;
26 | flex-direction: column;
27 | align-items: center;
28 | color: #fff;
29 | justify-content: space-around;
30 | border:7rpx solid linear-gradient(0deg,rgba(0,85,173,1) 0%,rgba(0,137,255,1) 100%);
31 | .s {
32 | font-size: 36rpx;
33 | text-shadow:0rpx 1rpx 1rpx rgba(60,70,91,0.75);
34 | }
35 | .x {
36 | font-size: 25rpx;
37 | text-shadow:0rpx 1rpx 1rpx rgba(60,70,91,0.75);
38 | }
39 | }
40 | /* // 按钮布局 */
41 | .box {
42 | display: flex;
43 | justify-content: space-between;
44 | align-items: center;
45 | font-weight: bold;
46 | .none {
47 | background: none;
48 | box-shadow:none;
49 | border: none;
50 | }
51 | }
52 | /* // 中间大按钮 */
53 | .center {
54 | justify-content: space-around;
55 | .left {
56 | width: 400rpx;
57 | height: 400rpx;
58 | background: none;
59 | box-shadow: none;
60 | background: url(../../static/images/big_btn.png) no-repeat;
61 | background-size: 100% 100%;
62 | position: relative;
63 | .center_btn {
64 | position: absolute;
65 | width:70rpx;
66 | height:70rpx;
67 | z-index: 1;
68 | }
69 | .top_btn {
70 | // top: 40rpx;
71 | top: 10rpx;
72 | padding-top: 10rpx;
73 | left: 50%;
74 | // width:51rpx;
75 | // height:31rpx;
76 | transform: translateX(-50%);
77 | background: url(../../static/images/top_btn.png) no-repeat center;
78 | background-size: 50rpx 30rpx;
79 | }
80 | .right_btn {
81 | top: 50%;
82 | // right: 40rpx;
83 | right: 10rpx;
84 | padding-right: 30rpx;
85 | // width: 31rpx;
86 | // height: 51rpx;
87 | transform: translateY(-50%);
88 | background: url(../../static/images/right_btn.png) no-repeat center;
89 | background-size: 30rpx 50rpx;
90 | }
91 | .left_btn {
92 | top: 50%;
93 | // left: 40rpx;
94 | padding-left: 30rpx;
95 | left: 10rpx;
96 | // width: 31rpx;
97 | // height: 51rpx;
98 | transform: translateY(-50%);
99 | background: url(../../static/images/left_btn.png) no-repeat center;
100 | background-size: 30rpx 50rpx;
101 | }
102 | .bottom_btn {
103 | // bottom: 40rpx;
104 | bottom: 10rpx;
105 | left: 50%;
106 | padding-bottom: 30rpx;
107 | // width:51rpx;
108 | // height:31rpx;
109 | transform: translateX(-50%);
110 | background: url(../../static/images/bottom_btn.png) no-repeat center;
111 | background-size: 50rpx 30rpx;
112 | }
113 | .circel_btn {
114 | position: absolute;
115 | top: 50%;
116 | left: 50%;
117 | width: 180rpx;
118 | height: 180rpx;
119 | transform: translate(-50%,-50%);
120 | background: url(../../static/images/circel_btn.png) no-repeat;
121 | background-size: 100% 100%;
122 | }
123 |
124 | /* // 点击特效
125 | // 中间大按钮特效
126 | // 上 */
127 | .top_btn_hover {
128 | background: url(../../static/images/top_btn_tap.png) no-repeat center;
129 | background-size: 50rpx 30rpx;
130 | }
131 | /* // 下 */
132 | .bottom_btn_hover {
133 | background: url(../../static/images/bottom_btn_tap.png) no-repeat center;
134 | background-size: 50rpx 30rpx;
135 | }
136 | /* // 左 */
137 | .left_btn_hover {
138 | background: url(../../static/images/left_btn_tap.png) no-repeat center;
139 | background-size: 30rpx 50rpx;
140 | }
141 | /* // 右 */
142 | .right_btn_hover {
143 | background: url(../../static/images/right_btn_tap.png) no-repeat center;
144 | background-size: 30rpx 50rpx;
145 | }
146 | /* // 中 */
147 | .circel_btn_hover {
148 | background: url(../../static/images/circel_btn_tap.png) no-repeat;
149 | background-size: 180rpx 180rpx;
150 | }
151 | }
152 | }
153 | /* // 下方按钮 */
154 | .bottom {
155 | .red {
156 | justify-content: space-between;
157 | flex-direction: row;
158 | .redChild {
159 | width:80rpx;
160 | height:160rpx;
161 | background:linear-gradient(0deg,rgba(199,34,99,1) 0%,rgba(250,44,160,1) 100%);
162 | // box-shadow:6rpx 10rpx 32rpx 0rpx rgba(12,19,36,0.48);
163 | border-radius: 80rpx 0 0 80rpx;
164 | display: flex;
165 | flex-direction: column;
166 | // justify-content: space-around;
167 | padding-top: 45rpx;
168 | align-items: center;
169 | }
170 |
171 | .auto {
172 | border-radius: 0rpx 80rpx 80rpx 0rpx;
173 | // margin-left: 1rpx;
174 | // background: url(../../static/images/right.png) no-repeat 100% 100% !important;
175 | }
176 | .Manu {
177 | padding-right: -1rpx;
178 | // background: url(../../static/images/left.png) no-repeat 100% 100% !important;
179 | }
180 | }
181 | }
182 | }
183 | /* // 手指点击 */
184 | .btn-hover {
185 | width:160rpx;
186 | height:160rpx;
187 | background:linear-gradient(0deg,rgba(0,78,158,1) 0%,rgba(31,145,243,1) 100%);
188 | background-size: 100% 100%;
189 | border:7rpx solid rgba(14, 109, 198, 1);
190 | box-shadow:3rpx 5rpx 20rpx 0rpx rgba(0,93,187,0.48), 1rpx 1rpx 32rpx 0rpx rgba(0,93,182,0.64);
191 | border-radius:50%;
192 | }
193 | /* // 手动自动点击特效 */
194 | .btn-hover-right {
195 | box-shadow:6px 10px 32px 0px rgba(12,19,36,0.3) inset;
196 | }
197 | .btn-hover-left {
198 | background: #0C1324;
199 | box-shadow: 6px 10px 32px 0px rgba(12,19,36,0.3) inset;
200 | }
201 | .btn-hover-left1 {
202 | background: darkred !important;
203 | }
204 |
205 | }
206 | .num {
207 | background: #000 !important;
208 | }
209 | .bg {
210 | background: #000 !important;
211 | // background: #0086B3;
212 | }
--------------------------------------------------------------------------------
/components/neil-modal/neil-modal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{content}}
9 |
10 |
11 |
12 |
13 |
14 |
26 |
27 |
28 |
29 |
30 |
117 |
118 |
268 |
--------------------------------------------------------------------------------
/components/neil-modal/readme.md:
--------------------------------------------------------------------------------
1 | ### Modal 模态框
2 |
3 | 自定义 Modal 模态框组件
4 |
5 | **使用方式:**
6 |
7 | 在 script 中引用组件
8 |
9 | ```javascript
10 | import neilModal from '@/components/neil-modal/neil-modal.vue';
11 | export default {
12 | components: {neilModal}
13 | }
14 | ```
15 |
16 | 基础使用方式
17 |
18 | ```html
19 |
26 |
27 | ```
28 |
29 | 单个确认按钮
30 |
31 | ```html
32 |
37 |
38 | ```
39 |
40 | **属性说明:**
41 |
42 | |属性名 |类型 |默认值 |说明 |
43 | |--- |---- |--- |--- |
44 | |title|String||标题 |
45 | |content|String||内容|
46 | |align|String|left|内容对齐方式,值为:left(左对齐)、center(居中对齐)、right(右对齐)|
47 | |show |Boolean |false |Modal的显示状态 |
48 | |show-cancel|Boolean|true |是否显示取消按钮|
49 | |auto-close|Boolean|true |点击遮罩是否自动关闭模态框|
50 | |confirm-color|String|#007aff|确认按钮的颜色 |
51 | |confirm-text|String|确定|确定按钮的文字 |
52 | |cancel-color|String|#333333|取消按钮的颜色 |
53 | |cancel-text|String|取消|取消按钮的文字 |
54 |
55 | **事件说明:**
56 |
57 | |事件名|说明 |
58 | |close|组件关闭时触发事件|
59 | |confirm|点击确认按钮时触发事件|
60 | |cancel|点击取消按钮时触发事件|
61 |
62 | **slot**
63 |
64 | 在 ``neil-modal`` 节点下,可以通过插入节点实现自定义 content 的需求(只有 content 属性为空的时候才会加载 slot)
65 |
66 | 使用示例:
67 |
68 | ```html
69 |
70 |
71 | 1. 修复标题颜色不对的问题
72 | 2. 增加支付宝支付功能
73 | 3. 增加更多示例
74 |
75 |
76 | ```
77 |
78 | **其他**
79 |
80 | * Modal 组件 z-index 为 1000;
81 | * Modal 组件非原生组件,使用时会被原生组件所覆盖;
82 | * 通过本页面下载按钮下载的zip为一个完整 ``uni-app`` 工程,拖入 HBuilderX即可运行体验效果;
83 | * 若想集成本组件到现有工程,可以将 components 目录下的 neil-modal 目录拷贝到自己工程的 components 目录;
84 | * 使用过程出现问题或有新的需求可在评论区留言。
85 |
--------------------------------------------------------------------------------
/components/uni-countdown/uni-countdown.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ h }}
7 | {{'时' }}
8 | {{ i }}
9 | {{ '分' }}
10 | {{ s }}
11 | 秒
12 |
13 |
14 | {{'h' }}
15 | {{ h }}
16 | {{ 'm' }}
17 | {{ i }}
18 | s
19 | {{ s }}
20 |
21 |
22 |
23 |
164 |
208 |
--------------------------------------------------------------------------------
/components/uni-popup/uni-popup.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
97 |
215 |
--------------------------------------------------------------------------------
/lang/en.js:
--------------------------------------------------------------------------------
1 | export default {
2 | lang: 'en',
3 | loading: 'loading...',
4 | title: 'AdverPlayer',
5 | login: {
6 | button: 'Scan the code',
7 | more: 'more'
8 |
9 | },
10 | password: {
11 | placeholder: 'Enter device password',
12 | change: 'Change password',
13 | oldPswd: 'Enter old password',
14 | newPswd: 'Enter new password',
15 | submit: 'Submit',
16 | confirm: 'Confirm',
17 | cancel: 'Cancel'
18 | },
19 | turnPage: {
20 |
21 | title: 'Turn Page',
22 | first: 'Last Page',
23 | last: 'First Page',
24 | down: 'Down One',
25 | up: 'Up One',
26 | // down: 'Down One',
27 | // up: 'Up One'
28 | },
29 | calibration: {
30 | title: 'Calibration',
31 | click: 'Click',
32 | Inching: 'Inching',
33 | pres: 'Pres',
34 | moving: 'Moving'
35 | },
36 | other: {
37 | closing: 'closing',
38 | sureConnBLE: "Make sure you're connected to bluetooth",
39 | corretTime: 'Automatic time correction?',
40 | y: 'Yes',
41 | n: 'Not',
42 | login: 'logining',
43 | AutoCan: 'Auto mode can be entered',
44 | offlineOrOther: 'Offline Or Other',
45 | settingFail: 'Fail',
46 | BLEList: 'Query bluetooth list',
47 | BLEConn: 'list (tap connect)',
48 | BLENone: 'None',
49 | corrected: 'Is connected',
50 | outLine: 'Out Line',
51 | setting1: 'setting',
52 | search: 'In the search',
53 | checking: 'checking',
54 | reconnection: 'reconnection',
55 | fail: 'fail',
56 | notStorage: 'Not in storage',
57 | conning: 'wait a moment',
58 | sucWait: 'Sucess waitting',
59 | searchNone: 'No device found/in use',
60 | searchFail: 'Query failed.Make sure bluetooth is enabled/located',
61 | stopMode: 'Stop Mode',
62 | back: 'Manu/Auto To ESC',
63 | save: 'ENTER To Save',
64 | toMenu: 'Esc To Back',
65 | move: 'To Move',
66 | enter: 'ENTER To Save',
67 | setItems: 'ENTER To Menu',
68 | sysTitle: 'System Time',
69 | modeTitle: 'Run Mode',
70 | host: 'Host',
71 | single: 'Single',
72 | test: 'Test',
73 | client: 'Client',
74 | delayTitle: 'Sync. Time',
75 | copyTitle: 'Data Copy',
76 | copy: 'Copying...',
77 | setIdTitle: 'Board ID'
78 | },
79 | Manu: {
80 | title: 'Manu Mode',
81 | title2: 'Auto Mode'
82 | },
83 | pageNum: {
84 | title: 'Page Number',
85 | set: 'Set To:',
86 | change: 'To Change'
87 | },
88 |
89 | showTime: {
90 | all: 'All Show Time',
91 | single: 'One Show Time',
92 | No: 'NO.',
93 | set: 'Set To:'
94 | },
95 | timer: {
96 | title1: 'ON Time A',
97 | title2: 'ON Time B',
98 | title3: 'OFF Time A',
99 | title4: 'OFF Time B',
100 | title5: 'ON Light Time',
101 | title6: 'OFF Light Time'
102 |
103 | },
104 | speedSet: {
105 | title: 'Speed',
106 | set: 'Set To:',
107 | change: 'To Change'
108 | },
109 | stopPic: {
110 | title: 'Stay Picture'
111 | },
112 | parameters: {
113 | OV: 'OV Parameters',
114 | OC: 'OC Parameters',
115 | OT: 'OT Parameters',
116 | UV: 'UV Parameters',
117 | UVT: 'UVT Parameters'
118 | },
119 | index: {
120 | GPRS: 'GPRS',
121 | BLE: 'BLE',
122 | Speed: 'Speed',
123 | modelContent: 'Sure back to the scan page?',
124 | manu: 'Manu',
125 | client: 'Client',
126 | ok: 'OK',
127 | OV: 'OV',
128 | OC: 'OC',
129 | OT: 'OT',
130 | UV: 'UV',
131 | F: 'FALSE',
132 | host: 'Host',
133 | single: 'Single',
134 | test: 'Test',
135 | auto: 'Auto',
136 | homeData: {
137 | // 当前画面
138 | nowPic: 1,
139 | auto: 'Auto',
140 | // 速度和类型
141 | speed: 0, // 速度为0-9
142 | type: 1, // 1GPRS 2BLE
143 | // // 年月日
144 | // year_l: 0,
145 | // year_r: 0,
146 | // month_l: 0,
147 | // month_r: 0,
148 | // day_l: 0,
149 | // day_r: 0,
150 | // // 时分秒
151 | // hour_l: 0,
152 | // hour_r: 0,
153 | // minute_l: 0,
154 | // minute_r: 0,
155 | // second_l: 0,
156 | // second_r: 0,
157 | d_l: 0,
158 | d_r: 0,
159 | f_l: 0,
160 | f_r: 0,
161 | h_l: 0,
162 | h_r: 0,
163 | m_l: 0,
164 | m_r: 0,
165 | s_l: 0,
166 | s_r: 0,
167 | y_l: 0,
168 | y_r: 0,
169 | // 第四行
170 | runStatus: 0, // 运行状态 0自动 7自检 3急停 4或1手动 2 或6 微调 8暂时未用到
171 | // 主从机模式
172 | model: 0, // 0-独立 1-主机,2-从机
173 | /* 主板工作状态
174 | 0--系统正常 2--过温保护 3--过流保护 4--过压保护 5--欠压保护) 其它:连接失败 */
175 | workStatus: 0
176 | },
177 | // 设置项数据
178 | setItemData: [
179 | { id: 1, value: 'Page Number' },
180 | { id: 2, value: 'Show Time' },
181 | { id: 3, value: 'Timer' },
182 | { id: 4, value: 'Speed' }
183 | ],
184 | setItemsData1: [
185 | { id: 1, value: 'Page Number' },
186 | { id: 2, value: 'Show Time' },
187 | { id: 3, value: 'Timer' },
188 | { id: 4, value: 'Speed' }
189 | ],
190 | setItemsData2: [
191 | { id: 5, value: 'Stop Page' },
192 | { id: 6, value: 'System Time' },
193 | { id: 7, value: 'Run Mode' },
194 | { id: 8, value: 'Sync. Time' }
195 | ],
196 | setItemsData3: [
197 | { id: 9, value: 'Data Copy' },
198 | { id: 10, value: 'Board ID' },
199 | { id: 11, value: 'Parameter Set' },
200 | { id: 12, value: 'BPP Set' }
201 | ],
202 | showTimeData: [
203 | { id: 1, value: 'All Show Time' },
204 | { id: 2, value: 'One Show Time' }
205 | ],
206 | // 定时设置
207 | timerData: [
208 | { id: 1, value: 'ON Time A' },
209 | { id: 2, value: 'OFF Time A' },
210 | { id: 3, value: 'ON Time B' },
211 | { id: 4, value: 'OFF Time B' },
212 | ],
213 | timerData1: [
214 | { id: 1, value: 'ON Time A' },
215 | { id: 2, value: 'OFF Time A' },
216 | { id: 3, value: 'ON Time B' },
217 | { id: 4, value: 'OFF Time B' },
218 | ],
219 | timerData2: [
220 | { id: 5, value: 'ON Light Time' },
221 | { id: 6, value: 'OFF Light Time' },
222 | ],
223 | // 其他参数设置数据
224 | otherParametersData: [
225 | {id: 1, value: 'OV Parameter'},
226 | {id: 2, value: 'OC Parameter'},
227 | {id: 3, value: 'OT Parameter'},
228 | {id: 4, value: 'UV Parameter'}
229 | ],
230 | otherParametersData1: [
231 | {id: 1, value: 'OV Parameter'},
232 | {id: 2, value: 'OC Parameter'},
233 | {id: 3, value: 'OT Parameter'},
234 | {id: 4, value: 'UV Parameter'}
235 | ],
236 | otherParametersData2: [
237 | {id: 5, value: 'UVT Parameter'}
238 | ],
239 | setBLEData: [
240 | { id: 1, value: 'Search BLE list' },
241 | { id: 2, value: 'Close the BLE' }
242 | ],
243 | },
244 | toast: 'You have cancelled authorization. Login failed',
245 | Toast: {
246 | complete: 'Please complete the information',
247 | pswdSuc: 'Password changed successfully',
248 | fillPswd: 'Please fill in the password',
249 | unKnown: 'Unknown error, please contact administrator',
250 | scanFalse: 'Sweep code failure',
251 | scanFailed: 'Please make sure the device code is correct and scan the code again',
252 | systemErr: 'System exceptions.',
253 | NotOpen: 'Temporary not open',
254 | modifie:'Modifie fail'
255 | }
256 |
257 | }
--------------------------------------------------------------------------------
/lang/index.js:
--------------------------------------------------------------------------------
1 | import LangEn from './en.js'
2 | import LangChs from './zh.js'
3 | import Vue from 'vue'
4 | import VueI18n from './vue-i18n'
5 | Vue.use(VueI18n)
6 | const system_info = uni.getStorageSync('system_info')
7 | if (!system_info) {
8 | // 获取设备信息
9 | uni.getSystemInfo({
10 | success: function (res) {
11 | uni.setStorageSync('system_info', res);
12 | }
13 | })
14 | }
15 | const cur_lang = system_info.language == 'en' ? 'en' : 'zh_CN'
16 | const i18n = new VueI18n({
17 | locale: cur_lang || 'zh_CN', // 默认选择的语言
18 | messages: {
19 | 'en': LangEn,
20 | 'zh_CN': LangChs
21 | }
22 | })
23 | export default i18n
--------------------------------------------------------------------------------
/lang/vue-i18n.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * vue-i18n v8.10.0
3 | * (c) 2019 kazuya kawaguchi
4 | * Released under the MIT License.
5 | */
6 | (function (global, factory) {
7 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
8 | typeof define === 'function' && define.amd ? define(factory) :
9 | (global.VueI18n = factory());
10 | }(this, (function () { 'use strict';
11 |
12 | /* */
13 |
14 | /**
15 | * constants
16 | */
17 |
18 | var numberFormatKeys = [
19 | 'style',
20 | 'currency',
21 | 'currencyDisplay',
22 | 'useGrouping',
23 | 'minimumIntegerDigits',
24 | 'minimumFractionDigits',
25 | 'maximumFractionDigits',
26 | 'minimumSignificantDigits',
27 | 'maximumSignificantDigits',
28 | 'localeMatcher',
29 | 'formatMatcher'
30 | ];
31 |
32 | /**
33 | * utilities
34 | */
35 |
36 | function warn (msg, err) {
37 | if (typeof console !== 'undefined') {
38 | console.warn('[vue-i18n] ' + msg);
39 | /* istanbul ignore if */
40 | if (err) {
41 | console.warn(err.stack);
42 | }
43 | }
44 | }
45 |
46 | function isObject (obj) {
47 | return obj !== null && typeof obj === 'object'
48 | }
49 |
50 | var toString = Object.prototype.toString;
51 | var OBJECT_STRING = '[object Object]';
52 | function isPlainObject (obj) {
53 | return toString.call(obj) === OBJECT_STRING
54 | }
55 |
56 | function isNull (val) {
57 | return val === null || val === undefined
58 | }
59 |
60 | function parseArgs () {
61 | var args = [], len = arguments.length;
62 | while ( len-- ) args[ len ] = arguments[ len ];
63 |
64 | var locale = null;
65 | var params = null;
66 | if (args.length === 1) {
67 | if (isObject(args[0]) || Array.isArray(args[0])) {
68 | params = args[0];
69 | } else if (typeof args[0] === 'string') {
70 | locale = args[0];
71 | }
72 | } else if (args.length === 2) {
73 | if (typeof args[0] === 'string') {
74 | locale = args[0];
75 | }
76 | /* istanbul ignore if */
77 | if (isObject(args[1]) || Array.isArray(args[1])) {
78 | params = args[1];
79 | }
80 | }
81 |
82 | return { locale: locale, params: params }
83 | }
84 |
85 | function looseClone (obj) {
86 | return JSON.parse(JSON.stringify(obj))
87 | }
88 |
89 | function remove (arr, item) {
90 | if (arr.length) {
91 | var index = arr.indexOf(item);
92 | if (index > -1) {
93 | return arr.splice(index, 1)
94 | }
95 | }
96 | }
97 |
98 | var hasOwnProperty = Object.prototype.hasOwnProperty;
99 | function hasOwn (obj, key) {
100 | return hasOwnProperty.call(obj, key)
101 | }
102 |
103 | function merge (target) {
104 | var arguments$1 = arguments;
105 |
106 | var output = Object(target);
107 | for (var i = 1; i < arguments.length; i++) {
108 | var source = arguments$1[i];
109 | if (source !== undefined && source !== null) {
110 | var key = (void 0);
111 | for (key in source) {
112 | if (hasOwn(source, key)) {
113 | if (isObject(source[key])) {
114 | output[key] = merge(output[key], source[key]);
115 | } else {
116 | output[key] = source[key];
117 | }
118 | }
119 | }
120 | }
121 | }
122 | return output
123 | }
124 |
125 | function looseEqual (a, b) {
126 | if (a === b) { return true }
127 | var isObjectA = isObject(a);
128 | var isObjectB = isObject(b);
129 | if (isObjectA && isObjectB) {
130 | try {
131 | var isArrayA = Array.isArray(a);
132 | var isArrayB = Array.isArray(b);
133 | if (isArrayA && isArrayB) {
134 | return a.length === b.length && a.every(function (e, i) {
135 | return looseEqual(e, b[i])
136 | })
137 | } else if (!isArrayA && !isArrayB) {
138 | var keysA = Object.keys(a);
139 | var keysB = Object.keys(b);
140 | return keysA.length === keysB.length && keysA.every(function (key) {
141 | return looseEqual(a[key], b[key])
142 | })
143 | } else {
144 | /* istanbul ignore next */
145 | return false
146 | }
147 | } catch (e) {
148 | /* istanbul ignore next */
149 | return false
150 | }
151 | } else if (!isObjectA && !isObjectB) {
152 | return String(a) === String(b)
153 | } else {
154 | return false
155 | }
156 | }
157 |
158 | /* */
159 |
160 | function extend (Vue) {
161 | if (!Vue.prototype.hasOwnProperty('$i18n')) {
162 | // $FlowFixMe
163 | Object.defineProperty(Vue.prototype, '$i18n', {
164 | get: function get () { return this._i18n }
165 | });
166 | }
167 |
168 | Vue.prototype.$t = function (key) {
169 | var values = [], len = arguments.length - 1;
170 | while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
171 |
172 | var i18n = this.$i18n;
173 | return i18n._t.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this ].concat( values ))
174 | };
175 |
176 | Vue.prototype.$tc = function (key, choice) {
177 | var values = [], len = arguments.length - 2;
178 | while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
179 |
180 | var i18n = this.$i18n;
181 | return i18n._tc.apply(i18n, [ key, i18n.locale, i18n._getMessages(), this, choice ].concat( values ))
182 | };
183 |
184 | Vue.prototype.$te = function (key, locale) {
185 | var i18n = this.$i18n;
186 | return i18n._te(key, i18n.locale, i18n._getMessages(), locale)
187 | };
188 |
189 | Vue.prototype.$d = function (value) {
190 | var ref;
191 |
192 | var args = [], len = arguments.length - 1;
193 | while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
194 | return (ref = this.$i18n).d.apply(ref, [ value ].concat( args ))
195 | };
196 |
197 | Vue.prototype.$n = function (value) {
198 | var ref;
199 |
200 | var args = [], len = arguments.length - 1;
201 | while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
202 | return (ref = this.$i18n).n.apply(ref, [ value ].concat( args ))
203 | };
204 | }
205 |
206 | /* */
207 |
208 | var mixin = {
209 | beforeCreate: function beforeCreate () {
210 | var options = this.$options;
211 | options.i18n = options.i18n || (options.__i18n ? {} : null);
212 |
213 | if (options.i18n) {
214 | if (options.i18n instanceof VueI18n) {
215 | // init locale messages via custom blocks
216 | if (options.__i18n) {
217 | try {
218 | var localeMessages = {};
219 | options.__i18n.forEach(function (resource) {
220 | localeMessages = merge(localeMessages, JSON.parse(resource));
221 | });
222 | Object.keys(localeMessages).forEach(function (locale) {
223 | options.i18n.mergeLocaleMessage(locale, localeMessages[locale]);
224 | });
225 | } catch (e) {
226 | {
227 | warn("Cannot parse locale messages via custom blocks.", e);
228 | }
229 | }
230 | }
231 | this._i18n = options.i18n;
232 | this._i18nWatcher = this._i18n.watchI18nData();
233 | this._i18n.subscribeDataChanging(this);
234 | this._subscribing = true;
235 | } else if (isPlainObject(options.i18n)) {
236 | // component local i18n
237 | if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
238 | options.i18n.root = this.$root;
239 | options.i18n.formatter = this.$root.$i18n.formatter;
240 | options.i18n.fallbackLocale = this.$root.$i18n.fallbackLocale;
241 | options.i18n.silentTranslationWarn = this.$root.$i18n.silentTranslationWarn;
242 | options.i18n.silentFallbackWarn = this.$root.$i18n.silentFallbackWarn;
243 | options.i18n.pluralizationRules = this.$root.$i18n.pluralizationRules;
244 | options.i18n.preserveDirectiveContent = this.$root.$i18n.preserveDirectiveContent;
245 | }
246 |
247 | // init locale messages via custom blocks
248 | if (options.__i18n) {
249 | try {
250 | var localeMessages$1 = {};
251 | options.__i18n.forEach(function (resource) {
252 | localeMessages$1 = merge(localeMessages$1, JSON.parse(resource));
253 | });
254 | options.i18n.messages = localeMessages$1;
255 | } catch (e) {
256 | {
257 | warn("Cannot parse locale messages via custom blocks.", e);
258 | }
259 | }
260 | }
261 |
262 | this._i18n = new VueI18n(options.i18n);
263 | this._i18nWatcher = this._i18n.watchI18nData();
264 | this._i18n.subscribeDataChanging(this);
265 | this._subscribing = true;
266 |
267 | if (options.i18n.sync === undefined || !!options.i18n.sync) {
268 | this._localeWatcher = this.$i18n.watchLocale();
269 | }
270 | } else {
271 | {
272 | warn("Cannot be interpreted 'i18n' option.");
273 | }
274 | }
275 | } else if (this.$root && this.$root.$i18n && this.$root.$i18n instanceof VueI18n) {
276 | // root i18n
277 | this._i18n = this.$root.$i18n;
278 | this._i18n.subscribeDataChanging(this);
279 | this._subscribing = true;
280 | } else if (options.parent && options.parent.$i18n && options.parent.$i18n instanceof VueI18n) {
281 | // parent i18n
282 | this._i18n = options.parent.$i18n;
283 | this._i18n.subscribeDataChanging(this);
284 | this._subscribing = true;
285 | }
286 | },
287 |
288 | beforeDestroy: function beforeDestroy () {
289 | if (!this._i18n) { return }
290 |
291 | var self = this;
292 | this.$nextTick(function () {
293 | if (self._subscribing) {
294 | self._i18n.unsubscribeDataChanging(self);
295 | delete self._subscribing;
296 | }
297 |
298 | if (self._i18nWatcher) {
299 | self._i18nWatcher();
300 | self._i18n.destroyVM();
301 | delete self._i18nWatcher;
302 | }
303 |
304 | if (self._localeWatcher) {
305 | self._localeWatcher();
306 | delete self._localeWatcher;
307 | }
308 |
309 | self._i18n = null;
310 | });
311 | }
312 | };
313 |
314 | /* */
315 |
316 | var interpolationComponent = {
317 | name: 'i18n',
318 | functional: true,
319 | props: {
320 | tag: {
321 | type: String,
322 | default: 'span'
323 | },
324 | path: {
325 | type: String,
326 | required: true
327 | },
328 | locale: {
329 | type: String
330 | },
331 | places: {
332 | type: [Array, Object]
333 | }
334 | },
335 | render: function render (h, ref) {
336 | var props = ref.props;
337 | var data = ref.data;
338 | var children = ref.children;
339 | var parent = ref.parent;
340 |
341 | var i18n = parent.$i18n;
342 |
343 | children = (children || []).filter(function (child) {
344 | return child.tag || (child.text = child.text.trim())
345 | });
346 |
347 | if (!i18n) {
348 | {
349 | warn('Cannot find VueI18n instance!');
350 | }
351 | return children
352 | }
353 |
354 | var path = props.path;
355 | var locale = props.locale;
356 |
357 | var params = {};
358 | var places = props.places || {};
359 |
360 | var hasPlaces = Array.isArray(places)
361 | ? places.length > 0
362 | : Object.keys(places).length > 0;
363 |
364 | var everyPlace = children.every(function (child) {
365 | if (child.data && child.data.attrs) {
366 | var place = child.data.attrs.place;
367 | return (typeof place !== 'undefined') && place !== ''
368 | }
369 | });
370 |
371 | if (hasPlaces && children.length > 0 && !everyPlace) {
372 | warn('If places prop is set, all child elements must have place prop set.');
373 | }
374 |
375 | if (Array.isArray(places)) {
376 | places.forEach(function (el, i) {
377 | params[i] = el;
378 | });
379 | } else {
380 | Object.keys(places).forEach(function (key) {
381 | params[key] = places[key];
382 | });
383 | }
384 |
385 | children.forEach(function (child, i) {
386 | var key = everyPlace
387 | ? ("" + (child.data.attrs.place))
388 | : ("" + i);
389 | params[key] = child;
390 | });
391 |
392 | return h(props.tag, data, i18n.i(path, locale, params))
393 | }
394 | };
395 |
396 | /* */
397 |
398 | var numberComponent = {
399 | name: 'i18n-n',
400 | functional: true,
401 | props: {
402 | tag: {
403 | type: String,
404 | default: 'span'
405 | },
406 | value: {
407 | type: Number,
408 | required: true
409 | },
410 | format: {
411 | type: [String, Object]
412 | },
413 | locale: {
414 | type: String
415 | }
416 | },
417 | render: function render (h, ref) {
418 | var props = ref.props;
419 | var parent = ref.parent;
420 | var data = ref.data;
421 |
422 | var i18n = parent.$i18n;
423 |
424 | if (!i18n) {
425 | {
426 | warn('Cannot find VueI18n instance!');
427 | }
428 | return null
429 | }
430 |
431 | var key = null;
432 | var options = null;
433 |
434 | if (typeof props.format === 'string') {
435 | key = props.format;
436 | } else if (isObject(props.format)) {
437 | if (props.format.key) {
438 | key = props.format.key;
439 | }
440 |
441 | // Filter out number format options only
442 | options = Object.keys(props.format).reduce(function (acc, prop) {
443 | var obj;
444 |
445 | if (numberFormatKeys.includes(prop)) {
446 | return Object.assign({}, acc, ( obj = {}, obj[prop] = props.format[prop], obj ))
447 | }
448 | return acc
449 | }, null);
450 | }
451 |
452 | var locale = props.locale || i18n.locale;
453 | var parts = i18n._ntp(props.value, locale, key, options);
454 |
455 | var values = parts.map(function (part, index) {
456 | var obj;
457 |
458 | var slot = data.scopedSlots && data.scopedSlots[part.type];
459 | return slot ? slot(( obj = {}, obj[part.type] = part.value, obj.index = index, obj.parts = parts, obj )) : part.value
460 | });
461 |
462 | return h(props.tag, {
463 | attrs: data.attrs,
464 | 'class': data['class'],
465 | staticClass: data.staticClass
466 | }, values)
467 | }
468 | };
469 |
470 | /* */
471 |
472 | function bind (el, binding, vnode) {
473 | if (!assert(el, vnode)) { return }
474 |
475 | t(el, binding, vnode);
476 | }
477 |
478 | function update (el, binding, vnode, oldVNode) {
479 | if (!assert(el, vnode)) { return }
480 |
481 | var i18n = vnode.context.$i18n;
482 | if (localeEqual(el, vnode) &&
483 | (looseEqual(binding.value, binding.oldValue) &&
484 | looseEqual(el._localeMessage, i18n.getLocaleMessage(i18n.locale)))) { return }
485 |
486 | t(el, binding, vnode);
487 | }
488 |
489 | function unbind (el, binding, vnode, oldVNode) {
490 | var vm = vnode.context;
491 | if (!vm) {
492 | warn('Vue instance does not exists in VNode context');
493 | return
494 | }
495 |
496 | var i18n = vnode.context.$i18n || {};
497 | if (!binding.modifiers.preserve && !i18n.preserveDirectiveContent) {
498 | el.textContent = '';
499 | }
500 | el._vt = undefined;
501 | delete el['_vt'];
502 | el._locale = undefined;
503 | delete el['_locale'];
504 | el._localeMessage = undefined;
505 | delete el['_localeMessage'];
506 | }
507 |
508 | function assert (el, vnode) {
509 | var vm = vnode.context;
510 | if (!vm) {
511 | warn('Vue instance does not exists in VNode context');
512 | return false
513 | }
514 |
515 | if (!vm.$i18n) {
516 | warn('VueI18n instance does not exists in Vue instance');
517 | return false
518 | }
519 |
520 | return true
521 | }
522 |
523 | function localeEqual (el, vnode) {
524 | var vm = vnode.context;
525 | return el._locale === vm.$i18n.locale
526 | }
527 |
528 | function t (el, binding, vnode) {
529 | var ref$1, ref$2;
530 |
531 | var value = binding.value;
532 |
533 | var ref = parseValue(value);
534 | var path = ref.path;
535 | var locale = ref.locale;
536 | var args = ref.args;
537 | var choice = ref.choice;
538 | if (!path && !locale && !args) {
539 | warn('value type not supported');
540 | return
541 | }
542 |
543 | if (!path) {
544 | warn('`path` is required in v-t directive');
545 | return
546 | }
547 |
548 | var vm = vnode.context;
549 | if (choice) {
550 | el._vt = el.textContent = (ref$1 = vm.$i18n).tc.apply(ref$1, [ path, choice ].concat( makeParams(locale, args) ));
551 | } else {
552 | el._vt = el.textContent = (ref$2 = vm.$i18n).t.apply(ref$2, [ path ].concat( makeParams(locale, args) ));
553 | }
554 | el._locale = vm.$i18n.locale;
555 | el._localeMessage = vm.$i18n.getLocaleMessage(vm.$i18n.locale);
556 | }
557 |
558 | function parseValue (value) {
559 | var path;
560 | var locale;
561 | var args;
562 | var choice;
563 |
564 | if (typeof value === 'string') {
565 | path = value;
566 | } else if (isPlainObject(value)) {
567 | path = value.path;
568 | locale = value.locale;
569 | args = value.args;
570 | choice = value.choice;
571 | }
572 |
573 | return { path: path, locale: locale, args: args, choice: choice }
574 | }
575 |
576 | function makeParams (locale, args) {
577 | var params = [];
578 |
579 | locale && params.push(locale);
580 | if (args && (Array.isArray(args) || isPlainObject(args))) {
581 | params.push(args);
582 | }
583 |
584 | return params
585 | }
586 |
587 | var Vue;
588 |
589 | function install (_Vue) {
590 | /* istanbul ignore if */
591 | if (install.installed && _Vue === Vue) {
592 | warn('already installed.');
593 | return
594 | }
595 | install.installed = true;
596 |
597 | Vue = _Vue;
598 |
599 | var version = (Vue.version && Number(Vue.version.split('.')[0])) || -1;
600 | /* istanbul ignore if */
601 | if (version < 2) {
602 | warn(("vue-i18n (" + (install.version) + ") need to use Vue 2.0 or later (Vue: " + (Vue.version) + ")."));
603 | return
604 | }
605 |
606 | extend(Vue);
607 | Vue.mixin(mixin);
608 | Vue.directive('t', { bind: bind, update: update, unbind: unbind });
609 | Vue.component(interpolationComponent.name, interpolationComponent);
610 | Vue.component(numberComponent.name, numberComponent);
611 |
612 | // use simple mergeStrategies to prevent i18n instance lose '__proto__'
613 | var strats = Vue.config.optionMergeStrategies;
614 | strats.i18n = function (parentVal, childVal) {
615 | return childVal === undefined
616 | ? parentVal
617 | : childVal
618 | };
619 | }
620 |
621 | /* */
622 |
623 | var BaseFormatter = function BaseFormatter () {
624 | this._caches = Object.create(null);
625 | };
626 |
627 | BaseFormatter.prototype.interpolate = function interpolate (message, values) {
628 | if (!values) {
629 | return [message]
630 | }
631 | var tokens = this._caches[message];
632 | if (!tokens) {
633 | tokens = parse(message);
634 | this._caches[message] = tokens;
635 | }
636 | return compile(tokens, values)
637 | };
638 |
639 |
640 |
641 | var RE_TOKEN_LIST_VALUE = /^(?:\d)+/;
642 | var RE_TOKEN_NAMED_VALUE = /^(?:\w)+/;
643 |
644 | function parse (format) {
645 | var tokens = [];
646 | var position = 0;
647 |
648 | var text = '';
649 | while (position < format.length) {
650 | var char = format[position++];
651 | if (char === '{') {
652 | if (text) {
653 | tokens.push({ type: 'text', value: text });
654 | }
655 |
656 | text = '';
657 | var sub = '';
658 | char = format[position++];
659 | while (char !== undefined && char !== '}') {
660 | sub += char;
661 | char = format[position++];
662 | }
663 | var isClosed = char === '}';
664 |
665 | var type = RE_TOKEN_LIST_VALUE.test(sub)
666 | ? 'list'
667 | : isClosed && RE_TOKEN_NAMED_VALUE.test(sub)
668 | ? 'named'
669 | : 'unknown';
670 | tokens.push({ value: sub, type: type });
671 | } else if (char === '%') {
672 | // when found rails i18n syntax, skip text capture
673 | if (format[(position)] !== '{') {
674 | text += char;
675 | }
676 | } else {
677 | text += char;
678 | }
679 | }
680 |
681 | text && tokens.push({ type: 'text', value: text });
682 |
683 | return tokens
684 | }
685 |
686 | function compile (tokens, values) {
687 | var compiled = [];
688 | var index = 0;
689 |
690 | var mode = Array.isArray(values)
691 | ? 'list'
692 | : isObject(values)
693 | ? 'named'
694 | : 'unknown';
695 | if (mode === 'unknown') { return compiled }
696 |
697 | while (index < tokens.length) {
698 | var token = tokens[index];
699 | switch (token.type) {
700 | case 'text':
701 | compiled.push(token.value);
702 | break
703 | case 'list':
704 | compiled.push(values[parseInt(token.value, 10)]);
705 | break
706 | case 'named':
707 | if (mode === 'named') {
708 | compiled.push((values)[token.value]);
709 | } else {
710 | {
711 | warn(("Type of token '" + (token.type) + "' and format of value '" + mode + "' don't match!"));
712 | }
713 | }
714 | break
715 | case 'unknown':
716 | {
717 | warn("Detect 'unknown' type of token!");
718 | }
719 | break
720 | }
721 | index++;
722 | }
723 |
724 | return compiled
725 | }
726 |
727 | /* */
728 |
729 | /**
730 | * Path parser
731 | * - Inspired:
732 | * Vue.js Path parser
733 | */
734 |
735 | // actions
736 | var APPEND = 0;
737 | var PUSH = 1;
738 | var INC_SUB_PATH_DEPTH = 2;
739 | var PUSH_SUB_PATH = 3;
740 |
741 | // states
742 | var BEFORE_PATH = 0;
743 | var IN_PATH = 1;
744 | var BEFORE_IDENT = 2;
745 | var IN_IDENT = 3;
746 | var IN_SUB_PATH = 4;
747 | var IN_SINGLE_QUOTE = 5;
748 | var IN_DOUBLE_QUOTE = 6;
749 | var AFTER_PATH = 7;
750 | var ERROR = 8;
751 |
752 | var pathStateMachine = [];
753 |
754 | pathStateMachine[BEFORE_PATH] = {
755 | 'ws': [BEFORE_PATH],
756 | 'ident': [IN_IDENT, APPEND],
757 | '[': [IN_SUB_PATH],
758 | 'eof': [AFTER_PATH]
759 | };
760 |
761 | pathStateMachine[IN_PATH] = {
762 | 'ws': [IN_PATH],
763 | '.': [BEFORE_IDENT],
764 | '[': [IN_SUB_PATH],
765 | 'eof': [AFTER_PATH]
766 | };
767 |
768 | pathStateMachine[BEFORE_IDENT] = {
769 | 'ws': [BEFORE_IDENT],
770 | 'ident': [IN_IDENT, APPEND],
771 | '0': [IN_IDENT, APPEND],
772 | 'number': [IN_IDENT, APPEND]
773 | };
774 |
775 | pathStateMachine[IN_IDENT] = {
776 | 'ident': [IN_IDENT, APPEND],
777 | '0': [IN_IDENT, APPEND],
778 | 'number': [IN_IDENT, APPEND],
779 | 'ws': [IN_PATH, PUSH],
780 | '.': [BEFORE_IDENT, PUSH],
781 | '[': [IN_SUB_PATH, PUSH],
782 | 'eof': [AFTER_PATH, PUSH]
783 | };
784 |
785 | pathStateMachine[IN_SUB_PATH] = {
786 | "'": [IN_SINGLE_QUOTE, APPEND],
787 | '"': [IN_DOUBLE_QUOTE, APPEND],
788 | '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH],
789 | ']': [IN_PATH, PUSH_SUB_PATH],
790 | 'eof': ERROR,
791 | 'else': [IN_SUB_PATH, APPEND]
792 | };
793 |
794 | pathStateMachine[IN_SINGLE_QUOTE] = {
795 | "'": [IN_SUB_PATH, APPEND],
796 | 'eof': ERROR,
797 | 'else': [IN_SINGLE_QUOTE, APPEND]
798 | };
799 |
800 | pathStateMachine[IN_DOUBLE_QUOTE] = {
801 | '"': [IN_SUB_PATH, APPEND],
802 | 'eof': ERROR,
803 | 'else': [IN_DOUBLE_QUOTE, APPEND]
804 | };
805 |
806 | /**
807 | * Check if an expression is a literal value.
808 | */
809 |
810 | var literalValueRE = /^\s?(?:true|false|-?[\d.]+|'[^']*'|"[^"]*")\s?$/;
811 | function isLiteral (exp) {
812 | return literalValueRE.test(exp)
813 | }
814 |
815 | /**
816 | * Strip quotes from a string
817 | */
818 |
819 | function stripQuotes (str) {
820 | var a = str.charCodeAt(0);
821 | var b = str.charCodeAt(str.length - 1);
822 | return a === b && (a === 0x22 || a === 0x27)
823 | ? str.slice(1, -1)
824 | : str
825 | }
826 |
827 | /**
828 | * Determine the type of a character in a keypath.
829 | */
830 |
831 | function getPathCharType (ch) {
832 | if (ch === undefined || ch === null) { return 'eof' }
833 |
834 | var code = ch.charCodeAt(0);
835 |
836 | switch (code) {
837 | case 0x5B: // [
838 | case 0x5D: // ]
839 | case 0x2E: // .
840 | case 0x22: // "
841 | case 0x27: // '
842 | return ch
843 |
844 | case 0x5F: // _
845 | case 0x24: // $
846 | case 0x2D: // -
847 | return 'ident'
848 |
849 | case 0x09: // Tab
850 | case 0x0A: // Newline
851 | case 0x0D: // Return
852 | case 0xA0: // No-break space
853 | case 0xFEFF: // Byte Order Mark
854 | case 0x2028: // Line Separator
855 | case 0x2029: // Paragraph Separator
856 | return 'ws'
857 | }
858 |
859 | return 'ident'
860 | }
861 |
862 | /**
863 | * Format a subPath, return its plain form if it is
864 | * a literal string or number. Otherwise prepend the
865 | * dynamic indicator (*).
866 | */
867 |
868 | function formatSubPath (path) {
869 | var trimmed = path.trim();
870 | // invalid leading 0
871 | if (path.charAt(0) === '0' && isNaN(path)) { return false }
872 |
873 | return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed
874 | }
875 |
876 | /**
877 | * Parse a string path into an array of segments
878 | */
879 |
880 | function parse$1 (path) {
881 | var keys = [];
882 | var index = -1;
883 | var mode = BEFORE_PATH;
884 | var subPathDepth = 0;
885 | var c;
886 | var key;
887 | var newChar;
888 | var type;
889 | var transition;
890 | var action;
891 | var typeMap;
892 | var actions = [];
893 |
894 | actions[PUSH] = function () {
895 | if (key !== undefined) {
896 | keys.push(key);
897 | key = undefined;
898 | }
899 | };
900 |
901 | actions[APPEND] = function () {
902 | if (key === undefined) {
903 | key = newChar;
904 | } else {
905 | key += newChar;
906 | }
907 | };
908 |
909 | actions[INC_SUB_PATH_DEPTH] = function () {
910 | actions[APPEND]();
911 | subPathDepth++;
912 | };
913 |
914 | actions[PUSH_SUB_PATH] = function () {
915 | if (subPathDepth > 0) {
916 | subPathDepth--;
917 | mode = IN_SUB_PATH;
918 | actions[APPEND]();
919 | } else {
920 | subPathDepth = 0;
921 | key = formatSubPath(key);
922 | if (key === false) {
923 | return false
924 | } else {
925 | actions[PUSH]();
926 | }
927 | }
928 | };
929 |
930 | function maybeUnescapeQuote () {
931 | var nextChar = path[index + 1];
932 | if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
933 | (mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
934 | index++;
935 | newChar = '\\' + nextChar;
936 | actions[APPEND]();
937 | return true
938 | }
939 | }
940 |
941 | while (mode !== null) {
942 | index++;
943 | c = path[index];
944 |
945 | if (c === '\\' && maybeUnescapeQuote()) {
946 | continue
947 | }
948 |
949 | type = getPathCharType(c);
950 | typeMap = pathStateMachine[mode];
951 | transition = typeMap[type] || typeMap['else'] || ERROR;
952 |
953 | if (transition === ERROR) {
954 | return // parse error
955 | }
956 |
957 | mode = transition[0];
958 | action = actions[transition[1]];
959 | if (action) {
960 | newChar = transition[2];
961 | newChar = newChar === undefined
962 | ? c
963 | : newChar;
964 | if (action() === false) {
965 | return
966 | }
967 | }
968 |
969 | if (mode === AFTER_PATH) {
970 | return keys
971 | }
972 | }
973 | }
974 |
975 |
976 |
977 |
978 |
979 | var I18nPath = function I18nPath () {
980 | this._cache = Object.create(null);
981 | };
982 |
983 | /**
984 | * External parse that check for a cache hit first
985 | */
986 | I18nPath.prototype.parsePath = function parsePath (path) {
987 | var hit = this._cache[path];
988 | if (!hit) {
989 | hit = parse$1(path);
990 | if (hit) {
991 | this._cache[path] = hit;
992 | }
993 | }
994 | return hit || []
995 | };
996 |
997 | /**
998 | * Get path value from path string
999 | */
1000 | I18nPath.prototype.getPathValue = function getPathValue (obj, path) {
1001 | if (!isObject(obj)) { return null }
1002 |
1003 | var paths = this.parsePath(path);
1004 | if (paths.length === 0) {
1005 | return null
1006 | } else {
1007 | var length = paths.length;
1008 | var last = obj;
1009 | var i = 0;
1010 | while (i < length) {
1011 | var value = last[paths[i]];
1012 | if (value === undefined) {
1013 | return null
1014 | }
1015 | last = value;
1016 | i++;
1017 | }
1018 |
1019 | return last
1020 | }
1021 | };
1022 |
1023 | /* */
1024 |
1025 |
1026 |
1027 | var linkKeyMatcher = /(?:@(?:\.[a-z]+)?:(?:[\w\-_|.]+|\([\w\-_|.]+\)))/g;
1028 | var linkKeyPrefixMatcher = /^@(?:\.([a-z]+))?:/;
1029 | var bracketsMatcher = /[()]/g;
1030 | var formatters = {
1031 | 'upper': function (str) { return str.toLocaleUpperCase(); },
1032 | 'lower': function (str) { return str.toLocaleLowerCase(); }
1033 | };
1034 |
1035 | var defaultFormatter = new BaseFormatter();
1036 |
1037 | var VueI18n = function VueI18n (options) {
1038 | var this$1 = this;
1039 | if ( options === void 0 ) options = {};
1040 |
1041 | // Auto install if it is not done yet and `window` has `Vue`.
1042 | // To allow users to avoid auto-installation in some cases,
1043 | // this code should be placed here. See #290
1044 | /* istanbul ignore if */
1045 | if (!Vue && typeof window !== 'undefined' && window.Vue) {
1046 | install(window.Vue);
1047 | }
1048 |
1049 | var locale = options.locale || 'en-US';
1050 | var fallbackLocale = options.fallbackLocale || 'en-US';
1051 | var messages = options.messages || {};
1052 | var dateTimeFormats = options.dateTimeFormats || {};
1053 | var numberFormats = options.numberFormats || {};
1054 |
1055 | this._vm = null;
1056 | this._formatter = options.formatter || defaultFormatter;
1057 | this._missing = options.missing || null;
1058 | this._root = options.root || null;
1059 | this._sync = options.sync === undefined ? true : !!options.sync;
1060 | this._fallbackRoot = options.fallbackRoot === undefined
1061 | ? true
1062 | : !!options.fallbackRoot;
1063 | this._silentTranslationWarn = options.silentTranslationWarn === undefined
1064 | ? false
1065 | : !!options.silentTranslationWarn;
1066 | this._silentFallbackWarn = options.silentFallbackWarn === undefined
1067 | ? false
1068 | : !!options.silentFallbackWarn;
1069 | this._dateTimeFormatters = {};
1070 | this._numberFormatters = {};
1071 | this._path = new I18nPath();
1072 | this._dataListeners = [];
1073 | this._preserveDirectiveContent = options.preserveDirectiveContent === undefined
1074 | ? false
1075 | : !!options.preserveDirectiveContent;
1076 | this.pluralizationRules = options.pluralizationRules || {};
1077 |
1078 | this._exist = function (message, key) {
1079 | if (!message || !key) { return false }
1080 | if (!isNull(this$1._path.getPathValue(message, key))) { return true }
1081 | // fallback for flat key
1082 | if (message[key]) { return true }
1083 | return false
1084 | };
1085 |
1086 | this._initVM({
1087 | locale: locale,
1088 | fallbackLocale: fallbackLocale,
1089 | messages: messages,
1090 | dateTimeFormats: dateTimeFormats,
1091 | numberFormats: numberFormats
1092 | });
1093 | };
1094 |
1095 | var prototypeAccessors = { vm: { configurable: true },messages: { configurable: true },dateTimeFormats: { configurable: true },numberFormats: { configurable: true },availableLocales: { configurable: true },locale: { configurable: true },fallbackLocale: { configurable: true },missing: { configurable: true },formatter: { configurable: true },silentTranslationWarn: { configurable: true },silentFallbackWarn: { configurable: true },preserveDirectiveContent: { configurable: true } };
1096 |
1097 | VueI18n.prototype._initVM = function _initVM (data) {
1098 | var silent = Vue.config.silent;
1099 | Vue.config.silent = true;
1100 | this._vm = new Vue({ data: data });
1101 | Vue.config.silent = silent;
1102 | };
1103 |
1104 | VueI18n.prototype.destroyVM = function destroyVM () {
1105 | this._vm.$destroy();
1106 | };
1107 |
1108 | VueI18n.prototype.subscribeDataChanging = function subscribeDataChanging (vm) {
1109 | this._dataListeners.push(vm);
1110 | };
1111 |
1112 | VueI18n.prototype.unsubscribeDataChanging = function unsubscribeDataChanging (vm) {
1113 | remove(this._dataListeners, vm);
1114 | };
1115 |
1116 | VueI18n.prototype.watchI18nData = function watchI18nData () {
1117 | var self = this;
1118 | return this._vm.$watch('$data', function () {
1119 | var i = self._dataListeners.length;
1120 | while (i--) {
1121 | Vue.nextTick(function () {
1122 | self._dataListeners[i] && self._dataListeners[i].$forceUpdate();
1123 | });
1124 | }
1125 | }, { deep: true })
1126 | };
1127 |
1128 | VueI18n.prototype.watchLocale = function watchLocale () {
1129 | /* istanbul ignore if */
1130 | if (!this._sync || !this._root) { return null }
1131 | var target = this._vm;
1132 | return this._root.$i18n.vm.$watch('locale', function (val) {
1133 | target.$set(target, 'locale', val);
1134 | target.$forceUpdate();
1135 | }, { immediate: true })
1136 | };
1137 |
1138 | prototypeAccessors.vm.get = function () { return this._vm };
1139 |
1140 | prototypeAccessors.messages.get = function () { return looseClone(this._getMessages()) };
1141 | prototypeAccessors.dateTimeFormats.get = function () { return looseClone(this._getDateTimeFormats()) };
1142 | prototypeAccessors.numberFormats.get = function () { return looseClone(this._getNumberFormats()) };
1143 | prototypeAccessors.availableLocales.get = function () { return Object.keys(this.messages).sort() };
1144 |
1145 | prototypeAccessors.locale.get = function () { return this._vm.locale };
1146 | prototypeAccessors.locale.set = function (locale) {
1147 | this._vm.$set(this._vm, 'locale', locale);
1148 | };
1149 |
1150 | prototypeAccessors.fallbackLocale.get = function () { return this._vm.fallbackLocale };
1151 | prototypeAccessors.fallbackLocale.set = function (locale) {
1152 | this._vm.$set(this._vm, 'fallbackLocale', locale);
1153 | };
1154 |
1155 | prototypeAccessors.missing.get = function () { return this._missing };
1156 | prototypeAccessors.missing.set = function (handler) { this._missing = handler; };
1157 |
1158 | prototypeAccessors.formatter.get = function () { return this._formatter };
1159 | prototypeAccessors.formatter.set = function (formatter) { this._formatter = formatter; };
1160 |
1161 | prototypeAccessors.silentTranslationWarn.get = function () { return this._silentTranslationWarn };
1162 | prototypeAccessors.silentTranslationWarn.set = function (silent) { this._silentTranslationWarn = silent; };
1163 |
1164 | prototypeAccessors.silentFallbackWarn.get = function () { return this._silentFallbackWarn };
1165 | prototypeAccessors.silentFallbackWarn.set = function (silent) { this._silentFallbackWarn = silent; };
1166 |
1167 | prototypeAccessors.preserveDirectiveContent.get = function () { return this._preserveDirectiveContent };
1168 | prototypeAccessors.preserveDirectiveContent.set = function (preserve) { this._preserveDirectiveContent = preserve; };
1169 |
1170 | VueI18n.prototype._getMessages = function _getMessages () { return this._vm.messages };
1171 | VueI18n.prototype._getDateTimeFormats = function _getDateTimeFormats () { return this._vm.dateTimeFormats };
1172 | VueI18n.prototype._getNumberFormats = function _getNumberFormats () { return this._vm.numberFormats };
1173 |
1174 | VueI18n.prototype._warnDefault = function _warnDefault (locale, key, result, vm, values) {
1175 | if (!isNull(result)) { return result }
1176 | if (this._missing) {
1177 | var missingRet = this._missing.apply(null, [locale, key, vm, values]);
1178 | if (typeof missingRet === 'string') {
1179 | return missingRet
1180 | }
1181 | } else {
1182 | if (!this._silentTranslationWarn) {
1183 | warn(
1184 | "Cannot translate the value of keypath '" + key + "'. " +
1185 | 'Use the value of keypath as default.'
1186 | );
1187 | }
1188 | }
1189 | return key
1190 | };
1191 |
1192 | VueI18n.prototype._isFallbackRoot = function _isFallbackRoot (val) {
1193 | return !val && !isNull(this._root) && this._fallbackRoot
1194 | };
1195 |
1196 | VueI18n.prototype._isSilentFallback = function _isSilentFallback (locale) {
1197 | return this._silentFallbackWarn && (this._isFallbackRoot() || locale !== this.fallbackLocale)
1198 | };
1199 |
1200 | VueI18n.prototype._interpolate = function _interpolate (
1201 | locale,
1202 | message,
1203 | key,
1204 | host,
1205 | interpolateMode,
1206 | values,
1207 | visitedLinkStack
1208 | ) {
1209 | if (!message) { return null }
1210 |
1211 | var pathRet = this._path.getPathValue(message, key);
1212 | if (Array.isArray(pathRet) || isPlainObject(pathRet)) { return pathRet }
1213 |
1214 | var ret;
1215 | if (isNull(pathRet)) {
1216 | /* istanbul ignore else */
1217 | if (isPlainObject(message)) {
1218 | ret = message[key];
1219 | if (typeof ret !== 'string') {
1220 | if (!this._silentTranslationWarn && !this._isSilentFallback(locale)) {
1221 | warn(("Value of key '" + key + "' is not a string!"));
1222 | }
1223 | return null
1224 | }
1225 | } else {
1226 | return null
1227 | }
1228 | } else {
1229 | /* istanbul ignore else */
1230 | if (typeof pathRet === 'string') {
1231 | ret = pathRet;
1232 | } else {
1233 | if (!this._silentTranslationWarn && !this._isSilentFallback(locale)) {
1234 | warn(("Value of key '" + key + "' is not a string!"));
1235 | }
1236 | return null
1237 | }
1238 | }
1239 |
1240 | // Check for the existence of links within the translated string
1241 | if (ret.indexOf('@:') >= 0 || ret.indexOf('@.') >= 0) {
1242 | ret = this._link(locale, message, ret, host, 'raw', values, visitedLinkStack);
1243 | }
1244 |
1245 | return this._render(ret, interpolateMode, values, key)
1246 | };
1247 |
1248 | VueI18n.prototype._link = function _link (
1249 | locale,
1250 | message,
1251 | str,
1252 | host,
1253 | interpolateMode,
1254 | values,
1255 | visitedLinkStack
1256 | ) {
1257 | var ret = str;
1258 |
1259 | // Match all the links within the local
1260 | // We are going to replace each of
1261 | // them with its translation
1262 | var matches = ret.match(linkKeyMatcher);
1263 | for (var idx in matches) {
1264 | // ie compatible: filter custom array
1265 | // prototype method
1266 | if (!matches.hasOwnProperty(idx)) {
1267 | continue
1268 | }
1269 | var link = matches[idx];
1270 | var linkKeyPrefixMatches = link.match(linkKeyPrefixMatcher);
1271 | var linkPrefix = linkKeyPrefixMatches[0];
1272 | var formatterName = linkKeyPrefixMatches[1];
1273 |
1274 | // Remove the leading @:, @.case: and the brackets
1275 | var linkPlaceholder = link.replace(linkPrefix, '').replace(bracketsMatcher, '');
1276 |
1277 | if (visitedLinkStack.includes(linkPlaceholder)) {
1278 | {
1279 | warn(("Circular reference found. \"" + link + "\" is already visited in the chain of " + (visitedLinkStack.reverse().join(' <- '))));
1280 | }
1281 | return ret
1282 | }
1283 | visitedLinkStack.push(linkPlaceholder);
1284 |
1285 | // Translate the link
1286 | var translated = this._interpolate(
1287 | locale, message, linkPlaceholder, host,
1288 | interpolateMode === 'raw' ? 'string' : interpolateMode,
1289 | interpolateMode === 'raw' ? undefined : values,
1290 | visitedLinkStack
1291 | );
1292 |
1293 | if (this._isFallbackRoot(translated)) {
1294 | if (!this._silentTranslationWarn) {
1295 | warn(("Fall back to translate the link placeholder '" + linkPlaceholder + "' with root locale."));
1296 | }
1297 | /* istanbul ignore if */
1298 | if (!this._root) { throw Error('unexpected error') }
1299 | var root = this._root.$i18n;
1300 | translated = root._translate(
1301 | root._getMessages(), root.locale, root.fallbackLocale,
1302 | linkPlaceholder, host, interpolateMode, values
1303 | );
1304 | }
1305 | translated = this._warnDefault(
1306 | locale, linkPlaceholder, translated, host,
1307 | Array.isArray(values) ? values : [values]
1308 | );
1309 | if (formatters.hasOwnProperty(formatterName)) {
1310 | translated = formatters[formatterName](translated);
1311 | }
1312 |
1313 | visitedLinkStack.pop();
1314 |
1315 | // Replace the link with the translated
1316 | ret = !translated ? ret : ret.replace(link, translated);
1317 | }
1318 |
1319 | return ret
1320 | };
1321 |
1322 | VueI18n.prototype._render = function _render (message, interpolateMode, values, path) {
1323 | var ret = this._formatter.interpolate(message, values, path);
1324 |
1325 | // If the custom formatter refuses to work - apply the default one
1326 | if (!ret) {
1327 | ret = defaultFormatter.interpolate(message, values, path);
1328 | }
1329 |
1330 | // if interpolateMode is **not** 'string' ('row'),
1331 | // return the compiled data (e.g. ['foo', VNode, 'bar']) with formatter
1332 | return interpolateMode === 'string' ? ret.join('') : ret
1333 | };
1334 |
1335 | VueI18n.prototype._translate = function _translate (
1336 | messages,
1337 | locale,
1338 | fallback,
1339 | key,
1340 | host,
1341 | interpolateMode,
1342 | args
1343 | ) {
1344 | var res =
1345 | this._interpolate(locale, messages[locale], key, host, interpolateMode, args, [key]);
1346 | if (!isNull(res)) { return res }
1347 |
1348 | res = this._interpolate(fallback, messages[fallback], key, host, interpolateMode, args, [key]);
1349 | if (!isNull(res)) {
1350 | if (!this._silentTranslationWarn && !this._silentFallbackWarn) {
1351 | warn(("Fall back to translate the keypath '" + key + "' with '" + fallback + "' locale."));
1352 | }
1353 | return res
1354 | } else {
1355 | return null
1356 | }
1357 | };
1358 |
1359 | VueI18n.prototype._t = function _t (key, _locale, messages, host) {
1360 | var ref;
1361 |
1362 | var values = [], len = arguments.length - 4;
1363 | while ( len-- > 0 ) values[ len ] = arguments[ len + 4 ];
1364 | if (!key) { return '' }
1365 |
1366 | var parsedArgs = parseArgs.apply(void 0, values);
1367 | var locale = parsedArgs.locale || _locale;
1368 |
1369 | var ret = this._translate(
1370 | messages, locale, this.fallbackLocale, key,
1371 | host, 'string', parsedArgs.params
1372 | );
1373 | if (this._isFallbackRoot(ret)) {
1374 | if (!this._silentTranslationWarn && !this._silentFallbackWarn) {
1375 | warn(("Fall back to translate the keypath '" + key + "' with root locale."));
1376 | }
1377 | /* istanbul ignore if */
1378 | if (!this._root) { throw Error('unexpected error') }
1379 | return (ref = this._root).$t.apply(ref, [ key ].concat( values ))
1380 | } else {
1381 | return this._warnDefault(locale, key, ret, host, values)
1382 | }
1383 | };
1384 |
1385 | VueI18n.prototype.t = function t (key) {
1386 | var ref;
1387 |
1388 | var values = [], len = arguments.length - 1;
1389 | while ( len-- > 0 ) values[ len ] = arguments[ len + 1 ];
1390 | return (ref = this)._t.apply(ref, [ key, this.locale, this._getMessages(), null ].concat( values ))
1391 | };
1392 |
1393 | VueI18n.prototype._i = function _i (key, locale, messages, host, values) {
1394 | var ret =
1395 | this._translate(messages, locale, this.fallbackLocale, key, host, 'raw', values);
1396 | if (this._isFallbackRoot(ret)) {
1397 | if (!this._silentTranslationWarn) {
1398 | warn(("Fall back to interpolate the keypath '" + key + "' with root locale."));
1399 | }
1400 | if (!this._root) { throw Error('unexpected error') }
1401 | return this._root.$i18n.i(key, locale, values)
1402 | } else {
1403 | return this._warnDefault(locale, key, ret, host, [values])
1404 | }
1405 | };
1406 |
1407 | VueI18n.prototype.i = function i (key, locale, values) {
1408 | /* istanbul ignore if */
1409 | if (!key) { return '' }
1410 |
1411 | if (typeof locale !== 'string') {
1412 | locale = this.locale;
1413 | }
1414 |
1415 | return this._i(key, locale, this._getMessages(), null, values)
1416 | };
1417 |
1418 | VueI18n.prototype._tc = function _tc (
1419 | key,
1420 | _locale,
1421 | messages,
1422 | host,
1423 | choice
1424 | ) {
1425 | var ref;
1426 |
1427 | var values = [], len = arguments.length - 5;
1428 | while ( len-- > 0 ) values[ len ] = arguments[ len + 5 ];
1429 | if (!key) { return '' }
1430 | if (choice === undefined) {
1431 | choice = 1;
1432 | }
1433 |
1434 | var predefined = { 'count': choice, 'n': choice };
1435 | var parsedArgs = parseArgs.apply(void 0, values);
1436 | parsedArgs.params = Object.assign(predefined, parsedArgs.params);
1437 | values = parsedArgs.locale === null ? [parsedArgs.params] : [parsedArgs.locale, parsedArgs.params];
1438 | return this.fetchChoice((ref = this)._t.apply(ref, [ key, _locale, messages, host ].concat( values )), choice)
1439 | };
1440 |
1441 | VueI18n.prototype.fetchChoice = function fetchChoice (message, choice) {
1442 | /* istanbul ignore if */
1443 | if (!message && typeof message !== 'string') { return null }
1444 | var choices = message.split('|');
1445 |
1446 | choice = this.getChoiceIndex(choice, choices.length);
1447 | if (!choices[choice]) { return message }
1448 | return choices[choice].trim()
1449 | };
1450 |
1451 | /**
1452 | * @param choice {number} a choice index given by the input to $tc: `$tc('path.to.rule', choiceIndex)`
1453 | * @param choicesLength {number} an overall amount of available choices
1454 | * @returns a final choice index
1455 | */
1456 | VueI18n.prototype.getChoiceIndex = function getChoiceIndex (choice, choicesLength) {
1457 | // Default (old) getChoiceIndex implementation - english-compatible
1458 | var defaultImpl = function (_choice, _choicesLength) {
1459 | _choice = Math.abs(_choice);
1460 |
1461 | if (_choicesLength === 2) {
1462 | return _choice
1463 | ? _choice > 1
1464 | ? 1
1465 | : 0
1466 | : 1
1467 | }
1468 |
1469 | return _choice ? Math.min(_choice, 2) : 0
1470 | };
1471 |
1472 | if (this.locale in this.pluralizationRules) {
1473 | return this.pluralizationRules[this.locale].apply(this, [choice, choicesLength])
1474 | } else {
1475 | return defaultImpl(choice, choicesLength)
1476 | }
1477 | };
1478 |
1479 | VueI18n.prototype.tc = function tc (key, choice) {
1480 | var ref;
1481 |
1482 | var values = [], len = arguments.length - 2;
1483 | while ( len-- > 0 ) values[ len ] = arguments[ len + 2 ];
1484 | return (ref = this)._tc.apply(ref, [ key, this.locale, this._getMessages(), null, choice ].concat( values ))
1485 | };
1486 |
1487 | VueI18n.prototype._te = function _te (key, locale, messages) {
1488 | var args = [], len = arguments.length - 3;
1489 | while ( len-- > 0 ) args[ len ] = arguments[ len + 3 ];
1490 |
1491 | var _locale = parseArgs.apply(void 0, args).locale || locale;
1492 | return this._exist(messages[_locale], key)
1493 | };
1494 |
1495 | VueI18n.prototype.te = function te (key, locale) {
1496 | return this._te(key, this.locale, this._getMessages(), locale)
1497 | };
1498 |
1499 | VueI18n.prototype.getLocaleMessage = function getLocaleMessage (locale) {
1500 | return looseClone(this._vm.messages[locale] || {})
1501 | };
1502 |
1503 | VueI18n.prototype.setLocaleMessage = function setLocaleMessage (locale, message) {
1504 | this._vm.$set(this._vm.messages, locale, message);
1505 | };
1506 |
1507 | VueI18n.prototype.mergeLocaleMessage = function mergeLocaleMessage (locale, message) {
1508 | this._vm.$set(this._vm.messages, locale, merge(this._vm.messages[locale] || {}, message));
1509 | };
1510 |
1511 | VueI18n.prototype.getDateTimeFormat = function getDateTimeFormat (locale) {
1512 | return looseClone(this._vm.dateTimeFormats[locale] || {})
1513 | };
1514 |
1515 | VueI18n.prototype.setDateTimeFormat = function setDateTimeFormat (locale, format) {
1516 | this._vm.$set(this._vm.dateTimeFormats, locale, format);
1517 | };
1518 |
1519 | VueI18n.prototype.mergeDateTimeFormat = function mergeDateTimeFormat (locale, format) {
1520 | this._vm.$set(this._vm.dateTimeFormats, locale, merge(this._vm.dateTimeFormats[locale] || {}, format));
1521 | };
1522 |
1523 | VueI18n.prototype._localizeDateTime = function _localizeDateTime (
1524 | value,
1525 | locale,
1526 | fallback,
1527 | dateTimeFormats,
1528 | key
1529 | ) {
1530 | var _locale = locale;
1531 | var formats = dateTimeFormats[_locale];
1532 |
1533 | // fallback locale
1534 | if (isNull(formats) || isNull(formats[key])) {
1535 | if (!this._silentTranslationWarn) {
1536 | warn(("Fall back to '" + fallback + "' datetime formats from '" + locale + " datetime formats."));
1537 | }
1538 | _locale = fallback;
1539 | formats = dateTimeFormats[_locale];
1540 | }
1541 |
1542 | if (isNull(formats) || isNull(formats[key])) {
1543 | return null
1544 | } else {
1545 | var format = formats[key];
1546 | var id = _locale + "__" + key;
1547 | var formatter = this._dateTimeFormatters[id];
1548 | if (!formatter) {
1549 | formatter = this._dateTimeFormatters[id] = new Intl.DateTimeFormat(_locale, format);
1550 | }
1551 | return formatter.format(value)
1552 | }
1553 | };
1554 |
1555 | VueI18n.prototype._d = function _d (value, locale, key) {
1556 | /* istanbul ignore if */
1557 | if (!VueI18n.availabilities.dateTimeFormat) {
1558 | warn('Cannot format a Date value due to not supported Intl.DateTimeFormat.');
1559 | return ''
1560 | }
1561 |
1562 | if (!key) {
1563 | return new Intl.DateTimeFormat(locale).format(value)
1564 | }
1565 |
1566 | var ret =
1567 | this._localizeDateTime(value, locale, this.fallbackLocale, this._getDateTimeFormats(), key);
1568 | if (this._isFallbackRoot(ret)) {
1569 | if (!this._silentTranslationWarn) {
1570 | warn(("Fall back to datetime localization of root: key '" + key + "' ."));
1571 | }
1572 | /* istanbul ignore if */
1573 | if (!this._root) { throw Error('unexpected error') }
1574 | return this._root.$i18n.d(value, key, locale)
1575 | } else {
1576 | return ret || ''
1577 | }
1578 | };
1579 |
1580 | VueI18n.prototype.d = function d (value) {
1581 | var args = [], len = arguments.length - 1;
1582 | while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
1583 |
1584 | var locale = this.locale;
1585 | var key = null;
1586 |
1587 | if (args.length === 1) {
1588 | if (typeof args[0] === 'string') {
1589 | key = args[0];
1590 | } else if (isObject(args[0])) {
1591 | if (args[0].locale) {
1592 | locale = args[0].locale;
1593 | }
1594 | if (args[0].key) {
1595 | key = args[0].key;
1596 | }
1597 | }
1598 | } else if (args.length === 2) {
1599 | if (typeof args[0] === 'string') {
1600 | key = args[0];
1601 | }
1602 | if (typeof args[1] === 'string') {
1603 | locale = args[1];
1604 | }
1605 | }
1606 |
1607 | return this._d(value, locale, key)
1608 | };
1609 |
1610 | VueI18n.prototype.getNumberFormat = function getNumberFormat (locale) {
1611 | return looseClone(this._vm.numberFormats[locale] || {})
1612 | };
1613 |
1614 | VueI18n.prototype.setNumberFormat = function setNumberFormat (locale, format) {
1615 | this._vm.$set(this._vm.numberFormats, locale, format);
1616 | };
1617 |
1618 | VueI18n.prototype.mergeNumberFormat = function mergeNumberFormat (locale, format) {
1619 | this._vm.$set(this._vm.numberFormats, locale, merge(this._vm.numberFormats[locale] || {}, format));
1620 | };
1621 |
1622 | VueI18n.prototype._getNumberFormatter = function _getNumberFormatter (
1623 | value,
1624 | locale,
1625 | fallback,
1626 | numberFormats,
1627 | key,
1628 | options
1629 | ) {
1630 | var _locale = locale;
1631 | var formats = numberFormats[_locale];
1632 |
1633 | // fallback locale
1634 | if (isNull(formats) || isNull(formats[key])) {
1635 | if (!this._silentTranslationWarn) {
1636 | warn(("Fall back to '" + fallback + "' number formats from '" + locale + " number formats."));
1637 | }
1638 | _locale = fallback;
1639 | formats = numberFormats[_locale];
1640 | }
1641 |
1642 | if (isNull(formats) || isNull(formats[key])) {
1643 | return null
1644 | } else {
1645 | var format = formats[key];
1646 |
1647 | var formatter;
1648 | if (options) {
1649 | // If options specified - create one time number formatter
1650 | formatter = new Intl.NumberFormat(_locale, Object.assign({}, format, options));
1651 | } else {
1652 | var id = _locale + "__" + key;
1653 | formatter = this._numberFormatters[id];
1654 | if (!formatter) {
1655 | formatter = this._numberFormatters[id] = new Intl.NumberFormat(_locale, format);
1656 | }
1657 | }
1658 | return formatter
1659 | }
1660 | };
1661 |
1662 | VueI18n.prototype._n = function _n (value, locale, key, options) {
1663 | /* istanbul ignore if */
1664 | if (!VueI18n.availabilities.numberFormat) {
1665 | {
1666 | warn('Cannot format a Number value due to not supported Intl.NumberFormat.');
1667 | }
1668 | return ''
1669 | }
1670 |
1671 | if (!key) {
1672 | var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
1673 | return nf.format(value)
1674 | }
1675 |
1676 | var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
1677 | var ret = formatter && formatter.format(value);
1678 | if (this._isFallbackRoot(ret)) {
1679 | if (!this._silentTranslationWarn) {
1680 | warn(("Fall back to number localization of root: key '" + key + "' ."));
1681 | }
1682 | /* istanbul ignore if */
1683 | if (!this._root) { throw Error('unexpected error') }
1684 | return this._root.$i18n.n(value, Object.assign({}, { key: key, locale: locale }, options))
1685 | } else {
1686 | return ret || ''
1687 | }
1688 | };
1689 |
1690 | VueI18n.prototype.n = function n (value) {
1691 | var args = [], len = arguments.length - 1;
1692 | while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
1693 |
1694 | var locale = this.locale;
1695 | var key = null;
1696 | var options = null;
1697 |
1698 | if (args.length === 1) {
1699 | if (typeof args[0] === 'string') {
1700 | key = args[0];
1701 | } else if (isObject(args[0])) {
1702 | if (args[0].locale) {
1703 | locale = args[0].locale;
1704 | }
1705 | if (args[0].key) {
1706 | key = args[0].key;
1707 | }
1708 |
1709 | // Filter out number format options only
1710 | options = Object.keys(args[0]).reduce(function (acc, key) {
1711 | var obj;
1712 |
1713 | if (numberFormatKeys.includes(key)) {
1714 | return Object.assign({}, acc, ( obj = {}, obj[key] = args[0][key], obj ))
1715 | }
1716 | return acc
1717 | }, null);
1718 | }
1719 | } else if (args.length === 2) {
1720 | if (typeof args[0] === 'string') {
1721 | key = args[0];
1722 | }
1723 | if (typeof args[1] === 'string') {
1724 | locale = args[1];
1725 | }
1726 | }
1727 |
1728 | return this._n(value, locale, key, options)
1729 | };
1730 |
1731 | VueI18n.prototype._ntp = function _ntp (value, locale, key, options) {
1732 | /* istanbul ignore if */
1733 | if (!VueI18n.availabilities.numberFormat) {
1734 | {
1735 | warn('Cannot format to parts a Number value due to not supported Intl.NumberFormat.');
1736 | }
1737 | return []
1738 | }
1739 |
1740 | if (!key) {
1741 | var nf = !options ? new Intl.NumberFormat(locale) : new Intl.NumberFormat(locale, options);
1742 | return nf.formatToParts(value)
1743 | }
1744 |
1745 | var formatter = this._getNumberFormatter(value, locale, this.fallbackLocale, this._getNumberFormats(), key, options);
1746 | var ret = formatter && formatter.formatToParts(value);
1747 | if (this._isFallbackRoot(ret)) {
1748 | if (!this._silentTranslationWarn) {
1749 | warn(("Fall back to format number to parts of root: key '" + key + "' ."));
1750 | }
1751 | /* istanbul ignore if */
1752 | if (!this._root) { throw Error('unexpected error') }
1753 | return this._root.$i18n._ntp(value, locale, key, options)
1754 | } else {
1755 | return ret || []
1756 | }
1757 | };
1758 |
1759 | Object.defineProperties( VueI18n.prototype, prototypeAccessors );
1760 |
1761 | var availabilities;
1762 | // $FlowFixMe
1763 | Object.defineProperty(VueI18n, 'availabilities', {
1764 | get: function get () {
1765 | if (!availabilities) {
1766 | var intlDefined = typeof Intl !== 'undefined';
1767 | availabilities = {
1768 | dateTimeFormat: intlDefined && typeof Intl.DateTimeFormat !== 'undefined',
1769 | numberFormat: intlDefined && typeof Intl.NumberFormat !== 'undefined'
1770 | };
1771 | }
1772 |
1773 | return availabilities
1774 | }
1775 | });
1776 |
1777 | VueI18n.install = install;
1778 | VueI18n.version = '8.10.0';
1779 |
1780 | return VueI18n;
1781 |
1782 | })));
1783 |
--------------------------------------------------------------------------------
/lang/zh.js:
--------------------------------------------------------------------------------
1 | export default {
2 | lang: 'zh',
3 | loading: '加载中...',
4 | title: '广告机',
5 | login: {
6 | button: '扫码录入',
7 | more: '更多'
8 |
9 | },
10 | password: {
11 | placeholder: '请输入设备密码',
12 | change: '修改密码',
13 | oldPswd: '请输入旧密码',
14 | newPswd: '请输入新密码',
15 | submit: '提交',
16 | confirm: '确定',
17 | cancel: '取消'
18 | },
19 | turnPage: {
20 | title: '翻页模式',
21 | last: '最后一副',
22 | first: '第一幅画',
23 | down: '下翻键向下翻页',
24 | // down: '下翻键向下翻页',
25 | up: '上翻键向上翻页'
26 | // up: '上翻键向上翻页'
27 | },
28 | calibration: {
29 | title: '画面校正',
30 | click: '短按',
31 | Inching: '点动运行',
32 | pres: '长按',
33 | moving: '低速运行',
34 |
35 | },
36 | other: {
37 | closing: '关闭中...',
38 | sureConnBLE: '请确保连接了蓝牙',
39 | corretTime: '是否自动校时?',
40 | y: '是',
41 | n: '否',
42 | login: '登录中',
43 | AutoCan: '自动模式可以进入',
44 | settingFail: '设置失败',
45 | offlineOrOther: '设备离线或异常',
46 | outLine: '设备不在线',
47 | setting1: '设置中',
48 | BLEList: '查询蓝牙列表',
49 | BLEConn: '蓝牙列表(点击连接设备)',
50 | BLENone: '暂无蓝牙设备',
51 | search: '搜索中',
52 | reconnection: '重连中',
53 | fail: '连接失败',
54 | checking: '检测中',
55 | corrected: '您当前连接的是此设备',
56 | conning: '连接中,请稍等',
57 |
58 | notStorage: '设备未入库',
59 | sucWait: '连接成功,请稍等',
60 | searchNone: '未搜索到设备/正在使用中',
61 | searchFail: '查询失败,请确保蓝牙可用/定位开启',
62 | stopMode: '急停模式',
63 | back: '手动自动键退出',
64 | save: '确认保存',
65 | toMenu: '返回退出',
66 | move: '键点击运行',
67 | enter: '按',
68 | setItems: '按确认键设置参数',
69 | sysTitle: '系统日期时间设置',
70 | modeTitle: '运行模式设置',
71 | host: '主机',
72 | single: '独立',
73 | test: '自检',
74 | client: '从机',
75 | delayTitle: '同步延时设置',
76 | copyTitle: '参数复制',
77 | copy: '复制中...',
78 | setIdTitle: '主板ID号设置'
79 | },
80 | Manu: {
81 | title: '手动运行模式',
82 | title2: '自动运行模式'
83 | },
84 | pageNum: {
85 | title: '画面幅数设置',
86 | set: '画面总数:',
87 | change: '键改变幅数'
88 | },
89 | showTime: {
90 | all: '全部展示时间设置',
91 | single: '单幅展示时间设置',
92 | No: '第',
93 | set: '幅展示时间:'
94 | },
95 | speedSet: {
96 | title: '运行速度设置',
97 | set: '运行速度为:',
98 | change: '键改变速度'
99 | },
100 | timer: {
101 | title1: '开机定时A设置',
102 | title2: '开机定时B设置',
103 | title3: '关机定时A设置',
104 | title4: '关机定时B设置',
105 | title5: '开灯定时设置',
106 | title6: '关灯定时设置'
107 |
108 | },
109 | stopPic: {
110 | title: '停机画面设置'
111 | },
112 | parameters: {
113 | OV: '过压保护',
114 | OC: '过流保护',
115 | OT: '过温保护',
116 | UV: '欠压保护',
117 | UVT: '欠压保护时间'
118 | },
119 | index: {
120 | auto: '自动',
121 | GPRS: '有线',
122 | BLE: '蓝牙',
123 | Speed: '速度',
124 | modelContent: '您确定要返回扫码页面吗?',
125 | manu: '手动',
126 | client: '从机',
127 | ok: '系统正常',
128 | OV: '过压保护',
129 | OC: '过流保护',
130 | OT: '过温保护 ',
131 | UV: '欠压保护',
132 | F: '连接失败',
133 | host: '主机',
134 | single: '独立',
135 | test: '自检',
136 | setBLEData: [
137 | { id: 1, value: '搜索蓝牙设备' },
138 | { id: 2, value: '断开连接' }
139 | ],
140 | homeData: {
141 | d_l: 0,
142 | d_r: 0,
143 | f_l: 0,
144 | f_r: 0,
145 | h_l: 0,
146 | h_r: 0,
147 | m_l: 0,
148 | m_r: 0,
149 | s_l: 0,
150 | s_r: 0,
151 | y_l: 0,
152 | y_r: 0,
153 | // // 年月日
154 | // year_l: 0,
155 | // year_r: 0,
156 | // month_l: 0,
157 | // month_r: 0,
158 | // day_l: 0,
159 | // day_r: 0,
160 | // // 时分秒
161 | // hour_l: 0,
162 | // hour_r: 0,
163 | // minute_l: 0,
164 | // minute_r: 0,
165 | // second_l: 0,
166 | // second_r: 0,
167 | // 第四行
168 | runStatus: 0, // 运行状态 0自动 7自检 3急停 4或1手动 2 或6 微调 8暂时未用到
169 | // 主从机模式
170 | model: 0, // 0-独立 1-主机,2-从机
171 | // 速度和类型
172 | speed: 0, // 速度为0-9
173 | /* 主板工作状态
174 | 0--系统正常 2--过温保护 3--过流保护 4--过压保护 5--欠压保护) 其它:连接失败 */
175 | workStatus: 6,
176 | type: 1, // 1GPRS 2BLE
177 | // 当前画面
178 | nowPic: 1
179 |
180 | },
181 | // 设置项数据
182 | setItemData: [
183 | { id: 1, value: '画面副数' },
184 | { id: 2, value: '展示时间' },
185 | { id: 3, value: '定时时间' },
186 | { id: 4, value: '运行速度' }
187 | ],
188 | setItemsData1: [
189 | { id: 1, value: '画面副数' },
190 | { id: 2, value: '展示时间' },
191 | { id: 3, value: '定时时间' },
192 | { id: 4, value: '运行速度' }
193 | ],
194 | setItemsData2: [
195 | { id: 5, value: '停机画面' },
196 | { id: 6, value: '系统时间' },
197 | { id: 7, value: '运行模式' },
198 | { id: 8, value: '同步延时' }
199 | ],
200 | setItemsData3: [
201 | { id: 9, value: '参数复制' },
202 | { id: 10, value: '主板ID号' },
203 | { id: 11, value: '其他参数' },
204 | { id: 12, value: '蓝牙设置' }
205 | ],
206 | // 展示时间数据
207 | showTimeData: [
208 | { id: 1, value: '全部展示时间' },
209 | { id: 2, value: '单幅展示时间' }
210 | ],
211 | // 定时设置
212 | timerData: [
213 | { id: 1, value: '开机定时A' },
214 | { id: 2, value: '关机定时A' },
215 | { id: 3, value: '开机定时B' },
216 | { id: 4, value: '关机定时B' },
217 | ],
218 | timerData1: [
219 | { id: 1, value: '开机定时A' },
220 | { id: 2, value: '关机定时A' },
221 | { id: 3, value: '开机定时B' },
222 | { id: 4, value: '关机定时B' },
223 | ],
224 | timerData2: [
225 | { id: 5, value: '开灯定时' },
226 | { id: 6, value: '关灯定时' },
227 | ],
228 | // 其他参数设置数据
229 | otherParametersData: [
230 | {id: 1, value: '过压保护'},
231 | {id: 2, value: '过流保护'},
232 | {id: 3, value: '过温保护'},
233 | {id: 4, value: '欠压保护'}
234 | ],
235 | otherParametersData1: [
236 | {id: 1, value: '过压保护'},
237 | {id: 2, value: '过流保护'},
238 | {id: 3, value: '过温保护'},
239 | {id: 4, value: '欠压保护'}
240 | ],
241 | otherParametersData2: [
242 | {id: 5, value: '欠压保护时间'}
243 | ],
244 | },
245 | toast: '您取消了授权,登录失败',
246 | Toast: {
247 | complete: '请将信息填写完整',
248 | pswdSuc: '密码修改成功',
249 | fillPswd: '请填写密码',
250 | unKnown: '未知错误,请联系管理员',
251 | scanFalse: '扫码失败',
252 | scanFailed: '请确保设备码正确后重新扫码',
253 | systemErr: '系统异常',
254 | NotOpen: '暂未开通',
255 | modifie:'修改失败'
256 | }
257 | }
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App'
3 |
4 | import { http } from '@/utils/request/index.js'
5 | Vue.config.productionTip = false
6 | Vue.prototype.$http = http
7 |
8 | App.mpType = 'app'
9 |
10 | import i18n from './lang/index'
11 | Vue.prototype._i18n = i18n
12 | // import { conn } from './utils/socket/send.js'
13 | // conn()
14 |
15 | const app = new Vue({
16 | i18n,
17 | ...App,
18 |
19 | })
20 | app.$mount()
21 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name" : "AdverPlayer",
3 | "appid" : "__UNI__1E99334",
4 | "description" : "",
5 | "versionName" : "1.0.0",
6 | "versionCode" : "100",
7 | "transformPx" : false,
8 | /* 5+App特有相关 */
9 | "app-plus" : {
10 | "usingComponents" : true,
11 | "nvueCompiler" : "uni-app",
12 | "splashscreen" : {
13 | "alwaysShowBeforeRender" : true,
14 | "waiting" : true,
15 | "autoclose" : true,
16 | "delay" : 0
17 | },
18 | /* 模块配置 */
19 | "modules" : {},
20 | /* 应用发布信息 */
21 | "distribute" : {
22 | /* android打包配置 */
23 | "android" : {
24 | "permissions" : [
25 | "",
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | "",
37 | "",
38 | "",
39 | "",
40 | "",
41 | "",
42 | "",
43 | "",
44 | "",
45 | "",
46 | ""
47 | ]
48 | },
49 | /* ios打包配置 */
50 | "ios" : {},
51 | /* SDK配置 */
52 | "sdkConfigs" : {}
53 | }
54 | },
55 | /* 快应用特有相关 */
56 | "quickapp" : {},
57 | /* 小程序特有相关 */
58 | "mp-weixin" : {
59 | "appid" : "",
60 | "setting" : {
61 | "urlCheck" : false,
62 | "es6" : true,
63 | "postcss" : true,
64 | "minified" : true
65 | },
66 | "usingComponents" : true,
67 | "permission" : {}
68 | },
69 | "mp-alipay" : {
70 | "usingComponents" : true
71 | },
72 | "mp-baidu" : {
73 | "usingComponents" : true
74 | },
75 | "mp-toutiao" : {
76 | "usingComponents" : true
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "AdverPlayer",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "js-md5": {
8 | "version": "0.7.3",
9 | "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
10 | "integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ=="
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "AdverPlayer",
3 | "version": "1.0.0",
4 | "description": "1. 页面命名小驼峰\r 2. request 请求使用全局 ` this.$http `\r 3. 本地储存storage key 必须在‘/utils/storageTypes.js’ 里定义常量,使用时也必须引入常量\r 4. 微信小程序现无法做到*强制登录*,在明确需要token的页面必须使用‘utils/user’ 里 ` checkLogin `,判断是否登录。如必须登录,弹框询问用户是否去登陆,如用户点击确定,则调用‘utils/myUtils.js’ 里 ` redirectLogin ` 函数\r 5. 全局css命名空间` am `",
5 | "main": "main.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "dependencies": {
13 | "js-md5": "^0.7.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "pages": [
3 |
4 | {
5 | "path": "pages/login/login",
6 | "permission": {
7 | "scope.userLocation": {
8 | "desc": "你的位置信息将用于小程序位置接口的效果展示" // 高速公路行驶持续后台定位
9 | }
10 | },
11 | "style": {
12 | "navigationBarTitleText": "低功耗蓝牙"
13 | }
14 | }
15 | ],
16 | "globalStyle": {
17 | "navigationBarTextStyle": "#fff",
18 | "navigationBarTitleText": "低功耗蓝牙",
19 | "navigationBarBackgroundColor": "#1D1D1D",
20 | "backgroundColor": "#F8F8F8"
21 | },
22 | "condition": { //模式配置,仅开发期间生效
23 | "current": 0, //当前激活的模式(list 的索引项)
24 | "list": [{
25 | "name": "", //模式名称
26 | "path": "", //启动页面,必选
27 | "query": "" //启动参数,在页面的onLoad函数里面得到
28 | }]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/pages/login/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 | {{ other.BLEConn }}
14 |
15 |
16 | {{item.name}}
17 |
18 |
19 | {{ other.BLENone }}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
163 |
164 |
185 |
--------------------------------------------------------------------------------
/static/1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/1.gif
--------------------------------------------------------------------------------
/static/images/bg_circle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/bg_circle.png
--------------------------------------------------------------------------------
/static/images/bg_login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/bg_login.png
--------------------------------------------------------------------------------
/static/images/big_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/big_btn.png
--------------------------------------------------------------------------------
/static/images/bottom_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/bottom_btn.png
--------------------------------------------------------------------------------
/static/images/bottom_btn_tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/bottom_btn_tap.png
--------------------------------------------------------------------------------
/static/images/circel_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/circel_btn.png
--------------------------------------------------------------------------------
/static/images/circel_btn_tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/circel_btn_tap.png
--------------------------------------------------------------------------------
/static/images/left-tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/left-tap.png
--------------------------------------------------------------------------------
/static/images/left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/left.png
--------------------------------------------------------------------------------
/static/images/left_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/left_btn.png
--------------------------------------------------------------------------------
/static/images/left_btn_tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/left_btn_tap.png
--------------------------------------------------------------------------------
/static/images/logo/LOGO.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/logo/LOGO.png
--------------------------------------------------------------------------------
/static/images/logo/LOGO1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/logo/LOGO1.png
--------------------------------------------------------------------------------
/static/images/right-tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/right-tap.png
--------------------------------------------------------------------------------
/static/images/right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/right.png
--------------------------------------------------------------------------------
/static/images/right_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/right_btn.png
--------------------------------------------------------------------------------
/static/images/right_btn_tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/right_btn_tap.png
--------------------------------------------------------------------------------
/static/images/tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/tap.png
--------------------------------------------------------------------------------
/static/images/top_btn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/top_btn.png
--------------------------------------------------------------------------------
/static/images/top_btn_tap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/images/top_btn_tap.png
--------------------------------------------------------------------------------
/static/neil-modal/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/menglin1997/BLEConn/dea76b6666dc8d2c61a26d308c4b255b13dc2614/static/neil-modal/logo.png
--------------------------------------------------------------------------------
/uni.scss:
--------------------------------------------------------------------------------
1 | /**
2 | * 这里是uni-app内置的常用样式变量
3 | *
4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
6 | *
7 | */
8 |
9 | /**
10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
11 | *
12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
13 | */
14 |
15 | /* 颜色变量 */
16 |
17 | /* 行为相关颜色 */
18 | $uni-color-primary: #007aff;
19 | $uni-color-success: #4cd964;
20 | $uni-color-warning: #f0ad4e;
21 | $uni-color-error: #dd524d;
22 |
23 | /* 文字基本颜色 */
24 | $uni-text-color:#333;//基本色
25 | $uni-text-color-inverse:#fff;//反色
26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
27 | $uni-text-color-placeholder: #808080;
28 | $uni-text-color-disable:#c0c0c0;
29 |
30 | /* 背景颜色 */
31 | $uni-bg-color:#ffffff;
32 | $uni-bg-color-grey:#f8f8f8;
33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色
34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
35 |
36 | /* 边框颜色 */
37 | $uni-border-color:#c8c7cc;
38 |
39 | /* 尺寸变量 */
40 |
41 | /* 文字尺寸 */
42 | $uni-font-size-sm:24upx;
43 | $uni-font-size-base:28upx;
44 | $uni-font-size-lg:32upx;
45 |
46 | /* 图片尺寸 */
47 | $uni-img-size-sm:40upx;
48 | $uni-img-size-base:52upx;
49 | $uni-img-size-lg:80upx;
50 |
51 | /* Border Radius */
52 | $uni-border-radius-sm: 4upx;
53 | $uni-border-radius-base: 6upx;
54 | $uni-border-radius-lg: 12upx;
55 | $uni-border-radius-circle: 50%;
56 |
57 | /* 水平间距 */
58 | $uni-spacing-row-sm: 10px;
59 | $uni-spacing-row-base: 20upx;
60 | $uni-spacing-row-lg: 30upx;
61 |
62 | /* 垂直间距 */
63 | $uni-spacing-col-sm: 8upx;
64 | $uni-spacing-col-base: 16upx;
65 | $uni-spacing-col-lg: 24upx;
66 |
67 | /* 透明度 */
68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
69 |
70 | /* 文章场景相关 */
71 | $uni-color-title: #2C405A; // 文章标题颜色
72 | $uni-font-size-title:40upx;
73 | $uni-color-subtitle: #555555; // 二级标题颜色
74 | $uni-font-size-subtitle:36upx;
75 | $uni-color-paragraph: #3F536E; // 文章段落颜色
76 | $uni-font-size-paragraph:30upx;
77 |
78 |
79 | $am-active-color: #2dc9ac; // 激活色 绿色
80 | $am-ornamentally-color: #feb374; // 装饰色 橘色
--------------------------------------------------------------------------------
/utils/request/index.js:
--------------------------------------------------------------------------------
1 | import Request from './request'
2 | import {
3 | apiBaseUrl
4 | } from '@/utils/service.js'
5 |
6 | const http = new Request()
7 |
8 | http.setConfig((config) => {
9 | config.baseUrl = apiBaseUrl
10 | config.header = {
11 | ...config.header
12 | }
13 | return config
14 | })
15 |
16 |
17 | http.interceptor.request((config, cancel) => { /* 请求之前拦截器 */
18 | config.header = {
19 | ...config.header,
20 | b: 1
21 | }
22 |
23 | return config
24 | })
25 |
26 | http.interceptor.response((response) => { /* 请求之后拦截器 */
27 | return response
28 | }, (response) => { // 请求错误做点什么
29 | return response
30 | })
31 |
32 | export {
33 | http
34 | }
35 |
--------------------------------------------------------------------------------
/utils/request/readme.md:
--------------------------------------------------------------------------------
1 | **插件使用说明**
2 |
3 | - 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
4 | - 支持全局挂载
5 | - 支持多个全局配置实例
6 | - 支持自定义验证器
7 | - 支持文件上传(如不使用可以删除class里upload 方法)
8 | - 支持` typescript `、` javascript ` 版本(如果不使用ts版本,则可以把luch-request-ts 文件夹删除)
9 | - 下载后把 http-request 文件夹放到项目 utils/ 目录下
10 |
11 |
12 | **Example**
13 | ---
14 | 创建实例
15 |
16 | ``` javascript
17 | const http = new Request();
18 | ```
19 |
20 | 执行` GET `请求
21 |
22 | ``` javascript
23 | http.get('/user/login', {params: {userName: 'name', password: '123456'}}).then(res => {
24 |
25 | }).catch(err => {
26 |
27 | })
28 | // 局部修改配置,局部配置优先级高于全局配置
29 | http.get('/user/login', {
30 | params: {userName: 'name', password: '123456'}, /* 会加在url上 */
31 | header: {}, /* 会覆盖全局header */
32 | dataType: 'json',
33 | responseType: 'text'
34 | }).then(res => {
35 |
36 | }).catch(err => {
37 |
38 | })
39 | ```
40 | 执行` POST `请求
41 |
42 | ``` javascript
43 | http.post('/user/login', {userName: 'name', password: '123456'} ).then(res => {
44 |
45 | }).catch(err => {
46 |
47 | })
48 | // 局部修改配置,局部配置优先级高于全局配置
49 | http.post('/user/login', {userName: 'name', password: '123456'}, {
50 | params: {}, /* 会加在url上 */
51 | header: {}, /* 会覆盖全局header */
52 | dataType: 'json',
53 | responseType: 'text'
54 | }).then(res => {
55 |
56 | }).catch(err => {
57 |
58 | })
59 | ```
60 | 执行` upload `请求
61 |
62 | ``` javascript
63 | http.upload('api/upload/img', {
64 | files: [], // 仅5+App支持
65 | fileType:'image/video/audio', // 仅支付宝小程序,且必填。
66 | filePath: '', // 要上传文件资源的路径。
67 | name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
68 | header: {},
69 | formData: {}, // HTTP 请求中其他额外的 form data
70 | }).then(res => {
71 |
72 | }).catch(err => {
73 |
74 | })
75 | ```
76 | **luch-request API**
77 | --
78 | ``` javascript
79 | http.request({
80 | method: 'POST', // 请求方法必须大写
81 | url: '/user/12345',
82 | data: {
83 | firstName: 'Fred',
84 | lastName: 'Flintstone'
85 | },
86 | params: { // 会拼接到url上
87 | token: '1111'
88 | }
89 | })
90 |
91 | 具体参数说明:[uni.uploadFile](https://uniapp.dcloud.io/api/request/network-file)
92 | http.upload('api/upload/img', {
93 | files: [], // 仅5+App支持
94 | fileType:'image/video/audio', // 仅支付宝小程序,且必填。
95 | filePath: '', // 要上传文件资源的路径。
96 | name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
97 | header: {}, // 如填写,会覆盖全局header
98 | formData: {}, // HTTP 请求中其他额外的 form data
99 | })
100 | ```
101 |
102 |
103 | 请求方法别名 / 实例方法
104 |
105 | ``` javascript
106 | http.request(config)
107 | http.get(url[, config])
108 | http.upload(url[, config])
109 | http.delete(url[, data[, config]])
110 | http.head(url[, data[, config]])
111 | http.post(url[, data[, config]])
112 | http.put(url[, data[, config]])
113 | http.connect(url[, data[, config]])
114 | http.options(url[, data[, config]])
115 | http.trace(url[, data[, config]])
116 | ```
117 |
118 | **全局请求配置**
119 | --
120 | ``` javascript
121 | {
122 | baseUrl: '', /* 全局根路径,需要注意,如果请求的路径为绝对路径,则不会应用baseUrl */
123 | header: {
124 | 'Content-Type': 'application/json;charset=UTF-8'
125 | },
126 | method: 'GET',
127 | dataType: 'json',
128 | responseType: 'text'
129 | }
130 | ```
131 |
132 |
133 | 全局配置修改` setConfig `
134 |
135 | ``` javascript
136 | /**
137 | * @description 修改全局默认配置
138 | * @param {Function}
139 | */
140 | http.setConfig((config) => { /* config 为默认全局配置*/
141 | config.baseUrl = 'http://www.bbb.cn'; /* 根域名 */
142 | config.header = {
143 | a: 1,
144 | b: 2
145 | }
146 | return config
147 | })
148 | ```
149 |
150 | 自定义验证器` validateStatus `
151 |
152 | ``` javascript
153 | /**
154 | * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
155 | * @param { Number } statusCode - 请求响应体statusCode(只读)
156 | * @return { Boolean } 如果为true,则 resolve, 否则 reject
157 | */
158 | http.validateStatus = (statusCode) => { // 默认
159 | return statusCode === 200
160 | }
161 |
162 | // 举个栗子
163 | http.validateStatus = (statusCode) => {
164 | return statusCode >= 200 && statusCode < 300
165 | }
166 | ```
167 |
168 | **拦截器**
169 | --
170 |
171 | 在请求之前拦截
172 |
173 | ``` javascript
174 | /**
175 | * @param { Function} cancel - 取消请求,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行; 不会进入响应拦截器
176 | *
177 | * @param {String} text ['handle cancel'| any] - catch((err) => {}) err.errMsg === 'handle cancel'。非必传,默认'handle cancel'
178 | * @cancel {Object} config - catch((err) => {}) err.config === config; 非必传,默认为request拦截器修改之前的config
179 | * function cancel(text, config) {}
180 | */
181 | http.interceptor.request((config, cancel) => { /* cancel 为函数,如果调用会取消本次请求。需要注意:调用cancel,本次请求的catch仍会执行。必须return config */
182 | config.header = {
183 | ...config.header,
184 | a: 1
185 | }
186 | /*
187 | if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
188 | cancel('token 不存在', config) // 把修改后的config传入,之后响应就可以拿到修改后的config。 如果调用了cancel但是不传修改后的config,则catch((err) => {}) err.config 为request拦截器修改之前的config
189 | }
190 | */
191 | return config;
192 | })
193 | ```
194 |
195 | 在请求之后拦截
196 |
197 | ``` javascript
198 | http.interceptor.response((response) => { /* 对响应成功做点什么 (statusCode === 200),必须return response*/
199 | // if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
200 | // return Promise.reject(response)
201 | // }
202 | console.log(response)
203 | return response
204 | }, (response) => { /* 对响应错误做点什么 (statusCode !== 200),必须return response*/
205 | console.log(response)
206 | return response
207 | })
208 | ```
209 |
210 | **typescript使用**
211 | --
212 | 在` request.ts `里还暴露了五个接口
213 | ```javascript
214 | {
215 | options, // request 方法配置参数
216 | handleOptions, // get/post 方法配置参数
217 | config, // init 全局config接口(setConfig 参数接口)
218 | requestConfig, // 请求之前参数配置项
219 | response // 响应体
220 | }
221 | ```
222 |
223 | **常见问题**
224 | --
225 | 1. 为什么会请求两次?
226 | - 总有些小白问这些很那啥的问题,有两种可能,一种是‘post三次握手’(不知道的请先给个五星好评,然后打自己一巴掌,并问自己,为什么这都不知道),还有一种可能是`本地访问接口时跨域请求,所以浏览器会先发一个option 去预测能否成功,然后再发一个真正的请求`(没有自己观察请求头,Request Method,就跑来问的,请再打自己一巴掌,并问自己,为什么这都不知道,不知道也行,为什么不百度)。
227 | 2. 如何跨域?
228 | - 问的人不少,可以先百度了解一下。如何跨域
229 | 3. post 怎么传不了数组的参数啊?
230 | - uni-request
231 | 可以点击看一下uni-request 的api 文档,data支持的文件类型只有Object/String/ArrayBuffer
这个真跟我没啥关系 0.0
232 | 4. 'Content-Type' 为什么要小写?
233 | - hbuilderX 更新至‘2.3.0.20190919’ 后,uni.request post请求,如果 ‘Content-Type’ 大写,就会在后面自动拼接‘ application/json’,请求头变成
234 | `Content-Type: application/json;charset=UTF-8 application/json`,导致后端无法解析类型,`Status Code 415`,post 请求失败。但是小写就不会出现这个问题。至于为什么我也没有深究,我现在也不清楚这是他们的bug,还是以后就这样规范了。我能做的只有立马兼容,至于后边uni官方会不会继续变动也不清楚。
235 |
236 |
237 | **tip**
238 | --
239 | - 不想使用upload 可把class 里的upload 删除
240 |
241 |
242 | **issue**
243 | --
244 | 有任何问题或者建议可以=> issue提交,先给个五星好评QAQ!!
245 |
246 |
247 | **作者想说**
248 | --
249 | - 主体代码3kb
250 | - 目前该插件已经上项目,遇到任何问题请先检查自己的代码(排除新版本发布的情况)。最近新上了` typescript ` 版本,因为本人没使用过ts,所以写的不好的地方,还请见谅~
251 | - 写代码很容易,为了让你们看懂写文档真的很lei 0.0
252 | - 最近发现有插件与我雷同,当初接触uni-app 就发现插件市场虽然有封装的不错的request库,但是都没有对多全局配置做处理,都是通过修改源码的方式配置。我首先推出通过class类,并仿照axios的api实现request请求库,并起名‘仿axios封装request网络请求库,支持拦截器全局配置’。他们虽然修改了部分代码,但是功能与性能并没有优化,反而使代码很冗余。希望能推出新的功能,和性能更加强悍的请求库。
253 | - 任何形式的‘参考’、‘借鉴’,请标明作者
254 | ```javascript
255 | luch-request
256 | ```
257 | - 关于问问题
258 | 1. 首先请善于利用搜索引擎,不管百度,还是Google,遇到问题请先自己尝试解决。自己尝试过无法解决,再问。
259 | 2. 不要问类似为什么我的xx无法使用这种问题。请仔细阅读文档,检查代码,或者说明运行环境,把相关代码贴至评论或者发送至我的邮箱,还可以点击上面的issue提交,在里面提问,可能我在里面已经回答了。
260 | 3. 我的代码如果真的出现bug,或者你有好的建议、需求,可以提issue,我看到后会立即解决
261 | 4. 不要问一些弱智问题!!!
262 | - 如何问问题
263 | 1. 仔细阅读文档,检查代码
264 | 2. 说明运行环境,比如:app端 ios、android 版本号、手机机型、普遍现象还是个别现象(越详细越好)
265 | 3. 发出代码片段或者截图至邮箱(很重要)
266 | 4. 或者可以在上方的'issue提交' 里发出详细的问题描述
267 | 5. 以上都觉得解决不了你的问题,可以加QQ:`370306150`
268 |
269 | **土豪赞赏**
270 | --
271 |
272 |
273 | ####创作不易,五星好评你懂得!
274 |
--------------------------------------------------------------------------------
/utils/request/request.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Request 1.0.2
3 | * @Class Request
4 | * @description luch-request 1.0.2 http请求插件
5 |
6 | */
7 | import {
8 | TOKEN
9 | } from '@/utils/storageTypes.js'
10 | export default class Request {
11 | config = {
12 | baseUrl: '',
13 | header: {
14 | 'content-type': 'application/json;charset=UTF-8'
15 | },
16 | method: 'GET',
17 | dataType: 'json',
18 | responseType: 'text'
19 | }
20 |
21 | // uni.getlo
22 | // var token = getLocal(TOKEN)
23 | // console.log(Window.localStorage.getItem(TOKEN))
24 | // if (Window.localStorage.getItem(TOKEN)) {
25 | // config.headers.token = Window.localStorage.getItem(TOKEN)
26 | // }
27 | // if (config.headers.token = `${store.state.token}`;)
28 | static posUrl (url) { /* 判断url是否为绝对路径 */
29 | return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
30 | }
31 |
32 | static addQueryString (params) {
33 | let paramsData = ''
34 | Object.keys(params).forEach(function (key) {
35 | paramsData += key + '=' + params[key] + '&'
36 | })
37 | return paramsData.substring(0, paramsData.length - 1)
38 | }
39 |
40 | /**
41 | * @property {Function} request 请求拦截器
42 | * @property {Function} response 响应拦截器
43 | * @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
44 | */
45 | interceptor = {
46 | /**
47 | * @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。
48 | */
49 | request: (cb) => {
50 | if (cb) {
51 | this.requestBeforeFun = cb
52 | }
53 | },
54 | /**
55 | * @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么
56 | * @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么
57 | */
58 | response: (cb, ecb) => {
59 | if (cb && ecb) {
60 | this.requestComFun = cb
61 | this.requestComFail = ecb
62 | }
63 | }
64 | }
65 |
66 | requestBeforeFun (config) {
67 | return config
68 | }
69 |
70 | requestComFun (response) {
71 | return response
72 | }
73 |
74 | requestComFail (response) {
75 | return response
76 | }
77 |
78 | /**
79 | * 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
80 | * @param { Number } statusCode - 请求响应体statusCode(只读)
81 | * @return { Boolean } 如果为true,则 resolve, 否则 reject
82 | */
83 | validateStatus (statusCode) {
84 | return statusCode === 200
85 | }
86 |
87 | /**
88 | * @Function
89 | * @param {Request~setConfigCallback} f - 设置全局默认配置
90 | */
91 | setConfig (f) {
92 | this.config = f(this.config)
93 | }
94 |
95 | /**
96 | * @Function
97 | * @param {Object} options - 请求配置项
98 | * @prop {String} options.url - 请求路径
99 | * @prop {Object} options.data - 请求参数
100 | * @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
101 | * @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
102 | * @prop {Object} [options.header = config.header] - 请求header
103 | * @prop {Object} [options.method = config.method] - 请求方法
104 | * @returns {Promise}
105 | */
106 | async request (options = {}) {
107 | options.baseUrl = this.config.baseUrl
108 | options.dataType = options.dataType || this.config.dataType
109 | options.responseType = options.responseType || this.config.responseType
110 | options.url = options.url || ''
111 | options.data = options.data || {}
112 | options.params = options.params || {}
113 | options.header = options.header || this.config.header
114 | options.method = options.method || this.config.method
115 | return new Promise((resolve, reject) => {
116 | let next = true
117 |
118 | let handleRe = {}
119 | options.complete = (response) => {
120 | response.config = handleRe
121 | if (this.validateStatus(response.statusCode)) { // 成功
122 | response = this.requestComFun(response)
123 | resolve(response)
124 | } else {
125 | response = this.requestComFail(response)
126 | reject(response)
127 | }
128 | }
129 | const cancel = (t = 'handle cancel', config = options) => {
130 | const err = {
131 | errMsg: t,
132 | config: config
133 | }
134 | reject(err)
135 | next = false
136 | }
137 |
138 | handleRe = { ...this.requestBeforeFun(options, cancel) }
139 | const _config = { ...handleRe }
140 | if (!next) return
141 |
142 | let mergeUrl = Request.posUrl(options.url) ? options.url : (options.baseUrl + options.url)
143 | if (JSON.stringify(options.params) !== '{}') {
144 | const paramsH = Request.addQueryString(options.params)
145 | mergeUrl += mergeUrl.indexOf('?') === -1 ? `?${paramsH}` : `&${paramsH}`
146 | }
147 | _config.url = mergeUrl
148 | uni.request(_config)
149 | })
150 | }
151 |
152 | get (url, options = {}) {
153 | return this.request({
154 | url,
155 | method: 'GET',
156 | ...options
157 | })
158 | }
159 |
160 | post (url, data, options = {}) {
161 | return this.request({
162 | url,
163 | data,
164 | method: 'POST',
165 | ...options
166 | })
167 | }
168 |
169 | // #ifndef MP-ALIPAY
170 | put (url, data, options = {}) {
171 | return this.request({
172 | url,
173 | data,
174 | method: 'PUT',
175 | ...options
176 | })
177 | }
178 |
179 | // #endif
180 |
181 | // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
182 | delete (url, data, options = {}) {
183 | return this.request({
184 | url,
185 | data,
186 | method: 'DELETE',
187 | ...options
188 | })
189 | }
190 |
191 | // #endif
192 |
193 | // #ifdef APP-PLUS || H5 || MP-WEIXIN
194 | connect (url, data, options = {}) {
195 | return this.request({
196 | url,
197 | data,
198 | method: 'CONNECT',
199 | ...options
200 | })
201 | }
202 |
203 | // #endif
204 |
205 | // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
206 | head (url, data, options = {}) {
207 | return this.request({
208 | url,
209 | data,
210 | method: 'HEAD',
211 | ...options
212 | })
213 | }
214 |
215 | // #endif
216 |
217 | // #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
218 | options (url, data, options = {}) {
219 | return this.request({
220 | url,
221 | data,
222 | method: 'OPTIONS',
223 | ...options
224 | })
225 | }
226 |
227 | // #endif
228 |
229 | // #ifdef APP-PLUS || H5 || MP-WEIXIN
230 | trace (url, data, options = {}) {
231 | return this.request({
232 | url,
233 | data,
234 | method: 'TRACE',
235 | ...options
236 | })
237 | }
238 |
239 | // #endif
240 |
241 | upload (url, {
242 | // #ifdef APP-PLUS
243 | files,
244 | // #endif
245 | // #ifdef MP-ALIPAY
246 | fileType,
247 | // #endif
248 | filePath,
249 | name,
250 | header,
251 | formData
252 | }) {
253 | return new Promise((resolve, reject) => {
254 | let next = true
255 | let handleRe = {}
256 | const pubConfig = {
257 | baseUrl: this.config.baseUrl,
258 | url,
259 | // #ifdef APP-PLUS
260 | files,
261 | // #endif
262 | // #ifdef MP-ALIPAY
263 | fileType,
264 | // #endif
265 | filePath,
266 | method: 'UPLOAD',
267 | name,
268 | header: header || this.config.header,
269 | formData,
270 | complete: (response) => {
271 | response.config = handleRe
272 | if (response.statusCode === 200) { // 成功
273 | response = this.requestComFun(response)
274 | resolve(response)
275 | } else {
276 | response = this.requestComFail(response)
277 | reject(response)
278 | }
279 | }
280 | }
281 | const cancel = (t = 'handle cancel', config = pubConfig) => {
282 | const err = {
283 | errMsg: t,
284 | config: config
285 | }
286 | reject(err)
287 | next = false
288 | }
289 |
290 | handleRe = { ...this.requestBeforeFun(pubConfig, cancel) }
291 | const _config = { ...handleRe }
292 | if (!next) return
293 | _config.url = Request.posUrl(url) ? url : (this.config.baseUrl + url)
294 | uni.uploadFile(_config)
295 | })
296 | }
297 | }
298 |
299 | /**
300 | * setConfig回调
301 | * @return {Object} - 返回操作后的config
302 | * @callback Request~setConfigCallback
303 | * @param {Object} config - 全局默认config
304 | */
305 | /**
306 | * 请求拦截器回调
307 | * @return {Object} - 返回操作后的config
308 | * @callback Request~requestCallback
309 | * @param {Object} config - 全局config
310 | * @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求
311 | */
312 | /**
313 | * 响应拦截器回调
314 | * @return {Object} - 返回操作后的response
315 | * @callback Request~responseCallback
316 | * @param {Object} response - 请求结果 response
317 | */
318 | /**
319 | * 响应错误拦截器回调
320 | * @return {Object} - 返回操作后的response
321 | * @callback Request~responseErrCallback
322 | * @param {Object} response - 请求结果 response
323 | */
324 |
--------------------------------------------------------------------------------
/utils/service.js:
--------------------------------------------------------------------------------
1 | const apiBaseUrl = 'www.123.com' // api 根路径
2 |
3 |
4 |
5 | export {
6 | apiBaseUrl,
7 |
8 | }
--------------------------------------------------------------------------------
/utils/socket/BLEConn.js:
--------------------------------------------------------------------------------
1 | let BLEList = []
2 | // 蓝牙模块
3 | // 注意: 小程序端不支持蓝牙4.0以下的版本
4 | // 1. 初始化(搜索蓝牙列表)
5 | function inArray(arr, key, val) {
6 | for (let i = 0; i < arr.length; i++) {
7 | if (arr[i][key] === val) {
8 | return i
9 | }
10 | }
11 | return -1
12 | }
13 | export function getBlooth() {
14 | BLEList = []
15 | return new Promise((resolve, reject) => {
16 | uni.openBluetoothAdapter({
17 | success(res) {
18 | searchBlooth().then(res => {
19 | console.log(res, 'searchBlooth')
20 | uni.onBluetoothDeviceFound(function (devices) {
21 | console.log(devices, '搜索到蓝牙设备')
22 | // let result = BLEList.find(devices.devices[0])
23 | // console.log(result, 'res')
24 | console.log(devices.devices[0])
25 | let idx = inArray(BLEList, 'deviceId', devices.devices[0].deviceId)
26 | console.log(idx, 'idx')
27 | if (idx == '-1') {
28 | if (devices.devices[0].localName && (!(devices.devices[0].name) || devices.devices[0].name == '未知设备')) {
29 | devices.devices[0].name = devices.devices[0].localName
30 | BLEList.push(devices.devices[0])
31 | } else if (devices.devices[0].name && devices.devices[0].name != '未知设备') {
32 | BLEList.push(devices.devices[0])
33 | }
34 | }
35 |
36 | // BLEList.push(devices.devices[0])
37 | // 5秒后停止搜索
38 | })
39 | setTimeout(() => {
40 | DeviceFound().then(res => {
41 | console.log(res, 'DeviceFound')
42 | console.log(BLEList, 'BLEList')
43 | resolve(res)
44 | }).catch(err => {
45 | console.log(err, 'searchBlooth1')
46 | reject(err)
47 | })
48 | }, 5000)
49 | }).catch(err => {
50 | console.log(err, 'DeviceFound1')
51 | reject(err)
52 | })
53 | },
54 | fail(err) {
55 | console.log(err, 'err')
56 | reject(err)
57 | }
58 | })
59 | })
60 | }
61 | // 2. 检查手机蓝牙开启状态
62 | function searchBlooth() {
63 | return new Promise((resolve, reject) => {
64 | uni.startBluetoothDevicesDiscovery({
65 | success(res) {
66 | console.log(res, 'startBluetoothDevicesDiscovery')
67 | // DeviceFound()
68 | resolve(res)
69 | },
70 | fail(err) {
71 | reject(err)
72 | }
73 | })
74 | })
75 | }
76 |
77 | // 3. 搜索蓝牙列表
78 | export function DeviceFound() {
79 |
80 | // uni.onBluetoothDeviceFound(function (devices) {
81 | // console.log(devices, '搜索到蓝牙设备')
82 | // // 5秒后停止搜索
83 | // })
84 | // setTimeout(() => {
85 | stopBlueth()
86 |
87 | // 搜索到的设备列表
88 | return new Promise((resolve, reject) => {
89 | uni.getBluetoothDevices({
90 | success(res) {
91 | console.log(res, '蓝牙列表')
92 | console.log(BLEList, 'BLEListBLEList')
93 | resolve({
94 | devices: BLEList
95 | })
96 | },
97 | fail(err) {
98 | console.log(err, '错误的')
99 |
100 | reject(err)
101 | }
102 | })
103 | })
104 |
105 | // }, 5000);
106 |
107 |
108 |
109 | }
110 | // 4.停止搜索蓝牙
111 | function stopBlueth() {
112 | uni.stopBluetoothDevicesDiscovery({
113 | success(res) {
114 | console.log(res, '蓝牙停止')
115 | }
116 | })
117 | }
118 |
119 | // 5. 断开蓝牙连接
120 | export function closeBle() {
121 | let deviceId = uni.getStorageSync('BLECONNID')
122 | // console.log(deviceId, 'closeBle')
123 | if (deviceId) {
124 | uni.closeBLEConnection({
125 | deviceId: deviceId,
126 | success(res) {
127 | // console.log(res)
128 | // 记得打开
129 | uni.removeStorageSync('RESCODE')
130 | },
131 | fail: (err) => {
132 | // console.log(err)
133 | }
134 | })
135 | // 断开蓝牙模块
136 | uni.closeBluetoothAdapter({
137 | success(res) {
138 | console.log(res)
139 | }
140 | })
141 | }
142 | }
143 | // 6.连接蓝牙(连接之前应该把之前连接的蓝牙设备断开)
144 | /*
145 | item,要连接的蓝牙
146 | uuid
147 | */
148 | export function createBLE(item, uuid) {
149 | // 连接之前应该把之前连接的蓝牙设备断开
150 | // closeBle()
151 | return new Promise((resolve, reject) => {
152 | uni.createBLEConnection({
153 | // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
154 | deviceId: item.deviceId,
155 | success(res) {
156 | console.log(res)
157 | // 监听蓝牙连接
158 | watchBle(item.name).then(res => {
159 | console.log(res)
160 | getAllService(item.deviceId, uuid).then(res => {
161 | console.log(res)
162 | getCharacteristics(item.deviceId, res.uuid).then(res => {
163 | console.log(res)
164 | resolve(res)
165 | }).catch(err => {
166 | reject(err)
167 | })
168 | }).catch(err => {
169 | reject(err)
170 | })
171 | }).catch(err => {
172 | reject(err)
173 | })
174 | },
175 | fail(err) {
176 | reject(err)
177 | }
178 | })
179 | })
180 |
181 | }
182 | // 7.监听蓝牙连接
183 | function watchBle(name) {
184 | return new Promise((resolve, reject) => {
185 | uni.onBLEConnectionStateChange(res => {
186 | // console.log(res, '监听蓝牙连接')
187 | // 该方法回调中可以用于处理连接意外断开等异常情况
188 | // console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
189 | if (res.deviceId && res.connected) {
190 | // 连接成功将本次连接的设备deviceId和name保存本地
191 | uni.setStorageSync('BLECONNID', res.deviceId)
192 | uni.setStorageSync('deviceName', name)
193 | // 获取所有服务
194 | setTimeout(() => {
195 | resolve(res)
196 | }, 500);
197 | } else {
198 | // uni.showToast({
199 | // title: res.name + '断开',
200 | // icon: "none"
201 | // })
202 | reject(res)
203 | }
204 | })
205 | })
206 |
207 | }
208 | // 8.获取所有服务
209 | function getAllService(deviceId, uuid) {
210 | return new Promise((resolve, reject) => {
211 | uni.getBLEDeviceServices({
212 | // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
213 | deviceId,
214 | success: (res) => {
215 | console.log(res, 'res')
216 | res.services.forEach((item,index) => {
217 | // console.log(item, 'deviceid')
218 | // 这里的uuid'0000FFE0-0000-1000-8000-00805F9B34FB'
219 | if (item.isPrimary && item.uuid === uuid) {
220 | uni.setStorageSync("deviceId", deviceId)
221 | uni.setStorageSync("serviceId", item.uuid)
222 | resolve(item)
223 | } else if (index == (res.services).length) {
224 | console.log(index, item, 'getBLEDeviceServices')
225 | reject(res)
226 | } else {
227 | console.log(index, item, 'getBLEDeviceServices')
228 | reject(res)
229 | }
230 | })
231 |
232 | // getCharacteristics(deviceId, res.services[0].uuid)
233 | },
234 | fail: (err) => {
235 | // console.log(err)
236 | reject(err)
237 | }
238 | })
239 | })
240 |
241 | }
242 | // 9. 获取某个服务的特征值
243 | function getCharacteristics(deviceId, serviceId) {
244 | return new Promise((resolve, reject) => {
245 | uni.getBLEDeviceCharacteristics({
246 | // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
247 | deviceId,
248 | // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
249 | serviceId,
250 | success(res) {
251 | // console.log(res, 'getCharacteristics')
252 | // console.log('device getBLEDeviceCharacteristics:', res.characteristics)
253 | uni.showLoading({
254 | title: "检测中...",
255 | icon: "loading",
256 | mask: true
257 | })
258 | res.characteristics.forEach((item, index) => {
259 | if (item.properties.notify) {
260 | // 启用 notify 功能(成功 监听 失败 进行读取操作)
261 | uni.setStorageSync("characteristicId", item.uuid)
262 | // console.log(1)
263 | setTimeout(function() {
264 | uni.hideLoading()
265 | }, 500);
266 | // console.log(deviceId, serviceId, item.uuid, 'res.deviceId, res.serviceId, item.uuid')
267 | notify(deviceId, serviceId, item.uuid).then(res => {
268 | // console.log(res, 'notify')
269 | resolve(res)
270 | })
271 | // watchNotify()
272 |
273 | } else if (index == (res.characteristics).length - 1 && !item.properties.write) {
274 | // console.log(index, item, 'fsakdfjslfj')
275 | setTimeout(function() {
276 | uni.hideLoading()
277 | uni.showToast({
278 | title: "该设备不支持读和写",
279 | icon: "none"
280 | })
281 | }, 500);
282 | reject(res)
283 | }
284 | })
285 | },
286 | fail(err) {
287 | reject(err)
288 | }
289 | })
290 | })
291 |
292 | }
293 | // 10. 启用 notify 功能
294 | export function notify(deviceId, serviceId, characteristicId) {
295 | return new Promise((resolve, reject) => {
296 | uni.notifyBLECharacteristicValueChange({
297 | state: true, // 启用 notify 功能
298 | // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
299 | deviceId,
300 | // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
301 | serviceId,
302 | // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
303 | characteristicId,
304 | success(res) {
305 | // console.log('notifyBLECharacteristicValueChange success', res.errMsg)
306 | resolve(res)
307 | },
308 | fail(err) {
309 | // console.log(err, 'notifuerr')
310 | // readBLE(deviceId, serviceId, characteristicId)
311 | reject(err)
312 | }
313 | })
314 | })
315 |
316 | }
317 | /**
318 | * 字符串转换为数组,个数为n,s为字符串
319 |
320 | */
321 | function strToArr(s, g) {
322 | // var s = "051102003"
323 | var re = new RegExp(".{" + g +"}","g")
324 | var a = []
325 | var n
326 | while ((n=re.exec(s)) != null){
327 | a[a.length] = n[0]
328 | }
329 | return a
330 | }
331 |
332 | // 11 监听notify功能
333 | var packData = '' // 分包操作
334 | var first = '' // 开始部分
335 | var center = '' // 中间拼接部分
336 | var last = '' // 结尾部分
337 | var len = 0 // 包的长度
338 | // var flag = true // flag判断是否进行了分包操作 如果分包需要清空开始部分 中间部分 结尾部分
339 | export function watchNotify() {
340 | // ArrayBuffer转16进度字符串示例
341 | function ab2hex(buffer) {
342 | const hexArr = Array.prototype.map.call(
343 | new Uint8Array(buffer),
344 | function (bit) {
345 | return ('00' + bit.toString(16)).slice(-2)
346 | }
347 | )
348 | return hexArr.join('')
349 | }
350 |
351 | return new Promise((resolve, reject) => {
352 | uni.onBLECharacteristicValueChange(function (res) {
353 | console.log(res)
354 | // 监听帧头帧尾
355 | var resCode1 = ab2hex(res.value)
356 | var resCode = resCode1.toUpperCase() // 收到蓝牙返回的命令(16进制)
357 | console.log(resCode, '收到的')
358 | /**
359 | *
360 | * 以下是我项目中的分包接收操作(可注释)
361 | *
362 | */
363 |
364 | // 1. 如果开头结尾和帧头帧尾一致并且长度一致就保留(说明长度完整)
365 | if (resCode.substring(resCode.length - 2, resCode.length) == 'FE' && resCode.substring(0, 2) == '05') {
366 | let lenTo10 = parseInt(resCode.substring(2,resCode.length - 2).length)/2
367 | // 1.1找出内容16进制
368 | let lengthTo16 = lenTo10.toString(16) + ''
369 | // 2.1 对16进制进行大小写转换并且补零操作
370 | lengthTo16 = lengthTo16.length == 1 ? '0' + lengthTo16.toUpperCase() : lengthTo16.toUpperCase()
371 | let length1 = resCode.substring(2,4)
372 | console.log(lengthTo16, length1)
373 | if (lengthTo16 == length1) {
374 | if (resCode.substring(8,12) == 'F020') {
375 | uni.setStorageSync('RESCODE', resCode)
376 | console.log(resCode, 'F020')
377 | } else {
378 | uni.setStorageSync('RESPARAMS', resCode)
379 | }
380 | }
381 |
382 | } else if (resCode.substring(resCode.length - 2, resCode.length) != 'FE' && resCode.substring(0, 2) == '05') {
383 | // 2. 如果帧头对 帧尾不对 则为前边部分
384 | first = resCode
385 |
386 | console.log(first, '这个是开头部分first')
387 | } else if (resCode.substring(resCode.length - 2, resCode.length) != 'FE' && resCode.substring(0, 2) != '05') {
388 | // 3. 如果帧头不对 帧尾不对 则为中间部分
389 | center = center + resCode
390 | console.log(center, '中间部分center')
391 | } else if (resCode.substring(resCode.length - 2, resCode.length) == 'FE' && resCode.substring(0, 2) != '05') {
392 | // 4. 如果帧头不对 帧尾对 则为最后一部分
393 | last = resCode
394 | console.log(last, '最后一部分last')
395 |
396 | // 得到实际内容
397 |
398 | var content = first + center + last
399 | console.log(content, '实际的内容')
400 | // 4.1找出内容16进制
401 | var resLen = (content.length - 4) / 2 // 减去4是剪掉帧头和帧尾,除以2是为了得到字节
402 | console.log(resLen, '实际的长度10进制')
403 | var lenTo16 = resLen.toString(16) + ''
404 | // 4.2 对16进制进行大小写转换并且补零操作
405 | lenTo16 = lenTo16.length == 1 ? '0' + lenTo16.toUpperCase() : lenTo16.toUpperCase()
406 | console.log(lenTo16, 'lenTo16')
407 | // 得到包的长度
408 | len = content.substring(2,4)
409 | console.log(len, 'len')
410 | // 判断是否得到长度和实际长度相等
411 | if (lenTo16 == len) {
412 | if (content.substring(8,12) == 'F020') {
413 | uni.setStorageSync('RESCODE', content)
414 | console.log(content, 'F020')
415 | } else {
416 | uni.setStorageSync('RESPARAMS', content)
417 | console.log(content, '!F020')
418 | }
419 | } else {
420 | // 如果不相等, 找一下字符串中间有没有FE 然后再进行长度 帧头帧尾判断
421 |
422 | // 1.截取帧尾的FE 求出帧尾之前是否有FE
423 | let substrFE = content.substring(0, content.length - 2)
424 | console.log(substrFE, 'substrFE')
425 | // 2.判断是否还有FE
426 | // 2.1 转换为两个字符一起的数组
427 | let hasFEArr = strToArr(substrFE, 2)
428 | // 2.2 找出是否有FE 并确定位置
429 | let hasFE = hasFEArr.indexOf('FE')
430 |
431 | console.log(hasFEArr, 'hasFEArr')
432 | console.log(hasFE, 'hasFE')
433 | // 如果有FE,截取到FE之处 判断帧头帧尾长度
434 | if (hasFE != -1) {
435 | // 将最后的数据保存到这个数组里面
436 | let lastConArr = []
437 | // 得到05-FE的内容
438 | hasFEArr.forEach((item, index) => {
439 | if (index <= hasFE) {
440 | lastConArr.push(item)
441 | }
442 | })
443 | console.log(lastConArr, 'lastConArr')
444 | // 判断是否是05开头FE结尾
445 | console.log(lastConArr[lastConArr.length - 1], 'lastConArr[lastConArr.length - 1]')
446 | console.log(lastConArr[0], 'lastConArr[0]')
447 | if (lastConArr[0] == '05' && lastConArr[lastConArr.length - 1] == 'FE') {
448 | // 判断得到的长度与实际长度
449 | let getLen = lastConArr[1]
450 | console.log(getLen, 'getLen')
451 | // 实际长度 并且转换为十六进制字符串
452 | let resLen = (lastConArr.length - 2).toString(16) + ''
453 | console.log(resLen, 'resLen1')
454 | // 实际长度补零操作
455 | resLen = resLen.length == 1 ? '0' + resLen : resLen,
456 | console.log(resLen, 'resLen2')
457 | // 判断得到的长度与实际长度相等之后才是实际的结果
458 | if (getLen == resLen.toUpperCase()) {
459 | // 数组转换为字符串
460 | let lastStr = lastConArr.join('')
461 | console.log(lastStr, 'lastStr')
462 | if (lastStr.substring(8,12) == 'F020') {
463 | uni.setStorageSync('RESCODE', lastStr)
464 | console.log(lastStr, 'F020')
465 | } else {
466 | uni.setStorageSync('RESPARAMS', lastStr)
467 | console.log('!f')
468 | }
469 | }
470 | }
471 | }
472 | }
473 | // 清空内容和收到的数据
474 | first = '' // 开始部分
475 | center = '' // 中间拼接部分
476 | last = '' // 结尾部分
477 | len = 0 // 包的长度
478 |
479 | }
480 | })
481 | })
482 |
483 | }
484 |
485 | // 12.读取功能
486 | export function readBLE(deviceId, serviceId, characteristicId) {
487 | uni.readBLECharacteristicValue({
488 | // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
489 | deviceId,
490 | // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
491 | serviceId,
492 | // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
493 | characteristicId,
494 | success(res) {
495 | // console.log('readBLECharacteristicValue:', res.errCode)
496 | },
497 | fail(err) {
498 | // console.log('readBLEerr:', err)
499 | }
500 | })
501 | }
502 |
503 | /**
504 | * s为要转换的字符串
505 | * n为转换为几个一起的数
506 | *'0102031A1B'类型转换为['0x11','0x02'] (n传2)
507 | */
508 | function strToArr1(s, n) {
509 | // var s = "051102003"
510 | // console.log(s)
511 | var re = new RegExp(".{" + n + "}", "g")
512 | var a = []
513 | var n
514 | while ((n = re.exec(s)) != null) {
515 | a[a.length] = '0x' + n[0]
516 | }
517 | return a
518 | }
519 |
520 | // 14.写入功能.
521 | /**
522 | *
523 | * @param {*} e
524 | * 需要发送给蓝牙的数据格式:['0x11','0x02']
525 | *
526 | * strToArr1函数可将十六进制转换为数组'0102031A1B'类型转换为['0x11','0x02']
527 | *
528 | */
529 | export function writeBLE(e) {
530 | var deviceId = uni.getStorageSync("deviceId")
531 | var serviceId = uni.getStorageSync("serviceId")
532 | var characteristicId = uni.getStorageSync("characteristicId")
533 | console.log(deviceId,serviceId, characteristicId)
534 | // 向蓝牙设备发送一个0x00的16进制数据
535 | return new Promise((resolve, reject) => {
536 | // 分包发送
537 | for (var i = 0;i < e.length; i += 20) {
538 | var endLength = 0
539 | if (i + 20 < e.length) {
540 | var senddata = e
541 | let buffer = new ArrayBuffer(20)
542 | let dataView = new DataView(buffer)
543 | for (var j = i; j < i + 20; j++) {
544 | dataView.setUint8(j - i, senddata[j])
545 | }
546 | uni.writeBLECharacteristicValue({
547 | // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
548 | deviceId: deviceId,
549 | // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
550 | serviceId: serviceId,
551 | // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
552 | characteristicId: characteristicId,
553 | // 这里的value是ArrayBuffer类型
554 | value: dataView.buffer,
555 | success(res) {
556 | resolve(res)
557 | },
558 | fail(err) {
559 | reject(err)
560 | }
561 | })
562 | // 等待
563 | sleep(0.02)
564 | } else {
565 | var senddata = e
566 | if (20 < e.length) {
567 | endLength = senddata.length - i
568 | } else{
569 | endLength = senddata.length
570 | }
571 |
572 | let buffer = new ArrayBuffer(endLength)
573 | let dataViewLast = new DataView(buffer)
574 | for (var k = i; k < senddata.length; k++) {
575 | dataViewLast.setUint8(k-i, senddata[k])
576 | }
577 | console.log('最后一包或第一数据:' + dataViewLast.buffer)
578 | uni.writeBLECharacteristicValue({
579 | // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
580 | deviceId: deviceId,
581 | // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
582 | serviceId: serviceId,
583 | // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
584 | characteristicId: characteristicId,
585 | // 这里的value是ArrayBuffer类型
586 | value: dataViewLast.buffer,
587 | success(res) {
588 | resolve(res)
589 | },
590 | fail(err) {
591 | reject(err)
592 | }
593 | })
594 | sleep(0.02)
595 | }
596 | }
597 | })
598 | }
599 | function sleep(delay) {
600 | var start = (new Date()).getTime();
601 | while ((new Date()).getTime() - start < delay) {
602 | continue;
603 | }
604 | }
605 |
606 | // 其他转换方法
607 | //16进制字符串转 ArrayBuffer
608 | const hexToArrayBuffer = (hex) => {
609 | return new Uint8Array(
610 | hex.match(/[\da-f]{2}/gi).map((byte) => {
611 | return parseInt(byte, 16)
612 | })
613 | ).buffer
614 | }
615 |
616 | //ArrayBuffer类型数据转为16进制字符串
617 | const bufToHex = (buffer) => {
618 | return Array.prototype.map.call(new Uint8Array(buffer), (x) => ('00' + x.toString(16)).slice(-2)).join('')
619 | }
620 |
621 |
622 |
623 |
--------------------------------------------------------------------------------
/utils/storageTypes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 本地储存键 types
3 | * **/
4 |
5 | const TOKEN = 'm_token'; // token
6 | /**
7 | * 用户信息
8 | * nickname 昵称; avatar 头像;
9 | *
10 | */
11 | const USERINFO = 'm_user_info'; // userinfo
12 | const CODE = 'm_code'; // code
13 | const OPENID = 'm_openid'; // openid
14 | const RECEIVE = 'm_receive'; // 收到的消息
15 | const EQUIPID = 'm_equipid'; // 设备id
16 | const REGION = 'm_region'; // 用户的地区 [省,市,区]
17 | const HEARTCODE = 'heart_code'; // 心跳数据
18 |
19 | export {
20 | TOKEN,
21 | USERINFO,
22 | CODE,
23 | REGION,
24 | OPENID,
25 | EQUIPID,
26 | HEARTCODE
27 | }
28 |
--------------------------------------------------------------------------------