正在显示
51 个修改的文件
包含
3959 行增加
和
0 行删除
.browserslistrc
0 → 100644
.env.devOnLine
0 → 100644
.gitignore
0 → 100644
| 1 | +.DS_Store | ||
| 2 | +node_modules | ||
| 3 | +/dist | ||
| 4 | + | ||
| 5 | + | ||
| 6 | +# local env files | ||
| 7 | +.env.local | ||
| 8 | +.env.*.local | ||
| 9 | + | ||
| 10 | +# Log files | ||
| 11 | +npm-debug.log* | ||
| 12 | +yarn-debug.log* | ||
| 13 | +yarn-error.log* | ||
| 14 | +pnpm-debug.log* | ||
| 15 | + | ||
| 16 | +# Editor directories and files | ||
| 17 | +.idea | ||
| 18 | +.vscode | ||
| 19 | +*.suo | ||
| 20 | +*.ntvs* | ||
| 21 | +*.njsproj | ||
| 22 | +*.sln | ||
| 23 | +*.sw? |
.prettierrc
0 → 100644
README.md
0 → 100644
| 1 | +# kraken-vue-app | ||
| 2 | + | ||
| 3 | +## Project setup | ||
| 4 | +``` | ||
| 5 | +npm install | ||
| 6 | +``` | ||
| 7 | + | ||
| 8 | +### Compiles and hot-reloads for development | ||
| 9 | +``` | ||
| 10 | +npm run serve | ||
| 11 | +``` | ||
| 12 | + | ||
| 13 | +### Compiles and minifies for production | ||
| 14 | +``` | ||
| 15 | +npm run build | ||
| 16 | +``` | ||
| 17 | + | ||
| 18 | +### Customize configuration | ||
| 19 | +See [Configuration Reference](https://cli.vuejs.org/config/). |
babel.config.js
0 → 100644
buildToApp.js
0 → 100644
jsconfig.json
0 → 100644
package copy.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "kraken-vue-app", | ||
| 3 | + "version": "0.1.0", | ||
| 4 | + "private": true, | ||
| 5 | + "scripts": { | ||
| 6 | + "serve": "vue-cli-service serve", | ||
| 7 | + "build": "vue-cli-service build", | ||
| 8 | + "dev": "vue-cli-service serve --mode dev --open", | ||
| 9 | + "dev:online": "vue-cli-service serve --mode devOnLine --open", | ||
| 10 | + "uat": "vue-cli-service build --mode uat", | ||
| 11 | + "pro": "vue-cli-service build --mode pro" | ||
| 12 | + }, | ||
| 13 | + "dependencies": { | ||
| 14 | + "axios": "^1.4.0", | ||
| 15 | + "core-js": "^3.8.3", | ||
| 16 | + "vant": "^2.12.54", | ||
| 17 | + "vue": "^2.6.14", | ||
| 18 | + "vue-router": "^3.5.1", | ||
| 19 | + "vuex": "^3.6.2" | ||
| 20 | + }, | ||
| 21 | + "devDependencies": { | ||
| 22 | + "@vue/cli-plugin-babel": "~5.0.0", | ||
| 23 | + "@vue/cli-plugin-router": "~5.0.0", | ||
| 24 | + "@vue/cli-plugin-vuex": "~5.0.0", | ||
| 25 | + "@vue/cli-service": "~5.0.0", | ||
| 26 | + "sass": "^1.32.7", | ||
| 27 | + "sass-loader": "^12.0.0", | ||
| 28 | + "vue-template-compiler": "^2.6.14" | ||
| 29 | + } | ||
| 30 | +} |
package-lock.json
0 → 100644
此 diff 太大无法显示。
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "kraken-vue-app", | ||
| 3 | + "version": "0.1.0", | ||
| 4 | + "private": true, | ||
| 5 | + "scripts": { | ||
| 6 | + "serve": "vue-cli-service serve", | ||
| 7 | + "build": "vue-cli-service build", | ||
| 8 | + "dev": "vue-cli-service serve --mode dev --open", | ||
| 9 | + "dev:online": "vue-cli-service serve --mode devOnLine --open", | ||
| 10 | + "uat": "vue-cli-service build --mode uat --no-source-map", | ||
| 11 | + "pro": "vue-cli-service build --mode pro --no-source-map" | ||
| 12 | + }, | ||
| 13 | + "dependencies": { | ||
| 14 | + "axios": "^1.4.0", | ||
| 15 | + "vant": "^2.12.54", | ||
| 16 | + "vue": "^2.6.14", | ||
| 17 | + "vue-drag-resize": "^1.5.4", | ||
| 18 | + "vue-router": "^3.5.1", | ||
| 19 | + "vuex": "^3.6.2" | ||
| 20 | + }, | ||
| 21 | + "devDependencies": { | ||
| 22 | + "@vue/cli-plugin-babel": "~5.0.0", | ||
| 23 | + "@vue/cli-plugin-router": "~5.0.0", | ||
| 24 | + "@vue/cli-plugin-vuex": "~5.0.0", | ||
| 25 | + "@vue/cli-service": "~5.0.0", | ||
| 26 | + "sass": "^1.32.7", | ||
| 27 | + "sass-loader": "^12.0.0", | ||
| 28 | + "vue-template-compiler": "^2.6.14" | ||
| 29 | + } | ||
| 30 | +} |
public/favicon.ico
0 → 100644
不能预览此文件类型
public/index.html
0 → 100644
| 1 | +<!DOCTYPE html> | ||
| 2 | +<html lang=""> | ||
| 3 | + | ||
| 4 | +<head> | ||
| 5 | + <meta charset="utf-8"> | ||
| 6 | + <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||
| 7 | + <meta name="viewport" content="width=device-width,initial-scale=1.0"> | ||
| 8 | + <link rel="icon" href="<%= BASE_URL %>favicon.ico"> | ||
| 9 | + <title> | ||
| 10 | + <%= htmlWebpackPlugin.options.title %> | ||
| 11 | + </title> | ||
| 12 | +</head> | ||
| 13 | + | ||
| 14 | +<body> | ||
| 15 | + <noscript> | ||
| 16 | + <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. | ||
| 17 | + Please enable it to continue.</strong> | ||
| 18 | + </noscript> | ||
| 19 | + <div id="app"></div> | ||
| 20 | + <!-- built files will be auto injected --> | ||
| 21 | +</body> | ||
| 22 | + | ||
| 23 | +</html> |
src/App.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="app" id="app" :style="{ paddingTop: systemInfo.statusHeight + 'px' }"> | ||
| 3 | + <MyNavBar | ||
| 4 | + :offset-top="systemInfo.statusHeight" | ||
| 5 | + :title="$route.meta.title" | ||
| 6 | + :show="getIsApp && $route.meta.isTabBarMenu && $route.meta.showNavBar" | ||
| 7 | + :right-text="rightBtnText" | ||
| 8 | + @right-callback="othersBtnFun" | ||
| 9 | + /> | ||
| 10 | + <MyNavBar | ||
| 11 | + :offset-top="systemInfo.statusHeight" | ||
| 12 | + :title="$route.meta.title" | ||
| 13 | + :show="getIsApp && !$route.meta.isTabBarMenu && $route.meta.showNavBar" | ||
| 14 | + :left-arrow="true" | ||
| 15 | + :left-arrow-style="{ color: '#f00' }" | ||
| 16 | + :right-text="rightBtnText" | ||
| 17 | + left-text="返回" | ||
| 18 | + @left-callback="backPrePage" | ||
| 19 | + @right-callback="othersBtnFun" | ||
| 20 | + /> | ||
| 21 | + | ||
| 22 | + <div class="router-page-view" :style="{ marginTop: getAppNavBarShow ? '46px' : '' }" :class="{ 'is-tab-bar-menu-page': $route.meta.isTabBarMenu }"> | ||
| 23 | + <!-- <keep-alive :include="keepAlivePageNameList"> --> | ||
| 24 | + <router-view /> | ||
| 25 | + <!-- </keep-alive> --> | ||
| 26 | + </div> | ||
| 27 | + | ||
| 28 | + <template v-if="$route.meta.isTabBarMenu"> | ||
| 29 | + <MyTabBar v-model="currentTabBarName" :tab-bar-list="tabBarList" @change="tabBarChange" /> | ||
| 30 | + </template> | ||
| 31 | + <MyDialog | ||
| 32 | + :show-popup="dialogOpen" | ||
| 33 | + :title="myDialogConfig.title" | ||
| 34 | + :description="myDialogConfig.description" | ||
| 35 | + :descriptionType="myDialogConfig.descriptionType" | ||
| 36 | + :show-cancel-btn="true" | ||
| 37 | + :cancel-btn-text="myDialogConfig.cancelBtnText" | ||
| 38 | + :ok-close="true" | ||
| 39 | + @ok="dialogOk" | ||
| 40 | + @cancel="dialogCancel" | ||
| 41 | + /> | ||
| 42 | + <MyToast loading-icon="icon-loading-1" ref="myToast" /> | ||
| 43 | + <!-- <MyConsole ref="MyConsole" theme="dark" /> --> | ||
| 44 | + </div> | ||
| 45 | +</template> | ||
| 46 | +<script> | ||
| 47 | +import { versionFormatting } from '@/libs/tools' | ||
| 48 | +import { getVersionApi } from '@/apis/commApis' | ||
| 49 | +import MyNavBar from '@/components/MyNavBar/MyNavBar.vue' | ||
| 50 | +import MyTabBar from '@/components/MyTabBar/MyTabBar.vue' | ||
| 51 | +import MyDialog from '@/components/MyDialog/MyDialog.vue' | ||
| 52 | +import MyToast from '@/components/MyToast/MyToast.vue' | ||
| 53 | +import MyConsole from '@/components/MyConsole/MyConsole.vue' | ||
| 54 | + | ||
| 55 | +const myDialogConfig = { | ||
| 56 | + title: '提示', | ||
| 57 | + description: '', | ||
| 58 | + descriptionType: 'html', | ||
| 59 | + cancelBtnText: '取消' | ||
| 60 | +} | ||
| 61 | +export default { | ||
| 62 | + name: 'myApp', | ||
| 63 | + components: { | ||
| 64 | + MyNavBar, | ||
| 65 | + MyTabBar, | ||
| 66 | + MyDialog, | ||
| 67 | + MyToast, | ||
| 68 | + MyConsole | ||
| 69 | + }, | ||
| 70 | + data() { | ||
| 71 | + return { | ||
| 72 | + currentTabBarName: '', | ||
| 73 | + dialogOpen: false, | ||
| 74 | + isOpenToast: false, | ||
| 75 | + systemInfo: { | ||
| 76 | + version: '0.0.0', | ||
| 77 | + statusHeight: 0 | ||
| 78 | + }, | ||
| 79 | + newVersion: '', | ||
| 80 | + myDialogConfig: myDialogConfig, | ||
| 81 | + rightBtnText: '', | ||
| 82 | + loadingToast: null | ||
| 83 | + } | ||
| 84 | + }, | ||
| 85 | + computed: { | ||
| 86 | + keepAlivePageNameList() { | ||
| 87 | + return this.$router.options.routes | ||
| 88 | + .filter(item => { | ||
| 89 | + return item.meta.keepAlive | ||
| 90 | + }) | ||
| 91 | + .map(item => { | ||
| 92 | + return item.name | ||
| 93 | + }) | ||
| 94 | + }, | ||
| 95 | + tabBarList() { | ||
| 96 | + return this.$router.options.routes | ||
| 97 | + .filter(item => { | ||
| 98 | + return item.meta.isTabBarMenu | ||
| 99 | + }) | ||
| 100 | + .map(item => { | ||
| 101 | + return { | ||
| 102 | + name: item.name, | ||
| 103 | + icon: item.meta.icon, | ||
| 104 | + label: item.meta.title | ||
| 105 | + } | ||
| 106 | + }) | ||
| 107 | + } | ||
| 108 | + }, | ||
| 109 | + watch: { | ||
| 110 | + $route(newVal) { | ||
| 111 | + if (this.getIsApp) { | ||
| 112 | + this.$store.commit('setAppNavBarShow', newVal.meta.showNavBar) | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + }, | ||
| 116 | + created() { | ||
| 117 | + if (this.getIsApp) { | ||
| 118 | + this.$store.commit('setAppNavBarShow', this.$route.meta.showNavBar) | ||
| 119 | + // 监听App端传过来的系统信息 | ||
| 120 | + webf.methodChannel.addMethodCallHandler('postSystemInfo', event => { | ||
| 121 | + console.log('event:', event) | ||
| 122 | + this.systemInfo = event | ||
| 123 | + this.$store.commit('setSystemInfo', event) | ||
| 124 | + }) | ||
| 125 | + // 监听下载进度 | ||
| 126 | + webf.methodChannel.addMethodCallHandler('updateVersionProgress', event => { | ||
| 127 | + this.loadingToast.ylTotasHintText = `下载中 ${event.toFixed(2)}%` | ||
| 128 | + if (event === 100) { | ||
| 129 | + this.loadingToast.ylTotasOpen = false | ||
| 130 | + this.dialogOk = this.updateVerOkRestart | ||
| 131 | + this.myDialogConfig.title = '下载完成' | ||
| 132 | + this.myDialogConfig.description = '重启App即可更新' | ||
| 133 | + this.myDialogConfig.cancelBtnText = '稍后重启' | ||
| 134 | + this.dialogOpen = true | ||
| 135 | + } | ||
| 136 | + }) | ||
| 137 | + } | ||
| 138 | + }, | ||
| 139 | + async mounted() { | ||
| 140 | + setTimeout(() => { | ||
| 141 | + this.currentTabBarName = this.$route.name | ||
| 142 | + }, 60) | ||
| 143 | + if (this.getIsApp) { | ||
| 144 | + // 如果是APP,需要检查版本号判断有没有更新 | ||
| 145 | + try { | ||
| 146 | + let res = await getVersionApi() | ||
| 147 | + let oldVersion = versionFormatting(this.systemInfo.version) | ||
| 148 | + let newVersion = versionFormatting(res.currentVersion) | ||
| 149 | + this.newVersion = res.currentVersion | ||
| 150 | + if (newVersion - oldVersion > 0) { | ||
| 151 | + this.myDialogConfig.title = '更新提示' | ||
| 152 | + this.myDialogConfig.description = res.description | ||
| 153 | + this.dialogOpen = true | ||
| 154 | + } | ||
| 155 | + } catch (error) { | ||
| 156 | + console.log('error:', error) | ||
| 157 | + } | ||
| 158 | + } | ||
| 159 | + }, | ||
| 160 | + methods: { | ||
| 161 | + // tabBar切换 | ||
| 162 | + tabBarChange(event) { | ||
| 163 | + this.$router.replace({ | ||
| 164 | + name: event.name | ||
| 165 | + }) | ||
| 166 | + }, | ||
| 167 | + backPrePage() { | ||
| 168 | + console.log('返回:') | ||
| 169 | + this.$router.go(-1) | ||
| 170 | + }, | ||
| 171 | + othersBtnFun() { | ||
| 172 | + console.log('this.getAppNavBarShow:', this.getAppNavBarShow) | ||
| 173 | + }, | ||
| 174 | + dialogCancel() { | ||
| 175 | + this.rightBtnText = '立即更新' | ||
| 176 | + }, | ||
| 177 | + dialogOk() { | ||
| 178 | + webf.methodChannel.invokeMethod('updateVersionDownloadConfirm', { | ||
| 179 | + msg: '确定更新', | ||
| 180 | + newVersion: this.newVersion | ||
| 181 | + }) | ||
| 182 | + this.dialogOpen = false | ||
| 183 | + this.loadingToast = this.$refs.myToast.loading({ | ||
| 184 | + hintText: '下载中', | ||
| 185 | + duration: 0, | ||
| 186 | + isOpen: true | ||
| 187 | + }) | ||
| 188 | + }, | ||
| 189 | + // 确定重启更新-触发flutter中webf监听的事件 | ||
| 190 | + updateVerOkRestart() { | ||
| 191 | + webf.methodChannel.invokeMethod('updateVersionRestart', { | ||
| 192 | + msg: '确定更新重启' | ||
| 193 | + }) | ||
| 194 | + } | ||
| 195 | + } | ||
| 196 | +} | ||
| 197 | +</script> | ||
| 198 | + | ||
| 199 | +<style lang="scss"> | ||
| 200 | +@import '@/assets/style/iconfont/iconfont.css'; | ||
| 201 | +@import '@/assets/style/vant-icon/vant-icon.css'; | ||
| 202 | +// @import '@/assets/style/common.scss'; | ||
| 203 | + | ||
| 204 | +* { | ||
| 205 | + margin: 0; | ||
| 206 | + padding: 0; | ||
| 207 | + box-sizing: border-box; | ||
| 208 | +} | ||
| 209 | +.router-page-view { | ||
| 210 | + overflow-x: hidden; | ||
| 211 | + &.is-tab-bar-menu-page { | ||
| 212 | + padding-bottom: 54px; | ||
| 213 | + } | ||
| 214 | +} | ||
| 215 | +#app { | ||
| 216 | + overflow-x: hidden; | ||
| 217 | + .my-tabbar-view { | ||
| 218 | + position: fixed; | ||
| 219 | + left: 0; | ||
| 220 | + bottom: 0; | ||
| 221 | + z-index: 999; | ||
| 222 | + width: 100%; | ||
| 223 | + } | ||
| 224 | +} | ||
| 225 | +</style> |
src/apis/commApis.js
0 → 100644
src/assets/style/common.scss
0 → 100644
| 1 | +/* 多行行文本溢出出现省略号 */ | ||
| 2 | +.clamp { | ||
| 3 | + overflow: hidden; | ||
| 4 | + text-overflow: ellipsis; | ||
| 5 | + display: -webkit-box; | ||
| 6 | + -webkit-box-orient: vertical; | ||
| 7 | +} | ||
| 8 | + | ||
| 9 | +.clamp.clamp-a { | ||
| 10 | + -webkit-line-clamp: 1; | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +.clamp.clamp-b { | ||
| 14 | + -webkit-line-clamp: 2; | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +.clamp.clamp-c { | ||
| 18 | + -webkit-line-clamp: 3; | ||
| 19 | +} | ||
| 20 | + | ||
| 21 | +/* 开启硬件加速 */ | ||
| 22 | +.threeD { | ||
| 23 | + transform: translateZ(0); | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +.anim { | ||
| 27 | + transition: all 0.4s; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +@keyframes turn { | ||
| 31 | + 0% { | ||
| 32 | + transform: rotate(0deg); | ||
| 33 | + } | ||
| 34 | + 25% { | ||
| 35 | + transform: rotate(90deg); | ||
| 36 | + } | ||
| 37 | + 50% { | ||
| 38 | + transform: rotate(180deg); | ||
| 39 | + } | ||
| 40 | + 75% { | ||
| 41 | + transform: rotate(270deg); | ||
| 42 | + } | ||
| 43 | + 100% { | ||
| 44 | + transform: rotate(360deg); | ||
| 45 | + } | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +@keyframes flicker { | ||
| 49 | + 0% { | ||
| 50 | + transform: scale(1); /*开始为原始大小*/ | ||
| 51 | + } | ||
| 52 | + 25% { | ||
| 53 | + transform: scale(1.1); /*放大1.1倍*/ | ||
| 54 | + } | ||
| 55 | + 50% { | ||
| 56 | + transform: scale(1); | ||
| 57 | + } | ||
| 58 | + 75% { | ||
| 59 | + transform: scale(1.1); | ||
| 60 | + } | ||
| 61 | +} | ||
| 62 | +.flicker { | ||
| 63 | + animation: flicker 2s infinite; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +@keyframes spin { | ||
| 67 | + to { | ||
| 68 | + transform: rotate(0deg); | ||
| 69 | + } | ||
| 70 | + from { | ||
| 71 | + transform: rotate(-360deg); | ||
| 72 | + } | ||
| 73 | +} | ||
| 74 | + | ||
| 75 | +.spin { | ||
| 76 | + animation: spin 1s infinite; | ||
| 77 | + animation-fill-mode: forwards; | ||
| 78 | +} | ||
| 79 | + | ||
| 80 | +.turn { | ||
| 81 | + animation: turn 1s infinite; | ||
| 82 | + animation-fill-mode: forwards; | ||
| 83 | +} | ||
| 84 | + | ||
| 85 | +.transform90 { | ||
| 86 | + transform: rotate(90deg); | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +.pos-rel { | ||
| 90 | + position: relative; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +.pos-abs { | ||
| 94 | + position: absolute; | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +.text-l { | ||
| 98 | + text-align: left !important; | ||
| 99 | +} | ||
| 100 | + | ||
| 101 | +.text-r { | ||
| 102 | + text-align: right !important; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +.text-c { | ||
| 106 | + text-align: center !important; | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +.left { | ||
| 110 | + float: left; | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +.right { | ||
| 114 | + float: right; | ||
| 115 | +} | ||
| 116 | + | ||
| 117 | +.clear:after { | ||
| 118 | + display: block; | ||
| 119 | + content: '.'; | ||
| 120 | + clear: both; | ||
| 121 | + visibility: hidden; | ||
| 122 | + overflow: hidden; | ||
| 123 | + width: 100%; | ||
| 124 | + height: 0; | ||
| 125 | +} | ||
| 126 | + | ||
| 127 | +.clear { | ||
| 128 | + clear: both; | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +.flex { | ||
| 132 | + display: flex; | ||
| 133 | +} | ||
| 134 | +.al-i-c { | ||
| 135 | + align-items: center; | ||
| 136 | +} | ||
| 137 | +.ju-c-sb { | ||
| 138 | + justify-content: space-between; | ||
| 139 | +} | ||
| 140 | +.ju-c-sa { | ||
| 141 | + justify-content: space-around; | ||
| 142 | +} | ||
| 143 | +.ju-c-c { | ||
| 144 | + justify-content: center; | ||
| 145 | +} | ||
| 146 | +.ju-c-end { | ||
| 147 | + justify-content: flex-end; | ||
| 148 | +} | ||
| 149 | +@for $i from 1 through 50 { | ||
| 150 | + .p-#{$i} { | ||
| 151 | + padding: (1px * $i) !important; | ||
| 152 | + } | ||
| 153 | + .p-t-#{$i} { | ||
| 154 | + padding-top: (1px * $i) !important; | ||
| 155 | + } | ||
| 156 | + .p-r-#{$i} { | ||
| 157 | + padding-right: (1px * $i) !important; | ||
| 158 | + } | ||
| 159 | + .p-b-#{$i} { | ||
| 160 | + padding-bottom: (1px * $i) !important; | ||
| 161 | + } | ||
| 162 | + .p-l-#{$i} { | ||
| 163 | + padding-left: (1px * $i) !important; | ||
| 164 | + } | ||
| 165 | + .m-#{$i} { | ||
| 166 | + margin: (1px * $i) !important; | ||
| 167 | + } | ||
| 168 | + .m-t-#{$i} { | ||
| 169 | + margin-top: (1px * $i) !important; | ||
| 170 | + } | ||
| 171 | + .m-r-#{$i} { | ||
| 172 | + margin-right: (1px * $i) !important; | ||
| 173 | + } | ||
| 174 | + .m-b-#{$i} { | ||
| 175 | + margin-bottom: (1px * $i) !important; | ||
| 176 | + } | ||
| 177 | + .m-l-#{$i} { | ||
| 178 | + margin-left: (1px * $i) !important; | ||
| 179 | + } | ||
| 180 | + .fz-#{$i} { | ||
| 181 | + font-size: (1px * $i) !important; | ||
| 182 | + } | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +.text-line-thr { | ||
| 186 | + text-decoration: line-through !important; | ||
| 187 | +} |
src/assets/style/iconfont/iconfont.css
0 → 100644
| 1 | +@font-face { | ||
| 2 | + font-family: 'iconfont'; | ||
| 3 | + src: url('iconfont.ttf?t=1687638242320') format('truetype'); | ||
| 4 | +} | ||
| 5 | + | ||
| 6 | +.iconfont { | ||
| 7 | + font-family: 'iconfont' !important; | ||
| 8 | + font-size: 16px; | ||
| 9 | + font-style: normal; | ||
| 10 | + -webkit-font-smoothing: antialiased; | ||
| 11 | + -moz-osx-font-smoothing: grayscale; | ||
| 12 | +} | ||
| 13 | + | ||
| 14 | +.icon-kaobei:before { | ||
| 15 | + content: '\e601'; | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +.icon-shezhi:before { | ||
| 19 | + content: '\e827'; | ||
| 20 | +} | ||
| 21 | + | ||
| 22 | +.icon-kefu:before { | ||
| 23 | + content: '\e642'; | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +.icon-loading-1:before { | ||
| 27 | + content: '\e61d'; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +.icon-loading-2:before { | ||
| 31 | + content: '\e93d'; | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +.icon-loading-3:before { | ||
| 35 | + content: '\e67b'; | ||
| 36 | +} |
src/assets/style/iconfont/iconfont.ttf
0 → 100644
不能预览此文件类型
src/assets/style/vant-icon/vant-icon.css
0 → 100644
src/assets/style/vant-icon/vant-icon.ttf
0 → 100644
不能预览此文件类型
src/components/Collapse/Collapse.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="collapse-com" :style="{ backgroundColor: backgroundColor }"> | ||
| 3 | + <slot></slot> | ||
| 4 | + </div> | ||
| 5 | +</template> | ||
| 6 | + | ||
| 7 | +<script> | ||
| 8 | +export default { | ||
| 9 | + name: 'Collapse', | ||
| 10 | + props: { | ||
| 11 | + value: [String, Array, Number], | ||
| 12 | + accordion: { | ||
| 13 | + type: Boolean, | ||
| 14 | + default: () => { | ||
| 15 | + return false | ||
| 16 | + } | ||
| 17 | + }, | ||
| 18 | + backgroundColor: { | ||
| 19 | + type: String, | ||
| 20 | + default: '#f5f5f5' | ||
| 21 | + } | ||
| 22 | + }, | ||
| 23 | + data() { | ||
| 24 | + return { | ||
| 25 | + currentValue: this.value | ||
| 26 | + } | ||
| 27 | + }, | ||
| 28 | + mounted() { | ||
| 29 | + this.setActive() | ||
| 30 | + }, | ||
| 31 | + methods: { | ||
| 32 | + setActive() { | ||
| 33 | + // 为它下面的子元素都设置一个index值 | ||
| 34 | + // console.log("setActive"); | ||
| 35 | + const activeKey = this.getActiveKey() | ||
| 36 | + this.$children.forEach((child, index) => { | ||
| 37 | + const name = child.name || index.toString() // toString 1=>"1"整数转换成为字符串 | ||
| 38 | + child.isActive = activeKey.indexOf(name) > -1 // 给选中的元素赋值活跃状态 | ||
| 39 | + // console.log(child); | ||
| 40 | + child.index = index | ||
| 41 | + }) | ||
| 42 | + }, | ||
| 43 | + toggle(data) { | ||
| 44 | + // console.log("toggle"); | ||
| 45 | + const name = data.name.toString() // 强行转换成为字符串 | ||
| 46 | + let newActivekey = [] | ||
| 47 | + if (this.accordion) { | ||
| 48 | + // 如果是手风琴模式 | ||
| 49 | + if (!data.isActive) { | ||
| 50 | + newActivekey.push(name) | ||
| 51 | + } | ||
| 52 | + } else { | ||
| 53 | + let activeKey = this.getActiveKey() | ||
| 54 | + const nameIndex = activeKey.indexOf(name) | ||
| 55 | + if (data.isActive) { | ||
| 56 | + // 如果当前是展开状态 | ||
| 57 | + if (nameIndex > -1) { | ||
| 58 | + activeKey.splice(nameIndex, 1) | ||
| 59 | + } | ||
| 60 | + } else { | ||
| 61 | + if (nameIndex < 0) { | ||
| 62 | + activeKey.push(name) | ||
| 63 | + } | ||
| 64 | + } | ||
| 65 | + newActivekey = activeKey | ||
| 66 | + } | ||
| 67 | + this.currentValue = newActivekey | ||
| 68 | + // console.log(data); | ||
| 69 | + this.$emit('input', newActivekey) | ||
| 70 | + this.$emit('on-change', newActivekey) | ||
| 71 | + }, | ||
| 72 | + getActiveKey() { | ||
| 73 | + // 获取当前展开的元素,并且做成数组的形式 1 => ["1"] | ||
| 74 | + let activeKey = this.currentValue || [] | ||
| 75 | + const accordion = this.accordion | ||
| 76 | + if (!Array.isArray(activeKey)) { | ||
| 77 | + // 判断 activeKey 是不是数组 | ||
| 78 | + activeKey = [activeKey] // 不是数组则让它变成数组 | ||
| 79 | + } | ||
| 80 | + if (accordion && activeKey.length > 1) { | ||
| 81 | + // 如果是手风琴模式,必定是只会有一个元素 | ||
| 82 | + activeKey = [activeKey[0]] | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + for (let i = 0; i < activeKey.length; i++) { | ||
| 86 | + activeKey[i] = activeKey[i].toString() | ||
| 87 | + } | ||
| 88 | + return activeKey | ||
| 89 | + } | ||
| 90 | + }, | ||
| 91 | + watch: { | ||
| 92 | + value(val) { | ||
| 93 | + this.currentValue = val | ||
| 94 | + }, | ||
| 95 | + currentValue() { | ||
| 96 | + this.setActive() | ||
| 97 | + } | ||
| 98 | + } | ||
| 99 | +} | ||
| 100 | +</script> | ||
| 101 | + | ||
| 102 | +<style lang="scss" scoped></style> |
src/components/Collapse/CollapseItem.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="collapse-item-com" :style="{ marginBottom: marginBottom + 'px' }"> | ||
| 3 | + <div class="title-view" @click="toggle"> | ||
| 4 | + <div class="title-str"> | ||
| 5 | + <slot v-if="$slots.leftIcon" name="leftIcon"></slot> | ||
| 6 | + <span v-else class="iconfont left-icon" :class="leftIcon"></span> | ||
| 7 | + <slot v-if="$slots.title" name="title"></slot> | ||
| 8 | + <span v-else class="title-text">{{ title }}</span> | ||
| 9 | + </div> | ||
| 10 | + <slot v-if="$slots.rightIcon" name="rightIcon"></slot> | ||
| 11 | + <span v-else class="iconfont right-icon" :class="_rightIcon"></span> | ||
| 12 | + </div> | ||
| 13 | + <div class="collapse-extra" :style="{ height: isActive ? height + 'px' : '0' }" ref="collapseExtra"> | ||
| 14 | + <slot v-if="$slots.collapseExtra" name="collapseExtra"></slot> | ||
| 15 | + </div> | ||
| 16 | + </div> | ||
| 17 | +</template> | ||
| 18 | + | ||
| 19 | +<script> | ||
| 20 | +export default { | ||
| 21 | + name: 'CollapseItem', | ||
| 22 | + props: { | ||
| 23 | + name: { | ||
| 24 | + type: String | ||
| 25 | + }, | ||
| 26 | + title: { | ||
| 27 | + type: String, | ||
| 28 | + default: null | ||
| 29 | + }, | ||
| 30 | + leftIcon: { | ||
| 31 | + type: String, | ||
| 32 | + default: null | ||
| 33 | + }, | ||
| 34 | + rightIcon: { | ||
| 35 | + type: String, | ||
| 36 | + default: null | ||
| 37 | + }, | ||
| 38 | + marginBottom: { | ||
| 39 | + type: [String, Number] | ||
| 40 | + } | ||
| 41 | + }, | ||
| 42 | + data() { | ||
| 43 | + return { | ||
| 44 | + index: 0, | ||
| 45 | + height: 0, | ||
| 46 | + isActive: false | ||
| 47 | + } | ||
| 48 | + }, | ||
| 49 | + computed: { | ||
| 50 | + _rightIcon() { | ||
| 51 | + if (this.rightIcon) { | ||
| 52 | + return this.rightIcon | ||
| 53 | + } else { | ||
| 54 | + if (this.isActive) { | ||
| 55 | + return 'icon-xiangshang2' | ||
| 56 | + } else { | ||
| 57 | + return 'icon-xiangxia2' | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + } | ||
| 61 | + }, | ||
| 62 | + methods: { | ||
| 63 | + toggle() { | ||
| 64 | + this.$parent.toggle({ | ||
| 65 | + name: this.name || this.index, | ||
| 66 | + isActive: this.isActive | ||
| 67 | + }) | ||
| 68 | + } | ||
| 69 | + }, | ||
| 70 | + mounted() { | ||
| 71 | + this.$nextTick(() => { | ||
| 72 | + this.height = this.$refs.collapseExtra.childNodes[0].offsetHeight | ||
| 73 | + }) | ||
| 74 | + } | ||
| 75 | +} | ||
| 76 | +</script> | ||
| 77 | + | ||
| 78 | +<style lang="scss" scoped> | ||
| 79 | +.collapse-item-com { | ||
| 80 | + background-color: #fff; | ||
| 81 | + .title-view { | ||
| 82 | + display: flex; | ||
| 83 | + align-items: center; | ||
| 84 | + justify-content: space-between; | ||
| 85 | + padding: 10px; | ||
| 86 | + border-bottom: 1px solid #f5f5f5; | ||
| 87 | + .title-str { | ||
| 88 | + .left-icon { | ||
| 89 | + color: #666; | ||
| 90 | + font-size: 12px; | ||
| 91 | + } | ||
| 92 | + .title-text { | ||
| 93 | + font-size: 16px; | ||
| 94 | + color: #333; | ||
| 95 | + } | ||
| 96 | + } | ||
| 97 | + .right-icon { | ||
| 98 | + color: #666; | ||
| 99 | + font-size: 12px; | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + .collapse-extra { | ||
| 103 | + height: 0; | ||
| 104 | + will-change: 'height'; | ||
| 105 | + overflow: hidden; | ||
| 106 | + transition: all 0.2s ease 0s; | ||
| 107 | + } | ||
| 108 | +} | ||
| 109 | +</style> |
src/components/MyConsole/MyConsole.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="my-console-view" :class="[myPopupOpen ? 'show' : '']"> | ||
| 3 | + <!-- <vue-drag-resize | ||
| 4 | + v-if="!getIsApp" | ||
| 5 | + :w="81" | ||
| 6 | + :h="32" | ||
| 7 | + :x="230" | ||
| 8 | + :y="400" | ||
| 9 | + :isResizable="false" | ||
| 10 | + :isActive="dragActive" | ||
| 11 | + :parentLimitation="true" | ||
| 12 | + @dragging="onDragging" | ||
| 13 | + @activated="onClicked" | ||
| 14 | + @deactivated="onDeactivated" | ||
| 15 | + @dragstop="onDragStop" | ||
| 16 | + > | ||
| 17 | + <van-button class="open-console-btn" size="small" type="primary" @click.stop="_opeConsole">MyConsole</van-button> | ||
| 18 | + </vue-drag-resize> --> | ||
| 19 | + <van-button v-if="!getIsApp" class="open-console-btn not-app" size="small" type="primary" @click.stop="_opeConsole">MyConsole</van-button> | ||
| 20 | + | ||
| 21 | + <MyPopup v-model="myPopupOpen" position="bottom" height="60" :theme="theme" content-bg-color="#1E1E20" :round="false" :show-close-icon="true" popup-title="控制台"> | ||
| 22 | + <div class="console-content"> | ||
| 23 | + <div class="tab-list" :class="`theme-${theme}`"> | ||
| 24 | + <div class="tab-item" v-for="(item, index) in tabList" :key="index" :class="{ 'current-item': current === item.value }" @click="changeTab(item.value)"> | ||
| 25 | + {{ item.name }} | ||
| 26 | + </div> | ||
| 27 | + </div> | ||
| 28 | + <div class="all-cell" ref="allCell"> | ||
| 29 | + <template v-if="current !== 'network'"> | ||
| 30 | + <div class="cell-item" :class="`theme-${theme}`" v-for="(item, index) in consoleList" :key="index"> | ||
| 31 | + <template v-for="(oItem, oIndex) in item.data"> | ||
| 32 | + <div class="log-item" :class="oItem.logType === 'log' ? 'log' : oItem.logType === 'error' ? 'error' : ''" :key="oIndex">{{ oItem.log }}</div> | ||
| 33 | + </template> | ||
| 34 | + </div> | ||
| 35 | + </template> | ||
| 36 | + <template v-else-if="current === 'network'"> | ||
| 37 | + <div class="cell-item network-cell-item network-cell-header" :class="`theme-${theme}`"> | ||
| 38 | + <div class="network-item">Name</div> | ||
| 39 | + <div class="network-item">Type</div> | ||
| 40 | + <div class="network-item">Status</div> | ||
| 41 | + <div class="network-item">Size</div> | ||
| 42 | + <div class="network-item">Time</div> | ||
| 43 | + </div> | ||
| 44 | + <div style="padding-top: 34px; height: 100%; overflow-y: auto"> | ||
| 45 | + <div v-for="(item, index) in consoleList" class="cell-item network-cell-item" :class="[`theme-${theme}`]" :key="index" @click="lookNetworkDetail(item)"> | ||
| 46 | + <div class="network-item">{{ item.name }}</div> | ||
| 47 | + <div class="network-item">{{ item.type }}</div> | ||
| 48 | + <div class="network-item">{{ item.status }}</div> | ||
| 49 | + <div class="network-item">{{ item.size }}</div> | ||
| 50 | + <div class="network-item">{{ item.time }} ms</div> | ||
| 51 | + </div> | ||
| 52 | + </div> | ||
| 53 | + </template> | ||
| 54 | + </div> | ||
| 55 | + <MyPopup | ||
| 56 | + v-model="openApiDetailPopup" | ||
| 57 | + position="right" | ||
| 58 | + height="60" | ||
| 59 | + width="70" | ||
| 60 | + bottom="0" | ||
| 61 | + content-bg-color="#1E1E20" | ||
| 62 | + :theme="theme" | ||
| 63 | + :round="false" | ||
| 64 | + :show-close-icon="true" | ||
| 65 | + popup-title="接口详情" | ||
| 66 | + > | ||
| 67 | + <div class="console-content" v-if="currentApiDetail"> | ||
| 68 | + <div class="all-cell detail-cell"> | ||
| 69 | + <van-cell title="名称" :label="currentApiDetail.name" /> | ||
| 70 | + <van-cell title="类型" :label="currentApiDetail.type" /> | ||
| 71 | + <van-cell class="pos-rel" title="地址" :label="currentApiDetail.responseURL"> | ||
| 72 | + <template #right-icon> | ||
| 73 | + <span class="pos-abs iconfont icon-kaobei"></span> | ||
| 74 | + </template> | ||
| 75 | + </van-cell> | ||
| 76 | + <van-cell title="状态" :label="currentApiDetail.status" /> | ||
| 77 | + <van-cell class="pos-rel" title="返回结果" style="white-space: pre" :label="currentApiDetail.response | prettyPrint"> | ||
| 78 | + <template #right-icon> | ||
| 79 | + <span class="pos-abs iconfont icon-kaobei"></span> | ||
| 80 | + </template> | ||
| 81 | + </van-cell> | ||
| 82 | + </div> | ||
| 83 | + </div> | ||
| 84 | + </MyPopup> | ||
| 85 | + </div> | ||
| 86 | + </MyPopup> | ||
| 87 | + </div> | ||
| 88 | +</template> | ||
| 89 | + | ||
| 90 | +<script> | ||
| 91 | +import { abilitySort } from '@/libs/tools' | ||
| 92 | +import MyPopup from '@/components/MyPopup/MyPopup.vue' | ||
| 93 | +export default { | ||
| 94 | + name: 'MyConsole', | ||
| 95 | + components: { MyPopup }, | ||
| 96 | + props: { | ||
| 97 | + /** | ||
| 98 | + * 主题色 | ||
| 99 | + * light | dark | ||
| 100 | + */ | ||
| 101 | + theme: { | ||
| 102 | + type: String, | ||
| 103 | + default: 'light' | ||
| 104 | + } | ||
| 105 | + }, | ||
| 106 | + data() { | ||
| 107 | + return { | ||
| 108 | + myPopupOpen: false, | ||
| 109 | + consoleList: [], | ||
| 110 | + dragActive: false, | ||
| 111 | + top: 0, | ||
| 112 | + left: 0, | ||
| 113 | + sT: null, | ||
| 114 | + draggNum: 0, | ||
| 115 | + tabList: [ | ||
| 116 | + { name: 'All', value: 'all' }, | ||
| 117 | + { name: 'Log', value: 'log' }, | ||
| 118 | + { name: 'Error', value: 'error' }, | ||
| 119 | + { name: 'Network', value: 'network' } | ||
| 120 | + ], | ||
| 121 | + current: 'all', | ||
| 122 | + currentApiDetail: null, | ||
| 123 | + openApiDetailPopup: false, | ||
| 124 | + global: this.$global | ||
| 125 | + } | ||
| 126 | + }, | ||
| 127 | + watch: { | ||
| 128 | + getLoggerData() { | ||
| 129 | + this.changeTab(this.current) | ||
| 130 | + }, | ||
| 131 | + getNetworkLoggerData() { | ||
| 132 | + this.changeTab(this.current) | ||
| 133 | + }, | ||
| 134 | + myPopupOpen(newVal) { | ||
| 135 | + if (!newVal) { | ||
| 136 | + this.consoleList = [] | ||
| 137 | + } | ||
| 138 | + }, | ||
| 139 | + draggNum(newVal) { | ||
| 140 | + if (newVal > 1) { | ||
| 141 | + if (this.sT) { | ||
| 142 | + clearTimeout(this.sT) | ||
| 143 | + this.sT = null | ||
| 144 | + } | ||
| 145 | + } | ||
| 146 | + } | ||
| 147 | + }, | ||
| 148 | + computed: { | ||
| 149 | + getLoggerData() { | ||
| 150 | + return this.$store.getters.getLoggerData | ||
| 151 | + }, | ||
| 152 | + getNetworkLoggerData() { | ||
| 153 | + return this.$store.getters.getNetworkLoggerData | ||
| 154 | + } | ||
| 155 | + }, | ||
| 156 | + created() { | ||
| 157 | + if (this.getIsApp) { | ||
| 158 | + /** | ||
| 159 | + * @description: 监听打开调试窗口 | ||
| 160 | + * @return {*} | ||
| 161 | + */ | ||
| 162 | + webf.methodChannel.addMethodCallHandler('openMyConsole', event => { | ||
| 163 | + this._opeConsole() | ||
| 164 | + }) | ||
| 165 | + /** | ||
| 166 | + * @description: 监听请求拦截器新增请求 | ||
| 167 | + * @return {*} | ||
| 168 | + */ | ||
| 169 | + webf.methodChannel.addMethodCallHandler('addHttpInterceptors', event => { | ||
| 170 | + console.log('addHttpInterceptors:', event) | ||
| 171 | + console.netwokrLog({ | ||
| 172 | + uid: JSON.parse(event.headers).uid, | ||
| 173 | + status: event.status || 0, | ||
| 174 | + statusText: event.statusText || '', | ||
| 175 | + name: event.url.split('/').pop().split('?')[0], | ||
| 176 | + type: event.type, | ||
| 177 | + requestHeaders: event.headers, | ||
| 178 | + method: event.method, | ||
| 179 | + responseURL: event.url, | ||
| 180 | + response: null, | ||
| 181 | + size: event.size || 0, | ||
| 182 | + time: 0 | ||
| 183 | + }) | ||
| 184 | + }) | ||
| 185 | + // 监听请求拦截器更新请求 | ||
| 186 | + webf.methodChannel.addMethodCallHandler('updateHttpInterceptors', event => { | ||
| 187 | + console.log('updateHttpInterceptors:', event) | ||
| 188 | + let updateItem = this.$global.cNetworkData.filter(item => { | ||
| 189 | + return item.uid === event.uid | ||
| 190 | + })[0] | ||
| 191 | + if (updateItem) { | ||
| 192 | + Object.assign(updateItem, event) | ||
| 193 | + // 触发获取返回结果 | ||
| 194 | + if (updateItem.type === 'Fetch/XHR') { | ||
| 195 | + webf.methodChannel.invokeMethod('getResponseBody', event.uid) | ||
| 196 | + } | ||
| 197 | + } | ||
| 198 | + }) | ||
| 199 | + // 监听请求拦截器更新返回结果事件 | ||
| 200 | + webf.methodChannel.addMethodCallHandler('httpInterceptorsSetResponseBody', event => { | ||
| 201 | + console.log('httpInterceptorsSetResponseBody:', event) | ||
| 202 | + let updateItem = this.$global.cNetworkData.filter(item => { | ||
| 203 | + return item.uid === event.uid | ||
| 204 | + })[0] | ||
| 205 | + if (updateItem) { | ||
| 206 | + updateItem.response = event.response | ||
| 207 | + } | ||
| 208 | + }) | ||
| 209 | + } | ||
| 210 | + }, | ||
| 211 | + methods: { | ||
| 212 | + _opeConsole() { | ||
| 213 | + this.changeTab(this.current) | ||
| 214 | + this.myPopupOpen = true | ||
| 215 | + }, | ||
| 216 | + onDragging(newRect) { | ||
| 217 | + this.draggNum += 1 | ||
| 218 | + this.top = newRect.top | ||
| 219 | + this.left = newRect.left | ||
| 220 | + }, | ||
| 221 | + onClicked() { | ||
| 222 | + this.sT = setTimeout(() => { | ||
| 223 | + this._opeConsole() | ||
| 224 | + }, 200) | ||
| 225 | + this.dragActive = true | ||
| 226 | + }, | ||
| 227 | + onDeactivated() { | ||
| 228 | + this.draggNum = 0 | ||
| 229 | + this.dragActive = false | ||
| 230 | + }, | ||
| 231 | + onDragStop() { | ||
| 232 | + this.dragActive = false | ||
| 233 | + }, | ||
| 234 | + changeTab(value) { | ||
| 235 | + this.current = value | ||
| 236 | + let temp = [] | ||
| 237 | + if (value === 'all') { | ||
| 238 | + temp = JSON.parse(JSON.stringify(this.getLoggerData)) | ||
| 239 | + } else if (value === 'log') { | ||
| 240 | + temp = JSON.parse(JSON.stringify(this.getLoggerData)).filter(item => { | ||
| 241 | + return item.logType === 'log' | ||
| 242 | + }) | ||
| 243 | + } else if (value === 'error') { | ||
| 244 | + temp = JSON.parse(JSON.stringify(this.getLoggerData)).filter(item => { | ||
| 245 | + return item.logType === 'error' | ||
| 246 | + }) | ||
| 247 | + } else if (value === 'network') { | ||
| 248 | + temp = JSON.parse(JSON.stringify(this.getNetworkLoggerData)) | ||
| 249 | + this.consoleList = temp | ||
| 250 | + return | ||
| 251 | + } | ||
| 252 | + this.consoleList = abilitySort(temp, 'logId') | ||
| 253 | + this.$nextTick(() => { | ||
| 254 | + this.scrollToBottom() | ||
| 255 | + }) | ||
| 256 | + }, | ||
| 257 | + // 滚动到最底部 | ||
| 258 | + scrollToBottom() { | ||
| 259 | + try { | ||
| 260 | + let allCell = this.$refs['allCell'] | ||
| 261 | + allCell.scrollTop = allCell.scrollHeight | ||
| 262 | + } catch (error) {} | ||
| 263 | + }, | ||
| 264 | + // 查看接口详情 | ||
| 265 | + lookNetworkDetail(detail) { | ||
| 266 | + this.currentApiDetail = detail | ||
| 267 | + this.openApiDetailPopup = true | ||
| 268 | + } | ||
| 269 | + } | ||
| 270 | +} | ||
| 271 | +</script> | ||
| 272 | + | ||
| 273 | +<style lang="scss" scoped> | ||
| 274 | +.my-console-view { | ||
| 275 | + position: fixed; | ||
| 276 | + top: 0; | ||
| 277 | + left: 0; | ||
| 278 | + z-index: 1000; | ||
| 279 | + // pointer-events: none; | ||
| 280 | + | ||
| 281 | + .console-content { | ||
| 282 | + position: relative; | ||
| 283 | + height: 100%; | ||
| 284 | + .all-cell { | ||
| 285 | + padding-top: 34px; | ||
| 286 | + height: 100%; | ||
| 287 | + overflow-y: auto; | ||
| 288 | + position: relative; | ||
| 289 | + &.detail-cell { | ||
| 290 | + padding-top: 0; | ||
| 291 | + .iconfont { | ||
| 292 | + right: 20px; | ||
| 293 | + } | ||
| 294 | + } | ||
| 295 | + } | ||
| 296 | + .van-cell__title { | ||
| 297 | + width: 100%; | ||
| 298 | + .van-cell__label { | ||
| 299 | + width: 100%; | ||
| 300 | + overflow-x: auto; | ||
| 301 | + } | ||
| 302 | + } | ||
| 303 | + } | ||
| 304 | + .tab-list { | ||
| 305 | + width: 100%; | ||
| 306 | + height: 34px; | ||
| 307 | + display: flex; | ||
| 308 | + align-items: center; | ||
| 309 | + justify-content: start; | ||
| 310 | + position: absolute; | ||
| 311 | + left: 0; | ||
| 312 | + top: 0; | ||
| 313 | + z-index: 100; | ||
| 314 | + .tab-item { | ||
| 315 | + height: 100%; | ||
| 316 | + display: flex; | ||
| 317 | + align-items: center; | ||
| 318 | + padding: 0 10px; | ||
| 319 | + } | ||
| 320 | + &.theme-light { | ||
| 321 | + color: #333; | ||
| 322 | + border: 1px solid #eee; | ||
| 323 | + background-color: #fff; | ||
| 324 | + .tab-item { | ||
| 325 | + border-right: 1px solid #eee; | ||
| 326 | + } | ||
| 327 | + } | ||
| 328 | + &.theme-dark { | ||
| 329 | + color: #a9b0b9; | ||
| 330 | + border: 1px solid #333; | ||
| 331 | + border-top: 0; | ||
| 332 | + background-color: #252528; | ||
| 333 | + .tab-item { | ||
| 334 | + border-right: 1px solid #333; | ||
| 335 | + &.current-item { | ||
| 336 | + background-color: #000; | ||
| 337 | + color: #fff; | ||
| 338 | + } | ||
| 339 | + } | ||
| 340 | + } | ||
| 341 | + } | ||
| 342 | + .cell-item { | ||
| 343 | + min-height: 44px; | ||
| 344 | + display: flex; | ||
| 345 | + flex-direction: column; | ||
| 346 | + align-items: start; | ||
| 347 | + justify-content: center; | ||
| 348 | + font-size: 14px; | ||
| 349 | + &:last-child { | ||
| 350 | + border-bottom: 0; | ||
| 351 | + } | ||
| 352 | + &.network-cell-item { | ||
| 353 | + flex-direction: row; | ||
| 354 | + align-items: center; | ||
| 355 | + justify-content: start; | ||
| 356 | + &.network-cell-header { | ||
| 357 | + position: absolute; | ||
| 358 | + top: 34px; | ||
| 359 | + left: 0; | ||
| 360 | + min-height: unset; | ||
| 361 | + height: 34px; | ||
| 362 | + width: 100%; | ||
| 363 | + .network-item { | ||
| 364 | + height: 34px; | ||
| 365 | + line-height: 34px; | ||
| 366 | + } | ||
| 367 | + } | ||
| 368 | + .network-item { | ||
| 369 | + height: 44px; | ||
| 370 | + line-height: 44px; | ||
| 371 | + flex: 1; | ||
| 372 | + padding-left: 6px; | ||
| 373 | + overflow: hidden; | ||
| 374 | + } | ||
| 375 | + } | ||
| 376 | + .log-item { | ||
| 377 | + padding: 4px 6px; | ||
| 378 | + width: 100%; | ||
| 379 | + &.error { | ||
| 380 | + background-color: #210700; | ||
| 381 | + color: #f00; | ||
| 382 | + border-bottom: 1px solid #491100; | ||
| 383 | + } | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + &.theme-light { | ||
| 387 | + color: #333; | ||
| 388 | + border-bottom: 1px solid #eee; | ||
| 389 | + background-color: #fff; | ||
| 390 | + &.network-cell-header { | ||
| 391 | + background-color: #fff; | ||
| 392 | + } | ||
| 393 | + &.network-cell-item { | ||
| 394 | + .network-item { | ||
| 395 | + border-right: 1px solid #eee; | ||
| 396 | + } | ||
| 397 | + } | ||
| 398 | + } | ||
| 399 | + &.theme-dark { | ||
| 400 | + color: #a9b0b9; | ||
| 401 | + border-bottom: 1px solid #333; | ||
| 402 | + &.network-cell-header { | ||
| 403 | + background-color: #252528; | ||
| 404 | + } | ||
| 405 | + &.network-cell-item { | ||
| 406 | + .network-item { | ||
| 407 | + border-right: 1px solid #333; | ||
| 408 | + } | ||
| 409 | + } | ||
| 410 | + } | ||
| 411 | + } | ||
| 412 | + .open-console-btn { | ||
| 413 | + // pointer-events: auto; | ||
| 414 | + z-index: 1000; | ||
| 415 | + border-radius: 6px; | ||
| 416 | + | ||
| 417 | + &.not-app { | ||
| 418 | + position: fixed; | ||
| 419 | + bottom: 20%; | ||
| 420 | + right: 50px; | ||
| 421 | + } | ||
| 422 | + } | ||
| 423 | + .close-drag-active { | ||
| 424 | + position: fixed; | ||
| 425 | + top: -100%; | ||
| 426 | + opacity: 0; | ||
| 427 | + } | ||
| 428 | +} | ||
| 429 | +</style> |
src/components/MyDialog/MyDialog.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="my-popup" v-if="isShow"> | ||
| 3 | + <div class="mask-view"></div> | ||
| 4 | + <div class="popup-content"> | ||
| 5 | + <div class="popup-title">{{ title }}</div> | ||
| 6 | + <div class="popup-description" v-if="descriptionType === ''">{{ description }}</div> | ||
| 7 | + <div class="popup-description" v-if="descriptionType === 'html'" v-html="description"></div> | ||
| 8 | + <div class="popup-btn-list"> | ||
| 9 | + <div class="btn" v-if="showCancelBtn" @click="_cancel">{{ cancelBtnText }}</div> | ||
| 10 | + <div class="btn" v-if="showOkBtn" @click="_ok">{{ okBtnText }}</div> | ||
| 11 | + </div> | ||
| 12 | + </div> | ||
| 13 | + </div> | ||
| 14 | +</template> | ||
| 15 | + | ||
| 16 | +<script> | ||
| 17 | +export default { | ||
| 18 | + name: 'MyPopup', | ||
| 19 | + props: { | ||
| 20 | + showPopup: { | ||
| 21 | + type: Boolean, | ||
| 22 | + default: () => { | ||
| 23 | + return false | ||
| 24 | + } | ||
| 25 | + }, | ||
| 26 | + title: { | ||
| 27 | + type: String, | ||
| 28 | + default: '提示' | ||
| 29 | + }, | ||
| 30 | + description: { | ||
| 31 | + type: String, | ||
| 32 | + default: '内容' | ||
| 33 | + }, | ||
| 34 | + descriptionType: { | ||
| 35 | + type: String, | ||
| 36 | + default: '' | ||
| 37 | + }, | ||
| 38 | + showOkBtn: { | ||
| 39 | + type: Boolean, | ||
| 40 | + default: () => { | ||
| 41 | + return true | ||
| 42 | + } | ||
| 43 | + }, | ||
| 44 | + showCancelBtn: { | ||
| 45 | + type: Boolean, | ||
| 46 | + default: () => { | ||
| 47 | + return false | ||
| 48 | + } | ||
| 49 | + }, | ||
| 50 | + okBtnText: { | ||
| 51 | + type: String, | ||
| 52 | + default: '确定' | ||
| 53 | + }, | ||
| 54 | + cancelBtnText: { | ||
| 55 | + type: String, | ||
| 56 | + default: '取消' | ||
| 57 | + }, | ||
| 58 | + okClose: { | ||
| 59 | + type: Boolean, | ||
| 60 | + default: () => { | ||
| 61 | + return false | ||
| 62 | + } | ||
| 63 | + } | ||
| 64 | + }, | ||
| 65 | + watch: { | ||
| 66 | + showPopup(newVal) { | ||
| 67 | + this.isShow = newVal | ||
| 68 | + } | ||
| 69 | + }, | ||
| 70 | + created() { | ||
| 71 | + this.isShow = this.showPopup | ||
| 72 | + }, | ||
| 73 | + data() { | ||
| 74 | + return { | ||
| 75 | + isShow: false | ||
| 76 | + } | ||
| 77 | + }, | ||
| 78 | + methods: { | ||
| 79 | + _ok() { | ||
| 80 | + if (this.okClose) { | ||
| 81 | + this.isShow = false | ||
| 82 | + } | ||
| 83 | + this.$emit('ok') | ||
| 84 | + }, | ||
| 85 | + _cancel() { | ||
| 86 | + this.isShow = false | ||
| 87 | + // this.$emit('cancel') | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | +} | ||
| 91 | +</script> | ||
| 92 | + | ||
| 93 | +<style lang="scss" scoped> | ||
| 94 | +.my-popup { | ||
| 95 | + position: fixed; | ||
| 96 | + width: 100%; | ||
| 97 | + height: 100%; | ||
| 98 | + top: 0; | ||
| 99 | + left: 0; | ||
| 100 | + z-index: 999; | ||
| 101 | + .mask-view { | ||
| 102 | + position: fixed; | ||
| 103 | + width: 100%; | ||
| 104 | + height: 100%; | ||
| 105 | + top: 0; | ||
| 106 | + left: 0; | ||
| 107 | + background-color: #000; | ||
| 108 | + opacity: 0.5; | ||
| 109 | + z-index: 10; | ||
| 110 | + } | ||
| 111 | + .popup-content { | ||
| 112 | + position: fixed; | ||
| 113 | + left: 20px; | ||
| 114 | + right: 20px; | ||
| 115 | + top: 50%; | ||
| 116 | + transform: translateY(-50%); | ||
| 117 | + background-color: #fff; | ||
| 118 | + border-radius: 14px; | ||
| 119 | + z-index: 11; | ||
| 120 | + .popup-title { | ||
| 121 | + font-size: 16px; | ||
| 122 | + font-weight: bold; | ||
| 123 | + height: 44px; | ||
| 124 | + line-height: 44px; | ||
| 125 | + text-align: center; | ||
| 126 | + border-bottom: 1px solid #ededed; | ||
| 127 | + } | ||
| 128 | + .popup-description { | ||
| 129 | + padding: 15px; | ||
| 130 | + } | ||
| 131 | + .popup-btn-list { | ||
| 132 | + display: flex; | ||
| 133 | + align-items: center; | ||
| 134 | + justify-content: space-between; | ||
| 135 | + border-top: 1px solid #ededed; | ||
| 136 | + .btn { | ||
| 137 | + flex: 1; | ||
| 138 | + text-align: center; | ||
| 139 | + height: 44px; | ||
| 140 | + line-height: 44px; | ||
| 141 | + font-size: 14px; | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + } | ||
| 145 | +} | ||
| 146 | +</style> |
src/components/MyNavBar/MyNavBar.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="app-nav-bar" v-if="show" :style="{ height: 46 + offsetTop + 'px', paddingTop: offsetTop + 'px', backgroundColor: background }"> | ||
| 3 | + <slot v-if="$slots.leftArrow" name="leftArrow"></slot> | ||
| 4 | + <div v-else-if="leftArrow" class="left-arrow" @click="leftClick" :style="leftArrowStyle"> | ||
| 5 | + <van-icon name="arrow-left" /> | ||
| 6 | + <span class="l-text">{{ leftText }}</span> | ||
| 7 | + </div> | ||
| 8 | + <div class="nav-title" :style="navTitleStyle">{{ title }}</div> | ||
| 9 | + <slot v-if="$slots.rightArrow" name="rightArrow"></slot> | ||
| 10 | + <div v-else-if="rightArrow" class="right-arrow" @click="rightClick" :style="rightArrowStyle"> | ||
| 11 | + <span class="r-text">{{ rightText }}</span> | ||
| 12 | + </div> | ||
| 13 | + </div> | ||
| 14 | +</template> | ||
| 15 | + | ||
| 16 | +<script> | ||
| 17 | +export default { | ||
| 18 | + name: 'MyNavBar', | ||
| 19 | + props: { | ||
| 20 | + show: { | ||
| 21 | + type: Boolean, | ||
| 22 | + default: () => { | ||
| 23 | + return false | ||
| 24 | + } | ||
| 25 | + }, | ||
| 26 | + title: { | ||
| 27 | + type: String, | ||
| 28 | + default: '' | ||
| 29 | + }, | ||
| 30 | + navTitleStyle: { | ||
| 31 | + type: Boolean, | ||
| 32 | + default: () => { | ||
| 33 | + return false | ||
| 34 | + } | ||
| 35 | + }, | ||
| 36 | + offsetTop: { | ||
| 37 | + type: Number, | ||
| 38 | + default: 0 | ||
| 39 | + }, | ||
| 40 | + background: { | ||
| 41 | + type: String, | ||
| 42 | + default: '#fff' | ||
| 43 | + }, | ||
| 44 | + leftArrow: { | ||
| 45 | + type: Boolean, | ||
| 46 | + default: () => { | ||
| 47 | + return false | ||
| 48 | + } | ||
| 49 | + }, | ||
| 50 | + leftText: { | ||
| 51 | + type: String, | ||
| 52 | + default: '' | ||
| 53 | + }, | ||
| 54 | + leftArrowStyle: { | ||
| 55 | + type: Object, | ||
| 56 | + default: () => { | ||
| 57 | + return {} | ||
| 58 | + } | ||
| 59 | + }, | ||
| 60 | + rightArrow: { | ||
| 61 | + type: Boolean, | ||
| 62 | + default: () => { | ||
| 63 | + return false | ||
| 64 | + } | ||
| 65 | + }, | ||
| 66 | + rightText: { | ||
| 67 | + type: String, | ||
| 68 | + default: '' | ||
| 69 | + }, | ||
| 70 | + rightArrowStyle: { | ||
| 71 | + type: Object, | ||
| 72 | + default: () => { | ||
| 73 | + return {} | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + }, | ||
| 77 | + data() { | ||
| 78 | + return {} | ||
| 79 | + }, | ||
| 80 | + methods: { | ||
| 81 | + leftClick() { | ||
| 82 | + this.$emit('left-callback') | ||
| 83 | + }, | ||
| 84 | + rightClick() { | ||
| 85 | + this.$emit('right-click') | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | +} | ||
| 89 | +</script> | ||
| 90 | + | ||
| 91 | +<style lang="scss" scoped> | ||
| 92 | +.app-nav-bar { | ||
| 93 | + position: fixed; | ||
| 94 | + top: 0; | ||
| 95 | + left: 0; | ||
| 96 | + right: 0; | ||
| 97 | + z-index: 100; | ||
| 98 | + height: 46px; | ||
| 99 | + background-color: #fff; | ||
| 100 | + display: flex; | ||
| 101 | + align-items: center; | ||
| 102 | + justify-content: center; | ||
| 103 | + border-bottom: 1px solid #ebedf0; | ||
| 104 | + | ||
| 105 | + .nav-title { | ||
| 106 | + font-size: 16px; | ||
| 107 | + font-weight: bold; | ||
| 108 | + color: #323233; | ||
| 109 | + } | ||
| 110 | + .left-arrow { | ||
| 111 | + position: absolute; | ||
| 112 | + z-index: 10; | ||
| 113 | + top: 0; | ||
| 114 | + left: 10px; | ||
| 115 | + min-width: 46px; | ||
| 116 | + height: 46px; | ||
| 117 | + display: flex; | ||
| 118 | + align-items: center; | ||
| 119 | + color: #333; | ||
| 120 | + font-size: 14px; | ||
| 121 | + .l-text { | ||
| 122 | + margin-left: 4px; | ||
| 123 | + } | ||
| 124 | + } | ||
| 125 | + .right-arrow { | ||
| 126 | + position: absolute; | ||
| 127 | + z-index: 10; | ||
| 128 | + top: 0; | ||
| 129 | + right: 10px; | ||
| 130 | + min-width: 46px; | ||
| 131 | + height: 46px; | ||
| 132 | + display: flex; | ||
| 133 | + align-items: center; | ||
| 134 | + color: #333; | ||
| 135 | + font-size: 14px; | ||
| 136 | + } | ||
| 137 | +} | ||
| 138 | +</style> |
src/components/MyPopup/MyPopup.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="my-popup-view" :class="`theme-${theme}`" v-if="open"> | ||
| 3 | + <div v-if="overlay" class="popup-mask-view" @click="closeOnClickOverlay ? _close() : ''"></div> | ||
| 4 | + <div class="popup-content-view" :class="`position-${position} ${transition ? `position-${position}-enter-to` : `position-${position}-leave-to`}`" :style="contentStyle"> | ||
| 5 | + <div | ||
| 6 | + v-if="popupTitle !== '' || showCloseIcon" | ||
| 7 | + class="popup-title" | ||
| 8 | + :style="[popupTitleTextStyle, theme !== 'light' && theme !== 'dark' ? { backgroundColor: contentBgColor } : {}]" | ||
| 9 | + > | ||
| 10 | + <div v-if="showCloseIcon" class="close-btn van-icon van-icon-cross" @click="_close"></div> | ||
| 11 | + <span>{{ popupTitle }}</span> | ||
| 12 | + </div> | ||
| 13 | + <div class="popup-slot"> | ||
| 14 | + <slot></slot> | ||
| 15 | + </div> | ||
| 16 | + </div> | ||
| 17 | + </div> | ||
| 18 | +</template> | ||
| 19 | + | ||
| 20 | +<script> | ||
| 21 | +export default { | ||
| 22 | + name: 'MyPopup', | ||
| 23 | + props: { | ||
| 24 | + theme: { | ||
| 25 | + type: String, | ||
| 26 | + default: 'light' | ||
| 27 | + }, | ||
| 28 | + // 是否显示 | ||
| 29 | + value: { | ||
| 30 | + type: Boolean, | ||
| 31 | + required: true | ||
| 32 | + }, | ||
| 33 | + // 遮罩 | ||
| 34 | + overlay: { | ||
| 35 | + type: Boolean, | ||
| 36 | + default: () => { | ||
| 37 | + return true | ||
| 38 | + } | ||
| 39 | + }, | ||
| 40 | + closeOnClickOverlay: { | ||
| 41 | + type: Boolean, | ||
| 42 | + default: () => { | ||
| 43 | + return true | ||
| 44 | + } | ||
| 45 | + }, | ||
| 46 | + popupTitle: { | ||
| 47 | + type: String, | ||
| 48 | + default: '' | ||
| 49 | + }, | ||
| 50 | + popupTitleTextStyle: { | ||
| 51 | + type: Object, | ||
| 52 | + default: () => { | ||
| 53 | + return {} | ||
| 54 | + } | ||
| 55 | + }, | ||
| 56 | + contentBgColor: { | ||
| 57 | + type: String, | ||
| 58 | + default: '#fff' | ||
| 59 | + }, | ||
| 60 | + showCloseIcon: { | ||
| 61 | + type: Boolean, | ||
| 62 | + default: () => { | ||
| 63 | + return false | ||
| 64 | + } | ||
| 65 | + }, | ||
| 66 | + | ||
| 67 | + // 弹出位置 | ||
| 68 | + // position.options: top bottom right left | ||
| 69 | + position: { | ||
| 70 | + type: String, | ||
| 71 | + default: 'center' | ||
| 72 | + }, | ||
| 73 | + // 圆角 | ||
| 74 | + round: { | ||
| 75 | + type: Boolean, | ||
| 76 | + default: () => { | ||
| 77 | + return false | ||
| 78 | + } | ||
| 79 | + }, | ||
| 80 | + width: { | ||
| 81 | + type: [String, Number], | ||
| 82 | + default: '80' | ||
| 83 | + }, | ||
| 84 | + height: { | ||
| 85 | + type: [String, Number], | ||
| 86 | + default: '300' | ||
| 87 | + }, | ||
| 88 | + bottom: { | ||
| 89 | + type: String, | ||
| 90 | + default: null | ||
| 91 | + } | ||
| 92 | + }, | ||
| 93 | + watch: { | ||
| 94 | + isOpen(newVal) { | ||
| 95 | + if (typeof newVal === 'boolean') { | ||
| 96 | + this.open = newVal | ||
| 97 | + } else { | ||
| 98 | + newVal.then(res => (this.open = res)) | ||
| 99 | + } | ||
| 100 | + } | ||
| 101 | + }, | ||
| 102 | + computed: { | ||
| 103 | + isOpen: { | ||
| 104 | + get() { | ||
| 105 | + if (this.value) { | ||
| 106 | + setTimeout(() => { | ||
| 107 | + this.transition = true | ||
| 108 | + }, 100) | ||
| 109 | + return this.value | ||
| 110 | + } else { | ||
| 111 | + this.transition = false | ||
| 112 | + return new Promise(resolve => { | ||
| 113 | + setTimeout(() => { | ||
| 114 | + resolve(false) | ||
| 115 | + }, 300) | ||
| 116 | + }) | ||
| 117 | + } | ||
| 118 | + }, | ||
| 119 | + set(show) { | ||
| 120 | + this.$emit('input', show) | ||
| 121 | + } | ||
| 122 | + }, | ||
| 123 | + contentStyle() { | ||
| 124 | + let bc = {} | ||
| 125 | + if (this.theme !== 'light' && this.theme !== 'dark') { | ||
| 126 | + bc = { | ||
| 127 | + backgroundColor: this.contentBgColor | ||
| 128 | + } | ||
| 129 | + } | ||
| 130 | + if (this.position === 'center') { | ||
| 131 | + return Object.assign(bc, { | ||
| 132 | + left: '50%', | ||
| 133 | + top: '50%', | ||
| 134 | + transform: 'translate(-50%, -50%)' | ||
| 135 | + }) | ||
| 136 | + } else if (this.position === 'top') { | ||
| 137 | + return Object.assign(bc, { | ||
| 138 | + left: 0, | ||
| 139 | + top: '-100%', | ||
| 140 | + maxHeight: '100%', | ||
| 141 | + width: '100%', | ||
| 142 | + height: | ||
| 143 | + typeof this.height === 'string' && this.height.indexOf('%') !== -1 | ||
| 144 | + ? this.height | ||
| 145 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100 | ||
| 146 | + ? this.height + '%' | ||
| 147 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100 | ||
| 148 | + ? this.height + 'px' | ||
| 149 | + : typeof this.height === 'number' | ||
| 150 | + ? this.height + 'px' | ||
| 151 | + : '300px', | ||
| 152 | + 'border-bottom-left-radius': this.round ? '12px' : '0', | ||
| 153 | + 'border-bottom-right-radius': this.round ? '12px' : '0' | ||
| 154 | + }) | ||
| 155 | + } else if (this.position === 'bottom') { | ||
| 156 | + return Object.assign(bc, { | ||
| 157 | + left: 0, | ||
| 158 | + bottom: '-100%', | ||
| 159 | + maxHeight: '100%', | ||
| 160 | + width: '100%', | ||
| 161 | + height: | ||
| 162 | + typeof this.height === 'string' && this.height.indexOf('%') !== -1 | ||
| 163 | + ? this.height | ||
| 164 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100 | ||
| 165 | + ? this.height + '%' | ||
| 166 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100 | ||
| 167 | + ? this.height + 'px' | ||
| 168 | + : typeof this.height === 'number' | ||
| 169 | + ? this.height + 'px' | ||
| 170 | + : '300px', | ||
| 171 | + 'border-top-left-radius': this.round ? '12px' : '0', | ||
| 172 | + 'border-top-right-radius': this.round ? '12px' : '0' | ||
| 173 | + }) | ||
| 174 | + } else if (this.position === 'left') { | ||
| 175 | + return Object.assign(bc, { | ||
| 176 | + left: '-100%', | ||
| 177 | + top: this.bottom ? 'unset' : '0', | ||
| 178 | + bottom: this.bottom ? this.bottom : 'unset', | ||
| 179 | + maxWidth: '100%', | ||
| 180 | + width: | ||
| 181 | + typeof this.width === 'string' && this.width.indexOf('%') !== -1 | ||
| 182 | + ? this.width | ||
| 183 | + : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) < 100 | ||
| 184 | + ? this.width + '%' | ||
| 185 | + : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) > 100 | ||
| 186 | + ? this.width + 'px' | ||
| 187 | + : typeof this.width === 'number' | ||
| 188 | + ? this.width + 'px' | ||
| 189 | + : '240px', | ||
| 190 | + height: | ||
| 191 | + typeof this.height === 'string' && this.height.indexOf('%') !== -1 | ||
| 192 | + ? this.height | ||
| 193 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100 | ||
| 194 | + ? this.height + '%' | ||
| 195 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100 | ||
| 196 | + ? this.height + 'px' | ||
| 197 | + : typeof this.height === 'number' | ||
| 198 | + ? this.height + 'px' | ||
| 199 | + : '100%', | ||
| 200 | + 'border-top-right-radius': this.round ? '12px' : '0', | ||
| 201 | + 'border-bottom-right-radius': this.round ? '12px' : '0' | ||
| 202 | + }) | ||
| 203 | + } else if (this.position === 'right') { | ||
| 204 | + return Object.assign(bc, { | ||
| 205 | + right: '-100%', | ||
| 206 | + top: this.bottom ? 'unset' : '0', | ||
| 207 | + bottom: this.bottom ? this.bottom : 'unset', | ||
| 208 | + maxWidth: '100%', | ||
| 209 | + width: | ||
| 210 | + typeof this.width === 'string' && this.width.indexOf('%') !== -1 | ||
| 211 | + ? this.width | ||
| 212 | + : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) < 100 | ||
| 213 | + ? this.width + '%' | ||
| 214 | + : typeof this.width === 'string' && this.width.indexOf('%') === -1 && Number(this.width) > 100 | ||
| 215 | + ? this.width + 'px' | ||
| 216 | + : typeof this.width === 'number' | ||
| 217 | + ? this.width + 'px' | ||
| 218 | + : '240px', | ||
| 219 | + height: | ||
| 220 | + typeof this.height === 'string' && this.height.indexOf('%') !== -1 | ||
| 221 | + ? this.height | ||
| 222 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) < 100 | ||
| 223 | + ? this.height + '%' | ||
| 224 | + : typeof this.height === 'string' && this.height.indexOf('%') === -1 && Number(this.height) > 100 | ||
| 225 | + ? this.height + 'px' | ||
| 226 | + : typeof this.height === 'number' | ||
| 227 | + ? this.height + 'px' | ||
| 228 | + : '100%', | ||
| 229 | + 'border-top-left-radius': this.round ? '12px' : '0', | ||
| 230 | + 'border-bottom-left-radius': this.round ? '12px' : '0' | ||
| 231 | + }) | ||
| 232 | + } | ||
| 233 | + } | ||
| 234 | + }, | ||
| 235 | + data() { | ||
| 236 | + return { | ||
| 237 | + open: false, | ||
| 238 | + transition: false | ||
| 239 | + } | ||
| 240 | + }, | ||
| 241 | + mounted() { | ||
| 242 | + this.isOpen.then(value => { | ||
| 243 | + this.open = value | ||
| 244 | + }) | ||
| 245 | + }, | ||
| 246 | + methods: { | ||
| 247 | + _close() { | ||
| 248 | + this.isOpen = false | ||
| 249 | + } | ||
| 250 | + } | ||
| 251 | +} | ||
| 252 | +</script> | ||
| 253 | + | ||
| 254 | +<style lang="scss" scoped> | ||
| 255 | +.my-popup-view { | ||
| 256 | + position: fixed; | ||
| 257 | + width: 100%; | ||
| 258 | + height: 100%; | ||
| 259 | + top: 0; | ||
| 260 | + left: 0; | ||
| 261 | + z-index: 999999; | ||
| 262 | + pointer-events: auto; | ||
| 263 | + | ||
| 264 | + .popup-mask-view { | ||
| 265 | + width: 100%; | ||
| 266 | + height: 100%; | ||
| 267 | + position: absolute; | ||
| 268 | + top: 0; | ||
| 269 | + left: 0; | ||
| 270 | + z-index: 10; | ||
| 271 | + background-color: #000; | ||
| 272 | + opacity: 0.5; | ||
| 273 | + } | ||
| 274 | + .popup-content-view { | ||
| 275 | + position: absolute; | ||
| 276 | + z-index: 11; | ||
| 277 | + overflow-y: auto; | ||
| 278 | + transition: all 0.3s; | ||
| 279 | + &.position-top-leave-to { | ||
| 280 | + top: -100% !important; | ||
| 281 | + } | ||
| 282 | + &.position-top-enter-to { | ||
| 283 | + top: 0 !important; | ||
| 284 | + } | ||
| 285 | + &.position-bottom-leave-to { | ||
| 286 | + bottom: -100% !important; | ||
| 287 | + } | ||
| 288 | + &.position-bottom-enter-to { | ||
| 289 | + bottom: 0 !important; | ||
| 290 | + } | ||
| 291 | + &.position-left-leave-to { | ||
| 292 | + left: -100% !important; | ||
| 293 | + } | ||
| 294 | + &.position-left-enter-to { | ||
| 295 | + left: 0 !important; | ||
| 296 | + } | ||
| 297 | + &.position-right-leave-to { | ||
| 298 | + right: -100% !important; | ||
| 299 | + } | ||
| 300 | + &.position-right-enter-to { | ||
| 301 | + right: 0 !important; | ||
| 302 | + } | ||
| 303 | + | ||
| 304 | + .popup-title { | ||
| 305 | + padding: 0 10px; | ||
| 306 | + font-size: 16px; | ||
| 307 | + font-weight: bold; | ||
| 308 | + text-align: center; | ||
| 309 | + height: 44px; | ||
| 310 | + line-height: 44px; | ||
| 311 | + position: relative; | ||
| 312 | + } | ||
| 313 | + .close-btn { | ||
| 314 | + width: 36px; | ||
| 315 | + height: 36px; | ||
| 316 | + line-height: 36px; | ||
| 317 | + position: absolute; | ||
| 318 | + top: 4px; | ||
| 319 | + right: 5px; | ||
| 320 | + text-align: center; | ||
| 321 | + font-size: 16px; | ||
| 322 | + color: #888; | ||
| 323 | + } | ||
| 324 | + .popup-slot { | ||
| 325 | + height: calc(100% - 44px); | ||
| 326 | + } | ||
| 327 | + } | ||
| 328 | + &.theme-light { | ||
| 329 | + .popup-content-view { | ||
| 330 | + background-color: #fff; | ||
| 331 | + .popup-title { | ||
| 332 | + background-color: #fff; | ||
| 333 | + border-bottom: 1px solid #eee; | ||
| 334 | + color: #333; | ||
| 335 | + } | ||
| 336 | + } | ||
| 337 | + } | ||
| 338 | + &.theme-dark { | ||
| 339 | + .popup-content-view { | ||
| 340 | + background-color: #1e1e20; | ||
| 341 | + .popup-title { | ||
| 342 | + background-color: #252528; | ||
| 343 | + border-bottom: 1px solid #414346; | ||
| 344 | + color: #797c82; | ||
| 345 | + } | ||
| 346 | + } | ||
| 347 | + } | ||
| 348 | +} | ||
| 349 | +</style> |
src/components/MyTabBar/MyTabBar.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="tab-bar-view"> | ||
| 3 | + <div class="tab-bar-item" v-for="(item, index) in tabBarList" :key="index" :class="{ activity: activityTabBarIndex === index }" @click="itemClick(item, index)"> | ||
| 4 | + <div v-if="item.icon" class="tab-bar-icon van-icon" :class="`van-icon-${item.icon}`" @click.stop="itemClick(item, index)"></div> | ||
| 5 | + <div class="tab-bar-name">{{ item.label }}</div> | ||
| 6 | + </div> | ||
| 7 | + </div> | ||
| 8 | +</template> | ||
| 9 | + | ||
| 10 | +<script> | ||
| 11 | +export default { | ||
| 12 | + name: 'MyTabBar', | ||
| 13 | + props: { | ||
| 14 | + value: { | ||
| 15 | + type: [String, Number], | ||
| 16 | + required: true | ||
| 17 | + }, | ||
| 18 | + tabBarList: { | ||
| 19 | + type: Array, | ||
| 20 | + default: () => { | ||
| 21 | + return [] | ||
| 22 | + } | ||
| 23 | + } | ||
| 24 | + }, | ||
| 25 | + computed: { | ||
| 26 | + activityTabBarIndex: { | ||
| 27 | + get() { | ||
| 28 | + let currentIndex = 0 | ||
| 29 | + if (typeof this.value === 'string') { | ||
| 30 | + this.tabBarList.forEach((item, index) => { | ||
| 31 | + if (item.name === this.value) { | ||
| 32 | + currentIndex = index | ||
| 33 | + } | ||
| 34 | + }) | ||
| 35 | + } else if (typeof this.value === 'number') { | ||
| 36 | + currentIndex = this.value | ||
| 37 | + } | ||
| 38 | + return currentIndex | ||
| 39 | + }, | ||
| 40 | + set(index) { | ||
| 41 | + this.$emit('input', index) | ||
| 42 | + } | ||
| 43 | + } | ||
| 44 | + }, | ||
| 45 | + data() { | ||
| 46 | + return {} | ||
| 47 | + }, | ||
| 48 | + methods: { | ||
| 49 | + itemClick(item, index) { | ||
| 50 | + console.log('tabbar-itemClick-index', index) | ||
| 51 | + this.activityTabBarIndex = index | ||
| 52 | + this.$emit('change', item) | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | +} | ||
| 56 | +</script> | ||
| 57 | + | ||
| 58 | +<style lang="scss" scoped> | ||
| 59 | +.tab-bar-view { | ||
| 60 | + position: fixed; | ||
| 61 | + left: 0; | ||
| 62 | + bottom: 0; | ||
| 63 | + width: 100%; | ||
| 64 | + height: 51px; | ||
| 65 | + display: flex; | ||
| 66 | + align-items: center; | ||
| 67 | + padding-bottom: constant(safe-area-inset-bottom); | ||
| 68 | + padding-bottom: env(safe-area-inset-bottom); | ||
| 69 | + background-color: #fff; | ||
| 70 | + box-shadow: 0 0 10px #d9d9d9; | ||
| 71 | + border-top: 1px solid #e3e3e3; | ||
| 72 | + .tab-bar-item { | ||
| 73 | + height: 100%; | ||
| 74 | + display: flex; | ||
| 75 | + flex-direction: column; | ||
| 76 | + align-items: center; | ||
| 77 | + justify-content: center; | ||
| 78 | + flex: 1; | ||
| 79 | + font-size: 12px; | ||
| 80 | + line-height: 1; | ||
| 81 | + color: #7d7e80; | ||
| 82 | + cursor: pointer; | ||
| 83 | + .tab-bar-icon { | ||
| 84 | + margin-bottom: 4px; | ||
| 85 | + font-size: 22px; | ||
| 86 | + } | ||
| 87 | + &.activity { | ||
| 88 | + color: #1989fa; | ||
| 89 | + } | ||
| 90 | + } | ||
| 91 | +} | ||
| 92 | +</style> |
src/components/MyToast/MyToast.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="dyson-toast" v-if="ylTotasOpen"> | ||
| 3 | + <div v-if="ylToastType === 'hint'" class="toast-view hint-toast"> | ||
| 4 | + <div class="totas-text">{{ ylTotasHintText }}</div> | ||
| 5 | + </div> | ||
| 6 | + <div v-if="ylToastType === 'loading'" class="toast-view loading-toast"> | ||
| 7 | + <div class="loading-img" v-if="isShowIcon"> | ||
| 8 | + <span class="iconfont" :class="ylTotasLoadingIcon"></span> | ||
| 9 | + </div> | ||
| 10 | + <div class="totas-text">{{ ylTotasHintText }}</div> | ||
| 11 | + </div> | ||
| 12 | + </div> | ||
| 13 | +</template> | ||
| 14 | + | ||
| 15 | +<script> | ||
| 16 | +export default { | ||
| 17 | + name: 'MyToast', | ||
| 18 | + props: { | ||
| 19 | + isOpen: { | ||
| 20 | + type: Boolean, | ||
| 21 | + default: () => { | ||
| 22 | + return false | ||
| 23 | + } | ||
| 24 | + }, | ||
| 25 | + isShowIcon: { | ||
| 26 | + type: Boolean, | ||
| 27 | + default: () => { | ||
| 28 | + return true | ||
| 29 | + } | ||
| 30 | + }, | ||
| 31 | + toastType: { | ||
| 32 | + type: String, | ||
| 33 | + default: 'hint' | ||
| 34 | + }, | ||
| 35 | + hintText: { | ||
| 36 | + type: String, | ||
| 37 | + default: '' | ||
| 38 | + }, | ||
| 39 | + duration: { | ||
| 40 | + type: Number, | ||
| 41 | + default: 3000 | ||
| 42 | + }, | ||
| 43 | + loadingIcon: { | ||
| 44 | + type: String, | ||
| 45 | + default: 'icon-loading-3' | ||
| 46 | + } | ||
| 47 | + }, | ||
| 48 | + data() { | ||
| 49 | + return { | ||
| 50 | + ylTotasOpen: false, | ||
| 51 | + ylToastType: '', | ||
| 52 | + ylTotasHintText: '', | ||
| 53 | + ylTotasDuration: 0, | ||
| 54 | + ylTotasLoadingIcon: '', | ||
| 55 | + st: null | ||
| 56 | + } | ||
| 57 | + }, | ||
| 58 | + watch: { | ||
| 59 | + ylTotasOpen(newVal) { | ||
| 60 | + if (newVal && this.ylToastType !== 'loading') { | ||
| 61 | + if (this.ylTotasDuration !== 0) { | ||
| 62 | + this.st = setTimeout(() => { | ||
| 63 | + this.ylTotasOpen = false | ||
| 64 | + }, this.ylTotasDuration) | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + }, | ||
| 68 | + isOpen(newVal) { | ||
| 69 | + this.ylTotasOpen = newVal | ||
| 70 | + }, | ||
| 71 | + hintText(newVal) { | ||
| 72 | + this.ylTotasHintText = newVal | ||
| 73 | + } | ||
| 74 | + }, | ||
| 75 | + created() { | ||
| 76 | + this.ylTotasOpen = this.isOpen | ||
| 77 | + this.ylTotasHintText = this.hintText | ||
| 78 | + this.ylToastType = this.toastType | ||
| 79 | + this.ylTotasDuration = this.duration | ||
| 80 | + this.ylTotasLoadingIcon = this.loadingIcon | ||
| 81 | + }, | ||
| 82 | + methods: { | ||
| 83 | + loading(config) { | ||
| 84 | + if (this.st) { | ||
| 85 | + clearTimeout(this.st) | ||
| 86 | + this.st = null | ||
| 87 | + } | ||
| 88 | + this.ylToastType = 'loading' | ||
| 89 | + this.ylTotasHintText = config.hintText | ||
| 90 | + this.ylTotasDuration = config.duration | ||
| 91 | + this.ylTotasOpen = config.isOpen | ||
| 92 | + if (config.loadingIcon) { | ||
| 93 | + this.ylTotasLoadingIcon = config.loadingIcon | ||
| 94 | + } | ||
| 95 | + return this | ||
| 96 | + } | ||
| 97 | + } | ||
| 98 | +} | ||
| 99 | +</script> | ||
| 100 | + | ||
| 101 | +<style lang="scss" scoped> | ||
| 102 | +.dyson-toast { | ||
| 103 | + position: fixed; | ||
| 104 | + width: 100%; | ||
| 105 | + height: 100%; | ||
| 106 | + top: 0; | ||
| 107 | + left: 0; | ||
| 108 | + display: flex; | ||
| 109 | + align-items: center; | ||
| 110 | + justify-content: center; | ||
| 111 | + .toast-view { | ||
| 112 | + min-width: 130px; | ||
| 113 | + padding: 8px 30px; | ||
| 114 | + border-radius: 6px; | ||
| 115 | + background-color: #333; | ||
| 116 | + .totas-text { | ||
| 117 | + font-size: 12px; | ||
| 118 | + color: #fff; | ||
| 119 | + text-align: center; | ||
| 120 | + } | ||
| 121 | + } | ||
| 122 | + .loading-toast { | ||
| 123 | + .loading-img { | ||
| 124 | + text-align: center; | ||
| 125 | + margin-top: 10px; | ||
| 126 | + margin-bottom: 10px; | ||
| 127 | + animation: rotate 1s linear infinite; | ||
| 128 | + .iconfont { | ||
| 129 | + color: #fff; | ||
| 130 | + font-size: 18px; | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + } | ||
| 134 | +} | ||
| 135 | + | ||
| 136 | +@keyframes rotate { | ||
| 137 | + 0% { | ||
| 138 | + transform: rotate(0deg); | ||
| 139 | + } | ||
| 140 | + 25% { | ||
| 141 | + transform: rotate(90deg); | ||
| 142 | + } | ||
| 143 | + 50% { | ||
| 144 | + transform: rotate(180deg); | ||
| 145 | + } | ||
| 146 | + 75% { | ||
| 147 | + transform: rotate(270deg); | ||
| 148 | + } | ||
| 149 | + 100% { | ||
| 150 | + transform: rotate(360deg); | ||
| 151 | + } | ||
| 152 | +} | ||
| 153 | +</style> |
src/config/index.js
0 → 100644
| 1 | +export default { | ||
| 2 | + /** | ||
| 3 | + * @description 配置显示在浏览器标签的title | ||
| 4 | + */ | ||
| 5 | + title: 'Admin', | ||
| 6 | + /** | ||
| 7 | + * @description token在Cookie中存储的天数,默认1天 | ||
| 8 | + */ | ||
| 9 | + cookieExpires: 1, | ||
| 10 | + /** | ||
| 11 | + * @description 是否使用国际化,默认为false | ||
| 12 | + * 如果不使用,则需要在路由中给需要在菜单中展示的路由设置meta: {title: 'xxx'} | ||
| 13 | + * 用来在菜单中显示文字 | ||
| 14 | + */ | ||
| 15 | + useI18n: true, | ||
| 16 | + /** | ||
| 17 | + * @description api请求基础路径 | ||
| 18 | + */ | ||
| 19 | + baseUrl: { | ||
| 20 | + dev: 'https://mock.apifox.cn/m1/2852848-0-default/', | ||
| 21 | + devOnLine: 'https://mock.apifox.cn/m1/2852848-0-default/', | ||
| 22 | + uat: 'https://mock.apifox.cn/m1/2852848-0-default/', | ||
| 23 | + pro: 'https://mock.apifox.cn/m1/2852848-0-default/' | ||
| 24 | + }, | ||
| 25 | + imgToCos: { | ||
| 26 | + dev: false, | ||
| 27 | + uat: true, | ||
| 28 | + pro: true | ||
| 29 | + }, | ||
| 30 | + //配置图片请求地址 | ||
| 31 | + imgBaseUrl: { | ||
| 32 | + dev: 'http://localhost:3001', | ||
| 33 | + // dev: 'http://localhost:3001/admin/cosGetObject?key=', | ||
| 34 | + devOnLine: 'http://localhost:3001/admin/cosGetObject?key=', | ||
| 35 | + uat: 'http://localhost:3001/admin/cosGetObject?key=', | ||
| 36 | + pro: 'http://localhost:3001/admin/cosGetObject?key=' | ||
| 37 | + }, | ||
| 38 | + /** | ||
| 39 | + * @description 默认打开的首页的路由name值,默认为home | ||
| 40 | + */ | ||
| 41 | + homeName: 'home', | ||
| 42 | + /** | ||
| 43 | + * @description 需要加载的插件 | ||
| 44 | + */ | ||
| 45 | + plugin: { | ||
| 46 | + 'error-store': { | ||
| 47 | + showInHeader: false, // 设为false后不会在顶部显示错误日志徽标 | ||
| 48 | + developmentOff: true // 设为true后在开发环境不会收集错误信息,方便开发中排查错误 | ||
| 49 | + } | ||
| 50 | + } | ||
| 51 | +} |
src/filters/index.js
0 → 100644
src/libs/api.request.js
0 → 100644
| 1 | +// import HttpRequest from '@/libs/axios' | ||
| 2 | +// import config from '@/config' | ||
| 3 | +// const baseUrl = config.baseUrl[process.env.VUE_APP_NAME] | ||
| 4 | +// const axios = new HttpRequest(baseUrl) | ||
| 5 | +// export default axios | ||
| 6 | + | ||
| 7 | +import HttpRequest from '@/libs/fetch' | ||
| 8 | +import config from '@/config' | ||
| 9 | +const baseUrl = config.baseUrl[process.env.VUE_APP_NAME] | ||
| 10 | +const axios = new HttpRequest(baseUrl) | ||
| 11 | +export default axios |
src/libs/axios.js
0 → 100644
| 1 | +import axios from 'axios' | ||
| 2 | +import router from '@/router' | ||
| 3 | +import store from '@/store' | ||
| 4 | +import { Notify } from 'vant' | ||
| 5 | +import { setToken, getToken } from '@/libs/util' | ||
| 6 | + | ||
| 7 | +const addErrorLog = errorInfo => { | ||
| 8 | + const { statusText, status, config } = errorInfo | ||
| 9 | + let info = { | ||
| 10 | + type: 'ajax', | ||
| 11 | + code: status, | ||
| 12 | + // osInfo: getOs(), | ||
| 13 | + // browserInfo: getBrowser().browser + '/' + getBrowser().version, | ||
| 14 | + msg: errorInfo.data.message ? errorInfo.data.message : statusText, | ||
| 15 | + url: config.url, | ||
| 16 | + dataInfo: config.data ? JSON.parse(config.data) : {}, | ||
| 17 | + headers: config.headers | ||
| 18 | + } | ||
| 19 | + if (!config.url.includes('save_error_logger')) { | ||
| 20 | + store.dispatch('addErrorLog', info) | ||
| 21 | + } | ||
| 22 | +} | ||
| 23 | + | ||
| 24 | +class HttpRequest { | ||
| 25 | + path = '' | ||
| 26 | + curPath = '' | ||
| 27 | + | ||
| 28 | + constructor(baseUrl = baseURL) { | ||
| 29 | + this.baseUrl = baseUrl | ||
| 30 | + this.queue = {} | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + getInsideConfig() { | ||
| 34 | + const config = { | ||
| 35 | + baseURL: this.baseUrl, | ||
| 36 | + headers: {} | ||
| 37 | + } | ||
| 38 | + if (getToken()) { | ||
| 39 | + config.headers.Authorization = 'Bearer ' + getToken() | ||
| 40 | + } | ||
| 41 | + return config | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + destroy(url) { | ||
| 45 | + delete this.queue[url] | ||
| 46 | + if (!Object.keys(this.queue).length) { | ||
| 47 | + // Spin.hide() | ||
| 48 | + } | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + interceptors(instance, url) { | ||
| 52 | + // 请求拦截 | ||
| 53 | + instance.interceptors.request.use( | ||
| 54 | + config => { | ||
| 55 | + // 添加全局的loading... | ||
| 56 | + // if (!Object.keys(this.queue).length) { | ||
| 57 | + // Spin.show() // 不建议开启,因为界面不友好 | ||
| 58 | + // } | ||
| 59 | + this.queue[url] = true | ||
| 60 | + return config | ||
| 61 | + }, | ||
| 62 | + error => { | ||
| 63 | + return Promise.reject(error) | ||
| 64 | + } | ||
| 65 | + ) | ||
| 66 | + // 响应拦截 | ||
| 67 | + instance.interceptors.response.use( | ||
| 68 | + response => { | ||
| 69 | + this.destroy(url) | ||
| 70 | + // console.log(response) | ||
| 71 | + if (response.data.code == 200) { | ||
| 72 | + return response.data | ||
| 73 | + } else { | ||
| 74 | + Notify({ type: 'warning', message: response.data.message }) | ||
| 75 | + return Promise.reject(response.data.message) | ||
| 76 | + } | ||
| 77 | + }, | ||
| 78 | + error => { | ||
| 79 | + this.destroy(url) | ||
| 80 | + let errorInfo = error.response | ||
| 81 | + console.log('errorInfo:', errorInfo) | ||
| 82 | + if (!errorInfo) { | ||
| 83 | + const { | ||
| 84 | + request: { statusText, status }, | ||
| 85 | + config | ||
| 86 | + } = JSON.parse(JSON.stringify(error)) | ||
| 87 | + console.log('config:', config) | ||
| 88 | + errorInfo = { | ||
| 89 | + statusText, | ||
| 90 | + status, | ||
| 91 | + config | ||
| 92 | + } | ||
| 93 | + } | ||
| 94 | + addErrorLog(errorInfo) | ||
| 95 | + if (errorInfo.status === 401) { | ||
| 96 | + setToken('') | ||
| 97 | + router.replace({ | ||
| 98 | + name: 'login' | ||
| 99 | + }) | ||
| 100 | + } | ||
| 101 | + return Promise.reject(error) | ||
| 102 | + } | ||
| 103 | + ) | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + setPath(...paths) { | ||
| 107 | + this.curPath = `${this.path}/${paths.join('/')}` | ||
| 108 | + return this | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + replace(...params) { | ||
| 112 | + let count = 0 | ||
| 113 | + this.curPath = this.curPath.replace(/\{.*?\}/g, _match => params[count++]) | ||
| 114 | + return this | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + request(options) { | ||
| 118 | + const instance = axios.create() | ||
| 119 | + options = Object.assign(this.getInsideConfig(), options) | ||
| 120 | + if (this.curPath !== '') { | ||
| 121 | + options.url = this.curPath | ||
| 122 | + this.curPath = '' | ||
| 123 | + this.path = '' | ||
| 124 | + } | ||
| 125 | + this.interceptors(instance, options.url) | ||
| 126 | + return instance(options) | ||
| 127 | + } | ||
| 128 | +} | ||
| 129 | + | ||
| 130 | +export default HttpRequest |
src/libs/fetch.js
0 → 100644
| 1 | +import router from '@/router' | ||
| 2 | +// import store from '@/store' | ||
| 3 | +// import { Toast } from 'vant' | ||
| 4 | +import { setToken, getToken } from '@/libs/util' | ||
| 5 | + | ||
| 6 | +const addErrorLog = errorInfo => { | ||
| 7 | + const { statusText, status, url, headers, data } = errorInfo | ||
| 8 | + let info = { | ||
| 9 | + type: 'ajax', | ||
| 10 | + code: status, | ||
| 11 | + // osInfo: getOs(), | ||
| 12 | + // browserInfo: getBrowser().browser + '/' + getBrowser().version, | ||
| 13 | + msg: errorInfo.message ? errorInfo.message : statusText, | ||
| 14 | + url: url, | ||
| 15 | + dataInfo: data, | ||
| 16 | + headers: headers | ||
| 17 | + } | ||
| 18 | + // if (!url.includes('save_error_logger')) { | ||
| 19 | + // store.dispatch('addErrorLog', info) | ||
| 20 | + // } | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +class HttpRequest { | ||
| 24 | + path = '' | ||
| 25 | + curPath = '' | ||
| 26 | + | ||
| 27 | + constructor(baseURL) { | ||
| 28 | + this.baseURL = baseURL | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + setPath(...paths) { | ||
| 32 | + this.curPath = `${this.path}/${paths.join('/')}` | ||
| 33 | + return this | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + replace(...params) { | ||
| 37 | + let count = 0 | ||
| 38 | + this.curPath = this.curPath.replace(/\{.*?\}/g, _match => params[count++]) | ||
| 39 | + return this | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + request(parames) { | ||
| 43 | + let url = parames.url || '' | ||
| 44 | + let method = parames.method || 'GET' | ||
| 45 | + let data = parames.data || parames.params | ||
| 46 | + let requestUrl = url.indexOf('http://') === -1 && url.indexOf('https://') === -1 ? this.baseURL + url : url | ||
| 47 | + if (this.curPath !== '') { | ||
| 48 | + requestUrl = this.curPath | ||
| 49 | + this.curPath = '' | ||
| 50 | + this.path = '' | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + let options = { | ||
| 54 | + method: method.toUpperCase(), | ||
| 55 | + headers: { | ||
| 56 | + 'Content-Type': 'application/json; charset=UTF-8' | ||
| 57 | + }, | ||
| 58 | + body: JSON.stringify(data) | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + // 设置token | ||
| 62 | + if (getToken()) { | ||
| 63 | + options.headers.token = getToken() | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + // get请求转换参数 | ||
| 67 | + if (method == 'GET') { | ||
| 68 | + delete options.body | ||
| 69 | + let qs = '?' | ||
| 70 | + for (const key in parames.data) { | ||
| 71 | + qs += key + '=' + parames.data[key] + '&' | ||
| 72 | + } | ||
| 73 | + qs = qs.substring(0, qs.length - 1) | ||
| 74 | + qs.length > 1 ? (requestUrl += qs) : '' | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + return new Promise((resolve, reject) => { | ||
| 78 | + fetch(requestUrl, options) | ||
| 79 | + .then(function (response) { | ||
| 80 | + if (response.ok) { | ||
| 81 | + return response.json() | ||
| 82 | + } else { | ||
| 83 | + const errorInfo = { | ||
| 84 | + statusText: response.statusText, | ||
| 85 | + status: response.status, | ||
| 86 | + url: response.url, | ||
| 87 | + headers: JSON.stringify(response.headers), | ||
| 88 | + data: response.data | ||
| 89 | + } | ||
| 90 | + addErrorLog(errorInfo) | ||
| 91 | + if (response.status === 404) { | ||
| 92 | + error('服务器开小差了,请联系客服~') | ||
| 93 | + } | ||
| 94 | + reject(response) | ||
| 95 | + } | ||
| 96 | + }) | ||
| 97 | + .then(res => { | ||
| 98 | + successCallBack(res, resolve, reject) | ||
| 99 | + }) | ||
| 100 | + .catch(err => { | ||
| 101 | + error() | ||
| 102 | + }) | ||
| 103 | + }) | ||
| 104 | + } | ||
| 105 | +} | ||
| 106 | + | ||
| 107 | +function error(msg = '请检查您的网络') { | ||
| 108 | + // Toast({ | ||
| 109 | + // message: msg, | ||
| 110 | + // icon: 'none', | ||
| 111 | + // duration: 3500 | ||
| 112 | + // }) | ||
| 113 | +} | ||
| 114 | +function successCallBack(res, resolve, reject) { | ||
| 115 | + switch (res.code) { | ||
| 116 | + case 200: | ||
| 117 | + resolve(res.data) | ||
| 118 | + break | ||
| 119 | + case 401: | ||
| 120 | + error('请重新登录!') | ||
| 121 | + setToken('') | ||
| 122 | + router.replace({ | ||
| 123 | + name: 'login' | ||
| 124 | + }) | ||
| 125 | + break | ||
| 126 | + default: | ||
| 127 | + businessError(res, reject) | ||
| 128 | + break | ||
| 129 | + } | ||
| 130 | +} | ||
| 131 | +function businessError(err, reject) { | ||
| 132 | + // Toast({ | ||
| 133 | + // message: err.message, | ||
| 134 | + // duration: 2000 | ||
| 135 | + // }) | ||
| 136 | + reject(err) | ||
| 137 | +} | ||
| 138 | + | ||
| 139 | +export default HttpRequest |
src/libs/storageUtil.js
0 → 100644
| 1 | +var storage = { | ||
| 2 | + get: (name) => { | ||
| 3 | + return localStorage.getItem(name) | ||
| 4 | + ? JSON.parse(localStorage.getItem(name)) | ||
| 5 | + : null | ||
| 6 | + }, | ||
| 7 | + set: (name, val) => { | ||
| 8 | + localStorage.setItem(name, JSON.stringify(val)) | ||
| 9 | + }, | ||
| 10 | + remove: (name) => { | ||
| 11 | + localStorage.removeItem(name) | ||
| 12 | + } | ||
| 13 | +} | ||
| 14 | +export default storage |
src/libs/tools.js
0 → 100644
| 1 | +export const forEach = (arr, fn) => { | ||
| 2 | + if (!arr.length || !fn) return | ||
| 3 | + let i = -1 | ||
| 4 | + let len = arr.length | ||
| 5 | + while (++i < len) { | ||
| 6 | + let item = arr[i] | ||
| 7 | + fn(item, i, arr) | ||
| 8 | + } | ||
| 9 | +} | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * @param {Array} arr1 | ||
| 13 | + * @param {Array} arr2 | ||
| 14 | + * @description 得到两个数组的交集, 两个数组的元素为数值或字符串 | ||
| 15 | + */ | ||
| 16 | +export const getIntersection = (arr1, arr2) => { | ||
| 17 | + let len = Math.min(arr1.length, arr2.length) | ||
| 18 | + let i = -1 | ||
| 19 | + let res = [] | ||
| 20 | + while (++i < len) { | ||
| 21 | + const item = arr2[i] | ||
| 22 | + if (arr1.indexOf(item) > -1) res.push(item) | ||
| 23 | + } | ||
| 24 | + return res | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * @param {Array} arr1 | ||
| 29 | + * @param {Array} arr2 | ||
| 30 | + * @description 得到两个数组的并集, 两个数组的元素为数值或字符串 | ||
| 31 | + */ | ||
| 32 | +export const getUnion = (arr1, arr2) => { | ||
| 33 | + return Array.from(new Set([...arr1, ...arr2])) | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +/** | ||
| 37 | + * @param {Array} target 目标数组 | ||
| 38 | + * @param {Array} arr 需要查询的数组 | ||
| 39 | + * @description 判断要查询的数组是否至少有一个元素包含在目标数组中 | ||
| 40 | + */ | ||
| 41 | +export const hasOneOf = (targetarr, arr) => { | ||
| 42 | + return targetarr.some(_ => arr.indexOf(_) > -1) | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +/** 根据传入的字段进行分组 | ||
| 46 | + * @param arr 需要分组的数组 | ||
| 47 | + * @param property 分组的字段 | ||
| 48 | + * @returns {*[]} 已分好组的数组 | ||
| 49 | + */ | ||
| 50 | +export const abilitySort = (arr, property) => { | ||
| 51 | + let map = {} | ||
| 52 | + for (let i = 0; i < arr.length; i++) { | ||
| 53 | + const ai = arr[i] | ||
| 54 | + if (!map[ai[property]]) map[ai[property]] = [ai] | ||
| 55 | + else map[ai[property]].push(ai) | ||
| 56 | + } | ||
| 57 | + let res = [] | ||
| 58 | + Object.keys(map).forEach(key => { | ||
| 59 | + res.push({ [property]: key, data: map[key] }) | ||
| 60 | + }) | ||
| 61 | + return res | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +/** | ||
| 65 | + * @param {String|Number} value 要验证的字符串或数值 | ||
| 66 | + * @param {*} validList 用来验证的列表 | ||
| 67 | + */ | ||
| 68 | +export function oneOf(value, validList) { | ||
| 69 | + for (let i = 0; i < validList.length; i++) { | ||
| 70 | + if (value === validList[i]) { | ||
| 71 | + return true | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + return false | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +/** | ||
| 78 | + * @param {Number} timeStamp 判断时间戳格式是否是毫秒 | ||
| 79 | + * @returns {Boolean} | ||
| 80 | + */ | ||
| 81 | +const isMillisecond = timeStamp => { | ||
| 82 | + const timeStr = String(timeStamp) | ||
| 83 | + return timeStr.length > 10 | ||
| 84 | +} | ||
| 85 | + | ||
| 86 | +/** | ||
| 87 | + * @param {Number} timeStamp 传入的时间戳 | ||
| 88 | + * @param {Number} currentTime 当前时间时间戳 | ||
| 89 | + * @returns {Boolean} 传入的时间戳是否早于当前时间戳 | ||
| 90 | + */ | ||
| 91 | +const isEarly = (timeStamp, currentTime) => { | ||
| 92 | + return timeStamp < currentTime | ||
| 93 | +} | ||
| 94 | + | ||
| 95 | +/** | ||
| 96 | + * @param {Number} num 数值 | ||
| 97 | + * @returns {String} 处理后的字符串 | ||
| 98 | + * @description 如果传入的数值小于10,即位数只有1位,则在前面补充0 | ||
| 99 | + */ | ||
| 100 | +const getHandledValue = num => { | ||
| 101 | + return num < 10 ? '0' + num : num | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +/** | ||
| 105 | + * @param {Number} timeStamp 传入的时间戳 | ||
| 106 | + * @param {Number} startType 要返回的时间字符串的格式类型,传入'year'则返回年开头的完整时间 | ||
| 107 | + */ | ||
| 108 | +export const getDate = (timeStamp, startType) => { | ||
| 109 | + const d = new Date(timeStamp * 1000) | ||
| 110 | + const year = d.getFullYear() | ||
| 111 | + const month = getHandledValue(d.getMonth() + 1) | ||
| 112 | + const date = getHandledValue(d.getDate()) | ||
| 113 | + const hours = getHandledValue(d.getHours()) | ||
| 114 | + const minutes = getHandledValue(d.getMinutes()) | ||
| 115 | + const second = getHandledValue(d.getSeconds()) | ||
| 116 | + let resStr = '' | ||
| 117 | + if (startType === 'year') { | ||
| 118 | + resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second | ||
| 119 | + } else resStr = month + '-' + date + ' ' + hours + ':' + minutes | ||
| 120 | + return resStr | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +/** | ||
| 124 | + * @param {String|Number} timeStamp 时间戳 | ||
| 125 | + * @returns {String} 相对时间字符串 | ||
| 126 | + */ | ||
| 127 | +export const getRelativeTime = timeStamp => { | ||
| 128 | + // 判断当前传入的时间戳是秒格式还是毫秒 | ||
| 129 | + const IS_MILLISECOND = isMillisecond(timeStamp) | ||
| 130 | + // 如果是毫秒格式则转为秒格式 | ||
| 131 | + if (IS_MILLISECOND) Math.floor((timeStamp /= 1000)) | ||
| 132 | + // 传入的时间戳可以是数值或字符串类型,这里统一转为数值类型 | ||
| 133 | + timeStamp = Number(timeStamp) | ||
| 134 | + // 获取当前时间时间戳 | ||
| 135 | + const currentTime = Math.floor(Date.parse(new Date()) / 1000) | ||
| 136 | + // 判断传入时间戳是否早于当前时间戳 | ||
| 137 | + const IS_EARLY = isEarly(timeStamp, currentTime) | ||
| 138 | + // 获取两个时间戳差值 | ||
| 139 | + let diff = currentTime - timeStamp | ||
| 140 | + // 如果IS_EARLY为false则差值取反 | ||
| 141 | + if (!IS_EARLY) diff = -diff | ||
| 142 | + let resStr = '' | ||
| 143 | + const dirStr = IS_EARLY ? '前' : '后' | ||
| 144 | + // 少于等于59秒 | ||
| 145 | + if (diff <= 59) resStr = diff + '秒' + dirStr | ||
| 146 | + // 多于59秒,少于等于59分钟59秒 | ||
| 147 | + else if (diff > 59 && diff <= 3599) { | ||
| 148 | + resStr = Math.floor(diff / 60) + '分钟' + dirStr | ||
| 149 | + } | ||
| 150 | + // 多于59分钟59秒,少于等于23小时59分钟59秒 | ||
| 151 | + else if (diff > 3599 && diff <= 86399) { | ||
| 152 | + resStr = Math.floor(diff / 3600) + '小时' + dirStr | ||
| 153 | + } | ||
| 154 | + // 多于23小时59分钟59秒,少于等于29天59分钟59秒 | ||
| 155 | + else if (diff > 86399 && diff <= 2623859) { | ||
| 156 | + resStr = Math.floor(diff / 86400) + '天' + dirStr | ||
| 157 | + } | ||
| 158 | + // 多于29天59分钟59秒,少于364天23小时59分钟59秒,且传入的时间戳早于当前 | ||
| 159 | + else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) { | ||
| 160 | + resStr = getDate(timeStamp) | ||
| 161 | + } else resStr = getDate(timeStamp, 'year') | ||
| 162 | + return resStr | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +/** | ||
| 166 | + * @returns {String} 当前浏览器名称 | ||
| 167 | + */ | ||
| 168 | +export const getExplorer = () => { | ||
| 169 | + const ua = window.navigator.userAgent | ||
| 170 | + const isExplorer = exp => { | ||
| 171 | + return ua.indexOf(exp) > -1 | ||
| 172 | + } | ||
| 173 | + if (isExplorer('MSIE')) return 'IE' | ||
| 174 | + else if (isExplorer('Firefox')) return 'Firefox' | ||
| 175 | + else if (isExplorer('Chrome')) return 'Chrome' | ||
| 176 | + else if (isExplorer('Opera')) return 'Opera' | ||
| 177 | + else if (isExplorer('Safari')) return 'Safari' | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +/** | ||
| 181 | + * @description 绑定事件 on(element, event, handler) | ||
| 182 | + */ | ||
| 183 | +export const on = (function () { | ||
| 184 | + if (document.addEventListener) { | ||
| 185 | + return function (element, event, handler) { | ||
| 186 | + if (element && event && handler) { | ||
| 187 | + element.addEventListener(event, handler, false) | ||
| 188 | + } | ||
| 189 | + } | ||
| 190 | + } else { | ||
| 191 | + return function (element, event, handler) { | ||
| 192 | + if (element && event && handler) { | ||
| 193 | + element.attachEvent('on' + event, handler) | ||
| 194 | + } | ||
| 195 | + } | ||
| 196 | + } | ||
| 197 | +})() | ||
| 198 | + | ||
| 199 | +/** | ||
| 200 | + * @description 解绑事件 off(element, event, handler) | ||
| 201 | + */ | ||
| 202 | +export const off = (function () { | ||
| 203 | + if (document.removeEventListener) { | ||
| 204 | + return function (element, event, handler) { | ||
| 205 | + if (element && event) { | ||
| 206 | + element.removeEventListener(event, handler, false) | ||
| 207 | + } | ||
| 208 | + } | ||
| 209 | + } else { | ||
| 210 | + return function (element, event, handler) { | ||
| 211 | + if (element && event) { | ||
| 212 | + element.detachEvent('on' + event, handler) | ||
| 213 | + } | ||
| 214 | + } | ||
| 215 | + } | ||
| 216 | +})() | ||
| 217 | + | ||
| 218 | +/** | ||
| 219 | + * 判断一个对象是否存在key,如果传入第二个参数key,则是判断这个obj对象是否存在key这个属性 | ||
| 220 | + * 如果没有传入key这个参数,则判断obj对象是否有键值对 | ||
| 221 | + */ | ||
| 222 | +export const hasKey = (obj, key) => { | ||
| 223 | + if (key) return key in obj | ||
| 224 | + else { | ||
| 225 | + let keysArr = Object.keys(obj) | ||
| 226 | + return keysArr.length | ||
| 227 | + } | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | +/** | ||
| 231 | + * @param {*} obj1 对象 | ||
| 232 | + * @param {*} obj2 对象 | ||
| 233 | + * @description 判断两个对象是否相等,这两个对象的值只能是数字或字符串 | ||
| 234 | + */ | ||
| 235 | +export const objEqual = (obj1, obj2) => { | ||
| 236 | + const keysArr1 = Object.keys(obj1) | ||
| 237 | + const keysArr2 = Object.keys(obj2) | ||
| 238 | + if (keysArr1.length !== keysArr2.length) return false | ||
| 239 | + else if (keysArr1.length === 0 && keysArr2.length === 0) return true | ||
| 240 | + /* eslint-disable-next-line */ else { | ||
| 241 | + return !keysArr1.some(key => obj1[key] != obj2[key]) | ||
| 242 | + } | ||
| 243 | +} | ||
| 244 | + | ||
| 245 | +// 检测json格式 | ||
| 246 | +export const isJSON = str => { | ||
| 247 | + if (typeof str === 'string') { | ||
| 248 | + try { | ||
| 249 | + var obj = JSON.parse(str) | ||
| 250 | + if (typeof obj === 'object' && obj) { | ||
| 251 | + return true | ||
| 252 | + } else { | ||
| 253 | + return false | ||
| 254 | + } | ||
| 255 | + } catch (e) { | ||
| 256 | + return false | ||
| 257 | + } | ||
| 258 | + } else if (typeof str === 'object' && str) { | ||
| 259 | + return true | ||
| 260 | + } | ||
| 261 | +} | ||
| 262 | + | ||
| 263 | +// 美化json字符串 | ||
| 264 | +export const beautifyJSONFormat = str => { | ||
| 265 | + if (!str) return | ||
| 266 | + return JSON.stringify(JSON.parse(str), null, '\t') | ||
| 267 | +} | ||
| 268 | + | ||
| 269 | +export const handleFullscreen = type => { | ||
| 270 | + let main = document.body | ||
| 271 | + if (!type) { | ||
| 272 | + if (document.exitFullscreen) { | ||
| 273 | + document.exitFullscreen() | ||
| 274 | + } else if (document.mozCancelFullScreen) { | ||
| 275 | + document.mozCancelFullScreen() | ||
| 276 | + } else if (document.webkitCancelFullScreen) { | ||
| 277 | + document.webkitCancelFullScreen() | ||
| 278 | + } else if (document.msExitFullscreen) { | ||
| 279 | + document.msExitFullscreen() | ||
| 280 | + } | ||
| 281 | + } else { | ||
| 282 | + if (main.requestFullscreen) { | ||
| 283 | + main.requestFullscreen() | ||
| 284 | + } else if (main.mozRequestFullScreen) { | ||
| 285 | + main.mozRequestFullScreen() | ||
| 286 | + } else if (main.webkitRequestFullScreen) { | ||
| 287 | + main.webkitRequestFullScreen() | ||
| 288 | + } else if (main.msRequestFullscreen) { | ||
| 289 | + main.msRequestFullscreen() | ||
| 290 | + } | ||
| 291 | + } | ||
| 292 | +} | ||
| 293 | + | ||
| 294 | +//深拷贝对象 | ||
| 295 | +export const deepCopyObj = obj => { | ||
| 296 | + return JSON.parse(JSON.stringify(obj)) | ||
| 297 | +} | ||
| 298 | + | ||
| 299 | +// 正则去除字符串空格 | ||
| 300 | +export const strTrim = str => { | ||
| 301 | + return str.replace(/\s*/g, '') | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +// 版本号处理 | ||
| 305 | +export const versionFormatting = version => { | ||
| 306 | + return Number(version.split('.').join('')) | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +// 递归打印对象 | ||
| 310 | +export const logProperties = obj => { | ||
| 311 | + if (typeof obj !== 'object' || obj === null) { | ||
| 312 | + console.log(obj) | ||
| 313 | + return | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | + var properties = Object.getOwnPropertyNames(obj) | ||
| 317 | + properties.forEach(function (prop) { | ||
| 318 | + console.log(prop + ':', obj[prop]) | ||
| 319 | + logProperties(obj[prop]) | ||
| 320 | + }) | ||
| 321 | +} |
src/libs/util.js
0 → 100644
| 1 | +import config from '@/config' | ||
| 2 | +import storage from '@/libs/storageUtil' | ||
| 3 | +import { forEach, hasOneOf, objEqual } from '@/libs/tools' | ||
| 4 | + | ||
| 5 | +const { title, useI18n } = config | ||
| 6 | + | ||
| 7 | +export const TOKEN_KEY = 'token' | ||
| 8 | + | ||
| 9 | +export const setToken = token => { | ||
| 10 | + storage.set(TOKEN_KEY, token) | ||
| 11 | +} | ||
| 12 | + | ||
| 13 | +export const getToken = () => { | ||
| 14 | + const token = storage.get(TOKEN_KEY) | ||
| 15 | + if (token) return token | ||
| 16 | + else return false | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +export const hasChild = item => { | ||
| 20 | + return item.children && item.children.length !== 0 | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +const showThisMenuEle = (item, access) => { | ||
| 24 | + if (item.meta && item.meta.access && item.meta.access.length) { | ||
| 25 | + if (hasOneOf(item.meta.access, access)) return true | ||
| 26 | + else return false | ||
| 27 | + } else return true | ||
| 28 | +} | ||
| 29 | +/** | ||
| 30 | + * @param {Array} list 通过路由列表得到菜单列表 | ||
| 31 | + * @returns {Array} | ||
| 32 | + */ | ||
| 33 | +export const getMenuByRouter = (list, access) => { | ||
| 34 | + let res = [] | ||
| 35 | + forEach(list, item => { | ||
| 36 | + if (!item.meta || (item.meta && !item.meta.hideInMenu)) { | ||
| 37 | + let obj = { | ||
| 38 | + icon: (item.meta && item.meta.icon) || '', | ||
| 39 | + name: item.name, | ||
| 40 | + meta: item.meta | ||
| 41 | + } | ||
| 42 | + if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) { | ||
| 43 | + obj.children = getMenuByRouter(item.children, access) | ||
| 44 | + } | ||
| 45 | + if (item.meta && item.meta.href) obj.href = item.meta.href | ||
| 46 | + if (showThisMenuEle(item, access)) res.push(obj) | ||
| 47 | + } | ||
| 48 | + }) | ||
| 49 | + return res | ||
| 50 | +} | ||
| 51 | + | ||
| 52 | +/** | ||
| 53 | + * @param {Array} routeMetched 当前路由metched | ||
| 54 | + * @returns {Array} | ||
| 55 | + */ | ||
| 56 | +export const getBreadCrumbList = (route, homeRoute) => { | ||
| 57 | + let homeItem = { ...homeRoute, icon: homeRoute.meta.icon } | ||
| 58 | + let routeMetched = route.matched | ||
| 59 | + if (routeMetched.some(item => item.name === homeRoute.name)) return [homeItem] | ||
| 60 | + let res = routeMetched | ||
| 61 | + .filter(item => { | ||
| 62 | + return item.meta === undefined || !item.meta.hideInBread | ||
| 63 | + }) | ||
| 64 | + .map(item => { | ||
| 65 | + let meta = { ...item.meta } | ||
| 66 | + if (meta.title && typeof meta.title === 'function') { | ||
| 67 | + meta.__titleIsFunction__ = true | ||
| 68 | + meta.title = meta.title(route) | ||
| 69 | + } | ||
| 70 | + let obj = { | ||
| 71 | + icon: (item.meta && item.meta.icon) || '', | ||
| 72 | + name: item.name, | ||
| 73 | + meta: meta | ||
| 74 | + } | ||
| 75 | + return obj | ||
| 76 | + }) | ||
| 77 | + res = res.filter(item => { | ||
| 78 | + return !item.meta.hideInMenu | ||
| 79 | + }) | ||
| 80 | + return [{ ...homeItem, to: homeRoute.path }, ...res] | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +export const getRouteTitleHandled = route => { | ||
| 84 | + let router = { ...route } | ||
| 85 | + let meta = { ...route.meta } | ||
| 86 | + let title = '' | ||
| 87 | + if (meta.title) { | ||
| 88 | + if (typeof meta.title === 'function') { | ||
| 89 | + meta.__titleIsFunction__ = true | ||
| 90 | + title = meta.title(router) | ||
| 91 | + } else title = meta.title | ||
| 92 | + } | ||
| 93 | + meta.title = title | ||
| 94 | + router.meta = meta | ||
| 95 | + return router | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +export const showTitle = (item, vm) => { | ||
| 99 | + let { title, __titleIsFunction__ } = item.meta | ||
| 100 | + if (!title) return | ||
| 101 | + if (useI18n) { | ||
| 102 | + if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\s\S]+?}})/, (m, str) => str.replace(/{{([\s\S]*)}}/, (m, _) => vm.$t(_.trim()))) | ||
| 103 | + else if (__titleIsFunction__) title = item.meta.title | ||
| 104 | + else title = vm.$t(item.name) | ||
| 105 | + } else title = (item.meta && item.meta.title) || item.name | ||
| 106 | + return title | ||
| 107 | +} | ||
| 108 | + | ||
| 109 | +/** | ||
| 110 | + * @description 本地存储和获取标签导航列表 | ||
| 111 | + */ | ||
| 112 | +export const setTagNavListInLocalstorage = list => { | ||
| 113 | + localStorage.tagNaveList = JSON.stringify(list) | ||
| 114 | +} | ||
| 115 | +/** | ||
| 116 | + * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项 | ||
| 117 | + */ | ||
| 118 | +export const getTagNavListFromLocalstorage = () => { | ||
| 119 | + const list = localStorage.tagNaveList | ||
| 120 | + return list ? JSON.parse(list) : [] | ||
| 121 | +} | ||
| 122 | + | ||
| 123 | +/** | ||
| 124 | + * @param {Array} routers 路由列表数组 | ||
| 125 | + * @description 用于找到路由列表中name为home的对象 | ||
| 126 | + */ | ||
| 127 | +export const getHomeRoute = (routers, homeName = 'home') => { | ||
| 128 | + let i = -1 | ||
| 129 | + let len = routers.length | ||
| 130 | + let homeRoute = {} | ||
| 131 | + while (++i < len) { | ||
| 132 | + let item = routers[i] | ||
| 133 | + if (item.children && item.children.length) { | ||
| 134 | + let res = getHomeRoute(item.children, homeName) | ||
| 135 | + if (res.name) return res | ||
| 136 | + } else { | ||
| 137 | + if (item.name === homeName) homeRoute = item | ||
| 138 | + } | ||
| 139 | + } | ||
| 140 | + return homeRoute | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +/** | ||
| 144 | + * @param {*} list 现有标签导航列表 | ||
| 145 | + * @param {*} newRoute 新添加的路由原信息对象 | ||
| 146 | + * @description 如果该newRoute已经存在则不再添加 | ||
| 147 | + */ | ||
| 148 | +export const getNewTagList = (list, newRoute) => { | ||
| 149 | + const { name, path, meta } = newRoute | ||
| 150 | + let newList = [...list] | ||
| 151 | + if (newList.findIndex(item => item.name === name) >= 0) return newList | ||
| 152 | + else newList.push({ name, path, meta }) | ||
| 153 | + return newList | ||
| 154 | +} | ||
| 155 | + | ||
| 156 | +/** | ||
| 157 | + * @param {*} access 用户权限数组,如 ['super_admin', 'admin'] | ||
| 158 | + * @param {*} route 路由列表 | ||
| 159 | + */ | ||
| 160 | +const hasAccess = (access, route) => { | ||
| 161 | + if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access) | ||
| 162 | + else return true | ||
| 163 | +} | ||
| 164 | + | ||
| 165 | +/** | ||
| 166 | + * 权鉴 | ||
| 167 | + * @param {*} name 即将跳转的路由name | ||
| 168 | + * @param {*} access 用户权限数组 | ||
| 169 | + * @param {*} routes 路由列表 | ||
| 170 | + * @description 用户是否可跳转到该页 | ||
| 171 | + */ | ||
| 172 | +export const canTurnTo = (name, access, routes) => { | ||
| 173 | + const routePermissionJudge = list => { | ||
| 174 | + return list.some(item => { | ||
| 175 | + if (item.children && item.children.length) { | ||
| 176 | + return routePermissionJudge(item.children) | ||
| 177 | + } else if (item.name === name) { | ||
| 178 | + return hasAccess(access, item) | ||
| 179 | + } | ||
| 180 | + }) | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + return routePermissionJudge(routes) | ||
| 184 | +} | ||
| 185 | + | ||
| 186 | +/** | ||
| 187 | + * @param {String} url | ||
| 188 | + * @description 从URL中解析参数 | ||
| 189 | + */ | ||
| 190 | +export const getParams = url => { | ||
| 191 | + const keyValueArr = url.split('?')[1].split('&') | ||
| 192 | + let paramObj = {} | ||
| 193 | + keyValueArr.forEach(item => { | ||
| 194 | + const keyValue = item.split('=') | ||
| 195 | + paramObj[keyValue[0]] = keyValue[1] | ||
| 196 | + }) | ||
| 197 | + return paramObj | ||
| 198 | +} | ||
| 199 | + | ||
| 200 | +/** | ||
| 201 | + * @param {Array} list 标签列表 | ||
| 202 | + * @param {String} name 当前关闭的标签的name | ||
| 203 | + */ | ||
| 204 | +export const getNextRoute = (list, route) => { | ||
| 205 | + let res = {} | ||
| 206 | + if (list.length === 2) { | ||
| 207 | + res = getHomeRoute(list) | ||
| 208 | + } else { | ||
| 209 | + const index = list.findIndex(item => routeEqual(item, route)) | ||
| 210 | + if (index === list.length - 1) res = list[list.length - 2] | ||
| 211 | + else res = list[index + 1] | ||
| 212 | + } | ||
| 213 | + return res | ||
| 214 | +} | ||
| 215 | + | ||
| 216 | +/** | ||
| 217 | + * @param {Number} times 回调函数需要执行的次数 | ||
| 218 | + * @param {Function} callback 回调函数 | ||
| 219 | + */ | ||
| 220 | +export const doCustomTimes = (times, callback) => { | ||
| 221 | + let i = -1 | ||
| 222 | + while (++i < times) { | ||
| 223 | + callback(i) | ||
| 224 | + } | ||
| 225 | +} | ||
| 226 | + | ||
| 227 | +/** | ||
| 228 | + * @param {Object} file 从上传组件得到的文件对象 | ||
| 229 | + * @returns {Promise} resolve参数是解析后的二维数组 | ||
| 230 | + * @description 从Csv文件中解析出表格,解析成二维数组 | ||
| 231 | + */ | ||
| 232 | +export const getArrayFromFile = file => { | ||
| 233 | + let nameSplit = file.name.split('.') | ||
| 234 | + let format = nameSplit[nameSplit.length - 1] | ||
| 235 | + return new Promise((resolve, reject) => { | ||
| 236 | + let reader = new FileReader() | ||
| 237 | + reader.readAsText(file) // 以文本格式读取 | ||
| 238 | + let arr = [] | ||
| 239 | + reader.onload = function (evt) { | ||
| 240 | + let data = evt.target.result // 读到的数据 | ||
| 241 | + let pasteData = data.trim() | ||
| 242 | + arr = pasteData | ||
| 243 | + .split(/[\n\u0085\u2028\u2029]|\r\n?/g) | ||
| 244 | + .map(row => { | ||
| 245 | + return row.split('\t') | ||
| 246 | + }) | ||
| 247 | + .map(item => { | ||
| 248 | + return item[0].split(',') | ||
| 249 | + }) | ||
| 250 | + if (format === 'csv') resolve(arr) | ||
| 251 | + else reject(new Error('[Format Error]:你上传的不是Csv文件')) | ||
| 252 | + } | ||
| 253 | + }) | ||
| 254 | +} | ||
| 255 | + | ||
| 256 | +/** | ||
| 257 | + * @param {Array} array 表格数据二维数组 | ||
| 258 | + * @returns {Object} { columns, tableData } | ||
| 259 | + * @description 从二维数组中获取表头和表格数据,将第一行作为表头,用于在iView的表格中展示数据 | ||
| 260 | + */ | ||
| 261 | +export const getTableDataFromArray = array => { | ||
| 262 | + let columns = [] | ||
| 263 | + let tableData = [] | ||
| 264 | + if (array.length > 1) { | ||
| 265 | + let titles = array.shift() | ||
| 266 | + columns = titles.map(item => { | ||
| 267 | + return { | ||
| 268 | + title: item, | ||
| 269 | + key: item | ||
| 270 | + } | ||
| 271 | + }) | ||
| 272 | + tableData = array.map(item => { | ||
| 273 | + let res = {} | ||
| 274 | + item.forEach((col, i) => { | ||
| 275 | + res[titles[i]] = col | ||
| 276 | + }) | ||
| 277 | + return res | ||
| 278 | + }) | ||
| 279 | + } | ||
| 280 | + return { | ||
| 281 | + columns, | ||
| 282 | + tableData | ||
| 283 | + } | ||
| 284 | +} | ||
| 285 | + | ||
| 286 | +export const findNodeUpper = (ele, tag) => { | ||
| 287 | + if (ele.parentNode) { | ||
| 288 | + if (ele.parentNode.tagName === tag.toUpperCase()) { | ||
| 289 | + return ele.parentNode | ||
| 290 | + } else { | ||
| 291 | + return findNodeUpper(ele.parentNode, tag) | ||
| 292 | + } | ||
| 293 | + } | ||
| 294 | +} | ||
| 295 | + | ||
| 296 | +export const findNodeUpperByClasses = (ele, classes) => { | ||
| 297 | + let parentNode = ele.parentNode | ||
| 298 | + if (parentNode) { | ||
| 299 | + let classList = parentNode.classList | ||
| 300 | + if (classList && classes.every(className => classList.contains(className))) { | ||
| 301 | + return parentNode | ||
| 302 | + } else { | ||
| 303 | + return findNodeUpperByClasses(parentNode, classes) | ||
| 304 | + } | ||
| 305 | + } | ||
| 306 | +} | ||
| 307 | + | ||
| 308 | +export const findNodeDownward = (ele, tag) => { | ||
| 309 | + const tagName = tag.toUpperCase() | ||
| 310 | + if (ele.childNodes.length) { | ||
| 311 | + let i = -1 | ||
| 312 | + let len = ele.childNodes.length | ||
| 313 | + while (++i < len) { | ||
| 314 | + let child = ele.childNodes[i] | ||
| 315 | + if (child.tagName === tagName) return child | ||
| 316 | + else return findNodeDownward(child, tag) | ||
| 317 | + } | ||
| 318 | + } | ||
| 319 | +} | ||
| 320 | + | ||
| 321 | +export const showByAccess = (access, canViewAccess) => { | ||
| 322 | + return hasOneOf(canViewAccess, access) | ||
| 323 | +} | ||
| 324 | + | ||
| 325 | +/** | ||
| 326 | + * @description 根据name/params/query判断两个路由对象是否相等 | ||
| 327 | + * @param {*} route1 路由对象 | ||
| 328 | + * @param {*} route2 路由对象 | ||
| 329 | + */ | ||
| 330 | +export const routeEqual = (route1, route2) => { | ||
| 331 | + const params1 = route1.params || {} | ||
| 332 | + const params2 = route2.params || {} | ||
| 333 | + const query1 = route1.query || {} | ||
| 334 | + const query2 = route2.query || {} | ||
| 335 | + return route1.name === route2.name && objEqual(params1, params2) && objEqual(query1, query2) | ||
| 336 | +} | ||
| 337 | + | ||
| 338 | +/** | ||
| 339 | + * 判断打开的标签列表里是否已存在这个新添加的路由对象 | ||
| 340 | + */ | ||
| 341 | +export const routeHasExist = (tagNavList, routeItem) => { | ||
| 342 | + let len = tagNavList.length | ||
| 343 | + let res = false | ||
| 344 | + doCustomTimes(len, index => { | ||
| 345 | + if (routeEqual(tagNavList[index], routeItem)) res = true | ||
| 346 | + }) | ||
| 347 | + return res | ||
| 348 | +} | ||
| 349 | + | ||
| 350 | +export const localSave = (key, value) => { | ||
| 351 | + localStorage.setItem(key, value) | ||
| 352 | +} | ||
| 353 | + | ||
| 354 | +export const localRead = key => { | ||
| 355 | + return localStorage.getItem(key) || '' | ||
| 356 | +} | ||
| 357 | + | ||
| 358 | +// scrollTop animation | ||
| 359 | +export const scrollTop = (el, from = 0, to, duration = 500, endCallback) => { | ||
| 360 | + if (!window.requestAnimationFrame) { | ||
| 361 | + window.requestAnimationFrame = | ||
| 362 | + window.webkitRequestAnimationFrame || | ||
| 363 | + window.mozRequestAnimationFrame || | ||
| 364 | + window.msRequestAnimationFrame || | ||
| 365 | + function (callback) { | ||
| 366 | + return window.setTimeout(callback, 1000 / 60) | ||
| 367 | + } | ||
| 368 | + } | ||
| 369 | + const difference = Math.abs(from - to) | ||
| 370 | + const step = Math.ceil((difference / duration) * 50) | ||
| 371 | + | ||
| 372 | + const scroll = (start, end, step) => { | ||
| 373 | + if (start === end) { | ||
| 374 | + endCallback && endCallback() | ||
| 375 | + return | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + let d = start + step > end ? end : start + step | ||
| 379 | + if (start > end) { | ||
| 380 | + d = start - step < end ? end : start - step | ||
| 381 | + } | ||
| 382 | + | ||
| 383 | + if (el === window) { | ||
| 384 | + window.scrollTo(d, d) | ||
| 385 | + } else { | ||
| 386 | + el.scrollTop = d | ||
| 387 | + } | ||
| 388 | + window.requestAnimationFrame(() => scroll(d, end, step)) | ||
| 389 | + } | ||
| 390 | + scroll(from, to, step) | ||
| 391 | +} | ||
| 392 | + | ||
| 393 | +/** | ||
| 394 | + * @description 根据当前跳转的路由设置显示在浏览器标签的title | ||
| 395 | + * @param {Object} routeItem 路由对象 | ||
| 396 | + * @param {Object} vm Vue实例 | ||
| 397 | + */ | ||
| 398 | +export const setTitle = (routeItem, vm) => { | ||
| 399 | + const handledRoute = getRouteTitleHandled(routeItem) | ||
| 400 | + const pageTitle = showTitle(handledRoute, vm) | ||
| 401 | + const resTitle = pageTitle ? `${title} - ${pageTitle}` : title | ||
| 402 | + window.document.title = resTitle | ||
| 403 | +} |
src/main.js
0 → 100644
| 1 | +import Vue from 'vue' | ||
| 2 | +import App from './App.vue' | ||
| 3 | +import router from './router' | ||
| 4 | +import store from './store' | ||
| 5 | +import Vant from 'vant' | ||
| 6 | +import { Lazyload } from 'vant' | ||
| 7 | +import commMixin from '@/mixin/comm' | ||
| 8 | +import 'vant/lib/index.css' | ||
| 9 | +import { prettyPrint } from './filters' | ||
| 10 | +import loggerPlugin from './plugin/loggerPlugin' | ||
| 11 | + | ||
| 12 | +// import VueDragResize from 'vue-drag-resize' | ||
| 13 | +// Vue.component('vue-drag-resize', VueDragResize) | ||
| 14 | +Vue.use(Vant) | ||
| 15 | +Vue.use(Lazyload) | ||
| 16 | +Vue.use(loggerPlugin, store) | ||
| 17 | + | ||
| 18 | +Vue.filter('prettyPrint', prettyPrint) | ||
| 19 | +Vue.config.productionTip = false | ||
| 20 | + | ||
| 21 | +Vue.config.errorHandler = function (error, vm, info) { | ||
| 22 | + console.error('Error message:' + error.message, 'Error stack:' + error.stack) | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +// function getObjToArrayBufferDoLength(obj) { | ||
| 26 | +// // 将对象序列化为 JSON 字符串 | ||
| 27 | +// const json = JSON.stringify(obj) | ||
| 28 | +// // 创建一个新的 TextEncoder 对象 | ||
| 29 | +// const encoder = new TextEncoder() | ||
| 30 | +// // 将 JSON 字符串编码为 Uint8Array | ||
| 31 | +// const data = encoder.encode(json) | ||
| 32 | +// // 创建一个新的 ArrayBuffer,大小为数据长度 | ||
| 33 | +// const buffer = new ArrayBuffer(data.length) | ||
| 34 | +// // 获取 ArrayBuffer 的视图(Uint8Array) | ||
| 35 | +// const view = new Uint8Array(buffer) | ||
| 36 | +// // 将数据复制到 ArrayBuffer 中 | ||
| 37 | +// view.set(data) | ||
| 38 | +// return view.byteLength | ||
| 39 | +// } | ||
| 40 | + | ||
| 41 | +Vue.mixin(commMixin) | ||
| 42 | +const app = new Vue({ | ||
| 43 | + router, | ||
| 44 | + store, | ||
| 45 | + created() { | ||
| 46 | + this.setIsApp() | ||
| 47 | + setTimeout(() => { | ||
| 48 | + if (!this.getIsApp) { | ||
| 49 | + rewriteXHR() | ||
| 50 | + } | ||
| 51 | + }, 0) | ||
| 52 | + }, | ||
| 53 | + methods: { | ||
| 54 | + setIsApp() { | ||
| 55 | + let isApp = false | ||
| 56 | + try { | ||
| 57 | + isApp = webf ? true : false | ||
| 58 | + } catch (error) { | ||
| 59 | + isApp = false | ||
| 60 | + } | ||
| 61 | + this.$store.commit('setIsApp', isApp) | ||
| 62 | + } | ||
| 63 | + }, | ||
| 64 | + render: h => h(App) | ||
| 65 | +}).$mount('#app') | ||
| 66 | + | ||
| 67 | +function rewriteXHR() { | ||
| 68 | + // 保存原始的 XMLHttpRequest 构造函数 | ||
| 69 | + let OriginalXHR = window.XMLHttpRequest | ||
| 70 | + // 创建新的 XMLHttpRequest 构造函数,扩展了原始的构造函数 | ||
| 71 | + function CustomXHR(app) { | ||
| 72 | + let xhr = new OriginalXHR() | ||
| 73 | + let sendDate = 0 | ||
| 74 | + xhr.addEventListener('loadstart', function (event) { | ||
| 75 | + sendDate = Date.now() | ||
| 76 | + }) | ||
| 77 | + xhr.addEventListener('load', function (event) { | ||
| 78 | + const responseURL = xhr.response.url || xhr.responseURL | ||
| 79 | + const response = xhr.response.body || xhr.response | ||
| 80 | + try { | ||
| 81 | + console.netwokrLog({ | ||
| 82 | + name: responseURL.split('/').pop(), | ||
| 83 | + status: xhr.status, | ||
| 84 | + statusText: xhr.statusText, | ||
| 85 | + response: response, | ||
| 86 | + responseURL: responseURL, | ||
| 87 | + size: 0, | ||
| 88 | + time: Date.now() - sendDate | ||
| 89 | + }) | ||
| 90 | + } catch (error) { | ||
| 91 | + console.log('error:', error) | ||
| 92 | + } | ||
| 93 | + }) | ||
| 94 | + xhr.addEventListener('error', function (event) { | ||
| 95 | + console.log('error:', event) | ||
| 96 | + }) | ||
| 97 | + return xhr | ||
| 98 | + } | ||
| 99 | + // 重写全局的 XMLHttpRequest 构造函数 | ||
| 100 | + window.XMLHttpRequest = CustomXHR | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +// new Vue({ | ||
| 104 | +// router, | ||
| 105 | +// store, | ||
| 106 | +// render: h => h(App) | ||
| 107 | +// }).$mount(document.body) |
src/mixin/comm.js
0 → 100644
src/plugin/loggerPlugin.js
0 → 100644
| 1 | +// loggerPlugin.js | ||
| 2 | +export default { | ||
| 3 | + install(Vue, options) { | ||
| 4 | + // 创建一个 Vue 实例作为事件总线 | ||
| 5 | + // const bus = new Vue() | ||
| 6 | + const cLog = console.log | ||
| 7 | + const cError = console.error | ||
| 8 | + options.commit('setStoreLog', cLog) | ||
| 9 | + | ||
| 10 | + console.netwokrLog = function (data) { | ||
| 11 | + options.commit('addNetworkLoggerData', { | ||
| 12 | + sendTime: Date.now(), | ||
| 13 | + ...data | ||
| 14 | + }) | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + console.log = function (data) { | ||
| 18 | + cLog(...arguments) | ||
| 19 | + if (arguments[0]) { | ||
| 20 | + let length = arguments.length | ||
| 21 | + let logTime = Date.now() | ||
| 22 | + for (let index = 0; index < length; index++) { | ||
| 23 | + const item = arguments[index] | ||
| 24 | + options.commit('addLoggerData', { | ||
| 25 | + logType: 'log', | ||
| 26 | + logId: logTime, | ||
| 27 | + type: typeof item, | ||
| 28 | + log: item | ||
| 29 | + }) | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + console.error = function () { | ||
| 35 | + cError(...arguments) | ||
| 36 | + let length = arguments.length | ||
| 37 | + let logTime = Date.now() | ||
| 38 | + for (let index = 0; index < length; index++) { | ||
| 39 | + const item = arguments[index] | ||
| 40 | + options.commit('addLoggerData', { | ||
| 41 | + logType: 'error', | ||
| 42 | + logId: logTime, | ||
| 43 | + type: typeof item, | ||
| 44 | + log: typeof item === 'object' ? JSON.stringify(item) : item | ||
| 45 | + }) | ||
| 46 | + } | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + Vue.config.errorHandler = function (error, vm, info) { | ||
| 50 | + console.error('Error message:' + error.message, 'Error stack:' + error.stack) | ||
| 51 | + } | ||
| 52 | + } | ||
| 53 | +} |
src/router/index.js
0 → 100644
| 1 | +import Vue from 'vue' | ||
| 2 | +import VueRouter from 'vue-router' | ||
| 3 | +import HomeView from '@/views/HomeView.vue' | ||
| 4 | + | ||
| 5 | +Vue.use(VueRouter) | ||
| 6 | + | ||
| 7 | +// 获取原型对象push函数 | ||
| 8 | +const originalPush = VueRouter.prototype.push | ||
| 9 | + | ||
| 10 | +// 获取原型对象replace函数 | ||
| 11 | +const originalReplace = VueRouter.prototype.replace | ||
| 12 | + | ||
| 13 | +// 修改原型对象中的push函数 | ||
| 14 | +VueRouter.prototype.push = function push(location) { | ||
| 15 | + return originalPush.call(this, location).catch(err => err) | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +// 修改原型对象中的replace函数 | ||
| 19 | +VueRouter.prototype.replace = function replace(location) { | ||
| 20 | + return originalReplace.call(this, location).catch(err => err) | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +const routes = [ | ||
| 24 | + { | ||
| 25 | + path: '/', | ||
| 26 | + name: 'HomeView', | ||
| 27 | + meta: { | ||
| 28 | + title: '首页', | ||
| 29 | + icon: 'home-o', | ||
| 30 | + isTabBarMenu: true, | ||
| 31 | + keepAlive: true, | ||
| 32 | + showNavBar: true | ||
| 33 | + }, | ||
| 34 | + component: HomeView | ||
| 35 | + }, | ||
| 36 | + { | ||
| 37 | + path: '/caricature', | ||
| 38 | + name: 'CaricatureView', | ||
| 39 | + meta: { | ||
| 40 | + title: '漫画', | ||
| 41 | + icon: 'home-o', | ||
| 42 | + isTabBarMenu: true, | ||
| 43 | + keepAlive: true, | ||
| 44 | + showNavBar: true | ||
| 45 | + }, | ||
| 46 | + component: () => import('../views/CaricatureView.vue') | ||
| 47 | + }, | ||
| 48 | + { | ||
| 49 | + path: '/active', | ||
| 50 | + name: 'ActiveView', | ||
| 51 | + meta: { | ||
| 52 | + title: '活动', | ||
| 53 | + icon: 'home-o', | ||
| 54 | + isTabBarMenu: true, | ||
| 55 | + keepAlive: true, | ||
| 56 | + showNavBar: true | ||
| 57 | + }, | ||
| 58 | + component: () => import('../views/ActiveView.vue') | ||
| 59 | + }, | ||
| 60 | + { | ||
| 61 | + path: '/live', | ||
| 62 | + name: 'LiveView', | ||
| 63 | + meta: { | ||
| 64 | + title: '直播', | ||
| 65 | + icon: 'home-o', | ||
| 66 | + isTabBarMenu: true, | ||
| 67 | + keepAlive: true, | ||
| 68 | + showNavBar: true | ||
| 69 | + }, | ||
| 70 | + component: () => import('../views/LiveView.vue') | ||
| 71 | + }, | ||
| 72 | + { | ||
| 73 | + path: '/mine', | ||
| 74 | + name: 'MineView', | ||
| 75 | + meta: { | ||
| 76 | + title: '我的', | ||
| 77 | + icon: 'home-o', | ||
| 78 | + isTabBarMenu: true, | ||
| 79 | + keepAlive: true, | ||
| 80 | + showNavBar: true | ||
| 81 | + }, | ||
| 82 | + component: () => import('../views/MineView.vue') | ||
| 83 | + }, | ||
| 84 | + { | ||
| 85 | + path: '/detail', | ||
| 86 | + name: 'DetailView', | ||
| 87 | + meta: { | ||
| 88 | + title: '详情', | ||
| 89 | + isTabBarMenu: false, | ||
| 90 | + showNavBar: true | ||
| 91 | + }, | ||
| 92 | + component: () => import('../views/DetailView.vue') | ||
| 93 | + } | ||
| 94 | +] | ||
| 95 | + | ||
| 96 | +const router = new VueRouter({ | ||
| 97 | + // mode: 'history', | ||
| 98 | + // base: process.env.BASE_URL, | ||
| 99 | + routes | ||
| 100 | +}) | ||
| 101 | + | ||
| 102 | +export default router |
src/store/index.js
0 → 100644
| 1 | +import Vue from 'vue' | ||
| 2 | +import Vuex from 'vuex' | ||
| 3 | + | ||
| 4 | +Vue.use(Vuex) | ||
| 5 | +export default new Vuex.Store({ | ||
| 6 | + state: { | ||
| 7 | + isApp: false, | ||
| 8 | + appNavBarShow: false, | ||
| 9 | + systemInfo: { | ||
| 10 | + statusHeight: 0 | ||
| 11 | + }, | ||
| 12 | + loggerData: [], | ||
| 13 | + networkLoggerData: [], | ||
| 14 | + /** | ||
| 15 | + * @description: 如果需要在addLoggerData/addNetworkLoggerData打印log | ||
| 16 | + * @description: 请使用:storeLog(log)的方式避免死循环 | ||
| 17 | + * @return {*} | ||
| 18 | + */ | ||
| 19 | + storeLog: null | ||
| 20 | + }, | ||
| 21 | + getters: { | ||
| 22 | + getIsApp: state => state.isApp, | ||
| 23 | + getAppNavBarShow: state => state.appNavBarShow, | ||
| 24 | + getSystemInfo: state => state.systemInfo, | ||
| 25 | + getTopOffsetHeight: state => { | ||
| 26 | + if (state.isApp) { | ||
| 27 | + if (state.appNavBarShow) { | ||
| 28 | + return 46 + state.systemInfo.statusHeight | ||
| 29 | + } else { | ||
| 30 | + return 46 | ||
| 31 | + } | ||
| 32 | + } else { | ||
| 33 | + return 0 | ||
| 34 | + } | ||
| 35 | + }, | ||
| 36 | + getLoggerData: state => state.loggerData, | ||
| 37 | + getNetworkLoggerData: state => state.networkLoggerData | ||
| 38 | + }, | ||
| 39 | + mutations: { | ||
| 40 | + setIsApp(state, isApp) { | ||
| 41 | + state.isApp = isApp | ||
| 42 | + }, | ||
| 43 | + setAppNavBarShow(state, isShow) { | ||
| 44 | + state.appNavBarShow = isShow | ||
| 45 | + }, | ||
| 46 | + | ||
| 47 | + setSystemInfo(state, systemInfo) { | ||
| 48 | + state.systemInfo = systemInfo | ||
| 49 | + }, | ||
| 50 | + setStoreLog(state, log) { | ||
| 51 | + state.storeLog = log | ||
| 52 | + }, | ||
| 53 | + addLoggerData(state, log) { | ||
| 54 | + state.loggerData.push(log) | ||
| 55 | + }, | ||
| 56 | + addNetworkLoggerData(state, networkLog) { | ||
| 57 | + state.networkLoggerData.push(networkLog) | ||
| 58 | + } | ||
| 59 | + }, | ||
| 60 | + actions: {}, | ||
| 61 | + modules: {} | ||
| 62 | +}) |
src/views/ActiveView.vue
0 → 100644
src/views/CaricatureView.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="about"> | ||
| 3 | + <h1>漫画</h1> | ||
| 4 | + <van-button type="primary" @click="jump">测试跳转</van-button> | ||
| 5 | + </div> | ||
| 6 | +</template> | ||
| 7 | +<script> | ||
| 8 | +export default { | ||
| 9 | + name: 'CaricatureView', | ||
| 10 | + data() { | ||
| 11 | + return {} | ||
| 12 | + }, | ||
| 13 | + methods: { | ||
| 14 | + jump() { | ||
| 15 | + this.$router.push({ | ||
| 16 | + name: 'DetailView' | ||
| 17 | + }) | ||
| 18 | + } | ||
| 19 | + } | ||
| 20 | +} | ||
| 21 | +</script> |
src/views/DetailView.vue
0 → 100644
src/views/HomeView.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="home-page"> | ||
| 3 | + <div class="header-nav-bar" :style="{ top: getTopOffsetHeight + 'px' }"> | ||
| 4 | + <div class="tabs-view"> | ||
| 5 | + <div class="tab-item" :class="{ active: activeIndex === index }" v-for="(item, index) in tabList" :key="item.value" @click="changeTabIndex(index)"> | ||
| 6 | + {{ item.label }} | ||
| 7 | + <div class="active-line" :class="{ show: activeIndex === index }" /> | ||
| 8 | + </div> | ||
| 9 | + </div> | ||
| 10 | + <div class="icon-list"> | ||
| 11 | + <span class="iconfont icon-shezhi"></span> | ||
| 12 | + <span class="iconfont icon-kefu"></span> | ||
| 13 | + </div> | ||
| 14 | + </div> | ||
| 15 | + <div class="search-view" :style="{ top: getTopOffsetHeight + 44 + 'px' }"> | ||
| 16 | + <van-search shape="round" v-model="searchKeyWord" placeholder="请输入搜索关键词" /> | ||
| 17 | + </div> | ||
| 18 | + <div v-if="activeIndex === 0"> | ||
| 19 | + <div class="banner-view"> | ||
| 20 | + <van-swipe class="my-swipe" indicator-color="white"> | ||
| 21 | + <van-swipe-item v-for="(item, index) in images" :key="index"> | ||
| 22 | + <img class="swipe-img" :src="item" /> | ||
| 23 | + </van-swipe-item> | ||
| 24 | + </van-swipe> | ||
| 25 | + </div> | ||
| 26 | + <div class="notice-view"> | ||
| 27 | + <van-notice-bar left-icon="volume-o" background="#f8f8fb" text="在代码阅读过程中人们说脏话的频率是衡量代码质量的唯一标准。"> | ||
| 28 | + <template #right-icon> | ||
| 29 | + <van-button class="hot-live-btn" size="mini" icon="fire-o" type="primary" @click="showGlobalLog">热门直播</van-button> | ||
| 30 | + </template> | ||
| 31 | + </van-notice-bar> | ||
| 32 | + </div> | ||
| 33 | + <div class="list-view"> | ||
| 34 | + <collapse v-model="competitionActiveNames"> | ||
| 35 | + <collapse-item v-for="(item, index) in competitionList" :key="index" :title="`标题${index + 1}`" :margin-bottom="4"> | ||
| 36 | + <template #collapseExtra> | ||
| 37 | + <div class="competition-content"> | ||
| 38 | + <div>{{ item.a }}</div> | ||
| 39 | + <div>{{ item.b }}</div> | ||
| 40 | + </div> | ||
| 41 | + </template> | ||
| 42 | + </collapse-item> | ||
| 43 | + </collapse> | ||
| 44 | + </div> | ||
| 45 | + </div> | ||
| 46 | + <div v-if="activeIndex === 1">娱乐123</div> | ||
| 47 | + <div v-if="activeIndex === 2">小说</div> | ||
| 48 | + <div v-if="activeIndex === 3">GPT</div> | ||
| 49 | + <MyPopup v-model="myPopupOpen" position="right" width="80%" :round="false" :show-close-icon="true" popup-title="弹出框标题" :popup-title-text-style="{ textAlign: 'left' }"> | ||
| 50 | + 内容 | ||
| 51 | + </MyPopup> | ||
| 52 | + </div> | ||
| 53 | +</template> | ||
| 54 | + | ||
| 55 | +<script> | ||
| 56 | +import Collapse from '@/components/Collapse/Collapse.vue' | ||
| 57 | +import CollapseItem from '@/components/Collapse/CollapseItem.vue' | ||
| 58 | +import MyPopup from '@/components/MyPopup/MyPopup.vue' | ||
| 59 | +export default { | ||
| 60 | + name: 'HomeView', | ||
| 61 | + components: { Collapse, CollapseItem, MyPopup }, | ||
| 62 | + data() { | ||
| 63 | + return { | ||
| 64 | + myPopupOpen: false, | ||
| 65 | + searchKeyWord: '', | ||
| 66 | + activeIndex: 0, | ||
| 67 | + isBottom: false, | ||
| 68 | + tabList: [ | ||
| 69 | + { | ||
| 70 | + label: '新闻', | ||
| 71 | + value: 'news' | ||
| 72 | + }, | ||
| 73 | + { | ||
| 74 | + label: '娱乐', | ||
| 75 | + value: 'recreation' | ||
| 76 | + }, | ||
| 77 | + { | ||
| 78 | + label: '小说', | ||
| 79 | + value: 'fiction' | ||
| 80 | + }, | ||
| 81 | + { | ||
| 82 | + label: 'GPT', | ||
| 83 | + value: 'gpt' | ||
| 84 | + } | ||
| 85 | + ], | ||
| 86 | + images: ['https://img01.yzcdn.cn/vant/apple-1.jpg?time=' + Date.now(), 'https://img01.yzcdn.cn/vant/apple-2.jpg'], | ||
| 87 | + competitionActiveNames: [], | ||
| 88 | + competitionList: [ | ||
| 89 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 90 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 91 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 92 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 93 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 94 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 95 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 96 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 97 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 98 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 99 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 100 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 101 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 102 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 103 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 104 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 105 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 106 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 107 | + { title: '名称', a: 'A', b: 'B' }, | ||
| 108 | + { title: '名称', a: 'A', b: 'B' } | ||
| 109 | + ] | ||
| 110 | + } | ||
| 111 | + }, | ||
| 112 | + mounted() { | ||
| 113 | + console.log('mounted:HomePage') | ||
| 114 | + console.log('mounted:Object', { a: 123 }) | ||
| 115 | + | ||
| 116 | + this.$nextTick(() => { | ||
| 117 | + window.addEventListener('scroll', this.pageScroll) | ||
| 118 | + }) | ||
| 119 | + }, | ||
| 120 | + activated() { | ||
| 121 | + console.log('HomePage page is activated') | ||
| 122 | + // 执行页面被缓存时的逻辑 | ||
| 123 | + }, | ||
| 124 | + deactivated() { | ||
| 125 | + console.log('HomePage page is deactivated') | ||
| 126 | + // 执行页面离开缓存时的逻辑 | ||
| 127 | + }, | ||
| 128 | + methods: { | ||
| 129 | + testRequest() { | ||
| 130 | + // 创建一个 XMLHttpRequest 实例 | ||
| 131 | + var xhr = new XMLHttpRequest() | ||
| 132 | + // 发送请求 | ||
| 133 | + xhr.open('GET', 'https://mock.apifox.cn/m1/2852848-0-default/api/getVersion') | ||
| 134 | + xhr.send() | ||
| 135 | + }, | ||
| 136 | + showGlobalLog() { | ||
| 137 | + this.testRequest() | ||
| 138 | + // this.myPopupOpen = !this.myPopupOpen | ||
| 139 | + console.log(asdfasf) | ||
| 140 | + // this.myPopupOpen = true | ||
| 141 | + }, | ||
| 142 | + changeTabIndex(index) { | ||
| 143 | + this.activeIndex = index | ||
| 144 | + }, | ||
| 145 | + emitWebF() { | ||
| 146 | + console.log('getIsApp:', this.getIsApp) | ||
| 147 | + if (this.getIsApp) { | ||
| 148 | + webf.methodChannel.invokeMethod('somemethod-webf', { | ||
| 149 | + isBottom: this.isBottom, | ||
| 150 | + getIsApp: this.getIsApp | ||
| 151 | + }) | ||
| 152 | + } | ||
| 153 | + }, | ||
| 154 | + pageScroll(e) { | ||
| 155 | + // console.log('pageScroll:', e) | ||
| 156 | + //scrollTop是滚动条滚动时,距离顶部的距离 | ||
| 157 | + var scrollTop = document.documentElement.scrollTop || document.body.scrollTop | ||
| 158 | + //windowHeight是可视区的高度 | ||
| 159 | + var windowHeight = document.documentElement.clientHeight || document.body.clientHeight | ||
| 160 | + //scrollHeight是滚动条的总高度 | ||
| 161 | + var scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight | ||
| 162 | + //滚动条到底部的条件 | ||
| 163 | + if (scrollTop + windowHeight == scrollHeight) { | ||
| 164 | + //到了这个就可以进行业务逻辑加载后台数据了 | ||
| 165 | + // console.log('到了底部') | ||
| 166 | + // console.log('getIsApp:', this.getIsApp) | ||
| 167 | + if (this.getIsApp) { | ||
| 168 | + // 有webf则表示是app | ||
| 169 | + // webf.methodChannel.invokeMethod('somemethod-webf', { | ||
| 170 | + // isBottom: true, | ||
| 171 | + // msg: '到底啦' | ||
| 172 | + // }) | ||
| 173 | + } | ||
| 174 | + } else if (scrollTop === 0) { | ||
| 175 | + console.log('到了顶部') | ||
| 176 | + } | ||
| 177 | + }, | ||
| 178 | + onRefresh() { | ||
| 179 | + setTimeout(() => { | ||
| 180 | + this.isLoading = false | ||
| 181 | + }, 1000) | ||
| 182 | + } | ||
| 183 | + } | ||
| 184 | +} | ||
| 185 | +</script> | ||
| 186 | +<style lang="scss" scoped> | ||
| 187 | +.home-page { | ||
| 188 | + width: 100%; | ||
| 189 | + overflow-x: hidden; | ||
| 190 | + padding-top: 98px; | ||
| 191 | + .header-nav-bar { | ||
| 192 | + position: fixed; | ||
| 193 | + width: 100%; | ||
| 194 | + height: 44px; | ||
| 195 | + z-index: 12; | ||
| 196 | + display: flex; | ||
| 197 | + align-items: center; | ||
| 198 | + background-color: #fff; | ||
| 199 | + box-shadow: 2px 2px 20px #ededed; | ||
| 200 | + | ||
| 201 | + .tabs-view { | ||
| 202 | + height: 44px; | ||
| 203 | + flex: 4; | ||
| 204 | + margin-right: 10px; | ||
| 205 | + display: flex; | ||
| 206 | + align-items: center; | ||
| 207 | + padding: 0 10px; | ||
| 208 | + | ||
| 209 | + .tab-item { | ||
| 210 | + flex: 1; | ||
| 211 | + text-align: center; | ||
| 212 | + font-size: 14px; | ||
| 213 | + color: #666; | ||
| 214 | + position: relative; | ||
| 215 | + transition: all 0.1s ease-in 0s; | ||
| 216 | + height: 44px; | ||
| 217 | + line-height: 44px; | ||
| 218 | + | ||
| 219 | + &.active { | ||
| 220 | + color: #f00; | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + .active-line { | ||
| 224 | + position: absolute; | ||
| 225 | + left: 50%; | ||
| 226 | + bottom: 2px; | ||
| 227 | + width: 0; | ||
| 228 | + height: 2px; | ||
| 229 | + background-color: #f00; | ||
| 230 | + transition: all 0.2s ease-in 0s; | ||
| 231 | + | ||
| 232 | + &.show { | ||
| 233 | + width: 70%; | ||
| 234 | + left: 15%; | ||
| 235 | + } | ||
| 236 | + } | ||
| 237 | + } | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + .icon-list { | ||
| 241 | + flex: 1; | ||
| 242 | + height: 44px; | ||
| 243 | + display: flex; | ||
| 244 | + align-items: center; | ||
| 245 | + justify-content: space-around; | ||
| 246 | + } | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + .icon-img { | ||
| 250 | + width: 50px; | ||
| 251 | + height: 50px; | ||
| 252 | + margin-right: 10px; | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + .search-view { | ||
| 256 | + position: fixed; | ||
| 257 | + width: 100%; | ||
| 258 | + height: 54px; | ||
| 259 | + z-index: 11; | ||
| 260 | + background-color: #fff; | ||
| 261 | + } | ||
| 262 | + | ||
| 263 | + .notice-view { | ||
| 264 | + position: sticky; | ||
| 265 | + top: 98px; | ||
| 266 | + z-index: 11; | ||
| 267 | + background-color: #fff; | ||
| 268 | + | ||
| 269 | + .hot-live-btn { | ||
| 270 | + margin-left: 5px; | ||
| 271 | + } | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + .list-view { | ||
| 275 | + margin-top: 5px; | ||
| 276 | + | ||
| 277 | + .competition-content { | ||
| 278 | + padding: 15px; | ||
| 279 | + } | ||
| 280 | + } | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +::v-deep .my-swipe { | ||
| 284 | + .van-swipe-item { | ||
| 285 | + color: #fff; | ||
| 286 | + height: 150px; | ||
| 287 | + text-align: center; | ||
| 288 | + padding: 10px; | ||
| 289 | + box-sizing: border-box; | ||
| 290 | + | ||
| 291 | + .swipe-img { | ||
| 292 | + width: 100%; | ||
| 293 | + height: 100%; | ||
| 294 | + border-radius: 10px; | ||
| 295 | + } | ||
| 296 | + } | ||
| 297 | +} | ||
| 298 | +</style> |
src/views/LiveView.vue
0 → 100644
src/views/MineView.vue
0 → 100644
vue.config.js
0 → 100644
| 1 | +const { defineConfig } = require('@vue/cli-service') | ||
| 2 | +module.exports = defineConfig({ | ||
| 3 | + transpileDependencies: true, | ||
| 4 | + | ||
| 5 | + chainWebpack: config => { | ||
| 6 | + config.mode('development') | ||
| 7 | + config.optimization.delete('splitChunks') | ||
| 8 | + }, | ||
| 9 | + | ||
| 10 | + filenameHashing: false, | ||
| 11 | + productionSourceMap: false, | ||
| 12 | + | ||
| 13 | + configureWebpack(config) { | ||
| 14 | + config.devtool = config.mode === 'production' ? false : 'source-map' | ||
| 15 | + }, | ||
| 16 | + | ||
| 17 | + css: { | ||
| 18 | + extract: false | ||
| 19 | + }, | ||
| 20 | + | ||
| 21 | + publicPath: './' | ||
| 22 | +}) |
-
请 注册 或 登录 后发表评论