├── public ├── favicon.ico ├── img │ ├── app.png │ ├── logo.png │ ├── login_bg.jpg │ ├── download_pattern_left.png │ └── download_pattern_right.png ├── config.js └── index.html ├── babel.config.js ├── src ├── plugins │ ├── element.js │ ├── myPlugin.js │ └── http │ │ ├── http.js │ │ └── axios.js ├── App.vue ├── style │ ├── variables.scss │ └── main.scss ├── store │ └── store.js ├── main.js ├── views │ ├── main │ │ ├── Main.vue │ │ ├── HeadBar.vue │ │ └── Login.vue │ ├── appDetail │ │ ├── AppVersionEdit.vue │ │ ├── AppVersions.vue │ │ └── Preview.vue │ └── appList │ │ ├── AppUploadDialog.vue │ │ └── AppList.vue ├── util │ ├── loginUtil.js │ └── constant.js ├── router │ └── router.js └── components │ ├── VmSelectInput.vue │ ├── VmSearch.vue │ └── VmTable.vue ├── .npmrc ├── alias.config.js ├── .gitignore ├── vue.config.js ├── Dockerfile ├── nginx.conf ├── readme.md └── package.json /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu-xinhui/appPublishFront/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/img/app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu-xinhui/appPublishFront/HEAD/public/img/app.png -------------------------------------------------------------------------------- /public/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu-xinhui/appPublishFront/HEAD/public/img/logo.png -------------------------------------------------------------------------------- /public/img/login_bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu-xinhui/appPublishFront/HEAD/public/img/login_bg.jpg -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | "@vue/cli-plugin-babel/preset", 4 | ], 5 | }; 6 | -------------------------------------------------------------------------------- /src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Element from "element-ui"; 3 | 4 | Vue.use(Element, {size: "medium"}); 5 | -------------------------------------------------------------------------------- /public/img/download_pattern_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu-xinhui/appPublishFront/HEAD/public/img/download_pattern_left.png -------------------------------------------------------------------------------- /public/img/download_pattern_right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu-xinhui/appPublishFront/HEAD/public/img/download_pattern_right.png -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs/ 2 | sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ 3 | registry=https://registry.npm.taobao.org 4 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /src/style/variables.scss: -------------------------------------------------------------------------------- 1 | $header-height: 82px; 2 | $color-primary: #F8BA0B; 3 | $border-color: #E6E6E6; 4 | 5 | @mixin gravityCenter { 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | } 10 | -------------------------------------------------------------------------------- /src/store/store.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | export let store = Vue.observable( 4 | { 5 | breadcrumbs: [], 6 | }); 7 | 8 | export let mutations = { 9 | setBreadcrumbs(breadcrumbs) { 10 | store.breadcrumbs = breadcrumbs; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /alias.config.js: -------------------------------------------------------------------------------- 1 | /*此文件未使用,只是为了让idea可以识别实际位置*/ 2 | const path = require("path"); 3 | 4 | function resolve(dir) { 5 | return path.join(__dirname, dir); 6 | } 7 | 8 | module.exports = { 9 | resolve: { 10 | alias: { 11 | "@": resolve("src"), 12 | }, 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | *.iml 24 | *.ipr 25 | *.iws -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "@/router/router"; 4 | import "./plugins/element.js"; 5 | import "./plugins/myPlugin.js"; 6 | import "normalize.css"; 7 | import "./style/main.scss"; 8 | 9 | Vue.config.productionTip = false; 10 | 11 | new Vue({ 12 | router, 13 | render: h => h(App), 14 | }).$mount("#app"); 15 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | productionSourceMap: false, 3 | devServer: { 4 | port: 8011, 5 | }, 6 | chainWebpack: config => { 7 | config 8 | .plugin("html") 9 | .tap(args => { 10 | args[0].title = "应用发布"; 11 | return args; 12 | }); 13 | // 用cdn方式引入 14 | config.externals({ 15 | "vue": "Vue", 16 | "vue-router": "VueRouter", 17 | "element-ui": "ELEMENT", 18 | }); 19 | }, 20 | }; -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:1.17.3-alpine 2 | 3 | MAINTAINER liunewshine@qq.com 4 | 5 | ENV TZ=Asia/Shanghai 6 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && mkdir -p /yanglaojin 7 | 8 | WORKDIR /yanglaojin 9 | 10 | ADD /dist/ /usr/share/nginx/html/ 11 | # 自定义nginx配置文件,启用gzip 12 | ADD /nginx.conf /etc/nginx/nginx.conf 13 | 14 | ENV env "test" 15 | 16 | CMD sed -i "s/vueEnv.active/\"${env}\"/g" /usr/share/nginx/html/config.js && nginx -g 'daemon off;' 17 | -------------------------------------------------------------------------------- /src/plugins/myPlugin.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import axios from "./http/axios"; 3 | import http from "./http/http"; 4 | import {Message} from "element-ui"; 5 | 6 | Plugin.install = function(Vue) { 7 | // 网络请求 8 | Vue.prototype.$http = http; 9 | Vue.prototype.$axios = axios; 10 | //全局异常处理 11 | Vue.config.errorHandler = function(err, vm, info) { 12 | console.error(err, vm, info); 13 | Message.error(err); 14 | }; 15 | }; 16 | Vue.use(Plugin); 17 | 18 | export default Plugin; 19 | -------------------------------------------------------------------------------- /src/style/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; 3 | color: #9b9b9b; 4 | } 5 | 6 | /*定义滚动条宽高及背景,宽高分别对应横竖滚动条的尺寸*/ 7 | ::-webkit-scrollbar { 8 | background-color: white; 9 | height: 13px; 10 | width: 13px; 11 | } 12 | 13 | /*定义滚动条的轨道,内阴影及圆角*/ 14 | ::-webkit-scrollbar-track { 15 | background-color: #f5f5f5; 16 | } 17 | 18 | /*定义滑块,内阴影及圆角*/ 19 | ::-webkit-scrollbar-thumb { 20 | background-color: #ccc; 21 | } -------------------------------------------------------------------------------- /public/config.js: -------------------------------------------------------------------------------- 1 | /*全局配置文件,url类型的必须以"/"结尾*/ 2 | var vueEnv = { 3 | //当前激活的环境 4 | active: "dev", 5 | //开发环境 6 | dev: { 7 | serverUrl: "http://127.0.0.1:8060/api/", 8 | }, 9 | test: { 10 | serverUrl: "http://118.25.44.86:8556/api/", 11 | }, 12 | prod: { 13 | serverUrl: "/api/", 14 | }, 15 | }; 16 | 17 | switch (vueEnv.active) { 18 | case "dev": 19 | window.config = vueEnv.dev; 20 | break; 21 | case "test": 22 | window.config = vueEnv.test; 23 | break; 24 | default: 25 | window.config = vueEnv.prod; 26 | break; 27 | } -------------------------------------------------------------------------------- /src/views/main/Main.vue: -------------------------------------------------------------------------------- 1 | 9 | 22 | 35 | -------------------------------------------------------------------------------- /src/util/loginUtil.js: -------------------------------------------------------------------------------- 1 | const USER_INFO = "hold_user_info"; 2 | const storage = window.localStorage; 3 | let userInfo = null; 4 | export default { 5 | login(obj) { 6 | userInfo = obj; 7 | storage.setItem(USER_INFO, JSON.stringify(obj)); 8 | }, 9 | logout() { 10 | userInfo = null; 11 | storage.clear(); 12 | }, 13 | isLogin() { 14 | return !!this.getToken(); 15 | }, 16 | getUserInfo() { 17 | try { 18 | if (!userInfo) { 19 | userInfo = JSON.parse(storage.getItem(USER_INFO)); 20 | } 21 | } catch (err) { 22 | console.log(err); 23 | } 24 | return userInfo || {}; 25 | }, 26 | getToken() { 27 | return this.getUserInfo().token; 28 | }, 29 | getUsername() { 30 | return this.getUserInfo().username; 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /src/util/constant.js: -------------------------------------------------------------------------------- 1 | //garage类型 2 | const capStatus = ["0:无效", "1:有效"]; 3 | 4 | let exportObj = { 5 | capStatus, 6 | }; 7 | 8 | //------------------------------以下部分请勿改动------------------------------ 9 | //转化为object和options 10 | //$options用于获取el-select可用的格式,$object用于根据key取中文描述 11 | for (let key in exportObj) { 12 | let value = exportObj[key]; 13 | if (Array.isArray(value)) { 14 | let options = []; 15 | let object = {}; 16 | value.forEach(item => { 17 | let itemArr = item.split(":"); 18 | let option = { 19 | value: itemArr[0], 20 | label: itemArr[1], 21 | }; 22 | object[option.value] = option.label; 23 | options.push(option); 24 | }); 25 | value.$options = options; 26 | value.$object = object; 27 | } 28 | } 29 | export default exportObj; 30 | -------------------------------------------------------------------------------- /src/router/router.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const routes = [ 7 | { 8 | path: "/", 9 | meta: {title: "Main中展示的组件"}, 10 | component: () => import("@/views/main/Main"), 11 | children: [ 12 | {path: "/", meta: {title: "首页"}, component: () => import("@/views/appList/AppList")}, 13 | {path: "apps/:id", meta: {title: "应用动态"}, component: () => import("@/views/appDetail/AppVersions")}, 14 | ], 15 | }, 16 | {path: "/login", meta: {title: "登录"}, component: () => import("@/views/main/Login")}, 17 | {path: "/:shortCode", meta: {title: "下载页"}, component: () => import("@/views/appDetail/Preview")}, 18 | ]; 19 | 20 | const router = new VueRouter({ 21 | mode: "history", 22 | routes, 23 | }); 24 | 25 | export const toLogin = () => { 26 | router.push({ 27 | path: "/login", 28 | }); 29 | }; 30 | 31 | export default router; -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | 2 | user nginx; 3 | worker_processes auto; 4 | 5 | error_log /var/log/nginx/error.log warn; 6 | pid /var/run/nginx.pid; 7 | 8 | 9 | events { 10 | worker_connections 1024; 11 | } 12 | 13 | 14 | http { 15 | include /etc/nginx/mime.types; 16 | default_type application/octet-stream; 17 | 18 | log_format main '$remote_addr - $remote_user [$time_local] "$request" ' 19 | '$status $body_bytes_sent "$http_referer" ' 20 | '"$http_user_agent" "$http_x_forwarded_for"'; 21 | 22 | access_log /var/log/nginx/access.log main; 23 | 24 | sendfile on; 25 | #tcp_nopush on; 26 | 27 | keepalive_timeout 65; 28 | 29 | gzip on; 30 | gzip_vary on; 31 | gzip_proxied any; 32 | gzip_comp_level 6; 33 | gzip_buffers 16 8k; 34 | gzip_http_version 1.1; 35 | gzip_min_length 256; 36 | gzip_types text/plain text/css application/json application/javascript; 37 | 38 | include /etc/nginx/conf.d/*.conf; 39 | } 40 | -------------------------------------------------------------------------------- /src/plugins/http/http.js: -------------------------------------------------------------------------------- 1 | import axios from "./axios"; 2 | 3 | export default { 4 | /** 5 | * Get方法获取数据 6 | * get(String url,Object params) 示例get('users',{pageNum:1}) 7 | */ 8 | get: function(url, params) { 9 | return axios({ 10 | url: url, 11 | method: "get", 12 | params: params, 13 | }); 14 | }, 15 | put: function(url, data, params) { 16 | return axios({ 17 | url: url, 18 | method: "put", 19 | data: data, 20 | params: params, 21 | }); 22 | }, 23 | post: function(url, data, params) { 24 | return axios({ 25 | url: url, 26 | method: "post", 27 | data: data, 28 | params: params, 29 | }); 30 | }, 31 | // 删除,示例delete('users',10) 32 | delete: (url, id) => { 33 | return axios({ 34 | url: id ? (url + "/" + id) : url, 35 | method: "delete", 36 | }); 37 | }, 38 | //保存,当id大于0时为修改采用put,其他为新增采用post 39 | save: (url, data) => { 40 | return axios({ 41 | url: url, 42 | method: !data.id || data.id === 0 ? "post" : "put", 43 | data: data, 44 | }); 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /src/components/VmSelectInput.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 36 | 50 | -------------------------------------------------------------------------------- /src/components/VmSearch.vue: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 39 | 40 | 62 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## 介绍 2 | 3 | 项目是 `vue` 开发的类似`fir.im`和`蒲公英`的企业 APP 分发平台,提供android app上传功能,以及ios和android二维码合并功能。用过可直接通过微信、浏览器、QQ等扫码下载app。 4 | 5 | 此项目为前后端分离,后端采用spring boot实现,请移步https://github.com/liu-xinhui/appPublish 6 | 7 | **为加快应用下载页访问速度,本项目已启用gzip,vue和element ui相关js采用cdn加载,路由懒加载等优化,和普通多页应用无异** 8 | 9 | ## 效果 10 | 11 | 样式完全参考http://fir.im 12 | 13 | 本项目预览地址http://118.25.44.86:8556/ 14 | 15 | 账号:admin 16 | 17 | 密码:123456 18 | 19 | ## 部署 20 | 21 | 本项目已经打包成docker镜像并上传到腾讯云的镜像仓库,可直接pull镜像进行部署,也可拉取代码做适应性修改后重新打包 22 | 23 | ```dockerfile 24 | #拉取镜像 25 | docker pull ccr.ccs.tencentyun.com/liuxh/app-publish 26 | 27 | #运行 28 | docker run -itd -p 8060:8060 -v /webapps/appPublish:/my-app/ap --restart=always --name app-publish --env SPRING_PROFILES_ACTIVE=prod ccr.ccs.tencentyun.com/liuxh/app-publish 29 | ``` 30 | 31 | - 项目内部端口为`8060` 32 | - 容器内部数据存储位置`/my-app/ap`,需要做`docker volume`防止容器删除后数据丢失`-v /webapps/appPublish:/my-app/ap` 33 | - 需要设置项目运行环境为`prod`,`--env SPRING_PROFILES_ACTIVE=prod` 34 | 35 | ## 技术栈 36 | 37 | - `vue cli4` 38 | - `vue 2.6.11` 39 | - `spring boot 2.3.2.RELEASE` 40 | - `h2数据库` 嵌入式数据库,性能好,部署方便,可与mysql无缝切换 41 | - `mybatis-plus 3.3.2` 42 | 43 | ## 截图 44 | 45 | ![应用列表](https://uploader.shimo.im/f/9ReWOLluDSUqzHRY.png) 46 | ![应用版本历史](https://uploader.shimo.im/f/xZaEsvCRnkTgCYAp.png) 47 | ![应用基本信息](https://uploader.shimo.im/f/Hk3NkwSheT6ornG3.png) 48 | ![应用合并](https://uploader.shimo.im/f/ZNeltMJb3VRkNN3M.png) 49 | ![应用下载页](https://uploader.shimo.im/f/ZoKqStY8HH4BU5Qr.png) -------------------------------------------------------------------------------- /src/plugins/http/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import auth from "@/util/loginUtil"; 3 | import {toLogin} from "@/router/router"; 4 | import {Notification} from "element-ui"; 5 | 6 | // axios 配置 7 | axios.defaults.timeout = 40000; 8 | axios.defaults.baseURL = window.config.serverUrl; 9 | // http request 拦截器 10 | axios.interceptors.request.use( 11 | config => { 12 | config.headers["Authorization"] = auth.getToken(); 13 | return config; 14 | }, 15 | err => { 16 | return Promise.reject(err); 17 | }, 18 | ); 19 | 20 | // http response 拦截器 21 | axios.interceptors.response.use( 22 | response => { 23 | return response; 24 | }, 25 | error => { 26 | console.log("-------------接口请求错误信息start-------------"); 27 | console.log(error); 28 | if (error.response) { 29 | // The request was made and the server responded with a status code 30 | // that falls out of the range of 2xx 31 | console.log(error.response.data, error.response.status, error.response.headers); 32 | Notification.error({title: "提示", message: error.response.data.message}); 33 | if (error.response.status === 401) { 34 | toLogin(); 35 | } 36 | } else if (error.request) { 37 | // The request was made but no response was received 38 | // `error.request` is an instance of XMLHttpRequest in the browser and an instance of 39 | // http.ClientRequest in node.js 40 | console.log(error.request); 41 | Notification.error({title: "提示", message: "连接服务器失败"}); 42 | } else { 43 | // Something happened in setting up the request that triggered an Error 44 | console.log("Error", error.message); 45 | Notification.error({title: "提示", message: "请求服务器出错"}); 46 | } 47 | console.log("-------------接口请求错误信息end-------------"); 48 | return Promise.reject(error); 49 | }, 50 | ); 51 | 52 | export default axios; 53 | -------------------------------------------------------------------------------- /src/views/appDetail/AppVersionEdit.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 64 | 65 | 67 | 68 | -------------------------------------------------------------------------------- /src/views/main/HeadBar.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 56 | 57 | 124 | -------------------------------------------------------------------------------- /src/views/main/Login.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 75 | 76 | 103 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <%= htmlWebpackPlugin.options.title %> 15 | 73 | 74 | 75 | 78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yanglaojin", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@chenfengyuan/vue-qrcode": "^1.0.2", 12 | "app-info-parser": "^0.3.10", 13 | "axios": "^0.19.2", 14 | "core-js": "^3.6.5", 15 | "normalize.css": "^8.0.1", 16 | "vue": "^2.6.11" 17 | }, 18 | "devDependencies": { 19 | "@vue/cli-plugin-babel": "~4.4.0", 20 | "@vue/cli-plugin-eslint": "~4.4.0", 21 | "@vue/cli-plugin-router": "~4.4.0", 22 | "@vue/cli-service": "~4.4.0", 23 | "babel-eslint": "^10.1.0", 24 | "eslint": "^6.7.2", 25 | "eslint-plugin-vue": "^6.2.2", 26 | "lint-staged": "^9.5.0", 27 | "node-sass": "^4.12.0", 28 | "sass-loader": "^8.0.2", 29 | "vue-template-compiler": "^2.6.11" 30 | }, 31 | "eslintConfig": { 32 | "root": true, 33 | "env": { 34 | "node": true 35 | }, 36 | "extends": [ 37 | "plugin:vue/essential", 38 | "eslint:recommended" 39 | ], 40 | "parserOptions": { 41 | "parser": "babel-eslint" 42 | }, 43 | "rules": { 44 | "no-console": "off", 45 | "no-irregular-whitespace": "off", 46 | "quotes": [ 47 | "error", 48 | "double" 49 | ], 50 | "semi": [ 51 | "error", 52 | "always" 53 | ], 54 | "space-in-parens": [ 55 | "warn", 56 | "never" 57 | ], 58 | "space-before-function-paren": [ 59 | "warn", 60 | "never" 61 | ], 62 | "space-before-blocks": [ 63 | "warn", 64 | "always" 65 | ], 66 | "vue/attribute-hyphenation": "warn", 67 | "vue/html-closing-bracket-spacing": [ 68 | "error", 69 | { 70 | "selfClosingTag": "never" 71 | } 72 | ], 73 | "vue/html-end-tags": "error", 74 | "vue/html-indent": [ 75 | "warn", 76 | 2, 77 | { 78 | "attribute": 2, 79 | "baseIndent": 1, 80 | "closeBracket": 0, 81 | "alignAttributesVertically": true, 82 | "ignores": [] 83 | } 84 | ], 85 | "vue/script-indent": [ 86 | "warn", 87 | 2, 88 | { 89 | "baseIndent": 1, 90 | "switchCase": 1, 91 | "ignores": [] 92 | } 93 | ], 94 | "vue/html-quotes": "error", 95 | "vue/html-self-closing": "error", 96 | "vue/max-attributes-per-line": [ 97 | "warn", 98 | { 99 | "singleline": 4, 100 | "multiline": { 101 | "max": 1, 102 | "allowFirstLine": false 103 | } 104 | } 105 | ], 106 | "vue/multiline-html-element-content-newline": "warn", 107 | "vue/mustache-interpolation-spacing": [ 108 | "error", 109 | "never" 110 | ], 111 | "vue/name-property-casing": "error", 112 | "vue/no-multi-spaces": "warn", 113 | "vue/no-spaces-around-equal-signs-in-attribute": "warn", 114 | "vue/no-template-shadow": "warn", 115 | "vue/prop-name-casing": "error", 116 | "vue/require-prop-types": "error", 117 | "vue/v-bind-style": "warn", 118 | "vue/v-on-style": "warn", 119 | "vue/attributes-order": "warn", 120 | "vue/no-confusing-v-for-v-if": "error", 121 | "vue/no-v-html": "off", 122 | "vue/order-in-components": "warn", 123 | "vue/this-in-template": "error", 124 | "vue/camelcase": "error", 125 | "comma-dangle": [ 126 | "error", 127 | "always-multiline" 128 | ], 129 | "vue/component-name-in-template-casing": [ 130 | "warn", 131 | "kebab-case" 132 | ], 133 | "vue/eqeqeq": "warn", 134 | "vue/match-component-file-name": "error" 135 | } 136 | }, 137 | "browserslist": [ 138 | "> 1%", 139 | "last 2 versions", 140 | "not dead" 141 | ], 142 | "lint-staged": { 143 | "*.{js,jsx,vue}": [ 144 | "vue-cli-service lint", 145 | "git add" 146 | ] 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/views/appList/AppUploadDialog.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 114 | 115 | 149 | -------------------------------------------------------------------------------- /src/views/appList/AppList.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 125 | 126 | 215 | -------------------------------------------------------------------------------- /src/components/VmTable.vue: -------------------------------------------------------------------------------- 1 | 2 | 75 | 76 | 189 | 190 | 229 | -------------------------------------------------------------------------------- /src/views/appDetail/AppVersions.vue: -------------------------------------------------------------------------------- 1 | 94 | 95 | 195 | 196 | -------------------------------------------------------------------------------- /src/views/appDetail/Preview.vue: -------------------------------------------------------------------------------- 1 | 60 | 154 | 3325 | --------------------------------------------------------------------------------