├── 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 | 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 | 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 | 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 | 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 | 16 | 17 | 49 | -------------------------------------------------------------------------------- /src/views/chat/threadlist.vue: -------------------------------------------------------------------------------- 1 | 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 | 38 | -------------------------------------------------------------------------------- /src/views/chat/messagelist.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 50 | -------------------------------------------------------------------------------- /src/components/progress.vue: -------------------------------------------------------------------------------- 1 | 50 | 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 | 14 | 15 | 47 | 48 | 64 | -------------------------------------------------------------------------------- /src/components/toast.vue: -------------------------------------------------------------------------------- 1 | 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 | 35 | 49 | -------------------------------------------------------------------------------- /src/views/shuo/stream.vue: -------------------------------------------------------------------------------- 1 | 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 | 79 | 84 | -------------------------------------------------------------------------------- /src/views/shuo/index.vue: -------------------------------------------------------------------------------- 1 | 34 | 95 | -------------------------------------------------------------------------------- /src/components/loading.vue: -------------------------------------------------------------------------------- 1 | 114 | 115 | 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 | 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 | 35 | 36 | 148 | -------------------------------------------------------------------------------- /src/views/shuo/timeline.vue: -------------------------------------------------------------------------------- 1 | 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 | 67 | 115 | 315 | -------------------------------------------------------------------------------- /src/views/loginx.vue: -------------------------------------------------------------------------------- 1 | 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 | --------------------------------------------------------------------------------