├── static
└── .gitkeep
├── config
├── prod.env.js
├── test.env.js
├── dev.env.js
└── index.js
├── notebook.txt
├── src
├── assets
│ ├── images
│ │ ├── logo.png
│ │ ├── common
│ │ │ ├── bg.png
│ │ │ ├── logo.png
│ │ │ ├── avatar
│ │ │ │ ├── 1.jpg
│ │ │ │ ├── 2.jpg
│ │ │ │ ├── 3.jpg
│ │ │ │ ├── 4.jpg
│ │ │ │ ├── 5.jpg
│ │ │ │ ├── 6.jpg
│ │ │ │ ├── 7.jpg
│ │ │ │ ├── 8.jpg
│ │ │ │ └── 9.jpg
│ │ │ ├── loading.png
│ │ │ └── web_wechat_login_bg.jpg
│ │ ├── panel
│ │ │ └── head
│ │ │ │ └── sprite.png
│ │ └── shuo
│ │ │ ├── spinner-rosetta-gray-14x14.gif
│ │ │ └── spinner-rosetta-gray-32x32.gif
│ ├── font
│ │ ├── materialize.woff2
│ │ ├── rosetta-icons-Regular.eot
│ │ ├── rosetta-icons-Regular.ttf
│ │ └── rosetta-icons-Regular.woff
│ └── styles
│ │ ├── chat.css
│ │ ├── login.css
│ │ ├── base.css
│ │ └── shuo.css
├── components
│ ├── overlay.vue
│ ├── index.js
│ ├── footer.vue
│ ├── progress.vue
│ ├── confirm.vue
│ ├── toast.vue
│ ├── cover.vue
│ ├── loading.vue
│ └── navbar.vue
├── views
│ ├── chat
│ │ ├── message.vue
│ │ ├── thread.vue
│ │ ├── index.vue
│ │ ├── threadlist.vue
│ │ └── messagelist.vue
│ ├── shuo
│ │ ├── main.vue
│ │ ├── left.vue
│ │ ├── stream.vue
│ │ ├── index.vue
│ │ └── timeline.vue
│ ├── login.vue
│ └── loginx.vue
├── vuex
│ ├── getters.js
│ ├── modules
│ │ ├── shuos.js
│ │ └── users.js
│ ├── actions.js
│ └── store.js
├── api
│ ├── classObject.js
│ ├── authApi.js
│ ├── index.js
│ ├── shuoApi.js
│ ├── index1.js
│ ├── mock-data.js
│ └── usersApi.js
├── router
│ └── index.js
├── main.js
└── app.vue
├── .babelrc
├── .editorconfig
├── .gitignore
├── README.md
├── index.html
└── package.json
/static/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"'
3 | }
4 |
--------------------------------------------------------------------------------
/notebook.txt:
--------------------------------------------------------------------------------
1 | --------需要解决的问题
2 | 1.登陆权限的判断问题
3 | 2.登陆查询的优化
4 | 3.聊天UI界面构建
5 | 4.注册,登陆流程的优化
6 |
--------------------------------------------------------------------------------
/src/assets/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/logo.png
--------------------------------------------------------------------------------
/src/assets/images/common/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/bg.png
--------------------------------------------------------------------------------
/src/assets/font/materialize.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/font/materialize.woff2
--------------------------------------------------------------------------------
/src/assets/images/common/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/logo.png
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-2"],
3 | "plugins": ["transform-runtime"],
4 | "comments": false
5 | }
6 |
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/1.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/2.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/3.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/4.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/5.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/6.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/7.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/8.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/avatar/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/avatar/9.jpg
--------------------------------------------------------------------------------
/src/assets/images/common/loading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/loading.png
--------------------------------------------------------------------------------
/src/assets/images/panel/head/sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/panel/head/sprite.png
--------------------------------------------------------------------------------
/src/assets/font/rosetta-icons-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/font/rosetta-icons-Regular.eot
--------------------------------------------------------------------------------
/src/assets/font/rosetta-icons-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/font/rosetta-icons-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/font/rosetta-icons-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/font/rosetta-icons-Regular.woff
--------------------------------------------------------------------------------
/src/assets/images/common/web_wechat_login_bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/common/web_wechat_login_bg.jpg
--------------------------------------------------------------------------------
/src/assets/images/shuo/spinner-rosetta-gray-14x14.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/shuo/spinner-rosetta-gray-14x14.gif
--------------------------------------------------------------------------------
/src/assets/images/shuo/spinner-rosetta-gray-32x32.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/andylei18/vue-chat/HEAD/src/assets/images/shuo/spinner-rosetta-gray-32x32.gif
--------------------------------------------------------------------------------
/config/test.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var devEnv = require('./dev.env')
3 |
4 | module.exports = merge(devEnv, {
5 | NODE_ENV: '"testing"'
6 | })
7 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | var merge = require('webpack-merge')
2 | var prodEnv = require('./prod.env')
3 |
4 | module.exports = merge(prodEnv, {
5 | NODE_ENV: '"development"'
6 | })
7 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | npm-debug.log
5 | selenium-debug.log
6 | test/unit/coverage
7 | test/e2e/reports
8 | <<<<<<< Updated upstream
9 | .idea
10 | =======
11 | >>>>>>> Stashed changes
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-chat
2 |
3 | ## Build Setup
4 |
5 | ``` bash
6 | # install dependencies
7 | npm install
8 |
9 | # serve with hot reload at localhost:9090
10 | npm run dev
11 |
12 | # build for production with minification
13 | npm run build
14 |
--------------------------------------------------------------------------------
/src/components/overlay.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | vue-chat
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/views/chat/message.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{ message.authorName }}
4 |
5 | {{ message.timestamp | time }}
6 |
7 | {{ message.text }}
8 |
9 |
10 |
11 |
16 |
--------------------------------------------------------------------------------
/src/components/index.js:
--------------------------------------------------------------------------------
1 | import proGress from './progress.vue'
2 | import comToast from './toast.vue'
3 | import comCover from './cover.vue'
4 | import conFirm from './confirm.vue'
5 | import overLay from './overlay.vue'
6 | import navBar from './navbar.vue'
7 | import fooTer from './footer.vue'
8 |
9 | export {
10 | proGress,
11 | comToast,
12 | comCover,
13 | conFirm,
14 | overLay,
15 | navBar,
16 | fooTer
17 | }
18 |
--------------------------------------------------------------------------------
/src/vuex/getters.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | export function currentThread (state) {
3 | return state.currentThreadID
4 | ? state.threads[state.currentThreadID]
5 | : {}
6 | }
7 |
8 | export function currentMessages (state) {
9 | const thread = currentThread(state)
10 | return thread.messages
11 | ? thread.messages.map(id => state.messages[id])
12 | : []
13 | }
14 |
15 | export const isLoginOnline = ({isLoginOnline}) => isLoginOnline
16 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | env: require('./prod.env'),
7 | index: path.resolve(__dirname, '../dist/index.html'),
8 | assetsRoot: path.resolve(__dirname, '../dist'),
9 | assetsSubDirectory: 'static',
10 | assetsPublicPath: '/',
11 | productionSourceMap: true
12 | },
13 | dev: {
14 | env: require('./dev.env'),
15 | port: 9090,
16 | proxyTable: {}
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/api/classObject.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const timestamp = Date.now()
3 |
4 | export class User extends Object {
5 | constructor() {
6 | super({
7 | nickname: "新用户",
8 | faceimg: "http://o7kxl993s.bkt.clouddn.com/chatAvatar1.jpg",
9 | creat:timestamp
10 | })
11 | }
12 | }
13 |
14 | export class Shuo extends Object {
15 | constructor() {
16 | super({
17 | nickname: "新用户",
18 | faceimg: "http://o7kxl993s.bkt.clouddn.com/chatAvatar1.jpg",
19 | creat:timestamp
20 | })
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import VueRouter from 'vue-router'
3 | import Vue from 'vue'
4 |
5 | Vue.use(VueRouter)
6 |
7 | var router = new VueRouter()
8 |
9 | router.map({
10 | //入口模块
11 | '/': {
12 | name:'index',
13 | component: (resolve) => {
14 | require(['../app.vue'], resolve)
15 | }
16 | },
17 |
18 | //核心模块
19 | '/shuo': {
20 | name:'shuo',
21 | component: (resolve) => {
22 | require(['../views/shuo/index.vue'], resolve)
23 | }
24 | },
25 |
26 | })
27 |
28 | export default router
29 |
--------------------------------------------------------------------------------
/src/vuex/modules/shuos.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import Vue from 'vue'
3 |
4 | const state = {
5 | shuolist: {
6 |
7 |
8 | }
9 | }
10 |
11 | const mutations = {
12 | ['USER_INIT'] (state, datasnapshot) {
13 | state.info = datasnapshot.val() || {}
14 | },
15 | ['RECEIVE_SHUO'] (state , datasnapshot) {
16 | let key = datasnapshot.key()
17 | let p = datasnapshot.val()
18 | state.shuolist.hasOwnProperty(key) || Vue.set(state.shuolist, key, p)
19 | },
20 | ['ERROR_SHUO'] (state) {
21 |
22 | }
23 |
24 | }
25 |
26 | export default {
27 | state,
28 | mutations
29 | }
30 |
--------------------------------------------------------------------------------
/src/views/shuo/main.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
34 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import Vue from 'vue'
3 | import Vuex from 'vuex'
4 | import VueRouter from 'vue-router'
5 | import { sync } from 'vuex-router-sync'
6 |
7 | import App from './app'
8 | import store from './vuex/store'
9 | import router from './router/index'
10 | import WildVue from 'wildvue'
11 | //import Wilddog from 'wilddog'
12 |
13 | //materialize作为公用css
14 | import "materialize-css/dist/css/materialize.min.css"
15 | import "assets/styles/base.css"
16 |
17 | Vue.use(VueRouter)
18 | Vue.use(Vuex)
19 | Vue.use(WildVue)
20 |
21 | Vue.config.debug = true
22 |
23 | Vue.filter('time', timestamp => {
24 | return new Date(timestamp).toLocaleTimeString()
25 | })
26 |
27 | sync(store, router)
28 |
29 | router.start(App, 'app')
30 |
--------------------------------------------------------------------------------
/src/views/chat/thread.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 | {{ thread.name }}
8 |
9 | {{ thread.lastMessage.timestamp | time }}
10 |
11 |
12 | {{ thread.lastMessage.text }}
13 |
14 |
15 |
16 |
17 |
18 |
35 |
--------------------------------------------------------------------------------
/src/api/authApi.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | export default function (ref) {
4 | // check if user has logged in to wilddog.
5 | const checkAuth = () => new Promise((resolve, reject) => ref.getAuth() ? resolve() : reject())
6 |
7 | // 登录野狗服务器
8 | const signIn = (email,password,faceid,faceurl) => {
9 | return new Promise((resolve, reject) => {
10 | ref.authWithPassword({
11 | email,
12 | password
13 | }, (error, data) => {
14 | if (error) {
15 | reject('登录失败!')
16 | } else {
17 | resolve(data)
18 | }
19 | })
20 | })
21 | }
22 |
23 | //退出野狗登录
24 | const signOut = () => {
25 | return new Promise(function (resolve, reject) {
26 | ref.unauth()
27 | resolve()
28 | })
29 | }
30 |
31 | return {
32 | checkAuth,
33 | signIn,
34 | signOut,
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/api/index.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import Wilddog from "wilddog"
3 | import authApi from "./authApi"
4 | import usersApi from "./usersApi"
5 | import shuoApi from "./shuoApi"
6 | const LATENCY = 16
7 |
8 | const USERDB = new Wilddog('https://userlist.wilddogio.com/')//用户表
9 | const MSGDB= new Wilddog("https://vuechat118.wilddogio.com/")//聊天表
10 |
11 | window.USERDB = USERDB
12 |
13 | //调用野狗
14 | const auth = authApi(USERDB)
15 | const shuos = shuoApi(USERDB)
16 | const users = usersApi(USERDB)
17 |
18 | //获取全部chat信息
19 | export function getAllMessages (cb) {
20 | setTimeout(() => {
21 | MSGDB.child('messages').on("value", (snapshot) =>{
22 | cb(snapshot)
23 | },(errorObject) => {
24 | console.log("The read failed: " + errorObject.code)
25 | })
26 | }, LATENCY)
27 | }
28 |
29 | export default {
30 | auth,
31 | users,
32 | getAllMessages
33 | }
34 |
--------------------------------------------------------------------------------
/src/views/chat/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
49 |
--------------------------------------------------------------------------------
/src/views/chat/threadlist.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 未读人数: {{ unreadCount }}
7 |
8 |
9 |
10 |
17 |
18 |
19 |
20 |
21 |
48 |
--------------------------------------------------------------------------------
/src/vuex/modules/users.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import Vue from 'vue'
3 |
4 | const state = {
5 | info: {}
6 | }
7 |
8 | const mutations = {
9 | ['USER_INIT'] (state, datasnapshot) {
10 | state.info = datasnapshot.val() || {}
11 | },
12 | ['USER_TOGGLE_EDIT'] (state, panel) {
13 |
14 | },
15 | ['USER_CLOSE_EDIT'] (state, key) {
16 | state.info.hasOwnProperty(key)
17 | },
18 | ['USER_UPDATE'] (state, userKey, data){
19 | let user = state.info[userKey]
20 | for (let field in data) {
21 | user[field] = data[field]
22 | }
23 | },
24 | ['USER_ADD'] (state, datasnapshot) {
25 | let key = datasnapshot.key()
26 | let p = datasnapshot.val()
27 | state.info.hasOwnProperty(key) || Vue.set(state.info, key, p)
28 | },
29 | ['USER_UPDATED'] (state, datasnapshot) {
30 | let key = datasnapshot.key()
31 | let p = datasnapshot.val()
32 | Vue.set(state.info, key, p)
33 | },
34 | ['USER_REMOVE'] (state, datasnapshot) {
35 | let key = datasnapshot.key()
36 | state.info.hasOwnProperty(key) && Vue.delete(state.info, key)
37 | },
38 | ['USER_ERROR'] (state, err, key){
39 | state.info.hasOwnProperty(key)
40 | }
41 |
42 | }
43 |
44 | export default {
45 | state,
46 | mutations
47 | }
48 |
--------------------------------------------------------------------------------
/src/api/shuoApi.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import { Shuo } from './classObject'
4 |
5 | export default function (ref) {
6 | let shuoRef
7 | //初始化用户信息
8 | const init = (dispatch) => {
9 | let authData = ref.getAuth()
10 | let uid = authData.uid.split('simplelogin:').join('')
11 | shuoRef = ref.child(uid).child('shuolist')//说说列表
12 | let shuoRefQuery = shuoRef.orderByChild('nickname')
13 |
14 | shuoRefQuery.off('value')
15 | shuoRefQuery.off('child_added')
16 | shuoRefQuery.off('child_changed')
17 | shuoRefQuery.off('child_removed')
18 |
19 | shuoRefQuery.once('value', datasnapshot => {
20 | dispatch('SHUOS_INIT', datasnapshot)
21 | })
22 | shuoRefQuery.on('child_added', datasnapshot => {
23 | dispatch('SHUOS_ADD', datasnapshot)
24 | })
25 |
26 | }
27 | //创建用户信息
28 | const creatShuo = (dispatch) => {
29 | let shuo = new Shuo()
30 | shuoRefQuery.once("value", function(snapshot) {
31 | //查询用户信息是否存在
32 | if(snapshot.exists()){
33 | shuoRefQuery.set(shuo, err => err && dispatch('SHUOS_ERROR', err))
34 | }else{
35 | shuoRefQuery.push(shuo, err => err && dispatch('SHUOS_ERROR', err))
36 | }
37 | })
38 | }
39 |
40 | return {
41 | init,
42 | creatShuo
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/components/footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/views/chat/messagelist.vue:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
50 |
--------------------------------------------------------------------------------
/src/components/progress.vue:
--------------------------------------------------------------------------------
1 |
50 |
51 |
52 |
53 |
54 |
55 |
61 |
--------------------------------------------------------------------------------
/src/api/index1.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import data from './mock-data'
4 | const LATENCY = 16
5 | import Wilddog from "wilddog"
6 | const ref = new Wilddog("https://vuechat118.wilddogio.com/")
7 | const userdb= new Wilddog("https://userlist.wilddogio.com/")
8 |
9 | export function getSignin ({ uemail, upwd }, cb) {
10 | //查询用户是否被注册
11 | // userdb.createUser({email:uemail,password:upwd},
12 | // function(err,data){
13 | // if(err!=null){
14 | // //not success
15 | // console.log(err)
16 | // } else {
17 | // //create user success
18 | // console.log(data)
19 | // }
20 | // })
21 | //登陆
22 | userdb.authWithPassword({email:uemail,password:upwd},
23 | function(err,data){
24 | if(err == null){
25 | console.log("auth success!");
26 | } else {
27 | console.log("auth failed,msg:",err);
28 | }
29 | }
30 | )
31 | }
32 |
33 | export function getAllMessages (cb) {
34 | setTimeout(() => {
35 | //cb(data)
36 | ref.child('messages').on("value", (snapshot) =>{
37 | cb(snapshot)
38 | },(errorObject) => {
39 | console.log("The read failed: " + errorObject.code)
40 | })
41 | }, LATENCY)
42 | }
43 |
44 | export function createMessage ({ text, thread }, cb) {
45 | const timestamp = Date.now()
46 | const id = 'm_' + timestamp
47 | const message = {
48 | id,
49 | text,
50 | timestamp,
51 | threadID: thread.id,
52 | threadName: thread.name,
53 | authorName: 'Evan'
54 | }
55 | setTimeout(function () {
56 | cb(message)
57 | ref.child('messages').push(message)
58 | }, LATENCY)
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/confirm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ Confirm.message }}
6 |
7 |
8 |
12 |
13 |
14 |
15 |
47 |
48 |
64 |
--------------------------------------------------------------------------------
/src/components/toast.vue:
--------------------------------------------------------------------------------
1 |
2 | {{toast.message}}
3 |
4 |
5 |
20 |
21 |
81 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-chat",
3 | "version": "1.0.0",
4 | "description": "A Vue.js project",
5 | "author": "dinglei",
6 | "private": true,
7 | "scripts": {
8 | "dev": "node build/dev-server.js",
9 | "build": "node build/build.js",
10 | "test": ""
11 | },
12 | "dependencies": {
13 | "avoscloud-sdk": "^0.6.10",
14 | "babel-runtime": "^6.0.0",
15 | "leancloud-realtime": "^3.0.0-beta.3",
16 | "leancloud-realtime-typed-messages": "^1.0.0-beta.2",
17 | "materialize-css": "^0.97.6",
18 | "vue": "^1.0.21",
19 | "wilddog": "^0.6.2",
20 | "wildvue": "^1.0.4"
21 | },
22 | "devDependencies": {
23 | "babel-core": "^6.0.0",
24 | "babel-loader": "^6.0.0",
25 | "babel-plugin-transform-runtime": "^6.0.0",
26 | "babel-preset-es2015": "^6.0.0",
27 | "babel-preset-stage-2": "^6.0.0",
28 | "connect-history-api-fallback": "^1.1.0",
29 | "css-loader": "^0.23.0",
30 | "eventsource-polyfill": "^0.9.6",
31 | "express": "^4.13.3",
32 | "extract-text-webpack-plugin": "^1.0.1",
33 | "file-loader": "^0.8.4",
34 | "function-bind": "^1.0.2",
35 | "html-webpack-plugin": "^2.8.1",
36 | "http-proxy-middleware": "^0.12.0",
37 | "json-loader": "^0.5.4",
38 | "ora": "^0.2.0",
39 | "shelljs": "^0.6.0",
40 | "url-loader": "^0.5.7",
41 | "vue-hot-reload-api": "^1.2.0",
42 | "vue-html-loader": "^1.0.0",
43 | "vue-loader": "^8.3.0",
44 | "vue-style-loader": "^1.0.0",
45 | "vue-toast-mobile": "^0.0.6",
46 | "vuex": "^0.6.2",
47 | "vue-router": "^0.7.13",
48 | "vuex-router-sync": "^1.0.0",
49 | "vue-antd": "^0.2.9",
50 |
51 | "webpack": "^1.12.2",
52 | "webpack-dev-middleware": "^1.4.0",
53 | "webpack-hot-middleware": "^2.6.0",
54 | "webpack-merge": "^0.8.3"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/api/mock-data.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | id: 'm_1',
4 | threadID: 't_1',
5 | threadName: 'Jing and Bill',
6 | authorName: 'Bill',
7 | text: 'Hey Jing, want to give a Flux talk at ForwardJS?',
8 | timestamp: Date.now() - 99999
9 | },
10 | {
11 | id: 'm_2',
12 | threadID: 't_1',
13 | threadName: 'Jing and Bill',
14 | authorName: 'Bill',
15 | text: 'Seems like a pretty cool conference.',
16 | timestamp: Date.now() - 89999
17 | },
18 | {
19 | id: 'm_3',
20 | threadID: 't_1',
21 | threadName: 'Jing and Bill',
22 | authorName: 'Jing',
23 | text: 'Sounds good. Will they be serving dessert?',
24 | timestamp: Date.now() - 79999
25 | },
26 | {
27 | id: 'm_4',
28 | threadID: 't_2',
29 | threadName: 'Dave and Bill',
30 | authorName: 'Bill',
31 | text: 'Hey Dave, want to get a beer after the conference?',
32 | timestamp: Date.now() - 69999
33 | },
34 | {
35 | id: 'm_5',
36 | threadID: 't_2',
37 | threadName: 'Dave and Bill',
38 | authorName: 'Dave',
39 | text: 'Totally! Meet you at the hotel bar.',
40 | timestamp: Date.now() - 59999
41 | },
42 | {
43 | id: 'm_6',
44 | threadID: 't_3',
45 | threadName: 'Functional Heads',
46 | authorName: 'Bill',
47 | text: 'Hey Brian, are you going to be talking about functional stuff?',
48 | timestamp: Date.now() - 49999
49 | },
50 | {
51 | id: 'm_7',
52 | threadID: 't_3',
53 | threadName: 'Bill and Brian',
54 | authorName: 'Brian',
55 | text: 'At ForwardJS? Yeah, of course. See you there!',
56 | timestamp: Date.now() - 39999
57 | },
58 | {
59 | id: 'm_8',
60 | threadID: 't_1',
61 | threadName: 'AAAAAAAAAAAAAAAAAAAA',
62 | authorName: 'Brian',
63 | text: '222222222222222',
64 | timestamp: Date.now() - 29999
65 | }
66 | ]
67 |
--------------------------------------------------------------------------------
/src/views/shuo/left.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
16 |
32 |
33 |
34 |
35 |
49 |
--------------------------------------------------------------------------------
/src/views/shuo/stream.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | -
9 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
48 |
--------------------------------------------------------------------------------
/src/api/usersApi.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | import { User } from './classObject'
4 |
5 | export default function (ref) {
6 | let userRef
7 | //初始化用户信息
8 | const init = (dispatch) => {
9 | let authData = ref.getAuth()
10 | let uid = authData.uid.split('simplelogin:').join('')
11 | userRef = ref.child(uid).child('info')//用户信息
12 | let userRefQuery = userRef.orderByChild('nickname')
13 |
14 | userRefQuery.off('value')
15 | userRefQuery.off('child_added')
16 | userRefQuery.off('child_changed')
17 | userRefQuery.off('child_removed')
18 |
19 | userRefQuery.once('value', datasnapshot => {
20 | dispatch('USER_INIT', datasnapshot)
21 | })
22 | userRefQuery.on('child_added', datasnapshot => {
23 | dispatch('USER_ADD', datasnapshot)
24 | })
25 | userRefQuery.on('child_changed', datasnapshot => {
26 | dispatch('USER_UPDATED', datasnapshot)
27 | })
28 | userRefQuery.on('child_removed', datasnapshot => {
29 | dispatch('USER_REMOVE', datasnapshot)
30 | })
31 |
32 | }
33 | //创建用户信息
34 | const addUser = (dispatch) => {
35 | let user = new User()
36 | userRef.once("value", function(snapshot) {
37 | //查询用户信息是否存在
38 | if(snapshot.exists()){
39 | userRef.set(user, err => err && dispatch('USERS_ERROR', err))
40 | }else{
41 | userRef.push(user, err => err && dispatch('USERS_ERROR', err))
42 | }
43 | })
44 | }
45 |
46 | const getuserinfo = (dispatch) => {
47 | userRef.once("value",function(snapshot){
48 | dispatch('USER_INFO_GET',snapshot.val())
49 | })
50 | }
51 |
52 | //更新用户信息
53 | const updateUser = (dispatch, key, user) => {
54 | userRef.child(key).update({
55 | nickname: user.nickname,
56 | faceimg: user.faceimg
57 | }, err => err ? dispatch('USERS_ERROR', err, key) : dispatch('PANELS_CLOSE_EDIT', key))
58 | }
59 | //删除用户信息
60 | const removeUser = (dispatch, key) => {
61 | userRef.child(key).remove(err => err ? dispatch('USERS_ERROR', err) : dispatch('CONFIRM_CLOSE'))
62 | }
63 |
64 | return {
65 | init,
66 | addUser,
67 | updateUser,
68 | removeUser
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/assets/styles/chat.css:
--------------------------------------------------------------------------------
1 | .bp_chat {
2 | position: absolute;
3 | height: 402px;
4 | width: 560px;
5 | background: #fff;
6 | box-shadow: -3px -2px 8px -1px rgba(0,0,0,0.2);
7 | border-radius: 2px 0px 0px 2px;
8 | color: #333;
9 | bottom: 0;
10 | right: 0;
11 | overflow: hidden;
12 | }
13 | .message-list, .thread-list {
14 | border: 1px solid #ccf;
15 | font-size: 16px;
16 | height: 400px;
17 | margin: 0;
18 | overflow-y: auto;
19 | padding: 0;
20 | }
21 |
22 | .message-section {
23 | float: right;
24 | width: 65%;
25 | }
26 |
27 | .thread-section {
28 | float: left;
29 | width: 32.5%;
30 | }
31 |
32 | .message-thread-heading,
33 | .thread-count {
34 | height: 40px;
35 | margin: 0;
36 | }
37 |
38 | .message-list-item, .thread-list-item {
39 | list-style: none;
40 | padding: 12px 14px 14px;
41 | }
42 |
43 | .chatapp {
44 | max-width: 960px;
45 | margin: 4em auto;
46 | overflow: hidden;
47 | }
48 | .thread-list-item {
49 | border-bottom: 1px solid #ccc;
50 | cursor: pointer;
51 | }
52 |
53 | .thread-list:hover .thread-list-item:hover {
54 | background-color: #f8f8ff;
55 | }
56 |
57 | .thread-list:hover .thread-list-item {
58 | background-color: #fff;
59 | }
60 |
61 | .thread-list-item.active,
62 | .thread-list:hover .thread-list-item.active,
63 | .thread-list:hover .thread-list-item.active:hover {
64 | background-color: #efefff;
65 | cursor: default;
66 | }
67 |
68 | .thread-img{
69 | width: 50px;
70 | height: 50px;
71 | border-radius: 50%;
72 | }
73 |
74 | .message-author-name,
75 | .thread-name {
76 | color: #66c;
77 | float: left;
78 | font-size: 13px;
79 | margin: 0;
80 | }
81 |
82 | .message-time, .thread-time {
83 | color: #aad;
84 | float: right;
85 | font-size: 12px;
86 | }
87 |
88 | .message-text, .thread-last-message {
89 | clear: both;
90 | font-size: 14px;
91 | padding-top: 10px;
92 | }
93 |
94 | .message-composer {
95 | box-sizing: border-box;
96 | font-family: inherit;
97 | font-size: 14px;
98 | height: 5em;
99 | width: 100%;
100 | margin: 20px 0 0;
101 | padding: 10px;
102 | }
103 |
--------------------------------------------------------------------------------
/src/vuex/actions.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import api from "../api"
3 |
4 | export const showConfirm = ({ dispatch }, msg) => {
5 | dispatch('SHOW_CONFIRM', msg)
6 | }
7 | export const showOverlay = ({ dispatch }) => {
8 | dispatch('SHOW_OVERLAY')
9 | }
10 |
11 | export const checkAuth = ({dispatch}) => {
12 | api.auth.checkAuth().then(
13 | () => {
14 | dispatch('AUTH_SIGN_IN_SUCCESS')
15 | //dispatch('MODAL_CLOSE', 'LOGIN')
16 | },
17 | () => {
18 | dispatch('AUTH_REQUIRED');
19 | //dispatch('MODAL_OPEN', 'LOGIN', {errorMsg: 'Please sign in ...'})
20 | }
21 | )
22 | }
23 |
24 | //登录
25 | export const singIn = ({dispatch}, email, password, faceid ,faceurl) => {
26 |
27 | return api.auth.signIn(email,password,faceid,faceurl).then(
28 | () => {
29 | dispatch('AUTH_SIGN_IN_SUCCESS',email, password, faceid ,faceurl)
30 | //dispatch('HIDE_LOGIN')
31 | },
32 | () => {
33 | dispatch('AUTH_SIGN_IN_FAILED')
34 | //dispatch('MODAL_OPEN', 'LOGIN', {errorMsg: 'Invalid email or password'})
35 | }
36 | )
37 |
38 |
39 | }
40 |
41 | //退出登录
42 | export const signOut = ({dispatch}) => {
43 | return api.auth.signOut().then(
44 | () => {
45 | dispatch('AUTH_REQUIRED')
46 | }
47 | )
48 | }
49 | //初始化说说列表
50 | export const initShuo = ({dispatch}) => {
51 | api.shuos.init(dispatch)
52 | }
53 | //发送说说
54 | export const sendTweet = ({dispatch} , text , shuolist) => {
55 | api.shuos.creatShuo(dispatch,text,shuolist)
56 | }
57 | //获取所有信息
58 | export const getAllMessages = ({ dispatch }) => {
59 | api.getAllMessages(messages => {
60 | dispatch('MESSAGES_ALL', messages)
61 | })
62 | }
63 |
64 | //切换用户
65 | export const switchThread = ({ dispatch }, id) => {
66 | dispatch('SWITCH_THREAD', id)
67 | }
68 |
69 | //发送消息
70 | export const sendMessage = ({ dispatch }, text, thread) => {
71 | api.createMessage({ text, thread }, message => {
72 | dispatch('RECEIVE_MESSAGE', message)
73 | })
74 | }
75 |
76 | //初始化用户
77 | export const initUser = ({dispatch}) => {
78 | api.users.init(dispatch)
79 | }
80 | //新增用户信息
81 | export const addUser = ({dispatch},id,url) => {
82 | api.users.addUser(dispatch,id,url)
83 | }
84 | //更新用户信息
85 | export const updateUser = ({dispatch}, key, user) => {
86 | api.users.updateUser(dispatch, key, user)
87 | }
88 |
--------------------------------------------------------------------------------
/src/components/cover.vue:
--------------------------------------------------------------------------------
1 |
2 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | 加载中
76 |
77 |
78 |
79 |
84 |
--------------------------------------------------------------------------------
/src/views/shuo/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | chat_bubble_outline
23 | 私信聊天
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
95 |
--------------------------------------------------------------------------------
/src/components/loading.vue:
--------------------------------------------------------------------------------
1 |
114 |
115 |
116 |
117 |
118 |
119 |
124 |
--------------------------------------------------------------------------------
/src/assets/styles/login.css:
--------------------------------------------------------------------------------
1 | .login {
2 | height: 100%;
3 | min-width: 860px;
4 | min-height: 700px;
5 | overflow: auto;
6 | position: relative;
7 | }
8 | .logo {
9 | position: absolute;
10 | top: 60px;
11 | left: 60px;
12 | }
13 | .web_wechat_login_logo {
14 | background: url(../../assets/images/panel/head/sprite.png) 0 -1107px;
15 | width: 36px;
16 | height: 28px;
17 | vertical-align: middle;
18 | display: inline-block;
19 | }
20 | .login_box {
21 | position: absolute;
22 | top: 50%;
23 | left: 50%;
24 | margin-left: -190px;
25 | margin-top: -270px;
26 | border-radius: 4px;
27 | -moz-border-radius: 4px;
28 | -webkit-border-radius: 4px;
29 | background-color: #fff;
30 | width: 380px;
31 | height: 540px;
32 | box-shadow: #999 0 2px 10px;
33 | -moz-box-shadow: #999 0 2px 10px;
34 | -webkit-box-shadow: #999 0 2px 10px;
35 | }
36 | .form_wrap {
37 | padding: 48px 60px 48px;
38 | }
39 | .mb15 {
40 | margin-bottom: 60px;
41 | }
42 | .form_mod {
43 | margin-bottom: 40px;
44 | font-size: 14px;
45 | line-height: 64px;
46 | background: #f2f2f2;
47 | -webkit-border-radius: 3px;
48 | border-radius: 3px;
49 | }
50 | .userselect {
51 | -webkit-user-select: text;
52 | }
53 | input[type=checkbox], input[type=email], input[type=number], input[type=password], input[type=radio], input[type=search], input[type=tel], input[type=text] {
54 | vertical-align: middle;
55 | outline: 0;
56 | color: #424242;
57 | -webkit-appearance: none;
58 | -webkit-user-select: text;
59 | }
60 | .form_mod .text {
61 | height: 88px;
62 | line-height: 88px;
63 | width: 100%;
64 | -webkit-box-flex: 1;
65 | display: block;
66 | font-size: 24px;
67 | border: none;
68 | color: #444;
69 | background: #f2f2f2;
70 | }
71 | .login_btn:hover,.regist_btn:hover{
72 | cursor: pointer;
73 | }
74 | .login_btn,.regist_btn {
75 | width: 100%;
76 | font-size: 22px;
77 | height: 80px;
78 | line-height: 80px;
79 | margin-bottom: 40px
80 | }
81 | .login_show .login_btn {
82 | display: inline-block;
83 | background: #04BE02;
84 | color: #fff;
85 | border: 0;
86 | border-radius: 3px;
87 | text-align: center;
88 | text-decoration: none;
89 | vertical-align: middle;
90 | white-space: nowrap;
91 | }
92 | .login_show .regist_btn {
93 | display: inline-block;
94 | background: #ff5777;
95 | color: #fff;
96 | border: 0;
97 | border-radius: 3px;
98 | text-align: center;
99 | text-decoration: none;
100 | vertical-align: middle;
101 | white-space: nowrap;
102 | }
103 | .login_cancas{
104 | position: absolute;
105 | right: 20%;
106 | width: 300px;
107 | height: 500px;
108 | }
109 |
--------------------------------------------------------------------------------
/src/views/login.vue:
--------------------------------------------------------------------------------
1 |
2 |
41 |
42 |
48 |
87 |
--------------------------------------------------------------------------------
/src/vuex/store.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | import Vue from 'vue'
3 | import { set } from 'vue'
4 | import Vuex from 'vuex'
5 | import users from "./modules/users"
6 | import shuos from "./modules/shuos"
7 |
8 | Vue.use(Vuex)
9 |
10 | const state = {
11 | isLoginOnline: false,
12 | currentThreadID: null,
13 | threads: {
14 |
15 | },
16 | messages: {
17 |
18 | },
19 | //login
20 | login:{
21 |
22 | },
23 | //overLay
24 | overLay: {
25 | show:false
26 | },
27 | //Confirm
28 | Confirm: {
29 | message:'',
30 | show:false
31 | },
32 |
33 | }
34 |
35 | const mutations = {
36 |
37 | ['MESSAGES_ALL'] (state, messages) {
38 | let latestMessage
39 | messages.forEach(message => {
40 | message = message.val()
41 | if (!state.threads[message.threadID]) {
42 | createThread(state, message.threadID, message.threadName)
43 | }
44 | if (!latestMessage || message.timestamp > latestMessage.timestamp) {
45 | latestMessage = message
46 | }
47 | addMessage(state, message)
48 | })
49 | setCurrentThread(state, latestMessage.threadID)
50 | },
51 | ['SWITCH_THREAD'] (state, id) {
52 | setCurrentThread(state, id)
53 | },
54 | ['RECEIVE_MESSAGE'] (state, message) {
55 | addMessage(state, message)
56 | },
57 |
58 |
59 |
60 |
61 |
62 | //login
63 | ['HIDE_LOGIN'] (state) {
64 | state.login.show = false
65 | },
66 | //overLay
67 | ['SHOW_OVERLAY'] (state) {
68 | state.overLay.show = true
69 | },
70 | //conFirm
71 | ['SHOW_CONFIRM'] (state, msg) {
72 | state.Confirm.message = msg
73 | state.Confirm.show = true
74 | },
75 | ['HIDE_CONFIRM'] (state, msg) {
76 | state.Confirm.message = ''
77 | state.Confirm.show = false
78 | },
79 |
80 | //退出登录
81 | ['AUTH_REQUIRED'] (state) {
82 | state.isLoginOnline = false
83 | },
84 | //登录成功
85 | ['AUTH_SIGN_IN_SUCCESS'] (state) {
86 | state.isLoginOnline = true
87 | },
88 | //登录失败
89 | ['AUTH_SIGN_IN_FAILED'] (state) {
90 | state.isLoginOnline = false
91 | },
92 | }
93 |
94 | function createThread (state, id, name) {
95 | Vue.set(state.threads, id, {
96 | id,
97 | name,
98 | messages: [],
99 | lastMessage: null
100 | })
101 | }
102 |
103 | function addMessage (state, message) {
104 | message.isRead = message.threadID === state.currentThreadID
105 | const thread = state.threads[message.threadID]
106 | if (!thread.messages.some(id => id === message.id)) {
107 | thread.messages.push(message.id)
108 | thread.lastMessage = message
109 | }
110 | Vue.set(state.messages, message.id, message)
111 | }
112 |
113 | function setCurrentThread (state, id) {
114 | state.currentThreadID = id
115 | state.threads[id].lastMessage.isRead = true
116 | }
117 |
118 |
119 | export default new Vuex.Store({
120 | state,
121 | mutations,
122 | modules: {
123 | users,
124 | shuos
125 | },
126 | strict: true
127 | })
128 |
--------------------------------------------------------------------------------
/src/app.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
148 |
--------------------------------------------------------------------------------
/src/views/shuo/timeline.vue:
--------------------------------------------------------------------------------
1 |
2 |
84 |
85 |
86 |
124 |
--------------------------------------------------------------------------------
/src/assets/styles/base.css:
--------------------------------------------------------------------------------
1 | body {
2 | font: 12px/1.3 'Arial','Microsoft YaHei';
3 | _font-family: simsun;
4 | overflow-x: hidden;
5 | color: #333;
6 | }
7 | ul,p {
8 | margin: 0;
9 | padding: 0;
10 | }
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | .btn, .btn-large, .btn-flat{
20 | height: auto;
21 | }
22 | .bp_frame_left{
23 | display: inline-block;
24 | letter-spacing: normal;
25 | word-spacing: normal;
26 | vertical-align: top;
27 | font-size: 12px;
28 | }
29 | .bp_func {
30 | text-align: center;
31 | }
32 | .bp_bg2_br {
33 | color: #fff;
34 | border-color: #fff;
35 | }
36 | .bp_top_nav .bp_line3, .bp_top_nav .bp_input {
37 | border-color: #cccccc;
38 | }
39 | .bp_arrow_bor_t i, .bp_arrow_bor_t em {
40 | _border-style: dashed dashed solid dashed;
41 | border-top-color: transparent;
42 | border-right-color: transparent;
43 | border-left-color: transparent;
44 | }
45 | .bp_arrow_bor i, .bp_arrow_bor em {
46 | display: inline-block;
47 | width: 0;
48 | height: 0;
49 | border-width: 7px;
50 | border-style: solid;
51 | overflow: hidden;
52 | font-size: 0;
53 | line-height: 0;
54 | vertical-align: top;
55 | }
56 | .bp_arrow_bor_t em {
57 | margin: 1px 0 0 -14px;
58 | }
59 | .bp_ficon {
60 | color: #696e78;
61 | }
62 | a:hover .bp_ficon,.bp_txt1:hover ,.bp_top_name:hover{
63 | color: #26a69a;
64 | }
65 | .bp_line1 {
66 | border-color: #d9d9d9;
67 | }
68 | .bp_container {
69 | padding-top: 50px
70 | }
71 | .bp_block {
72 | display: block !important;
73 | }
74 | .bp_bgUserColor, .bp_bgUserColorHover:hover, .bp_bgUserColorHover:focus {
75 | background-color: #19CF86 !important;
76 | }
77 | .bp_inlineBlock {
78 | display: inline-block !important;
79 | max-width: 100%;
80 | }
81 | .bp_textTruncate {
82 | max-width: 100%;
83 | overflow: hidden !important;
84 | text-overflow: ellipsis !important;
85 | white-space: nowrap !important;
86 | word-wrap: normal !important;
87 | }
88 | .bp_textInheritColor {
89 | color: inherit !important;
90 | }
91 | .bp_dir[dir="ltr"] {
92 | direction: ltr !important;
93 | text-align: left !important;
94 | unicode-bidi: embed;
95 | }
96 | .bp_linkComplex, .bp_linkComplex:hover, .bp_linkComplex:focus, .bp_linkComplex:active {
97 | text-decoration: none !important;
98 | }
99 | .bp_Arrange {
100 | -moz-box-sizing: border-box;
101 | box-sizing: border-box;
102 | display: table;
103 | margin: 0;
104 | min-width: 100%;
105 | padding: 0;
106 | table-layout: auto;
107 | }
108 | .bp_Arrange_equal {
109 | table-layout: fixed;
110 | }
111 | .bp_Arrange_sizeFill, .bp_Arrange_sizeFit {
112 | display: table-cell;
113 | padding: 0;
114 | vertical-align: top;
115 | }
116 | .bp_Arrange_bottom .bp_Arrange_sizeFill, .bp_Arrange_bottom .bp_Arrange_sizeFit {
117 | vertical-align: bottom;
118 | }
119 | .bp_Arrange_equal>.bp_Arrange_sizeFill, .bp_Arrange_equal>.bp_Arrange_sizeFit {
120 | width: 1%;
121 | }
122 | .bp_linkClean, .bp_linkClean:hover, .bp_linkClean:focus, .bp_linkClean:active {
123 | text-decoration: none !important;
124 | }
125 | .bp_textUserColor, .bp_textUserColorHover:hover, .bp_textUserColorHover:focus {
126 | color: #19CF86 !important;
127 | }
128 | .clearfix:before, .clearfix:after, .control-group:before, .control-group:after, .stream-item>div:before, .stream-item>div:after, .module:after, .module:before, .follow-card header:after, .follow-card header:before, .follow-bar:after, .follow-bar:before, .tweet-form:after, .tweet-form:before, .modal-footer:after, .modal-footer:before, .local-trends-breadcrumb:after, .local-trends-breadcrumb:before, .input-prepend .add-on, .input-append .add-on, .wrapper:after, .wrapper:before, .expanded-content:before, .expanded-content:after, .stream-item:before, .stream-item:after {
129 | content: " ";
130 | display: table;
131 | }
132 |
133 | button, input, optgroup, select, textarea {
134 | color: inherit;
135 | font: inherit;
136 | margin: 0;
137 | }
138 | button {
139 | overflow: visible;
140 | }
141 | button, select {
142 | text-transform: none;
143 | }
144 | button, html input[type="button"], input[type="reset"], input[type="submit"] {
145 | -webkit-appearance: button;
146 | cursor: pointer;
147 | }
148 | button {
149 | background: transparent;
150 | border: 0;
151 | padding: 0;
152 | }
153 | button {
154 | border: 0;
155 | }
156 | .btn {
157 | background-color: #ccd6dd;
158 | background-repeat: no-repeat;
159 | border: 1px solid #e1e8ed;
160 | border-radius: 4px;
161 | color: #66757f;
162 | cursor: pointer;
163 | display: inline-block;
164 | font-size: 14px;
165 | font-weight: bold;
166 | line-height: normal;
167 | padding: 8px 16px;
168 | position: relative;
169 | }
170 | .btn {
171 | background-color: #f5f8fa;
172 | background-image: linear-gradient(#fff,#f5f8fa);
173 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#fff, endColorstr=#f5f8fa)";
174 | }
175 | .icon-btn {
176 | background: transparent;
177 | border: 1px solid transparent;
178 | color: #1b95e0;
179 | opacity: .8;
180 | -ms-filter: "alpha(opacity=80)";
181 | padding: 4px 10px;
182 | }
183 | .Icon {
184 | background: transparent;
185 | display: inline-block;
186 | font-style: normal;
187 | vertical-align: baseline;
188 | position: relative;
189 | }
190 | .Icon:after, .Icon:before {
191 | display: block;
192 | font-family: "rosettaicons";
193 | font-weight: normal;
194 | font-style: normal;
195 | text-align: center;
196 | -webkit-font-smoothing: antialiased;
197 | }
198 | .Icon--camera:before {
199 | content: "\f027";
200 | }
201 | .Icon--geo:before {
202 | content: "\f031";
203 | }
204 | .Icon--tweet:before {
205 | content: "\f029";
206 | }
207 | input[type=file] {
208 | background-color: #fff;
209 | padding: initial;
210 | border: initial;
211 | line-height: initial;
212 | box-shadow: none;
213 | }
214 | @font-face {
215 | font-family: 'rosettaicons';
216 | src: url('../font/rosetta-icons-Regular.eot');
217 | src: url('../font/rosetta-icons-Regular.eot?#iefix') format('embedded-opentype'),
218 | url('../font/rosetta-icons-Regular.woff') format('woff'),
219 | url('../font/rosetta-icons-Regular.ttf') format('truetype');
220 | font-style: normal;
221 | font-weight: normal
222 | }
223 | a, .btn-link, .btn-link:focus, .icon-btn, .pretty-link b, .pretty-link:hover s, .pretty-link:hover b, .pretty-link:focus s, .pretty-link:focus b, .metadata a:hover, .metadata a:focus, .account-group:hover .fullname, .account-group:focus .fullname, .account-summary:focus .fullname, .message .message-text a, .stats a strong, .plain-btn:hover, .plain-btn:focus, .dropdown.open .user-dropdown.plain-btn, .open > .plain-btn, #global-actions .new:before, .module .list-link:hover, .module .list-link:focus, .stats a:hover, .stats a:hover strong, .stats a:focus, .stats a:focus strong, .profile-modal-header .fullname a:hover, .profile-modal-header .username a:hover, .profile-modal-header .fullname a:focus, .profile-modal-header .username a:focus, .find-friends-sources li:hover .source, .stream-item a:hover .fullname, .stream-item a:focus .fullname, .stream-item .view-all-supplements:hover, .stream-item .view-all-supplements:focus, .tweet .time a:hover, .tweet .time a:focus, .tweet .details.with-icn b, .tweet .details.with-icn .Icon, .tweet .tweet-geo-text a:hover, .stream-item:hover .original-tweet .details b, .stream-item .original-tweet.focus .details b, .stream-item.open .original-tweet .details b, .client-and-actions a:hover, .client-and-actions a:focus, .dismiss-btn:hover b, .tweet .context .pretty-link:hover s, .tweet .context .pretty-link:hover b, .tweet .context .pretty-link:focus s, .tweet .context .pretty-link:focus b, .list .username a:hover, .list .username a:focus, .list-membership-container .create-a-list, .list-membership-container .create-a-list:hover, .card .list-details a:hover, .card .list-details a:focus, .card .card-body:hover .attribution, .card .card-body .attribution:focus, .new-tweets-bar, .onebox .soccer ul.ticker a:hover, .onebox .soccer ul.ticker a:focus, .remove-background-btn, .stream-item-activity-notification .latest-tweet .tweet-row a:hover, .stream-item-activity-notification .latest-tweet .tweet-row a:focus, .stream-item-activity-notification .latest-tweet .tweet-row a:hover b, .stream-item-activity-notification .latest-tweet .tweet-row a:focus b {
224 | color: #19CF86;
225 | }
226 |
--------------------------------------------------------------------------------
/src/components/navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
66 |
67 |
115 |
315 |
--------------------------------------------------------------------------------
/src/views/loginx.vue:
--------------------------------------------------------------------------------
1 |
2 |
63 |
64 |
220 |
357 |
--------------------------------------------------------------------------------
/src/assets/styles/shuo.css:
--------------------------------------------------------------------------------
1 | .bp_chat_btn {
2 | position: absolute;
3 | bottom: 0;
4 | right: 0;
5 | box-shadow: -3px -2px 8px -1px rgba(0,0,0,0.2);
6 | cursor: pointer;
7 | }
8 | .bp_chat_btn .bp_fold_bg{
9 | background: #19CF86;
10 | filter: alpha(opacity=95);
11 | opacity: 0.95;
12 | border-radius: 2px 0px 0px 2px;
13 | }
14 | .bp_chat_btn .bp_fold_cont {
15 | position: absolute;
16 | top: 0;
17 | left: 0;
18 | }
19 | .bp_fold_bg, .bp_chat .bp_fold_cont {
20 | width: 270px;
21 | height: 40px;
22 | }
23 | .chat_icons{
24 | margin: 11px 16px 0 20px;
25 | width: 18px;
26 | height: 16px;
27 | color: #fff;
28 | }
29 | .bp_chat_btn .bp_fold_font {
30 | line-height: 40px;
31 | color: #fff;
32 | font-size: 14px;
33 | font-style: normal;
34 | font-weight: normal;
35 | }
36 | /* 必需 */
37 | .chat-transition {
38 | transition: all .3s ease;
39 | height: 402px;
40 | overflow: hidden;
41 | }
42 |
43 | /* .chat-enter 定义进入的开始状态 */
44 | /* .chat-leave 定义离开的结束状态 */
45 | .chat-enter, .chat-leave {
46 | height: 0;
47 | padding: 0 10px;
48 | opacity: 0;
49 | }
50 | .bp_frame{
51 | min-height: 680px;
52 | margin: 0 auto;
53 | padding: 16px 0 0 0;
54 | width: 1000px;
55 | }
56 | .bp_frame_left{
57 | width: 290px;
58 | }
59 | .bp_frame_main{
60 | width: 590px;
61 | display: inline-block;
62 | }
63 |
64 | /*左侧样式*/
65 |
66 | /*个人信息*/
67 | .bp_module {
68 | background-clip: padding-box;
69 | border: 0;
70 | border-radius: 6px;
71 | line-height: 16px;
72 | position: relative;
73 | margin-bottom: 10px;
74 | }
75 | .shuoCard {
76 | border: 1px solid #e1e8ed;
77 | background: #FFF;
78 | border-radius: 6px;
79 | }
80 | .shuoCard_bg {
81 | background-size: 100%;
82 | border-bottom: 1px solid #e1e8ed;
83 | height: 95px;
84 | border-radius: 4px 4px 0 0;
85 | background-position: 0 50%;
86 | width: 100%;
87 | padding: 0 1px;
88 | margin-left: -1px;
89 | margin-top: -1px;
90 | }
91 | .shuoCard_avatarLink {
92 | background-color: #fff;
93 | border-radius: 6px;
94 | margin: -30px 0 0 8px;
95 | padding: 1px;
96 | vertical-align: bottom;
97 | }
98 | .shuoCard_avatarImage {
99 | border-radius: 7px;
100 | height: 72px;
101 | width: 72px;
102 | border: 2px solid #fff;
103 | -moz-box-sizing: border-box;
104 | box-sizing: border-box;
105 | color: #fff;
106 | }
107 | .shuoCard_userFields {
108 | position: absolute;
109 | top: 103px;
110 | left: 90px;
111 | width: 185px;
112 | }
113 | .shuoCard_name {
114 | font-weight: 700;
115 | font-size: 18px;
116 | line-height: 21px;
117 | }
118 | .shuoCard_screennameLink {
119 | font-size: 12px;
120 | padding-right: 5px;
121 | color: #66757f;
122 | }
123 | .shuoCard_Stats {
124 | margin-left: 11px;
125 | padding: 10px 0;
126 | }
127 | .shuoCard_Stats_stat {
128 | -moz-box-sizing: border-box;
129 | box-sizing: border-box;
130 | line-height: 1;
131 | overflow: hidden;
132 | transition: all .15s ease-in-out;
133 | }
134 | .shuoCard_Stats .shuoCard_Stats_stat {
135 | vertical-align: bottom;
136 | }
137 | .shuoCard_Stats_statLabel {
138 | color: #8899a6;
139 | font-size: 10px;
140 | letter-spacing: .02em;
141 | overflow: hidden;
142 | text-transform: uppercase;
143 | transition: color .15s ease-in-out;
144 | }
145 | .shuoCard_Stats_statValue {
146 | display: block;
147 | font-size: 18px;
148 | font-weight: 500;
149 | padding-top: 3px;
150 | transition: color .15s ease-in-out;
151 | }
152 |
153 |
154 |
155 | /*中间内容main*/
156 |
157 | /*发布txt*/
158 | @media (min-width: 1236px){
159 | body .content-main {
160 | float: left;
161 | margin: 0 10px;
162 | }
163 | }
164 |
165 | .content-main, .content-narrow, .page-canvas, .permalink {
166 | border-radius: 6px;
167 | }
168 | .content-main {
169 | float: right;
170 | width: 590px;
171 | }
172 | .top-timeline-tweetbox .timeline-tweet-box {
173 | border-radius: 5px 5px 0 0;
174 | border: 1px solid #e1e8ed;
175 | }
176 | .tweet-user {
177 | padding: 10px 12px;
178 | background-color: #f5f8fa;
179 | border-radius: 0 0 5px 5px;
180 | }
181 | .home-tweet-box, .LiveVideo-tweetBox, .RetweetDialog-commentBox, .WebToast-box--altColor, .content-main .conversations-enabled .expansion-container .inline-reply-tweetbox {
182 | background-color: #E8FAF2;
183 | }
184 | .top-timeline-tweetbox .tweet-user {
185 | border-radius: 3px 3px 0 0;
186 | position: relative;
187 | }
188 | .avatar {
189 | width: 48px;
190 | height: 48px;
191 | border-radius: 5px;
192 | -moz-force-broken-image-icon: 1;
193 | }
194 | .size32 {
195 | width: 32px;
196 | height: 32px;
197 | border-radius: 4px;
198 | }
199 | .top-timeline-tweet-box-user-image {
200 | left: 28px;
201 | position: absolute;
202 | top: 13px;
203 | }
204 | .t1-form {
205 | font-size: 12px;
206 | margin-bottom: 0;
207 | }
208 | .top-timeline-tweetbox .timeline-tweet-box .t1-form {
209 | margin-left: 56px;
210 | }
211 | .inline-reply-user-image {
212 | position: absolute;
213 | display: none;
214 | top: 16px;
215 | left: 28px;
216 | border-radius: 4px;
217 | }
218 | .tweet-form .tweet-content {
219 | position: relative;
220 | margin-bottom: 8px;
221 | }
222 | .RichEditor {
223 | border: 1px solid #e1e8ed;
224 | border-radius: 3px;
225 | overflow: hidden;
226 | }
227 | .RichEditor {
228 | border-color: #C5F3E0;
229 | }
230 | .tweet-form .RichEditor, .tweet-form .tweet-box {
231 | transition: padding-bottom 300ms cubic-bezier(0.455,0.03,0.515,0.955);
232 | }
233 | .tweet-box textarea:focus, .tweet-box input[type=text], .currently-dragging .tweet-form.is-droppable .tweet-drag-help, .tweet-box[contenteditable="true"]:focus, .RichEditor.is-fakeFocus {
234 | border-color: #A3EBCE;
235 | box-shadow: none;
236 | }
237 | .RichEditor-mozillaCursorWorkaround {
238 | display: inline;
239 | }
240 | .RichEditor-scrollContainer {
241 | overflow-y: auto;
242 | margin: -18px 0;
243 | max-height: 284px;
244 | position: relative;
245 | }
246 | .top-timeline-tweetbox .RichEditor-scrollContainer, .inline-reply-tweetbox .RichEditor-scrollContainer {
247 | max-height: none;
248 | }
249 | .t1-label, input, textarea, div[contenteditable], .t1-select {
250 | margin: 0;
251 | font-size: 13px;
252 | line-height: 20px;
253 | }
254 | input, textarea, div[contenteditable], .t1-select {
255 | display: inline-block;
256 | width: 210px;
257 | padding: 4px;
258 | margin: 0;
259 | outline: 0;
260 | background-color: #fff;
261 | border: 1px solid #e1e8ed;
262 | border-radius: 3px;
263 | }
264 | input, textarea, div[contenteditable] {
265 | transition: background .2s linear;
266 | }
267 | .tweet-form .tweet-box {
268 | min-height: 64px;
269 | overflow: hidden;
270 | padding: 8px;
271 | text-shadow: none;
272 | vertical-align: top;
273 | width: auto;
274 | }
275 | .tweet-form div.rich-editor {
276 | display: block;
277 | word-wrap: break-word;
278 | -webkit-nbsp-mode: normal;
279 | }
280 | .RichEditor div[contenteditable], .RichEditor div[contenteditable]:focus, .RichEditor div[contenteditable].fake-focus {
281 | border: 0;
282 | border-radius: 0;
283 | box-shadow: none;
284 | font-size: 14px;
285 | padding: 8px 10px;
286 | }
287 | .RichEditor-pictographs {
288 | font-size: 14px;
289 | }
290 | .hidden {
291 | display: none;
292 | }
293 | .tweet-box textarea {
294 | resize: vertical;
295 | border-radius: 4px;
296 | box-shadow: inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 #fff;
297 | }
298 | *, *:before, *:after {
299 | box-sizing: initial;
300 | }
301 | .TweetBoxToolbar {
302 | display: -webkit-flex;
303 | display: -moz-box;
304 | display: -ms-flexbox;
305 | display: flex;
306 | -webkit-align-items: center;
307 | -moz-box-align: center;
308 | -ms-flex-align: center;
309 | align-items: center;
310 | }
311 | .TweetBoxExtras-item {
312 | display: inline-block;
313 | position: relative;
314 | }
315 | .photo-selector {
316 | position: relative;
317 | }
318 | .tweet-hash, .tweet-at, .tweet-camera, .tweet-geo, .tweet-alert, .tweet-poll {
319 | height: 17px;
320 | }
321 | .tweet-camera {
322 | background-position: -60px -530px;
323 | width: 20px;
324 | }
325 | .tweet-camera {
326 | height: auto;
327 | width: auto;
328 | }
329 | .btn .Icon {
330 | background: transparent;
331 | }
332 | .icon-btn .Icon {
333 | font-size: 20px;
334 | }
335 | .TweetBoxExtras .icon-btn .Icon {
336 | font-size: 24px;
337 | line-height: 24px;
338 | }
339 | .u-hiddenVisually {
340 | border: 0!important;
341 | clip: rect(1px,1px,1px,1px)!important;
342 | font-size: 1px!important;
343 | height: 1px!important;
344 | overflow: hidden!important;
345 | padding: 0!important;
346 | position: absolute!important;
347 | width: 1px!important;
348 | }
349 | .icon-btn .text {
350 | font-size: 12px;
351 | font-weight: normal;
352 | margin-left: 3px;
353 | position: relative;
354 | top: -6px;
355 | }
356 | .image-selector {
357 | position: absolute;
358 | top: 0;
359 | left: 0;
360 | z-index: 10;
361 | height: 100%;
362 | width: 100%;
363 | overflow: hidden;
364 | cursor: pointer;
365 | }
366 | .t1-label, input, textarea, div[contenteditable], .t1-select {
367 | margin: 0;
368 | font-size: 13px;
369 | line-height: 20px;
370 | }
371 | input, textarea, div[contenteditable], .t1-select {
372 | display: inline-block;
373 | width: 210px;
374 | padding: 4px;
375 | margin: 0;
376 | outline: 0;
377 | background-color: #fff;
378 | border: 1px solid #e1e8ed;
379 | border-radius: 3px;
380 | }
381 | input, textarea, div[contenteditable] {
382 | transition: background .2s linear;
383 | }
384 | .t1-label {
385 | display: block;
386 | margin-bottom: 5px;
387 | color: #292f33;
388 | cursor: pointer;
389 | }
390 | .visuallyhidden {
391 | border: 0;
392 | clip: rect(0 0 0 0);
393 | height: 1px;
394 | margin: -1px;
395 | overflow: hidden;
396 | padding: 0;
397 | position: absolute;
398 | width: 1px;
399 | }
400 | .t1-select, input[type=file] {
401 | height: 30px;
402 | line-height: 30px;
403 | }
404 | .image-selector .file-input {
405 | position: absolute;
406 | top: 0;
407 | right: 0;
408 | z-index: 10;
409 | width: 100%;
410 | height: 100%;
411 | font-size: 50px;
412 | cursor: pointer;
413 | opacity: 0;
414 | -ms-filter: "alpha(opacity=0)";
415 | padding-top: 100px;
416 | }
417 | .image-selector .swf-container {
418 | position: absolute;
419 | top: 0;
420 | left: 0;
421 | z-index: 11;
422 | cursor: pointer;
423 | }
424 | .TweetBoxExtras-item+.TweetBoxExtras-item {
425 | margin-left: 5px;
426 | }
427 | .dropdown {
428 | position: relative;
429 | }
430 | .geo-picker .geo-status {
431 | display: inline-block;
432 | vertical-align: middle;
433 | max-width: 95px;
434 | margin-left: 3px;
435 | overflow: hidden;
436 | color: inherit;
437 | text-overflow: ellipsis;
438 | white-space: nowrap;
439 | width: auto;
440 | }
441 | .TweetBoxToolbar-tweetButton {
442 | margin-left: auto;
443 | }
444 | .tweet-form .tweet-button {
445 | float: right;
446 | }
447 | .spinner {
448 | display: block;
449 | width: 32px;
450 | height: 32px;
451 | margin: 0 auto;
452 | background: url(../images/shuo/spinner-rosetta-gray-32x32.gif) no-repeat 0 0;
453 | }
454 | .tweet-form .spinner {
455 | background-image: url(../images/shuo/spinner-rosetta-gray-14x14.gif);
456 | display: none;
457 | height: 14px;
458 | margin-right: 10px;
459 | position: relative;
460 | top: 2px;
461 | width: 14px;
462 | }
463 | .tweet-counter {
464 | background-color: transparent;
465 | color: #8899a6;
466 | display: inline-block;
467 | width: 35px;
468 | border: 0;
469 | padding: 0 3px;
470 | position: relative;
471 | vertical-align: top;
472 | top: 7px;
473 | text-align: right;
474 | font-size: 14px;
475 | text-shadow: 0 1px 1px rgba(255,255,255,.75);
476 | }
477 | .tweet-form .tweet-counter {
478 | top: 10px;
479 | }
480 | .tweet-form .tweet-button .tweet-counter {
481 | margin-right: 5px;
482 | }
483 | .primary-btn, .following .first-hover .follow-btn:hover, .following .follow-btn, .following .follow-button .cancel-hover-style:hover, .following .follow-button, .notifying .device-follow-button, .button-group-item .content .selected {
484 | color: #fff;
485 | background-color: #1da1f2;
486 | background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.05));
487 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00000000, endColorstr=#0C000000)";
488 | border: 1px solid #3b88c3;
489 | box-shadow: inset 0 1px 0 rgba(255,255,255,0.15);
490 | }
491 | .tweet-btn {
492 | background-color: #1b95e0;
493 | border: 1px solid transparent;
494 | color: #fff;
495 | height:auto;
496 | display: block;
497 | padding: 9px 16px 8px 17px;
498 | text-align: center;
499 | }
500 | .tweet-btn, .tweet-btn:focus {
501 | background-color: #19CF86;
502 | background: rgba(25,207,134,.8);
503 | }
504 | .tweet-form .tweet-btn {
505 | display: inline-block;
506 | }
507 | .btn.disabled, .btn.disabled:hover, .btn[disabled], .btn[aria-disabled=true] {
508 | color: #66757f;
509 | cursor: default;
510 | background-color: #ccd6dd;
511 | background-image: linear-gradient(#fff,#f5f8fa);
512 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#fff, endColorstr=#f5f8fa)";
513 | border-color: #ccd6dd;
514 | opacity: .5;
515 | -ms-filter: "alpha(opacity=50)";
516 | }
517 | .disabled.btn, .disabled.btn:active {
518 | cursor: default;
519 | box-shadow: none;
520 | }
521 | .disabled.btn, .disabled.btn:active {
522 | cursor: default;
523 | box-shadow: none;
524 | }
525 | .btn.disabled, .disabled.btn-large, .btn-floating.disabled, .btn-large.disabled, .btn:disabled .btn-large:disabled, .btn-large:disabled .btn-large:disabled, .btn-floating:disabled {
526 | background-color: #19CF86 !important;
527 | box-shadow: none;
528 | color: #ffffff !important;
529 | cursor: default;
530 | }
531 | .primary-btn.btn.disabled, .primary-btn.btn.disabled:hover, .primary-btn.btn[disabled], .primary-btn[aria-disabled=true] {
532 | background-color: #1da1f2;
533 | background-image: linear-gradient(rgba(0,0,0,0),rgba(0,0,0,0.05));
534 | -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00000000, endColorstr=#0C000000)";
535 | border-color: #3b88c3;
536 | color: #fff;
537 | text-shadow: none;
538 | opacity: .5;
539 | -ms-filter: "alpha(opacity=50)";
540 | }
541 | .tweet-btn.btn.disabled, .tweet-btn.btn.disabled:hover, .tweet-btn.btn[disabled], .tweet-btn.btn[aria-disabled=true] {
542 | background: #1b95e0;
543 | border-color: transparent;
544 | color: white;
545 | opacity: .2;
546 | -ms-filter: "alpha(opacity=20)";
547 | text-shadow: none;
548 | }
549 | .tweet-btn.btn.disabled, .tweet-btn.btn.disabled:hover, .tweet-btn.btn[disabled], .tweet-btn.btn[aria-disabled=true] {
550 | background: #19CF86;
551 | }
552 | .tweet-btn .Icon {
553 | font-size: 19px;
554 | height: 19px;
555 | position: relative;
556 | vertical-align: middle;
557 | width: 17px;
558 | }
559 | .tweet-btn .Icon--tweet {
560 | left: -2px;
561 | top: -5px;
562 | }
563 | .primary-btn .messaging-text {
564 | display: none;
565 | }
566 | .tweet-btn .Icon--dm {
567 | font-size: 17px;
568 | left: -3px;
569 | top: -2px;
570 | }
571 | .photo-selector:hover .btn, .icon-btn:hover, .icon-btn:active, .icon-btn.active, .icon-btn.enabled {
572 | border-color: #19CF86;
573 | border-color: rgba(25,207,134,.5);
574 | color: #19CF86;
575 | }
576 | .photo-selector:hover .btn {
577 | border-color: #1b95e0;
578 | background-color: rgba(255,255,255,0.25);
579 | background-image: linear-gradient(rgba(255,255,255,0),rgba(0,132,180,0.1));
580 | }
581 | .photo-selector:hover .btn, .icon-btn:hover, .icon-btn:active, .icon-btn.active, .icon-btn.enabled {
582 | border-color: #19CF86;
583 | border-color: rgba(25,207,134,.5);
584 | color: #19CF86;
585 | }
586 | .photo-selector:hover .btn, .icon-btn:hover {
587 | background-image: linear-gradient(rgba(255,255,255,0), rgba(25,207,134,.1));
588 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00FFFFFF', endColorstr='#1919CF86');
589 | }
590 | .z-depth-1, nav, .card-panel, .card, .toast, .btn, .btn-large, .btn-floating, .dropdown-content, .collapsible, .side-nav {
591 | box-shadow:none;
592 | }
593 |
594 |
595 |
596 | /*说说消息列表*/
597 | .stream, .stream-container, .stream-items {
598 | position: relative;
599 | margin-left: 0;
600 | list-style: none;
601 | }
602 | .stream-item:not(.no-header-background-module) {
603 | background: #fff;
604 | border-left: 1px solid #e1e8ed;
605 | border-right: 1px solid #e1e8ed;
606 | background-clip: padding-box;
607 | }
608 | .stream, .stream-container, .stream-items {
609 | position: relative;
610 | margin-left: 0;
611 | list-style: none;
612 | }
613 | .stream, .stream-container, .stream-items {
614 | position: relative;
615 | margin-left: 0;
616 | list-style: none;
617 | }
618 | ol, ul {
619 | margin: 0;
620 | list-style: none;
621 | padding: 0;
622 | }
623 | .in-reply-to>ol>li, li.stream-item, .tweets-wrapper li, .recent-tweets li {
624 | line-height: inherit;
625 | }
626 | .stream-item:not(.no-header-background-module) {
627 | background: #fff;
628 | border-left: 1px solid #e1e8ed;
629 | border-right: 1px solid #e1e8ed;
630 | background-clip: padding-box;
631 | }
632 | .account, .tweet, .app {
633 | position: relative;
634 | min-height: 51px;
635 | padding: 9px 12px;
636 | border-bottom: 1px solid #e1e8ed;
637 | }
638 | .tweet {
639 | border-bottom: 1px solid #e1e8ed;
640 | cursor: pointer;
641 | }
642 | .tweet>.context {
643 | margin-left: 58px;
644 | }
645 | .stream-item .content, .permalink-tweet .content {
646 | margin-left: 58px;
647 | }
648 | .stream-item-header .account-group {
649 | color: #8899a6;
650 | }
651 | .avatar {
652 | border-radius: 5px;
653 | -moz-force-broken-image-icon: 1;
654 | }
655 | .stream-item-header .avatar, .permalink-header .avatar {
656 | float: left;
657 | margin-top: 3px;
658 | margin-left: -58px;
659 | }
660 | .fullname {
661 | font-weight: bold;
662 | color: #292f33;
663 | }
664 | .username, .time, .time a, .metadata, .metadata button.btn-link, .metadata a {
665 | font-size: 13px;
666 | color: #8899a6;
667 | }
668 | .username {
669 | direction: ltr;
670 | unicode-bidi: embed;
671 | }
672 | .username s, .account-group-inner s {
673 | color: #b1bbc3;
674 | }
675 | .username, .time, .time a, .metadata, .metadata button.btn-link, .metadata a {
676 | font-size: 13px;
677 | color: #8899a6;
678 | }
679 | .tweet .time {
680 | color: #8899a6;
681 | }
682 | .tweet.retweeted .time, .tweet.favorited .time {
683 | margin-right: 5px;
684 | }
685 | .tweet .time:before {
686 | content: "\00b7";
687 | }
688 | .username, .time, .time a, .metadata, .metadata button.btn-link, .metadata a {
689 | font-size: 13px;
690 | color: #8899a6;
691 | }
692 | .TweetTextSize {
693 | font-size: 14px;
694 | line-height: 18px;
695 | white-space: pre-wrap;
696 | word-wrap: break-word;
697 | }
698 | .tweet p {
699 | word-wrap: break-word;
700 | }
701 | .tweet .js-tweet-text {
702 | white-space: pre-wrap;
703 | }
704 | .expanded-content {
705 | position: relative;
706 | height: 0;
707 | display: none;
708 | }
709 | .tweet .stream-item-footer {
710 | font-size: 12px;
711 | padding-top: 1px;
712 | }
713 | .stream .stream-item-footer {
714 | display: table;
715 | width: 100%;
716 | }
717 | .ProfileTweet-actionCount {
718 | color: #aab8c2;
719 | display: inline-block;
720 | font-size: 12px;
721 | font-weight: bold;
722 | margin-left: 6px;
723 | position: relative;
724 | top: -2px;
725 | }
726 | .retweeted .ProfileTweet-action--retweet .ProfileTweet-actionCount, .favorited .ProfileTweet-action--favorite .ProfileTweet-actionCount {
727 | color: inherit;
728 | }
729 | .ProfileTweet-actionList {
730 | font-size: 0;
731 | line-height: 1;
732 | margin-bottom: 2px;
733 | margin-top: 10px;
734 | }
735 | .ProfileTweet-action {
736 | display: inline-block;
737 | width: 80px;
738 | }
739 | .ProfileTweet-actionButton, .ProfileTweet-actionButtonUndo {
740 | color: #aab8c2;
741 | display: inline-block;
742 | font-size: 16px;
743 | line-height: 1;
744 | padding: 0 2px;
745 | position: relative;
746 | }
747 | .IconContainer, .IconTextContainer {
748 | display: inline-block;
749 | }
750 | .Icon--reply:before {
751 | content: "\f151";
752 | }
753 | .ProfileTweet-action--retweet .ProfileTweet-actionButton.is-disabled, .ProfileTweet-action--retweet .ProfileTweet-actionButton.is-disabled .ProfileTweet-actionCount, .ProfileTweet-action--retweet:hover .ProfileTweet-actionButton.is-disabled, .ProfileTweet-action--retweet:focus .ProfileTweet-actionButton.is-disabled, .ProfileTweet-action--retweet:hover .ProfileTweet-actionButton.is-disabled .ProfileTweet-actionCount, .ProfileTweet-action--retweet:focus .ProfileTweet-actionButton.is-disabled .ProfileTweet-actionCount {
754 | color: #e1e8ed;
755 | cursor: default!important;
756 | }
757 | .Icon--retweet:before {
758 | content: "\f152";
759 | }
760 | .ProfileTweet-actionCount {
761 | color: #aab8c2;
762 | display: inline-block;
763 | font-size: 12px;
764 | font-weight: bold;
765 | margin-left: 6px;
766 | position: relative;
767 | top: -2px;
768 | }
769 | .ProfileTweet-actionCount--isZero {
770 | padding: 0;
771 | }
772 |
--------------------------------------------------------------------------------