├── static └── .gitkeep ├── demo ├── demo.gif └── weChat.png ├── config ├── prod.env.js ├── dev.env.js └── index.js ├── src ├── assets │ ├── logo.png │ ├── images │ │ ├── timg.jpeg │ │ ├── avatar.jpeg │ │ ├── bg_login.jpg │ │ └── default.png │ └── css │ │ ├── article.css │ │ └── articleDetail.css ├── common │ ├── config.js │ └── httpInterceptor.js ├── resources │ ├── friendMessage.js │ └── user.js ├── filters │ ├── utils │ │ ├── TimeFilter.js │ │ ├── index.js │ │ ├── FriendMenuTimeFilter.js │ │ └── ChatMessageTimeFilter.js │ └── index.js ├── store │ ├── index.js │ ├── getters.js │ └── modules │ │ ├── friendMessage.js │ │ ├── user.js │ │ └── message.js ├── App.vue ├── components │ ├── avatar │ │ └── Identicon.vue │ ├── DemoList.vue │ ├── TimeLine.vue │ ├── ChatList.vue │ ├── index.vue │ ├── ChatMenu.vue │ ├── Login.vue │ ├── Register.vue │ ├── chat │ │ ├── chatRoom.vue │ │ └── friendsMenu.vue │ └── HelloWorld.vue ├── main.js ├── utils │ ├── validate.js │ ├── notification.js │ ├── util.js │ └── sortPickerView.js └── router │ └── index.js ├── .editorconfig ├── .gitignore ├── .postcssrc.js ├── index.html ├── .babelrc ├── README.md └── package.json /static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /demo/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/demo/demo.gif -------------------------------------------------------------------------------- /demo/weChat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/demo/weChat.png -------------------------------------------------------------------------------- /config/prod.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | module.exports = { 3 | NODE_ENV: '"production"' 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/images/timg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/src/assets/images/timg.jpeg -------------------------------------------------------------------------------- /src/assets/images/avatar.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/src/assets/images/avatar.jpeg -------------------------------------------------------------------------------- /src/assets/images/bg_login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/src/assets/images/bg_login.jpg -------------------------------------------------------------------------------- /src/assets/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luanxuechao/vue-blog/HEAD/src/assets/images/default.png -------------------------------------------------------------------------------- /config/dev.env.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const merge = require('webpack-merge') 3 | const prodEnv = require('./prod.env') 4 | 5 | module.exports = merge(prodEnv, { 6 | NODE_ENV: '"development"' 7 | }) 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/common/config.js: -------------------------------------------------------------------------------- 1 | const host = location.hostname 2 | 3 | console.log('主机域名', host) 4 | let URL = process.env.NODE_ENV == 'production'?'http://www.csails.cn:3001': 'http://localhost:3001' 5 | 6 | 7 | export { URL } 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | "plugins": { 5 | // to edit target browsers: use "browserslist" field in package.json 6 | "autoprefixer": {} 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/resources/friendMessage.js: -------------------------------------------------------------------------------- 1 | import httpServer from '../common/httpInterceptor' 2 | import Qs from 'qs' 3 | export function delFriendMessage(){ 4 | return httpServer({ 5 | url: 'FriendMessages/deletedMessage', 6 | method: 'delete' 7 | }); 8 | } 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | blogclient 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/filters/utils/TimeFilter.js: -------------------------------------------------------------------------------- 1 | import * as moment from 'moment'; 2 | 3 | function timefilter(value, formatString) { 4 | formatString = formatString || 'YYYY-MM-DD HH:mm:ss'; 5 | return moment(value).format(formatString); 6 | }; 7 | 8 | 9 | export default timefilter; 10 | -------------------------------------------------------------------------------- /src/filters/utils/index.js: -------------------------------------------------------------------------------- 1 | import timefilter from './TimeFilter' 2 | import friendMenuTimefilter from './FriendMenuTimeFilter' 3 | import chatMessageTimeFilter from './ChatMessageTimeFilter' 4 | 5 | export { 6 | timefilter, 7 | friendMenuTimefilter, 8 | chatMessageTimeFilter 9 | } 10 | -------------------------------------------------------------------------------- /src/filters/index.js: -------------------------------------------------------------------------------- 1 | import util from '../utils/util' 2 | import * as filters from'./utils' 3 | let filter = { 4 | install: function(Vue) { 5 | util.each(filters, function(value, key) { 6 | Vue.filter(key, value) 7 | }) 8 | Vue.mixin({}); 9 | } 10 | } 11 | 12 | export default filter; 13 | -------------------------------------------------------------------------------- /src/filters/utils/FriendMenuTimeFilter.js: -------------------------------------------------------------------------------- 1 | import * as moment from 'moment'; 2 | 3 | function friendMenuTimeFilter(value){ 4 | value = moment(value); 5 | let formatString = 'HH:mm'; 6 | if(value.diff(new Date(), 'days')){ 7 | formatString = 'YYYY/M/DD' 8 | } 9 | return moment(value).format(formatString); 10 | } 11 | export default friendMenuTimeFilter; 12 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-runtime"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": ["istanbul"] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import user from './modules/user'; 4 | import getters from './getters'; 5 | import messages from './modules/message'; 6 | import friendMessages from './modules/friendMessage' 7 | 8 | Vue.use(Vuex); 9 | 10 | const store = new Vuex.Store({ 11 | modules: { 12 | user, 13 | friendMessages, 14 | messages 15 | }, 16 | getters 17 | }); 18 | 19 | export default store 20 | -------------------------------------------------------------------------------- /src/store/getters.js: -------------------------------------------------------------------------------- 1 | const getters = { 2 | token: state => state.user.token, 3 | name: state => state.user.name, 4 | uid: state => state.user.uid, 5 | userId: state => state.user.userId, 6 | email: state => state.user.email, 7 | username: state => state.user.username, 8 | mobile: state => state.user.mobile, 9 | friendMessageCount: state =>state.friendMessages.friendMessageCount, 10 | friendMessages: state =>state.friendMessages.friendMessages, 11 | friendList: state=>state.messages.friendList, 12 | messages: state => state.messages.messages 13 | }; 14 | export default getters 15 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 27 | -------------------------------------------------------------------------------- /src/components/avatar/Identicon.vue: -------------------------------------------------------------------------------- 1 | 4 | 33 | -------------------------------------------------------------------------------- /src/components/DemoList.vue: -------------------------------------------------------------------------------- 1 | 16 | 32 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | 4 | //components 5 | import Vue from 'vue' 6 | import App from './App' 7 | import router from './router' 8 | import iview from 'iview' 9 | import VueQuillEditor from 'vue-quill-editor' 10 | import mavonEditor from 'mavon-editor' 11 | import store from './store' 12 | import filter from './filters' 13 | 14 | 15 | // css 16 | import 'iview/dist/styles/iview.css' 17 | import './assets/css/article.css' 18 | import 'mavon-editor/dist/css/index.css' 19 | import 'github-markdown-css/github-markdown.css' 20 | import './assets/css/articleDetail.css' 21 | 22 | 23 | // add 24 | Vue.use(iview); 25 | Vue.use(mavonEditor) 26 | Vue.use(VueQuillEditor) 27 | Vue.use(filter) 28 | 29 | 30 | Vue.config.productionTip = false 31 | 32 | /* eslint-disable no-new */ 33 | new Vue({ 34 | el: '#app', 35 | store, 36 | router, 37 | template: '', 38 | components: {App } 39 | }) 40 | -------------------------------------------------------------------------------- /src/common/httpInterceptor.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import store from '../store'; 3 | import Vue from 'vue'; 4 | import { URL } from './config' 5 | 6 | 7 | const service = axios.create({ 8 | baseURL: URL + '/api/v1/', // 基本路径 9 | timeout: 5000 // 请求超时时间 10 | }); 11 | 12 | // request拦截器 13 | service.interceptors.request.use(config => { 14 | if (store.getters.token) { 15 | config.headers.Authorization = store.getters.token; 16 | } 17 | return config; 18 | }, error => { 19 | Promise.reject(error); 20 | }) 21 | 22 | service.interceptors.response.use( 23 | response => response, 24 | error => { 25 | const statusCode = error.response.data.error.statusCode 26 | if (statusCode === 401) { 27 | store.dispatch('FedLogOut').then(() => { 28 | location.reload() 29 | }) 30 | } else { 31 | const vueObj = new Vue() 32 | const msg = error.response.data.error.message 33 | vueObj.$Notice.error({ 34 | title: '请求出错', 35 | desc: msg 36 | }); 37 | } 38 | return Promise.reject(error); 39 | } 40 | ) 41 | 42 | export default service; 43 | -------------------------------------------------------------------------------- /src/filters/utils/ChatMessageTimeFilter.js: -------------------------------------------------------------------------------- 1 | import * as moment from 'moment'; 2 | 3 | function chatMessageTimeFilter(value){ 4 | value = moment(value); 5 | let formatString = 'HH:mm'; 6 | let dateDiff = Math.abs(value.diff(new Date(), 'days')); 7 | switch (true){ 8 | case (dateDiff == 0): 9 | return moment(value).format(formatString); 10 | case (dateDiff >=1 && dateDiff <7 ): 11 | return fGetCurrentWeek(value)+ ' '+moment(value).format(formatString); 12 | default: 13 | return moment(value).format('YYYY年M月DD日 HH:mm') 14 | } 15 | } 16 | 17 | function fGetCurrentWeek(value){ 18 | var sWeek= moment(value).format('dddd'); 19 | switch (sWeek){ 20 | case 'Monday': sWeek='星期一'; 21 | break; 22 | case 'Tuesday': sWeek='星期二'; 23 | break; 24 | case 'Wednesday': sWeek='星期三'; 25 | break; 26 | case 'Thursday': sWeek='星期四'; 27 | break; 28 | case 'Friday': sWeek='星期五'; 29 | break; 30 | case 'Saturday': sWeek='星期六'; 31 | break; 32 | case 'Sunday': sWeek='星期日'; 33 | break; 34 | default: 35 | break; 36 | } 37 | return sWeek; 38 | } 39 | 40 | export default chatMessageTimeFilter; 41 | -------------------------------------------------------------------------------- /src/components/TimeLine.vue: -------------------------------------------------------------------------------- 1 | 10 | 40 | 45 | -------------------------------------------------------------------------------- /src/resources/user.js: -------------------------------------------------------------------------------- 1 | import httpServer from '../common/httpInterceptor' 2 | import Qs from 'qs' 3 | 4 | // 用户登录 5 | export function loginByMobile(mobile, password) { 6 | const data = { 7 | mobile:mobile, 8 | password:password 9 | }; 10 | return httpServer({ 11 | url: 'BlogUsers/login', 12 | method: 'post', 13 | data: Qs.stringify(data) 14 | }); 15 | } 16 | 17 | // 用户登录 18 | export function register(userForm) { 19 | let userData = { 20 | mobile:userForm.mobile, 21 | nickName:userForm.nickName, 22 | sex:userForm.sex, 23 | password:userForm.password 24 | } 25 | return httpServer({ 26 | url: 'BlogUsers', 27 | method: 'post', 28 | data: Qs.stringify(userData) 29 | }); 30 | } 31 | 32 | export function logout(){ 33 | return httpServer({ 34 | url: 'BlogUsers/logout', 35 | method: 'post' 36 | }); 37 | } 38 | 39 | export function getFriendList(){ 40 | return httpServer({ 41 | url: 'ChatRooms/findChatRooms', 42 | method: 'get' 43 | }); 44 | } 45 | 46 | export function getPersonalInfo(id){ 47 | return httpServer({ 48 | url: `BlogUsers/${id}`, 49 | method: 'get' 50 | }); 51 | } 52 | 53 | export function updatePersonalInfo(id,user){ 54 | return httpServer({ 55 | url: `BlogUsers/${id}`, 56 | method: 'put', 57 | data: Qs.stringify(user) 58 | }); 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/utils/validate.js: -------------------------------------------------------------------------------- 1 | 2 | // 是否合法的邮箱 3 | export function isEmail(str) { 4 | const reg = /^[a-z0-9]+([._\\-]*[a-z0-9])*@([a-z0-9]+[-a-z0-9]*[a-z0-9]+.){1,63}[a-z0-9]+$/i 5 | return reg.test(str) 6 | } 7 | 8 | /* 是否合法的手机 */ 9 | export function isMobile(mobile) { 10 | const reg = /^1[3|4|5|7|8|9][0-9]{9}$/ 11 | return reg.test(mobile) 12 | } 13 | 14 | /* 合法uri*/ 15 | export function validateURL(textval) { 16 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; 17 | return urlregex.test(textval); 18 | } 19 | 20 | /* 小写字母*/ 21 | export function validateLowerCase(str) { 22 | const reg = /^[a-z]+$/; 23 | return reg.test(str); 24 | } 25 | 26 | 27 | 28 | /* 大写字母*/ 29 | export function validateUpperCase(str) { 30 | const reg = /^[A-Z]+$/; 31 | return reg.test(str); 32 | } 33 | 34 | /* 大小写字母*/ 35 | export function validatAlphabets(str) { 36 | const reg = /^[A-Za-z]+$/; 37 | return reg.test(str); 38 | } 39 | 40 | export function oneOf(value, validList) { 41 | for (let i = 0; i < validList.length; i++) { 42 | if (value === validList[i]) { 43 | return true; 44 | } 45 | } 46 | return false; 47 | } 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/store/modules/friendMessage.js: -------------------------------------------------------------------------------- 1 | import notification from '@/utils/notification'; 2 | import Vue from 'vue' 3 | const messages = { 4 | state: { 5 | friendMessageCount: 0, 6 | friendMessages:[] 7 | }, 8 | mutations: { 9 | SOCKET_NEWFRIEND: (state, message) => { 10 | console.log('SOCKET_NEWFRIEND',message); 11 | message.title ='新的好友'; 12 | message.content =message[0].creator.nickName+'请求添加你为好友' 13 | notification(message); 14 | state.friendMessageCount++; 15 | }, 16 | SET_UNREADMESSAGE:(state,count)=>{ 17 | state.friendMessageCount =count; 18 | }, 19 | SET_FRIENDMESSAGES:(state,datas)=>{ 20 | state.friendMessages = datas; 21 | }, 22 | }, 23 | actions: { 24 | getFriendMessageCount:(context, status)=>{ 25 | let vueObj = new Vue(); 26 | Vue.prototype.$socket.emit('unReadFriendMessageCount',(err,result)=>{ 27 | if(err){ 28 | vueObj.$Notice.error({ 29 | title: '请求出错', 30 | desc: err.message 31 | }); 32 | } 33 | context.commit('SET_UNREADMESSAGE',result.count); 34 | }); 35 | }, 36 | getFriendMessage:(context,status)=>{ 37 | let vueObj = new Vue(); 38 | Vue.prototype.$socket.emit('getFriendMessages', (err, result) => { 39 | context.commit('SET_FRIENDMESSAGES',result.datas); 40 | }); 41 | } 42 | } 43 | } 44 | 45 | export default messages 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-blog 2 | [![Travis](https://img.shields.io/badge/Build-passing-brightgreen.svg?style=flat-square)](https://github.com/luanxuechao/vue-blog) 3 | [![node](https://img.shields.io/badge/node-v8.1.4-blue.svg?style=flat-square)](https://github.com/luanxuechao/vue-blog) 4 | [![socket.io](https://img.shields.io/badge/socket.io-%3E%3D2.0.0-blue.svg?style=flat-square)](https://github.com/luanxuechao/vue-blog) 5 | [![license](https://img.shields.io/github/license/mashape/apistatus.svg?style=flat-square)](https://github.com/luanxuechao/vue-blog) 6 | 7 | 这是一个运用前端框架VueJS,UI框架Iview的博客项目,后端使用Node框架LoopBack,后端传送门。[这里](https://github.com/luanxuechao/loopback-chat) 8 | 9 | ## 演示 10 | ![](https://github.com/luanxuechao/vue-blog/blob/master/demo/demo.gif?raw=true) 11 | 12 | ## 线上地址 13 | [DEMO](http://www.csails.cn) 14 | 15 | ## 快速开始 16 | 17 | ``` bash 18 | # install dependencies 19 | npm install 20 | 21 | # serve with hot reload at localhost:8080 22 | npm run dev 23 | 24 | ``` 25 | 26 | ## 技术栈 27 | - [vueJS](https://cn.vuejs.org/) 28 | - [iview](https://www.iviewui.com/) 29 | - [socket.io](https://socket.io/) 30 | 31 | ## 功能列表 32 | - [x] 登录 33 | - [x] 注册 34 | - [x] 根据姓名生成随机头像 35 | - [x] 聊天 36 | - [x] 添加好友 37 | - [x] 实时消息通知 38 | - [x] 根据好友昵称 分组 好友列表 39 | - [x] 登出 40 | - [ ] 用sass 重构css 41 | - [ ] 语音消息、文件消息、图片消息 42 | - [ ] 语音通话、视频通话 43 | - [x] 个人中心 44 | - [ ] 修改备注 45 | 46 | ## 联系我 47 | |Author|Chevalier| 48 | |---|--- 49 | |E-mail|luanxuechaowd@gmail.com 50 | ### 微信 51 | ![](https://github.com/luanxuechao/vue-blog/blob/master/demo/weChat.png) 52 | -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict' 3 | // Template version: 1.1.1 4 | // see http://vuejs-templates.github.io/webpack for documentation. 5 | 6 | const path = require('path') 7 | 8 | module.exports = { 9 | build: { 10 | env: require('./prod.env'), 11 | index: path.resolve(__dirname, '../dist/index.html'), 12 | assetsRoot: path.resolve(__dirname, '../dist'), 13 | assetsSubDirectory: 'static', 14 | assetsPublicPath: '/', 15 | productionSourceMap: false, 16 | // Gzip off by default as many popular static hosts such as 17 | // Surge or Netlify already gzip all static assets for you. 18 | // Before setting to `true`, make sure to: 19 | // npm install --save-dev compression-webpack-plugin 20 | productionGzip: true, 21 | productionGzipExtensions: ['js', 'css'], 22 | // Run the build command with an extra argument to 23 | // View the bundle analyzer report after build finishes: 24 | // `npm run build --report` 25 | // Set to `true` or `false` to always turn it on or off 26 | bundleAnalyzerReport: process.env.npm_config_report 27 | }, 28 | dev: { 29 | env: require('./dev.env'), 30 | port: process.env.PORT || 8080, 31 | autoOpenBrowser: true, 32 | assetsSubDirectory: 'static', 33 | assetsPublicPath: '/', 34 | proxyTable: {}, 35 | // CSS Sourcemaps off by default because relative paths are "buggy" 36 | // with this option, according to the CSS-Loader README 37 | // (https://github.com/webpack/css-loader#sourcemaps) 38 | // In our experience, they generally work as expected, 39 | // just be aware of this issue when enabling this option. 40 | cssSourceMap: false 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import HelloWorld from '@/components/HelloWorld' 4 | import TimeLine from '@/components/TimeLine' 5 | import DemoList from '@/components/DemoList' 6 | import Login from '@/components/Login' 7 | import index from '@/components/index' 8 | import ChatMenu from '@/components/ChatMenu' 9 | import ChatList from '@/components/ChatList' 10 | import FriendsMenu from '@/components/chat/friendsMenu' 11 | import Register from '@/components/Register' 12 | 13 | Vue.use(Router) 14 | 15 | export default new Router({ 16 | routes: [ 17 | { 18 | path: '/', 19 | name: 'index', 20 | component: index, 21 | redirect:'/Hello', 22 | children:[ 23 | { 24 | path: '/Hello', 25 | name: 'Hello', 26 | component: HelloWorld, 27 | }, 28 | { 29 | path: '/TimeLine', 30 | name: 'TimeLine', 31 | component: TimeLine 32 | }, 33 | { 34 | path: '/DemoList', 35 | name: 'DemoList', 36 | component: DemoList 37 | } 38 | ] 39 | }, 40 | { 41 | path: '/Login', 42 | name: 'Login', 43 | component: Login 44 | }, 45 | { 46 | path: '/Register', 47 | name: 'Register', 48 | component: Register 49 | }, 50 | { 51 | path: '/ChatMenu', 52 | name: 'ChatMenu', 53 | component: ChatMenu, 54 | children:[ 55 | { 56 | path: 'ChatList', 57 | name: 'ChatList', 58 | component: ChatList 59 | }, 60 | { 61 | path: 'FriendsMenu', 62 | name: 'FriendsMenu', 63 | component: FriendsMenu 64 | } 65 | ] 66 | } 67 | ] 68 | }) 69 | -------------------------------------------------------------------------------- /src/utils/notification.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | const vue = new Vue() 4 | 5 | const notification = message => { 6 | const title = message && message.title 7 | const content = message && message.content 8 | if (window.Notification) { 9 | const ua = navigator.userAgent.toLowerCase() 10 | if (ua.indexOf('safari') !== -1) { 11 | if (ua.indexOf('chrome') > -1) { 12 | // Chrome 13 | Notification.requestPermission().then(permission => { 14 | if (permission === 'granted' && message) { 15 | const notification = new Notification(title, { 16 | body: content 17 | // , 18 | // icon: 'some/icon/url' 19 | }); 20 | notification.onclick = () => { 21 | console.log('点击'); 22 | notification.close(); 23 | }; 24 | } else { 25 | Notification.requestPermission(); 26 | console.log('没有权限,用户拒绝:Notification'); 27 | 28 | vue.$Notice.info({ 29 | title, 30 | desc: content 31 | }) 32 | } 33 | }); 34 | } else { 35 | // Safari 36 | Notification.requestPermission(permission => { 37 | if (permission === 'granted' && message) { 38 | const notification = new Notification(title, { 39 | body: content 40 | // , 41 | // icon: 'some/icon/url' 42 | }); 43 | 44 | notification.onclick = () => { 45 | // console.log('点击'); 46 | notification.close(); 47 | }; 48 | } else { 49 | Notification.requestPermission(); 50 | console.log('没有权限,用户拒绝:Notification'); 51 | vue.$Notice.info({ 52 | title, 53 | desc: content 54 | }) 55 | } 56 | }) 57 | } 58 | } 59 | } else { 60 | console.log('不支持Notification'); 61 | vue.$Notice.info({ 62 | title, 63 | desc: content 64 | }) 65 | } 66 | } 67 | 68 | export default notification 69 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import { 2 | loginByMobile, 3 | logout 4 | } from '../../resources/user' 5 | import Cookies from 'js-cookie' 6 | import { URL } from '../../common/config' 7 | 8 | const user = { 9 | state: { 10 | mobile: Cookies.get('mobile'), 11 | nickName: Cookies.get('nickName'), 12 | userId: Cookies.get('userId'), 13 | token: Cookies.get('token'), 14 | }, 15 | 16 | mutations: { 17 | SET_TOKEN: (state, token) => { 18 | state.token = token; 19 | }, 20 | SET_USERID: (state, userId) => { 21 | state.userId = userId 22 | }, 23 | SET_MOBILE: (state, mobile) => { 24 | state.mobile = mobile 25 | }, 26 | SET_NAME: (state, name) => { 27 | state.nickName = name; 28 | } 29 | }, 30 | actions: { 31 | // 用户名登录 32 | LoginByMobile({ 33 | commit, 34 | dispatch 35 | }, userInfo) { 36 | const mobile = userInfo.mobile.trim(); 37 | return new Promise((resolve, reject) => { 38 | loginByMobile(mobile, userInfo.password).then(response => { 39 | const data = response.data 40 | Cookies.set('token', data.token) 41 | Cookies.set('userId', data.id) 42 | Cookies.set('nickName', data.nickName) 43 | Cookies.set('mobile', data.mobile) 44 | Cookies.set('user',data); 45 | commit('SET_TOKEN', data.token) 46 | commit('SET_NAME', data.username) 47 | commit('SET_USERID', data.id) 48 | commit('SET_MOBILE', mobile) 49 | dispatch('getFriendList') 50 | // commit('SET_AVATAR', data.avatar ? URL + data.avatar.url : defaultAvatar) 51 | resolve() 52 | }).catch(error => { 53 | reject(error) 54 | }) 55 | }) 56 | }, 57 | // 登出 58 | LogOut({ 59 | commit, 60 | state 61 | }) { 62 | return new Promise((resolve, reject) => { 63 | logout(state.token).then(() => { 64 | commit('SET_TOKEN', '') 65 | commit('SET_NAME', '') 66 | commit('SET_USERID', '') 67 | commit('SET_MOBILE', '') 68 | Cookies.remove('token') 69 | Cookies.remove('nickName') 70 | Cookies.remove('userId') 71 | resolve() 72 | }).catch(error => { 73 | reject(error) 74 | }) 75 | }) 76 | }, 77 | } 78 | } 79 | export default user 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blogclient", 3 | "version": "1.0.0", 4 | "description": "A Vue.js project", 5 | "author": "chao ", 6 | "private": true, 7 | "scripts": { 8 | "dev": "node build/dev-server.js", 9 | "start": "npm run dev", 10 | "build": "node build/build.js" 11 | }, 12 | "dependencies": { 13 | "@xkeshi/vue-countdown": "^0.4.0", 14 | "axios": "^0.17.1", 15 | "compression-webpack-plugin": "^1.1.6", 16 | "github-markdown-css": "^2.9.0", 17 | "iview": "^2.8.0", 18 | "js-cookie": "^2.2.0", 19 | "mavon-editor": "^2.2.10", 20 | "moment": "^2.20.1", 21 | "socket.io-client": "^2.0.4", 22 | "sosnail": "0.0.8", 23 | "v-distpicker": "^1.0.16", 24 | "vue": "^2.5.13", 25 | "vue-quill-editor": "^2.3.2", 26 | "vue-router": "^2.7.0", 27 | "vue-socket.io": "^2.1.1-b", 28 | "vuex": "^3.0.1" 29 | }, 30 | "devDependencies": { 31 | "autoprefixer": "^7.1.2", 32 | "babel-core": "^6.22.1", 33 | "babel-loader": "^7.1.1", 34 | "babel-plugin-transform-runtime": "^6.22.0", 35 | "babel-preset-env": "^1.3.2", 36 | "babel-preset-stage-2": "^6.22.0", 37 | "babel-register": "^6.22.0", 38 | "chalk": "^2.0.1", 39 | "connect-history-api-fallback": "^1.3.0", 40 | "copy-webpack-plugin": "^4.0.1", 41 | "css-loader": "^0.28.7", 42 | "eventsource-polyfill": "^0.9.6", 43 | "express": "^4.14.1", 44 | "extract-text-webpack-plugin": "^3.0.0", 45 | "file-loader": "^1.1.4", 46 | "friendly-errors-webpack-plugin": "^1.6.1", 47 | "html-webpack-plugin": "^2.30.1", 48 | "http-proxy-middleware": "^0.17.3", 49 | "node-sass": "^4.7.2", 50 | "opn": "^5.1.0", 51 | "optimize-css-assets-webpack-plugin": "^3.2.0", 52 | "ora": "^1.2.0", 53 | "portfinder": "^1.0.13", 54 | "rimraf": "^2.6.0", 55 | "sass-loader": "^6.0.6", 56 | "semver": "^5.3.0", 57 | "shelljs": "^0.7.6", 58 | "style-loader": "^0.19.0", 59 | "stylus-loader": "^3.0.1", 60 | "url-loader": "^0.5.8", 61 | "vue-loader": "^13.0.4", 62 | "vue-style-loader": "^3.0.3", 63 | "vue-template-compiler": "^2.5.13", 64 | "webpack": "^3.6.0", 65 | "webpack-bundle-analyzer": "^2.9.0", 66 | "webpack-dev-middleware": "^1.12.0", 67 | "webpack-hot-middleware": "^2.18.2", 68 | "webpack-merge": "^4.1.0" 69 | }, 70 | "engines": { 71 | "node": ">= 4.0.0", 72 | "npm": ">= 3.0.0" 73 | }, 74 | "browserslist": [ 75 | "> 1%", 76 | "last 2 versions", 77 | "not ie <= 8" 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /src/components/ChatList.vue: -------------------------------------------------------------------------------- 1 | 31 | 61 | 67 | -------------------------------------------------------------------------------- /src/components/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 52 | 84 | 85 | 106 | -------------------------------------------------------------------------------- /src/assets/css/article.css: -------------------------------------------------------------------------------- 1 | .article-list{padding: 5px 0; background-color: #fff; border-radius: 2px;font-size: 12px;} 2 | .article-list li:last-child{border-bottom: none;} 3 | 4 | .article-list .article-list-li{position: relative; height: 120px; margin-top: 0; padding: 10px 0 10px 75px; border-bottom: 1px dotted #E9E9E9;} 5 | .article-list-li .article-list-avatar{position: absolute; left: 15px; top: 10px;} 6 | .article-list-li h2{line-height: 26px; font-size: 0;} 7 | .article-list-li h2 *{display: inline-block; *display: inline; *zoom: 1; vertical-align: top;} 8 | .article-list-li h2 a{max-width: 86%; margin-left: 100px; margin-right: 10px;overflow: hidden; color:#333;text-overflow: ellipsis; white-space: nowrap; font-size: 16px;} 9 | .article-list-li h2 span{position: relative; top: 3px; margin-left: 5px;} 10 | .article-list-li a:hover{color: #009E94;} 11 | .article-list-li p{position: relative; line-height: 20px; margin-left: 100px;font-size: 12px; color: #999;margin-top:10px; } 12 | .article-list-li p span{padding-right: 15px;} 13 | .article-list-li p span a{color: #999;} 14 | .article-list-avatar img{width: 120px; height: 100px} 15 | .article-list-hint{position: absolute; right: 0; top: -2px;} 16 | .article-list-hint i{padding-left: 10px; padding-right:5px;font-size:18px;color: #ccc;vertical-align: middle;} 17 | .article-list-li .article-abstract{position: relative; line-height: 24px; margin-left: 100px;margin-right: 10px;font-size: 13px;color:#333;font-weight:bold;overflow:hidden; text-overflow:ellipsis; 18 | display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;} 19 | 20 | /* Tip标签 */ 21 | .article-tip{position: relative;} 22 | .article-tip span{display: inline-block; *display: inline; *zoom: 1; vertical-align: top;} 23 | .article-tip span{height: 20px; line-height: 20px; padding: 0 5px; background-color: #999; color: #fff; font-size: 12px; border-radius: 0;} 24 | .article-tip .article-tip-stick{background-color: #393D49;} 25 | .article-tip .article-tip-jing{background-color: #c00;} 26 | .article-tip .article-tip-jie{background-color: #8FCDA0;} 27 | 28 | .personal-content{ position: relative;min-height: 250px;min-width:320px; margin-top: 15px;background: #fff;border-radius: 4px;} 29 | .personal-avatar{position: absolute;left:50%;transform: translate(-50%)} 30 | .personal-avatar img{width: 80px; height: 80px;border-radius:40px;} 31 | .personal-avatar p{text-align: center} 32 | .motto{position: absolute;top:120px;left:50%;width: 100%;transform: translateX(-50%);margin-bottom:5px;border-bottom: 1px solid #E9E9E9;} 33 | .motto h2{text-align: center;margin-bottom:5px;} 34 | 35 | .contact{position: absolute; bottom: 10px;left:50%;transform: translateX(-50%);} 36 | .contact i{text-align:center;padding:5px;color: #4B505E} 37 | 38 | .hot-content{ position: relative;min-height: 200px;min-width:320px; margin-top: 15px;overflow: hidden;background: #fff;border-radius: 4px;} 39 | .hot-content .title{position: absolute; top:10px;width:100%;border-bottom: 1px solid #E9E9E9;} 40 | .hot-content .title a{color:#333;margin-left:15px;font-size: 14px} 41 | .hot-content-main{ position: absolute; top:40px;margin-left: 15px} 42 | .hot-content-main li{list-style-type: disc;list-style-position: inside;margin-bottom: 5px} 43 | .hot-content-main li a{padding: 10px 15px;font-size: 16px;font-weight:500;} 44 | 45 | .left-login{position: relative;padding:10px;min-width:320px;min-height: 50px; margin-top: 15px;overflow: hidden;background: #fff;border-radius: 4px;} 46 | .left-login .button{position: absolute;left:50%;transform: translateX(-50%);} 47 | 48 | .left-personal{ 49 | position: relative;min-width:320px;min-height: 50px; margin-top: 15px;overflow: hidden;background: #fff;border-radius: 4px; 50 | } 51 | .chat-container { 52 | height: 100vh; 53 | background-color: #2d3a4b; 54 | background-image: url("../images/bg_login.jpg"); 55 | background-repeat: no-repeat; 56 | background-size: cover; 57 | } 58 | .chat-content { 59 | position: fixed; 60 | top: 50%; 61 | width: 700px; 62 | height: 470px; 63 | background-color: #fff; 64 | transform: translateY(-50%); 65 | } 66 | -------------------------------------------------------------------------------- /src/store/modules/message.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueSocketio from 'vue-socket.io'; 3 | import store from '../../store'; 4 | import socketio from 'socket.io-client'; 5 | import { 6 | URL 7 | } from '../../common/config' 8 | import notification from '@/utils/notification' 9 | import Cookies from 'js-cookie' 10 | import { 11 | getFriendList 12 | } from '../../resources/user' 13 | import util from '../../utils/util' 14 | const messages = { 15 | state: { 16 | connect: false, 17 | messages: [], 18 | changed: null, 19 | friendList: [], 20 | chatRoomId:null 21 | }, 22 | mutations: { 23 | SOCKET_CONNECT: (state, status) => { 24 | state.connect = true; 25 | }, 26 | SET_FRIENDS: (state, list) => { 27 | state.friendList = list 28 | }, 29 | SET_MESSAGE: (state, message) => { 30 | if(state.chatRoomId == message.chatRoomId){ 31 | state.messages = util.addDateTimeChatMessage(message,state.messages,false); 32 | } 33 | }, 34 | SET_MESSAGES: (state, messages) => { 35 | for(let i =0 ; i{ 40 | state.messages=[]; 41 | }, 42 | SET_CHATROOMID: (state, chatRoomId) => { 43 | state.chatRoomId = chatRoomId; 44 | }, 45 | SET_UNREADMESSAGENUM:(state,chatMessage) => { 46 | for(let i =0;i{ 57 | for(let i =0;i { 67 | if (!Vue.prototype.$socket) { 68 | Vue.use(VueSocketio, socketio(URL + '/chat', { 69 | query: { 70 | access_token: Cookies.get('token'), 71 | mobile: Cookies.get('mobile') 72 | }, 73 | transports: ['websocket'] 74 | }), store); 75 | } else { 76 | Vue.prototype.$socket.query.access_token = Cookies.get('token'); 77 | Vue.prototype.$socket.query.mobile = Cookies.get('mobile'); 78 | Vue.prototype.$socket.connect(); 79 | } 80 | }, 81 | socket_socketConnect: (context, message) => { 82 | context.dispatch('getFriendMessageCount'); 83 | context.dispatch('getFriendList'); 84 | context.dispatch('getFriendMessage'); 85 | context.dispatch('joinRooms'); 86 | }, 87 | socket_socketNewchatroom: (context, message) => { 88 | context.dispatch('getFriendList'); 89 | }, 90 | joinRooms: (context) => { 91 | Vue.prototype.$socket.emit('joinRooms',function(err,isJoin){ 92 | console.log('isJoin',isJoin); 93 | }) 94 | }, 95 | getFriendList: (context) => { 96 | return new Promise((resolve, reject) => { 97 | getFriendList().then(response => { 98 | 99 | context.commit('SET_FRIENDS', response.data); 100 | resolve() 101 | }).catch(error => { 102 | reject(error) 103 | }) 104 | }); 105 | }, 106 | socket_socketMessage: (context, message) => { 107 | context.commit('SET_MESSAGE',message[0]); 108 | context.commit('SET_UNREADMESSAGENUM',message[0]); 109 | notification({title:message[0].sender.nickName,content:message[0].messageContent}); 110 | }, 111 | getMessages: (context, chatRoomId) => { 112 | context.commit('CLEAR_MESSAGES'); 113 | context.commit('SET_CHATROOMID',chatRoomId); 114 | context.dispatch('readMessages',chatRoomId); 115 | Vue.prototype.$socket.emit('getHistoryMessage', { 116 | chatRoomId: chatRoomId, 117 | limit: 5 118 | }, (err, messages) => { 119 | context.commit('SET_MESSAGES', messages); 120 | }); 121 | }, 122 | readMessages:(context,chatRoomId)=> { 123 | Vue.prototype.$socket.emit('readChatMessage', { 124 | chatRoomId: chatRoomId, 125 | }, (err, messages) => { 126 | context.commit('CLEAR_UNREADMESSAGENUM',chatRoomId); 127 | }); 128 | }, 129 | setMessage:(context,message) =>{ 130 | context.commit('SET_MESSAGE',message); 131 | } 132 | } 133 | } 134 | 135 | export default messages 136 | -------------------------------------------------------------------------------- /src/utils/util.js: -------------------------------------------------------------------------------- 1 | var 2 | ArrayProto = Array.prototype, 3 | ObjProto = Object.prototype; 4 | 5 | var 6 | slice = ArrayProto.slice, 7 | toString = ObjProto.toString; 8 | 9 | var util = {}; 10 | 11 | util.isArray = function(obj) { 12 | return Array.isArray(obj); 13 | }; 14 | 15 | var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; 16 | util.isArrayLike = function(obj) { 17 | if (typeof obj !== 'object' || !obj) { 18 | return false; 19 | } 20 | var length = obj.length; 21 | return typeof length === 'number' && 22 | length % 1 === 0 && length >= 0 && length <= MAX_ARRAY_INDEX; 23 | }; 24 | 25 | util.isObject = function(obj) { 26 | var type = typeof obj; 27 | return type === 'function' || type === 'object' && !!obj; 28 | }; 29 | 30 | 31 | util.each = function(obj, callback) { 32 | var i, 33 | len; 34 | if (util.isArray(obj)) { 35 | for (i = 0, len = obj.length; i < len; i++) { 36 | if (callback(obj[i], i, obj) === false) { 37 | break; 38 | } 39 | } 40 | } else { 41 | for (i in obj) { 42 | if (callback(obj[i], i, obj) === false) { 43 | break; 44 | } 45 | } 46 | } 47 | return obj; 48 | }; 49 | 50 | util.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) { 51 | util['is' + name] = function(obj) { 52 | return toString.call(obj) === '[object ' + name + ']'; 53 | }; 54 | }); 55 | 56 | util.toArray = function(list, start) { 57 | start = start || 0 58 | var i = list.length - start 59 | var ret = new Array(i) 60 | while (i--) { 61 | ret[i] = list[i + start] 62 | } 63 | return ret 64 | } 65 | 66 | util.toNumber = function(value) { 67 | if (typeof value !== 'string') { 68 | return value 69 | } else { 70 | var parsed = Number(value) 71 | return isNaN(parsed) ? 72 | value : 73 | parsed 74 | } 75 | }; 76 | 77 | util.convertArray = function(value) { 78 | if (util.isArray(value)) { 79 | return value 80 | } else if (util.isPlainObject(value)) { 81 | // convert plain object to array. 82 | var keys = Object.keys(value) 83 | var i = keys.length 84 | var res = new Array(i) 85 | var key 86 | while (i--) { 87 | key = keys[i] 88 | res[i] = { 89 | $key: key, 90 | $value: value[key] 91 | } 92 | } 93 | return res 94 | } else { 95 | return value || [] 96 | } 97 | } 98 | 99 | function multiIndex(obj, is) { // obj,['1','2','3'] -> ((obj['1'])['2'])['3'] 100 | return is.length ? multiIndex(obj[is[0]], is.slice(1)) : obj 101 | } 102 | 103 | util.getPath = function(obj, is) { // obj,'1.2.3' -> multiIndex(obj,['1','2','3']) 104 | return multiIndex(obj, is.split('.')) 105 | } 106 | 107 | /** 108 | * Strict object type check. Only returns true 109 | * for plain JavaScript objects. 110 | * 111 | * @param {*} obj 112 | * @return {Boolean} 113 | */ 114 | 115 | var toString = Object.prototype.toString 116 | var OBJECT_STRING = '[object Object]' 117 | util.isPlainObject = function(obj) { 118 | return toString.call(obj) === OBJECT_STRING 119 | } 120 | 121 | util.addDateTimeChatMessage = function(chatMessage, oldChatMessages, reverse) { 122 | var messages = oldChatMessages; 123 | var dateTimeMessage; 124 | var l = messages.length; 125 | var c1, c2; 126 | //历史消息 127 | if (reverse == true) { 128 | if (messages.length > 0) { 129 | c1 = new Date(messages[0].createdAt).getTime(); 130 | c2 = new Date(chatMessage.createdAt).getTime(); 131 | if (enableAddTime(c1, c2,l)) { 132 | dateTimeMessage = buildDateTimeMessage(c1); 133 | messages.unshift(dateTimeMessage); 134 | messages.unshift(chatMessage); 135 | } else { 136 | messages.unshift(chatMessage); 137 | } 138 | } else { 139 | messages.unshift(chatMessage); 140 | } 141 | } else { 142 | //新消息 143 | if (messages.length == 0) { 144 | messages.push(chatMessage); 145 | } else { 146 | c1 = new Date(messages[l - 1].createdAt).getTime(); 147 | c2 = new Date(chatMessage.createdAt).getTime(); 148 | if (enableAddTime(c1, c2, l)) { 149 | dateTimeMessage = buildDateTimeMessage(c2); 150 | messages.push(dateTimeMessage); 151 | messages.push(chatMessage); 152 | } else { 153 | messages.push(chatMessage); 154 | } 155 | } 156 | } 157 | return messages; 158 | } 159 | 160 | function buildDateTimeMessage(dateTime) { 161 | var chatMessage = {}; 162 | chatMessage.id = guidGenerator(); 163 | chatMessage.createdAt = dateTime - 1; 164 | chatMessage.messageContent = new Date(chatMessage.createdAt); 165 | chatMessage.messageType = 'DATETIME'; 166 | return chatMessage; 167 | } 168 | 169 | function guidGenerator() { 170 | function s4() { 171 | return (((1 + Math.random()) * 0x10000) || 0).toString(16).substring(1); 172 | } 173 | 174 | return (s4() + s4() + "-" + s4() + "-" + s4() + "-" + s4() + "-" + s4() + s4() + s4()); 175 | } 176 | 177 | function enableAddTime(c1, c2,length) { 178 | return Boolean(Math.abs(c2 - c1) > 5 * 60 * 1000 || length % 10 == 0); 179 | } 180 | export default util; 181 | 182 | Date.prototype.toJSON = function () { return this.toLocaleString(); } 183 | -------------------------------------------------------------------------------- /src/components/ChatMenu.vue: -------------------------------------------------------------------------------- 1 | 70 | 161 | -------------------------------------------------------------------------------- /src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 94 | 95 | 113 | 114 | 206 | -------------------------------------------------------------------------------- /src/assets/css/articleDetail.css: -------------------------------------------------------------------------------- 1 | .article-detail-hint{margin: 5px 0 15px;} 2 | .article-detail-hint .layui-btn+.layui-btn{margin-left: 0;} 3 | .article-tip{position: relative;} 4 | .article-tip span{display: inline-block; *display: inline; *zoom: 1; vertical-align: top;} 5 | .article-tip span{height: 20px; line-height: 20px; padding: 0 5px; background-color: #999; color: #fff; font-size: 16px; border-radius: 0;} 6 | .article-tip .article-tip-stick{background-color: #393D49;} 7 | .article-tip .article-tip-jing{background-color: #c00;} 8 | .article-tip .article-tip-jie{background-color: #8FCDA0;} 9 | 10 | .article-panel{background-color: #fff; border-radius: 2px;} 11 | .article-panel[pad20]{padding: 20px;} 12 | .article-panel-title{position: relative; height: 40px; line-height: 40px; padding: 0 15px; margin-bottom: 5px; background-color: #f8f8f8; color: #333; border-radius: 2px 2px 0 0; font-size: 14px;} 13 | 14 | .detail-box{margin-bottom: 15px; padding: 20px;} 15 | .detail h1{font-size: 24px; line-height: 30px; padding: 5px 0;} 16 | .detail-about{position:relative; margin-top:5px; line-height:20px; background-color: #F2F2F2; padding: 15px; color:#999;} 17 | .detail-about span, 18 | .jie-about span, 19 | .detail-about .jie-user{margin-right:10px; display:inline-block; *display:inline; *zoom:1; vertical-align:top; font-size:12px;} 20 | .detail-about .jie-status, .detail-about .jie-status-ok{color:#fff;} 21 | .detail-about .article-jing{padding:0 6px; background-color:#c00; color:#fff;} 22 | .detail-about .jie-user{position:relative;} 23 | .detail-about .jie-user cite{top: -3px; left: 56px; width: 260px; color: #4f99cf; font-size: 14px;} 24 | .detail-about .jie-user cite em{padding-left:5px; color:#999; cursor:default; font-size:12px; font-style: normal;} 25 | .detail-about .jie-user img{position: relative; display: block; margin: 0; top: 0; border-radius: 2px;} 26 | .detail-hits{position:absolute; left: 71px; bottom: 15px; font-size: 0;} 27 | .detail-hits span{margin-top: 0; font-size: 12px;} 28 | .detail-hits .layui-btn{border-radius: 0;} 29 | .detail-hits .layui-btn+.layui-btn{margin-left: 5px;} 30 | .detail-hits .jie-admin{margin-right: 1px;} 31 | .detail-body{margin: 20px 0 50px; min-height: 202px; line-height:26px; font-size:16px; word-wrap: break-word;} 32 | .detail-body p{margin-bottom:15px;} 33 | .detail-body a{color:#4f99cf;} 34 | .detail-body img{max-width: 100%; cursor: crosshair;} 35 | .detail-body table{margin: 10px 0 15px;} 36 | .detail-body table thead{background-color:#f2f2f2;} 37 | .detail-body table th, 38 | .detail-body table td{padding: 10px 20px; line-height: 22px; border: 1px solid #DFDFDF; font-size: 14px; font-weight: 400;} 39 | .detail-about-reply{background: none; padding: 0;} 40 | .detail-about-reply .detail-hits{left: 56px; bottom: 0;} 41 | .detail .page-title{ border: none; background-color: #f2f2f2;} 42 | .jie-row li{position: relative; margin-bottom: 10px; padding-bottom: 10px; border-bottom: 1px dotted #E9E9E9; font-size: 0;} 43 | .jie-row li *{display:inline-block; *display:inline; *zoom:1; vertical-align:top; line-height: 24px; font-size:12px;} 44 | .jie-row li span{height: 24px; line-height: 24px; padding: 0 10px; margin-right: 10px; background-color: #DADADA; color:#fff; font-size:12px;} 45 | .jie-row li .fly-stick{background-color:#393D49;} 46 | .jie-row li .fly-jing{background-color:#CC0000;} 47 | .jie-row li .jie-status{margin:0 10px 0 0;} 48 | .jie-row li .jie-status-ok{background-color:#8FCDA0;} 49 | .jie-row li a{ padding-right:15px; font-size:14px;} 50 | .jie-row li cite{padding-right:15px;} 51 | .jie-row li i, .jie-row li em, .jie-row li cite{font-size:12px; color:#999; font-style: normal;} 52 | .jie-row li .mine-edit{margin-left:15px; padding:0 6px; background-color: #8FCDA0; color:#fff; font-size:12px;} 53 | .jie-row li em{position:absolute; right:0; top:0;} 54 | .jie-row li .jie-user{} 55 | .jie-row li .jie-title{max-width: 330px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;} 56 | .jie-row li .jie-user img{position:relative; top: 16px; width: 35px; height: 35px;} 57 | 58 | .jie-add{position:absolute; right:0; top:0; font-size:14px;} 59 | .jie-list li h2 span{display:inline-block; *display:inline; *zoom:1; vertical-align:top;} 60 | .jie-list li{position:relative; margin-bottom:0; padding:10px 0 10px 60px; border-bottom:1px dotted #E9E9E9} 61 | .jie-user img{position:absolute; left:0; top:50%; width:46px; height:46px; margin-top:-23px;} 62 | .jie-user cite{position:absolute; right:0; top:10px; line-height:26px; color:#999;} 63 | .jie-user cite i{font-style: normal;} 64 | .jie-list li h2{margin-right:80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;} 65 | .jie-list li h2 a{font-size:16px; line-height:28px;} 66 | .jie-list li h2 span{position:relative; top:4px; height:20px; line-height:20px; padding:0 10px; margin-left:10px; font-size:12px; background-color:#393D49; color:#fff; } 67 | .jie-list li h2 .fly-jing{padding:0 6px; background-color:#c00;} 68 | .jie-about{position:relative; margin-top:5px; line-height:26px; font-size:0; color:#666;} 69 | 70 | .jie-status{line-height:20px; margin-top:3px; padding:0 8px; background-color:#ccc;} 71 | .jie-status-ok{background-color:#8FCDA0;} 72 | .jie-about .jie-status, .jie-about .jie-status-ok{color:#fff;} 73 | .jie-about span{color:#999;} 74 | .jie-about .jie-hits{position:absolute; right:0; top:0; margin:0;} 75 | .jie-hits *{padding-left:15px;} 76 | 77 | .jie-form{width:860px;} 78 | .jie-form .jie-addexp{margin-left:15px;} 79 | .jie-form .jie-addexp span{color:#FF7200;} 80 | .jie-form .jie-addexp .layui-form-sltitle{width:55px;} 81 | .jie-form .jie-addexp li a{min-width:87px;} 82 | 83 | /* 求解管理 */ 84 | .jie-admin{cursor:pointer;} 85 | .detail-hits .jie-admin{color:#fff; padding:0 10px; } 86 | .detail-hits .jie-admin a{color:#fff;} 87 | .jieda-admin{position:absolute; right:0; top:0;} 88 | 89 | /* 回答 */ 90 | .jieda{margin-bottom: 30px;} 91 | .jieda li{position: relative; padding: 20px 0 10px; border-bottom:1px dotted #DFDFDF;} 92 | .jieda li:last-child{border-bottom: none;} 93 | .jieda .fly-none{height: 50px; min-height: 0; padding: 50px 0 0;} 94 | .jieda .icon-caina{position:absolute; right:10px; top:15px; font-size:60px; color: #58A571;} 95 | 96 | .jieda-body{margin: 25px 0 20px; min-height: 0; line-height: 24px; font-size:14px;} 97 | .jieda-body p{margin-bottom: 10px;} 98 | .jieda-body a{color:#4f99cf} 99 | .jieda-reply{position:relative;} 100 | .jieda-reply span{padding-right:20px; color:#999; cursor:pointer;} 101 | .jieda-reply span:hover{color:#666;} 102 | .jieda-reply span i{margin-right:5px; font-size:16px;} 103 | .jieda-reply span em{font-style: normal;font-size:18px} 104 | .jieda-reply span .icon-zan{font-size: 22px;} 105 | .jieda-reply .zanok, 106 | .jieda-reply .jieda-zan:hover{color:#c00} 107 | .jieda-reply span .icon-svgmoban53{position: relative; top: 1px;} 108 | -------------------------------------------------------------------------------- /src/components/Register.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 119 | 120 | 138 | 139 | 236 | -------------------------------------------------------------------------------- /src/components/chat/chatRoom.vue: -------------------------------------------------------------------------------- 1 | 58 | 59 | 161 | 251 | -------------------------------------------------------------------------------- /src/components/chat/friendsMenu.vue: -------------------------------------------------------------------------------- 1 | 122 | 225 | 245 | -------------------------------------------------------------------------------- /src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 276 | import "../assets/css/article.css" 277 | 389 | 390 | 391 | -------------------------------------------------------------------------------- /src/utils/sortPickerView.js: -------------------------------------------------------------------------------- 1 | 2 | // 这段是我直接copy的 具体使用方法 如下 3 | /** 4 | * author: Di (微信小程序开发工程师) 5 | * organization: WeAppDev(微信小程序开发论坛)(http://weappdev.com) 6 | * 垂直微信小程序开发交流社区 7 | * 8 | * github地址: https://github.com/icindy/wxParse 9 | * 10 | * for: 微信小程序富文本解析 11 | * detail : http://weappdev.com/t/wxparse-alpha0-1-html-markdown/184 12 | */ 13 | 14 | //函数参考http://wuxiangqian.iteye.com/blog/1426339 15 | // 改动为判定输入数组,产出排名 16 | // 汉字拼音首字母列表 本列表包含了20902个汉字,用于配合 ToChineseSpell 17 | //函数使用,本表收录的字符的Unicode编码范围为19968至40869, XDesigner 整理 18 | let strChineseFirstPY =`YDYQSXMWZSSXJBYMGCCZQPSSQBYCDSCDQLDYLYBSSJGYZZJJFKCCLZDHWDWZJLJPFYYNWJJTMYHZWZHFLZPPQHGSCYYYNJQYXXGJHHSDSJNKKTMOMLCRXYPSNQSECCQZGGLLYJLMYZZSECYKYYHQWJSSGGYXYZYJWWKDJHYCHMYXJTLXJYQBYXZLDWRDJRWYSRLDZJPCBZJJBRCFTLECZSTZFXXZHTRQHYBDLYCZSSYMMRFMYQZPWWJJYFCRWFDFZQPYDDWYXKYJAWJFFXYPSFTZYHHYZYSWCJYXSCLCXXWZZXNBGNNXBXLZSZSBSGPYSYZDHMDZBQBZCWDZZYYTZHBTSYYBZGNTNXQYWQSKBPHHLXGYBFMJEBJHHGQTJCYSXSTKZHLYCKGLYSMZXYALMELDCCXGZYRJXSDLTYZCQKCNNJWHJTZZCQLJSTSTBNXBTYXCEQXGKWJYFLZQLYHYXSPSFXLMPBYSXXXYDJCZYLLLSJXFHJXPJBTFFYABYXBHZZBJYZLWLCZGGBTSSMDTJZXPTHYQTGLJSCQFZKJZJQNLZWLSLHDZBWJNCJZYZSQQYCQYRZCJJWYBRTWPYFTWEXCSKDZCTBZHYZZYYJXZCFFZZMJYXXSDZZOTTBZLQWFCKSZSXFYRLNYJMBDTHJXSQQCCSBXYYTSYFBXDZTGBCNSLCYZZPSAZYZZSCJCSHZQYDXLBPJLLMQXTYDZXSQJTZPXLCGLQTZWJBHCTSYJSFXYEJJTLBGXSXJMYJQQPFZASYJNTYDJXKJCDJSZCBARTDCLYJQMWNQNCLLLKBYBZZSYHQQLTWLCCXTXLLZNTYLNEWYZYXCZXXGRKRMTCNDNJTSYYSSDQDGHSDBJGHRWRQLYBGLXHLGTGXBQJDZPYJSJYJCTMRNYMGRZJCZGJMZMGXMPRYXKJNYMSGMZJYMKMFXMLDTGFBHCJHKYLPFMDXLQJJSMTQGZSJLQDLDGJYCALCMZCSDJLLNXDJFFFFJCZFMZFFPFKHKGDPSXKTACJDHHZDDCRRCFQYJKQCCWJDXHWJLYLLZGCFCQDSMLZPBJJPLSBCJGGDCKKDEZSQCCKJGCGKDJTJDLZYCXKLQSCGJCLTFPCQCZGWPJDQYZJJBYJHSJDZWGFSJGZKQCCZLLPSPKJGQJHZZLJPLGJGJJTHJJYJZCZMLZLYQBGJWMLJKXZDZNJQSYZMLJLLJKYWXMKJLHSKJGBMCLYYMKXJQLBMLLKMDXXKWYXYSLMLPSJQQJQXYXFJTJDXMXXLLCXQBSYJBGWYMBGGBCYXPJYGPEPFGDJGBHBNSQJYZJKJKHXQFGQZKFHYGKHDKLLSDJQXPQYKYBNQSXQNSZSWHBSXWHXWBZZXDMNSJBSBKBBZKLYLXGWXDRWYQZMYWSJQLCJXXJXKJEQXSCYETLZHLYYYSDZPAQYZCMTLSHTZCFYZYXYLJSDCJQAGYSLCQLYYYSHMRQQKLDXZSCSSSYDYCJYSFSJBFRSSZQSBXXPXJYSDRCKGJLGDKZJZBDKTCSYQPYHSTCLDJDHMXMCGXYZHJDDTMHLTXZXYLYMOHYJCLTYFBQQXPFBDFHHTKSQHZYYWCNXXCRWHOWGYJLEGWDQCWGFJYCSNTMYTOLBYGWQWESJPWNMLRYDZSZTXYQPZGCWXHNGPYXSHMYQJXZTDPPBFYHZHTJYFDZWKGKZBLDNTSXHQEEGZZYLZMMZYJZGXZXKHKSTXNXXWYLYAPSTHXDWHZYMPXAGKYDXBHNHXKDPJNMYHYLPMGOCSLNZHKXXLPZZLBMLSFBHHGYGYYGGBHSCYAQTYWLXTZQCEZYDQDQMMHTKLLSZHLSJZWFYHQSWSCWLQAZYNYTLSXTHAZNKZZSZZLAXXZWWCTGQQTDDYZTCCHYQZFLXPSLZYGPZSZNGLNDQTBDLXGTCTAJDKYWNSYZLJHHZZCWNYYZYWMHYCHHYXHJKZWSXHZYXLYSKQYSPSLYZWMYPPKBYGLKZHTYXAXQSYSHXASMCHKDSCRSWJPWXSGZJLWWSCHSJHSQNHCSEGNDAQTBAALZZMSSTDQJCJKTSCJAXPLGGXHHGXXZCXPDMMHLDGTYBYSJMXHMRCPXXJZCKZXSHMLQXXTTHXWZFKHCCZDYTCJYXQHLXDHYPJQXYLSYYDZOZJNYXQEZYSQYAYXWYPDGXDDXSPPYZNDLTWRHXYDXZZJHTCXMCZLHPYYYYMHZLLHNXMYLLLMDCPPXHMXDKYCYRDLTXJCHHZZXZLCCLYLNZSHZJZZLNNRLWHYQSNJHXYNTTTKYJPYCHHYEGKCTTWLGQRLGGTGTYGYHPYHYLQYQGCWYQKPYYYTTTTLHYHLLTYTTSPLKYZXGZWGPYDSSZZDQXSKCQNMJJZZBXYQMJRTFFBTKHZKBXLJJKDXJTLBWFZPPTKQTZTGPDGNTPJYFALQMKGXBDCLZFHZCLLLLADPMXDJHLCCLGYHDZFGYDDGCYYFGYDXKSSEBDHYKDKDKHNAXXYBPBYYHXZQGAFFQYJXDMLJCSQZLLPCHBSXGJYNDYBYQSPZWJLZKSDDTACTBXZDYZYPJZQSJNKKTKNJDJGYYPGTLFYQKASDNTCYHBLWDZHBBYDWJRYGKZYHEYYFJMSDTYFZJJHGCXPLXHLDWXXJKYTCYKSSSMTWCTTQZLPBSZDZWZXGZAGYKTYWXLHLSPBCLLOQMMZSSLCMBJCSZZKYDCZJGQQDSMCYTZQQLWZQZXSSFPTTFQMDDZDSHDTDWFHTDYZJYQJQKYPBDJYYXTLJHDRQXXXHAYDHRJLKLYTWHLLRLLRCXYLBWSRSZZSYMKZZHHKYHXKSMDSYDYCJPBZBSQLFCXXXNXKXWYWSDZYQOGGQMMYHCDZTTFJYYBGSTTTYBYKJDHKYXBELHTYPJQNFXFDYKZHQKZBYJTZBXHFDXKDASWTAWAJLDYJSFHBLDNNTNQJTJNCHXFJSRFWHZFMDRYJYJWZPDJKZYJYMPCYZNYNXFBYTFYFWYGDBNZZZDNYTXZEMMQBSQEHXFZMBMFLZZSRXYMJGSXWZJSPRYDJSJGXHJJGLJJYNZZJXHGXKYMLPYYYCXYTWQZSWHWLYRJLPXSLSXMFSWWKLCTNXNYNPSJSZHDZEPTXMYYWXYYSYWLXJQZQXZDCLEEELMCPJPCLWBXSQHFWWTFFJTNQJHJQDXHWLBYZNFJLALKYYJLDXHHYCSTYYWNRJYXYWTRMDRQHWQCMFJDYZMHMYYXJWMYZQZXTLMRSPWWCHAQBXYGZYPXYYRRCLMPYMGKSJSZYSRMYJSNXTPLNBAPPYPYLXYYZKYNLDZYJZCZNNLMZHHARQMPGWQTZMXXMLLHGDZXYHXKYXYCJMFFYYHJFSBSSQLXXNDYCANNMTCJCYPRRNYTYQNYYMBMSXNDLYLYSLJRLXYSXQMLLYZLZJJJKYZZCSFBZXXMSTBJGNXYZHLXNMCWSCYZYFZLXBRNNNYLBNRTGZQYSATSWRYHYJZMZDHZGZDWYBSSCSKXSYHYTXXGCQGXZZSHYXJSCRHMKKBXCZJYJYMKQHZJFNBHMQHYSNJNZYBKNQMCLGQHWLZNZSWXKHLJHYYBQLBFCDSXDLDSPFZPSKJYZWZXZDDXJSMMEGJSCSSMGCLXXKYYYLNYPWWWGYDKZJGGGZGGSYCKNJWNJPCXBJJTQTJWDSSPJXZXNZXUMELPXFSXTLLXCLJXJJLJZXCTPSWXLYDHLYQRWHSYCSQYYBYAYWJJJQFWQCQQCJQGXALDBZZYJGKGXPLTZYFXJLTPADKYQHPMATLCPDCKBMTXYBHKLENXDLEEGQDYMSAWHZMLJTWYGXLYQZLJEEYYBQQFFNLYXRDSCTGJGXYYNKLLYQKCCTLHJLQMKKZGCYYGLLLJDZGYDHZWXPYSJBZKDZGYZZHYWYFQYTYZSZYEZZLYMHJJHTSMQWYZLKYYWZCSRKQYTLTDXWCTYJKLWSQZWBDCQYNCJSRSZJLKCDCDTLZZZACQQZZDDXYPLXZBQJYLZLLLQDDZQJYJYJZYXNYYYNYJXKXDAZWYRDLJYYYRJLXLLDYXJCYWYWNQCCLDDNYYYNYCKCZHXXCCLGZQJGKWPPCQQJYSBZZXYJSQPXJPZBSBDSFNSFPZXHDWZTDWPPTFLZZBZDMYYPQJRSDZSQZSQXBDGCPZSWDWCSQZGMDHZXMWWFYBPDGPHTMJTHZSMMBGZMBZJCFZWFZBBZMQCFMBDMCJXLGPNJBBXGYHYYJGPTZGZMQBQTCGYXJXLWZKYDPDYMGCFTPFXYZTZXDZXTGKMTYBBCLBJASKYTSSQYYMSZXFJEWLXLLSZBQJJJAKLYLXLYCCTSXMCWFKKKBSXLLLLJYXTYLTJYYTDPJHNHNNKBYQNFQYYZBYYESSESSGDYHFHWTCJBSDZZTFDMXHCNJZYMQWSRYJDZJQPDQBBSTJGGFBKJBXTGQHNGWJXJGDLLTHZHHYYYYYYSXWTYYYCCBDBPYPZYCCZYJPZYWCBDLFWZCWJDXXHYHLHWZZXJTCZLCDPXUJCZZZLYXJJTXPHFXWPYWXZPTDZZBDZCYHJHMLXBQXSBYLRDTGJRRCTTTHYTCZWMXFYTWWZCWJWXJYWCSKYBZSCCTZQNHXNWXXKHKFHTSWOCCJYBCMPZZYKBNNZPBZHHZDLSYDDYTYFJPXYNGFXBYQXCBHXCPSXTYZDMKYSNXSXLHKMZXLYHDHKWHXXSSKQYHHCJYXGLHZXCSNHEKDTGZXQYPKDHEXTYKCNYMYYYPKQYYYKXZLTHJQTBYQHXBMYHSQCKWWYLLHCYYLNNEQXQWMCFBDCCMLJGGXDQKTLXKGNQCDGZJWYJJLYHHQTTTNWCHMXCXWHWSZJYDJCCDBQCDGDNYXZTHCQRXCBHZTQCBXWGQWYYBXHMBYMYQTYEXMQKYAQYRGYZSLFYKKQHYSSQYSHJGJCNXKZYCXSBXYXHYYLSTYCXQTHYSMGSCPMMGCCCCCMTZTASMGQZJHKLOSQYLSWTMXSYQKDZLJQQYPLSYCZTCQQPBBQJZCLPKHQZYYXXDTDDTSJCXFFLLCHQXMJLWCJCXTSPYCXNDTJSHJWXDQQJSKXYAMYLSJHMLALYKXCYYDMNMDQMXMCZNNCYBZKKYFLMCHCMLHXRCJJHSYLNMTJZGZGYWJXSRXCWJGJQHQZDQJDCJJZKJKGDZQGJJYJYLXZXXCDQHHHEYTMHLFSBDJSYYSHFYSTCZQLPBDRFRZTZYKYWHSZYQKWDQZRKMSYNBCRXQBJYFAZPZZEDZCJYWBCJWHYJBQSZYWRYSZPTDKZPFPBNZTKLQYHBBZPNPPTYZZYBQNYDCPJMMCYCQMCYFZZDCMNLFPBPLNGQJTBTTNJZPZBBZNJKLJQYLNBZQHKSJZNGGQSZZKYXSHPZSNBCGZKDDZQANZHJKDRTLZLSWJLJZLYWTJNDJZJHXYAYNCBGTZCSSQMNJPJYTYSWXZFKWJQTKHTZPLBHSNJZSYZBWZZZZLSYLSBJHDWWQPSLMMFBJDWAQYZTCJTBNNWZXQXCDSLQGDSDPDZHJTQQPSWLYYJZLGYXYZLCTCBJTKTYCZJTQKBSJLGMGZDMCSGPYNJZYQYYKNXRPWSZXMTNCSZZYXYBYHYZAXYWQCJTLLCKJJTJHGDXDXYQYZZBYWDLWQCGLZGJGQRQZCZSSBCRPCSKYDZNXJSQGXSSJMYDNSTZTPBDLTKZWXQWQTZEXNQCZGWEZKSSBYBRTSSSLCCGBPSZQSZLCCGLLLZXHZQTHCZMQGYZQZNMCOCSZJMMZSQPJYGQLJYJPPLDXRGZYXCCSXHSHGTZNLZWZKJCXTCFCJXLBMQBCZZWPQDNHXLJCTHYZLGYLNLSZZPCXDSCQQHJQKSXZPBAJYEMSMJTZDXLCJYRYYNWJBNGZZTMJXLTBSLYRZPYLSSCNXPHLLHYLLQQZQLXYMRSYCXZLMMCZLTZSDWTJJLLNZGGQXPFSKYGYGHBFZPDKMWGHCXMSGDXJMCJZDYCABXJDLNBCDQYGSKYDQTXDJJYXMSZQAZDZFSLQXYJSJZYLBTXXWXQQZBJZUFBBLYLWDSLJHXJYZJWTDJCZFQZQZZDZSXZZQLZCDZFJHYSPYMPQZMLPPLFFXJJNZZYLSJEYQZFPFZKSYWJJJHRDJZZXTXXGLGHYDXCSKYSWMMZCWYBAZBJKSHFHJCXMHFQHYXXYZFTSJYZFXYXPZLCHMZMBXHZZSXYFYMNCWDABAZLXKTCSHHXKXJJZJSTHYGXSXYYHHHJWXKZXSSBZZWHHHCWTZZZPJXSNXQQJGZYZYWLLCWXZFXXYXYHXMKYYSWSQMNLNAYCYSPMJKHWCQHYLAJJMZXHMMCNZHBHXCLXTJPLTXYJHDYYLTTXFSZHYXXSJBJYAYRSMXYPLCKDUYHLXRLNLLSTYZYYQYGYHHSCCSMZCTZQXKYQFPYYRPFFLKQUNTSZLLZMWWTCQQYZWTLLMLMPWMBZSSTZRBPDDTLQJJBXZCSRZQQYGWCSXFWZLXCCRSZDZMCYGGDZQSGTJSWLJMYMMZYHFBJDGYXCCPSHXNZCSBSJYJGJMPPWAFFYFNXHYZXZYLREMZGZCYZSSZDLLJCSQFNXZKPTXZGXJJGFMYYYSNBTYLBNLHPFZDCYFBMGQRRSSSZXYSGTZRNYDZZCDGPJAFJFZKNZBLCZSZPSGCYCJSZLMLRSZBZZLDLSLLYSXSQZQLYXZLSKKBRXBRBZCYCXZZZEEYFGKLZLYYHGZSGZLFJHGTGWKRAAJYZKZQTSSHJJXDCYZUYJLZYRZDQQHGJZXSSZBYKJPBFRTJXLLFQWJHYLQTYMBLPZDXTZYGBDHZZRBGXHWNJTJXLKSCFSMWLSDQYSJTXKZSCFWJLBXFTZLLJZLLQBLSQMQQCGCZFPBPHZCZJLPYYGGDTGWDCFCZQYYYQYSSCLXZSKLZZZGFFCQNWGLHQYZJJCZLQZZYJPJZZBPDCCMHJGXDQDGDLZQMFGPSYTSDYFWWDJZJYSXYYCZCYHZWPBYKXRYLYBHKJKSFXTZJMMCKHLLTNYYMSYXYZPYJQYCSYCWMTJJKQYRHLLQXPSGTLYYCLJSCPXJYZFNMLRGJJTYZBXYZMSJYJHHFZQMSYXRSZCWTLRTQZSSTKXGQKGSPTGCZNJSJCQCXHMXGGZTQYDJKZDLBZSXJLHYQGGGTHQSZPYHJHHGYYGKGGCWJZZYLCZLXQSFTGZSLLLMLJSKCTBLLZZSZMMNYTPZSXQHJCJYQXYZXZQZCPSHKZZYSXCDFGMWQRLLQXRFZTLYSTCTMJCXJJXHJNXTNRZTZFQYHQGLLGCXSZSJDJLJCYDSJTLNYXHSZXCGJZYQPYLFHDJSBPCCZHJJJQZJQDYBSSLLCMYTTMQTBHJQNNYGKYRQYQMZGCJKPDCGMYZHQLLSLLCLMHOLZGDYYFZSLJCQZLYLZQJESHNYLLJXGJXLYSYYYXNBZLJSSZCQQCJYLLZLTJYLLZLLBNYLGQCHXYYXOXCXQKYJXXXYKLXSXXYQXCYKQXQCSGYXXYQXYGYTQOHXHXPYXXXULCYEYCHZZCBWQBBWJQZSCSZSSLZYLKDESJZWMYMCYTSDSXXSCJPQQSQYLYYZYCMDJDZYWCBTJSYDJKCYDDJLBDJJSODZYSYXQQYXDHHGQQYQHDYXWGMMMAJDYBBBPPBCMUUPLJZSMTXERXJMHQNUTPJDCBSSMSSSTKJTSSMMTRCPLZSZMLQDSDMJMQPNQDXCFYNBFSDQXYXHYAYKQYDDLQYYYSSZBYDSLNTFQTZQPZMCHDHCZCWFDXTMYQSPHQYYXSRGJCWTJTZZQMGWJJTJHTQJBBHWZPXXHYQFXXQYWYYHYSCDYDHHQMNMTMWCPBSZPPZZGLMZFOLLCFWHMMSJZTTDHZZYFFYTZZGZYSKYJXQYJZQBHMBZZLYGHGFMSHPZFZSNCLPBQSNJXZSLXXFPMTYJYGBXLLDLXPZJYZJYHHZCYWHJYLSJEXFSZZYWXKZJLUYDTMLYMQJPWXYHXSKTQJEZRPXXZHHMHWQPWQLYJJQJJZSZCPHJLCHHNXJLQWZJHBMZYXBDHHYPZLHLHLGFWLCHYYTLHJXCJMSCPXSTKPNHQXSRTYXXTESYJCTLSSLSTDLLLWWYHDHRJZSFGXTSYCZYNYHTDHWJSLHTZDQDJZXXQHGYLTZPHCSQFCLNJTCLZPFSTPDYNYLGMJLLYCQHYSSHCHYLHQYQTMZYPBYWRFQYKQSYSLZDQJMPXYYSSRHZJNYWTQDFZBWWTWWRXCWHGYHXMKMYYYQMSMZHNGCEPMLQQMTCWCTMMPXJPJJHFXYYZSXZHTYBMSTSYJTTQQQYYLHYNPYQZLCYZHZWSMYLKFJXLWGXYPJYTYSYXYMZCKTTWLKSMZSYLMPWLZWXWQZSSAQSYXYRHSSNTSRAPXCPWCMGDXHXZDZYFJHGZTTSBJHGYZSZYSMYCLLLXBTYXHBBZJKSSDMALXHYCFYGMQYPJYCQXJLLLJGSLZGQLYCJCCZOTYXMTMTTLLWTGPXYMZMKLPSZZZXHKQYSXCTYJZYHXSHYXZKXLZWPSQPYHJWPJPWXQQYLXSDHMRSLZZYZWTTCYXYSZZSHBSCCSTPLWSSCJCHNLCGCHSSPHYLHFHHXJSXYLLNYLSZDHZXYLSXLWZYKCLDYAXZCMDDYSPJTQJZLNWQPSSSWCTSTSZLBLNXSMNYYMJQBQHRZWTYYDCHQLXKPZWBGQYBKFCMZWPZLLYYLSZYDWHXPSBCMLJBSCGBHXLQHYRLJXYSWXWXZSLDFHLSLYNJLZYFLYJYCDRJLFSYZFSLLCQYQFGJYHYXZLYLMSTDJCYHBZLLNWLXXYGYYHSMGDHXXHHLZZJZXCZZZCYQZFNGWPYLCPKPYYPMCLQKDGXZGGWQBDXZZKZFBXXLZXJTPJPTTBYTSZZDWSLCHZHSLTYXHQLHYXXXYYZYSWTXZKHLXZXZPYHGCHKCFSYHUTJRLXFJXPTZTWHPLYXFCRHXSHXKYXXYHZQDXQWULHYHMJTBFLKHTXCWHJFWJCFPQRYQXCYYYQYGRPYWSGSUNGWCHKZDXYFLXXHJJBYZWTSXXNCYJJYMSWZJQRMHXZWFQSYLZJZGBHYNSLBGTTCSYBYXXWXYHXYYXNSQYXMQYWRGYQLXBBZLJSYLPSYTJZYHYZAWLRORJMKSCZJXXXYXCHDYXRYXXJDTSQFXLYLTSFFYXLMTYJMJUYYYXLTZCSXQZQHZXLYYXZHDNBRXXXJCTYHLBRLMBRLLAXKYLLLJLYXXLYCRYLCJTGJCMTLZLLCYZZPZPCYAWHJJFYBDYYZSMPCKZDQYQPBPCJPDCYZMDPBCYYDYCNNPLMTMLRMFMMGWYZBSJGYGSMZQQQZTXMKQWGXLLPJGZBQCDJJJFPKJKCXBLJMSWMDTQJXLDLPPBXCWRCQFBFQJCZAHZGMYKPHYYHZYKNDKZMBPJYXPXYHLFPNYYGXJDBKXNXHJMZJXSTRSTLDXSKZYSYBZXJLXYSLBZYSLHXJPFXPQNBYLLJQKYGZMCYZZYMCCSLCLHZFWFWYXZMWSXTYNXJHPYYMCYSPMHYSMYDYSHQYZCHMJJMZCAAGCFJBBHPLYZYLXXSDJGXDHKXXTXXNBHRMLYJSLTXMRHNLXQJXYZLLYSWQGDLBJHDCGJYQYCMHWFMJYBMBYJYJWYMDPWHXQLDYGPDFXXBCGJSPCKRSSYZJMSLBZZJFLJJJLGXZGYXYXLSZQYXBEXYXHGCXBPLDYHWETTWWCJMBTXCHXYQXLLXFLYXLLJLSSFWDPZSMYJCLMWYTCZPCHQEKCQBWLCQYDPLQPPQZQFJQDJHYMMCXTXDRMJWRHXCJZYLQXDYYNHYYHRSLSRSYWWZJYMTLTLLGTQCJZYABTCKZCJYCCQLJZQXALMZYHYWLWDXZXQDLLQSHGPJFJLJHJABCQZDJGTKHSSTCYJLPSWZLXZXRWGLDLZRLZXTGSLLLLZLYXXWGDZYGBDPHZPBRLWSXQBPFDWOFMWHLYPCBJCCLDMBZPBZZLCYQXLDOMZBLZWPDWYYGDSTTHCSQSCCRSSSYSLFYBFNTYJSZDFNDPDHDZZMBBLSLCMYFFGTJJQWFTMTPJWFNLBZCMMJTGBDZLQLPYFHYYMJYLSDCHDZJWJCCTLJCLDTLJJCPDDSQDSSZYBNDBJLGGJZXSXNLYCYBJXQYCBYLZCFZPPGKCXZDZFZTJJFJSJXZBNZYJQTTYJYHTYCZHYMDJXTTMPXSPLZCDWSLSHXYPZGTFMLCJTYCBPMGDKWYCYZCDSZZYHFLYCTYGWHKJYYLSJCXGYWJCBLLCSNDDBTZBSCLYZCZZSSQDLLMQYYHFSLQLLXFTYHABXGWNYWYYPLLSDLDLLBJCYXJZMLHLJDXYYQYTDLLLBUGBFDFBBQJZZMDPJHGCLGMJJPGAEHHBWCQXAXHHHZCHXYPHJAXHLPHJPGPZJQCQZGJJZZUZDMQYYBZZPHYHYBWHAZYJHYKFGDPFQSDLZMLJXKXGALXZDAGLMDGXMWZQYXXDXXPFDMMSSYMPFMDMMKXKSYZYSHDZKXSYSMMZZZMSYDNZZCZXFPLSTMZDNMXCKJMZTYYMZMZZMSXHHDCZJEMXXKLJSTLWLSQLYJZLLZJSSDPPMHNLZJCZYHMXXHGZCJMDHXTKGRMXFWMCGMWKDTKSXQMMMFZZYDKMSCLCMPCGMHSPXQPZDSSLCXKYXTWLWJYAHZJGZQMCSNXYYMMPMLKJXMHLMLQMXCTKZMJQYSZJSYSZHSYJZJCDAJZYBSDQJZGWZQQXFKDMSDJLFWEHKZQKJPEYPZYSZCDWYJFFMZZYLTTDZZEFMZLBNPPLPLPEPSZALLTYLKCKQZKGENQLWAGYXYDPXLHSXQQWQCQXQCLHYXXMLYCCWLYMQYSKGCHLCJNSZKPYZKCQZQLJPDMDZHLASXLBYDWQLWDNBQCRYDDZTJYBKBWSZDXDTNPJDTCTQDFXQQMGNXECLTTBKPWSLCTYQLPWYZZKLPYGZCQQPLLKCCYLPQMZCZQCLJSLQZDJXLDDHPZQDLJJXZQDXYZQKZLJCYQDYJPPYPQYKJYRMPCBYMCXKLLZLLFQPYLLLMBSGLCYSSLRSYSQTMXYXZQZFDZUYSYZTFFMZZSMZQHZSSCCMLYXWTPZGXZJGZGSJSGKDDHTQGGZLLBJDZLCBCHYXYZHZFYWXYZYMSDBZZYJGTSMTFXQYXQSTDGSLNXDLRYZZLRYYLXQHTXSRTZNGZXBNQQZFMYKMZJBZYMKBPNLYZPBLMCNQYZZZSJZHJCTZKHYZZJRDYZHNPXGLFZTLKGJTCTSSYLLGZRZBBQZZKLPKLCZYSSUYXBJFPNJZZXCDWXZYJXZZDJJKGGRSRJKMSMZJLSJYWQSKYHQJSXPJZZZLSNSHRNYPZTWCHKLPSRZLZXYJQXQKYSJYCZTLQZYBBYBWZPQDWWYZCYTJCJXCKCWDKKZXSGKDZXWWYYJQYYTCYTDLLXWKCZKKLCCLZCQQDZLQLCSFQCHQHSFSMQZZLNBJJZBSJHTSZDYSJQJPDLZCDCWJKJZZLPYCGMZWDJJBSJQZSYZYHHXJPBJYDSSXDZNCGLQMBTSFSBPDZDLZNFGFJGFSMPXJQLMBLGQCYYXBQKDJJQYRFKZTJDHCZKLBSDZCFJTPLLJGXHYXZCSSZZXSTJYGKGCKGYOQXJPLZPBPGTGYJZGHZQZZLBJLSQFZGKQQJZGYCZBZQTLDXRJXBSXXPZXHYZYCLWDXJJHXMFDZPFZHQHQMQGKSLYHTYCGFRZGNQXCLPDLBZCSCZQLLJBLHBZCYPZZPPDYMZZSGYHCKCPZJGSLJLNSCDSLDLXBMSTLDDFJMKDJDHZLZXLSZQPQPGJLLYBDSZGQLBZLSLKYYHZTTNTJYQTZZPSZQZTLLJTYYLLQLLQYZQLBDZLSLYYZYMDFSZSNHLXZNCZQZPBWSKRFBSYZMTHBLGJPMCZZLSTLXSHTCSYZLZBLFEQHLXFLCJLYLJQCBZLZJHHSSTBRMHXZHJZCLXFNBGXGTQJCZTMSFZKJMSSNXLJKBHSJXNTNLZDNTLMSJXGZJYJCZXYJYJWRWWQNZTNFJSZPZSHZJFYRDJSFSZJZBJFZQZZHZLXFYSBZQLZSGYFTZDCSZXZJBQMSZKJRHYJZCKMJKHCHGTXKXQGLXPXFXTRTYLXJXHDTSJXHJZJXZWZLCQSBTXWXGXTXXHXFTSDKFJHZYJFJXRZSDLLLTQSQQZQWZXSYQTWGWBZCGZLLYZBCLMQQTZHZXZXLJFRMYZFLXYSQXXJKXRMQDZDMMYYBSQBHGZMWFWXGMXLZPYYTGZYCCDXYZXYWGSYJYZNBHPZJSQSYXSXRTFYZGRHZTXSZZTHCBFCLSYXZLZQMZLMPLMXZJXSFLBYZMYQHXJSXRXSQZZZSSLYFRCZJRCRXHHZXQYDYHXSJJHZCXZBTYNSYSXJBQLPXZQPYMLXZKYXLXCJLCYSXXZZLXDLLLJJYHZXGYJWKJRWYHCPSGNRZLFZWFZZNSXGXFLZSXZZZBFCSYJDBRJKRDHHGXJLJJTGXJXXSTJTJXLYXQFCSGSWMSBCTLQZZWLZZKXJMLTMJYHSDDBXGZHDLBMYJFRZFSGCLYJBPMLYSMSXLSZJQQHJZFXGFQFQBPXZGYYQXGZTCQWYLTLGWSGWHRLFSFGZJMGMGBGTJFSYZZGZYZAFLSSPMLPFLCWBJZCLJJMZLPJJLYMQDMYYYFBGYGYZMLYZDXQYXRQQQHSYYYQXYLJTYXFSFSLLGNQCYHYCWFHCCCFXPYLYPLLZYXXXXXKQHHXSHJZCFZSCZJXCPZWHHHHHAPYLQALPQAFYHXDYLUKMZQGGGDDESRNNZLTZGCHYPPYSQJJHCLLJTOLNJPZLJLHYMHEYDYDSQYCDDHGZUNDZCLZYZLLZNTNYZGSLHSLPJJBDGWXPCDUTJCKLKCLWKLLCASSTKZZDNQNTTLYYZSSYSSZZRYLJQKCQDHHCRXRZYDGRGCWCGZQFFFPPJFZYNAKRGYWYQPQXXFKJTSZZXSWZDDFBBXTBGTZKZNPZZPZXZPJSZBMQHKCYXYLDKLJNYPKYGHGDZJXXEAHPNZKZTZCMXCXMMJXNKSZQNMNLWBWWXJKYHCPSTMCSQTZJYXTPCTPDTNNPGLLLZSJLSPBLPLQHDTNJNLYYRSZFFJFQWDPHZDWMRZCCLODAXNSSNYZRESTYJWJYJDBCFXNMWTTBYLWSTSZGYBLJPXGLBOCLHPCBJLTMXZLJYLZXCLTPNCLCKXTPZJSWCYXSFYSZDKNTLBYJCYJLLSTGQCBXRYZXBXKLYLHZLQZLNZCXWJZLJZJNCJHXMNZZGJZZXTZJXYCYYCXXJYYXJJXSSSJSTSSTTPPGQTCSXWZDCSYFPTFBFHFBBLZJCLZZDBXGCXLQPXKFZFLSYLTUWBMQJHSZBMDDBCYSCCLDXYCDDQLYJJWMQLLCSGLJJSYFPYYCCYLTJANTJJPWYCMMGQYYSXDXQMZHSZXPFTWWZQSWQRFKJLZJQQYFBRXJHHFWJJZYQAZMYFRHCYYBYQWLPEXCCZSTYRLTTDMQLYKMBBGMYYJPRKZNPBSXYXBHYZDJDNGHPMFSGMWFZMFQMMBCMZZCJJLCNUXYQLMLRYGQZCYXZLWJGCJCGGMCJNFYZZJHYCPRRCMTZQZXHFQGTJXCCJEAQCRJYHPLQLSZDJRBCQHQDYRHYLYXJSYMHZYDWLDFRYHBPYDTSSCNWBXGLPZMLZZTQSSCPJMXXYCSJYTYCGHYCJWYRXXLFEMWJNMKLLSWTXHYYYNCMMCWJDQDJZGLLJWJRKHPZGGFLCCSCZMCBLTBHBQJXQDSPDJZZGKGLFQYWBZYZJLTSTDHQHCTCBCHFLQMPWDSHYYTQWCNZZJTLBYMBPDYYYXSQKXWYYFLXXNCWCXYPMAELYKKJMZZZBRXYYQJFLJPFHHHYTZZXSGQQMHSPGDZQWBWPJHZJDYSCQWZKTXXSQLZYYMYSDZGRXCKKUJLWPYSYSCSYZLRMLQSYLJXBCXTLWDQZPCYCYKPPPNSXFYZJJRCEMHSZMSXLXGLRWGCSTLRSXBZGBZGZTCPLUJLSLYLYMTXMTZPALZXPXJTJWTCYYZLBLXBZLQMYLXPGHDSLSSDMXMBDZZSXWHAMLCZCPJMCNHJYSNSYGCHSKQMZZQDLLKABLWJXSFMOCDXJRRLYQZKJMYBYQLYHETFJZFRFKSRYXFJTWDSXXSYSQJYSLYXWJHSNLXYYXHBHAWHHJZXWMYLJCSSLKYDZTXBZSYFDXGXZJKHSXXYBSSXDPYNZWRPTQZCZENYGCXQFJYKJBZMLJCMQQXUOXSLYXXLYLLJDZBTYMHPFSTTQQWLHOKYBLZZALZXQLHZWRRQHLSTMYPYXJJXMQSJFNBXYXYJXXYQYLTHYLQYFMLKLJTMLLHSZWKZHLJMLHLJKLJSTLQXYLMBHHLNLZXQJHXCFXXLHYHJJGBYZZKBXSCQDJQDSUJZYYHZHHMGSXCSYMXFEBCQWWRBPYYJQTYZCYQYQQZYHMWFFHGZFRJFCDPXNTQYZPDYKHJLFRZXPPXZDBBGZQSTLGDGYLCQMLCHHMFYWLZYXKJLYPQHSYWMQQGQZMLZJNSQXJQSYJYCBEHSXFSZPXZWFLLBCYYJDYTDTHWZSFJMQQYJLMQXXLLDTTKHHYBFPWTYYSQQWNQWLGWDEBZWCMYGCULKJXTMXMYJSXHYBRWFYMWFRXYQMXYSZTZZTFYKMLDHQDXWYYNLCRYJBLPSXCXYWLSPRRJWXHQYPHTYDNXHHMMYWYTZCSQMTSSCCDALWZTCPQPYJLLQZYJSWXMZZMMYLMXCLMXCZMXMZSQTZPPQQBLPGXQZHFLJJHYTJSRXWZXSCCDLXTYJDCQJXSLQYCLZXLZZXMXQRJMHRHZJBHMFLJLMLCLQNLDXZLLLPYPSYJYSXCQQDCMQJZZXHNPNXZMEKMXHYKYQLXSXTXJYYHWDCWDZHQYYBGYBCYSCFGPSJNZDYZZJZXRZRQJJYMCANYRJTLDPPYZBSTJKXXZYPFDWFGZZRPYMTNGXZQBYXNBUFNQKRJQZMJEGRZGYCLKXZDSKKNSXKCLJSPJYYZLQQJYBZSSQLLLKJXTBKTYLCCDDBLSPPFYLGYDTZJYQGGKQTTFZXBDKTYYHYBBFYTYYBCLPDYTGDHRYRNJSPTCSNYJQHKLLLZSLYDXXWBCJQSPXBPJZJCJDZFFXXBRMLAZHCSNDLBJDSZBLPRZTSWSBXBCLLXXLZDJZSJPYLYXXYFTFFFBHJJXGBYXJPMMMPSSJZJMTLYZJXSWXTYLEDQPJMYGQZJGDJLQJWJQLLSJGJGYGMSCLJJXDTYGJQJQJCJZCJGDZZSXQGSJGGCXHQXSNQLZZBXHSGZXCXYLJXYXYYDFQQJHJFXDHCTXJYRXYSQTJXYEFYYSSYYJXNCYZXFXMSYSZXYYSCHSHXZZZGZZZGFJDLTYLNPZGYJYZYYQZPBXQBDZTZCZYXXYHHSQXSHDHGQHJHGYWSZTMZMLHYXGEBTYLZKQWYTJZRCLEKYSTDBCYKQQSAYXCJXWWGSBHJYZYDHCSJKQCXSWXFLTYNYZPZCCZJQTZWJQDZZZQZLJJXLSBHPYXXPSXSHHEZTXFPTLQYZZXHYTXNCFZYYHXGNXMYWXTZSJPTHHGYMXMXQZXTSBCZYJYXXTYYZYPCQLMMSZMJZZLLZXGXZAAJZYXJMZXWDXZSXZDZXLEYJJZQBHZWZZZQTZPSXZTDSXJJJZNYAZPHXYYSRNQDTHZHYYKYJHDZXZLSWCLYBZYECWCYCRYLCXNHZYDZYDYJDFRJJHTRSQTXYXJRJHOJYNXELXSFSFJZGHPZSXZSZDZCQZBYYKLSGSJHCZSHDGQGXYZGXCHXZJWYQWGYHKSSEQZZNDZFKWYSSTCLZSTSYMCDHJXXYWEYXCZAYDMPXMDSXYBSQMJMZJMTZQLPJYQZCGQHXJHHLXXHLHDLDJQCLDWBSXFZZYYSCHTYTYYBHECXHYKGJPXHHYZJFXHWHBDZFYZBCAPNPGNYDMSXHMMMMAMYNBYJTMPXYYMCTHJBZYFCGTYHWPHFTWZZEZSBZEGPFMTSKFTYCMHFLLHGPZJXZJGZJYXZSBBQSCZZLZCCSTPGXMJSFTCCZJZDJXCYBZLFCJSYZFGSZLYBCWZZBYZDZYPSWYJZXZBDSYUXLZZBZFYGCZXBZHZFTPBGZGEJBSTGKDMFHYZZJHZLLZZGJQZLSFDJSSCBZGPDLFZFZSZYZYZSYGCXSNXXCHCZXTZZLJFZGQSQYXZJQDCCZTQCDXZJYQJQCHXZTDLGSCXZSYQJQTZWLQDQZTQCHQQJZYEZZZPBWKDJFCJPZTYPQYQTTYNLMBDKTJZPQZQZZFPZSBNJLGYJDXJDZZKZGQKXDLPZJTCJDQBXDJQJSTCKNXBXZMSLYJCQMTJQWWCJQNJNLLLHJCWQTBZQYDZCZPZZDZYDDCYZZZCCJTTJFZDPRRTZTJDCQTQZDTJNPLZBCLLCTZSXKJZQZPZLBZRBTJDCXFCZDBCCJJLTQQPLDCGZDBBZJCQDCJWYNLLZYZCCDWLLXWZLXRXNTQQCZXKQLSGDFQTDDGLRLAJJTKUYMKQLLTZYTDYYCZGJWYXDXFRSKSTQTENQMRKQZHHQKDLDAZFKYPBGGPZREBZZYKZZSPEGJXGYKQZZZSLYSYYYZWFQZYLZZLZHWCHKYPQGNPGBLPLRRJYXCCSYYHSFZFYBZYYTGZXYLXCZWXXZJZBLFFLGSKHYJZEYJHLPLLLLCZGXDRZELRHGKLZZYHZLYQSZZJZQLJZFLNBHGWLCZCFJYSPYXZLZLXGCCPZBLLCYBBBBUBBCBPCRNNZCZYRBFSRLDCGQYYQXYGMQZWTZYTYJXYFWTEHZZJYWLCCNTZYJJZDEDPZDZTSYQJHDYMBJNYJZLXTSSTPHNDJXXBYXQTZQDDTJTDYYTGWSCSZQFLSHLGLBCZPHDLYZJYCKWTYTYLBNYTSDSYCCTYSZYYEBHEXHQDTWNYGYCLXTSZYSTQMYGZAZCCSZZDSLZCLZRQXYYELJSBYMXSXZTEMBBLLYYLLYTDQYSHYMRQWKFKBFXNXSBYCHXBWJYHTQBPBSBWDZYLKGZSKYHXQZJXHXJXGNLJKZLYYCDXLFYFGHLJGJYBXQLYBXQPQGZTZPLNCYPXDJYQYDYMRBESJYYHKXXSTMXRCZZYWXYQYBMCLLYZHQYZWQXDBXBZWZMSLPDMYSKFMZKLZCYQYCZLQXFZZYDQZPZYGYJYZMZXDZFYFYTTQTZHGSPCZMLCCYTZXJCYTJMKSLPZHYSNZLLYTPZCTZZCKTXDHXXTQCYFKSMQCCYYAZHTJPCYLZLYJBJXTPNYLJYYNRXSYLMMNXJSMYBCSYSYLZYLXJJQYLDZLPQBFZZBLFNDXQKCZFYWHGQMRDSXYCYTXNQQJZYYPFZXDYZFPRXEJDGYQBXRCNFYYQPGHYJDYZXGRHTKYLNWDZNTSMPKLBTHBPYSZBZTJZSZZJTYYXZPHSSZZBZCZPTQFZMYFLYPYBBJQXZMXXDJMTSYSKKBJZXHJCKLPSMKYJZCXTMLJYXRZZQSLXXQPYZXMKYXXXJCLJPRMYYGADYSKQLSNDHYZKQXZYZTCGHZTLMLWZYBWSYCTBHJHJFCWZTXWYTKZLXQSHLYJZJXTMPLPYCGLTBZZTLZJCYJGDTCLKLPLLQPJMZPAPXYZLKKTKDZCZZBNZDYDYQZJYJGMCTXLTGXSZLMLHBGLKFWNWZHDXUHLFMKYSLGXDTWWFRJEJZTZHYDXYKSHWFZCQSHKTMQQHTZHYMJDJSKHXZJZBZZXYMPAGQMSTPXLSKLZYNWRTSQLSZBPSPSGZWYHTLKSSSWHZZLYYTNXJGMJSZSUFWNLSOZTXGXLSAMMLBWLDSZYLAKQCQCTMYCFJBSLXCLZZCLXXKSBZQCLHJPSQPLSXXCKSLNHPSFQQYTXYJZLQLDXZQJZDYYDJNZPTUZDSKJFSLJHYLZSQZLBTXYDGTQFDBYAZXDZHZJNHHQBYKNXJJQCZMLLJZKSPLDYCLBBLXKLELXJLBQYCXJXGCNLCQPLZLZYJTZLJGYZDZPLTQCSXFDMNYCXGBTJDCZNBGBQYQJWGKFHTNPYQZQGBKPBBYZMTJDYTBLSQMPSXTBNPDXKLEMYYCJYNZCTLDYKZZXDDXHQSHDGMZSJYCCTAYRZLPYLTLKXSLZCGGEXCLFXLKJRTLQJAQZNCMBYDKKCXGLCZJZXJHPTDJJMZQYKQSECQZDSHHADMLZFMMZBGNTJNNLGBYJBRBTMLBYJDZXLCJLPLDLPCQDHLXZLYCBLCXZZJADJLNZMMSSSMYBHBSQKBHRSXXJMXSDZNZPXLGBRHWGGFCXGMSKLLTSJYYCQLTSKYWYYHYWXBXQYWPYWYKQLSQPTNTKHQCWDQKTWPXXHCPTHTWUMSSYHBWCRWXHJMKMZNGWTMLKFGHKJYLSYYCXWHYECLQHKQHTTQKHFZLDXQWYZYYDESBPKYRZPJFYYZJCEQDZZDLATZBBFJLLCXDLMJSSXEGYGSJQXCWBXSSZPDYZCXDNYXPPZYDLYJCZPLTXLSXYZYRXCYYYDYLWWNZSAHJSYQYHGYWWAXTJZDAXYSRLTDPSSYYFNEJDXYZHLXLLLZQZSJNYQYQQXYJGHZGZCYJCHZLYCDSHWSHJZYJXCLLNXZJJYYXNFXMWFPYLCYLLABWDDHWDXJMCXZTZPMLQZHSFHZYNZTLLDYWLSLXHYMMYLMBWWKYXYADTXYLLDJPYBPWUXJMWMLLSAFDLLYFLBHHHBQQLTZJCQJLDJTFFKMMMBYTHYGDCQRDDWRQJXNBYSNWZDBYYTBJHPYBYTTJXAAHGQDQTMYSTQXKBTZPKJLZRBEQQSSMJJBDJOTGTBXPGBKTLHQXJJJCTHXQDWJLWRFWQGWSHCKRYSWGFTGYGBXSDWDWRFHWYTJJXXXJYZYSLPYYYPAYXHYDQKXSHXYXGSKQHYWFDDDPPLCJLQQEEWXKSYYKDYPLTJTHKJLTCYYHHJTTPLTZZCDLTHQKZXQYSTEEYWYYZYXXYYSTTJKLLPZMCYHQGXYHSRMBXPLLNQYDQHXSXXWGDQBSHYLLPJJJTHYJKYPPTHYYKTYEZYENMDSHLCRPQFDGFXZPSFTLJXXJBSWYYSKSFLXLPPLBBBLBSFXFYZBSJSSYLPBBFFFFSSCJDSTZSXZRYYSYFFSYZYZBJTBCTSBSDHRTJJBYTCXYJEYLXCBNEBJDSYXYKGSJZBXBYTFZWGENYHHTHZHHXFWGCSTBGXKLSXYWMTMBYXJSTZSCDYQRCYTWXZFHMYMCXLZNSDJTTTXRYCFYJSBSDYERXJLJXBBDEYNJGHXGCKGSCYMBLXJMSZNSKGXFBNBPTHFJAAFXYXFPXMYPQDTZCXZZPXRSYWZDLYBBKTYQPQJPZYPZJZNJPZJLZZFYSBTTSLMPTZRTDXQSJEHBZYLZDHLJSQMLHTXTJECXSLZZSPKTLZKQQYFSYGYWPCPQFHQHYTQXZKRSGTTSQCZLPTXCDYYZXSQZSLXLZMYCPCQBZYXHBSXLZDLTCDXTYLZJYYZPZYZLTXJSJXHLPMYTXCQRBLZSSFJZZTNJYTXMYJHLHPPLCYXQJQQKZZSCPZKSWALQSBLCCZJSXGWWWYGYKTJBBZTDKHXHKGTGPBKQYSLPXPJCKBMLLXDZSTBKLGGQKQLSBKKTFXRMDKBFTPZFRTBBRFERQGXYJPZSSTLBZTPSZQZSJDHLJQLZBPMSMMSXLQQNHKNBLRDDNXXDHDDJCYYGYLXGZLXSYGMQQGKHBPMXYXLYTQWLWGCPBMQXCYZYDRJBHTDJYHQSHTMJSBYPLWHLZFFNYPMHXXHPLTBQPFBJWQDBYGPNZTPFZJGSDDTQSHZEAWZZYLLTYYBWJKXXGHLFKXDJTMSZSQYNZGGSWQSPHTLSSKMCLZXYSZQZXNCJDQGZDLFNYKLJCJLLZLMZZNHYDSSHTHZZLZZBBHQZWWYCRZHLYQQJBEYFXXXWHSRXWQHWPSLMSSKZTTYGYQQWRSLALHMJTQJSMXQBJJZJXZYZKXBYQXBJXSHZTSFJLXMXZXFGHKZSZGGYLCLSARJYHSLLLMZXELGLXYDJYTLFBHBPNLYZFBBHPTGJKWETZHKJJXZXXGLLJLSTGSHJJYQLQZFKCGNNDJSSZFDBCTWWSEQFHQJBSAQTGYPQLBXBMMYWXGSLZHGLZGQYFLZBYFZJFRYSFMBYZHQGFWZSYFYJJPHZBYYZFFWODGRLMFTWLBZGYCQXCDJYGZYYYYTYTYDWEGAZYHXJLZYYHLRMGRXXZCLHNELJJTJTPWJYBJJBXJJTJTEEKHWSLJPLPSFYZPQQBDLQJJTYYQLYZKDKSQJYYQZLDQTGJQYZJSUCMRYQTHTEJMFCTYHYPKMHYZWJDQFHYYXWSHCTXRLJHQXHCCYYYJLTKTTYTMXGTCJTZAYYOCZLYLBSZYWJYTSJYHBYSHFJLYGJXXTMZYYLTXXYPZLXYJZYZYYPNHMYMDYYLBLHLSYYQQLLNJJYMSOYQBZGDLYXYLCQYXTSZEGXHZGLHWBLJHEYXTWQMAKBPQCGYSHHEGQCMWYYWLJYJHYYZLLJJYLHZYHMGSLJLJXCJJYCLYCJPCPZJZJMMYLCQLNQLJQJSXYJMLSZLJQLYCMMHCFMMFPQQMFYLQMCFFQMMMMHMZNFHHJGTTHHKHSLNCHHYQDXTMMQDCYZYXYQMYQYLTDCYYYZAZZCYMZYDLZFFFMMYCQZWZZMABTBYZTDMNZZGGDFTYPCGQYTTSSFFWFDTZQSSYSTWXJHXYTSXXYLBYQHWWKXHZXWZNNZZJZJJQJCCCHYYXBZXZCYZTLLCQXYNJYCYYCYNZZQYYYEWYCZDCJYCCHYJLBTZYYCQWMPWPYMLGKDLDLGKQQBGYCHJXY`; 19 | //此处收录了375个多音字,数据来自于http://www.51window.net/page/pinyin 20 | var oMultiDiff = { "19969": "DZ", "19975": "WM", "19988": "QJ", "20048": "YL", "20056": "SC", "20060": "NM", "20094": "QG", "20127": "QJ", "20167": "QC", "20193": "YG", "20250": "KH", "20256": "ZC", "20282": "SC", "20285": "QJG", "20291": "TD", "20314": "YD", "20340": "NE", "20375": "TD", "20389": "YJ", "20391": "CZ", "20415": "PB", "20446": "YS", "20447": "SQ", "20504": "TC", "20608": "KG", "20854": "QJ", "20857": "ZC", "20911": "PF", "20504": "TC", "20608": "KG", "20854": "QJ", "20857": "ZC", "20911": "PF", "20985": "AW", "21032": "PB", "21048": "XQ", "21049": "SC", "21089": "YS", "21119": "JC", "21242": "SB", "21273": "SC", "21305": "YP", "21306": "QO", "21330": "ZC", "21333": "SDC", "21345": "QK", "21378": "CA", "21397": "SC", "21414": "XS", "21442": "SC", "21477": "JG", "21480": "TD", "21484": "ZS", "21494": "YX", "21505": "YX", "21512": "HG", "21523": "XH", "21537": "PB", "21542": "PF", "21549": "KH", "21571": "E", "21574": "DA", "21588": "TD", "21589": "O", "21618": "ZC", "21621": "KHA", "21632": "ZJ", "21654": "KG", "21679": "LKG", "21683": "KH", "21710": "A", "21719": "YH", "21734": "WOE", "21769": "A", "21780": "WN", "21804": "XH", "21834": "A", "21899": "ZD", "21903": "RN", "21908": "WO", "21939": "ZC", "21956": "SA", "21964": "YA", "21970": "TD", "22003": "A", "22031": "JG", "22040": "XS", "22060": "ZC", "22066": "ZC", "22079": "MH", "22129": "XJ", "22179": "XA", "22237": "NJ", "22244": "TD", "22280": "JQ", "22300": "YH", "22313": "XW", "22331": "YQ", "22343": "YJ", "22351": "PH", "22395": "DC", "22412": "TD", "22484": "PB", "22500": "PB", "22534": "ZD", "22549": "DH", "22561": "PB", "22612": "TD", "22771": "KQ", "22831": "HB", "22841": "JG", "22855": "QJ", "22865": "XQ", "23013": "ML", "23081": "WM", "23487": "SX", "23558": "QJ", "23561": "YW", "23586": "YW", "23614": "YW", "23615": "SN", "23631": "PB", "23646": "ZS", "23663": "ZT", "23673": "YG", "23762": "TD", "23769": "ZS", "23780": "QJ", "23884": "QK", "24055": "XH", "24113": "DC", "24162": "ZC", "24191": "GA", "24273": "QJ", "24324": "NL", "24377": "TD", "24378": "QJ", "24439": "PF", "24554": "ZS", "24683": "TD", "24694": "WE", "24733": "LK", "24925": "TN", "25094": "ZG", "25100": "XQ", "25103": "XH", "25153": "PB", "25170": "PB", "25179": "KG", "25203": "PB", "25240": "ZS", "25282": "FB", "25303": "NA", "25324": "KG", "25341": "ZY", "25373": "WZ", "25375": "XJ", "25384": "A", "25457": "A", "25528": "SD", "25530": "SC", "25552": "TD", "25774": "ZC", "25874": "ZC", "26044": "YW", "26080": "WM", "26292": "PB", "26333": "PB", "26355": "ZY", "26366": "CZ", "26397": "ZC", "26399": "QJ", "26415": "ZS", "26451": "SB", "26526": "ZC", "26552": "JG", "26561": "TD", "26588": "JG", "26597": "CZ", "26629": "ZS", "26638": "YL", "26646": "XQ", "26653": "KG", "26657": "XJ", "26727": "HG", "26894": "ZC", "26937": "ZS", "26946": "ZC", "26999": "KJ", "27099": "KJ", "27449": "YQ", "27481": "XS", "27542": "ZS", "27663": "ZS", "27748": "TS", "27784": "SC", "27788": "ZD", "27795": "TD", "27812": "O", "27850": "PB", "27852": "MB", "27895": "SL", "27898": "PL", "27973": "QJ", "27981": "KH", "27986": "HX", "27994": "XJ", "28044": "YC", "28065": "WG", "28177": "SM", "28267": "QJ", "28291": "KH", "28337": "ZQ", "28463": "TL", "28548": "DC", "28601": "TD", "28689": "PB", "28805": "JG", "28820": "QG", "28846": "PB", "28952": "TD", "28975": "ZC", "29100": "A", "29325": "QJ", "29575": "SL", "29602": "FB", "30010": "TD", "30044": "CX", "30058": "PF", "30091": "YSP", "30111": "YN", "30229": "XJ", "30427": "SC", "30465": "SX", "30631": "YQ", "30655": "QJ", "30684": "QJG", "30707": "SD", "30729": "XH", "30796": "LG", "30917": "PB", "31074": "NM", "31085": "JZ", "31109": "SC", "31181": "ZC", "31192": "MLB", "31293": "JQ", "31400": "YX", "31584": "YJ", "31896": "ZN", "31909": "ZY", "31995": "XJ", "32321": "PF", "32327": "ZY", "32418": "HG", "32420": "XQ", "32421": "HG", "32438": "LG", "32473": "GJ", "32488": "TD", "32521": "QJ", "32527": "PB", "32562": "ZSQ", "32564": "JZ", "32735": "ZD", "32793": "PB", "33071": "PF", "33098": "XL", "33100": "YA", "33152": "PB", "33261": "CX", "33324": "BP", "33333": "TD", "33406": "YA", "33426": "WM", "33432": "PB", "33445": "JG", "33486": "ZN", "33493": "TS", "33507": "QJ", "33540": "QJ", "33544": "ZC", "33564": "XQ", "33617": "YT", "33632": "QJ", "33636": "XH", "33637": "YX", "33694": "WG", "33705": "PF", "33728": "YW", "33882": "SR", "34067": "WM", "34074": "YW", "34121": "QJ", "34255": "ZC", "34259": "XL", "34425": "JH", "34430": "XH", "34485": "KH", "34503": "YS", "34532": "HG", "34552": "XS", "34558": "YE", "34593": "ZL", "34660": "YQ", "34892": "XH", "34928": "SC", "34999": "QJ", "35048": "PB", "35059": "SC", "35098": "ZC", "35203": "TQ", "35265": "JX", "35299": "JX", "35782": "SZ", "35828": "YS", "35830": "E", "35843": "TD", "35895": "YG", "35977": "MH", "36158": "JG", "36228": "QJ", "36426": "XQ", "36466": "DC", "36710": "JC", "36711": "ZYG", "36767": "PB", "36866": "SK", "36951": "YW", "37034": "YX", "37063": "XH", "37218": "ZC", "37325": "ZC", "38063": "PB", "38079": "TD", "38085": "QY", "38107": "DC", "38116": "TD", "38123": "YD", "38224": "HG", "38241": "XTC", "38271": "ZC", "38415": "YE", "38426": "KH", "38461": "YD", "38463": "AE", "38466": "PB", "38477": "XJ", "38518": "YT", "38551": "WK", "38585": "ZC", "38704": "XS", "38739": "LJ", "38761": "GJ", "38808": "SQ", "39048": "JG", "39049": "XJ", "39052": "HG", "39076": "CZ", "39271": "XT", "39534": "TD", "39552": "TD", "39584": "PB", "39647": "SB", "39730": "LG", "39748": "TPB", "40109": "ZQ", "40479": "ND", "40516": "HG", "40536": "HG", "40583": "QJ", "40765": "YQ", "40784": "QJ", "40840": "YK", "40863": "QJG" }; 21 | //参数,中文字符串 22 | //返回值:拼音首字母串数组 23 | function makePy(str) { 24 | if (typeof (str) != "string") 25 | throw new Error(-1, "函数makePy需要字符串类型参数!"); 26 | var arrResult = new Array(); //保存中间结果的数组 27 | for (var i = 0, len = str.length; i < len; i++) { 28 | //获得unicode码 29 | var ch = str.charAt(i); 30 | //检查该unicode码是否在处理范围之内,在则返回该码对映汉字的拼音首字母,不在则调用其它函数处理 31 | arrResult.push(checkCh(ch)); 32 | } 33 | //处理arrResult,返回所有可能的拼音首字母串数组 34 | return mkRslt(arrResult); 35 | } 36 | function checkCh(ch) { 37 | var uni = ch.charCodeAt(0); 38 | //如果不在汉字处理范围之内,返回原字符,也可以调用自己的处理函数 39 | if (uni > 40869 || uni < 19968) 40 | return ch; //dealWithOthers(ch); 41 | //检查是否是多音字,是按多音字处理,不是就直接在strChineseFirstPY字符串中找对应的首字母 42 | return (oMultiDiff[uni] ? oMultiDiff[uni] : (strChineseFirstPY.charAt(uni - 19968))); 43 | } 44 | function mkRslt(arr) { 45 | var arrRslt = [""]; 46 | for (var i = 0, len = arr.length; i < len; i++) { 47 | var str = arr[i]; 48 | var strlen = str.length; 49 | if (strlen == 1) { 50 | for (var k = 0; k < arrRslt.length; k++) { 51 | arrRslt[k] += str; 52 | } 53 | } else { 54 | var tmpArr = arrRslt.slice(0); 55 | arrRslt = []; 56 | for (k = 0; k < strlen; k++) { 57 | //复制一个相同的arrRslt 58 | var tmp = tmpArr.slice(0); 59 | //把当前字符str[k]添加到每个元素末尾 60 | for (var j = 0; j < tmp.length; j++) { 61 | tmp[j] += str.charAt(k); 62 | } 63 | //把复制并修改后的数组连接到arrRslt上 64 | arrRslt = arrRslt.concat(tmp); 65 | } 66 | } 67 | } 68 | return arrRslt; 69 | } 70 | //两端去空格函数 71 | String.prototype.trim = function () { return this.replace(/(^\s*)|(\s*$)/g, ""); } 72 | function query(text) { 73 | var str = text.trim(); 74 | if (str == "") return; 75 | var arrRslt = makePy(str); 76 | return arrRslt; 77 | } 78 | 79 | export function init(array,key,callback) { 80 | return buildTextData(array,key); 81 | } 82 | 83 | function buildTextData(arr,key){ 84 | var textData = [{ tag: "A", textArray: [] }, 85 | { tag: "B", textArray: [] }, 86 | { tag: "C", textArray: [] }, 87 | { tag: "D", textArray: [] }, 88 | { tag: "E", textArray: [] }, 89 | { tag: "F", textArray: [] }, 90 | { tag: "G", textArray: [] }, 91 | { tag: "H", textArray: [] }, 92 | { tag: "I", textArray: [] }, 93 | { tag: "J", textArray: [] }, 94 | { tag: "K", textArray: [] }, 95 | { tag: "L", textArray: [] }, 96 | { tag: "M", textArray: [] }, 97 | { tag: "N", textArray: [] }, 98 | { tag: "O", textArray: [] }, 99 | { tag: "P", textArray: [] }, 100 | { tag: "Q", textArray: [] }, 101 | { tag: "R", textArray: [] }, 102 | { tag: "S", textArray: [] }, 103 | { tag: "T", textArray: [] }, 104 | { tag: "U", textArray: [] }, 105 | { tag: "V", textArray: [] }, 106 | { tag: "W", textArray: [] }, 107 | { tag: "X", textArray: [] }, 108 | { tag: "Y", textArray: [] }, 109 | { tag: "Z", textArray: [] }, 110 | { tag: "#", textArray: [] }]; 111 | 112 | var temABC = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '#']; 113 | 114 | for (var i = 0; i < arr.length; i++ ){ 115 | var text = arr[i][key]; 116 | var firstChar = text.substr(0, 1); 117 | var reg = query(firstChar)[0].toUpperCase(); 118 | var temIndex = temABC.indexOf(reg); 119 | if(temIndex == -1 ){ 120 | temIndex = textData.length-1; 121 | } 122 | textData[temIndex].textArray.push(arr[i]); 123 | } 124 | let temData = {}; 125 | temData = textData; 126 | return temData; 127 | } 128 | 129 | // module.exports = { 130 | // init: init, 131 | // query: query 132 | // } 133 | --------------------------------------------------------------------------------