Commit b163771f authored by lipengcheng 's avatar lipengcheng

feat: xxx

parent b6de4c4d
module.exports = {
extends: ['@commitlint/config-conventional'],
}
{
"path": "cz-conventional-changelog"
}
...@@ -7,16 +7,25 @@ module.exports = { ...@@ -7,16 +7,25 @@ module.exports = {
parserOptions: { parserOptions: {
parser: 'babel-eslint', parser: 'babel-eslint',
}, },
extends: [ extends: ['prettier/vue', '@nuxtjs', 'plugin:nuxt/recommended'],
'@nuxtjs',
'prettier',
'prettier/vue',
'plugin:prettier/recommended',
'plugin:nuxt/recommended',
],
plugins: ['prettier'], plugins: ['prettier'],
// add your custom rules here // add your custom rules here
rules: { rules: {
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-console': [
0,
{
llow: ['warn', 'error'],
},
],
// 变量声明后必须使用;参数arg不做限制
'no-unused-vars': [
1,
{
vars: 'all',
args: 'none',
},
],
indent: [ indent: [
'error', 'error',
4, 4,
...@@ -25,5 +34,33 @@ module.exports = { ...@@ -25,5 +34,33 @@ module.exports = {
flatTernaryExpressions: true, flatTernaryExpressions: true,
}, },
], ],
'vue/html-closing-bracket-spacing': [
'error',
{
startTag: 'never',
endTag: 'never',
selfClosingTag: 'never',
},
],
'vue/html-closing-bracket-newline': [
'error',
{
singleline: 'never',
multiline: 'never',
},
],
'vue/html-indent': [
'error',
4,
{
attribute: 1,
baseIndent: 1,
closeBracket: 0,
alignAttributesVertically: true,
ignores: [],
},
],
// 'vue/valid-v-model': 'off',
// "prefer-const": "off"
}, },
} }
...@@ -88,3 +88,5 @@ sw.* ...@@ -88,3 +88,5 @@ sw.*
# Vim swap files # Vim swap files
*.swp *.swp
.history/
image: node:12.16.3-alpine
variables:
IMAGE_NAME: 'laihua-webh5'
IMAGE_TAG: 'latest'
ROBOT_WEBHOOK_URL: 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=b314c032-b7e2-4781-b2fa-f0bd26b75ebd'
VIEW_URL: 'https://beta.laihua.com/mobile'
PIPELINES_URL: 'https://gitlab.ilaihua.com/laihua-web/laihua-webh5/pipelines'
stages:
- stage-test
- stage-packaging
- stage-docker
- stage-deploy
- stage-final
- stage-pro
cache: &global_cache
key: ${CI_BUILD_REF_NAME}
paths:
- node_modules/
- .nuxt
- static
policy: pull-push
.test-execute:
stage: stage-test
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^build.*/
tags:
- webh5-runner
script:
- pwd && whoami
- echo '单元测试=====>start'
- echo '单元测试=====>end'
test-ci:
stage: stage-test
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^testci.*/
tags:
- webh5-runner
script:
- echo '所有预置变量:=======>'
- export
- echo ${CI_PROJECT_DIR}
- echo ${CI_PROJECT_DIR}/bins
packaging-execute:
stage: stage-packaging
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^build.*/
tags:
- webh5-runner
script:
- pwd && whoami && node -v && npm -v && yarn -v
# - npm install -g yarn
- yarn && yarn uat:build
docker-execute:
stage: stage-docker
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^build.*/
tags:
- webh5-runner
cache:
<<: *global_cache
policy: pull
script:
- echo '构建docker镜像=====>start'
- docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
- docker tag ${IMAGE_NAME}:${IMAGE_TAG} 551975756714.dkr.ecr.cn-northwest-1.amazonaws.com.cn/${IMAGE_NAME}:${IMAGE_TAG}
- echo '构建docker镜像=====>end'
- echo '登录及推送docker镜像=====>start'
- login_register_cmd=`aws ecr get-login --region cn-northwest-1 | sed 's/-e none/''/g'`
- eval $login_register_cmd
- docker push 551975756714.dkr.ecr.cn-northwest-1.amazonaws.com.cn/${IMAGE_NAME}:${IMAGE_TAG}
- echo '登录及推送docker镜像=====>end'
deploy_execute:
stage: stage-deploy
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^build.*/
tags:
- webh5-runner
cache: {}
script:
- echo '测试服部署=====>start'
- chmod +x ./bins/deploy_uat.sh
- bash ./bins/deploy_uat.sh ${IMAGE_NAME} ${IMAGE_TAG}
- echo '测试服部署=====>end'
pipenline-failed:
stage: stage-final
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^build.*/
tags:
- webh5-runner
when: on_failure
cache: {}
script:
- echo "构建失败======>"
- sudo bash ./bins/notification_robot.sh "on_failure" ${CI_COMMIT_TITLE// /-} ${GITLAB_USER_NAME} ${CI_COMMIT_REF_NAME} ${VIEW_URL} ${PIPELINES_URL}
pipenline-successed:
stage: stage-final
only:
refs:
- develop
variables:
- $CI_COMMIT_MESSAGE =~ /^build.*/
tags:
- webh5-runner
when: on_success
cache: {}
script:
- echo "构建成功======>"
# - if test ${NOTIFICATION} -eq 1;then sudo bash ./bins/notification_robot.sh "on_success" ${CI_COMMIT_TITLE// /-} ${GITLAB_USER_NAME} ${CI_COMMIT_REF_NAME} ${VIEW_URL} ${PIPELINES_URL};fi
- sudo bash ./bins/notification_robot.sh "on_success" ${CI_COMMIT_TITLE// /-} ${GITLAB_USER_NAME} ${CI_COMMIT_REF_NAME} ${VIEW_URL} ${PIPELINES_URL}
#--------------------------------------------
# master分支生产环境构建
# 目前的功能只是每次有mr到master分支,读取package.json中版本号,
# 然后以pro模式打包项目再打包为镜像上传至aws做备份保存
#--------------------------------------------
.build_pro:
stage: stage-pro
tags:
- webh5-runner
only:
- master
# when: manual
script:
- echo '部署生产环境=====>start'
- IMAGE_TAG="$(node -e "console.log(require('./package.json')['version'])")"
- echo '项目打包=====>start'
- yarn && yarn pro:build
- echo '构建docker镜像=====>start'
- docker build -t ${IMAGE_NAME}:${IMAGE_TAG} .
- docker tag ${IMAGE_NAME}:${IMAGE_TAG} 551975756714.dkr.ecr.cn-northwest-1.amazonaws.com.cn/${IMAGE_NAME}:${IMAGE_TAG}
- echo '构建docker镜像=====>end'
- echo '登录及推送docker镜像=====>start'
- login_register_cmd=`aws ecr get-login --region cn-northwest-1 | sed 's/-e none/''/g'`
- eval $login_register_cmd
- docker push 551975756714.dkr.ecr.cn-northwest-1.amazonaws.com.cn/${IMAGE_NAME}:${IMAGE_TAG}
- echo '登录及推送docker镜像=====>end'
const tasks = (arr) => arr.join(' && ')
module.exports = {
// hooks: {
// 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
// 'pre-commit': 'yarn test -u && git add *.md *.snap',
// 'pre-push': 'yarn lint && yarn test'
// }
hooks: {
'pre-commit': 'lint-staged',
// 'pre-commit': tasks(['lint-staged', 'yarn commit']),
'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
'pre-push': "echo 'pushing....'",
},
}
module.exports = {
'*': 'ls-lint',
// '**/*.{js,vue}': ['yarn format', 'git add .'],
'**/*.{js,vue}': ['prettier --write', 'eslint --fix'],
'**/*.{vue,css,scss}': ['stylelint --fix'],
}
# =========== comment start ===========
# 参考:https://ls-lint.org/1.x/configuration/the-rules.html
# kebab-case: 短横线,例如apple-pen
# camelcase: 小写驼峰,混合使用大小写字母区分逻辑断点,并且首字母小写。例如: applePen
# PascalCase: 帕斯卡,大写驼峰,混合使用大小写字母区分逻辑断点,并且首字母大写。例如: ApplePen
# lowercase: 全小写字母。例如:applepen
# snakecase: 蛇形命名。通过_区分逻辑断点。例如:apple_pen
# pointcase:只允许出现小写字母、数字和.。 例如:apple.pen
# =========== comment end ===========
ls:
# .js: kebab-case
# .ts: kebab-case
api:
.js: kebab-case
assets:
.dir: kebab-case
components:
.dir: lowercase | kebab-case
.vue: PascalCase
layouts:
.vue: camelcase
middleware:
.js: kebab-case
pages:
.vue: kebab-case
store:
.js: camelcase
ignore:
- .git
- node_modules
registry=https://registry.npm.taobao.org
*.js
*.png
*.eot
*.ttf
*.woff
module.exports = {
plugins: [
// 'stylelint-order',
// 'stylelint-selector-bem-pattern',
],
extends: [
'stylelint-config-prettier',
// 标准集合,继承自stylelint-config-recommend
'stylelint-config-standard',
'stylelint-config-sass-guidelines',
// 内置stylelint-order作为排序
'stylelint-config-recess-order',
],
// add your custom config here
// https://stylelint.io/user-guide/configuration
rules: {
// "order/order": [
// [],
// {
// "disableFix": false
// }
// ],
'scss/selector-no-redundant-nesting-selector': null,
'order/properties-alphabetical-order': null,
'declaration-block-no-duplicate-properties': true,
'block-no-empty': true,
'comment-no-empty': true,
'no-extra-semicolons': true,
'length-zero-no-unit': [
true,
{
ignore: ['custom-properties'],
},
],
'max-nesting-depth': [
2,
{
ignore: ['pseudo-classes'],
},
],
'number-no-trailing-zeros': true,
'number-leading-zero': 'always',
'unit-case': 'lower',
'string-quotes': 'double',
// 'value-keyword-case': 'lower',
'comment-whitespace-inside': 'always',
'comment-empty-line-before': 'never',
indentation: [
4,
{
baseIndentLevel: 0,
indentClosingBrace: false,
},
],
'max-empty-lines': 1,
// 'max-line-length': 120,
'value-list-max-empty-lines': 0,
'no-empty-first-line': true,
'no-eol-whitespace': true,
'at-rule-name-newline-after': 'always-multi-line',
'color-named': 'never',
'no-descending-specificity': null,
// "at-rule-no-unknown": [true, {
// ignoreAtRules: ['extend', 'at-root', 'debug', 'warn', 'error', 'if', 'else', 'for', 'each', 'while', 'mixin', 'include', 'content', 'return', 'function']
// }]
// 'selector-class-pattern': "foo-[a-z]+",
'string-quotes': 'double',
/**=========== stylelint-selector-bem-pattern =========== */
// 'plugin/selector-bem-pattern': {
// preset: 'bem',
// // 知道组件名称的检查规则
// componentName: '[a-z]+',
// /**
// * 指定组合的选择器检查规则,其实就是指定class名称规则
// * 支持正则字符串、返回正则的函数、包含2个选项参数的对象等写法
// */
// componentSelectors: {
// // 只初始的选择器规则(可以理解为外层的class规则)
// initial: '^\\.{componentName}(?:__[-a-z]+)?(?:--[a-z]+)?$',
// // 可以理解为外层yi
// combined: '^\\.{componentName}(?:__[-a-z]+)(?:--[a-z]+)?$',
// },
// utilitySelectors: '^\\.u-[a-z]+$',
// ignoreSelectors: ['^\\.el-', '/deep/', '>>>', '^\\.icon-'],
// },
},
}
export default {
getBanner: { method: 'get', url: '/home/banner' },
login: { method: 'post', url: '/login' },
getH5Order: { method: 'post', url: '/user/vip/orders/getH5Order' }
// getH5RightsOrder: { method: 'post', url: '/user/vip/rights/getH5Order' },
// getPlatform: { method: 'get', url: '/login/wechat/platform' }
}
# ASSETS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your un-compiled assets such as LESS, SASS, or JavaScript.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#webpacked).
@charset "UTF-8";
// px转换为rem
@function px2rem ($px, $base-font-size: 75px) {
@if unitless($px) {
// @warn "Assuming #{$px} to be in pixels, attempting to convert it into pixels for you";
@return px2rem($px * 1px);
}
@else if unit($px) == rem {
@return $px;
}
@return ($px / $base-font-size) * 1rem;
}
* {
box-sizing: border-box;
padding: 0;
margin: 0;
line-height: 1;
touch-action: pan-y;
}
span,
h1,
h2,
h3,
h4,
h5,
h6,
a,
p,
ul,
li {
padding: 0;
margin: 0;
}
html,
body {
font-family: "Microsoft YaHei", "微软雅黑", "Source Sans Pro", Arial, sans-serif;
font-size: 14px;
color: #000;
letter-spacing: 0.5px;
background-color: #fff;
// 顺滑滚动
scroll-behavior: smooth;
}
ul,
li {
list-style: none;
}
a,
a:hover,
a:focus,
a:visited {
color: #000;
}
a {
text-decoration: none;
outline: none;
}
/* 去掉获取焦点时的蓝色发光边框 */
input,
textarea,
video,
button {
padding: 0;
margin: 0;
border: 0;
outline: none;
}
/* 解决谷歌浏览器中的input背景色默认是黄色 */
input:-webkit-autofill {
box-shadow: 0 0 0 1000px #fff inset !important;
}
<template> <template>
<svg <svg
class="NuxtLogo" class="nuxt-logo"
width="245" width="245"
height="180" height="180"
viewBox="0 0 452 342" viewBox="0 0 452 342"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg">
>
<path <path
d="M139 330l-1-2c-2-4-2-8-1-13H29L189 31l67 121 22-16-67-121c-1-2-9-14-22-14-6 0-15 2-22 15L5 303c-1 3-8 16-2 27 4 6 10 12 24 12h136c-14 0-21-6-24-12z" d="M139 330l-1-2c-2-4-2-8-1-13H29L189 31l67 121 22-16-67-121c-1-2-9-14-22-14-6 0-15 2-22 15L5 303c-1 3-8 16-2 27 4 6 10 12 24 12h136c-14 0-21-6-24-12z"
fill="#00C58E" fill="#00C58E"/>
/>
<path <path
d="M447 304L317 70c-2-2-9-15-22-15-6 0-15 3-22 15l-17 28v54l39-67 129 230h-49a23 23 0 0 1-2 14l-1 1c-6 11-21 12-23 12h76c3 0 17-1 24-12 3-5 5-14-2-26z" d="M447 304L317 70c-2-2-9-15-22-15-6 0-15 3-22 15l-17 28v54l39-67 129 230h-49a23 23 0 0 1-2 14l-1 1c-6 11-21 12-23 12h76c3 0 17-1 24-12 3-5 5-14-2-26z"
fill="#108775" fill="#108775"/>
/>
<path <path
d="M376 330v-1l1-2c1-4 2-8 1-12l-4-12-102-178-15-27h-1l-15 27-102 178-4 12a24 24 0 0 0 2 15c4 6 10 12 24 12h190c3 0 18-1 25-12zM256 152l93 163H163l93-163z" d="M376 330v-1l1-2c1-4 2-8 1-12l-4-12-102-178-15-27h-1l-15 27-102 178-4 12a24 24 0 0 0 2 15c4 6 10 12 24 12h190c3 0 18-1 25-12zM256 152l93 163H163l93-163z"
fill="#2F495E" fill="#2F495E"/>
/>
</svg> </svg>
</template> </template>
<style> <style>
.NuxtLogo { .nuxt-logo {
animation: 1s appear;
margin: auto; margin: auto;
animation: 1s appear;
} }
@keyframes appear { @keyframes appear {
......
# COMPONENTS
**This directory is not required, you can delete it if you don't want to use it.**
The components directory contains your Vue.js Components.
_Nuxt.js doesn't supercharge these components._
const serverConf = {
dev: {
HOSTNAME: '0.0.0.0',
// HOSTNAME: 'localhost',
PORT: 3021 || '0'
},
uat: {
HOSTNAME: '0.0.0.0' || 'localhost',
PORT: 3021
},
pro: {
HOSTNAME: '0.0.0.0' || 'localhost',
PORT: 3021
}
}
const SERVER_CONF = serverConf[process.env.ENV_MODE] || {}
const appApiConf = {
dev: {
API_DOMAIN: 'http://beta.laihua.com/appapi',
API_LOCAL_URL: '',
BASE_URL: `http://localhost:${SERVER_CONF.PORT}/mobile/devappapi`,
BASE_URL_BROWSER: '/mobile/devappapi'
},
uat: {
API_DOMAIN: 'http://beta.laihua.com/appapi',
API_LOCAL_URL: '',
BASE_URL: `http://localhost:${SERVER_CONF.PORT}/mobile/uatappapi`,
BASE_URL_BROWSER: '/mobile/uatappapi'
},
pro: {
API_DOMAIN: 'https://app.laihua.com',
API_LOCAL_URL: '',
BASE_URL: `http://localhost:${SERVER_CONF.PORT}/mobile/proappapi`,
BASE_URL_BROWSER: '/mobile/proappapi'
}
}
const webApiConf = {
dev: {
API_DOMAIN: 'https://beta.laihua.com/webapi',
// api项目本地的地址及端口
API_LOCAL_URL: '',
BASE_URL: `http://localhost:${SERVER_CONF.PORT}/mobile/devapi`,
BASE_URL_BROWSER: '/mobile/devapi'
},
uat: {
API_DOMAIN: 'https://beta.laihua.com/webapi',
API_LOCAL_URL: '',
BASE_URL: `http://localhost:${SERVER_CONF.PORT}/mobile/uatapi`,
BASE_URL_BROWSER: '/mobile/uatapi'
},
pro: {
API_DOMAIN: 'https://webapi4.laihua.com',
API_LOCAL_URL: '',
BASE_URL: `http://localhost:${SERVER_CONF.PORT}/mobile/proapi`,
BASE_URL_BROWSER: '/mobile/proapi'
}
}
const WEB_API_CONF = webApiConf[process.env.ENV_MODE] || {}
const APP_API_CONF = appApiConf[process.env.ENV_MODE] || {}
export { SERVER_CONF, WEB_API_CONF, APP_API_CONF }
export default {
/** webapi代理 */
'/devapi': {
target: 'https://beta.laihua.com/webapi',
changeOrigin: true,
pathRewrite: { '^/devapi': '/' }
},
'/uatapi': {
target: 'https://beta.laihua.com/webapi',
changeOrigin: true,
pathRewrite: { '^/uatapi': '/' }
},
'/proapi': {
target: 'https://webapi4.laihua.com',
changeOrigin: true,
pathRewrite: { '^/proapi': '/' }
},
/** appapi代理 */
'/devappapi': {
target: 'https://beta.laihua.com/appapi',
changeOrigin: true,
pathRewrite: { '^/devappapi': '/' }
},
'/uatappapi': {
target: 'https://beta.laihua.com/appapi',
changeOrigin: true,
pathRewrite: { '^/uatappapi': '/' }
},
'/proappapi': {
target: 'https://app.laihua.com/',
changeOrigin: true,
pathRewrite: { '^/proappapi': '/' }
}
}
export default {
fullName: '深圳市前海手绘科技文化有限公司',
fullNameEn: 'Shenzhen Qianhai Hand-painted Technology and Culture Co. Ltd.',
ICP: '粤ICP备15083104号',
title: '来画',
slogan: '短视频智能创作营销平台',
description: `来画是有创意的AI动画短视频制作平台,仅需几张图片,几段文字和声音简单组合,
即可在短时间内生成一个有趣的动画短视频。丰富视频模板素材免费用!对于企业用户,还有动画高端定制服务。`,
mail: 'user@laihua.com',
tel: '400-8126-909',
address: '深圳市南山区前海世茂大厦36楼'
}
# LAYOUTS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your Application Layouts.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/views#layouts).
<template> <template>
<div> <div>
<Nuxt /> <Nuxt/>
</div> </div>
</template> </template>
<style> <style>
html { html {
font-family: 'Source Sans Pro', -apple-system, BlinkMacSystemFont, 'Segoe UI', box-sizing: border-box;
Roboto, 'Helvetica Neue', Arial, sans-serif; font-family:
"Source Sans Pro",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
sans-serif;
text-size-adjust: 100%;
font-size: 16px; font-size: 16px;
-webkit-font-smoothing: antialiased;
word-spacing: 1px; word-spacing: 1px;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
} }
*, *,
*::before, *::before,
*::after { *::after {
box-sizing: border-box; /* box-sizing: border-box; */
margin: 0; margin: 0;
} }
.button--green { .button--green {
display: inline-block; display: inline-block;
border-radius: 4px; padding: 10px 30px;
border: 1px solid #3b8070;
color: #3b8070; color: #3b8070;
text-decoration: none; text-decoration: none;
padding: 10px 30px; border: 1px solid #3b8070;
border-radius: 4px;
} }
.button--green:hover { .button--green:hover {
...@@ -40,12 +46,12 @@ html { ...@@ -40,12 +46,12 @@ html {
.button--grey { .button--grey {
display: inline-block; display: inline-block;
border-radius: 4px;
border: 1px solid #35495e;
color: #35495e;
text-decoration: none;
padding: 10px 30px; padding: 10px 30px;
margin-left: 15px; margin-left: 15px;
color: #35495e;
text-decoration: none;
border: 1px solid #35495e;
border-radius: 4px;
} }
.button--grey:hover { .button--grey:hover {
......
<template>
<div class="layout-error">
<h2 v-if="error.statusCode === 404">
404页面不存在
</h2>
<h2 v-else>
500服务器错误
</h2>
<ul>
<li>
<nuxt-link to="/">
HOME
</nuxt-link>
</li>
</ul>
</div>
</template>
<script>
export default {
props: {
error: {
type: Object,
default: () => {}
}
}
}
</script>
# MIDDLEWARE
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your application middleware.
Middleware let you define custom functions that can be run before rendering either a page or a group of pages.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing#middleware).
/* eslint-disable */
export default async function (ctx) {
const { route, store, redirect } = ctx
}
import { SERVER_CONF, WEB_API_CONF, APP_API_CONF } from './config/env.config.js'
import proxyConfig from './config/proxy.config.js'
// import hooks from './hooks'
import webInfo from './config/webInfo.js'
export default { export default {
server: {
host: SERVER_CONF.HOSTNAME,
port: SERVER_CONF.PORT
},
// env: {
// nodeenv: process.env.NODE_ENV,
// ...WEB_API_CONF
// },
privateRuntimeConfig: {
axios: {
baseURL: WEB_API_CONF.API_LOCAL_URL || WEB_API_CONF.BASE_URL
},
WEB_API_CONF,
APP_API_CONF,
ENV_MODE: process.env.ENV_MODE
},
publicRuntimeConfig: {
axios: {
browserBaseURL: WEB_API_CONF.BASE_URL_BROWSER
},
WEB_API_CONF,
APP_API_CONF,
ENV_MODE: process.env.ENV_MODE
},
// Global page headers (https://go.nuxtjs.dev/config-head) // Global page headers (https://go.nuxtjs.dev/config-head)
head: { head: {
title: 'mould-nuxt', title: webInfo.title,
titleTemplate: `%s-${webInfo.slogan}`,
meta: [ meta: [
{ charset: 'utf-8' }, { charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }, {
{ hid: 'description', name: 'description', content: '' }, name: 'viewport',
content: 'width=device-width, initial-scale=1,user-scalable=no'
},
// { 'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests' },
{
hid: 'description',
name: 'description',
content: webInfo.description
}
], ],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }], link: [{ rel: 'icon', type: 'image/x-icon', href: 'favicon.ico' }],
script: [
{ src: 'scripts/flexible.js' },
{ src: '//res.wx.qq.com/open/js/jweixin-1.4.0.js' },
{
src: '//at.alicdn.com/t/font_1909239_s4u6l1v1ysn.js',
async: true,
rel: 'preload'
}, // 平台 icon
{
src: '//at.alicdn.com/t/font_2037105_pnqqt3hxv5j.js',
async: true,
rel: 'preload'
} // 视频办公宝 icon
]
}, },
// Global CSS (https://go.nuxtjs.dev/config-css) // Global CSS (https://go.nuxtjs.dev/config-css)
css: ['element-ui/lib/theme-chalk/index.css'], css: [
'@/assets/styles/reset.scss'
// 'swiper/dist/css/swiper.min.css'
// 'ant-design-vue/dist/antd.css'
],
// Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins) // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
plugins: ['@/plugins/element-ui'], plugins: [
'~/plugins/inject-demo',
'~/plugins/inject-api',
'~/plugins/inject-utils',
{ src: '~/plugins/vue-bus' },
'~/plugins/axios/index.js',
// '~/plugins/axios/index.bak.js',
'~/plugins/nuxt-client-init.client.js',
{ src: '@/plugins/vant-ui' },
{ src: '~/plugins/vconsole', mode: 'client' }
],
// Auto import components (https://go.nuxtjs.dev/config-components) // Auto import components (https://go.nuxtjs.dev/config-components)
components: true, components: true,
...@@ -23,28 +93,164 @@ export default { ...@@ -23,28 +93,164 @@ export default {
buildModules: [ buildModules: [
// https://go.nuxtjs.dev/eslint // https://go.nuxtjs.dev/eslint
'@nuxtjs/eslint-module', '@nuxtjs/eslint-module',
'@nuxtjs/style-resources',
// https://go.nuxtjs.dev/stylelint // https://go.nuxtjs.dev/stylelint
'@nuxtjs/stylelint-module', // ['@nuxtjs/stylelint-module',{}]
'@nuxtjs/stylelint-module'
], ],
// Modules (https://go.nuxtjs.dev/config-modules) // Modules (https://go.nuxtjs.dev/config-modules)
modules: [ modules: [
// https://go.nuxtjs.dev/axios // https://go.nuxtjs.dev/axios
'@nuxtjs/axios', '@nuxtjs/axios',
// https://dayjs.gitee.io/
'@nuxtjs/dayjs',
// https://go.nuxtjs.dev/pwa // https://go.nuxtjs.dev/pwa
'@nuxtjs/pwa', '@nuxtjs/pwa',
'@nuxtjs/toast'
// https://go.nuxtjs.dev/content // https://go.nuxtjs.dev/content
'@nuxt/content', // '@nuxt/content'
], ],
// Axios module configuration (https://go.nuxtjs.dev/config-axios) eslint: {
axios: {}, fix: true
},
// Content module configuration (https://go.nuxtjs.dev/config-content) stylelint: {
content: {}, fix: true
},
/** 注意,这里只能引入样式的variables, mixins, functions文件,不能引入实际样式! */
styleResources: {
scss: [
// './assets/styles/variables.scss', // 全局 scss 变量
'./assets/styles/mixins.scss' // 全局 scss 混合
]
},
axios: {
// host: '',
// port: '',
// prefix: '/', // Default: "/"
// baseURL: 'https://beta.laihua.com/webapi', // Default: http://[HOST]:[PORT][PREFIX]
// browserBaseURL: '/webapi',
proxy: true,
credentials: true
},
proxy: proxyConfig,
dayjs: {
locales: ['zh-cn', 'en'],
defaultLocale: 'zh-cn'
},
toast: {
position: 'top-center',
duration: 2000
},
buildDir: '.nuxt',
extendPlugins (plugins) {
if (process.env.ENV_MODE === 'pro') {
const pluginIndex = plugins.findIndex(
({ src }) => src === '~/plugins/vconsole'
)
pluginIndex !== -1 && plugins.splice(pluginIndex, 1)
return plugins
}
},
// Build Configuration (https://go.nuxtjs.dev/config-build) // Build Configuration (https://go.nuxtjs.dev/config-build)
build: { build: {
transpile: [/^element-ui/], // analyze: {
// analyzerMode: 'static'
// },
publicPath: '/_nuxt/',
extractCSS: {
ignoreOrder: true
// allChunks: true,
},
postcss: {
plugins: {
'postcss-pxtorem': {
rootValue: 37.5,
propList: ['*']
}
}
},
transpile: [/^vant/],
babel: {
plugins: [
[
'import',
{
libraryName: 'vant',
// "libraryDirectory": "es",
style: true
}, },
'vant'
]
],
comments: true
},
extend (config, ctx) {
const { isDev, isClient } = ctx
if (isClient && process.env.ENV_MODE === 'pro') {
config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true
}
// console.log('config信息===>:', config)
// console.log('config.module.rules==>:', config.module.rules)
}
},
// 配置loader
loaders: {
// sass: {
// implementation: require("sass")
// },
// scss: {
// implementation: require("sass")
// },
// imgUrl: {
// limit: 3 * 1024,
// },
// vue: {
// transformAssetUrls: {
// video: ["src", "poster"],
// source: "src",
// }
// },
less: {
lessOptions: {
javascriptEnabled: true
}
}
},
router: {
// base: '/',
// middleware: ['route-chagne']
},
// performance: {
// hints: false,
// prefetch: false,
// maxEntrypointSize: 1024 * 1024,
// maxAssetSize: 1024 * 960,
// },
// hooks: hooks(this),
render: {
static: {
prefix: true
}
},
// Content module configuration (https://go.nuxtjs.dev/config-content)
content: {}
} }
{ {
"name": "mould-nuxt", "name": "mould-nuxt",
"version": "1.0.0", "version": "1.2.1",
"private": true, "private": true,
"files": [
"打包需要的文件"
],
"scripts": { "scripts": {
"dev": "nuxt", "dev": "cross-env ENV_MODE=dev NODE_ENV=dev nuxt",
"build": "nuxt build", "uat:build": "cross-env ENV_MODE=uat NODE_ENV=uat nuxt build",
"start": "nuxt start", "uat:start": "cross-env ENV_MODE=uat NODE_ENV=uat nuxt start",
"pro:build": "cross-env ENV_MODE=pro NODE_ENV=pro nuxt build",
"pro:start": "cross-env ENV_MODE=pro NODE_ENV=pro nuxt start",
"generate": "nuxt generate", "generate": "nuxt generate",
"lint:js": "eslint --ext .js,.vue --ignore-path .gitignore .", "lint:js": "eslint --ext .js,.vue",
"lint:style": "stylelint **/*.{vue,css} --ignore-path .gitignore", "lint:style": "stylelint **/*.{vue,css,scss,html,htm}",
"lint": "yarn lint:js && yarn lint:style" "lint": "yarn lint:js && yarn lint:style",
}, "fix:js": "eslint --fix --ext .js,.vue \"**\"",
"lint-staged": { "fix:style": "stylelint --fix **/*.{vue,css,scss}",
"*.{js,vue}": "eslint", "fix": "yarn fix:js && yarn fix:style ",
"*.{css,vue}": "stylelint" "prettier": "prettier --write \"**/*.js\" \"**/*.vue\"",
}, "format": "yarn prettier && yarn fix",
"husky": { "commit": "git-cz",
"hooks": { "release:init": "standard-version -f",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS", "release:patch": "standard-version -r patch",
"pre-commit": "lint-staged" "release:minor": "standard-version -r minor",
} "release:major": "standard-version -r major",
"release:push": "git push --follow-tags",
"analyze": "nuxt build -a"
}, },
"dependencies": { "dependencies": {
"@nuxt/content": "^1.9.0", "@nuxt/content": "^1.9.0",
"@nuxtjs/axios": "^5.12.2", "@nuxtjs/axios": "^5.12.2",
"@nuxtjs/dayjs": "^1.2.1",
"@nuxtjs/proxy": "^2.0.1",
"@nuxtjs/pwa": "^3.0.2", "@nuxtjs/pwa": "^3.0.2",
"@nuxtjs/style-resources": "^1.0.0",
"@nuxtjs/toast": "^3.3.1",
"clipboard": "^2.0.6",
"core-js": "^3.6.5", "core-js": "^3.6.5",
"element-ui": "^2.13.2", "cross-env": "^7.0.2",
"nuxt": "^2.14.6" "eslint-plugin-prettier": "^3.1.4",
"lint-staged": "^10.5.3",
"nuxt": "^2.14.6",
"swiper": "^3.4.2",
"vant": "^2.11.1",
"vue-bus": "^1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^11.0.0", "@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0", "@commitlint/config-conventional": "^11.0.0",
"@ls-lint/ls-lint": "^1.9.2",
"@nuxtjs/eslint-config": "^3.1.0", "@nuxtjs/eslint-config": "^3.1.0",
"@nuxtjs/eslint-module": "^2.0.0", "@nuxtjs/eslint-module": "^2.0.0",
"@nuxtjs/stylelint-module": "^4.0.0", "@nuxtjs/stylelint-module": "^4.0.0",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"babel-plugin-import": "^1.13.3",
"commitizen": "^4.2.0",
"cz-customizable": "^6.3.0",
"eslint": "^7.10.0", "eslint": "^7.10.0",
"eslint-config-prettier": "^6.12.0", "eslint-config-prettier": "^6.15.0",
"eslint-plugin-nuxt": "^1.0.0", "eslint-plugin-nuxt": "^1.0.0",
"eslint-plugin-prettier": "^3.1.4",
"husky": "^4.3.0", "husky": "^4.3.0",
"lint-staged": "^10.4.0", "postcss-pxtorem": "^5.1.1",
"prettier": "^2.1.2", "prettier": "^2.1.2",
"stylelint": "^13.7.2", "sass": "^1.29.0",
"sass-loader": "^10.1.0",
"standard-version": "^9.0.0",
"stylelint": "^13.8.0",
"stylelint-config-prettier": "^8.0.2", "stylelint-config-prettier": "^8.0.2",
"stylelint-config-standard": "^20.0.0" "stylelint-config-recess-order": "^2.3.0",
"stylelint-config-sass-guidelines": "^7.1.0",
"stylelint-config-standard": "^20.0.0",
"stylelint-selector-bem-pattern": "^2.1.0",
"vconsole": "^3.3.4"
} }
} }
# PAGES
This directory contains your Application Views and Routes.
The framework reads all the `*.vue` files inside this directory and creates the router of your application.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/routing).
<template> <template>
<div class="container"> <div class="container">
<div> <div>
<Logo /> <Logo/>
<h1 class="title">mould-nuxt</h1> <h1 class="title">
mould-nuxt
</h1>
<div class="links"> <div class="links">
<a <a
href="https://nuxtjs.org/" href="https://nuxtjs.org/"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="button--green" class="button--green">
>
Documentation Documentation
</a> </a>
<a <a
href="https://github.com/nuxt/nuxt.js" href="https://github.com/nuxt/nuxt.js"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
class="button--grey" class="button--grey">
>
GitHub GitHub
</a> </a>
</div> </div>
...@@ -31,31 +31,38 @@ export default {} ...@@ -31,31 +31,38 @@ export default {}
<style> <style>
.container { .container {
margin: 0 auto;
min-height: 100vh;
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0 auto;
text-align: center; text-align: center;
} }
.title { .title {
font-family: 'Quicksand', 'Source Sans Pro', -apple-system,
BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
sans-serif;
display: block; display: block;
font-weight: 300; font-family:
"Quicksand",
"Source Sans Pro",
-apple-system,
BlinkMacSystemFont,
"Segoe UI",
Roboto,
"Helvetica Neue",
Arial,
sans-serif;
font-size: 100px; font-size: 100px;
font-weight: 300;
color: #35495e; color: #35495e;
letter-spacing: 1px; letter-spacing: 1px;
} }
.subtitle { .subtitle {
font-weight: 300; padding-bottom: 15px;
font-size: 42px; font-size: 42px;
font-weight: 300;
color: #526488; color: #526488;
word-spacing: 5px; word-spacing: 5px;
padding-bottom: 15px;
} }
.links { .links {
......
# PLUGINS
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains Javascript plugins that you want to run before mounting the root Vue.js application.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/plugins).
export default (err) => {
const { response } = err
if (!response.status) {
err.code = ''
err.message = '有response但没有response.status的情况'
}
err.code = response.status
switch (response.status) {
case 200:
err.message = '错误响应也会有状态码为200的情况'
break
case 400:
err.message = '请求错误(400)'
break
case 401:
err.message = '未授权,请重新登录(401)'
break
case 403:
err.message = '拒绝访问(403)'
break
case 404:
err.message = '请求出错(404)'
break
case 408:
err.message = '请求超时(408)'
break
case 500:
err.message = '服务器错误(500)'
break
case 501:
err.message = '服务未实现(501)'
break
case 502:
err.message = '网络错误(502)'
break
case 503:
err.message = '服务不可用(503)'
break
case 504:
err.message = '网络超时(504)'
break
case 505:
err.message = 'HTTP版本不受支持(505)'
break
default:
err.message = `连接出错,状态码:(${err.response.status})!`
}
return err
}
// 处理响应错误码
export default (response) => {
const status = response.status
// 如果http响应状态码response.status正常,则直接返回数据
if ((status >= 200 && status <= 300) || status === 304) {
return response
} else {
// status不正常的话,根据与后端约定好的code,做出对应的提示与处理
// 返回一个带有code和message属性的对象
const code = parseInt(response.data && response.data.code)
// msg为服务端返回的错误信息,字段名自定义,此处以msg为例
let message = (response.data || {}).msg
switch (code) {
case 400:
break
case 4001:
if (process.server) {
return
}
message = message || '登录设备数量超出限制'
// store.commit('savehttpResult', { res: response.data })
break
case 403:
message = message || '未登录'
break
case 404:
message = message || '请求地址错误'
break
case 412:
message = message || '未找到有效session'
break
default:
// message = message || err.response.data.msg
break
}
return {
code,
message
}
}
}
export default (axios, config = {}) => {
// axios.defaults.baseURL = process.env.VUE_APP_BASEURL
// axios.defaults.timeout = 10000
// axios.defaults.headers['custom-defined-header-key'] = 'custom-defined-header-value'
// // 自定义请求头:对所有请求方法生效
// axios.defaults.headers.common['common-defined-key-b'] = 'custom value: for all methods'
// // 自定义请求头:只对post方法生效
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// // 自定义请求头:只对get方法生效
// axios.defaults.headers.get['get-custom-key'] = 'custom value: only for get method';
const defaultConfig = {
baseURL: '',
timeout: 10000,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
'custom-defined-header-key': 'custom-defined-header-value',
common: {
'common-defined-key-b': 'custom value: for all methods'
},
post: {
'post-custom-key': 'custom value: only for post method'
},
get: {
'get-custom-key': 'custom value: only for get method'
}
}
}
Object.assign(axios.defaults, defaultConfig, config)
return axios
}
import axios from 'axios'
import setConfig from '@/plugins/axios/axios.setConfig.js'
import handleResponse from '@/plugins/axios/axios.handleResponse.js'
import handleError from '@/plugins/axios/axios.handleError.js'
export default function ({ $config }, inject) {
const baseURL = process.server
? $config.API_LOCAL_URL ||
`http://localhost:${$config.PORT}${$config.BASE_URL_BROWSER}`
: $config.BASE_URL_BROWSER
const intactRequest = setConfig(axios, { baseURL })
const request = setConfig(intactRequest.create(), { baseURL })
// 请求中的api
const pendingPool = new Map()
/**
* 请求拦截
*/
const requestInterceptorId = request.interceptors.request.use(
(config) => {
// 对于异常的响应也需要在pendingPool中将其删除,但响应拦截器中的异常响应有些获取不到请求信息,这里将其保存在实例上
request.config = Object.assign({}, config)
// 在发送请求之前做些什么
// config.headers.common['cookie-id'] = cookieId
config.cancelToken = new axios.CancelToken((cancelFn) => {
pendingPool.has(config.url)
? cancelFn(`${config.url}请求重复`)
: pendingPool.set(config.url, {
cancelFn,
global: config.global
})
})
return config
},
(err) => {
console.log('请求拦截err:', err)
// 对请求错误做些什么
return Promise.reject(err)
}
)
/**
* 响应拦截
*/
const responseInterceptorId = request.interceptors.response.use(
(response) => {
const { config } = response
pendingPool.delete(config.url)
// console.log('响应response suc:', response)
// showTip(err.message)
return Promise.resolve(handleResponse(response))
},
// 对异常响应处理
(err) => {
const { config } = request
// 异常响应删除需要加一个判断:是否为请求被取消的异常,如果不是才会将这个请求从pendingPool中删除。
// 否则会出现一种情况:网速非常慢的情况下,在网速非常慢的情况下多次重复发送同一个请求,第一个请求还在pending状态中,
// 第二个请求发不出去会直接被cancel掉进入到异常响应,然后从pendignPool中删除,第三次请求发出的时候就无法正确判断这个请求是否还是pending状态会正常发出
if (!axios.isCancel(err)) {
pendingPool.delete(config.url)
}
// console.log('响应拦截err1:', err)
// console.log('err.stack:', err.stack)
// console.log('err.response:', err.response)
// // console.log('err.response.status:', err.response.status)
// // console.log('err.response.data:',err.response.data)
// // console.log('err.response.data.code:', err.response.data.code)
// // console.log('err.response.data.message:', err.response.data.message)
// console.log('axios.isCancel(err):', axios.isCancel(err))
// console.log('Array.from(pendingPool.keys()):', Array.from(pendingPool.keys()))
// console.log('pendingPool:', pendingPool.keys())
if (!err) {
return Promise.reject(err)
}
if (err.response) {
err = handleError(err)
return Promise.reject(err)
}
// eg: 超时;断网;请求重复被取消;主动取消请求;
// 错误信息err传入isCancel方法,可以判断请求是否被取消
if (axios.isCancel(err)) {
throw new axios.Cancel(
err.message || `请求'${request.config.url}'被取消`
)
} else if (err.stack && err.stack.includes('timeout')) {
err.message = '请求超时!'
} else {
err.message = '连接服务器失败!'
}
// showTip(err.message)
return Promise.reject(err)
}
)
// 移除全局的请求拦截器
function removeRequestInterceptor () {
request.interceptors.request.eject(requestInterceptorId)
}
// 移除全局的响应拦截器
function removeResponseInterceptor () {
request.interceptors.response.eject(responseInterceptorId)
}
/**
* 清除所有pending状态的请求
* @param {Array} whiteList 白名单,里面的请求不会被取消
* 返回值 被取消了的api请求
*/
function clearPendingPool (whiteList = []) {
if (!pendingPool.size) {
return
}
// const pendingUrlList = [...pendingPool.keys()].filter((url) => !whiteList.includes(url))
const pendingUrlList = Array.from(pendingPool.keys()).filter(
url => !whiteList.includes(url)
)
if (!pendingUrlList.length) {
return
}
pendingUrlList.forEach((pendingUrl) => {
// 清除掉所有非全局的pending状态下的请求
if (!pendingPool.get(pendingUrl).global) {
pendingPool.get(pendingUrl).cancelFn()
pendingPool.delete(pendingUrl)
}
})
return pendingUrlList
}
request.removeRequestInterceptor = removeRequestInterceptor
request.removeResponseInterceptor = removeResponseInterceptor
request.clearPendingPool = clearPendingPool
inject('request', request)
inject('intactRequest', intactRequest)
}
// eslint-disable-next-line
/* eslint-disable */
import axios from 'axios'
import utils from 'axios/lib/utils'
// import setConfig from '@/plugins/axios/axios.setConfig.js'
import handleResponse from '@/plugins/axios/axios.handleResponse.js'
import handleError from '@/plugins/axios/axios.handleError.js'
const { Cancel } = axios
const pendingPool = new Map()
export default function (context, inject) {
const { $axios, $config, store, req } = context
const xRealIp = store.getters['x-real-ip']
const intactAxios = $axios.create({})
intactAxios.interceptors.request.use((config) => {
config.headers.common['x-real-ip'] = xRealIp
return config
})
const { APP_API_CONF } = $config
const appAxios = $axios.create({})
const appBaseURL = process.server
? APP_API_CONF.API_LOCAL_URL || APP_API_CONF.BASE_URL
: APP_API_CONF.BASE_URL_BROWSER
appAxios.setBaseURL(appBaseURL)
appAxios.interceptors.request.use((config) => {
config.headers.common['x-real-ip'] = xRealIp
return config
})
/**
* 请求拦截
*/
const requestInterceptorId = $axios.interceptors.request.use(
(config) => {
$axios.config = Object.assign({}, config)
// $axios.setHeader('x-real-ip', 11111)
config.headers.common['x-real-ip'] = xRealIp
config.cancelToken = new $axios.CancelToken((cancelFn) => {
pendingPool.has(config.url)
? cancelFn(`${config.url}请求重复`)
: pendingPool.set(config.url, {
cancelFn,
global: config.global,
})
})
return config
},
(err) => {
return Promise.reject(err)
}
)
/**
* 响应拦截
*/
const responseInterceptorId = $axios.interceptors.response.use(
(response) => {
const { config } = response
pendingPool.delete(config.url)
return Promise.resolve(handleResponse(response))
},
// 对异常响应处理
(err) => {
const { config } = $axios
if (!$axios.isCancel(err)) pendingPool.delete(config.url)
if (!err) return Promise.reject(err)
if (err.response) {
err = handleError(err)
}
// 没有response(没有状态码)的情况
// eg: 超时;断网;请求重复被取消;主动取消请求;
else {
if ($axios.isCancel(err)) {
throw new Cancel(
err.message || `请求'${$axios.config.url}'被取消`
)
} else if (err.stack && err.stack.includes('timeout')) {
err.message = '请求超时!'
} else {
err.message = '连接服务器失败!'
}
}
return Promise.reject(err)
}
)
// $axios.onError((error) => {
// throw new Error('xxx1212')
// // return {}
// // return { error, data: {} }
// })
// 移除全局的请求拦截器
function removeRequestInterceptor() {
$axios.interceptors.request.eject(requestInterceptorId)
}
// 移除全局的响应拦截器
function removeResponseInterceptor() {
$axios.interceptors.response.eject(responseInterceptorId)
}
/**
* 清除所有pending状态的请求
* @param {Array} whiteList 白名单,里面的请求不会被取消
* 返回值 被取消了的api请求
*/
function clearPendingPool(whiteList = []) {
if (!pendingPool.size) return
// const pendingUrlList = [...pendingPool.keys()].filter((url) => !whiteList.includes(url))
const pendingUrlList = Array.from(pendingPool.keys()).filter(
(url) => !whiteList.includes(url)
)
if (!pendingUrlList.length) return
pendingUrlList.forEach((pendingUrl) => {
// 清除掉所有非全局的pending状态下的请求
if (!pendingPool.get(pendingUrl).global) {
pendingPool.get(pendingUrl).cancelFn()
pendingPool.delete(pendingUrl)
}
})
return pendingUrlList
}
$axios.removeRequestInterceptor = removeRequestInterceptor
$axios.removeResponseInterceptor = removeResponseInterceptor
$axios.clearPendingPool = clearPendingPool
inject('intactAxios', intactAxios)
inject('appAxios', appAxios)
}
import Vue from 'vue'
import Element from 'element-ui'
import locale from 'element-ui/lib/locale/lang/en'
Vue.use(Element, { locale })
import apiMap from '@@/api/index.js'
function injectRequest (apiObj, axios) {
const requestMap = {}
Object.keys(apiObj).forEach((alias) => {
const { method, url, config } = apiObj[alias]
// const method = apiObj[alias].method.toUpperCase()
const keyName = ['PUT', 'POST', 'PATCH'].includes(method.toUpperCase())
? 'data'
: 'params'
requestMap[alias] = (dataOrParams = {}, instanceConf = {}) => {
return new Promise((resolve, reject) => {
axios({
method,
url,
// [keyName]: methods === 'POST' ? qs.stringify(dataOrParams) : dataOrParams,
[keyName]: dataOrParams,
...Object.assign(config || {}, instanceConf)
})
.then(res => resolve(res.data))
.catch(err => reject(err))
})
}
})
return requestMap
}
export default ({ $axios }, inject) => {
inject('api', injectRequest(apiMap, $axios))
}
export default ({ app }, inject) => {
/**
* Inject $hello(msg) in Vue, context and store.
* mounted: this.$hello()
* asyncData: ctx.app.$hello() ctx.$hello()
* vuex actions: this.$hello()
*/
inject('fn', msg => console.log(`Hello ${msg || 'slevin'}!`))
inject('string', 'im a inject string')
}
import utils from '@@/utils/utils.js'
export default ({ app }, inject) => {
/**
* Inject $hello(msg) in Vue, context and store.
* mounted: this.$hello()
* asyncData: ctx.app.$hello() ctx.$hello()
* vuex actions: this.$hello()
*/
inject('utils', utils)
}
export default async (context) => {
await context.store.dispatch('nuxtClientInit', context)
}
import Vue from 'vue'
import { Button, Swipe, SwipeItem, Lazyload, Dialog, Loading } from 'vant'
Vue.use(Button)
Vue.use(Swipe)
Vue.use(SwipeItem)
Vue.use(Lazyload, {
lazyComponent: true
})
Vue.use(Loading)
Vue.use(Dialog)
import VConsole from 'vconsole'
export default ({ $config }, inject) => {
return $config.ENV_MODE === 'pro' ? {} : new VConsole()
}
import Vue from 'vue'
import VueBus from 'vue-bus'
// 全局注册vue-bus
Vue.use(VueBus)
# STATIC
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your static files.
Each file inside this directory is mapped to `/`.
Thus you'd want to delete this README.md before deploying to production.
Example: `/static/robots.txt` is mapped as `/robots.txt`.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/assets#static).
static/favicon.ico

1.36 KB | W: | H:

static/favicon.ico

4.19 KB | W: | H:

static/favicon.ico
static/favicon.ico
static/favicon.ico
static/favicon.ico
  • 2-up
  • Swipe
  • Onion skin
;(function (win, lib) {
var doc = win.document
var docEl = doc.documentElement
var metaEl = doc.querySelector('meta[name="viewport"]')
var flexibleEl = doc.querySelector('meta[name="flexible"]')
var dpr = 0
var scale = 0
var tid
var flexible = lib.flexible || (lib.flexible = {})
if (metaEl) {
//console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl
.getAttribute('content')
.match(/initial\-scale=([\d\.]+)/)
if (match) {
scale = parseFloat(match[1])
dpr = parseInt(1 / scale)
}
} else if (flexibleEl) {
var content = flexibleEl.getAttribute('content')
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/)
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/)
if (initialDpr) {
dpr = parseFloat(initialDpr[1])
scale = parseFloat((1 / dpr).toFixed(2))
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1])
scale = parseFloat((1 / dpr).toFixed(2))
}
}
}
if (!dpr && !scale) {
var isAndroid = win.navigator.appVersion.match(/android/gi)
var isIPhone = win.navigator.appVersion.match(/iphone/gi)
var isPad = win.navigator.appVersion.match(/ipod|ipad/gi / gi)
var devicePixelRatio = win.devicePixelRatio
if (isIPhone) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2
} else {
dpr = 1
}
} else if (isPad) {
// iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
dpr = 2
} else {
dpr = 1
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1
}
scale = 1 / dpr
}
docEl.setAttribute('data-dpr', dpr)
if (!metaEl) {
metaEl = doc.createElement('meta')
metaEl.setAttribute('name', 'viewport')
metaEl.setAttribute(
'content',
'initial-scale=' +
scale +
', maximum-scale=' +
scale +
', minimum-scale=' +
scale +
', user-scalable=no'
)
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl)
} else {
var wrap = doc.createElement('div')
wrap.appendChild(metaEl)
doc.write(wrap.innerHTML)
}
}
function refreshRem() {
var width = docEl.getBoundingClientRect().width
if (width / dpr > 540) {
width = 540 * dpr
}
var rem = width / 10
docEl.style.fontSize = rem + 'px'
flexible.rem = win.rem = rem
}
win.addEventListener(
'resize',
function () {
clearTimeout(tid)
tid = setTimeout(refreshRem, 300)
},
false
)
win.addEventListener(
'pageshow',
function (e) {
if (e.persisted) {
clearTimeout(tid)
tid = setTimeout(refreshRem, 300)
}
},
false
)
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px'
} else {
doc.addEventListener(
'DOMContentLoaded',
function (e) {
doc.body.style.fontSize = 12 * dpr + 'px'
},
false
)
}
refreshRem()
flexible.dpr = win.dpr = dpr
flexible.refreshRem = refreshRem
flexible.rem2px = function (d) {
var val = parseFloat(d) * this.rem
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px'
}
return val
}
flexible.px2rem = function (d) {
var val = parseFloat(d) / this.rem
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem'
}
return val
}
})(window, window['lib'] || (window['lib'] = {}))
# STORE
**This directory is not required, you can delete it if you don't want to use it.**
This directory contains your Vuex Store files.
Vuex Store option is implemented in the Nuxt.js framework.
Creating a file in this directory automatically activates the option in the framework.
More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store).
export const state = () => ({
list: [1, 2, 3]
})
export const getters = {
listReverse: state => state.list.slice().reverse()
}
export const mutations = {
pushList (state, payload) {
state.list.push(payload)
},
popList (state, payload) {
state.list.pop()
},
reverseList (state, payload) {
state.list = state.list.reverse()
}
}
export const actions = {}
export const state = () => ({
counter: 0,
lowerLetter: 'lower',
supportWebp: false, // 是否支持webp图片
userAgent: '', // userAgent信息
reqHeaders: {},
apiPrefix: ''
})
export const getters = {
lowerToUpper: (state) => {
return state.lowerLetter.toUpperCase()
},
'x-real-ip': state => state.reqHeaders['x-real-ip'] || 'xxxxx'
}
export const mutations = {
// 检查是否支持webp格式的图片
checkWebp (state) {
const { isIos } = this.$utils.judgeUserAgent(navigator.userAgent || '')
if (process.server || isIos) {
return
}
try {
const isWebp = this.$utils.checkWebpAvailable()
state.supportWebp = isWebp
// state.assetsDir = 'assets/imgWebp'
// state.webpSuffix = '.webp'
window.supportWebp = isWebp
document.cookie = `supportWebp=${isWebp}`
document.documentElement.className += 'webp-available'
} catch (error) {
console.log('error:', error)
}
},
setUserAgent (state, userAgent) {
state.userAgent = {
info: userAgent,
...this.$utils.judgeUserAgent(userAgent)
}
},
saveRequestHeaders (state, payload) {
const { req } = payload
if (!req) {
return
}
state.reqHeaders = req.headers
},
/**
* 保存api前缀
*/
setApiPrefix (state) {
try {
state.apiPrefix = this.$config.WEB_API_CONF.BASE_URL_BROWSER
} catch (error) {
console.log('error:', error)
}
}
}
export const actions = {
nuxtServerInit ({ commit }, context) {
console.log('server init====>:')
// 保存设备信息
const ua = process.server
? context.req.headers['user-agent']
: navigator.userAgent
commit('setUserAgent', ua)
// 保存用户真实ip
commit('saveRequestHeaders', { req: context.req })
commit('setApiPrefix')
// if (req.session && req.session.authUser) {
// commit('SET_USER', req.session.authUser)
// }
},
async nuxtClientInit (store, context) {
console.log('client init====>:')
// eslint-disable-next-line
const { getters, dispatch, commit } = store
await commit('checkWebp')
}
}
let time
const util = {
/**
* 处理所有非本地的资源链接,返回为七牛云地址
* @param {String} url 资源链接
*/
handleAssetsUrl (url) {
if (!url) {
return
}
const httpReg = /^http/
const reg = /^\//
const slashHttpReg = /^\/http/
const qiniuAssetsUrl = 'https://resources.laihua.com'
if (httpReg.test(url) || url.includes('base64')) {
// 兼容第三方登录的头像地址为http的情况,替换为https
return url.replace(/http:/, 'https:')
}
// 兼容以/开头的绝对路径:/http://xxxxx.com
if (slashHttpReg.test(url)) {
return url.substr(1)
} else if (reg.test(url)) {
return qiniuAssetsUrl + url
} else {
return qiniuAssetsUrl + '/' + url
}
},
/**
* 检测浏览器是否支持webp
*/
checkWebpAvailable: () => {
try {
return document
.createElement('canvas')
.toDataURL('image/webp')
.includes('data:image/webp')
} catch (error) {
return false
}
},
/**
* 判断设备
* @param {*} ua
*/
judgeUserAgent: (ua) => {
ua =
ua ||
(window
? window.navigator.userAgent
: '非浏览器环境,无法自动判断请传入userAgent')
return {
trident: ua.includes('Trident'), // IE内核
presto: ua.includes('Presto'), // opera内核
Alipay: ua.includes('Alipay'), // 支付宝
webKit: ua.includes('AppleWebKit'), // 苹果、谷歌内核
gecko: ua.includes('Gecko') && !ua.includes('KHTML'), // 火狐内核
mobile: !!ua.match(/AppleWebKit.*Mobile.*/), // 是否为移动终端
ios: !!ua.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), // ios终端
android: ua.includes('Android') || ua.includes('Linux'), // android终端或者uc浏览器
iPhone: ua.includes('iPhone') || ua.includes('Mac'), // 是否为iPhone或者安卓QQ浏览器
// iPhone: ua.match(/iphone|ipod|ipad/),//
iPad: ua.includes('iPad'), // 是否为iPad
webApp: !ua.includes('Safari'), // 是否为web应用程序,没有头部与底部
weixin: ua.includes('MicroMessenger'), // 是否为微信浏览器
qq: ua.match(/\sQQ/i) !== null, // 是否QQ
Safari: ua.includes('Safari'), // Safari浏览器,
weibo: ua.includes('Weibo'), // 是否微博
isEdge: ua.includes('Edge'),
isWxwork: ua.includes('wxwork'), // 企业微信浏览器
isFF: ua.includes('Firefox'),
isOpera: ua.includes('Opera'),
isBB: ua.includes('BlackBerry'),
isChrome: ua.includes('Chrome'),
isMaxthon: ua.includes('Maxthon'),
isIos: /(iPhone|iPad|iPod|iOS)/i.test(ua),
isIE: ['compatible', 'MSIE'].every(n => ua.includes(n)),
isSafari: ua.includes('Safari') && !ua.includes('Chrome'),
isMobile: /(iPhone|iPad|iPod|iOS|Opera Mini|Android.*Mobile|NetFront|PSP|BlackBerry|Windows Phone|micromessenger)/gi.test(
ua
),
isLhAndroid: ua.includes('os=Android'),
isLhIos: ua.includes('os=ios'),
isLhApp: ua.includes('os=Android') || ua.includes('os=ios')
}
},
/**
* 正则匹配
*/
reg: {
/**
*
* @param {Number} idChinese 15及18位长度大陆身份证校验
*/
idChinese (id) {
const reg = /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx])|([1−9]\d5\d2((0[1−9])|(10|11|12))(([0−2][1−9])|10|20|30|31)\d2[0−9Xx])|([1−9]\d5\d2((0[1−9])|(10|11|12))(([0−2][1−9])|10|20|30|31)\d2[0−9Xx])/
return reg.test(id)
},
/**
*
* @param {Number} idHk 8位长度香港身份证校验
*/
idHK (id) {
const reg = /[A-Z]{1,2}[0-9]{6}([0-9A])/
return reg.test(id)
},
/**
*
* @param {Number} phone 手机号码校验
*/
phone (phone) {
const reg = /^(0|86|17951|12593)?1[3|4|5|6|7|8|9][0-9]{5,9}$/
return reg.test(phone)
}
},
/**
* 微信分享
* @param {*} option
*/
shareWx (option) {
clearTimeout(time)
// 防止重复
time = setTimeout(() => {
const shareUrl =
option && option.url ? option.url : window.location.href
const shareTitle =
option && option.title
? option.title
: '来画-短视频智能创作营销平台-MG动画制作工具-来画视频'
const shareDesc =
option && option.desc
? option.desc
: '做短视频像做PPT一样简单?快挪步到“来画视频”一探究竟吧!'
const sharePic =
option && option.pic
? option.pic
: 'https://resources.laihua.com/2018-11-15/1650326b-0fc4-4c65-9ea8-14f09b08d26d.png'
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,
// config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,
// 则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,
// 则可以直接调用,不需要放在ready函数中。
// eslint-disable-next-line
wx.updateAppMessageShareData({
title: shareTitle, // 分享标题
desc: shareDesc, // 分享描述
link: shareUrl, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: sharePic, // 分享图标
success () {
// 设置成功
}
})
// eslint-disable-next-line
wx.updateTimelineShareData({
title: shareTitle, // 分享标题
link: shareUrl, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: sharePic, // 分享图标
success () {
// 设置成功
}
})
// eslint-disable-next-line
wx.onMenuShareTimeline({
title: shareTitle, // 分享标题
desc: shareDesc, // 分享描述
link: shareUrl, // 分享链接
imgUrl: sharePic // 分享图标
})
// 分享给好友
// eslint-disable-next-line
wx.onMenuShareAppMessage({
title: shareTitle, // 分享标题
desc: shareDesc, // 分享描述
link: shareUrl, // 分享链接
imgUrl: sharePic, // 分享图标
type: '' // 分享类型,music、video或link,不填默认为link
})
// 分享到qq
// eslint-disable-next-line
wx.onMenuShareQQ({
title: shareTitle, // 分享标题
desc: shareDesc, // 分享描述
link: shareUrl, // 分享链接
imgUrl: sharePic // 分享图标
})
// 分享到qq空间
// eslint-disable-next-line
wx.onMenuShareQZone({
title: shareTitle, // 分享标题
desc: shareDesc, // 分享描述
link: shareUrl, // 分享链接
imgUrl: sharePic // 分享图标
})
}, 500)
},
/**
* 根据指定属性名的值,按照升序排序
*/
sortList (propertyName) {
return function (object1, object2) {
const value1 = parseFloat(object1[propertyName])
const value2 = parseFloat(object2[propertyName])
if (value1 < value2) {
return -1
} else if (value1 > value2) {
return 1
} else {
return 0
}
}
},
/**
* 时间格式化
* @param {*} times 可以为秒或者毫秒,必填
* @param {*} formatStr 返回时间格式 非必填
*/
formatDate (times, formatStr) {
let timestamp
if (/^\d{12,14}$/.test(times)) {
timestamp = times
} else if (/^\d{9,11}$/.test(times)) {
timestamp = times * 1000
} else {
return times
}
let format = formatStr || 'yyyy/MM/dd'
const currentDate = new Date(parseInt(timestamp))
const date = {
'M+': currentDate.getMonth() + 1,
'd+': currentDate.getDate(),
'h+': currentDate.getHours(),
'm+': currentDate.getMinutes(),
's+': currentDate.getSeconds()
}
if (/(y+)/i.test(format)) {
format = format.replace(
RegExp.$1,
(currentDate.getFullYear() + '').substr(4 - RegExp.$1.length)
)
}
for (const k in date) {
if (new RegExp('(' + k + ')').test(format)) {
format = format.replace(
RegExp.$1,
RegExp.$1.length === 1
? date[k]
: ('00' + date[k]).substr(('' + date[k]).length)
)
}
}
return format
},
/**
* 秒转化为分钟格式
*/
secToMin (secs) {
return [
// parseInt(secs / 60 / 60),
parseInt((secs / 60) % 60),
parseInt(secs % 60)
]
.join(':')
.replace(/\b(\d)\b/g, '0$1')
},
/**
* 处理时区转换
* @param {*} UTCDateString 时区字符串
*/
convertUTCTimeToLocalTime (UTCDateString) {
if (!UTCDateString) {
return '-'
}
function formatFunc (str) {
// 格式化显示
return str > 9 ? str : '0' + str
}
const date2 = new Date(UTCDateString) // 这步是关键
const year = date2.getFullYear()
const mon = formatFunc(date2.getMonth() + 1)
const day = formatFunc(date2.getDate())
let hour = date2.getHours()
hour = formatFunc(hour)
const min = formatFunc(date2.getMinutes())
const dateStr = year + '/' + mon + '/' + day + ' ' + hour + ':' + min
return dateStr
},
/**
* 是否在来画app内
*/
ifLaihua () {
let lhAndroid = false
let lhIOS = false
// 是否在安卓来画中
lhAndroid = !!navigator.userAgent.includes('os=Android')
// 是否在ios来画中
lhIOS = !!navigator.userAgent.includes('os=ios')
return lhAndroid || lhIOS
},
/**
* 生成uuid
*/
generateUuid () {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
function (c) {
const r = (Math.random() * 16) | 0
const v = c === 'x' ? r : (r & 0x3) | 0x8
return v.toString(16)
}
)
}
}
export const {
checkWebpAvailable,
judgeUserAgent,
sortList,
formatDate,
secToMin,
convertUTCTimeToLocalTime,
ifLaihua,
generateUuid
} = util
export default util
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