├── .browserslistrc ├── public ├── favicon.ico └── index.html ├── babel.config.js ├── src ├── assets │ ├── logo.png │ ├── img │ │ └── logo.jpeg │ ├── iconfont │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.woff2 │ │ ├── iconfont.json │ │ ├── iconfont.css │ │ ├── iconfont.svg │ │ ├── iconfont.js │ │ ├── demo.css │ │ └── demo_index.html │ └── style │ │ ├── mixin.scss │ │ ├── theme.scss │ │ └── style.scss ├── mixin │ └── routeTransition.ts ├── shims-vue.d.ts ├── views │ ├── home │ │ ├── pneumonia │ │ │ ├── syncComponents.vue │ │ │ ├── loadingComponent.vue │ │ │ ├── asyncComponents.vue │ │ │ ├── asyncComponent.ts │ │ │ └── pneumonia.vue │ │ ├── attention │ │ │ └── attention.vue │ │ ├── hotList │ │ │ └── hotList.vue │ │ ├── Home.vue │ │ └── recommend │ │ │ └── recommend.vue │ ├── notify │ │ └── notify.vue │ ├── vip │ │ └── vip.vue │ └── self │ │ └── self.vue ├── components │ ├── loading │ │ └── loading.vue │ ├── container │ │ └── container.vue │ ├── icon │ │ └── icon.vue │ ├── nav │ │ ├── topNavItem.vue │ │ ├── bottomNav.vue │ │ └── topNav.vue │ ├── card │ │ └── card.vue │ └── refresh │ │ └── drop-refresh.vue ├── App.vue ├── directive │ └── directive.ts ├── store │ └── index.ts ├── main.ts └── router │ └── index.ts ├── vue.config.js ├── .gitignore ├── .eslintrc.js ├── tsconfig.json ├── .postcssrc.js ├── package.json ├── doc ├── WebSocket.md └── README.md └── README.md /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/mixin/routeTransition.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | 3 | export const transitionName = ref('') 4 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | export default Vue 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/img/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/src/assets/img/logo.jpeg -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kaiqiangren/vue-next-ts-preview/HEAD/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // 项目配置 2 | module.exports = { 3 | devServer: { 4 | // 手机浏览器需要开启https才可以使用WebRTC 5 | https: true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/style/mixin.scss: -------------------------------------------------------------------------------- 1 | @mixin text-overflow($row) { 2 | display: -webkit-box; 3 | -webkit-box-orient: vertical; 4 | -webkit-line-clamp: $row; 5 | overflow: hidden; 6 | } 7 | -------------------------------------------------------------------------------- /src/views/home/pneumonia/syncComponents.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /src/components/loading/loading.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | -------------------------------------------------------------------------------- /src/views/home/pneumonia/loadingComponent.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | 13 | -------------------------------------------------------------------------------- /src/views/notify/notify.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 17 | -------------------------------------------------------------------------------- /src/views/home/pneumonia/asyncComponents.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /src/views/vip/vip.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 20 | 21 | 23 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 26 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/typescript/recommended' 10 | ], 11 | parserOptions: { 12 | ecmaVersion: 2020 13 | }, 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/directive/directive.ts: -------------------------------------------------------------------------------- 1 | import { ObjectDirective } from 'vue' 2 | 3 | // 使用ObjectDirective声明指令类型即可,因为源码内部指定了默认类型说明 4 | export const customDirective: ObjectDirective = { 5 | beforeMount(el, binding, vnode, prevVnode) { 6 | console.log(el, binding, vnode, prevVnode) 7 | }, 8 | mounted() { console.log('mounted') }, 9 | beforeUpdate() { console.log('beforeUpdate') }, 10 | updated() { console.log('updated') }, 11 | beforeUnmount() { console.log('beforeUnmount') }, 12 | unmounted() { console.log('unmounted') } 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/views/home/pneumonia/asyncComponent.ts: -------------------------------------------------------------------------------- 1 | import { defineAsyncComponent, defineComponent } from 'vue' 2 | const loadingComponent = () => import('./loadingComponent.vue') 3 | 4 | // 异步组件 5 | export const asyncComponent = defineAsyncComponent({ 6 | loader: () => import("./asyncComponents.vue"), 7 | loadingComponent: loadingComponent, 8 | errorComponent: loadingComponent, 9 | delay: 2000, 10 | timeout: 3000 11 | }); 12 | 13 | // 同步组件 14 | export const syncComponent = defineComponent({ 15 | setup () { 16 | return () => `我是同步组件` 17 | } 18 | }) 19 | 20 | -------------------------------------------------------------------------------- /src/assets/style/theme.scss: -------------------------------------------------------------------------------- 1 | 2 | // 白天颜色 3 | $bg-color-night: #f6f6f6 !default; 4 | $text-color-night: #000 !default; 5 | 6 | // 晚上颜色 7 | $bg-color-dark: #000 !default; 8 | $text-color-dark: #eee !default; 9 | 10 | // 切换主题 11 | @mixin themeChange($theme) { 12 | @if($theme == "night") { 13 | background: $bg-color-night; 14 | color: $text-color-night; 15 | } 16 | @if($theme == "dark") { 17 | background: $bg-color-dark; 18 | color: $text-color-dark; 19 | } 20 | } 21 | 22 | .night { 23 | @include themeChange("night"); 24 | } 25 | 26 | .dark { 27 | @include themeChange("dark"); 28 | } 29 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from 'vuex' 2 | 3 | const store = createStore({ 4 | state: { 5 | userInfo: { 6 | name:'renkq' 7 | } 8 | }, 9 | mutations: { 10 | getUserInfo (state, name) { 11 | state.userInfo.name = name 12 | } 13 | }, 14 | actions: { 15 | asyncGetUserInfo ({ commit }) { 16 | setTimeout(() => { 17 | commit("getUserInfo", +new Date() + 'action') 18 | },2000) 19 | } 20 | }, 21 | getters: { 22 | userInfoGetter (state) { 23 | return state.userInfo.name 24 | } 25 | } 26 | }) 27 | 28 | export default store 29 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp} from 'vue'; 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store/index' 5 | import 'normalize.css' 6 | import './assets/iconfont/iconfont.css' 7 | import './assets/style/style.scss' 8 | // h5调试 9 | import Vconsole from 'vconsole' 10 | console.log(process.env.NODE_ENV); 11 | if(process.env.NODE_ENV === 'development') { 12 | new Vconsole() 13 | } 14 | // import { customDirective } from './directive/directive' 15 | // 主题样式 16 | // import './assets/style/theme.scss' 17 | 18 | const app = createApp(App) 19 | app.use(router) 20 | app.use(store) 21 | // 全局注册directive 22 | // app.directive('custom', customDirective) 23 | app.mount('#app') 24 | -------------------------------------------------------------------------------- /src/components/container/container.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | 33 | 38 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "esModuleInterop": true, 10 | "allowSyntheticDefaultImports": true, 11 | "sourceMap": true, 12 | "baseUrl": ".", 13 | "types": [ 14 | "webpack-env" 15 | ], 16 | "paths": { 17 | "@/*": [ 18 | "src/*" 19 | ] 20 | }, 21 | "lib": [ 22 | "esnext", 23 | "dom", 24 | "dom.iterable", 25 | "scripthost" 26 | ] 27 | }, 28 | "include": [ 29 | "src/**/*.ts", 30 | "src/**/*.tsx", 31 | "src/**/*.vue", 32 | "tests/**/*.ts", 33 | "tests/**/*.tsx" 34 | ], 35 | "exclude": [ 36 | "node_modules" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /src/components/icon/icon.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 28 | 29 | 44 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "plugins": { 3 | "autoprefixer": {}, 4 | "postcss-import": {}, 5 | "postcss-url": {}, 6 | "postcss-write-svg": { 7 | utf8: false 8 | }, 9 | // 官方说明文档 https://github.com/evrone/postcss-px-to-viewport/blob/HEAD/README_CN.md 10 | "postcss-px-to-viewport": { 11 | viewportWidth: 375, // (Number) UI设计图的宽度. 12 | viewportHeight: 605, // (Number)UI设计图的高度,一般可以不设置. 13 | unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to. 14 | viewportUnit: 'vw', // (String) Expected units. 15 | selectorBlackList: [], // (Array) The selectors to ignore and leave as px. 16 | minPixelValue: 1, // (Number) 设置要替换的最小像素值. 17 | mediaQuery: false // (Boolean) 允许在媒体查询中转换px 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/assets/style/style.scss: -------------------------------------------------------------------------------- 1 | .left-enter-active { 2 | transition: transform .3s; 3 | transform: translateX(100%); 4 | } 5 | .left-leave-active { 6 | transition: transform .3s; 7 | } 8 | .left-enter-to { 9 | transform: translateX(0); 10 | } 11 | .left-leave-to { 12 | transform: translateX(100%); 13 | } 14 | 15 | .right-enter-active { 16 | transition: transform .5s; 17 | transform: translateX(-100%); 18 | } 19 | .right-leave-active { 20 | transition: transform .5s; 21 | } 22 | .right-enter-to { 23 | transform: translateX(0); 24 | } 25 | .right-leave-to { 26 | transform: translateX(-100%); 27 | } 28 | 29 | .top-enter-active { 30 | transition: transform .5s; 31 | transform: translateY(-100%); 32 | } 33 | .top-leave-active { 34 | transition: transform .5s; 35 | } 36 | .top-enter-to { 37 | transform: translateY(0); 38 | } 39 | .top-leave-to { 40 | transform: translateY(-100%); 41 | } 42 | -------------------------------------------------------------------------------- /src/components/nav/topNavItem.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 29 | 30 | 48 | -------------------------------------------------------------------------------- /src/views/home/pneumonia/pneumonia.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 46 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-next-ts-preview", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "vue-cli-service build", 7 | "lint": "vue-cli-service lint", 8 | "dev": "vue-cli-service serve" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.4", 12 | "normalize.css": "^8.0.1", 13 | "socket.io-client": "^2.3.0", 14 | "vconsole": "^3.3.4", 15 | "vue": "^3.0.0-beta.14", 16 | "vue-router": "^4.0.0-alpha.12", 17 | "vuex": "^4.0.0-beta.2" 18 | }, 19 | "devDependencies": { 20 | "@typescript-eslint/eslint-plugin": "^2.26.0", 21 | "@typescript-eslint/parser": "^2.26.0", 22 | "@vue/cli-plugin-babel": "~4.3.0", 23 | "@vue/cli-plugin-eslint": "~4.3.0", 24 | "@vue/cli-plugin-router": "~4.3.0", 25 | "@vue/cli-plugin-typescript": "~4.3.0", 26 | "@vue/cli-plugin-vuex": "~4.3.0", 27 | "@vue/cli-service": "~4.3.0", 28 | "@vue/compiler-sfc": "^3.0.0-beta.1", 29 | "@vue/eslint-config-typescript": "^5.0.2", 30 | "eslint": "^6.7.2", 31 | "eslint-plugin-vue": "^7.0.0-alpha.0", 32 | "node-sass": "^4.12.0", 33 | "postcss-import": "^12.0.1", 34 | "postcss-px-to-viewport": "^1.1.1", 35 | "postcss-url": "^8.0.0", 36 | "postcss-write-svg": "^3.0.1", 37 | "sass-loader": "^8.0.2", 38 | "typescript": "~3.8.3", 39 | "vue-cli-plugin-vue-next": "~0.1.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/views/self/self.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 55 | 56 | 58 | -------------------------------------------------------------------------------- /src/views/home/attention/attention.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 62 | 63 | 73 | -------------------------------------------------------------------------------- /src/views/home/hotList/hotList.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 64 | 65 | 76 | -------------------------------------------------------------------------------- /doc/WebSocket.md: -------------------------------------------------------------------------------- 1 | # websocket相关 2 | 3 | ## 一、koa依赖 4 | ```json 5 | { 6 | "name": "webrtc-sever", 7 | "version": "1.0.0", 8 | "description": "", 9 | "main": "index.js", 10 | "scripts": { 11 | "dev": "cross-env NODE_ENV=development nodemon index.js --watch index.js --exec babel-node", 12 | "start": "cross-env NODE_ENV=production node index.js --exec babel-node" 13 | }, 14 | "keywords": [], 15 | "author": "kaiqiangren", 16 | "license": "MIT", 17 | "dependencies": { 18 | "consola": "^2.12.2", 19 | "koa": "^2.12.0", 20 | "koa-bodyparser": "^4.3.0", 21 | "koa-json": "^2.0.2", 22 | "koa-onerror": "^4.1.0", 23 | "koa-router": "^9.0.1", 24 | "socket.io": "^2.3.0" 25 | }, 26 | "devDependencies": { 27 | "babel-cli": "^6.26.0", 28 | "babel-preset-es2015": "^6.24.1", 29 | "cross-env": "^7.0.2", 30 | "nodemon": "^2.0.4" 31 | } 32 | } 33 | ``` 34 | 35 | ## 二、服务端代码 36 | 37 | ```js 38 | import Koa from 'koa' 39 | import bodyParser from 'koa-bodyparser' 40 | import json from 'koa-json' 41 | import onerror from 'koa-onerror' 42 | import consola from 'consola' 43 | import ioSocket from 'socket.io' 44 | import http from 'http' 45 | 46 | const app = new Koa() 47 | app.keys = ['keys', 'keyskeys'] 48 | app.proxy = true 49 | app.use(bodyParser()) 50 | app.use(json()) 51 | 52 | // 创建server 53 | const server = http.Server(app.callback()); 54 | const io = ioSocket(server); 55 | 56 | io.on('connection', (socket) => { 57 | socket.send('连接成功!') 58 | 59 | }); 60 | 61 | onerror(app) 62 | 63 | server.listen(8000, () => { 64 | consola.ready({ 65 | message: `Server listening on localhost:8000`, 66 | badge: true 67 | }) 68 | }) 69 | ``` 70 | 71 | 72 | ## 三、客户端代码 73 | 74 | ```js 75 | import io from 'socket.io-client' 76 | 77 | const socket = io('ws://localhost:8000') 78 | socket.on('connection', client => { 79 | console.log(client) 80 | }) 81 | socket.on('message', async (message) => { 82 | console.log(message) 83 | }) 84 | ``` 85 | -------------------------------------------------------------------------------- /src/views/home/Home.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 72 | 73 | 79 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # WebRTC相关文档 2 | 3 | ## 一、浏览器支持检测 4 | ```js 5 | //判断浏览器是否支持这些 API 6 | if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) { 7 | console.log("enumerateDevices() not supported.") 8 | } 9 | ``` 10 | 11 | ## 二、检测硬件设备列表 12 | ```js 13 | navigator.mediaDevices.enumerateDevices().then(list => { 14 | console.log(list) 15 | }) 16 | ``` 17 | 18 | ## 三、获取摄像头、音频 19 | ```js 20 | // video标签元素 21 | const view = document.getElementById('view') 22 | let promise = navigator.mediaDevices.getUserMedia({ 23 | video: true, // 是否抓取视频 24 | audio:true //是否抓取音频 25 | }); 26 | promise.then((stream) => { 27 | // 使用video标签播放视频流,注意video必须设置autoplay,否则需要手动调用play方法 28 | view.srcObject = stream; 29 | }) 30 | ``` 31 | 32 | ## 四、录制视频流 33 | ```js 34 | // var mediaRecorder = new MediaRecorder(stream[, options]); 35 | /* 36 | stream,通过 getUserMedia 获取的本地视频流或通过 RTCPeerConnection 获取的远程视频流。 37 | options,可选项,指定视频格式、编解码器、码率等相关信息,如 mimeType: 'video/webm;codecs=vp8'。 38 | MediaRecorder 对象还有一个特别重要的事件,即 ondataavailable 事件。 39 | 当 MediaRecoder 捕获到数据时就会触发该事件。通过它,我们才能将音视频数据录制下来。 40 | */ 41 | ``` 42 | 43 | ## 五、抓取桌面 44 | ```js 45 | // view 为video元素容器 46 | navigator.mediaDevices.getDisplayMedia({ 47 | video: true, // 是否抓取视频 48 | audio:true //是否抓取音频 49 | }).then(stream => { 50 | view.srcObject = stream; 51 | }) 52 | ``` 53 | 54 | ## 六、WebRTC核心api 55 | ```js 56 | var pcConfig = null; 57 | var pc = new RTCPeerConnection(pcConfig); 58 | 59 | //呼叫方创建 Offer 60 | pc.createOffer(setLocalAndSendMessage, handleCreateOfferError); 61 | 62 | function setLocalAndSendMessage(sessionDescription) { 63 | pc.setLocalDescription(sessionDescription); 64 | sendMessage(sessionDescription); 65 | } 66 | 67 | // 被呼叫方收到 Offer 68 | socket.on('message', function(message) { 69 | if (message.type === 'offer') { 70 | pc.setRemoteDescription(new RTCSessionDescription(message)); 71 | doAnswer(); 72 | } 73 | }) 74 | 75 | // 被呼叫方创建 Answer 76 | function doAnswer() { 77 | pc.createAnswer().then( setLocalAndSendMessage, onCreateSessionDescriptionError ); 78 | } 79 | 80 | // 呼叫方收到 Answer 81 | socket.on('message', function(message) { 82 | if (message.type === 'answer') { 83 | pc.setRemoteDescription(new RTCSessionDescription(message)); 84 | } 85 | }) 86 | ``` 87 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from 'vue-router'; 2 | 3 | interface RoutesType { 4 | path: string; 5 | name: string; 6 | component: () => {}; 7 | meta?: { 8 | index?: number; 9 | keepAlive?: boolean; 10 | }; 11 | children?: RoutesType[]; 12 | } 13 | 14 | const routes: RoutesType[] = [ 15 | { 16 | path: '/', 17 | name: 'Home', 18 | component: () => import(/* webpackChunkName: "home" */ '../views/home/Home.vue'), 19 | children: [ 20 | { 21 | path: '', 22 | name: 'Recommend', 23 | component: () => import(/* webpackChunkName: "recommend" */ '../views/home/recommend/recommend.vue'), 24 | meta: { 25 | index: 1, 26 | keepAlive:false 27 | } 28 | }, 29 | { 30 | path: 'attention/:id', 31 | name: 'Attention', 32 | component: () => import(/* webpackChunkName: "attention" */ '../views/home/attention/attention.vue'), 33 | meta: { 34 | index: 0, 35 | keepAlive:false 36 | } 37 | }, 38 | { 39 | path: 'hotList', 40 | name: 'HotList', 41 | component: () => import(/* webpackChunkName: "hostList" */ '../views/home/hotList/hotList.vue'), 42 | meta: { 43 | index: 2, 44 | keepAlive:false 45 | } 46 | }, 47 | { 48 | path: 'pneumonia', 49 | name: 'Pneumonia', 50 | component: () => import(/* webpackChunkName: "pneumonia" */ '../views/home/pneumonia/pneumonia.vue'), 51 | meta: { 52 | index: 4, 53 | keepAlive:false 54 | } 55 | } 56 | ] 57 | }, 58 | { 59 | path: '/vip', 60 | name: 'Vip', 61 | component: () => import(/* webpackChunkName: "vip" */ '../views/vip/vip.vue'), 62 | }, 63 | { 64 | path: '/self', 65 | name: 'Self', 66 | component: () => import(/* webpackChunkName: "self" */ '../views/self/self.vue'), 67 | }, 68 | { 69 | path: '/notify', 70 | name: 'Notify', 71 | component: () => import(/* webpackChunkName: "notify" */ '../views/notify/notify.vue'), 72 | } 73 | ] 74 | 75 | const router = createRouter({ 76 | // createWebHistory 第一个参数为以前路由的base 77 | history: createWebHistory(process.env.BASE_URL), 78 | routes 79 | }) 80 | 81 | export default router 82 | -------------------------------------------------------------------------------- /src/components/card/card.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 44 | 45 | 98 | -------------------------------------------------------------------------------- /src/components/nav/bottomNav.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 84 | 85 | 106 | -------------------------------------------------------------------------------- /src/views/home/recommend/recommend.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 94 | 95 | 100 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "1784116", 3 | "name": "zhihu", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "", 6 | "description": "知乎demo项目", 7 | "glyphs": [ 8 | { 9 | "icon_id": "1001157", 10 | "name": "下拉", 11 | "font_class": "iconxlsx", 12 | "unicode": "e629", 13 | "unicode_decimal": 58921 14 | }, 15 | { 16 | "icon_id": "1486824", 17 | "name": "加载", 18 | "font_class": "jiazai", 19 | "unicode": "e62b", 20 | "unicode_decimal": 58923 21 | }, 22 | { 23 | "icon_id": "1725607", 24 | "name": "对", 25 | "font_class": "dui", 26 | "unicode": "e613", 27 | "unicode_decimal": 58899 28 | }, 29 | { 30 | "icon_id": "166640", 31 | "name": "首页", 32 | "font_class": "shouye", 33 | "unicode": "e637", 34 | "unicode_decimal": 58935 35 | }, 36 | { 37 | "icon_id": "902044", 38 | "name": "用户", 39 | "font_class": "yonghu", 40 | "unicode": "e634", 41 | "unicode_decimal": 58932 42 | }, 43 | { 44 | "icon_id": "1506397", 45 | "name": "通知", 46 | "font_class": "tongzhi", 47 | "unicode": "e61e", 48 | "unicode_decimal": 58910 49 | }, 50 | { 51 | "icon_id": "4294111", 52 | "name": "皇冠", 53 | "font_class": "huangguan1", 54 | "unicode": "e798", 55 | "unicode_decimal": 59288 56 | }, 57 | { 58 | "icon_id": "9626878", 59 | "name": "皇冠", 60 | "font_class": "huangguan2", 61 | "unicode": "e870", 62 | "unicode_decimal": 59504 63 | }, 64 | { 65 | "icon_id": "788018", 66 | "name": "加号", 67 | "font_class": "jiahao", 68 | "unicode": "e640", 69 | "unicode_decimal": 58944 70 | }, 71 | { 72 | "icon_id": "1637081", 73 | "name": "皇冠", 74 | "font_class": "huangguan", 75 | "unicode": "e603", 76 | "unicode_decimal": 58883 77 | }, 78 | { 79 | "icon_id": "4766293", 80 | "name": "user", 81 | "font_class": "user", 82 | "unicode": "e7ae", 83 | "unicode_decimal": 59310 84 | }, 85 | { 86 | "icon_id": "4766680", 87 | "name": "bell", 88 | "font_class": "bell", 89 | "unicode": "e7c4", 90 | "unicode_decimal": 59332 91 | }, 92 | { 93 | "icon_id": "4766685", 94 | "name": "home", 95 | "font_class": "home", 96 | "unicode": "e7c6", 97 | "unicode_decimal": 59334 98 | }, 99 | { 100 | "icon_id": "4766856", 101 | "name": "carry out", 102 | "font_class": "carryout", 103 | "unicode": "e7d3", 104 | "unicode_decimal": 59347 105 | }, 106 | { 107 | "icon_id": "6486534", 108 | "name": "放大镜", 109 | "font_class": "fangdajing", 110 | "unicode": "e60c", 111 | "unicode_decimal": 58892 112 | }, 113 | { 114 | "icon_id": "14095303", 115 | "name": "直播", 116 | "font_class": "zhibo", 117 | "unicode": "e78b", 118 | "unicode_decimal": 59275 119 | } 120 | ] 121 | } 122 | -------------------------------------------------------------------------------- /src/components/nav/topNav.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 82 | 83 | 124 | -------------------------------------------------------------------------------- /src/components/refresh/drop-refresh.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 109 | 110 | 139 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1588902381829'); /* IE9 */ 3 | src: url('iconfont.eot?t=1588902381829#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1588902381829') format('woff'), 6 | url('iconfont.ttf?t=1588902381829') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1588902381829#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .iconxlsx:before { 19 | content: "\e629"; 20 | } 21 | 22 | .jiazai:before { 23 | content: "\e62b"; 24 | } 25 | 26 | .dui:before { 27 | content: "\e613"; 28 | } 29 | 30 | .shouye:before { 31 | content: "\e637"; 32 | } 33 | 34 | .yonghu:before { 35 | content: "\e634"; 36 | } 37 | 38 | .tongzhi:before { 39 | content: "\e61e"; 40 | } 41 | 42 | .huangguan1:before { 43 | content: "\e798"; 44 | } 45 | 46 | .huangguan2:before { 47 | content: "\e870"; 48 | } 49 | 50 | .jiahao:before { 51 | content: "\e640"; 52 | } 53 | 54 | .huangguan:before { 55 | content: "\e603"; 56 | } 57 | 58 | .user:before { 59 | content: "\e7ae"; 60 | } 61 | 62 | .bell:before { 63 | content: "\e7c4"; 64 | } 65 | 66 | .home:before { 67 | content: "\e7c6"; 68 | } 69 | 70 | .carryout:before { 71 | content: "\e7d3"; 72 | } 73 | 74 | .fangdajing:before { 75 | content: "\e60c"; 76 | } 77 | 78 | .zhibo:before { 79 | content: "\e78b"; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(i){var t,h='',e=(t=document.getElementsByTagName("script"))[t.length-1].getAttribute("data-injectcss");if(e&&!i.__iconfont__svg__cssinject__){i.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}!function(t){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(t,0);else{var e=function(){document.removeEventListener("DOMContentLoaded",e,!1),t()};document.addEventListener("DOMContentLoaded",e,!1)}else document.attachEvent&&(l=t,c=i.document,o=!1,(h=function(){try{c.documentElement.doScroll("left")}catch(t){return void setTimeout(h,50)}a()})(),c.onreadystatechange=function(){"complete"==c.readyState&&(c.onreadystatechange=null,a())});function a(){o||(o=!0,l())}var l,c,o,h}(function(){var t,e,a,l,c,o;(t=document.createElement("div")).innerHTML=h,h=null,(e=t.getElementsByTagName("svg")[0])&&(e.setAttribute("aria-hidden","true"),e.style.position="absolute",e.style.width=0,e.style.height=0,e.style.overflow="hidden",a=e,(l=document.body).firstChild?(c=a,(o=l.firstChild).parentNode.insertBefore(c,o)):l.appendChild(a))})}(window); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue3.0 2 | 3 | > Vue3.0是2020年4月刚刚发布了beta版本的全新Vue版本 4 | ## 项目地址: 5 | https://github.com/kaiqiangren/vue-next-ts-preview 6 | 7 | ## 一、Vue3.0与Vue2.0的对比: 8 | ### 优点: 9 | 1. 将Vue内部的绝大部分api对外暴露,使Vue具备开发大型项目的能力,例如compile编译api等 10 | 2. webpack的treeshaking支持度友好 11 | 3. 使用Proxy进行响应式变量定义,性能提高2-3倍 12 | 4. 可在Vue2.0中单独使用composition-api插件,或者直接用它开发插件 13 | 5. 对typescript支持更加友好 14 | 6. 面向未来:对于尤雨溪最近创新的vite开发服务器(舍弃webpack、底层为Koa框架的高性能开发服务器),直接使用的Vue3.0语法 15 | ### 缺点: 16 | 1. 只支持IE11及以上 17 | 2. 对于习惯了Vue2.0开发模式的开发者来说,增加了心智负担,对开发者代码组织能力有考验 18 | > 同时也是能力提升的机会吧,特别喜欢Vue作者的设计初心:让开发者随着框架一起成长! 19 | 20 | 21 | ## 二、Vue3.0正确的打开方式 22 | ### 1、项目搭建 23 | 1. 需要安装vue-cli4代最新脚手架,可以通过执行如下npm 命令安装/更新脚手架版本 24 | ```npm 25 | npm i @vue/cli -g 26 | ``` 27 | 2. 然后在创建项目后,执行vue add vue-next向项目添加Vue3.0 28 | ```npm 29 | vue create [projectName] 30 | cd [projectName] 31 | vue add vue-next 32 | ``` 33 | 3. 如下例子为使用typescript + Vue3.0 开发的项目依赖,也可以直接使用 34 | ```json 35 | { 36 | "name": "vue-next-ts-preview", 37 | "version": "0.1.0", 38 | "private": true, 39 | "scripts": { 40 | "build": "vue-cli-service build", 41 | "lint": "vue-cli-service lint", 42 | "dev": "vue-cli-service serve" 43 | }, 44 | "dependencies": { 45 | "core-js": "^3.6.4", 46 | "normalize.css": "^8.0.1", 47 | "vue": "^3.0.0-beta.14", 48 | "vue-router": "^4.0.0-alpha.12", 49 | "vuex": "^4.0.0-beta.2" 50 | }, 51 | "devDependencies": { 52 | "@typescript-eslint/eslint-plugin": "^2.26.0", 53 | "@typescript-eslint/parser": "^2.26.0", 54 | "@vue/cli-plugin-babel": "~4.3.0", 55 | "@vue/cli-plugin-eslint": "~4.3.0", 56 | "@vue/cli-plugin-router": "~4.3.0", 57 | "@vue/cli-plugin-typescript": "~4.3.0", 58 | "@vue/cli-plugin-vuex": "~4.3.0", 59 | "@vue/cli-service": "~4.3.0", 60 | "@vue/compiler-sfc": "^3.0.0-beta.1", 61 | "@vue/eslint-config-typescript": "^5.0.2", 62 | "eslint": "^6.7.2", 63 | "eslint-plugin-vue": "^7.0.0-alpha.0", 64 | "node-sass": "^4.12.0", 65 | "sass-loader": "^8.0.2", 66 | "typescript": "~3.8.3", 67 | "vue-cli-plugin-vue-next": "~0.1.2" 68 | } 69 | } 70 | ``` 71 | 72 | ### 2、使用文档 73 | 1. 项目入口main.ts 74 | ```typescript 75 | import { createApp } from 'vue'; 76 | import App from './App.vue' 77 | import router from './router' 78 | 79 | const app = createApp(App) 80 | app.use(router) 81 | app.mount('#app') 82 | ``` 83 | 84 | ### 3、语法相关 85 | 1. 响应式变量声明 86 | ```js 87 | import { ref, reactive } from 'vue' 88 | // 方式一: 可传入任意类型的值,改变值的时候必须使用其value属性,例 refData.value = 2 89 | const refData = ref(0) 90 | 91 | // 方式二: 只能传入引用类型的值 92 | const data = reactive({ 93 | tableData: [ 94 | { 95 | name: '姓名1' 96 | } 97 | ] 98 | }) 99 | 100 | // 使用响应式变量前,必须在Vue文件的setup函数中 执行/return 出去 101 | setup (props, context){ 102 | return { 103 | refData, 104 | data 105 | } 106 | } 107 | ``` 108 | 109 | 2. computed 110 | ```js 111 | import { watch, watchEffect, computed } from 'vue' 112 | 113 | // 1、创建只读的计算属性 114 | const computedCount = computed(() => count.value + 1) 115 | 116 | 117 | // 2、创建可读可写的计算属性 118 | const computedCount2 = computed({ 119 | get: () => writeCount.value + 2, 120 | set: (val) => { 121 | return writeCount.value = val + 2 122 | } 123 | }) 124 | 125 | // 可以直接修改computed的值,在Vue2.x中无法修改 126 | // computedCount2 = 123 127 | ``` 128 | 129 | 3. watch & watchEffect 130 | ```js 131 | import { ref, watch, watchEffect } from 'vue' 132 | const count = ref(0) 133 | // watchEffect会自动收集响应式依赖 134 | watchEffect(() => console.log(count.value)) 135 | 136 | // 监听指定基础类型数据 137 | watch(count, (now, prev) => { 138 | console.log(now, prev, 'count') 139 | }) 140 | 141 | const data = reactive({ 142 | tableData: [ 143 | { 144 | name: '姓名1' 145 | } 146 | ] 147 | }) 148 | // 监听reactive创建的响应式变量,可以直接监听对象,必须使用内联函数 149 | watch(() => data.tableData, (now, prev) => { 150 | console.log(now, prev, 'tableData') 151 | }) 152 | 153 | // 监听多个参数- composition-api中无法使用 154 | let position = reactive({ 155 | x: 1, 156 | y: 1 157 | }) 158 | watch([ 159 | () => position.x, 160 | () => position.y, 161 | ], ([x1, y1], [nx1, ny1]) => { 162 | console.log('x1,y1', x1, y1) 163 | console.log('nx1,ny1', nx1, ny1) 164 | }, 165 | { 166 | flush: 'post', // 默认, 在视图渲染后触发 167 | // flush: 'pre', // 在视图渲染之前触发 168 | // flush: 'sync' // 无阻塞,异步触发 169 | }) 170 | ``` 171 | 172 | 4. provide & inject 173 | ```js 174 | import { reactive, provide , inject} from 'vue' 175 | 176 | const data = reactive({ 177 | tableData: [ 178 | { 179 | name: '姓名1' 180 | } 181 | ] 182 | }) 183 | // 根级/父级组件 184 | // provide 这里如果提供的是响应式变量,inject也会触发响应 185 | provide('provideName', 'provideData') 186 | provide('provideReactive', data.tableData) 187 | 188 | 189 | // 子级/孙级组件 190 | setup () { 191 | const provideData = inject('provideName') 192 | const provideReactive = inject('provideReactive') 193 | return { 194 | provideData, 195 | provideReactive 196 | } 197 | } 198 | ``` 199 | 200 | 5. 生命周期 201 | ```html 202 | Vue3.0生命周期 说明 对应的Vue2.0生命周期 203 | setup | 初始化数据阶段的生命周期,介于beforeCreate与created之间 相当于beforeCreate、created的合并 204 | onBeforeMount | 组件挂载前 beforeMount 205 | onMounted | 实例挂载完毕 mounted 206 | onBeforeUpdate | 响应式数据变化前 beforeUpdate 207 | onUpdated | 响应式数据变化完成 updated 208 | onBeforeUnmount | 实例销毁前 beforeDestroy 209 | onUnmounted | 实例已销毁 destroyed 210 | onErrorCaptured | 错误数据捕捉 -- 211 | ``` 212 | 213 | 214 | 6. 路由 215 | ```js 216 | // 组件内部路由拦截器的使用方式 217 | import { useRouter, useRoute } from "vue-router" 218 | 219 | setup() { 220 | // 组件内路由 221 | const router = useRouter() 222 | router.beforeEach((to, from, next) => { 223 | next() 224 | }) 225 | // 组件内路由信息 226 | const route = useRoute() 227 | } 228 | 229 | ``` 230 | 231 | 7.vuex 232 | > 创建Store 233 | ```js 234 | import { createStore } from 'vuex' 235 | 236 | const store = createStore({ 237 | state: { 238 | userInfo: { 239 | name:'renkq' 240 | } 241 | }, 242 | mutations: { 243 | getUserInfo (state, name) { 244 | state.userInfo.name = name 245 | } 246 | }, 247 | actions: { 248 | asyncGetUserInfo ({ commit }) { 249 | setTimeout(() => { 250 | commit("getUserInfo", +new Date() + 'action') 251 | },2000) 252 | } 253 | }, 254 | getters: { 255 | userInfoGetter (state) { 256 | return state.userInfo.name 257 | } 258 | } 259 | }) 260 | 261 | export default store 262 | 263 | ``` 264 | > 组件内使用store 265 | ```js 266 | import { 267 | useStore, 268 | // mapState, 269 | // mapMutations, 270 | // mapActions, 271 | // mapGetters 272 | } from 'vuex' 273 | 274 | export default { 275 | name: 'self', 276 | setup() { 277 | const store = useStore() 278 | console.log(store, 'store') 279 | console.log(store.getters, 'getters') 280 | const state = store.state 281 | const getters = store.getters 282 | // console.log(mapState(store.state),'mapState') 283 | // console.log(mapMutations(store._mutations),'mapMutations') 284 | // console.log(mapActions(store._actions),'mapActions') 285 | // console.log(mapGetters(store.getters),'mapGetters') 286 | const methods = { 287 | // 处理commit 288 | handleMutation: () => { 289 | store.commit('getUserInfo', +new Date) 290 | }, 291 | // 处理dispatch 292 | handleAction: () => { 293 | store.dispatch('asyncGetUserInfo') 294 | } 295 | } 296 | return { 297 | state, 298 | getters, 299 | ...methods 300 | } 301 | } 302 | } 303 | ``` 304 | 305 | 8. v-model 306 | 307 | ```js 308 | // 自定义v-model组件时,需要使用update:modelValue事件进行触发 309 | setup(props, { emit }){ 310 | const handleClick = () => { 311 | emit('update:modelValue', params) 312 | } 313 | return { 314 | handleClick 315 | } 316 | } 317 | ``` 318 | 319 | 9. directive 320 | > 定义指令 321 | ```js 322 | import { ObjectDirective } from 'vue' 323 | // 使用ObjectDirective声明指令类型即可,因为源码内部指定了默认类型说明 324 | export const customDirective: ObjectDirective = { 325 | beforeMount(el, binding, vnode, prevVnode) { 326 | console.log(el, binding, vnode, prevVnode) 327 | }, 328 | mounted() { console.log('mounted') }, 329 | beforeUpdate() { console.log('beforeUpdate') }, 330 | updated() { console.log('updated') }, 331 | beforeUnmount() { console.log('beforeUnmount') }, 332 | unmounted() { console.log('unmounted') } 333 | } 334 | 335 | 336 | ``` 337 | > 全局注册指令 338 | ```js 339 | const app = createApp(App) 340 | app.use(router) 341 | app.use(store) 342 | app.directive('custom', customDirective) 343 | app.mount('#app') 344 | 345 | ``` 346 | 347 | > 组件内使用指令 348 | ```js 349 | import { customDirective } from '../../directive/directive' 350 | export default { 351 | setup() { 352 | return {} 353 | }, 354 | directives: { 355 | custom: customDirective 356 | } 357 | } 358 | ``` 359 | 360 | 10. nextTick 361 | ```js 362 | import { nextTick, onBeforeMount } from 'vue' 363 | 364 | { 365 | setup () { 366 | 367 | onBeforeMount(() => { 368 | nextTick(() => { 369 | 370 | }) 371 | }) 372 | 373 | } 374 | } 375 | ``` 376 | 377 | 11. 定义组件defineAsyncComponent & defineComponent 378 | 379 | > 同步组件与异步组件的区别: 380 | > 同步组件:在组件加载时自动加载; 381 | > 异步组件:在渲染时加载; 382 | 383 | ```js 384 | // 一、定义同步组件 385 | const syncComponent = defineComponent({ 386 | setup () { 387 | return () => `我是同步组件` 388 | } 389 | }) 390 | 391 | 392 | // 二、定义异步组件 393 | // 方式1 394 | const asyncComponent = defineAsyncComponent({ 395 | loader: () => import("./asyncComponents.vue"), 396 | loadingComponent: loadingComponent, 397 | errorComponent: loadingComponent, 398 | delay: 2000, 399 | timeout: 3000 400 | }); 401 | // 方式2 402 | const asyncComponent = defineAsyncComponent(() => import('./syncComponents.vue')); 403 | ``` 404 | -------------------------------------------------------------------------------- /src/assets/iconfont/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /src/assets/iconfont/demo_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IconFont Demo 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 | 29 |
30 |
31 |
    32 | 33 |
  • 34 | 35 |
    下拉
    36 |
    &#xe629;
    37 |
  • 38 | 39 |
  • 40 | 41 |
    加载
    42 |
    &#xe62b;
    43 |
  • 44 | 45 |
  • 46 | 47 |
    48 |
    &#xe613;
    49 |
  • 50 | 51 |
  • 52 | 53 |
    首页
    54 |
    &#xe637;
    55 |
  • 56 | 57 |
  • 58 | 59 |
    用户
    60 |
    &#xe634;
    61 |
  • 62 | 63 |
  • 64 | 65 |
    通知
    66 |
    &#xe61e;
    67 |
  • 68 | 69 |
  • 70 | 71 |
    皇冠
    72 |
    &#xe798;
    73 |
  • 74 | 75 |
  • 76 | 77 |
    皇冠
    78 |
    &#xe870;
    79 |
  • 80 | 81 |
  • 82 | 83 |
    加号
    84 |
    &#xe640;
    85 |
  • 86 | 87 |
  • 88 | 89 |
    皇冠
    90 |
    &#xe603;
    91 |
  • 92 | 93 |
  • 94 | 95 |
    user
    96 |
    &#xe7ae;
    97 |
  • 98 | 99 |
  • 100 | 101 |
    bell
    102 |
    &#xe7c4;
    103 |
  • 104 | 105 |
  • 106 | 107 |
    home
    108 |
    &#xe7c6;
    109 |
  • 110 | 111 |
  • 112 | 113 |
    carry out
    114 |
    &#xe7d3;
    115 |
  • 116 | 117 |
  • 118 | 119 |
    放大镜
    120 |
    &#xe60c;
    121 |
  • 122 | 123 |
  • 124 | 125 |
    直播
    126 |
    &#xe78b;
    127 |
  • 128 | 129 |
130 |
131 |

Unicode 引用

132 |
133 | 134 |

Unicode 是字体在网页端最原始的应用方式,特点是:

135 |
    136 |
  • 兼容性最好,支持 IE6+,及所有现代浏览器。
  • 137 |
  • 支持按字体的方式去动态调整图标大小,颜色等等。
  • 138 |
  • 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
  • 139 |
140 |
141 |

注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式

142 |
143 |

Unicode 使用步骤如下:

144 |

第一步:拷贝项目下面生成的 @font-face

145 |
@font-face {
147 |   font-family: 'iconfont';
148 |   src: url('iconfont.eot');
149 |   src: url('iconfont.eot?#iefix') format('embedded-opentype'),
150 |       url('iconfont.woff2') format('woff2'),
151 |       url('iconfont.woff') format('woff'),
152 |       url('iconfont.ttf') format('truetype'),
153 |       url('iconfont.svg#iconfont') format('svg');
154 | }
155 | 
156 |

第二步:定义使用 iconfont 的样式

157 |
.iconfont {
159 |   font-family: "iconfont" !important;
160 |   font-size: 16px;
161 |   font-style: normal;
162 |   -webkit-font-smoothing: antialiased;
163 |   -moz-osx-font-smoothing: grayscale;
164 | }
165 | 
166 |

第三步:挑选相应图标并获取字体编码,应用于页面

167 |
168 | <span class="iconfont">&#x33;</span>
170 | 
171 |
172 |

"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

173 |
174 |
175 |
176 |
177 |
    178 | 179 |
  • 180 | 181 |
    182 | 下拉 183 |
    184 |
    .iconxlsx 185 |
    186 |
  • 187 | 188 |
  • 189 | 190 |
    191 | 加载 192 |
    193 |
    .jiazai 194 |
    195 |
  • 196 | 197 |
  • 198 | 199 |
    200 | 对 201 |
    202 |
    .dui 203 |
    204 |
  • 205 | 206 |
  • 207 | 208 |
    209 | 首页 210 |
    211 |
    .shouye 212 |
    213 |
  • 214 | 215 |
  • 216 | 217 |
    218 | 用户 219 |
    220 |
    .yonghu 221 |
    222 |
  • 223 | 224 |
  • 225 | 226 |
    227 | 通知 228 |
    229 |
    .tongzhi 230 |
    231 |
  • 232 | 233 |
  • 234 | 235 |
    236 | 皇冠 237 |
    238 |
    .huangguan1 239 |
    240 |
  • 241 | 242 |
  • 243 | 244 |
    245 | 皇冠 246 |
    247 |
    .huangguan2 248 |
    249 |
  • 250 | 251 |
  • 252 | 253 |
    254 | 加号 255 |
    256 |
    .jiahao 257 |
    258 |
  • 259 | 260 |
  • 261 | 262 |
    263 | 皇冠 264 |
    265 |
    .huangguan 266 |
    267 |
  • 268 | 269 |
  • 270 | 271 |
    272 | user 273 |
    274 |
    .user 275 |
    276 |
  • 277 | 278 |
  • 279 | 280 |
    281 | bell 282 |
    283 |
    .bell 284 |
    285 |
  • 286 | 287 |
  • 288 | 289 |
    290 | home 291 |
    292 |
    .home 293 |
    294 |
  • 295 | 296 |
  • 297 | 298 |
    299 | carry out 300 |
    301 |
    .carryout 302 |
    303 |
  • 304 | 305 |
  • 306 | 307 |
    308 | 放大镜 309 |
    310 |
    .fangdajing 311 |
    312 |
  • 313 | 314 |
  • 315 | 316 |
    317 | 直播 318 |
    319 |
    .zhibo 320 |
    321 |
  • 322 | 323 |
324 |
325 |

font-class 引用

326 |
327 | 328 |

font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。

329 |

与 Unicode 使用方式相比,具有如下特点:

330 |
    331 |
  • 兼容性良好,支持 IE8+,及所有现代浏览器。
  • 332 |
  • 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
  • 333 |
  • 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
  • 334 |
  • 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
  • 335 |
336 |

使用步骤如下:

337 |

第一步:引入项目下面生成的 fontclass 代码:

338 |
<link rel="stylesheet" href="./iconfont.css">
339 | 
340 |

第二步:挑选相应图标并获取类名,应用于页面:

341 |
<span class="iconfont xxx"></span>
342 | 
343 |
344 |

" 345 | iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。

346 |
347 |
348 |
349 |
350 |
    351 | 352 |
  • 353 | 356 |
    下拉
    357 |
    #iconxlsx
    358 |
  • 359 | 360 |
  • 361 | 364 |
    加载
    365 |
    #jiazai
    366 |
  • 367 | 368 |
  • 369 | 372 |
    373 |
    #dui
    374 |
  • 375 | 376 |
  • 377 | 380 |
    首页
    381 |
    #shouye
    382 |
  • 383 | 384 |
  • 385 | 388 |
    用户
    389 |
    #yonghu
    390 |
  • 391 | 392 |
  • 393 | 396 |
    通知
    397 |
    #tongzhi
    398 |
  • 399 | 400 |
  • 401 | 404 |
    皇冠
    405 |
    #huangguan1
    406 |
  • 407 | 408 |
  • 409 | 412 |
    皇冠
    413 |
    #huangguan2
    414 |
  • 415 | 416 |
  • 417 | 420 |
    加号
    421 |
    #jiahao
    422 |
  • 423 | 424 |
  • 425 | 428 |
    皇冠
    429 |
    #huangguan
    430 |
  • 431 | 432 |
  • 433 | 436 |
    user
    437 |
    #user
    438 |
  • 439 | 440 |
  • 441 | 444 |
    bell
    445 |
    #bell
    446 |
  • 447 | 448 |
  • 449 | 452 |
    home
    453 |
    #home
    454 |
  • 455 | 456 |
  • 457 | 460 |
    carry out
    461 |
    #carryout
    462 |
  • 463 | 464 |
  • 465 | 468 |
    放大镜
    469 |
    #fangdajing
    470 |
  • 471 | 472 |
  • 473 | 476 |
    直播
    477 |
    #zhibo
    478 |
  • 479 | 480 |
481 |
482 |

Symbol 引用

483 |
484 | 485 |

这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章 486 | 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:

487 |
    488 |
  • 支持多色图标了,不再受单色限制。
  • 489 |
  • 通过一些技巧,支持像字体那样,通过 font-size, color 来调整样式。
  • 490 |
  • 兼容性较差,支持 IE9+,及现代浏览器。
  • 491 |
  • 浏览器渲染 SVG 的性能一般,还不如 png。
  • 492 |
493 |

使用步骤如下:

494 |

第一步:引入项目下面生成的 symbol 代码:

495 |
<script src="./iconfont.js"></script>
496 | 
497 |

第二步:加入通用 CSS 代码(引入一次就行):

498 |
<style>
499 | .icon {
500 |   width: 1em;
501 |   height: 1em;
502 |   vertical-align: -0.15em;
503 |   fill: currentColor;
504 |   overflow: hidden;
505 | }
506 | </style>
507 | 
508 |

第三步:挑选相应图标并获取类名,应用于页面:

509 |
<svg class="icon" aria-hidden="true">
510 |   <use xlink:href="#icon-xxx"></use>
511 | </svg>
512 | 
513 |
514 |
515 | 516 |
517 |
518 | 537 | 538 | 539 | --------------------------------------------------------------------------------