├── .env.development ├── .env.production ├── .gitignore ├── LICENSE ├── README.md ├── config ├── base.conf.js ├── dev.conf.js ├── index.js └── prod.conf.js ├── index.html ├── package.json ├── pictures ├── 1.png ├── 2.png ├── 3.png ├── 4.png └── 5.png ├── public └── favicon.ico ├── server ├── app.js ├── log.js ├── router │ ├── about.js │ ├── data.js │ ├── home.js │ ├── index.js │ └── user.js └── utils │ └── index.js ├── src ├── App.vue ├── api │ ├── about.ts │ ├── data.ts │ ├── home.ts │ └── login.ts ├── assets │ ├── css │ │ ├── base.css │ │ ├── global.scss │ │ └── variables.scss │ ├── iconfont │ │ ├── demo.css │ │ ├── demo_index.html │ │ ├── iconfont.css │ │ ├── iconfont.eot │ │ ├── iconfont.js │ │ ├── iconfont.json │ │ ├── iconfont.svg │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ ├── images │ │ ├── bg.jpeg │ │ └── profile.jpg │ └── logo.png ├── components │ ├── au-header.vue │ ├── au-input.vue │ ├── au-layout-empty.vue │ ├── au-layout.vue │ ├── au-nav.vue │ ├── au-pagination.ts │ └── au-screen.ts ├── icons │ └── svg │ │ ├── g1.svg │ │ ├── g2.svg │ │ ├── g3.svg │ │ ├── g4.svg │ │ ├── g5.svg │ │ ├── g6.svg │ │ ├── g7.svg │ │ ├── v1.svg │ │ ├── v2.svg │ │ ├── v3.svg │ │ ├── v4.svg │ │ ├── v5.svg │ │ ├── v6.svg │ │ ├── v7.svg │ │ ├── v8.svg │ │ └── v9.svg ├── main.ts ├── plugins │ ├── axios.ts │ ├── element-ui.ts │ ├── index.ts │ ├── router.ts │ └── store.ts ├── router │ ├── index.ts │ └── permision.ts ├── shims-vue.d.ts ├── store │ ├── actions.ts │ ├── getters.ts │ ├── index.ts │ ├── mutations.ts │ ├── state.ts │ └── types.ts ├── utils │ ├── _ls.ts │ ├── _sign.ts │ └── index.ts └── views │ ├── about │ ├── au-message.vue │ ├── au-produce.vue │ ├── au-record.vue │ └── index.vue │ ├── data │ ├── au-list.vue │ ├── au-rank.vue │ ├── chart-config.js │ ├── index.vue │ └── rank.vue │ ├── error │ └── 404.vue │ ├── home │ ├── index.vue │ └── notice.vue │ ├── login │ └── index.vue │ └── profile │ ├── au-profile-mobile.vue │ ├── au-profile-pc.vue │ ├── au-upload.ts │ ├── config.js │ └── index.vue └── tsconfig.json /.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VITE_BASE_URL=/au/api -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VITE_BASE_URL=http://47.243.59.143:8089/api/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | package-lock.json 7 | /server/log -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 coderly 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Typescript + Vite + Vuex + Vue-Router 2 | 3 | This template should help get you started developing with Vue 3 and Typescript and Vuex and Vue-Router in Vite. 4 | 5 | 6 | # Introduction ⚡ 7 | 8 | > all about 9 | 10 | - 💡 Vue 3.x 11 | - ⚡️ Typescript 12 | - 🛠️ Vite 2.x 13 | - 📦 Vuex 4.x 14 | - 🔩 Vue-Router 4.x 15 | - 🔑 Md5 And Sign Provide Transmission For Website Security 16 | - 👍 Mock In Development 17 | - 🔖 Iconfont And Svg 18 | - 🔍 Element-plus UI 19 | - 📌 And More... 20 | 21 | ## Some pages 22 | ![wl-mfe](https://github.com/coderlyu/vite-frontend-template/blob/master/pictures/1.png) 23 | ![wl-mfe](https://github.com/coderlyu/vite-frontend-template/blob/master/pictures/2.png) 24 | ![wl-mfe](https://github.com/coderlyu/vite-frontend-template/blob/master/pictures/3.png) 25 | ![wl-mfe](https://github.com/coderlyu/vite-frontend-template/blob/master/pictures/4.png) 26 | ![wl-mfe](https://github.com/coderlyu/vite-frontend-template/blob/master/pictures/5.png) 27 | 28 | # Start 29 | 1. `npm install` 30 | 2. `npm run dev` -------------------------------------------------------------------------------- /config/base.conf.js: -------------------------------------------------------------------------------- 1 | 2 | import vue from '@vitejs/plugin-vue' 3 | import svgLoader from 'vite-svg-loader' 4 | import styleImport from 'vite-plugin-style-import' 5 | const path = require('path') 6 | 7 | export default { 8 | root: process.cwd(), 9 | publicDir: 'public', 10 | logLevel: 'error', 11 | envDir: process.cwd(), 12 | resolve: { 13 | alias: { 14 | '@': path.resolve(process.cwd(), 'src'), 15 | 'views': path.resolve(process.cwd(), 'src/views'), 16 | } 17 | }, 18 | css: { 19 | postcss: { 20 | plugins: [require('autoprefixer')] 21 | }, 22 | // 引入全局 scss 23 | preprocessorOptions: { 24 | scss: { 25 | additionalData: "@import './src/assets/css/global.scss';" 26 | } 27 | } 28 | }, 29 | plugins: [ 30 | vue(), 31 | styleImport({ 32 | libs: [{ 33 | libraryName: 'element-plus', 34 | esModule: true, 35 | ensureStyleFile: true, 36 | resolveStyle: (name) => { 37 | return `element-plus/lib/theme-chalk/${name}.css` 38 | }, 39 | resolveComponent: (name) => { 40 | return `element-plus/lib/${name}`; 41 | } 42 | }] 43 | }), 44 | svgLoader() 45 | ] 46 | } -------------------------------------------------------------------------------- /config/dev.conf.js: -------------------------------------------------------------------------------- 1 | 2 | import { merge } from 'lodash' 3 | import baseConfig from './base.conf.js' 4 | 5 | const devConfig = { 6 | mode: 'development', 7 | logLevel: 'error', 8 | clearScreen: false, // 不会清空上一次控制台打印信息 9 | server: { 10 | host: '127.0.0.1', 11 | open: true, 12 | port: 3030, 13 | strictPort: true, // 若端口已被占用则会直接退出,而不是尝试下一个可用端口 14 | force: true, // 强制使依赖预构建 15 | proxy: { 16 | '/au': { 17 | target: 'http://localhost:8089', 18 | changeOrigin: true, 19 | rewrite: (path) => path.replace(/^\/au/, '') 20 | } 21 | } 22 | } 23 | } 24 | export default function devConfigFunc() { 25 | return merge(devConfig, baseConfig) 26 | } -------------------------------------------------------------------------------- /config/index.js: -------------------------------------------------------------------------------- 1 | import dev from './dev.conf.js' 2 | import prod from './prod.conf.js' 3 | export default (({ mode }) => { 4 | if (mode === 'development') { 5 | return dev() 6 | } else { 7 | console.info('当前模式为:', mode) 8 | return prod() 9 | } 10 | }) -------------------------------------------------------------------------------- /config/prod.conf.js: -------------------------------------------------------------------------------- 1 | import baseConfig from './base.conf.js' 2 | import { merge } from 'lodash' 3 | const packJson = require('../package.json') 4 | const execSync = require('child_process').execSync // 同步子进程 5 | let date = new Date(execSync('git show -s --format=%cd').toString()) 6 | let ref = execSync('git show -s --format=%D').toString().trim() 7 | let hash = execSync('git show -s --format=%H').toString().trim() 8 | 9 | /** 格式化 - 日期 */ 10 | function formatDate(t) { 11 | let year = t.getFullYear() 12 | let month = t.getMonth() 13 | let day = t.getDate() 14 | let h = t.getHours() 15 | let m = t.getMinutes() 16 | let s = t.getSeconds() 17 | month = month < 10 ? '0' + month : month 18 | day = day < 10 ? '0' + day : day 19 | h = h < 10 ? '0' + h : h 20 | m = m < 10 ? '0' + m : m 21 | s = s < 10 ? '0' + s : s 22 | return `${year}-${month}-${day} ${h}:${m}:${s}` 23 | } 24 | /** 格式化 - 当前分支信息 */ 25 | function currentRef(str) { 26 | let fs = str.split(',') 27 | let s = fs.filter((s) => s.indexOf('->') !== -1)[0] 28 | return s.slice(s.indexOf('->') + 2) 29 | } 30 | 31 | let messages = '/*' + Buffer.from(`ref: ${currentRef(ref)}, date: ${formatDate(date)}, hash: ${hash}, project: -name- ${packJson.name} -v- ${packJson.version}`).toString('base64') + '*/' 32 | 33 | const prodConfig = { 34 | mode: 'production', 35 | logLevel: 'info', 36 | build: { 37 | target: 'modules', 38 | polyfillDynamicImport: true, // 自动注入 动态导入 polyfill 39 | outDir: 'dist', 40 | assetsDir: 'assets', 41 | assetsInlineLimit: 8192, // 小于 8kb 的导入或引用资源将内联为 base64 编码 42 | cssCodeSplit: false, // 在异步 chunk 中导入的 CSS 将内联到异步 chunk 本身,并在其被加载时插入 43 | sourcemap: false, // 构建后是否生成 source map 文件 44 | rollupOptions: { 45 | output: { 46 | banner: messages, 47 | manualChunks: { 48 | lodash: ['lodash'], 49 | echarts: ['echarts'], 50 | // axios: ['axios'], 51 | // nprogress: ['nprogress'], 52 | // vue: ['vue'], 53 | 'element-plus': ['element-plus'] 54 | } 55 | } 56 | }, 57 | minify: 'terser', 58 | chunkSizeWarningLimit: 800, // chunk 大小警告的限制 59 | } 60 | } 61 | 62 | export default function prodConfigFunc() { 63 | return merge(prodConfig, baseConfig) 64 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 13 | 14 | 15 | 后台管理模板 16 | 17 | 18 |
19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-frontend-template", 3 | "version": "1.0.0", 4 | "keywords": [ 5 | "vue", 6 | "vue3.x", 7 | "vite", 8 | "vite2.x", 9 | "typescript", 10 | "element-plus", 11 | "vuex", 12 | "vuex4.x", 13 | "vue-router", 14 | "vue-router4.x", 15 | "md5", 16 | "axios", 17 | "echarts", 18 | "svg", 19 | "template", 20 | "front-end", 21 | "vue模板", 22 | "admin-template", 23 | "后台管理模板", 24 | "管理" 25 | ], 26 | "description": "a front end template development by vue3.x and typescript and vite2.x and vuex4.x and vue-router 4.x and mock and element-plus", 27 | "main": "./src/main.ts", 28 | "license": "MIT", 29 | "author": { 30 | "name": "刘誉", 31 | "email": "974257574@qq.com" 32 | }, 33 | "scripts": { 34 | "dev": "concurrently \"npm run dev:e \" \"npm run dev:f\"", 35 | "dev:f": "vite --mode development --config ./config/index.js", 36 | "dev:e": "node ./server/app.js", 37 | "build": "vite build --mode production --config ./config/index.js", 38 | "serve": "vite preview" 39 | }, 40 | "dependencies": { 41 | "axios": "^0.21.1", 42 | "concurrently": "^6.2.0", 43 | "echarts": "^5.0.2", 44 | "element-plus": "^1.0.2-beta.32", 45 | "koa": "^2.13.4", 46 | "koa-body": "^5.0.0", 47 | "koa-router": "^10.1.1", 48 | "koa2-cors": "^2.0.6", 49 | "lodash": "^4.17.21", 50 | "log4js": "^6.4.6", 51 | "mockjs": "^1.1.0", 52 | "nprogress": "^0.2.0", 53 | "qs": "^6.10.1", 54 | "vite-plugin-mockit": "^1.0.1", 55 | "vue": "^3.0.5", 56 | "vue-router": "^4.0.4", 57 | "vuex": "^4.0.0" 58 | }, 59 | "devDependencies": { 60 | "@types/js-md5": "^0.4.2", 61 | "@types/node": "^14.14.31", 62 | "@types/nprogress": "^0.2.0", 63 | "@types/qs": "^6.9.5", 64 | "@vitejs/plugin-vue": "^1.1.4", 65 | "@vue/compiler-sfc": "^3.0.5", 66 | "autoprefixer": "^10.2.6", 67 | "js-md5": "^0.7.3", 68 | "less": "^4.1.1", 69 | "nodemon": "^2.0.16", 70 | "postcss": "^8.3.0", 71 | "sass": "^1.32.8", 72 | "typescript": "^4.1.3", 73 | "vite": "^2.0.1", 74 | "vite-plugin-style-import": "^0.7.3", 75 | "vite-svg-loader": "^1.4.2" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /pictures/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/pictures/1.png -------------------------------------------------------------------------------- /pictures/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/pictures/2.png -------------------------------------------------------------------------------- /pictures/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/pictures/3.png -------------------------------------------------------------------------------- /pictures/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/pictures/4.png -------------------------------------------------------------------------------- /pictures/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/pictures/5.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/public/favicon.ico -------------------------------------------------------------------------------- /server/app.js: -------------------------------------------------------------------------------- 1 | const Koa = require('koa') 2 | const cors = require('koa2-cors') 3 | const app = new Koa() 4 | 5 | app.use(cors()) 6 | require('./router/index.js')(app) 7 | 8 | app.listen(8089, (error) => { 9 | if (!error) { 10 | console.log('server is running at http://localhost:8089') 11 | } 12 | }) -------------------------------------------------------------------------------- /server/log.js: -------------------------------------------------------------------------------- 1 | const log4js = require('log4js') 2 | const path = require('path') 3 | 4 | const config = { 5 | appenders: { 6 | default: { 7 | type: 'dateFile', 8 | keepFileExt: true, 9 | filename: path.resolve(__dirname, './log/default.log') 10 | }, 11 | koaRoute: { 12 | type: 'dateFile', 13 | keepFileExt: true, 14 | filename: path.resolve(__dirname, './log/koaRoute.log') 15 | }, 16 | success: { 17 | type: 'dateFile', 18 | keepFileExt: true, 19 | filename: path.resolve(__dirname, './log/success.log') 20 | } 21 | }, 22 | categories: { 23 | default: { appenders: ['default'], level: 'debug' }, 24 | koaRoute: { appenders: ['koaRoute'], level: 'info' }, 25 | success: { appenders: ['success'], level: 'all' } 26 | } 27 | } 28 | 29 | log4js.configure(config) 30 | const koaLogger = log4js.getLogger('koaRoute') 31 | const logger = log4js.getLogger() 32 | const successLogger = log4js.getLogger('success') 33 | 34 | module.exports = { 35 | koaLogger, 36 | logger, 37 | successLogger 38 | } -------------------------------------------------------------------------------- /server/router/about.js: -------------------------------------------------------------------------------- 1 | const mockjs = require('mockjs') 2 | const utils = require('../utils/index.js') 3 | 4 | async function actives(ctx) { 5 | const { limit = 10, page = 1 } = ctx.request.query 6 | const data = mockjs.mock({ 7 | [`data|${limit}`]: [ 8 | { 9 | "id|+1": 1, 10 | "imgUrl|1": [ 11 | "https://pic3.zhimg.com/v2-03371b64a1da395c519938e65965ff61.jpg?source=8c23436a", 12 | "https://pic4.zhimg.com/v2-f93156f6bd568c80d5e2d54833286550.jpg?source=8c23436a", 13 | "https://pic2.zhimg.com/v2-37852e07af93ea1675d8cd50a699f4ff.jpg?source=8c23436a", 14 | "https://pic4.zhimg.com/v2-400ebc21c25b0b7bb0740e6e25a7b1c5.jpg?source=8c23436a", 15 | "https://pic2.zhimg.com/v2-ce896400cea15a93a05bc6b0188b668c.jpg?source=8c23436a", 16 | "https://pic1.zhimg.com/v2-456a0f551ce75a8d5adab52e6413ab1d.jpg?source=8c23436a", 17 | "https://pic4.zhimg.com/v2-a73ecc1ba6adedb07c21b85937560192.jpg?source=8c23436a", 18 | ], 19 | "url": "", 20 | "time": () =>`${mockjs.Random.date('yyyy 年 MM 月 dd 日')} - ${mockjs.Random.date('yyyy 年 MM 月 dd 日')}` 21 | } 22 | ] 23 | }) 24 | utils.errorHandle(ctx, 200, '获取公告列表成功', data.data, { total: 88 }) 25 | } 26 | 27 | async function messages(ctx) { 28 | const { limit = 10, page = 1 } = ctx.request.query 29 | const data = mockjs.mock({ 30 | [`data|${limit}`]: [ 31 | { 32 | "id|+1": 1, 33 | "label|1": ["【系统通知】", '', '【自动触发】', '【科技】', '【新闻】'], 34 | "title|1": ["该系统将于今晚凌晨2点到5点进行升级维护", '好友给你发来一条元宵祝福', '你的文章被评为年度优秀作品', 'xx炮轰安卓高抽成,称其对产业生态非常不利', '旧代码获新生!AI 重写的应用程序可用于最新计算平台', '今天你打到车了吗?滴滴突然崩了,司机接不到人、乘客付不了钱', '技术写作技巧分享:我是如何从写作小白成长为多平台优秀作者的?'], 35 | "time": () => mockjs.Random.date('yyyy-MM-dd HH:mm:ss') 36 | } 37 | ] 38 | }) 39 | utils.errorHandle(ctx, 200, '获取列表成功', data.data, { total: 120 }) 40 | } 41 | 42 | async function records(ctx) { 43 | const { limit = 10, page = 1 } = ctx.request.query 44 | const data = mockjs.mock({ 45 | [`data|${limit}`]: [ 46 | { 47 | "id|+1": 1, 48 | "date": () => mockjs.Random.date('yyyy/MM/dd'), 49 | "time": () => mockjs.Random.date('HH:mm:ss'), 50 | "author|1": ["coderly", '立方体转移工程师', '空间物质转移监督员', '黑白黄的猫'], 51 | "operation|1": ["修改密码", '添加系统管理员', '发布作品', '删除管理员'] 52 | } 53 | ] 54 | }) 55 | utils.errorHandle(ctx, 200, '获取列表成功', data.data, { total: 120 }) 56 | } 57 | 58 | module.exports = { 59 | actives, 60 | messages, 61 | records 62 | } -------------------------------------------------------------------------------- /server/router/data.js: -------------------------------------------------------------------------------- 1 | const mockjs = require('mockjs') 2 | const utils = require('../utils/index.js') 3 | 4 | async function list(ctx) { 5 | const { limit = 10, page = 1 } = ctx.request.body 6 | 7 | const data = mockjs.mock({ 8 | [`data|${limit}`]: [ 9 | { 10 | 'id|+1': 1, 11 | 'name|1': ['品牌任务', '最佳推手', '每日上新', '每日一问'], 12 | 'title|1': ['帖子-流行指南', '帖子-合理熬夜', '热搜-如何写出别人无法维护的代码', '问题-优秀的人自恋程度', '帖子-合格韭菜的自我修养'], 13 | 'imgUrl': 'https://img.alicdn.com/tfs/TB1sbkkXmBYBeNjy0FeXXbnmFXa-280-498.png', 14 | 'content': '分享日常的真人穿搭或专业的教程,对时尚有自己的理解,能够给消费者提供时尚搭配心得', 15 | 'link|1': ['好的长文章应该怎么写?', '怎么能让别人注意到这里?'] 16 | } 17 | ] 18 | }) 19 | utils.errorHandle(ctx, 200, '登录成功', data.data, { total: 68 }) 20 | } 21 | 22 | async function news(ctx) { 23 | const { limit = 10, page = 1 } = ctx.request.body 24 | const data = mockjs.mock({ 25 | [`data|${limit}`]: [ 26 | { 27 | 'id|+1': 1, 28 | 'title|1': ['秋季外套', '冬季毛衣', '牛仔裤', '今年流行的毛衣短款', '早春人气爆棚卫衣', '春季宝藏新品外套', '学生党开学必备连衣裙', '不沾杯口红拒绝尴尬', '干饭人必学偷懒菜', '驱魔司带你一起梦长安'], 29 | 'num': () => mockjs.Random.natural(1,1000) 30 | } 31 | ] 32 | }) 33 | utils.errorHandle(ctx, 200, '登录成功', data.data) 34 | } 35 | 36 | module.exports = { 37 | list, 38 | news 39 | } -------------------------------------------------------------------------------- /server/router/home.js: -------------------------------------------------------------------------------- 1 | const mockjs = require('mockjs') 2 | const utils = require('../utils/index.js') 3 | 4 | async function levels(ctx) { 5 | utils.errorHandle(ctx, 200, '获取用户等级成功', mockjs.Random.natural(1, 5)) 6 | } 7 | 8 | async function counts(ctx) { 9 | const data = mockjs.mock([ 10 | { 11 | "id": 1, 12 | "title": "昨日阅读总数", 13 | "icon": "iconyinzhangrenzheng", 14 | "num": mockjs.Random.natural(1,1000) 15 | }, 16 | { 17 | "id": 2, 18 | "title": "昨日赞同总数", 19 | "icon": "iconhaxi", 20 | "num": mockjs.Random.natural(1,1000) 21 | }, 22 | { 23 | "id": 3, 24 | "title": "昨日粉丝数", 25 | "icon": "iconliuzhuan", 26 | "num": mockjs.Random.natural(1,1000) 27 | }, 28 | { 29 | "id": 4, 30 | "title": "昨日活跃粉丝数", 31 | "icon": "iconqukuai", 32 | "num": mockjs.Random.natural(1,1000) 33 | }, 34 | { 35 | "id": 5, 36 | "title": "内容健康度", 37 | "icon": "iconsuyuan", 38 | "num": "90分" 39 | }, 40 | { 41 | "id": 6, 42 | "title": "昨日收益", 43 | "icon": "iconshujuku", 44 | "num": "92,340.00" 45 | } 46 | ]) 47 | utils.errorHandle(ctx, 200, '获取统计信息成功', data) 48 | } 49 | 50 | async function notices(ctx) { 51 | const { limit = 10, page = 1 } = ctx.request.query 52 | const data = mockjs.mock({ 53 | [`data|${limit}`]: [ 54 | { 55 | "id|+1": 1, 56 | "tags": () => [ 57 | { 58 | "value": 1, 59 | "label": "HOT" 60 | }, 61 | { 62 | "value": 2, 63 | "label": "TOP" 64 | }, 65 | { 66 | "value": 3, 67 | "label": "NEW" 68 | }, 69 | { 70 | "value": 4, 71 | "label": "置顶" 72 | }, 73 | { 74 | "value": 5, 75 | "label": "HOT" 76 | } 77 | ].splice(mockjs.Random.natural(0,4), mockjs.Random.natural(0,2)), 78 | "content|1": ["【活动入口已开启】元宵猜灯谜、战胜小龙人一系列活动来袭,快来体验下", '【创作大赛】创作大赛欢迎你的加入', '【征文】发表文章即可'], 79 | "time": () => mockjs.Random.date('yyyy/MM/dd') 80 | } 81 | ] 82 | }) 83 | utils.errorHandle(ctx, 200, '获取公告列表成功', data.data, { total: 203 }) 84 | } 85 | 86 | module.exports = { 87 | levels, 88 | counts, 89 | notices 90 | } -------------------------------------------------------------------------------- /server/router/index.js: -------------------------------------------------------------------------------- 1 | const KoaRouter = require('koa-router') 2 | const KoaBody = require('koa-body') // post解析参数并支持附件上传 3 | 4 | const user = require('./user.js') 5 | const data = require('./data.js') 6 | const home = require('./home.js') 7 | const about = require('./about.js') 8 | const router = new KoaRouter() 9 | 10 | /** user */ 11 | router.get('/api/user/info', user.info) 12 | .post('/api/user/login', user.login) 13 | 14 | /** data */ 15 | 16 | router.get('/api/data/list', data.list) 17 | .get('/api/data/news', data.news) 18 | 19 | /** home */ 20 | 21 | router.get('/api/home/levels', home.levels) 22 | .get('/api/home/counts', home.counts) 23 | .get('/api/home/notices', home.notices) 24 | 25 | /** about */ 26 | 27 | router.get('/api/about/actives', about.actives) 28 | .get('/api/about/messages', about.messages) 29 | .get('/api/about/records', about.records) 30 | 31 | module.exports = function useRoutes(app) { 32 | app.use(KoaBody({ 33 | multipart: true, // 支持文件上传 34 | })) 35 | app.use(router.routes()) 36 | app.use(router.allowedMethods()) 37 | } -------------------------------------------------------------------------------- /server/router/user.js: -------------------------------------------------------------------------------- 1 | const mockjs = require('mockjs') 2 | const md5 = require('js-md5') 3 | const utils = require('../utils/index.js') 4 | 5 | async function login(ctx) { 6 | const { username = '', password = '', sign = '' } = ctx.request.body 7 | let body = utils.cloneData(ctx.request.body) 8 | delete body.sign 9 | const sign_code = utils.sign(body) 10 | if (username === 'admin' && password === md5('admin') && sign_code === sign) { 11 | utils.errorHandle(ctx, 200, '登录成功', '', { token: md5('au真是个天才,哈哈哈') }) 12 | return 13 | } 14 | utils.errorHandle(ctx, 30000, '用户信息有误') 15 | } 16 | 17 | async function info(ctx) { 18 | utils.errorHandle(ctx, 200, '获取用户信息成功', mockjs.mock({ username: mockjs.Random.name(), 'sex|1': ['男', '女', '未知'] })) 19 | } 20 | 21 | module.exports = { 22 | login, 23 | info 24 | } -------------------------------------------------------------------------------- /server/utils/index.js: -------------------------------------------------------------------------------- 1 | const md5 = require('js-md5') 2 | const querystring = require('querystring') 3 | const loggers = require('../log.js') 4 | const { koaLogger } = loggers 5 | /** 6 | * 7 | * @param {加盐数据} data 8 | * @returns 9 | */ 10 | function sign(data) { 11 | var newkey = Object.keys(data).sort() 12 | var newObj = {} 13 | for (var i = 0; i < newkey.length; i++) { 14 | newObj[newkey[i]] = data[newkey[i]] 15 | } 16 | let signature = 'au' 17 | for (var j = 0; j < newkey.length; j++) { 18 | signature += newObj[newkey[j]] 19 | } 20 | var date = new Date(+new Date() + 8 * 3600 * 1000).toISOString().replace(/T/g, '').replace(/\.[\d]{3}Z/, '') 21 | signature += date.substring(0, 10) 22 | signature += 'au' 23 | signature = md5(signature.toUpperCase()) 24 | return signature 25 | } 26 | 27 | function errorHandle(ctx, code, message = '', data = '', options = {}) { 28 | if (code === 200) { 29 | ctx.body = { 30 | error_code: code, 31 | data, 32 | message, 33 | ...options 34 | }; 35 | } else if (message && message.message) { 36 | ctx.body = { 37 | error_code: code, 38 | data, 39 | message: message.message, 40 | ...options 41 | }; 42 | } else { 43 | ctx.body = { 44 | error_code: code, 45 | data, 46 | message, 47 | ...options 48 | } 49 | } 50 | code != 200 && koaLogger.error(`错误码:${code},数据:${JSON.stringify(data)},错误信息:${JSON.stringify(message)}`) 51 | } 52 | 53 | function cloneData(data) { 54 | return JSON.parse(JSON.stringify(data)) 55 | } 56 | 57 | module.exports = { 58 | sign, 59 | errorHandle, 60 | cloneData 61 | } -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/api/about.ts: -------------------------------------------------------------------------------- 1 | import request from '../plugins/axios' 2 | 3 | export function getActiveList(params: unknown) { 4 | return request({ 5 | url: '/about/actives', 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | export function getMessageList(params: unknown) { 12 | return request({ 13 | url: '/about/messages', 14 | method: 'get', 15 | params 16 | }) 17 | } 18 | 19 | export function getRecordList(params: unknown) { 20 | return request({ 21 | url: '/about/records', 22 | method: 'get', 23 | params 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/api/data.ts: -------------------------------------------------------------------------------- 1 | import request from '../plugins/axios' 2 | 3 | export function getList(params: unknown) { 4 | return request({ 5 | url: '/data/list', 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | export function getNewList(params: unknown) { 12 | return request({ 13 | url: '/data/news', 14 | method: 'get', 15 | params 16 | }) 17 | } 18 | -------------------------------------------------------------------------------- /src/api/home.ts: -------------------------------------------------------------------------------- 1 | import request from '../plugins/axios' 2 | 3 | export function getNoticelist(params) { 4 | return request({ 5 | url: '/home/notices', 6 | method: 'get', 7 | params 8 | }) 9 | } 10 | 11 | export function getCountlist() { 12 | return request({ 13 | url: '/home/counts', 14 | method: 'get' 15 | }) 16 | } 17 | 18 | export function getCurrentLevel() { 19 | return request({ 20 | url: '/home/levels', 21 | method: 'get' 22 | }) 23 | } -------------------------------------------------------------------------------- /src/api/login.ts: -------------------------------------------------------------------------------- 1 | import request, { PromiseLoginName } from '../plugins/axios' 2 | import md5 from 'js-md5' 3 | export function login(data: { password: string, username: string }){ 4 | let password = md5(data.password) 5 | return request({ 6 | url: '/user/login', 7 | method: 'post', 8 | data: { 9 | ...data, 10 | password 11 | } 12 | }) 13 | } 14 | 15 | export function info() { 16 | return request({ 17 | url: '/user/info', 18 | method: 'get' 19 | }) 20 | } -------------------------------------------------------------------------------- /src/assets/css/base.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | background: #f9f9f9; 4 | overflow: hidden; /** 不让 html 和 body 滚动,让 #app 滚动,可以添加 element-plus 回到顶部功能 */ 5 | } 6 | * { 7 | padding: 0; 8 | margin: 0; 9 | box-sizing: border-box; 10 | } 11 | #app { 12 | height: 100%; 13 | overflow: auto; 14 | } 15 | 16 | ul { 17 | list-style-type: none; 18 | } -------------------------------------------------------------------------------- /src/assets/css/global.scss: -------------------------------------------------------------------------------- 1 | @import './variables.scss'; 2 | @mixin mixinBgColor { 3 | background-color: $--var-background-white-; 4 | } 5 | .au-bg-white { 6 | @include mixinBgColor; 7 | } 8 | .au-bg-grey { 9 | background-color: #f9f9f9 !important; 10 | } 11 | 12 | /** card */ 13 | .au-card { 14 | @include mixinBgColor; 15 | border-radius: 4px; 16 | border: 1px solid #ebeef5; 17 | overflow: hidden; 18 | color: #303133; 19 | transition: .3s; 20 | &:hover { 21 | box-shadow: 0 2px 12px 0 rgb(0 0 0 / 20%); 22 | } 23 | } 24 | .au-box-shadow { 25 | box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); 26 | } 27 | 28 | 29 | /** 单行省略 */ 30 | .au-text-line-one { 31 | overflow: hidden; 32 | text-overflow: ellipsis; 33 | white-space: nowrap; 34 | } 35 | .au-text-line-two { 36 | white-space: pre-wrap; 37 | overflow : hidden; 38 | text-overflow: ellipsis; 39 | display: -webkit-box; 40 | -webkit-line-clamp: 2; 41 | -webkit-box-orient: vertical; 42 | } 43 | 44 | 45 | /** svg style */ 46 | .au-icon { 47 | width: 20px; 48 | height: 20px; 49 | } 50 | 51 | 52 | /** elements arrangement */ 53 | .au-ml-8 { 54 | margin-left: 8px; 55 | } 56 | .au-mt-8 { 57 | margin-top: 8px; 58 | } 59 | .au-pd-8 { 60 | padding: 8px; 61 | } 62 | .au-mt-16 { 63 | margin-top: 16px; 64 | } 65 | .au-ml-16 { 66 | margin-left: 16px; 67 | } 68 | .au-mr-16 { 69 | margin-right: 16px; 70 | } 71 | .au-pd-16 { 72 | padding: 16px; 73 | } 74 | .au-mt-32 { 75 | margin-top: 32px; 76 | } 77 | .au-mr-32 { 78 | margin-right: 32px; 79 | } 80 | .au-pd-32 { 81 | padding: 32px; 82 | } 83 | 84 | /** text alignment direction */ 85 | .au-text-end { 86 | text-align: end; 87 | } 88 | .au-text-center { 89 | text-align: center; 90 | } 91 | .au-text-link { 92 | color: #444; 93 | text-decoration: none; 94 | &:hover { 95 | color: #444; 96 | text-decoration: none; 97 | } 98 | } 99 | 100 | .au-block { 101 | display: block; 102 | } 103 | .au-in-block { 104 | display: inline-block; 105 | } 106 | .au-overflow-hidden { 107 | overflow: hidden; 108 | } 109 | .au-flex { 110 | display: flex; 111 | } 112 | .au-flex-rc-center { 113 | display: flex; 114 | align-items: center; 115 | justify-content: center; 116 | } 117 | .au-flex-row-center { 118 | justify-content: center; 119 | } 120 | .au-flex-column-center { 121 | align-items: center; 122 | } 123 | -------------------------------------------------------------------------------- /src/assets/css/variables.scss: -------------------------------------------------------------------------------- 1 | // 全局变量 2 | // 颜色 3 | $--white-: #fff; 4 | $--blue-: #409EFF; 5 | // 背景 6 | $--var-background-white-: $--white-; 7 | -------------------------------------------------------------------------------- /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 |
    &#xe98f;
    37 |
  • 38 | 39 |
  • 40 | 41 |
    流转
    42 |
    &#xe992;
    43 |
  • 44 | 45 |
  • 46 | 47 |
    区块
    48 |
    &#xe994;
    49 |
  • 50 | 51 |
  • 52 | 53 |
    欧元
    54 |
    &#xe995;
    55 |
  • 56 | 57 |
  • 58 | 59 |
    商业化全球
    60 |
    &#xe996;
    61 |
  • 62 | 63 |
  • 64 | 65 |
    溯源
    66 |
    &#xe997;
    67 |
  • 68 | 69 |
  • 70 | 71 |
    数据库
    72 |
    &#xe998;
    73 |
  • 74 | 75 |
  • 76 | 77 |
    印章认证
    78 |
    &#xe99b;
    79 |
  • 80 | 81 |
  • 82 | 83 |
    v6
    84 |
    &#xe666;
    85 |
  • 86 | 87 |
  • 88 | 89 |
    v2
    90 |
    &#xe665;
    91 |
  • 92 | 93 |
  • 94 | 95 |
    v7
    96 |
    &#xe667;
    97 |
  • 98 | 99 |
  • 100 | 101 |
    v5
    102 |
    &#xe672;
    103 |
  • 104 | 105 |
  • 106 | 107 |
    v4
    108 |
    &#xe673;
    109 |
  • 110 | 111 |
  • 112 | 113 |
    v9
    114 |
    &#xe674;
    115 |
  • 116 | 117 |
  • 118 | 119 |
    v8
    120 |
    &#xe675;
    121 |
  • 122 | 123 |
  • 124 | 125 |
    v1
    126 |
    &#xe676;
    127 |
  • 128 | 129 |
  • 130 | 131 |
    v3
    132 |
    &#xe677;
    133 |
  • 134 | 135 |
  • 136 | 137 |
    v
    138 |
    &#xe7dd;
    139 |
  • 140 | 141 |
  • 142 | 143 |
    user
    144 |
    &#xe657;
    145 |
  • 146 | 147 |
148 |
149 |

Unicode 引用

150 |
151 | 152 |

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

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

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

160 |
161 |

Unicode 使用步骤如下:

162 |

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

163 |
@font-face {
165 |   font-family: 'iconfont';
166 |   src: url('iconfont.eot');
167 |   src: url('iconfont.eot?#iefix') format('embedded-opentype'),
168 |       url('iconfont.woff2') format('woff2'),
169 |       url('iconfont.woff') format('woff'),
170 |       url('iconfont.ttf') format('truetype'),
171 |       url('iconfont.svg#iconfont') format('svg');
172 | }
173 | 
174 |

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

175 |
.iconfont {
177 |   font-family: "iconfont" !important;
178 |   font-size: 16px;
179 |   font-style: normal;
180 |   -webkit-font-smoothing: antialiased;
181 |   -moz-osx-font-smoothing: grayscale;
182 | }
183 | 
184 |

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

185 |
186 | <span class="iconfont">&#x33;</span>
188 | 
189 |
190 |

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

191 |
192 |
193 |
194 |
195 |
    196 | 197 |
  • 198 | 199 |
    200 | 哈希 201 |
    202 |
    .iconhaxi 203 |
    204 |
  • 205 | 206 |
  • 207 | 208 |
    209 | 流转 210 |
    211 |
    .iconliuzhuan 212 |
    213 |
  • 214 | 215 |
  • 216 | 217 |
    218 | 区块 219 |
    220 |
    .iconqukuai 221 |
    222 |
  • 223 | 224 |
  • 225 | 226 |
    227 | 欧元 228 |
    229 |
    .iconouyuan 230 |
    231 |
  • 232 | 233 |
  • 234 | 235 |
    236 | 商业化全球 237 |
    238 |
    .iconshangyehuaquanqiu 239 |
    240 |
  • 241 | 242 |
  • 243 | 244 |
    245 | 溯源 246 |
    247 |
    .iconsuyuan 248 |
    249 |
  • 250 | 251 |
  • 252 | 253 |
    254 | 数据库 255 |
    256 |
    .iconshujuku 257 |
    258 |
  • 259 | 260 |
  • 261 | 262 |
    263 | 印章认证 264 |
    265 |
    .iconyinzhangrenzheng 266 |
    267 |
  • 268 | 269 |
  • 270 | 271 |
    272 | v6 273 |
    274 |
    .iconv 275 |
    276 |
  • 277 | 278 |
  • 279 | 280 |
    281 | v2 282 |
    283 |
    .iconv1 284 |
    285 |
  • 286 | 287 |
  • 288 | 289 |
    290 | v7 291 |
    292 |
    .iconv2 293 |
    294 |
  • 295 | 296 |
  • 297 | 298 |
    299 | v5 300 |
    301 |
    .iconv3 302 |
    303 |
  • 304 | 305 |
  • 306 | 307 |
    308 | v4 309 |
    310 |
    .iconv4 311 |
    312 |
  • 313 | 314 |
  • 315 | 316 |
    317 | v9 318 |
    319 |
    .iconv5 320 |
    321 |
  • 322 | 323 |
  • 324 | 325 |
    326 | v8 327 |
    328 |
    .iconv6 329 |
    330 |
  • 331 | 332 |
  • 333 | 334 |
    335 | v1 336 |
    337 |
    .iconv7 338 |
    339 |
  • 340 | 341 |
  • 342 | 343 |
    344 | v3 345 |
    346 |
    .iconv8 347 |
    348 |
  • 349 | 350 |
  • 351 | 352 |
    353 | v 354 |
    355 |
    .iconv9 356 |
    357 |
  • 358 | 359 |
  • 360 | 361 |
    362 | user 363 |
    364 |
    .iconuser 365 |
    366 |
  • 367 | 368 |
369 |
370 |

font-class 引用

371 |
372 | 373 |

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

374 |

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

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

使用步骤如下:

382 |

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

383 |
<link rel="stylesheet" href="./iconfont.css">
384 | 
385 |

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

386 |
<span class="iconfont iconxxx"></span>
387 | 
388 |
389 |

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

391 |
392 |
393 |
394 |
395 |
    396 | 397 |
  • 398 | 401 |
    哈希
    402 |
    #iconhaxi
    403 |
  • 404 | 405 |
  • 406 | 409 |
    流转
    410 |
    #iconliuzhuan
    411 |
  • 412 | 413 |
  • 414 | 417 |
    区块
    418 |
    #iconqukuai
    419 |
  • 420 | 421 |
  • 422 | 425 |
    欧元
    426 |
    #iconouyuan
    427 |
  • 428 | 429 |
  • 430 | 433 |
    商业化全球
    434 |
    #iconshangyehuaquanqiu
    435 |
  • 436 | 437 |
  • 438 | 441 |
    溯源
    442 |
    #iconsuyuan
    443 |
  • 444 | 445 |
  • 446 | 449 |
    数据库
    450 |
    #iconshujuku
    451 |
  • 452 | 453 |
  • 454 | 457 |
    印章认证
    458 |
    #iconyinzhangrenzheng
    459 |
  • 460 | 461 |
  • 462 | 465 |
    v6
    466 |
    #iconv
    467 |
  • 468 | 469 |
  • 470 | 473 |
    v2
    474 |
    #iconv1
    475 |
  • 476 | 477 |
  • 478 | 481 |
    v7
    482 |
    #iconv2
    483 |
  • 484 | 485 |
  • 486 | 489 |
    v5
    490 |
    #iconv3
    491 |
  • 492 | 493 |
  • 494 | 497 |
    v4
    498 |
    #iconv4
    499 |
  • 500 | 501 |
  • 502 | 505 |
    v9
    506 |
    #iconv5
    507 |
  • 508 | 509 |
  • 510 | 513 |
    v8
    514 |
    #iconv6
    515 |
  • 516 | 517 |
  • 518 | 521 |
    v1
    522 |
    #iconv7
    523 |
  • 524 | 525 |
  • 526 | 529 |
    v3
    530 |
    #iconv8
    531 |
  • 532 | 533 |
  • 534 | 537 |
    v
    538 |
    #iconv9
    539 |
  • 540 | 541 |
  • 542 | 545 |
    user
    546 |
    #iconuser
    547 |
  • 548 | 549 |
550 |
551 |

Symbol 引用

552 |
553 | 554 |

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

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

使用步骤如下:

563 |

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

564 |
<script src="./iconfont.js"></script>
565 | 
566 |

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

567 |
<style>
568 | .icon {
569 |   width: 1em;
570 |   height: 1em;
571 |   vertical-align: -0.15em;
572 |   fill: currentColor;
573 |   overflow: hidden;
574 | }
575 | </style>
576 | 
577 |

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

578 |
<svg class="icon" aria-hidden="true">
579 |   <use xlink:href="#icon-xxx"></use>
580 | </svg>
581 | 
582 |
583 |
584 | 585 |
586 |
587 | 606 | 607 | 608 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1614132660938'); /* IE9 */ 3 | src: url('iconfont.eot?t=1614132660938#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=1614132660938') format('woff'), 6 | url('iconfont.ttf?t=1614132660938') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1614132660938#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 | .iconhaxi:before { 19 | content: "\e98f"; 20 | } 21 | 22 | .iconliuzhuan:before { 23 | content: "\e992"; 24 | } 25 | 26 | .iconqukuai:before { 27 | content: "\e994"; 28 | } 29 | 30 | .iconouyuan:before { 31 | content: "\e995"; 32 | } 33 | 34 | .iconshangyehuaquanqiu:before { 35 | content: "\e996"; 36 | } 37 | 38 | .iconsuyuan:before { 39 | content: "\e997"; 40 | } 41 | 42 | .iconshujuku:before { 43 | content: "\e998"; 44 | } 45 | 46 | .iconyinzhangrenzheng:before { 47 | content: "\e99b"; 48 | } 49 | 50 | .iconv:before { 51 | content: "\e666"; 52 | } 53 | 54 | .iconv1:before { 55 | content: "\e665"; 56 | } 57 | 58 | .iconv2:before { 59 | content: "\e667"; 60 | } 61 | 62 | .iconv3:before { 63 | content: "\e672"; 64 | } 65 | 66 | .iconv4:before { 67 | content: "\e673"; 68 | } 69 | 70 | .iconv5:before { 71 | content: "\e674"; 72 | } 73 | 74 | .iconv6:before { 75 | content: "\e675"; 76 | } 77 | 78 | .iconv7:before { 79 | content: "\e676"; 80 | } 81 | 82 | .iconv8:before { 83 | content: "\e677"; 84 | } 85 | 86 | .iconv9:before { 87 | content: "\e7dd"; 88 | } 89 | 90 | .iconuser:before { 91 | content: "\e657"; 92 | } 93 | 94 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/iconfont/iconfont.eot -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2382143", 3 | "name": "vite2-bg-template", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "18267923", 10 | "name": "哈希", 11 | "font_class": "haxi", 12 | "unicode": "e98f", 13 | "unicode_decimal": 59791 14 | }, 15 | { 16 | "icon_id": "18267934", 17 | "name": "流转", 18 | "font_class": "liuzhuan", 19 | "unicode": "e992", 20 | "unicode_decimal": 59794 21 | }, 22 | { 23 | "icon_id": "18267938", 24 | "name": "区块", 25 | "font_class": "qukuai", 26 | "unicode": "e994", 27 | "unicode_decimal": 59796 28 | }, 29 | { 30 | "icon_id": "18267942", 31 | "name": "欧元", 32 | "font_class": "ouyuan", 33 | "unicode": "e995", 34 | "unicode_decimal": 59797 35 | }, 36 | { 37 | "icon_id": "18267949", 38 | "name": "商业化全球", 39 | "font_class": "shangyehuaquanqiu", 40 | "unicode": "e996", 41 | "unicode_decimal": 59798 42 | }, 43 | { 44 | "icon_id": "18267957", 45 | "name": "溯源", 46 | "font_class": "suyuan", 47 | "unicode": "e997", 48 | "unicode_decimal": 59799 49 | }, 50 | { 51 | "icon_id": "18267958", 52 | "name": "数据库", 53 | "font_class": "shujuku", 54 | "unicode": "e998", 55 | "unicode_decimal": 59800 56 | }, 57 | { 58 | "icon_id": "18267973", 59 | "name": "印章认证", 60 | "font_class": "yinzhangrenzheng", 61 | "unicode": "e99b", 62 | "unicode_decimal": 59803 63 | }, 64 | { 65 | "icon_id": "12548806", 66 | "name": "v6", 67 | "font_class": "v", 68 | "unicode": "e666", 69 | "unicode_decimal": 58982 70 | }, 71 | { 72 | "icon_id": "12548807", 73 | "name": "v2", 74 | "font_class": "v1", 75 | "unicode": "e665", 76 | "unicode_decimal": 58981 77 | }, 78 | { 79 | "icon_id": "12548809", 80 | "name": "v7", 81 | "font_class": "v2", 82 | "unicode": "e667", 83 | "unicode_decimal": 58983 84 | }, 85 | { 86 | "icon_id": "12643756", 87 | "name": "v5", 88 | "font_class": "v3", 89 | "unicode": "e672", 90 | "unicode_decimal": 58994 91 | }, 92 | { 93 | "icon_id": "12643757", 94 | "name": "v4", 95 | "font_class": "v4", 96 | "unicode": "e673", 97 | "unicode_decimal": 58995 98 | }, 99 | { 100 | "icon_id": "12643761", 101 | "name": "v9", 102 | "font_class": "v5", 103 | "unicode": "e674", 104 | "unicode_decimal": 58996 105 | }, 106 | { 107 | "icon_id": "12643765", 108 | "name": "v8", 109 | "font_class": "v6", 110 | "unicode": "e675", 111 | "unicode_decimal": 58997 112 | }, 113 | { 114 | "icon_id": "12643786", 115 | "name": "v1", 116 | "font_class": "v7", 117 | "unicode": "e676", 118 | "unicode_decimal": 58998 119 | }, 120 | { 121 | "icon_id": "12643827", 122 | "name": "v3", 123 | "font_class": "v8", 124 | "unicode": "e677", 125 | "unicode_decimal": 58999 126 | }, 127 | { 128 | "icon_id": "16561802", 129 | "name": "v", 130 | "font_class": "v9", 131 | "unicode": "e7dd", 132 | "unicode_decimal": 59357 133 | }, 134 | { 135 | "icon_id": "15167625", 136 | "name": "user", 137 | "font_class": "user", 138 | "unicode": "e657", 139 | "unicode_decimal": 58967 140 | } 141 | ] 142 | } 143 | -------------------------------------------------------------------------------- /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 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/iconfont/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/iconfont/iconfont.woff -------------------------------------------------------------------------------- /src/assets/iconfont/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/iconfont/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/images/bg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/images/bg.jpeg -------------------------------------------------------------------------------- /src/assets/images/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/images/profile.jpg -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/au-header.vue: -------------------------------------------------------------------------------- 1 | 24 | 60 | 106 | -------------------------------------------------------------------------------- /src/components/au-input.vue: -------------------------------------------------------------------------------- 1 | 17 | 58 | -------------------------------------------------------------------------------- /src/components/au-layout-empty.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/au-layout.vue: -------------------------------------------------------------------------------- 1 | 30 | 80 | 151 | -------------------------------------------------------------------------------- /src/components/au-nav.vue: -------------------------------------------------------------------------------- 1 | 37 | 64 | 96 | -------------------------------------------------------------------------------- /src/components/au-pagination.ts: -------------------------------------------------------------------------------- 1 | import { AppContext, ref, watch, onMounted } from 'vue' 2 | 3 | export default function pagination(ctx?: AppContext, callback?: (v: unknown) => unknown, autoFetch: boolean = false) { 4 | const limit = ref(10) 5 | const page = ref(1) 6 | const total = ref(100) 7 | const clear = () => { 8 | limit.value = 10 9 | page.value = 1 10 | total.value = 100 11 | if (!autoFetch) return 12 | if (typeof callback === 'function') { 13 | callback({ 14 | limit: limit.value, 15 | page: page.value, 16 | total: total.value 17 | }) 18 | } 19 | } 20 | const changeLimit = (newLimit: number) => { 21 | limit.value = newLimit 22 | } 23 | const changePage = (newPage: number) => { 24 | page.value = newPage 25 | } 26 | const changeTotal = (newTotal: number) => { 27 | total.value = newTotal 28 | } 29 | const pageSizeChange = (...args: []) => { 30 | console.log('sizeChange', args) 31 | } 32 | const currentPageChange = (currPage: number) => { 33 | page.value = currPage 34 | } 35 | watch([limit, page, total], ([l, p, t], [prevLimit, prevPage, prevTotal]) => { 36 | if (!autoFetch) return 37 | if (t !== prevTotal) { 38 | page.value = 1 39 | return 40 | } 41 | if (typeof callback === 'function') { 42 | callback({ 43 | limit: limit.value, 44 | page: page.value, 45 | total: total.value 46 | }) 47 | } 48 | }) 49 | 50 | onMounted(() => { 51 | if(autoFetch && typeof callback === 'function') { 52 | callback({ 53 | limit: limit.value, 54 | page: page.value, 55 | total: total.value 56 | }) 57 | } 58 | }) 59 | 60 | return { 61 | limit, 62 | page, 63 | total, 64 | clear, 65 | changeLimit, 66 | changePage, 67 | changeTotal, 68 | pageSizeChange, 69 | currentPageChange 70 | } 71 | } -------------------------------------------------------------------------------- /src/components/au-screen.ts: -------------------------------------------------------------------------------- 1 | import { SetupContext, computed, ComputedRef } from 'vue' 2 | import { getStore } from '../store/index' 3 | 4 | export default function screen(ctx?: SetupContext, callback?: (m: ComputedRef, c: ComputedRef) => string) { 5 | const { store } = getStore() 6 | const isMobile = computed(() => store.getters.isMobile) 7 | const closed = computed(() => store.getters.toggleClosed) 8 | const toggleClick = () => { 9 | store.dispatch('updateToggleType', !closed.value) 10 | } 11 | let toggleClass = computed(() => { 12 | if(typeof callback === 'function') return callback(isMobile, closed) 13 | else if (!isMobile.value) return '' 14 | else return closed.value ? 'el-icon-s-unfold' : 'el-icon-s-fold' 15 | }) 16 | 17 | return { 18 | isMobile, 19 | closed, 20 | toggleClick, 21 | toggleClass 22 | } 23 | } -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | // style 5 | import './assets/css/base.css' 6 | import './assets/iconfont/iconfont.css' 7 | import 'nprogress/nprogress.css' 8 | import 'element-plus/lib/theme-chalk/display.css' 9 | import 'element-plus/lib/theme-chalk/index.css' 10 | 11 | // plugins 12 | import plugins from './plugins' 13 | 14 | createApp(App) 15 | .use(plugins) 16 | .mount('#app') 17 | -------------------------------------------------------------------------------- /src/plugins/axios.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosResponse } from 'axios' 2 | import qs from 'qs' 3 | import _sign from '../utils/_sign' 4 | 5 | const request = axios.create({ 6 | baseURL: import.meta.env.VITE_BASE_URL, 7 | timeout: 10000, 8 | transformRequest: [function (data) { 9 | const d = qs.stringify(data) 10 | return d 11 | }] 12 | }) 13 | 14 | request.interceptors.request.use( 15 | config => { 16 | if (config.method?.toLowerCase() === 'post') { 17 | _sign(config.data) 18 | } 19 | return config 20 | }, 21 | error => { 22 | return Promise.reject(error) 23 | } 24 | ) 25 | 26 | request.interceptors.response.use( 27 | (response) => { 28 | const res = response.data 29 | return res 30 | }, 31 | error => { 32 | console.log('err' + error) 33 | return Promise.reject(error) 34 | } 35 | ) 36 | 37 | export interface PromiseBaseName { 38 | data: string | Array | number | Object | undefined | null, 39 | error_code: number, 40 | message?: string 41 | } 42 | 43 | export interface PromiseLoginName extends PromiseBaseName{ 44 | token: string 45 | } 46 | export type ResponseName = AxiosResponse | AxiosResponse 47 | 48 | export default request 49 | -------------------------------------------------------------------------------- /src/plugins/element-ui.ts: -------------------------------------------------------------------------------- 1 | import { App } from 'vue' 2 | import { 3 | ElAlert, 4 | ElIcon, 5 | ElBacktop, 6 | ElBadge, 7 | ElButton, 8 | ElButtonGroup, 9 | ElCard, 10 | ElCheckbox, 11 | ElCheckboxButton, 12 | ElCheckboxGroup, 13 | ElCol, 14 | ElContainer, 15 | ElDialog, 16 | ElDropdown, 17 | ElDropdownItem, 18 | ElDropdownMenu, 19 | ElForm, 20 | ElFormItem, 21 | ElHeader, 22 | ElInput, 23 | ElInputNumber, 24 | ElLink, 25 | ElMain, 26 | ElMenu, 27 | ElMenuItem, 28 | ElMenuItemGroup, 29 | ElPageHeader, 30 | ElPagination, 31 | ElPopconfirm, 32 | ElPopover, 33 | ElPopper, 34 | ElProgress, 35 | ElRadio, 36 | ElRadioButton, 37 | ElRadioGroup, 38 | ElRow, 39 | ElSelect, 40 | ElStep, 41 | ElSteps, 42 | ElSubmenu, 43 | ElSwitch, 44 | ElTabPane, 45 | ElTable, 46 | ElTableColumn, 47 | ElTabs, 48 | ElTag, 49 | ElTooltip, 50 | ElUpload, 51 | ElLoading, 52 | ElMessage, 53 | ElMessageBox, 54 | ElNotification, 55 | ElTimeline, 56 | ElTimelineItem, 57 | } from 'element-plus' 58 | 59 | const elements = [ 60 | ElAlert, 61 | ElIcon, 62 | ElBacktop, 63 | ElBadge, 64 | ElButton, 65 | ElButtonGroup, 66 | ElCard, 67 | ElCheckbox, 68 | ElCheckboxButton, 69 | ElCheckboxGroup, 70 | ElCol, 71 | ElContainer, 72 | ElDialog, 73 | ElDropdown, 74 | ElDropdownItem, 75 | ElDropdownMenu, 76 | ElForm, 77 | ElFormItem, 78 | ElHeader, 79 | ElInput, 80 | ElInputNumber, 81 | ElLink, 82 | ElMain, 83 | ElMenu, 84 | ElMenuItem, 85 | ElMenuItemGroup, 86 | ElPageHeader, 87 | ElPagination, 88 | ElPopconfirm, 89 | ElPopover, 90 | ElPopper, 91 | ElProgress, 92 | ElRadio, 93 | ElRadioButton, 94 | ElRadioGroup, 95 | ElRow, 96 | ElSelect, 97 | ElStep, 98 | ElSteps, 99 | ElSubmenu, 100 | ElSwitch, 101 | ElTabPane, 102 | ElTable, 103 | ElTableColumn, 104 | ElTabs, 105 | ElTag, 106 | ElTooltip, 107 | ElUpload, 108 | ElTimeline, 109 | ElTimelineItem, 110 | ] 111 | 112 | const plugins = [ElLoading, ElMessage, ElMessageBox, ElNotification] 113 | 114 | export default { 115 | install(app: App) { 116 | elements.forEach((component) => { 117 | app.component(component.name, component) 118 | }) 119 | 120 | plugins.forEach((plugin) => { 121 | app.use(plugin) 122 | }) 123 | }, 124 | } 125 | -------------------------------------------------------------------------------- /src/plugins/index.ts: -------------------------------------------------------------------------------- 1 | import { router } from './router' 2 | import { store, key } from './store' 3 | import ElementUI from './element-ui' 4 | import { App } from 'vue' 5 | import lang from 'element-plus/lib/locale/lang/zh-cn' 6 | import 'dayjs/locale/zh-cn' 7 | import locale from 'element-plus/lib/locale' 8 | 9 | export default { 10 | install(vue: App) { 11 | vue.use(router) 12 | vue.use(store, key) 13 | vue.use(ElementUI, { locale }) 14 | // locale.use(lang) // 设置 element-plus 中文,暂时没法起作用,原因待研究 15 | } 16 | } -------------------------------------------------------------------------------- /src/plugins/router.ts: -------------------------------------------------------------------------------- 1 | import '../router/permision' // permision 2 | 3 | export { default as router } from '../router/index' -------------------------------------------------------------------------------- /src/plugins/store.ts: -------------------------------------------------------------------------------- 1 | export { key, store } from './../store/index' -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' 2 | import Layout from '../components/au-layout.vue' 3 | import EmptyLaoyout from '../components/au-layout-empty.vue' 4 | const routes: Array = [ 5 | { 6 | path: '', 7 | redirect: (_) => { 8 | return { path: '/home' } 9 | }, 10 | }, 11 | { 12 | path: '/', 13 | component: Layout, 14 | children: [ 15 | { 16 | path: 'home', 17 | name: 'Home', 18 | component: () => 19 | import(/* webpackChunkName: 'Home' */ '../views/home/index.vue'), 20 | meta: { 21 | title: '主页', 22 | }, 23 | }, 24 | { 25 | path: 'about', 26 | name: 'About', 27 | component: () => 28 | import(/* webpackChunkName: 'About' */ '../views/about/index.vue'), 29 | meta: { 30 | title: '关于', 31 | }, 32 | }, 33 | { 34 | path: 'profile', 35 | name: 'Profile', 36 | component: () => 37 | import( 38 | /* webpackChunkName: 'Profile' */ '../views/profile/index.vue' 39 | ), 40 | meta: { 41 | title: '个人中心', 42 | }, 43 | }, 44 | { 45 | path: 'data', 46 | name: 'Census', 47 | component: EmptyLaoyout, 48 | meta: { 49 | title: '数据统计', 50 | }, 51 | children: [ 52 | { 53 | path: 'rank', 54 | name: 'Rank', 55 | component: () => 56 | import(/* webpackChunkName: 'Rank' */ '../views/data/rank.vue'), 57 | meta: { 58 | title: '最新排名', 59 | }, 60 | }, 61 | { 62 | path: 'data', 63 | name: 'Data', 64 | component: () => 65 | import(/* webpackChunkName: 'Data' */ '../views/data/index.vue'), 66 | meta: { 67 | title: '实时信息', 68 | }, 69 | }, 70 | ], 71 | }, 72 | ], 73 | }, 74 | { 75 | path: '/login', 76 | name: 'Login', 77 | component: () => 78 | import(/* webpackChunkName: 'Login' */ '../views/login/index.vue'), 79 | meta: { 80 | title: '登录', 81 | }, 82 | }, 83 | { 84 | path: '/404', 85 | component: () => 86 | import(/* webpackChunkName: 'Error' */ '../views/error/404.vue'), 87 | meta: { 88 | title: '404', 89 | }, 90 | }, 91 | { 92 | path: '/', 93 | redirect: (_) => { 94 | return { path: '/home' } 95 | }, 96 | }, 97 | { 98 | path: '/:currentPath(.*)*', 99 | redirect: (_) => { 100 | return { path: '/404' } 101 | }, 102 | }, 103 | ] 104 | 105 | const router = createRouter({ 106 | history: createWebHistory(), 107 | routes, 108 | scrollBehavior(to, from, savedPosition) { 109 | return { 110 | el: '#app', 111 | top: 0, 112 | behavior: 'smooth', 113 | } 114 | }, 115 | }) 116 | 117 | export default router 118 | -------------------------------------------------------------------------------- /src/router/permision.ts: -------------------------------------------------------------------------------- 1 | import { store } from '../store' 2 | import { getlocalStorageToken } from '../utils/_ls' 3 | import NProgress from 'nprogress' 4 | import router from './index' 5 | 6 | router.beforeEach((to, from, next) => { 7 | NProgress.start() 8 | const hasToken = getlocalStorageToken() || store?.state?.token || '' 9 | if (hasToken) { 10 | if (to.path === '/login') { 11 | next('/home') 12 | } else { 13 | next() 14 | } 15 | } else { 16 | if (to.path === '/login'){ 17 | next() 18 | } else { 19 | next('/login') 20 | } 21 | } 22 | }) 23 | 24 | router.afterEach((to, from) => { 25 | NProgress.done() 26 | const isMobile = store.state.isMobile 27 | const closed = store.state.toggleClosed 28 | if (isMobile && !closed) { 29 | store.dispatch('updateToggleType', true) 30 | } 31 | document!.title = to?.meta?.title as string + '-后台管理模板' || '后台管理模板' 32 | }) -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } -------------------------------------------------------------------------------- /src/store/actions.ts: -------------------------------------------------------------------------------- 1 | import { State, UserInfo } from './state'; 2 | import { ActionContext } from 'vuex' 3 | import { login } from '../api/login' 4 | import { SET_TOKEN, CLEAR_TOKEN, USER_LOGIN, UPDATE_SCREEN_WIDTH, UPDATE_IS_MOBILE, UPDATE_TOGGLE_TYPE } from './types' 5 | import { PromiseLoginName } from '../plugins/axios' 6 | export default { 7 | setToken({ commit }: ActionContext, token: string) { 8 | commit(SET_TOKEN, token) 9 | }, 10 | clearToken({ commit }: ActionContext) { 11 | commit(CLEAR_TOKEN) 12 | }, 13 | login({ commit }: ActionContext, { username, password }: UserInfo) { 14 | return new Promise((resolve, reject) => { 15 | let _query = { 16 | username: username.trim(), password: password.trim() 17 | } 18 | login(_query).then((res) => { 19 | const { error_code, message, token } = res as unknown as PromiseLoginName 20 | if (error_code === 200) { 21 | commit(USER_LOGIN, _query) 22 | commit(SET_TOKEN, token) 23 | resolve(message) 24 | } else { 25 | reject(message) 26 | } 27 | }).catch((error: unknown) => { 28 | reject(error) 29 | }) 30 | }) 31 | }, 32 | updetaScreenWidth({ commit }: ActionContext, width: number) { 33 | if (width < 769) { 34 | commit(UPDATE_IS_MOBILE, true) 35 | commit(UPDATE_TOGGLE_TYPE, true) 36 | } else { 37 | commit(UPDATE_IS_MOBILE, false) 38 | commit(UPDATE_TOGGLE_TYPE, true) 39 | } 40 | commit(UPDATE_SCREEN_WIDTH, width) 41 | }, 42 | updateToggleType({ commit }: ActionContext, closed: number) { 43 | commit(UPDATE_TOGGLE_TYPE, closed) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/store/getters.ts: -------------------------------------------------------------------------------- 1 | import { State } from './state' 2 | export default { 3 | getToken(state: State) { 4 | return state.token 5 | }, 6 | getUserInfo(state: State) { 7 | return state.userInfo 8 | }, 9 | getScreenWidth(state: State) { 10 | return state.screenWidth 11 | }, 12 | isMobile(state: State) { 13 | return state.isMobile 14 | }, 15 | toggleClosed(state: State) { 16 | return state.toggleClosed 17 | } 18 | } -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey } from 'vue' 2 | import { createStore, Store, useStore } from 'vuex' 3 | import { state, State } from './state' 4 | import mutations from './mutations' 5 | import actions from './actions' 6 | import getters from './getters' 7 | export const key: InjectionKey> = Symbol() 8 | 9 | export const store = createStore({ 10 | state, 11 | getters, 12 | actions, 13 | mutations 14 | }) 15 | 16 | export function getStore() { 17 | return { 18 | store: useStore(key) 19 | } 20 | } -------------------------------------------------------------------------------- /src/store/mutations.ts: -------------------------------------------------------------------------------- 1 | import { removelocalStorageToken, setlocalStorageToken } from '../utils/_ls' 2 | import { State, UserInfo } from './state' 3 | import { SET_TOKEN, CLEAR_TOKEN, USER_LOGIN, UPDATE_IS_MOBILE, UPDATE_SCREEN_WIDTH, UPDATE_TOGGLE_TYPE } from './types' 4 | export default { 5 | [SET_TOKEN](state: State, token: string) { 6 | state.token = token 7 | setlocalStorageToken(token) 8 | }, 9 | [CLEAR_TOKEN](state: State) { 10 | state.token = '' 11 | removelocalStorageToken() 12 | }, 13 | [USER_LOGIN](state: State, user: UserInfo) { 14 | state.userInfo = user 15 | }, 16 | [UPDATE_SCREEN_WIDTH](state: State, width: number) { 17 | state.screenWidth = width 18 | }, 19 | [UPDATE_IS_MOBILE](state: State, isMobile: boolean) { 20 | state.isMobile = isMobile 21 | }, 22 | [UPDATE_TOGGLE_TYPE](state: State, closed: boolean) { 23 | state.toggleClosed = closed 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/store/state.ts: -------------------------------------------------------------------------------- 1 | import { getlocalStorageToken } from "../utils/_ls" 2 | export interface State { 3 | token: string; 4 | userInfo: UserInfo; 5 | screenWidth: number; 6 | isMobile: boolean; 7 | toggleClosed: boolean; 8 | [props: string]: any; 9 | } 10 | 11 | export interface UserInfo { 12 | username: string; 13 | password: string; 14 | [props: string]: any; 15 | } 16 | 17 | export function state() { 18 | return { 19 | token: getlocalStorageToken() || '', 20 | userInfo: { 21 | username: '', 22 | password: '' 23 | }, 24 | screenWidth: 1000, 25 | toggleClosed: false, 26 | isMobile: false 27 | } 28 | } -------------------------------------------------------------------------------- /src/store/types.ts: -------------------------------------------------------------------------------- 1 | export const SET_TOKEN = 'set_token' 2 | export const CLEAR_TOKEN = 'clear_token' 3 | export const USER_LOGIN = 'user_login' 4 | export const UPDATE_SCREEN_WIDTH = 'update_screen_width' 5 | export const UPDATE_IS_MOBILE = 'update_is_mobile' 6 | export const UPDATE_TOGGLE_TYPE = 'update_toggle_type' -------------------------------------------------------------------------------- /src/utils/_ls.ts: -------------------------------------------------------------------------------- 1 | const key = 'au-user' 2 | 3 | export function setlocalStorageToken(value: string) { 4 | localStorage.setItem(key, value) 5 | } 6 | 7 | export function removelocalStorageToken() { 8 | localStorage.removeItem(key) 9 | } 10 | 11 | export const getlocalStorageToken: () => string = function(): string { 12 | return localStorage.getItem(key) as string 13 | } -------------------------------------------------------------------------------- /src/utils/_sign.ts: -------------------------------------------------------------------------------- 1 | import md5 from 'js-md5' 2 | interface TransformData { 3 | [prop: string]: string | number | Array | Object | undefined | null 4 | } 5 | export default function objKeySort(data: TransformData): string { 6 | var newkey = Object.keys(data).sort() 7 | var newObj: TransformData = {} 8 | for (var i = 0; i < newkey.length; i++) { 9 | newObj[newkey[i]] = data[newkey[i]] 10 | } 11 | let signature = 'au' 12 | for (var j = 0; j < newkey.length; j++) { 13 | signature += newObj[newkey[j]] 14 | } 15 | var date = new Date(+new Date() + 8 * 3600 * 1000).toISOString().replace(/T/g, '').replace(/\.[\d]{3}Z/, '') 16 | signature += date.substring(0, 10) 17 | signature += 'au' 18 | signature = md5(signature.toUpperCase()) 19 | 20 | data.sign = signature 21 | 22 | return signature 23 | } -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coderlyu/vite-frontend-template/51ffa5db33be55e973b3d0c50b63438fcb5b5552/src/utils/index.ts -------------------------------------------------------------------------------- /src/views/about/au-message.vue: -------------------------------------------------------------------------------- 1 | 52 | 124 | 213 | -------------------------------------------------------------------------------- /src/views/about/au-produce.vue: -------------------------------------------------------------------------------- 1 | 79 | 126 | -------------------------------------------------------------------------------- /src/views/about/au-record.vue: -------------------------------------------------------------------------------- 1 | 31 | 61 | 102 | -------------------------------------------------------------------------------- /src/views/about/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 66 | -------------------------------------------------------------------------------- /src/views/data/au-list.vue: -------------------------------------------------------------------------------- 1 | 43 | 61 | -------------------------------------------------------------------------------- /src/views/data/au-rank.vue: -------------------------------------------------------------------------------- 1 | 17 | 42 | 113 | -------------------------------------------------------------------------------- /src/views/data/chart-config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | pie: function (data) { 3 | let options = { 4 | tooltip: { 5 | trigger: 'item' 6 | }, 7 | legend: { 8 | orient: 'vertical', 9 | left: 'left', 10 | }, 11 | series: [ 12 | { 13 | name: '人群特征', 14 | type: 'pie', 15 | radius: '50%', 16 | data: [ 17 | { value: 1048, name: '12 ~ 16岁' }, 18 | { value: 735, name: '16 ~ 18岁' }, 19 | { value: 580, name: '18 ~ 30岁' }, 20 | { value: 484, name: '30岁 ~' } 21 | ], 22 | emphasis: { 23 | itemStyle: { 24 | shadowBlur: 10, 25 | shadowOffsetX: 0, 26 | shadowColor: 'rgba(0, 0, 0, 0.5)' 27 | } 28 | } 29 | } 30 | ] 31 | } 32 | return options 33 | }, 34 | line: function (data) { 35 | let options = { 36 | xAxis: { 37 | type: 'category', 38 | data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月'] 39 | }, 40 | yAxis: { 41 | type: 'value' 42 | }, 43 | series: [{ 44 | data: [820, 932, 901, 934, 1290, 1330, 1320], 45 | type: 'line', 46 | smooth: true 47 | }, 48 | { 49 | data: [80, 92, 91, 94, 190, 130, 120], 50 | type: 'line', 51 | smooth: true 52 | }] 53 | }; 54 | return options 55 | }, 56 | bar: function (data) { 57 | let options = { 58 | xAxis: { 59 | type: 'category', 60 | data: ['2015', '2016', '2017', '2018', '2019', '2020', '2021'] 61 | }, 62 | yAxis: { 63 | type: 'value' 64 | }, 65 | series: [{ 66 | data: [120, 200, 150, 80, 70, 110, 130], 67 | type: 'bar', 68 | showBackground: true, 69 | backgroundStyle: { 70 | color: 'rgba(180, 180, 180, 0.2)' 71 | } 72 | }] 73 | } 74 | return options 75 | } 76 | } -------------------------------------------------------------------------------- /src/views/data/index.vue: -------------------------------------------------------------------------------- 1 | 40 | 79 | -------------------------------------------------------------------------------- /src/views/data/rank.vue: -------------------------------------------------------------------------------- 1 | 43 | 87 | 94 | -------------------------------------------------------------------------------- /src/views/error/404.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /src/views/home/index.vue: -------------------------------------------------------------------------------- 1 | 61 | 120 | 254 | -------------------------------------------------------------------------------- /src/views/home/notice.vue: -------------------------------------------------------------------------------- 1 | 23 | 70 | -------------------------------------------------------------------------------- /src/views/login/index.vue: -------------------------------------------------------------------------------- 1 | 37 | 71 | 160 | -------------------------------------------------------------------------------- /src/views/profile/au-profile-mobile.vue: -------------------------------------------------------------------------------- 1 | 42 | 72 | -------------------------------------------------------------------------------- /src/views/profile/au-profile-pc.vue: -------------------------------------------------------------------------------- 1 | 90 | 139 | 254 | -------------------------------------------------------------------------------- /src/views/profile/au-upload.ts: -------------------------------------------------------------------------------- 1 | import { ref, SetupContext } from 'vue' 2 | 3 | export function uploadState(ctx: SetupContext) { 4 | const imageUrl = ref('data:image/jpeg;base64,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') 5 | const tempUrl = ref('') 6 | const confirmAvatarVisible = ref(false) 7 | const handleAvatarSuccess = (...args: any[]) => { 8 | console.log('handleAvatarSuccess', args) 9 | } 10 | const beforeAvatarUpload = (file: File) => { 11 | const reader = new FileReader() 12 | reader.onload = function(ev){ 13 | tempUrl.value = ev!.target!.result as string 14 | confirmAvatarVisible.value = true 15 | } 16 | reader.readAsDataURL(file) 17 | } 18 | const handleConfirmClose = () => { 19 | confirmAvatarVisible.value = false 20 | } 21 | const confirmAvatar = () => { 22 | imageUrl.value = tempUrl.value 23 | confirmAvatarVisible.value = false 24 | } 25 | return { 26 | imageUrl, 27 | tempUrl, 28 | confirmAvatarVisible, 29 | handleAvatarSuccess, 30 | beforeAvatarUpload, 31 | handleConfirmClose, 32 | confirmAvatar 33 | } 34 | } -------------------------------------------------------------------------------- /src/views/profile/config.js: -------------------------------------------------------------------------------- 1 | export default function ({ }) { 2 | return { 3 | columns: [ 4 | { 5 | label: '性别', 6 | props: 'sex' 7 | }, 8 | { 9 | label: '一句话介绍', 10 | props: 'intro' 11 | }, 12 | { 13 | label: '所在行业', 14 | props: 'work' 15 | }, 16 | { 17 | label: '个人简介', 18 | props: 'mark' 19 | } 20 | ], 21 | config: { 22 | nick: { props: 'nick', type: 'input', placeholder: '请输入昵称' }, 23 | sex: { 24 | props: 'sex', 25 | type: 'radio', 26 | options: [ 27 | { 28 | label: '男', 29 | value: '男' 30 | }, 31 | { 32 | label: '女', 33 | value: '女' 34 | } 35 | ] 36 | }, 37 | intro: { 38 | props: 'intro', 39 | type: 'input', 40 | placeholder: '请输入一句话介绍' 41 | }, 42 | work: { 43 | props: 'work', 44 | type: 'input', 45 | placeholder: '请输入所在行业' 46 | }, 47 | mark: { 48 | props: 'mark', 49 | type: 'input', 50 | placeholder: '请输入个人简介' 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /src/views/profile/index.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "lib": ["esnext", "dom"], 12 | "types": ["vite/client", "node", "vite-svg-loader"] 13 | }, 14 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 15 | } 16 | --------------------------------------------------------------------------------