Commit 72b06e2e authored by Xuguangxing's avatar Xuguangxing

feat: project init

parents
Pipeline #1195 canceled with stages
> 1%
last 2 versions
not dead
safari >= 8
chrome >= 38
ie >= 11
\ No newline at end of file
[*.{js,jsx,ts,tsx,vue}]
indent_style = space
indent_size = 2
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 100
node_modules/*
build/*
config/*
dist/*
lib/*
static/*
\ No newline at end of file
module.exports = {
root: true,
env: {
node: true,
browser: true,
},
extends: ["plugin:vue/essential", "plugin:vue/recommended", "eslint:recommended", "@vue/prettier"],
parserOptions: {
parser: "babel-eslint"
},
// TODO 配置强制使用模板字符串
rules: {
'prettier/prettier': 'error',
'vue/max-attributes-per-line': 'off',
"vue/html-self-closing": ["error", {
"html": {
"void": "always",
"normal": "always",
"component": "always"
},
"svg": "always",
"math": "always"
}],
'no-empty': ["error", { "allowEmptyCatch": true }],
'vue/no-v-html': 'off',
'vue/require-default-prop': 'off',
'arrow-parens': 'off', // allow paren-less arrow functions
'generator-star-spacing': 'off', // allow async-await
'no-unused-vars': 'error', // disabled no ununsed var
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // no use debugger in production
'indent': [2, 2, { SwitchCase: 1 }], // 2 space for tab for perttier
'space-before-function-paren': ['error', 'never'], // no space in function name for perttier
}
};
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
.eslintcache
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
/*
* @Description:
* @Date: 2020-11-18 16:24:20
* @LastEditors: gzw
* @LastEditTime: 2020-11-18 16:47:05
*/
// prettier.config.js or .prettierrc.js
module.exports = {
printWidth: 100, // 设置prettier单行输出(不折行)的(最大)长度
tabWidth: 2, // 设置工具每一个水平缩进的空格数
semi: true, // 在语句末尾添加分号
singleQuote: true, // 使用单引号而非双引号
arrowParens: 'avoid', // 为单行箭头函数的参数添加圆括号,参数个数为1时可以省略圆括号
rangeStart: 0, // 只格式化某个文件的一部分
rangeEnd: Infinity, // 只格式化某个文件的一部分
};
module.exports = {
// Sentry options are required
organization: 'sentry',
project: 'vcc-spider-center-ui',
apiKey: '0d3fa45c10f047d7bfe03eb268a93c23a878d7585dc749418f728f88d65e1477',
// Release version name/hash is required
release: 'vcc-spider-center-ui@1.0.1',
baseSentryURL: 'http://sentry.quantgroups.com/api/0',
deleteAfterCompile: true,
include: /static\/js\//, // 只上传js和map文件
ignore: ['node_modules'],
suppressErrors: true,
filenameTransform: function (filename) {
return '~/vcc-spider-center-ui/public/' + filename
},
}
# vue-standard-h5-project
基于vue-cli 3.0整合的针对前端业务的vue移动端项目模板
## Quick start
你可以直接clone这个项目,作为项目的基础,一切都是准备好的。
```bash
git clone http://git.quantgroup.cn/zhiwei.guo/vue-standard-h5-project.git
cd vue-standard-h5-project
npm install && npm run serve
```
## Feature
- **对项目结构进行细微优化**。对各资源模块进行归类、细分;编写、维护、查找更便利。
- **数据请求策略化**。对axios封装,统一请求配置,可配置针对不同业务接口返回参数的处理。
- **内置样式处理方案**。支持less编译,并内置变量和mixin,轻松开启样式编写、复用、修改等。
- **集成组件库**。可通过cherry-ui快速开发功能,配置全局主题风格。
- **集成 Prettier & Eslint**。检测代码潜在问题的同时,统一团队代码规范。
- **集成git hooks**。只允许提交符合规范的代码,保持代码库干净整洁。
- **打包资源自动上传cdn**。项目代码上传cdn,加快访问速度。
## Directory
```bash
├── README.md // 项目说明
├── babel.config.js // babel配置
├── .browserslistrc // 目标浏览器配置
├── .editorconfig // 编辑器统一编码规范配置
├── .eslintignore // eslint忽略清单
├── .eslintrc.js // eslint配置
├── .gitignore // git忽略清单
├── .prettierrc.js // prettier配置
├── .sentryclirc.js // sentry配置
├── jsconfig.json // vsc js配置
├── vue.config.js // vue-cli3主配置
├── package-lock.json // package.json锁定
├── yarn.lock // package.json锁定-yarn
├── package.json // npm配置
├── postcss.config.js // postcss配置
├── .env.production // 生产环境变量配置
├── .env.development // 开发环境变量配置
├── .env.test // 测试环境变量配置
├── public // 公用文件,不经过webpack处理
│ ├── fixIosTitle.html // ios兼容修改title
│ ├── index.html
│ └── logo.png
└── src
├── main.js // 主入口
├── App.vue // 主页面
├── api // 接口层
│ └── common.js // 原则:按接口或业务区分不同文件夹
├── assets // lottie动画、image、svg等资源
│ ├── images // image图标库
│ │ └── pay // 不同业务对应不同文件夹
│ │ └── xhk.png
│ ├── logo-txt.png
│ ├── logo.png
│ ├── lottie // lottie动画库
│ │ └── red-card-popup.json
│ └── svg // svg图标库
│ └── ufo.svg
├── components // 业务公用组件、原子组件
│ ├── LoginForm.vue
│ ├── NetError.vue
│ ├── PayWaitLayer.vue
│ ├── SvgIcon.vue // svg公共组件
│ └── lottie.vue // lottie动画公共组件
├── config // 配置层,包括常量配置、接口配置
│ ├── constant.js // 常量
│ ├── dev.config.js // 本地开发环境配置
│ ├── index.js // 聚合
│ └── prod.config.js // 生产/测试环境配置
├── mixins // 公共mixins
│ ├── formValidate.mixin.js
│ └── inputFix.mixin.js
├── router // 路由层
│ ├── index.js // router功能配置、路由监听
│ └── routes.js // 所有路由
├── service // 工具层
│ ├── cherry-ui.js // ui库按需引入
│ ├── cookie.js // cookies操作
│ ├── http.js // axios二次封装
│ ├── init.service.js // 路由鉴权、修改title
│ ├── localstorage.js // localstorage操作
│ ├── pay.js // 支付
│ ├── sa.js // 神策
│ ├── svg.js // 生成
│ ├── utils.js // 工具库
│ ├── validation.js // 校验规则
│ └── vconsole.js
├── store // vuex数据
│ └── index.js
├── style //所有less资源
│ ├── iconfont.less // iconfont
│ ├── index.less // 聚合
│ ├── mixins.less // mixins
│ ├── reset.less // 兼容各浏览器
│ ├── theme.less //ui库主题定制
│ └── var.less // less变量和function等
└── views // UI层(原则:轻page,重component)
└── Home
├── Demo // 业务
│ ├── index.less // 业务样式(少可以不分离,但建议分离)
│ └── index.vue // 业务page
└── modules // 业务功能组件
└── Child.vue
```
module.exports = {
presets: ['@vue/cli-plugin-babel/preset'],
plugins: [
[
'import',
{
libraryName: '@qg/cherry-ui',
libraryDirectory: 'src',
style: name => {
// eslint-disable-next-line no-useless-escape
const lessName = name.match(/[^\/]+$/)[0];
return `${name}/${lessName}.less`;
}
}
],
'@babel/plugin-proposal-nullish-coalescing-operator',
'@babel/plugin-proposal-optional-chaining'
]
};
{
"compilerOptions": {
"target": "es2017",
"allowSyntheticDefaultImports": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
"~@/*": ["src/*"]
}
},
"exclude": ["node_modules", "dist"],
"include": ["src"]
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "vcc-spider-center-ui",
"version": "1.0.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --open",
"report": "vue-cli-service build --report",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@better-scroll/core": "^2.0.0-beta.6",
"@qg/cherry-ui": "^2.16.65",
"@qg/js-bridge": "^1.1.9",
"@qg/qg-scroll": "^1.4.2",
"@qg/ui-request": "^0.0.5",
"core-js": "^3.6.5",
"js-cookie": "^2.2.1",
"raven-js": "^3.27.2",
"sa-sdk-javascript": "^1.15.16",
"vue": "2.6.11",
"vue-router": "^3.2.0",
"vuex": "^3.4.0"
},
"devDependencies": {
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.12.1",
"@babel/plugin-proposal-optional-chaining": "^7.12.7",
"@qg/webpack-cos-cdn-upload-plugin": "0.0.4",
"@vue/cli-plugin-babel": "^4.4.0",
"@vue/cli-plugin-eslint": "^4.4.0",
"@vue/cli-plugin-router": "^4.4.0",
"@vue/cli-plugin-vuex": "^4.4.0",
"@vue/cli-service": "^4.4.0",
"@vue/eslint-config-prettier": "^6.0.0",
"babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.0",
"eslint": "^6.7.2",
"eslint-plugin-prettier": "^3.1.3",
"eslint-plugin-vue": "^6.2.2",
"husky": "^4.2.5",
"less": "^3.0.4",
"less-loader": "^5.0.0",
"lint-staged": "^10.2.11",
"postcss-px2rem": "^0.3.0",
"prettier": "^1.19.1",
"svg-sprite-loader": "^5.2.1",
"vue-template-compiler": "^2.6.11",
"webpack-sentry-plugin": "^2.0.3"
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}
module.exports = {
plugins: {
'postcss-px2rem': {
remUnit: 37.5
}
}
};
ca1c52b4801a7741ccdbbe47797d574e
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="format-detection" content="telephone=yes">
<meta name="viewport" content="width=device-width,initial-scale=1.0,user-scalable=no,viewport-fit=cover">
<link rel="icon" href="./logo.png" type="image/x-icon">
<title><%= htmlWebpackPlugin.options.title %></title>
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<link href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" rel="external nofollow" rel="external nofollow" rel="stylesheet">
<% } %>
<style>
.mainload {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
text-align: center;
width: 100px;
height: 100px;
}
.mainload p {
font-size: 16px;
color: #666;
margin-top: 10px;
}
.mainload .circular {
height: 36px;
width: 36px;
animation: loading-rotate 2s linear infinite;
}
.mainload .path {
animation: loading-dash 1.5s ease-in-out infinite;
stroke-dasharray: 90,150;
stroke-dashoffset: 0;
stroke-width: 3;
stroke: #ec3333;
stroke-linecap: round;
}
@keyframes loading-rotate {
to {
transform: rotate(1turn)
}
}
@keyframes loading-dash {
0% {
stroke-dasharray: 1,200;
stroke-dashoffset: 0
}
50% {
stroke-dasharray: 90,150;
stroke-dashoffset: -40px
}
to {
stroke-dasharray: 90,150;
stroke-dashoffset: -120px
}
}
@media screen and (min-width: 768px) {
html {
font-size: 37.5px !important;
background-color: #EEF0F3;
box-shadow: 0 1px 6px rgba(0,0,0,.117647), 0 1px 4px rgba(0,0,0,.117647);
}
body {
max-width: 375px;
left: 0;
right: 0;
margin: 0 auto !important;
}
.cr-nav-bar {
max-width: 375px;
left: 0;
right: 0;
margin: 0 auto !important;
z-index: 999;
}
}
</style>
<script>
window.onload = function() {
// 阻止双击放大
var lastTouchEnd = 0;
document.addEventListener('touchstart', function(event) {
if (event.touches.length > 1) {
event.preventDefault();
}
});
document.addEventListener('touchend', function(event) {
var now = (new Date()).getTime();
if (now - lastTouchEnd <= 300) {
event.preventDefault();
}
lastTouchEnd = now;
}, false);
// 阻止双指放大
document.addEventListener('gesturestart', function(event) {
event.preventDefault();
});
}
</script>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="app">
<div class="mainload">
<svg viewBox="25 25 50 50" class="circular"><circle cx="50" cy="50" r="20" fill="none" class="path"></circle></svg>
<p>加载中</p>
</div>
</div>
<!-- built files will be auto injected -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body>
</html>
<template>
<div id="app">
<cr-nav-bar v-if="header" :title="title" left-text="" @click-left="backFun" />
<div class="app">
<router-view />
</div>
<div v-if="loading" class="loading-container">
<cr-loading class="loading" size="24px">加载中...</cr-loading>
</div>
<net-error />
</div>
</template>
<script>
import { mapState } from 'vuex';
import NetError from '@/components/NetError';
export default {
name: 'App',
components: {
NetError
},
data() {
return {};
},
computed: {
...mapState({
title: state => state.pay.title,
header: state => state.pay.header,
loading: state => state.pay.loading
})
},
watch: {
header: {
immediate: true,
handler(val, oldVal) {
if (val !== oldVal) {
document.body.className = val ? 'has-header' : '';
}
}
}
},
methods: {
backFun() {
this.$router.go(-1);
}
}
};
</script>
<style lang="less">
.app {
user-select: none;
height: 100%;
min-height: 100%;
position: relative;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.loading-container {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 999;
display: flex;
justify-content: center;
align-items: center;
.loading {
padding: 6px;
border-radius: 6px;
background-color: rgba(0, 0, 0, 0.5);
color: #fff;
@{deep} .cr-loading--text {
color: #fff;
}
}
}
</style>
import config from '@/config';
import { saDeviceId } from '@/service/sa.service';
import http from '@/service/httpDecorator';
const { talosHost } = config;
export default {
// 猜你喜欢-(商品详情页)
recommendLike: async function() {
// return http.get(talosHost + '/vcc/xyqb/recommend/like');
return http.get(`${talosHost}/vcc/xyqb/recommend/goods-list`, {
customHeader: {
scDeviceId: await saDeviceId()
}
});
}
};
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1600935447036" class="icon" viewBox="0 0 1150 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13605" xmlns:xlink="http://www.w3.org/1999/xlink" width="898.4375" height="800"><defs><style type="text/css"></style></defs><path d="M815.338057 760.110324c-3.569371 0-6.456076-2.720914-6.456076-6.075734s2.886705-6.075733 6.456076-6.075733h226.0992c3.559619 0 6.446324 2.720914 6.446324 6.075733s-2.886705 6.075733-6.436571 6.075734H815.328305z m-804.015543 0c-3.559619 0-6.446324-2.720914-6.446324-6.075734s2.886705-6.075733 6.446324-6.075733h257.589638c3.559619 0 6.436571 2.720914 6.436572 6.075733s-2.876952 6.075733-6.436572 6.075734H11.312762z m286.573715 0c-3.559619 0-6.446324-2.720914-6.446324-6.075734s2.886705-6.075733 6.436571-6.075733h33.83101c3.559619 0 6.446324 2.720914 6.446324 6.075733s-2.886705 6.075733-6.446324 6.075734h-33.821257z m787.504761 0c-3.559619 0-6.436571-2.720914-6.436571-6.075734s2.876952-6.075733 6.436571-6.075733h50.25402c3.559619 0 6.436571 2.720914 6.436571 6.075733s-2.876952 6.075733-6.436571 6.075734h-50.244267z" fill="#666666" p-id="13606"></path><path d="M256.585143 713.56221a30.086095 30.086095 0 1 1 0-60.172191 30.086095 30.086095 0 1 1 0 60.172191z m0-12.892648a17.193448 17.193448 0 1 0 0-34.386895 17.193448 17.193448 0 0 0 0 34.386895z m55.881143-640.487619a30.086095 30.086095 0 1 1 0-60.181943 30.086095 30.086095 0 1 1 0 60.17219z m0-12.892648a17.193448 17.193448 0 1 0 0-34.396647 17.193448 17.193448 0 1 0 0 34.396647z" fill="#999999" p-id="13607"></path><path d="M174.723657 455.387429a7.528838 7.528838 0 0 0 7.3728-7.3728v-13.165715h13.692343a7.528838 7.528838 0 0 0 7.3728-7.3728 7.528838 7.528838 0 0 0-7.3728-7.3728h-13.692343v-13.165714a7.528838 7.528838 0 0 0-7.3728-7.3728 7.528838 7.528838 0 0 0-7.3728 7.3728v13.165714h-13.692343a7.528838 7.528838 0 0 0-7.3728 7.3728c0 3.6864 3.159771 7.3728 7.3728 7.3728h13.692343v13.165715c0 4.213029 3.159771 7.3728 7.3728 7.3728z" fill="#D1E7FF" p-id="13608"></path><path d="M1008.620495 479.534324c0.692419-0.029257 0.897219 0.195048 0.877715 0.877714-0.029257 3.637638 0 7.285029 0 10.922667 0 3.676648-0.029257 7.353295 0 11.02019 0 0.57539-0.126781 0.838705-0.546134 0.906972l-0.37059 0.019504c-1.755429-0.03901-3.510857-0.03901-5.256534 0-0.565638 0-0.741181-0.195048-0.741181-0.750933v-22.206171c0-0.585143 0.175543-0.809448 0.780191-0.789943 1.755429 0.019505 3.510857 0.029257 5.256533 0z m21.767315-10.990934l18.178438 18.217448c0.03901 0.03901 0.068267 0.097524 0.195047 0.321829-1.531124 1.501867-3.052495 3.03299-4.612876 4.486095-0.126781 0.117029-0.672914-0.03901-0.877714-0.234057-2.555124-2.506362-5.090743-5.061486-7.626362-7.587353l-9.908419-9.918171-0.682667-0.721676c0.117029-0.175543 0.214552-0.360838 0.331581-0.487619 1.355581-1.355581 2.691657-2.681905 4.017981-4.027734 0.331581-0.341333 0.585143-0.44861 0.984991-0.048762z m-42.383848 0.175543c1.170286 1.26781 2.408838 2.4576 3.64739 3.657143 0.341333 0.341333 0.44861 0.594895 0.058515 1.004495l-17.905372 17.885867c-0.078019 0.078019-0.195048 0.126781-0.390095 0.273067-1.531124-1.521371-3.023238-2.993981-4.46659-4.5056-0.107276-0.107276-0.068267-0.44861 0-0.643657 0.03901-0.146286 0.224305-0.253562 0.341333-0.370591 5.753905-5.763657 11.537067-11.517562 17.290971-17.300724 0.555886-0.585143 0.897219-0.565638 1.423848 0z m-7.5776-22.89859c0.594895 0 0.887467 0.117029 0.965486 0.565638l0.019504 0.399848a97.123962 97.123962 0 0 0 0 5.168761c0 0.594895-0.2048 0.78019-0.789942 0.780191-7.41181-0.019505-14.823619-0.019505-22.206172 0-0.643657 0-0.770438-0.263314-0.770438-0.828952 0.03901-1.755429 0.03901-3.501105 0-5.246781 0-0.643657 0.2048-0.838705 0.848457-0.838705l5.500343 0.019505h5.510095c3.637638 0 7.285029 0.019505 10.922667-0.019505z m78.682209 0c0.702171 0 0.955733 0.185295 0.936229 0.926476a116.736 116.736 0 0 0 0 5.237029c0 0.565638-0.195048 0.750933-0.750933 0.750933h-22.196419c-0.682667 0-0.8192-0.263314-0.809448-0.877714 0.029257-1.706667 0.03901-3.44259 0-5.168762-0.029257-0.682667 0.214552-0.867962 0.877714-0.867962l5.510096 0.019505h5.510095c3.64739 0 7.285029 0.019505 10.922666-0.019505z m-85.586895-33.704229l0.721676 0.682667 17.534781 17.505524c0.585143 0.585143 0.526629 0.897219-0.019504 1.4336-1.39459 1.345829-2.750171 2.759924-4.183772 4.242285-0.390095-0.351086-0.643657-0.565638-0.877714-0.809447-5.783162-5.77341-11.546819-11.566324-17.359238-17.320229-0.633905-0.633905-0.585143-0.975238 0.029257-1.560381 1.414095-1.336076 2.750171-2.750171 4.154514-4.174019z m70.090362 0c1.511619 1.531124 2.993981 3.003733 4.447086 4.5056 0.107276 0.117029 0.068267 0.44861 0.009752 0.643657-0.048762 0.146286-0.224305 0.253562-0.331581 0.370591-5.783162 5.77341-11.556571 11.527314-17.300724 17.310476-0.565638 0.57539-0.887467 0.565638-1.4336-0.009752-1.170286-1.26781-2.408838-2.4576-3.637638-3.666896-0.331581-0.331581-0.458362-0.585143-0.068266-0.98499l17.915123-17.895619c0.087771-0.078019 0.185295-0.126781 0.390096-0.273067z m-34.98179-11.234743c0.702171-0.029257 0.877714 0.224305 0.877714 0.877715-0.029257 3.676648-0.019505 7.353295-0.019505 11.02019v0.009753c0 3.618133-0.019505 7.236267 0.019505 10.8544 0 0.633905-0.146286 0.916724-0.604648 0.994742l-0.4096 0.019505a101.015162 101.015162 0 0 0-5.090743-0.009752c-0.565638 0-0.8192-0.126781-0.8192-0.750934v-22.225676c0-0.604648 0.195048-0.809448 0.780191-0.78019 1.765181 0.019505 3.52061 0.029257 5.266286 0z" fill="#52CC90" p-id="13609"></path><path d="M890.63619 235.129905a13.390019 13.390019 0 0 1-8.97219 3.462095c-3.540114 0-7.12899-1.414095-9.752381-4.174019-66.540495-69.992838-160.797257-110.162895-258.769676-110.162895s-188.884114 38.180571-256.195048 107.539504a13.507048 13.507048 0 0 1-18.724571 0.507124 12.65859 12.65859 0 0 1-0.585143-18.217447c72.313905-74.556952 170.159543-115.614476 275.446248-115.614477s206.652952 43.17379 278.196419 118.432915c5.002971 5.198019 4.681143 13.409524-0.643658 18.2272z m-277.494247-24.254172c79.306362 0 155.140876 32.914286 208.0768 90.277791 4.934705 5.383314 4.486095 13.653333-1.024 18.471009-2.574629 2.243048-5.77341 3.335314-8.981943 3.335315-3.72541 0-7.382552-1.462857-10.07421-4.359315-47.806171-51.843657-116.3264-81.607924-187.92838-81.607923-70.256152 0-137.820648 28.808533-185.490286 78.97478a13.789867 13.789867 0 0 1-19.056153 0.780191 12.804876 12.804876 0 0 1-0.770438-18.490514c52.682362-55.559314 127.561143-87.381333 205.24861-87.381334z m0.068267 125.942248c48.313295 0 93.671619 22.528 124.411123 61.732571a14.316495 14.316495 0 0 1-1.80419 19.436496 12.648838 12.648838 0 0 1-18.47101-1.930972c-25.736533-32.846019-63.712305-51.707124-104.135923-51.707124-39.136305 0-76.283124 17.837105-101.883124 48.956953a12.746362 12.746362 0 0 1-18.549029 1.404343 14.238476 14.238476 0 0 1-1.345828-19.436496c30.671238-37.156571 74.99581-58.455771 121.777981-58.455771z m1.287314 98.108952a56.046933 56.046933 0 0 1 56.07619 56.076191 56.046933 56.046933 0 0 1-56.07619 56.07619 56.046933 56.046933 0 0 1-56.076191-56.07619 56.134705 56.134705 0 0 1 56.076191-56.076191z m0 84.114286a28.067352 28.067352 0 0 0 28.038095-28.038095 28.028343 28.028343 0 0 0-28.038095-28.038095 28.028343 28.028343 0 0 0-28.038095 28.038095 28.067352 28.067352 0 0 0 28.038095 28.038095zM616.272457 565.062705c6.524343 0 11.6736 5.15901 11.6736 11.332266l0.341333 61.810591h35.108572a132.047238 132.047238 0 0 0 75.12259 80.115809v17.408a24.478476 24.478476 0 0 1-24.380952 24.380953H419.498667a24.478476 24.478476 0 0 1-24.380953-24.380953v-73.142857a24.478476 24.478476 0 0 1 24.380953-24.380952H603.574857v-61.810591c0-6.173257 5.149257-11.332267 11.332267-11.332266h1.365333zM505.699962 687.318552c-6.875429 0-12.366019 5.49059-12.366019 12.014934s5.49059 12.014933 12.366019 12.014933c6.865676 0 12.356267-5.49059 12.356267-12.014933 0-6.865676-5.49059-12.014933-12.356267-12.014934z m48.761905 0c-6.875429 0-12.366019 5.49059-12.366019 12.014934 0.341333 6.524343 5.841676 12.014933 12.366019 12.014933 6.865676 0 12.356267-5.49059 12.356266-12.014933 0-6.865676-5.49059-12.014933-12.356266-12.014934z m-97.874896 0c-6.865676 0-12.356267 5.49059-12.356266 12.014934s5.49059 12.014933 12.356266 12.014933c6.875429 0 12.366019-5.49059 12.366019-12.014933 0-6.865676-5.49059-12.014933-12.366019-12.014934z" fill="#999999" p-id="13610"></path><path d="M788.284952 493.987352c-56.554057 0-102.4 45.855695-102.4 102.4s45.845943 102.4 102.4 102.4c56.534552 0 102.4-45.855695 102.4-102.4s-45.865448-102.4-102.4-102.4z m49.893181 137.840153a10.24 10.24 0 0 1-14.492038 14.482285l-35.401143-35.440152-35.440152 35.410895a10.259505 10.259505 0 0 1-14.482286 0 10.24 10.24 0 0 1 0-14.482285l35.4304-35.410896-35.410895-35.440152a10.24 10.24 0 0 1 14.482286-14.482286l35.410895 35.440153 35.440152-35.440153a10.24 10.24 0 0 1 14.482286 14.482286l-35.4304 35.440152 35.401143 35.440153z" fill="#FC5A5A" p-id="13611"></path></svg>
\ No newline at end of file
<template>
<cr-overlay custom-class="net-error-mask" :show="showError">
<svg-icon icon-class="ufo" />
<p class="net-error-title">网络异常</p>
<cr-button type="danger" size="large" @click="refresh">刷新试试</cr-button>
</cr-overlay>
</template>
<script>
export default {
name: 'NetError',
data() {
return {
showError: false
};
},
mounted() {
this.init();
},
methods: {
refresh() {
window.location.reload();
},
init() {
window.addEventListener(
'online',
() => {
this.showError = false;
},
true
);
window.addEventListener(
'offline',
() => {
this.showError = true;
},
true
);
}
}
};
</script>
<style lang="less" scoped>
@import '../style/var.less';
.net-error {
&-mask {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 101;
background-color: #fff;
.svg-icon {
width: 180px;
height: 180px;
margin-top: -120px;
}
.cr-button {
margin-top: 32px;
width: 200px;
border-radius: 6px;
}
}
&-title {
color: @gray-3;
.text-16;
}
}
</style>
<template>
<svg :class="[className, 'svg-icon']" aria-hidden="true">
<use :xlink:href="iconName" />
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: [String, Array],
required: true
},
className: {
type: [String, Array],
default: ''
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`;
}
}
};
</script>
<style>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>
<template>
<div class="page error">
<cr-image src="@/assets/images/error/error404.png" width="298px" height="153px" />
<p>你要访问的页面找不到了</p>
<cr-button type="primary" block shape="circle" @click="backurl">返回</cr-button>
</div>
</template>
<script>
export default {
name: '',
data() {
return {};
},
mounted() {},
methods: {
backurl() {
this.$router.go(-1);
}
}
};
</script>
<style lang="less" scoped>
@import '../../style/var.less';
.error {
text-align: center;
padding-top: 0;
padding-bottom: 0;
.cr-image {
margin-top: @padding-sm * 3;
}
p {
color: @black;
.text-14;
margin: @padding-unit * 7 0;
}
}
</style>
<template>
<div class="page error">
<cr-image src="@/assets/images/error/error.png" width="298px" height="153px" />
<p>oh,no…出错了</p>
<cr-button type="primary" block shape="circle" @click="backurl">返回</cr-button>
</div>
</template>
<script>
export default {
name: '',
data() {
return {};
},
mounted() {},
methods: {
backurl() {
this.$router.go(-1);
}
}
};
</script>
<style lang="less" scoped>
@import '../../style/var.less';
.error {
text-align: center;
padding-top: 0;
padding-bottom: 0;
.cr-image {
margin-top: @padding-sm * 3;
}
p {
color: @black;
.text-14;
margin: @padding-unit * 7 0;
}
}
</style>
/*
* @Description:
* @Date: 2021-03-31 19:59:17
* @LastEditors: gzw
* @LastEditTime: 2021-03-31 19:59:17
*/
/*
* @Description:
* @Date: 2021-03-31 19:59:12
* @LastEditors: gzw
* @LastEditTime: 2021-03-31 19:59:13
*/
let protocol = window.location.protocol;
let payHost = protocol + '//mapi-qa.liangkebang.net/pay';
let shenceHost = 'https://bn.xyqb.com/sa?project=default'; // 测试地址
let talosHost = 'https://talos-vcc2.liangkebang.net'; // 电商分期测试环境服务地址
let operatorHost = 'https://operator.liangkebang.com';
export default {
talosHost,
operatorHost,
payHost,
test: true,
shenceHost
};
import production from './production.config';
import env from './env.config';
export default process.env.NODE_ENV === 'production' ? production : env;
const protocol = window.location.protocol;
const talosHost = protocol + '//talos.xyqb.com';
const operatorHost = protocol + '//auth.quantgroup.cn';
// const payHost = protocol + '//mapi.q-gp.com/pay';
const payHost = protocol + '//payapi.xyqb.com';
const shenceHost = 'https://bn.xyqb.com/sa?project=production';
export default {
// apiHost,
test: false,
shenceHost,
talosHost,
payHost,
operatorHost
};
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import '@/service/svg.service';
import '@/service/cherryUI.service';
import './style/index.less';
import saService from '@/service/sa.service';
import Bridge from '@qg/js-bridge';
import Raven from 'raven-js';
import RavenVue from 'raven-js/plugins/vue';
import { release } from '../.sentryclirc';
if (process.env.SENTRY_ENV !== 'test' && process.env.NODE_ENV === 'production') {
Raven.config('//21779c2dcb594299bdc803c5560cfecd@sentry.q-gp.com/60', {
release,
environment: process.env.NODE_ENV
})
.addPlugin(RavenVue, Vue)
.install();
}
Vue.prototype.util = new Bridge();
saService.init(router);
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app');
/*
* @Description: autoSaveForm mixins
* @LastEditors: gzw
* @Date: 2019-08-14 15:53:37
* @LastEditTime: 2020-12-15 19:17:44
*/
import localStorage from '@/service/localStorage.service';
export default {
data() {
return {
autoSaveTimer: null
};
},
methods: {
autoSaveHandler(key, saveKey, interval = 1000) {
if (this.autoSaveTimer) {
clearTimeout(this.autoSaveTimer);
}
const val = this[key];
this.autoSaveTimer = setTimeout(() => {
localStorage.set(saveKey, val);
}, interval);
},
clearSaveHandler(key) {
localStorage.remove(key);
},
hasAutoSaveData(key) {
return !!localStorage.get(key);
},
getSaveParams(key) {
return localStorage.get(key);
},
getSaveInfoHandler(key, target) {
this[target] = localStorage.get(key);
this[target + 'Cache'] = JSON.parse(JSON.stringify(localStorage.get(key)));
this.$forceUpdate();
}
}
};
/*
* @Description: 页面跳转时保存当前浏览位置和数据,返回时加载原有数据
* @Date: 2020-11-19 11:49:00
* @LastEditors: gzw
* @LastEditTime: 2020-11-19 14:46:51
*/
export default {
data() {
return {
cacheKey: '',
saveKeys: [],
loadCache: false
};
},
methods: {
setListCache() {
const val = {};
if (!this.saveKeys || !this.saveKeys.length) return;
this.saveKeys.forEach(item => {
val[item] = this[item];
});
val.scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
sessionStorage.setItem(this.cacheKey, JSON.stringify(val));
},
clearListCache() {
sessionStorage.removeItem(this.cacheKey);
},
hasListCache() {
return !!sessionStorage.getItem(this.cacheKey);
},
getListCache() {
this.loadCache = true;
const listCache = JSON.parse(sessionStorage.getItem(this.cacheKey));
this.saveKeys.forEach(item => {
this[item] = listCache[item];
});
document.documentElement.scrollTop = listCache.scrollTop;
document.body.scrollTop = listCache.scrollTop;
this.clearListCache();
this.$nextTick(() => {
this.loadCache = false;
});
}
}
};
import Vue from 'vue';
import VueRouter from 'vue-router';
import initService from '@/service/init.service';
import routes from './routes';
Vue.use(VueRouter);
// 解决跳转相同路由报错
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
return originalPush.call(this, location).catch(err => err);
};
const originalReplace = VueRouter.prototype.replace;
VueRouter.prototype.replace = function replace(location) {
return originalReplace.call(this, location).catch(err => err);
};
const scrollBehavior = (to, from, savedPosition) => {
if (savedPosition) {
return savedPosition;
}
return {
x: 0,
y: 0
};
};
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior
});
initService.init(router);
export default router;
export default [
{
path: '/',
redirect: '/error'
},
{
path: '/demo',
alias: ['/demo-page'],
name: 'demo-page',
back: false,
meta: {
title: 'DEMO',
has: {
header: true,
footer: true
}
},
component: () => import('../views/demo')
},
{
path: '/error',
name: 'error',
meta: {
title: '出错了'
},
component: () => import('../components/error')
},
{
path: '*',
name: 'notFound',
meta: {
title: '找不到页面'
},
component: () => import('../components/error/NotFound')
}
];
import Vue from 'vue';
// import store from '../store';
import {
Button,
Image,
Icon,
Cell,
CellGroup,
Row,
Col,
Dialog,
Popup,
Overlay,
Divider,
Loading,
Toast,
Picker,
NavBar,
Field,
Checkbox,
CardList,
List,
Form,
Sticky,
Tab,
Tabs
} from '@qg/cherry-ui';
import DialogFn from '@qg/cherry-ui/src/dialog/func';
// import "@qg/cherry-ui/dist/cherry.css";
Vue.use(NavBar);
Vue.use(Button);
Vue.use(Image);
Vue.use(Cell);
Vue.use(CellGroup);
Vue.use(Row);
Vue.use(Col);
Vue.use(Popup);
Vue.use(Divider);
Vue.use(Toast);
Vue.use(Picker);
Vue.use(Field);
Vue.use(Dialog);
Vue.use(Checkbox);
Vue.use(Form);
Vue.use(Icon);
Vue.use(Sticky);
Vue.use(Overlay);
Vue.use(Form);
Vue.use(CardList);
Vue.use(Loading);
Vue.use(List);
Vue.use(Tab);
Vue.use(Tabs);
// const _proto = Vue.prototype;
// const proto = Object.create(_proto);
// proto.$notify = option => {
// option.customClass = store.state.pay.header ? 'has-header' : '';
// return _proto.$notify(option);
// };
// Vue.prototype = proto;
Vue.prototype.$dialog = DialogFn;
import cookies from 'js-cookie';
const option = {
domain: window.location.host.indexOf('.q-gp.com') >= 0 ? '.q-gp.com' : '',
expires: 365
};
const Cookies = {
get(key) {
return cookies.get(key) || '';
},
set(key, value) {
return cookies.set(key, value, option);
},
clear() {
return Object.keys(cookies.get()).forEach(function(cookie) {
Cookies.remove(cookie, option);
});
},
remove(key) {
return cookies.remove(key, option);
}
};
export default Cookies;
import HttpRequest from '@qg/ui-request';
import { Toast } from '@qg/cherry-ui';
import store from '@/store';
const http = new HttpRequest(
{},
{},
function(msg) {
Toast(msg);
},
function(loadingState) {
store.dispatch('change_loading', loadingState);
}
);
export default http;
/**
* Created by jialiu on 17/1/11.
*/
import store from '../store';
import { isWechat, isApp } from './validation.service';
import Cookies from './cookieStorage.service';
import localStorage from './localStorage.service';
//formXcxPage:标识是从小程序跳转过来的;
const localStorageParams = ['creditToken', 'vccToken', 'vccChannel', 'formXcxPage'];
const cookiesParams = ['h'];
export default {
// token校验,整个流程都是登陆后的
init: router => {
router.beforeEach((to, from, next) => {
// 所有自定义路由字段在此处理
const { meta } = to;
meta?.has?.header && store.commit('CHANGE_HEADER', meta.has.header); // 改变header
localStorageParams.forEach(item => {
to.query[item] && localStorage.set(item, to.query[item]);
});
cookiesParams.forEach(item => {
to.query[item] && Cookies.set(item, to.query[item]);
});
(isWechat || isApp || Cookies.get('h') === '0') && store.commit('CHANGE_HEADER', false); // 改变header
document.body.className = store.state.pay.header ? 'has-header' : '';
store.commit('CHANGE_TITLE', meta?.title); // 改变title
next();
});
}
};
export default {
get(key) {
let result = window.localStorage.getItem(key);
try {
return JSON.parse(result);
} catch (e) {
return result;
}
},
set(key, value) {
let toString = Object.prototype.toString;
if (toString.call(value) === '[object Array]' || toString.call(value) === '[object Object]') {
value = JSON.stringify(value);
}
return window.localStorage.setItem(key, value);
},
remove(key) {
return window.localStorage.removeItem(key);
},
clear() {
return window.localStorage.clear();
}
};
import Vue from 'vue';
import sa from 'sa-sdk-javascript';
import config from '@/config';
export default {
init: function(router) {
sa.init({
server_url: config.shenceHost,
heatmap: {
//是否开启点击图,默认 default 表示开启,自动采集 $WebClick 事件,可以设置 'not_collect' 表示关闭
clickmap: 'default',
//是否开启触达注意力图,默认 default 表示开启,自动采集 $WebStay 事件,可以设置 'not_collect' 表示关闭
scroll_notice_map: 'default'
},
//判断外层是否有 App 的 SDK ,如果有的话,会往 App 的 SDK 发数据。如果没有,就正常发送数据。
use_app_track: true,
show_log: config.test,
app_js_bridge: true
});
// 添加公共属性
sa.registerPage({
platformType: 'H5',
uuid: '',
qg_device_id: '',
channel_code: '',
splitStream: ''
});
router.afterEach(() => {
Vue.nextTick(() => {
sa.quick('autoTrackSinglePage');
});
});
}
};
export function registeredEvents(eventName, eventData, callback = () => {}) {
sa.track(eventName, eventData, callback);
}
// 用户登录神策埋点
export function loginSa(uuid = localStorage.get('uuid')) {
if (!uuid) return;
localStorage.set('uuid', uuid);
sa.login(uuid);
}
export function saDeviceId() {
return new Promise(resolve => {
sa.quick('isReady', function() {
resolve(sa.quick('getAnonymousID'));
});
});
}
import localStorage from './localStorage.service';
export const basicOptions = {
// 职业选项
jobs: [
{
value: 'WORKER',
name: '工人'
},
{
value: 'TEACHER',
name: '教师'
},
{
value: 'WHITE_COLLAR',
name: '白领'
},
{
value: 'CAREER_BUILDER',
name: '创业者'
},
{
value: 'SELF_EMPLOYER',
name: '个体户'
},
{
value: 'EMPLOYEE',
name: '公司职员'
},
{
value: 'BISUNESS_ENTITY',
name: '企业法人'
},
{
value: 'ONLINE_STORE_OWNER',
name: '网店店主'
},
{
value: 'UNEMPLOYED',
name: '暂无职业'
},
{
value: 'OTHER',
name: '其他'
}
],
// 学历选项
educationList: [
{
value: 'MASTER',
name: '硕士及以上'
},
{
value: 'UNDER_GRADUATE',
name: '本科'
},
{
value: 'JUNIOR_COLLEGE',
name: '大专'
},
{
value: 'TECHNICAL_SECONDARY_SCHOOL',
name: '中专'
},
{
value: 'TECHNICAL_SCHOOL',
name: '技校'
},
{
value: 'HIGH_SCHOOL',
name: '高中'
},
{
value: 'MIDDLE_SCHOOL',
name: '初中'
},
{
value: 'PRIMARY_SCHOOL',
name: '小学'
},
{
value: 'OTHER',
name: '其他'
}
],
// 月收入选项
incomeRangeList: [
{
value: 'BELOW_1000',
name: '小于1000元'
},
{
value: 'BELOW_3000',
name: '1000至3000元'
},
{
value: 'BELOW_5000',
name: '3000至5000元'
},
{
value: 'BELOW_8000',
name: '5000至8000元'
},
{
value: 'BELOW_10000',
name: '8000至10000元'
},
{
value: 'BELOW_15000',
name: '10000至15000元'
},
{
value: 'BELOW_20000',
name: '15000至20000元'
},
{
value: 'ABOVE_20000',
name: '大于20000元'
}
],
emailList: [
{
value: 1,
name: 'qq.com'
},
{
value: 2,
name: '163.com'
},
{
value: 3,
name: '126.com'
},
{
value: 4,
name: 'icloud.com'
},
{
value: 5,
name: '139.com'
},
{
value: 6,
name: '189.com'
},
{
value: 7,
name: 'gmail.com'
}
]
};
export const relation = [
{
value: 'PARENT',
name: '父母'
},
{
value: 'CHILDREN',
name: '子女'
},
{
value: 'BROTHER',
name: '兄弟姐妹'
},
{
value: 'COLLEAGUE',
name: '同事'
},
{
value: 'CLASSMATE',
name: '同学'
},
{
value: 'FRIEND',
name: '朋友'
},
{
value: 'SPOUSE',
name: '夫妻'
},
{
value: 'SELF',
name: '本人'
},
{
value: 'OTHER',
name: '其他'
}
];
// 消费额度申请合同列表
export const authList = [
{
id: 328,
contractTemplateName: '客户非学生承诺函'
},
{
id: 9,
contractTemplateName: '个人信息查询及使用综合授权书'
}
];
export const qoutaList = [
{
id: 281,
contractTemplateName: '上海银行直销银行电子账户服务协议'
},
{
id: 414,
contractTemplateName: '享花卡消费贷款服务协议'
},
{
id: 413,
contractTemplateName: '享花卡服务协议'
},
{
id: 412,
contractTemplateName: '委托扣款授权书'
}
];
export const xyqbDownLink =
'https://activity.q-gp.com/activity/59a4d7f7f70cff7a37fed830?from=singlemessage&isappinstalled=0';
export const toUseUrl = 'xyqb://mall/goodsList?title=品牌尖货&labelId=58&categoryId=';
export function wxsdk() {
if (+localStorage.get('vccChannel') !== 159913) return; // 是否是小程序
var oHead = document.getElementsByTagName('head').item(0);
var oScript = document.createElement('script');
oScript.type = 'text/javascript';
oScript.src = 'https://res.wx.qq.com/open/js/jweixin-1.4.0.js';
oHead.appendChild(oScript);
}
import Vue from 'vue';
import SvgIcon from '@/components/SvgIcon'; // svg component
// register globally
Vue.component('svg-icon', SvgIcon);
const req = require.context('../assets/svg', false, /\.svg$/);
const requireAll = requireContext => requireContext.keys().map(requireContext);
requireAll(req);
import { isAndroid, isIOS } from './validation.service';
/**
* 替换邮箱字符
* @param {String} email 输入字符串
* @return: {Function} new_email 邮箱
*/
export function regEmail(email) {
if (String(email).indexOf('@') > 0) {
const str = email.split('@');
let _s = '';
if (str[0].length > 3) {
for (var i = 0; i < str[0].length - 3; i++) {
_s += '*';
}
}
var new_email = str[0].substr(0, 3) + _s + '@' + str[1];
}
return new_email;
}
/**
* 替换手机字符
* @param {String} mobile 输入字符串
* @return: {Function} new_email 邮箱
*/
export function regMobile(mobile) {
if (mobile.length > 7) {
var new_mobile = mobile.substr(0, 3) + '****' + mobile.substr(7);
}
return new_mobile;
}
/**
* 去除两侧空格
* @param {String} s 输入字符串
* @return: {Function} new_email 邮箱
*/
export function stringTrim(s) {
s = stringTrimLeft(s);
return stringTrimRight(s);
}
/**
* 去除左侧空格
* @param {String} s 输入字符串
* @return: {Function} new_email 邮箱
*/
export function stringTrimLeft(s) {
return s.replace(/^[\s\n\t]+/g, '');
}
/**
* 去除右侧空格
* @param {String} s 输入字符串
* @return: {Function} new_email 邮箱
*/
export function stringTrimRight(s) {
return s.replace(/[\s\n\t]+$/g, '');
}
/**
* 只能输入中文、英文、数字
* @param {String} str 输入字符串
* @return: {String} new_stremail 中文、英文、数字
*/
export function filterSc(str) {
// eslint-disable-next-line
return str.replace(/[^\a-\z\A-\Z0-9\u4E00-\u9FA5]/g, "");
}
/**
* 字符长度(中文)
* @param {String} str 输入字符串
* @return: {Number} len 长度
*/
export function strLen(str) {
var len = 0;
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
//单字节加1
if ((c >= 0x0001 && c <= 0x007e) || (0xff60 <= c && c <= 0xff9f)) {
len++;
} else {
len += 2;
}
}
return len;
}
/**
* 替换emoji表情
* @param {String} name 输入字符串
* @return: {String} str 字符串
*/
export function filterEmoji(name) {
// eslint-disable-next-line
let str = name.replace(
// eslint-disable-next-line
/[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF][\u200D|\uFE0F]|[\uD83C|\uD83D|\uD83E][\uDC00-\uDFFF]|[0-9|*|#]\uFE0F\u20E3|[0-9|#]\u20E3|[\u203C-\u3299]\uFE0F\u200D|[\u203C-\u3299]\uFE0F|[\u2122-\u2B55]|\u303D|[\A9|\AE]\u3030|\uA9|\uAE|\u3030/gi,
''
);
return str;
}
/**
* hash路由获取url参数
* @param {String} searchString 输入字符串
* @return: {String} str 字符串
*/
export function parseSearch(searchString) {
if (!searchString) {
return {};
}
if (!searchString.includes('?')) {
return {};
}
return searchString
.split('?')[1]
.split('#')[0]
.split('&')
.reduce((result, next) => {
const pair = next.split('=');
try {
result[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
} catch (e) {
// eslint-disable-next-line
// result[decodeURIComponent(pair[0])] = window.$URL.decode(pair[1]);
}
return result;
}, {});
}
/**
* 时间转换
* @param {String} time 需要转换的时间
* @param {String} cFormat 格式 {y}-{m}-{d} {h}:{i}:{s}
* @return: {String} timeStr 转换完成的时间
*/
export function parseTime(time, cFormat) {
if (arguments.length === 0) {
return '-';
}
if (time == null) {
return '-';
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
let date;
if (time == '') {
date = new Date();
} else if (typeof time === 'object') {
date = time;
} else if (!isNaN(time) && ('' + time).length === 10) {
time = parseInt(time) * 1000;
} else {
if (String(time).indexOf('T') > -1) {
time = time.replace(/T/g, ' ').replace(/\..*/g, '');
}
if (
(String(time).indexOf('-') > -1 || String(time).indexOf('.') > -1) &&
String(time).indexOf('T') == -1
) {
time = time.replace(/-|\./g, '/');
}
date = new Date(time);
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
};
const timeStr = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key];
if (key === 'a') {
return ['', '', '', '', '', '', ''][value];
}
if (result.length > 0 && value < 10) {
value = '0' + value;
}
return value || 0;
});
return timeStr;
}
//返回拨打标签
export function regPhone(data) {
const regExp = new RegExp(/((0\d{2,3}-\d{7,8})|(1[358674]\d{9}))/, 'g'); //匹配电话,以便能直接拨打
const newData = data.replace(regExp, "<a class='tel' href='tel:$1'>$1</a>");
return newData;
}
export function formatPhone(phone) {
const reg = /^(\d{3})(\d{4})(\d{4})$/;
const matches = reg.exec(phone);
const newPhone = matches[1] + ' ' + matches[2] + ' ' + matches[3];
return newPhone;
}
/**
* @description: 移动端监听软键盘弹起或收起,IOS,android
* @param {Boolean} off 是否销毁
* @return {Function} callback 软键盘弹起或收取回调, true => 弹起, false => 隐藏
*/
export function onKeyboardStateChange(callback = () => {}, off = false) {
if (isIOS) {
let isReset = true; //是否归位
const focusinHandler = () => {
isReset = false; //聚焦时键盘弹出,焦点在输入框之间切换时,会先触发上一个输入框的失焦事件,再触发下一个输入框的聚焦事件
callback(true);
};
const focusoutHandler = () => {
isReset = true;
setTimeout(() => {
//当焦点在弹出层的输入框之间切换时先不归位
if (isReset) {
// window.scroll(0, 0); // 【暂时取掉】确定延时后没有聚焦下一元素,是由收起键盘引起的失焦,则强制让页面归位
document.body.scrollTop = 0;
document.documentElement.scrollTop = 0;
callback(false);
}
}, 30);
};
if (off) {
document.body.removeEventListener('focusin', focusinHandler);
document.body.removeEventListener('focusout', focusoutHandler);
} else {
document.body.addEventListener('focusin', focusinHandler);
document.body.addEventListener('focusout', focusoutHandler);
}
}
if (isAndroid) {
const originHeight = document.documentElement.clientHeight || document.body.clientHeight;
const resizeHandler = () => {
const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
const activeElement = document.activeElement;
if (resizeHeight < originHeight) {
// 键盘弹起后逻辑
if (
activeElement &&
(activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA')
) {
setTimeout(() => {
activeElement.scrollIntoView({ block: 'center' }); //焦点元素滚到可视区域的问题
}, 0);
callback(true);
}
} else {
// 键盘收起后逻辑
callback(false);
}
};
if (off) {
window.removeEventListener('resize', resizeHandler);
} else {
window.addEventListener('resize', resizeHandler);
}
}
}
export function changeTitleOfIOS() {
if (isIOS) {
const hackIframe = document.createElement('iframe');
hackIframe.style.display = 'none';
hackIframe.src = '/fixIosTitle.html?r=' + Math.random();
document.body.appendChild(hackIframe);
setTimeout(() => {
document.body.removeChild(hackIframe);
}, 300);
}
}
export function getParameterByName(name, url) {
if (!url) url = window.location.href;
// eslint-disable-next-line no-useless-escape
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
// 清除键盘
export function clearKeyboard() {
var input = document.getElementsByTagName('input');
for (let i = 0; i < input.length; i++) {
input[i].blur();
}
var textarea = document.getElementsByTagName('textarea');
for (let i = 0; i < textarea.length; i++) {
textarea[i].blur();
}
}
export function idNoFormat(value) {
return value.replace(/(\d{6})(\d{0,8})?(\d{0,4})?/, function(res, $1, $2, $3) {
if (res.length <= 6) {
return $1;
}
if (res.length <= 14) {
return $1 + ' ' + $2;
}
return $1 + ' ' + $2 + ' ' + $3;
});
}
export function dateFormat(value) {
return value.replace(/(\d{4})(\d{2})(\d{2})/, '$1.$2.$3');
}
/*eslint-disable*/
import { stringTrim } from './utils.service';
// 判断输入内容是否为空
export function isNull(str) {
return str === undefined || str.length === 0 || str === null;
}
// 判断输入内容去掉空格是否为空
export function isTrimNull(str) {
if (str === undefined || str.length === 0 || str === null) {
return true;
}
return stringTrim(str).length === 0;
}
// 判断日期类型是否为YYYY-MM-DD格式的类型
export function isDate(str) {
if (!isNull(str)) {
const reg = /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2})$/;
const r = str.match(reg);
return r !== null;
}
}
// 判断日期类型是否为YYYY-MM-DD hh:mm:ss格式的类型
export function isDateTime(str) {
if (!isNull(str)) {
const reg = /^(\d{1,4})(-|\/)(\d{1,2})\2(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})$/;
const r = str.match(reg);
return r !== null;
}
return false;
}
// 判断日期类型是否为hh:mm:ss格式的类型
export function isTime(str) {
if (!isNull(str)) {
const reg = /^((20|21|22|23|[0-1]\d)\:[0-5][0-9])(\:[0-5][0-9])?$/;
return reg.test(str);
}
return false;
}
// 校验是否合法日期
export function isValidDate(date) {
return date instanceof Date && !isNaN(date.getTime());
}
// 判断输入的字符是否为英文字母
export function isLetter(str) {
if (!isNull(str)) {
const reg = /^[a-zA-Z]+$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为整数
export function isInteger(str) {
if (!isNull(str)) {
const reg = /^[-+]?\d*$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为身份证号
export function isIdNo(str) {
if (!isNull(str)) {
const reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为中文姓名最少两个中文字符
export function isChineseName(str) {
if (!isNull(str)) {
const reg = /^[\u0391-\uFFE5\·]{2,}$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否以数字开头
export function isStartWithInteger(str) {
if (!isNull(str)) {
const reg = /^[0-9].*$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为双精度
export function isDouble(str) {
if (!isNull(str)) {
const reg = /^[-\+]?\d+(\.\d+)?$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为:a-z,A-Z,0-9,_,并以字母开头
export function isProgramVar(str) {
if (!isNull(str)) {
const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为:a-z,A-Z,0-9,_,并以大写字母开头
export function isProgramClassName(str) {
if (!isNull(str)) {
const reg = /^[A-Z][a-zA-Z0-9_]*$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为中文
export function isChinese(str) {
if (!isNull(str)) {
const reg = /^[\u0391-\uFFE5]+$/;
return reg.test(str);
}
return false;
}
// 判断输入的EMAIL格式是否正确
export function isEmail(str) {
if (!isNull(str)) {
const reg = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/g;
return reg.test(str);
}
return false;
}
// 判断输入的邮编(只能为六位)是否正确
export function isZIP(str) {
if (!isNull(str)) {
const reg = /^\d{6}$/;
return reg.test(str);
}
}
// 判断字符串的长度是否满足条件
export function isLengthValidate(str, len) {
if (str) {
if (str.length <= len) {
return true;
} else {
return false;
}
} else {
return true;
}
}
// 判断输入的字符是否为:a-z,A-Z,0-9,_,汉字
export function isProgramName(str) {
if (!isNull(str)) {
const reg = /^[a-zA-Z0-9_\u4e00-\u9fa5]+$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否为:a-z,A-Z,0-9,_,汉字
export function isProgramCode(str) {
if (!isNull(str)) {
const reg = /^[a-zA-Z0-9_\u4e00-\u9fa5]+$/;
return reg.test(str);
}
return false;
}
// 判断输入的字符是否以http://或者https://开头,且必须是个网址
export function isProgramWeb(str) {
if (!isNull(str)) {
let reg = /^((https|http)?:\/\/)[^\s]+/;
return reg.test(str);
}
return false;
}
export function isValueTypeMatched(type, value) {
switch (type) {
case 'Integer':
if (!/^\-?\d+$/.test(value)) {
return false;
}
break;
case 'Double':
if (!/^\-?\d+\.\d+$/.test(value)) {
return false;
}
break;
case 'Long':
if (!/^\-?\d+$/.test(value)) {
return false;
}
break;
case 'Boolean':
if (value !== 'true' && value !== 'false') {
return false;
}
break;
default:
return true;
}
return true;
}
export function isProperties(str) {
if (!isNull(str)) {
let reg = /^[a-zA-Z\u4e00-\u9fa5][a-zA-Z_0-9\u4e00-\u9fa5]*\s*=\s*[a-zA-Z_0-9\u4e00-\u9fa5]+/;
return reg.test(str);
}
return false;
}
// 拒绝码以大写字母开头,包含数字、大写字母、下划线,且为四段式
export function isUppercase(str) {
const reg = /^[A-Z]([A-Z0-9]*_){1}([A-Z0-9]+_){2}[A-Z0-9]+$/;
return reg.test(str);
}
//判断用户名
export function isUserName(str) {
if (!isNull(str)) {
const reg = /^[a-zA-Z\d_]{4,20}$/;
return reg.test(str);
}
return false;
}
//判断密码
export function isPassword(str) {
if (!isNull(str)) {
const reg = /^[a-zA-Z\d]{6,14}$/;
return reg.test(str);
}
return false;
}
//判断手机号
export function isPhone(str) {
if (!isNull(str)) {
const reg = /^1[3456789]\d{9}$/;
return reg.test(str);
}
return false;
}
// 判断税号
export function isTax(str) {
if (!isNull(str)) {
const reg = /^[A-Z0-9]{15}$|^[A-Z0-9]{17}$|^[A-Z0-9]{18}$|^[A-Z0-9]{20}$/;
return reg.test(str);
}
return false;
}
// 判断银行号
export function isBankNumber(str) {
if (!isNull(str)) {
const reg = /^[0-9]{16}$|^[0-9]{17}$|^[0-9]{19}$/;
return reg.test(str);
}
return false;
}
// 判断中文地址不能少于5个汉字,且必须有一位数字
export function isChnAddress(str) {
const isNum = /[0-9]\d*/;
const isChinese_var = /[\u4e00-\u9fa5]/g;
if (!isNull(str)) {
return isNum.test(str) && str.match(isChinese_var).length >= 5;
}
return false;
}
const ua = window.navigator.userAgent.toLowerCase();
// 判断微信环境
export const isWechat = ua.match(/MicroMessenger/i) == "micromessenger";
// 判断羊小咩(信用钱包)环境
export const isApp= ua.match(/xyqb/i) == "xyqb";
// 判断真享生活
export const isVcc= ua.match(/VCC/i) == "vcc";
// 判断IOS环境
export const isIOS = /iphone|ipad|ipod/.test(ua);
// 判读Android环境
export const isAndroid = /android/.test(ua);
// 判断
export const appVersion = ua.match(/xyqb\/([\d|.]+)/);
export const isQQ = ua.match(/QQ/i) == 'qq';
export const isWeiBo = ua.match(/Weibo/i) == 'weibo';
export const isDingTalk = ua.match(/DingTalk/i) == 'dingtalk';
import Vue from 'vue';
import Vuex from 'vuex';
import payModule from './module';
Vue.use(Vuex);
let store = new Vuex.Store({
modules: {
pay: payModule
}
});
export default store;
import * as types from './type';
const state = {
header: true,
title: '支付中心',
loading: false,
meta: {}
};
// getters
const getters = {};
// actions
const actions = {
change_header({ commit }, header) {
commit(types.CHANGE_HEADER, header);
},
change_title({ commit }, title) {
commit(types.CHANGE_TITLE, title);
},
change_meta({ commit }, meta) {
commit(types.CHANGE_META, meta);
},
change_loading({ commit }, loading) {
commit(types.CHANGE_LOADING, loading);
}
};
// mutations
const mutations = {
[types.CHANGE_HEADER](state, header) {
if (header !== undefined) {
state.header = header;
}
},
[types.CHANGE_LOADING](state, loading) {
state.loading = loading;
},
[types.CHANGE_TITLE](state, title) {
if (title) {
state.title = title;
}
document.title = state.title;
},
[types.CHANGE_META](state, meta) {
for (let i in meta) {
state.meta[i] = meta[i];
}
}
};
export default {
state,
getters,
actions,
mutations
};
export const CHANGE_HEADER = 'CHANGE_HEADER';
export const CHANGE_TITLE = 'CHANGE_TITLE';
export const CHANGE_META = 'CHANGE_META';
export const CHANGE_LOADING = 'CHANGE_LOADING';
// @font-face {
// font-family: 'iconfont'; /* project id 2032151 */
// src: url('//at.alicdn.com/t/font_2032151_38j664if0q9.eot');
// src: url('//at.alicdn.com/t/font_2032151_38j664if0q9.eot?#iefix') format('embedded-opentype'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.woff2') format('woff2'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.woff') format('woff'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.ttf') format('truetype'),
// url('//at.alicdn.com/t/font_2032151_38j664if0q9.svg#iconfont') format('svg');
// }
@font-face {
font-family: 'din';
src: url('./DIN.ttf') format('truetype');
}
\ No newline at end of file
@import 'reset.less';
@import 'var.less';
@import 'mixins.less';
@import 'iconfont.less';
html,
body,
#app {
height: 100%;
}
html {
font-size: 37.5px;
font-size: 10vw;
}
body {
font-family: @font-family;
background-color: @background-color;
}
.page {
background-size: contain;
background-color: @background-color;
height: 100%;
padding: 0 @page-padding;
&__ios-fix-bottom {
&::after {
content: '';
display: block;
padding-bottom: @ios-bottom-height;
}
}
&__nopad {
padding: 0;
}
}
a {
color: @red;
.text-14;
}
textarea::-webkit-input-placeholder,
input::-webkit-input-placeholder {
color: @gray-3;
}
input,
textarea {
outline: none !important;
}
input {
-webkit-appearance: none;
}
strong {
font-weight: @font-weight-bold;
}
.cr-nav-bar {
&__title {
font-weight: 400 !important;
}
&__left {
color: @gray-4 !important;
}
}
.cr-cell-group::after, .cr-cell-group_content::after {
display: none !important;
}
.cr-picker {
&--toolbar {
&-title {
font-size: 16px;
color: #333;
}
&-cancel {
color: #999999 !important;
}
&-confirm {
color: @red !important;
}
&::after {
display: none;
}
}
}
.has-header {
.app {
height: calc(100% - @nav-bar-height);
min-height: calc(100% - @nav-bar-height);
}
}
.cr-dialog--confirm, .cr-dialog--confirm:active {
color: @red;
}
.cr-cell:last-child::after {
position: absolute;
box-sizing: border-box;
content: ' ';
pointer-events: none;
bottom: 0;
left: 0.426667rem;
right: 0;
border-bottom: 0.026667rem solid #f2f3f5;
transform: scaleY(0.5);
}
\ No newline at end of file
.iphonex-fix-padding {
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.iphonex-fix-margin {
margin-bottom: constant(safe-area-inset-bottom);
margin-bottom: env(safe-area-inset-bottom);
}
/**
* Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/)
* http://cssreset.com
*/
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video,
input {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font-weight: normal;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
/* custom */
a {
color: #7e8c8d;
text-decoration: none;
}
li {
list-style: none;
}
body {
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
// 覆盖cherry-ui样式
@import "./var.less";
@button-border-width: 0;
@button-default-height: 37px;
@button-default-line-height: 37px;
@button-default-font-size: 16px;
@button-primary-color: @white;
@button-primary-background-color: @gradient-red;
@button-danger-background-color: @red;
@button-danger-border-color: @red;
@button-large-height: 48px;
@button-large-line-height: 48px;
@field-error-message-color: @red-dark;
@field-error-message-font-size: 12px;
@field-error-placeholder-color: @red-dark;
@checkbox-icon-border: @border-width-base solid @gray-2;
@checkbox-label-color: @text-color;
@checkbox-checked-color: @white;
@checkbox-checked-border-color: @red;
@checkbox-checked-background-color: @red;
@cell-group-title-background: @gray-1;
@cell-group-title-color: @orange;
@cell-group-title-padding: @padding-xs @padding-lg @padding-xs;
@cell-group-title-font-size: 12px;
@cell-border-color: @grey-border;
@cell-right-icon-color: @gray-3;
@cell-icon-size: 16px;
@field-label-width: 75px;
@dialog-width: 290px;
@dialog-font-size: 14px;
@dialog-border-radius: @border-radius-lg;
@dialog-message-font-size: 16px;
@dialog-has-title-message-text-color: @text-color;
@dialog-confirm-button-text-color: @red;
@navbar-default-height: @nav-bar-height;
@picker-toolbar-padding: @padding-unit - 2 @padding-md;
@picker-font-size: 14px;
@loading-color: @white;
@loading-text-color: @white;
\ No newline at end of file
// Color Palette
@black: #333333;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #dcdcdc;
@gray-3: #c8c9cc;
@gray-4: #999999;
@gray-5: #666666;
@red: #ec1500;
@red-light: #ec3333;
@red-dark: #ee0a24;
@orange: #faab0c;
@grey-border: #f2f3f5;
// Gradient Colors
@gradient-red: linear-gradient(269deg, #ff5d00 12%, #ff1900 86%);
@gradient-pink: linear-gradient(180deg, #fff7f0 0%, #ffe4dc 100%);
// Component Colors
@text-color: @black;
@text-grey: @gray-4;
@active-opacity: 0.8;
@disabled-opacity: 0.7;
@background-color: @gray-1;
@line-height-sm: 16px;
@line-height-md: 22px;
@line-height-lg: 24px;
// Border
@border-width-base: 1px;
@border-radius-sm: 6px;
@border-radius-md: 8px;
@border-radius-lg: 16px;
@border-radius-lx: 20px;
@border-radius-max: 999px;
// Padding
@padding-unit: 4px;
@padding-xs: @padding-unit * 2;
@padding-sm: @padding-unit * 3;
@padding-md: @padding-unit * 4;
@padding-lg: @padding-unit * 5;
@padding-xl: @padding-unit * 8;
// Font
@font-size-list: 10,11, 12, 13, 14, 16, 17, 18, 20, 26, 28, 30, 52;
.generate-text(12);
.generate-text(@n, @i: 1) when (@i =< @n) {
@font: extract(@font-size-list, @i);
.text-@{font} {
font-size: @font + 0px;
line-height: @font + 6px;
}
.generate-text(@n, @i + 1);
}
@font-weight-bold: 600;
@font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
@font-din: 'din';
@deep: ~'>>>';
// 页面内边距
@page-padding: @padding-sm;
@page-padding-lg: @padding-lg;
// ios底部防遮挡高度
@ios-bottom-height: 60px;
// 页面顶部header高度
@nav-bar-height: 48px;
\ No newline at end of file
@import '../../style/var.less';
.page {
padding: @page-padding-lg @page-padding-lg 0;
height: calc(100% - @page-padding-lg);
}
.banner {
position: relative;
display: flex;
justify-content: center;
background-image: @gradient-red;
background-repeat: no-repeat;
margin: -1 * @page-padding-lg -1 * @page-padding-lg 48px -1 * @page-padding-lg;
padding: @padding-md @padding-lg 0;
}
.card {
&__icon {
height: 200px;
width: 200px;
margin: 0 auto;
display: block;
}
&__info {
padding: 20px 0;
color: #fff;
font-size: 28px;
text-align: center;
}
}
<template>
<div class="page">
<div class="banner">
<div class="card">
<p class="card__info">{{ list.length }}</p>
</div>
</div>
<svg-icon icon-class="ufo" class="card__icon" />
</div>
</template>
<script>
import demoApi from '@/api/demo.auth';
export default {
data() {
return {
list: []
};
},
mounted() {
this.getList();
},
methods: {
async getList() {
const res = await demoApi.recommendLike();
this.list = res;
}
}
};
</script>
<style lang="less" src="./index.less" scoped></style>
const path = require('path');
const resolve = dir => path.join(__dirname, dir);
const IS_PROD = process.env.NODE_ENV === 'production';
const WebpackCosCdnUploadPlugin = require('@qg/webpack-cos-cdn-upload-plugin');
// const SentryPlugin = require('webpack-sentry-plugin');
// const SentryConfig = require('./.sentryclirc');
// cdn预加载使用
const externals = {
vue: 'Vue',
'vue-router': 'VueRouter',
vuex: 'Vuex',
'sa-sdk-javascript': 'sensorsDataAnalytic201505' // 神策js在window下的全局引用会出现两个,这个才是真物
// axios: "axios"
};
const cdn = {
// 生产环境
build: {
js: [
'https://activitystatic.lkbang.net/vue/2.6.11/vue.min.js',
'https://activitystatic.lkbang.net/vue-router/3.2.0/vue-router.min.js',
'https://activitystatic.lkbang.net/vuex/3.4.0/vuex.min.js',
'https://activitystatic.lkbang.net/sa-sdk-javascript/1.15.16/sensorsdata.min.js'
]
}
};
module.exports = {
transpileDependencies: [/[/\\]node_modules[/\\]@qg[/\\]cherry-ui[/\\]/],
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].title = '';
if (IS_PROD) {
args[0].cdn = cdn.build;
}
return args;
});
// 修复HMR
config.resolve.symlinks(true);
// 移除 prefetch 插件(针对生产环境首屏请求数进行优化)
config.plugins.delete('prefetch');
// 移除 preload 插件(针对生产环境首屏请求数进行优化) preload 插件的用途:https://cli.vuejs.org/zh/guide/html-and-static-assets.html#preload
config.plugins.delete('preload');
// 添加别名
config.resolve.alias.set('@', resolve('src'));
// svg-sprite
config.module
.rule('svg')
.exclude.add(resolve('src/assets/svg'))
.end();
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(resolve('src/assets/svg'))
.end()
.use('svg-sprite-loader')
.loader('svg-sprite-loader')
.options({
symbolId: 'icon-[name]'
})
.end();
// 组件静态资源引入
config.module
.rule('vue')
.use('vue-loader')
.loader('vue-loader')
.tap(options => {
return {
...options,
transformAssetUrls: {
'cr-image': 'src',
'cr-button': 'icon',
'cr-icon': 'type',
'cr-field': ['right-icon', 'left-icon'],
'cr-cell': ['right-icon', 'icon']
}
};
});
// 分包优化
config.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
initial: {
name: 'chunk-init', // 分离需要在入口加载的依赖
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // only package third parties that are initially dependent
},
libs: {
name: 'chunk-libs', // 分离非在入口加载的,但是引用超过2次的依赖
test: /[\\/]node_modules[\\/]/,
priority: 8,
minChunks: 2,
chunks: 'all' // only package third parties that are initially dependent
},
svg: {
name: 'chunk-svg',
priority: 9,
chunks: 'initial',
test: resolve('src/assets/svg'),
reuseExistingChunk: true,
enforce: true
},
cherryUI: {
name: 'chunk-cherryUI', // 分离组件库
priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
test: /[\\/]node_modules[\\/]@qg[\\/]_?cherry-ui(.*)/ // in order to adapt to cnpm
},
commons: {
name: 'chunk-commons', // 将src中components|service|mixins|api|config三个文件夹下的引用次数超过2次的js合并
test: /[\\/]src[\\/](components|service|mixins|api|config)[\\/]/, // can customize your rules
minChunks: 2, // minimum common number
priority: 5,
reuseExistingChunk: true
}
}
});
// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunk
config.optimization.runtimeChunk('single');
return config;
},
configureWebpack: config => {
let configObj = null;
if (IS_PROD) {
config.externals = externals;
configObj = {
// plugins: [new SentryPlugin(SentryConfig), new WebpackCosCdnUploadPlugin()]
plugins: [new WebpackCosCdnUploadPlugin()]
};
}
return configObj;
},
lintOnSave: true,
runtimeCompiler: false, // 是否使用包含运行时编译器的 Vue 构建版本
productionSourceMap: IS_PROD, // 生产环境的 source map
devServer: {
proxy: 'http://kdsp-vcc.liangkebang.net'
},
css: {
loaderOptions: {
less: {
// 若使用 less-loader@5,请移除 lessOptions 这一级,直接配置选项。
modifyVars: {
// 直接覆盖变量
hack: `true; @import "${resolve('./src/style/theme.less')}";`
}
}
}
}
};
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment