first commit
This commit is contained in:
9
.env.development
Normal file
9
.env.development
Normal file
@@ -0,0 +1,9 @@
|
||||
# 环境
|
||||
VITE_ENV = development
|
||||
|
||||
# vconsole开关控制器
|
||||
VITE_CONSOLE = 1
|
||||
|
||||
# 接口地址
|
||||
VITE_BASE_API =
|
||||
|
||||
13
.env.production
Normal file
13
.env.production
Normal file
@@ -0,0 +1,13 @@
|
||||
# 环境
|
||||
VITE_ENV = production
|
||||
|
||||
# vconsole开关控制器
|
||||
VITE_CONSOLE = 0
|
||||
|
||||
# 接口地址
|
||||
VITE_BASE_API =
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
14
.env.staging
Normal file
14
.env.staging
Normal file
@@ -0,0 +1,14 @@
|
||||
# 环境
|
||||
VITE_ENV = staging
|
||||
|
||||
# vconsole开关控制器
|
||||
VITE_CONSOLE = 1
|
||||
|
||||
# 接口地址
|
||||
VITE_BASE_API =
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
/dist/
|
||||
/node_modules/
|
||||
/src/common/Track.js
|
||||
86
.eslintrc.js
Normal file
86
.eslintrc.js
Normal file
@@ -0,0 +1,86 @@
|
||||
// .eslintrc.js
|
||||
module.exports = {
|
||||
root: true,
|
||||
parserOptions: {
|
||||
parser: '@babel/eslint-parser',
|
||||
requireConfigFile: false,
|
||||
sourceType: 'module'
|
||||
},
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true
|
||||
},
|
||||
extends: [
|
||||
'plugin:vue/vue3-essential',
|
||||
'eslint:recommended',
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
'no-undef': 0,
|
||||
// "off" or 0 - 关闭规则
|
||||
// "warn" or 1 - 将规则视为一个警告
|
||||
// "error" or 2 - 将规则视为一个错误
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// allow async-await
|
||||
'generator-star-spacing': 'off',
|
||||
// 'no-console': process.env.VITE_ENV === 'production' ? 'error' : 'off',
|
||||
// allow debugger during development
|
||||
// 'no-debugger': process.env.VITE_ENV === 'production' ? 'error' : 'off',
|
||||
|
||||
/**
|
||||
* 最佳实践
|
||||
*/
|
||||
eqeqeq: 2, // 强制使用 === 和 !==
|
||||
'default-case': 1, // 要求 switch 语句中有 default 分支
|
||||
'no-else-return': 1, // 禁止 if 语句中 return 语句之后有 else 块
|
||||
'no-empty-function': 0, // 禁止出现空函数
|
||||
'no-unused-vars': 0, // 禁止出现空函数
|
||||
'no-multi-spaces': 1, // 禁止使用多个空格
|
||||
radix: 1, // 强制在parseInt()使用基数参数
|
||||
'no-useless-return': 1, // 禁止多余的 return 语句
|
||||
'no-with': 2, //禁用 with 语句
|
||||
/**
|
||||
* 变量声明
|
||||
*/
|
||||
'init-declarations': ['error', 'always'], // 声明变量必须赋值
|
||||
|
||||
/**
|
||||
* ECMAScript6
|
||||
*/
|
||||
'arrow-spacing': ['error', { before: true, after: true }], // 强制箭头函数的箭头前后使用空格
|
||||
'no-var': 2, // 禁止使用 var 声明变量
|
||||
'object-shorthand': 2, // 要求使用对象方法名和属性名简写
|
||||
'prefer-arrow-callback': 2, // 要求回调函数使用箭头函数
|
||||
'prefer-const': 2, // 使用 const 声明那些声明后不再被修改的变量
|
||||
'prefer-rest-params': 2, // 要求使用剩余参数而不是 arguments
|
||||
'no-duplicate-imports': 2, // 禁止重复模块导入
|
||||
'prefer-destructuring': 1, // 优先使用数组和对象解构
|
||||
/**
|
||||
* 风格指南
|
||||
*/
|
||||
'space-before-function-paren': 0, // 函数名称或function关键字与开始参数之间允许有空格
|
||||
'array-bracket-spacing': 0, // 数组方括号内必须空格
|
||||
'comma-dangle': 2, // 禁止末尾逗号
|
||||
'eol-last': 2, // 要求文件末尾存在空行
|
||||
// 对象冒号前禁止空格,冒号后必须空格
|
||||
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
||||
// 关键字(if、else等)前后必须有空格
|
||||
'keyword-spacing': ['error', { before: true, after: true }],
|
||||
// 禁止出现多行空行
|
||||
'no-multiple-empty-lines': ['error', { max: 1 }],
|
||||
semi: 0, // 禁止末尾分号
|
||||
quotes: ['error', 'single'], // 强制使用单引号
|
||||
'space-infix-ops': 2, // 操作符周围必须有空格
|
||||
'spaced-comment': ['error', 'always'], // 注释后面必须跟随至少一个空白
|
||||
'object-curly-spacing': 0,
|
||||
'no-unused-expressions': 0,
|
||||
'vue/multi-word-component-names': 0,
|
||||
'no-process-env': 2,
|
||||
camelcase: 2,
|
||||
'no-lonely-if': 2,
|
||||
'func-style': 2 // 禁止使用function声明,请使用箭头函数声明
|
||||
}
|
||||
}
|
||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
stats.html
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
3
.npmrc
Normal file
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
||||
registry=https://r.npm.taobao.org/
|
||||
sass_binary_site=https://cdn.npmmirror.com/binaries/node-sass
|
||||
shamefully-hoist=true
|
||||
23
.prettierrc.js
Normal file
23
.prettierrc.js
Normal file
@@ -0,0 +1,23 @@
|
||||
// .preitterrc.js
|
||||
module.exports = {
|
||||
// 一行的字符数,如果超过会进行换行,默认为80
|
||||
printWidth: 80,
|
||||
// 一个tab代表几个空格数,默认为80
|
||||
tabWidth: 2,
|
||||
// 是否使用tab进行缩进,默认为false,表示用空格进行缩减
|
||||
useTabs: false,
|
||||
// 字符串是否使用单引号,默认为false,使用双引号
|
||||
singleQuote: true,
|
||||
// 行位是否使用分号,默认为true
|
||||
semi: false,
|
||||
// 是否使用尾逗号,有三个可选值"<none|es5|all>"
|
||||
trailingComma: "none",
|
||||
// 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
|
||||
bracketSpacing: true,
|
||||
// 代码的解析引擎,默认为babylon,与babel相同
|
||||
// "parser": "babylon",
|
||||
|
||||
// 开启 eslint 支持
|
||||
eslintIntegration: true,
|
||||
autocrlf: false
|
||||
}
|
||||
45
README.md
Normal file
45
README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# 智念移动端单页应用Vue3模板
|
||||
|
||||
# 安装依赖
|
||||
|
||||
Yarn 安装
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
npm 安装
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
# 运行开发
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
# 测试打包
|
||||
|
||||
```bash
|
||||
yarn build:stage
|
||||
```
|
||||
|
||||
```bash
|
||||
npm build:stage
|
||||
```
|
||||
|
||||
# 生产打包
|
||||
|
||||
```bash
|
||||
yarn build:prod
|
||||
```
|
||||
|
||||
```bash
|
||||
npm build:prod
|
||||
```
|
||||
22
commitlint.config.js
Normal file
22
commitlint.config.js
Normal file
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* feat:新功能
|
||||
* fix:修补bug
|
||||
* refactor:重构某个功能
|
||||
* revert: 回滚到上一个版本
|
||||
* style:仅样式改动
|
||||
* docs:仅文档新增/改动
|
||||
* chore:构建过程或辅助工具的变动
|
||||
*/
|
||||
module.exports = {
|
||||
extends: [
|
||||
// 直接继承官网规则
|
||||
'@commitlint/config-conventional'
|
||||
],
|
||||
rules: {
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
['feat', 'fix', 'refactor', 'revert', 'style', 'docs', 'chore']
|
||||
]
|
||||
}
|
||||
}
|
||||
20
index.html
Normal file
20
index.html
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, viewport-fit=cover, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="applicable-device" content="mobile" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
|
||||
<title>‎</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
13
jsconfig.json
Normal file
13
jsconfig.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
50
package.json
Normal file
50
package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "webapp-vue-frontend",
|
||||
"description": "智念移动端单页应用Vue3模板",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build:prod": "vite build && rimraf ./dist/assets/*.map && rimraf stats.html",
|
||||
"build:stage": "vite build --mode staging",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --ext .vue,.js src",
|
||||
"lint:fix": "eslint --fix --ext .vue,.js src",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.13.4",
|
||||
"compressorjs": "^1.2.1",
|
||||
"mitt": "^3.0.1",
|
||||
"vant": "^4.9.22",
|
||||
"vconsole": "^3.15.1",
|
||||
"vue": "^3.5.27",
|
||||
"vue-router": "^4.6.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.19.6",
|
||||
"@babel/eslint-parser": "^7.19.1",
|
||||
"@commitlint/cli": "^17.1.2",
|
||||
"@commitlint/config-conventional": "^17.1.0",
|
||||
"@vitejs/plugin-legacy": "^2.2.0",
|
||||
"@vitejs/plugin-vue": "^3.1.0",
|
||||
"consola": "^2.15.3",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.6.0",
|
||||
"husky": "^8.0.1",
|
||||
"postcss-pxtorem": "^6.0.0",
|
||||
"prettier": "^2.7.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup-plugin-visualizer": "^5.8.3",
|
||||
"sass": "^1.58.3",
|
||||
"terser": "^5.15.1",
|
||||
"typescript": "^4.8.4",
|
||||
"unplugin-auto-import": "^0.11.3",
|
||||
"unplugin-vue-components": "^0.22.9",
|
||||
"vite": "3.1.0",
|
||||
"vite-plugin-style-import": "^2.0.0",
|
||||
"vite-plugin-vconsole": "^1.2.2",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0"
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
46
src/App.vue
Normal file
46
src/App.vue
Normal file
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<keep-alive :include="KeepAliveList">
|
||||
<component :is="Component" />
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const router = useRouter()
|
||||
const KeepAliveList = ref([])
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
const keepAlive = to?.meta?.keepAlive
|
||||
|
||||
if (!router.hasRoute(to.name)) {
|
||||
router.push('/error')
|
||||
return
|
||||
}
|
||||
|
||||
if (keepAlive) {
|
||||
if (KeepAliveList.value.indexOf(to.name) === -1) {
|
||||
KeepAliveList.value.push(to.name)
|
||||
} else {
|
||||
const index = KeepAliveList.value.findIndex((name) => name === from.name)
|
||||
index > -1 ? KeepAliveList.value.splice(index, 1) : null
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#app {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
@media only screen and (min-width: 500px) {
|
||||
max-width: 500px;
|
||||
margin: auto;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
BIN
src/assets/font/bebas.ttf
Normal file
BIN
src/assets/font/bebas.ttf
Normal file
Binary file not shown.
20
src/assets/font/icon.css
Normal file
20
src/assets/font/icon.css
Normal file
@@ -0,0 +1,20 @@
|
||||
@font-face {
|
||||
font-family: 'icon-font'; /* Project id 634611 (更新icon直接替换新的cdn地址即可,无需下载) */
|
||||
src: url('//at.alicdn.com/t/font_634611_fe4z5kh8e8n.woff2?t=1654585244447') format('woff2'),
|
||||
url('//at.alicdn.com/t/font_634611_fe4z5kh8e8n.woff?t=1654585244447') format('woff'),
|
||||
url('//at.alicdn.com/t/font_634611_fe4z5kh8e8n.ttf?t=1654585244447') format('truetype');
|
||||
}
|
||||
|
||||
.icon-font {
|
||||
font-family: 'icon-font' !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'money';
|
||||
src: url('./bebas.ttf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
96
src/assets/scss/index.scss
Normal file
96
src/assets/scss/index.scss
Normal file
@@ -0,0 +1,96 @@
|
||||
@import './normalize';
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
-webkit-tap-highlight-color: rgba(255, 0, 0, 0); // 移除移动端阴影
|
||||
-webkit-overflow-scrolling: touch; //滚动回弹 ios13+
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
|
||||
&,
|
||||
* {
|
||||
color-scheme: dark !important;
|
||||
transition: background-color 300ms;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='light'] {
|
||||
|
||||
&,
|
||||
* {
|
||||
color-scheme: light !important;
|
||||
transition: background-color 300ms;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
// 安全区域变量初始化
|
||||
:root {
|
||||
--safe-area-inset-top: 24px;
|
||||
--safe-area-inset-right: 0px;
|
||||
--safe-area-inset-bottom: 0px;
|
||||
--safe-area-inset-left: 0px;
|
||||
|
||||
@supports (top: constant(safe-area-inset-top)) and (padding: Max(10px, 20px)) {
|
||||
--safe-area-inset-top: Max(constant(safe-area-inset-top), 24px);
|
||||
--safe-area-inset-right: constant(safe-area-inset-right);
|
||||
--safe-area-inset-bottom: constant(safe-area-inset-bottom);
|
||||
--safe-area-inset-left: constant(safe-area-inset-left);
|
||||
}
|
||||
|
||||
@supports (top: env(safe-area-inset-top)) and (padding: Max(10px, 20px)) {
|
||||
--safe-area-inset-top: Max(env(safe-area-inset-top), 24px);
|
||||
--safe-area-inset-right: env(safe-area-inset-right);
|
||||
--safe-area-inset-bottom: env(safe-area-inset-bottom);
|
||||
--safe-area-inset-left: env(safe-area-inset-left);
|
||||
}
|
||||
}
|
||||
|
||||
.van-safe-area-top {
|
||||
padding-top: var(--safe-area-inset-top) !important;
|
||||
}
|
||||
|
||||
.van-image-preview__index,
|
||||
.van-image-preview__close-icon--top-right {
|
||||
top: 46px !important;
|
||||
}
|
||||
|
||||
// 表单样式
|
||||
.van-field__error-message {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.van-hairline--top-bottom:after,
|
||||
.van-hairline-unset--top-bottom:after {
|
||||
border-width: 0 !important;
|
||||
}
|
||||
|
||||
// 弹窗样式
|
||||
.van-dialog {
|
||||
width: 270px !important;
|
||||
}
|
||||
|
||||
.van-dialog__confirm,
|
||||
.van-dialog__confirm:active {
|
||||
color: #14b498 !important;
|
||||
}
|
||||
|
||||
.van-dialog__content--isolated {
|
||||
min-height: 72px !important;
|
||||
}
|
||||
|
||||
.van-dialog__message {
|
||||
color: #333333;
|
||||
font-size: 17px !important;
|
||||
font-family: PingFangSC-Semibold, PingFang SC;
|
||||
font-weight: 600;
|
||||
line-height: 24px !important;
|
||||
}
|
||||
355
src/assets/scss/normalize.scss
vendored
Normal file
355
src/assets/scss/normalize.scss
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
|
||||
|
||||
/* Document
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Correct the line height in all browsers.
|
||||
* 2. Prevent adjustments of font size after orientation changes in iOS.
|
||||
*/
|
||||
|
||||
html {
|
||||
line-height: 1.15; /* 1 */
|
||||
-webkit-text-size-adjust: 100%; /* 2 */
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Sections
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the margin in all browsers.
|
||||
*/
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the `main` element consistently in IE.
|
||||
*/
|
||||
|
||||
main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the font size and margin on `h1` elements within `section` and
|
||||
* `article` contexts in Chrome, Firefox, and Safari.
|
||||
*/
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin: 0.67em 0;
|
||||
}
|
||||
|
||||
/* Grouping content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in Firefox.
|
||||
* 2. Show the overflow in Edge and IE.
|
||||
*/
|
||||
|
||||
hr {
|
||||
box-sizing: content-box; /* 1 */
|
||||
height: 0; /* 1 */
|
||||
overflow: visible; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
pre {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/* Text-level semantics
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the gray background on active links in IE 10.
|
||||
*/
|
||||
|
||||
a {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Remove the bottom border in Chrome 57-
|
||||
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||
*/
|
||||
|
||||
abbr[title] {
|
||||
border-bottom: none; /* 1 */
|
||||
text-decoration: underline; /* 2 */
|
||||
text-decoration: underline dotted; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||
*/
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||
* 2. Correct the odd `em` font sizing in all browsers.
|
||||
*/
|
||||
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: monospace, monospace; /* 1 */
|
||||
font-size: 1em; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct font size in all browsers.
|
||||
*/
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||
* all browsers.
|
||||
*/
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
/* Embedded content
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Remove the border on images inside links in IE 10.
|
||||
*/
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
/* Forms
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* 1. Change the font styles in all browsers.
|
||||
* 2. Remove the margin in Firefox and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit; /* 1 */
|
||||
font-size: 100%; /* 1 */
|
||||
line-height: 1.15; /* 1 */
|
||||
margin: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the overflow in IE.
|
||||
* 1. Show the overflow in Edge.
|
||||
*/
|
||||
|
||||
button,
|
||||
input { /* 1 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||
* 1. Remove the inheritance of text transform in Firefox.
|
||||
*/
|
||||
|
||||
button,
|
||||
select { /* 1 */
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the inability to style clickable types in iOS and Safari.
|
||||
*/
|
||||
|
||||
button,
|
||||
[type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner border and padding in Firefox.
|
||||
*/
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the focus styles unset by the previous rule.
|
||||
*/
|
||||
|
||||
button:-moz-focusring,
|
||||
[type="button"]:-moz-focusring,
|
||||
[type="reset"]:-moz-focusring,
|
||||
[type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the padding in Firefox.
|
||||
*/
|
||||
|
||||
fieldset {
|
||||
padding: 0.35em 0.75em 0.625em;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the text wrapping in Edge and IE.
|
||||
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||
* 3. Remove the padding so developers are not caught out when they zero out
|
||||
* `fieldset` elements in all browsers.
|
||||
*/
|
||||
|
||||
legend {
|
||||
box-sizing: border-box; /* 1 */
|
||||
color: inherit; /* 2 */
|
||||
display: table; /* 1 */
|
||||
max-width: 100%; /* 1 */
|
||||
padding: 0; /* 3 */
|
||||
white-space: normal; /* 1 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||
*/
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the default vertical scrollbar in IE 10+.
|
||||
*/
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Add the correct box sizing in IE 10.
|
||||
* 2. Remove the padding in IE 10.
|
||||
*/
|
||||
|
||||
[type="checkbox"],
|
||||
[type="radio"] {
|
||||
box-sizing: border-box; /* 1 */
|
||||
padding: 0; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||
*/
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the odd appearance in Chrome and Safari.
|
||||
* 2. Correct the outline style in Safari.
|
||||
*/
|
||||
|
||||
[type="search"] {
|
||||
-webkit-appearance: textfield; /* 1 */
|
||||
outline-offset: -2px; /* 2 */
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inner padding in Chrome and Safari on macOS.
|
||||
*/
|
||||
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||
* 2. Change font properties to `inherit` in Safari.
|
||||
*/
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button; /* 1 */
|
||||
font: inherit; /* 2 */
|
||||
}
|
||||
|
||||
/* Interactive
|
||||
========================================================================== */
|
||||
|
||||
/*
|
||||
* Add the correct display in Edge, IE 10+, and Firefox.
|
||||
*/
|
||||
|
||||
details {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add the correct display in all browsers.
|
||||
*/
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
/* Misc
|
||||
========================================================================== */
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10+.
|
||||
*/
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the correct display in IE 10.
|
||||
*/
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button {
|
||||
border: 0;
|
||||
}
|
||||
222
src/common/Track.js
Normal file
222
src/common/Track.js
Normal file
@@ -0,0 +1,222 @@
|
||||
import { bindEvent, last } from 'vtils'
|
||||
|
||||
const Track = {
|
||||
app: null,
|
||||
ready: false,
|
||||
App: {
|
||||
created() {
|
||||
Track.app = this
|
||||
},
|
||||
watch: {
|
||||
'$route.path': {
|
||||
immediate: true,
|
||||
handler: async (fromPath, toPath) => {
|
||||
await Track.ensureReady()
|
||||
Track.app.$nextTick(async () => {
|
||||
const { id, name } = await Track.injectMark()
|
||||
window.loadEventPush(
|
||||
fromPath,
|
||||
toPath || '',
|
||||
Track.app.$route.query,
|
||||
{
|
||||
mark_id: id,
|
||||
mark_name: name
|
||||
}
|
||||
)
|
||||
Track.app.$nextTick(() => {
|
||||
window.elementAddEventListener()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
ensureReady: async () => {
|
||||
if (Track.ready) return
|
||||
return new Promise((resolve) => {
|
||||
const timer = setInterval(() => {
|
||||
if (
|
||||
// @ts-ignore
|
||||
typeof window.Countly !== 'undefined' &&
|
||||
// @ts-ignore
|
||||
typeof window.anyEventPush !== 'undefined' &&
|
||||
Track.app !== null
|
||||
) {
|
||||
clearInterval(timer)
|
||||
Track.ready = true
|
||||
resolve()
|
||||
}
|
||||
}, 60)
|
||||
})
|
||||
},
|
||||
prepareData(data) {
|
||||
return {
|
||||
...(data.id && data.name
|
||||
? {
|
||||
control_id: data.id,
|
||||
control_name: data.name,
|
||||
control_value: data.value || data.name
|
||||
}
|
||||
: {}),
|
||||
...(data.goods
|
||||
? data.goods.isUniGoods
|
||||
? {
|
||||
content_id: data.goods.id,
|
||||
content_name: data.goods.name,
|
||||
resource_id: data.goods.resourceId,
|
||||
resource_name: data.goods.resourceName,
|
||||
resource_position_id: data.goods.resourcePositionId,
|
||||
resource_position_name: data.goods.resourcePositionName,
|
||||
resource_position_no: data.index || 0
|
||||
}
|
||||
: {
|
||||
content_id: data.goods.commodityId,
|
||||
content_name:
|
||||
data.goods.commodityTitle ||
|
||||
// 兼容订单下的商品
|
||||
data.goods.name,
|
||||
...(data.goods.commodityResourceId
|
||||
? {
|
||||
resource_id: data.goods.commodityResourceId,
|
||||
resource_name: data.goods.commodityTitle,
|
||||
resource_position_id: '',
|
||||
resource_position_name: data.name,
|
||||
resource_position_no: data.index || 0
|
||||
}
|
||||
: {})
|
||||
}
|
||||
: {}),
|
||||
...(data.resource
|
||||
? {
|
||||
resource_id: data.resource.id,
|
||||
resource_name: data.resource.title || data.resource.name || '',
|
||||
resource_position_id: data.resource.stateId || '',
|
||||
resource_position_name: data.resource.stateName || '',
|
||||
resource_position_no: data.index || 0
|
||||
}
|
||||
: {}),
|
||||
...(data.resourcePosition
|
||||
? {
|
||||
resource_position_id: data.resourcePosition.id,
|
||||
resource_position_name: data.resourcePosition.name,
|
||||
resource_position_no: data.index || 0
|
||||
}
|
||||
: {}),
|
||||
...(data.mark
|
||||
? {
|
||||
mark_id: data.mark.id,
|
||||
mark_name: data.mark.name
|
||||
}
|
||||
: {}),
|
||||
...(data.content
|
||||
? {
|
||||
content_name: data.content
|
||||
}
|
||||
: {})
|
||||
}
|
||||
},
|
||||
pushEvent: async (data) => {
|
||||
await Track.ensureReady()
|
||||
const trackData = Track.prepareData(data)
|
||||
if (!trackData.mark_id && !trackData.mark_name) {
|
||||
const { id, name } = await Track.injectMark()
|
||||
trackData.mark_id = id
|
||||
trackData.mark_name = name
|
||||
}
|
||||
window.anyEventPush(data.type, data.path, data.name, trackData)
|
||||
},
|
||||
provideMark(cb) {
|
||||
return {
|
||||
methods: {
|
||||
provideTrackMark: cb
|
||||
}
|
||||
}
|
||||
},
|
||||
injectMark: async () => {
|
||||
const currentRoute = Track.app.$route
|
||||
const matched = currentRoute.matched
|
||||
const currentPage = last(matched)
|
||||
const currentPageInstance = currentPage?.instances.default
|
||||
const provideTrackMark = currentPageInstance?.provideTrackMark
|
||||
const { id = 'NONE', name = 'NONE' } =
|
||||
typeof provideTrackMark === 'function' ? await provideTrackMark() : {}
|
||||
return {
|
||||
id,
|
||||
name
|
||||
}
|
||||
},
|
||||
install(app, options) {
|
||||
Track.ensureReady().then(() => {
|
||||
// @ts-ignore
|
||||
countlyConfig.baseContextPath = options.appid
|
||||
})
|
||||
|
||||
async function applyTrack(el, payload) {
|
||||
await Track.ensureReady()
|
||||
let data = payload.value
|
||||
// @ts-ignore
|
||||
if (data && !data.disabled) {
|
||||
data = {
|
||||
...data,
|
||||
name: `${options.prefix || ''}${data.name}`,
|
||||
// @ts-ignore
|
||||
path: el.__track_data__?.path || window.getRoutePath()
|
||||
}
|
||||
|
||||
el.__track_data__ = data
|
||||
|
||||
// 兼容曝光事件
|
||||
if (data.expose !== false) {
|
||||
el.setAttribute('exposure-event', data.name)
|
||||
el.setAttribute(
|
||||
'extend_attr',
|
||||
JSON.stringify(Track.prepareData(data))
|
||||
)
|
||||
if (data.value !== null) {
|
||||
el.setAttribute('alt', data.value)
|
||||
}
|
||||
}
|
||||
|
||||
const shouldListen = !el.dataset.track
|
||||
if (shouldListen) {
|
||||
el.dataset.track = '1'
|
||||
el.__track_dispose__ = bindEvent(el)(
|
||||
'click',
|
||||
() => {
|
||||
console.log(`埋点:点击「${data.name}」`)
|
||||
Track.pushEvent({
|
||||
type: 'click',
|
||||
...el.__track_data__,
|
||||
value: el.innerText.trim().replace(/\s+/g, '')
|
||||
})
|
||||
},
|
||||
{
|
||||
// 必须使用捕获模式以在其他事件触发前处理(比如其他事件触发了页面跳转,就会导致问题)
|
||||
capture: true,
|
||||
// 使用被动模式告诉浏览器这不会产生阻塞行为
|
||||
passive: true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function disposeTrack(el) {
|
||||
// 需延时一下,不然当遇到点击跳转时页面卸载把事件也卸载了就触发不了了
|
||||
setTimeout(() => {
|
||||
delete el.__track_data__
|
||||
el.__track_dispose__?.()
|
||||
delete el.__track_dispose__
|
||||
}, 0)
|
||||
}
|
||||
|
||||
app.directive('track', {
|
||||
mounted: applyTrack,
|
||||
updated: applyTrack,
|
||||
beforeUpdate: applyTrack,
|
||||
unmounted: disposeTrack
|
||||
})
|
||||
console.log(1)
|
||||
}
|
||||
}
|
||||
export default Track
|
||||
131
src/common/ajax.js
Normal file
131
src/common/ajax.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import axios from 'axios'
|
||||
import {
|
||||
md5Hash,
|
||||
rsaEncrypt,
|
||||
rsaDecrypt,
|
||||
aesEncrypt,
|
||||
aesDecrypt,
|
||||
generateUUID,
|
||||
dataSign,
|
||||
generateRandomString,
|
||||
getLocalStorage
|
||||
} from '@dcb/utils'
|
||||
|
||||
const encryptionOff = Number(import.meta.env.VITE_ENCRYPTION)
|
||||
const env = import.meta.env.VITE_ENV === 'development'
|
||||
const STORE_OFF = Number(import.meta.env.VITE_STORE_OFF)
|
||||
const RSA = import.meta.env.VITE_APP_RSA
|
||||
const DATA_RSA = STORE_OFF ? import.meta.env.VITE_DATA_RSA : RSA
|
||||
const martId = import.meta.env.VITE_MART_ID
|
||||
const appKey = import.meta.env.VITE_KEY
|
||||
const appSecret = import.meta.env.VITE_SECRET
|
||||
|
||||
const request = async (method, url, data, config = {}) => {
|
||||
const p = env ? import.meta.env.VITE_P : decodeURI(getLocalStorage('p'))
|
||||
const options = Object.assign({}, config, {
|
||||
url,
|
||||
method
|
||||
})
|
||||
|
||||
console.log('入参', data)
|
||||
|
||||
const uuid = generateUUID()
|
||||
const secret = '4RBA7^@0$@KM$5D2333'
|
||||
const title = md5Hash(uuid + secret)
|
||||
const aesKey = generateRandomString(16) // 生成16位随机数
|
||||
|
||||
const init = rsaEncrypt(aesKey, RSA)
|
||||
const base = aesEncrypt(JSON.stringify(data), aesKey)
|
||||
|
||||
options.data = options.data || {}
|
||||
|
||||
/*
|
||||
* @ 验签
|
||||
* @wiki: http://wiki.gogpay.cn/pages/viewpage.action?pageId=75301563
|
||||
* */
|
||||
url = url.replace(/^https?:\/\/.+?\/([^/]+)/, '')
|
||||
|
||||
const timestamp = Date.now()
|
||||
let asHex = ''
|
||||
const header = Object.assign(
|
||||
{ clientId: '03' },
|
||||
STORE_OFF ? { martId, path: url, timestamp } : { path: url, timestamp }
|
||||
)
|
||||
|
||||
for (const [key, value] of Object.entries(header)) {
|
||||
asHex += key + value
|
||||
}
|
||||
|
||||
// console.log(asHex + appSecret)
|
||||
const sign = dataSign(header, appSecret)
|
||||
|
||||
if (method === 'post') {
|
||||
if (!config.white && encryptionOff) {
|
||||
options.data = {
|
||||
timestamp: uuid,
|
||||
title,
|
||||
init,
|
||||
base
|
||||
}
|
||||
} else {
|
||||
options.data = data
|
||||
}
|
||||
}
|
||||
|
||||
options.headers = Object.assign({}, options.headers, header, {
|
||||
sign,
|
||||
appKey,
|
||||
p
|
||||
})
|
||||
|
||||
console.log(options.headers, STORE_OFF)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.request(options)
|
||||
.then((res) => {
|
||||
let { data } = res
|
||||
|
||||
if (!data) {
|
||||
return resolve(data)
|
||||
}
|
||||
|
||||
if (method === 'post' && encryptionOff && !config.white) {
|
||||
data = JSON.parse(
|
||||
aesDecrypt(data.base, rsaDecrypt(data.init, DATA_RSA))
|
||||
)
|
||||
}
|
||||
|
||||
console.log('返回值', data)
|
||||
resolve(data)
|
||||
})
|
||||
.catch((res) => {
|
||||
reject(res)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const ajax = {
|
||||
request,
|
||||
get(url, config) {
|
||||
return request('get', url, null, config)
|
||||
},
|
||||
delete(url, config) {
|
||||
return request('delete', url, null, config)
|
||||
},
|
||||
head(url, config) {
|
||||
return request('head', url, null, config)
|
||||
},
|
||||
post(url, data, config) {
|
||||
return request('post', url, data, config)
|
||||
},
|
||||
put(url, data, config) {
|
||||
return request('put', url, data, config)
|
||||
},
|
||||
patch(url, data, config) {
|
||||
return request('path', url, data, config)
|
||||
},
|
||||
setCommonHeader(key, value) {
|
||||
window.axios.defaults.headers.common[key] = value
|
||||
}
|
||||
}
|
||||
16
src/common/dcb.ajax.config.js
Normal file
16
src/common/dcb.ajax.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* eslint-disable no-process-env */
|
||||
import { initAjax } from '@dcb/ajax'
|
||||
const debug = import.meta.env.VITE_ENV
|
||||
const RSA = import.meta.env.VITE_APP_RSA
|
||||
const appid = import.meta.env.VITE_APP_ID
|
||||
const BASE_API = import.meta.env.VITE_BASE_API
|
||||
const encryptionOff = import.meta.env.VITE_ENCRYPTION
|
||||
|
||||
export const useAjax = function () {
|
||||
const ajax = initAjax(true)
|
||||
const options = { RSA, appid, BASE_API, encryptionOff, debug }
|
||||
|
||||
ajax.config(options)
|
||||
|
||||
return ajax
|
||||
}
|
||||
50
src/common/flexible.js
Normal file
50
src/common/flexible.js
Normal file
@@ -0,0 +1,50 @@
|
||||
// ref: https://github.com/amfe/lib-flexible/blob/2.0/index.js
|
||||
;(function flexible(window, document) {
|
||||
const docEl = document.documentElement
|
||||
const dpr = window.devicePixelRatio || 1
|
||||
|
||||
// adjust body font size
|
||||
const setBodyFontSize = () => {
|
||||
if (document.body) {
|
||||
document.body.style.fontSize = `${12 * dpr}px`
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', setBodyFontSize)
|
||||
}
|
||||
}
|
||||
|
||||
setBodyFontSize()
|
||||
|
||||
// set 1rem = viewWidth / 10
|
||||
const setRemUnit = () => {
|
||||
const rem =
|
||||
Math.min(
|
||||
// 宽度限制在 500 内
|
||||
500,
|
||||
docEl.clientWidth
|
||||
) / 10
|
||||
docEl.style.fontSize = `${rem}px`
|
||||
}
|
||||
|
||||
setRemUnit()
|
||||
|
||||
// reset rem unit on page resize
|
||||
window.addEventListener('resize', setRemUnit)
|
||||
window.addEventListener('pageshow', (e) => {
|
||||
if (e.persisted) {
|
||||
setRemUnit()
|
||||
}
|
||||
})
|
||||
|
||||
// detect 0.5px supports
|
||||
if (dpr >= 2) {
|
||||
const fakeBody = document.createElement('body')
|
||||
const testElement = document.createElement('div')
|
||||
testElement.style.border = '.5px solid transparent'
|
||||
fakeBody.appendChild(testElement)
|
||||
docEl.appendChild(fakeBody)
|
||||
if (testElement.offsetHeight === 1) {
|
||||
docEl.classList.add('hairlines')
|
||||
}
|
||||
docEl.removeChild(fakeBody)
|
||||
}
|
||||
})(window, document)
|
||||
3
src/common/index.js
Normal file
3
src/common/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { ajax } from './ajax'
|
||||
|
||||
export { ajax }
|
||||
53
src/components/Empty.vue
Normal file
53
src/components/Empty.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div class="empty">
|
||||
<img class="empty__img" :src="img" />
|
||||
<div class="empty__desc" v-if="description" :style="{ color }">
|
||||
{{ description }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import imgEmpty from '@/assets/images/img_empty_order.png'
|
||||
|
||||
const props = defineProps({
|
||||
description: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
img: {
|
||||
type: Object,
|
||||
default: imgEmpty
|
||||
},
|
||||
|
||||
color: {
|
||||
type: String,
|
||||
default: '#999999'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.empty {
|
||||
width: 100%;
|
||||
height: 288px;
|
||||
@extend %flex-center;
|
||||
|
||||
&__img {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
display: block;
|
||||
margin: 105px auto 0;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
font-size: 14px;
|
||||
font-family: PingFangSC-Regular, PingFang SC;
|
||||
color: #999999;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
148
src/components/PageContainer.vue
Normal file
148
src/components/PageContainer.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<div class="page-container" :style="{ backgroundColor: bgColor }">
|
||||
<van-nav-bar :class="['nav-bar', navBgColor, isCustom ? 'custom-style' : '']" safe-area-inset-top left-arrow fixed
|
||||
v-if="showNavigator" :border="false" :left-text="navBarLeftText" @click-left="handleBack">
|
||||
<template #title>
|
||||
<slot name="title">{{ title === '首页' ? '' : title }}</slot>
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<slot name="nav-bar-right"></slot>
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
const props = defineProps({
|
||||
// nav-bar返回按钮位置的文字
|
||||
navBarLeftText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
// nav-bar标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
// 背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: 'white'
|
||||
},
|
||||
|
||||
// nav-bar背景颜色
|
||||
navBgColor: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
// fixed情况下返回按钮背景颜色
|
||||
backBgColor: {
|
||||
type: String,
|
||||
default: 'lightgray'
|
||||
},
|
||||
|
||||
// 是否自定义样式
|
||||
isCustom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 是否显示导航栏
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
goback: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
// 是否显示隐藏导航
|
||||
const showNavigator = ref(props.show)
|
||||
|
||||
// 获取页面标题
|
||||
const { title } = proxy.$router.currentRoute.value.meta
|
||||
|
||||
const handleBack = () => {
|
||||
if (props.goback > 0) {
|
||||
proxy.$router.go(-props.goback)
|
||||
} else {
|
||||
proxy.$router.back()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.page-container {
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
background-color: white;
|
||||
padding-bottom: var(--safe-area-inset-bottom);
|
||||
|
||||
.body {
|
||||
min-height: calc(100vh - 46px + var(--safe-area-inset-top));
|
||||
padding-top: calc(46px + var(--safe-area-inset-top));
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-bar {
|
||||
background-color: transparent;
|
||||
z-index: 2000;
|
||||
position: fixed;
|
||||
|
||||
:deep(.van-icon-arrow-left) {
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
:deep(.van-nav-bar__title) {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
line-height: 22px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.style-1,
|
||||
&.style-2 {
|
||||
:deep(.van-icon-arrow-left) {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
:deep(.van-nav-bar__title) {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
&.style-1 {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
&.style-2 {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
&.custom-style {
|
||||
background-color: rgba(22, 187, 191, 1);
|
||||
|
||||
:deep(.van-icon-arrow-left) {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
2
src/components/index.js
Normal file
2
src/components/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as PageContainer } from './PageContainer.vue'
|
||||
export { default as Empty } from './Empty.vue'
|
||||
5
src/hooks/tool.js
Normal file
5
src/hooks/tool.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export const observer = reactive({
|
||||
userInfo: undefined
|
||||
})
|
||||
13
src/main.js
Normal file
13
src/main.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { createApp } from 'vue'
|
||||
import '@/assets/font/icon.css'
|
||||
import '@/common/flexible'
|
||||
import App from './App.vue'
|
||||
import router from '@/router'
|
||||
import store from '@/store'
|
||||
|
||||
const app = createApp(App)
|
||||
app.config.globalProperties.$store = store
|
||||
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
22
src/router/index.js
Normal file
22
src/router/index.js
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
|
||||
// 路由规则
|
||||
const routes = [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home', // 请和文件名一样
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
meta: {
|
||||
title: '首页', // 自动设置当前页面的标题
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
// vueRouter@3版本的mode改成了history,hash模式配置createWebHashHistory,history模式配置createWebHistory
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
})
|
||||
|
||||
export default router
|
||||
18
src/store/index.js
Normal file
18
src/store/index.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { reactive } from 'vue'
|
||||
|
||||
export default {
|
||||
state: reactive({
|
||||
userInfo: undefined
|
||||
}),
|
||||
|
||||
mutations: {
|
||||
setUserInfo(state, data) {
|
||||
state.userInfo = data
|
||||
}
|
||||
},
|
||||
|
||||
commit(eventName, { data, dept }) {
|
||||
data.dept.preCheck = dept.preCheck
|
||||
this.mutations[eventName](this.state, data)
|
||||
}
|
||||
}
|
||||
22
src/views/home/index.vue
Normal file
22
src/views/home/index.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<van-button @click="handleClick">跳转记录</van-button>
|
||||
</template>
|
||||
|
||||
<script setup name="home">
|
||||
const router = useRouter()
|
||||
|
||||
// 跳转按钮操作
|
||||
const handleClick = () => {
|
||||
console.log('跳转记录')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
console.log('onMounted')
|
||||
})
|
||||
|
||||
onActivated(() => {
|
||||
console.log('onActivated')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
107
vite.config.js
Normal file
107
vite.config.js
Normal file
@@ -0,0 +1,107 @@
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import VueSetupExtend from 'vite-plugin-vue-setup-extend'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
import { createStyleImportPlugin, VantResolve } from 'vite-plugin-style-import'
|
||||
import postCssPxToRem from 'postcss-pxtorem'
|
||||
import legacy from '@vitejs/plugin-legacy'
|
||||
import { resolve } from 'path'
|
||||
import { viteVConsole } from 'vite-plugin-vconsole'
|
||||
import visualizer from 'rollup-plugin-visualizer'
|
||||
import Components from 'unplugin-vue-components/vite'
|
||||
import { VantResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(({ mode }) => {
|
||||
const { VITE_ENV, VITE_CONSOLE } = loadEnv(mode, process.cwd())
|
||||
const isProd = VITE_ENV === 'production'
|
||||
const isVconsole = Number(VITE_CONSOLE)
|
||||
|
||||
return {
|
||||
base: isProd ? './' : './', // 是否是生产环境
|
||||
build: {
|
||||
cssCodeSplit: false,
|
||||
chunkSizeWarningLimit: 2048,
|
||||
sourcemap: isProd
|
||||
},
|
||||
esbuild: {
|
||||
drop: isProd ? ['console', 'debugger'] : undefined
|
||||
},
|
||||
plugins: [
|
||||
legacy({
|
||||
targets: ['defaults', 'not IE 11']
|
||||
}),
|
||||
vue(),
|
||||
VueSetupExtend(),
|
||||
Components({
|
||||
dts: false,
|
||||
resolvers: [VantResolver()]
|
||||
}),
|
||||
AutoImport({
|
||||
// 后续vue/vue-router的API都不需要再单独import到setup里面了
|
||||
imports: ['vue', 'vue-router'],
|
||||
// dts: 'src/auto-imports...', // 可以自定义文件生成的位置与是否生成,默认是根目录下
|
||||
dts: false
|
||||
}),
|
||||
createStyleImportPlugin({
|
||||
resolves: [VantResolve()],
|
||||
libs: [
|
||||
{
|
||||
libraryName: 'vant',
|
||||
esModule: false,
|
||||
resolveStyle: (name) => {
|
||||
return `vant/es/${name}/style`
|
||||
}
|
||||
}
|
||||
]
|
||||
}),
|
||||
viteVConsole({
|
||||
entry: [resolve('src/main.js')],
|
||||
localEnabled: isVconsole,
|
||||
enabled: isVconsole,
|
||||
config: {
|
||||
maxLogNumber: 1000,
|
||||
theme: 'light'
|
||||
}
|
||||
}),
|
||||
!isProd
|
||||
? visualizer({
|
||||
open: true,
|
||||
gzipSize: true,
|
||||
brotliSize: true
|
||||
})
|
||||
: null
|
||||
],
|
||||
resolve: {
|
||||
// 设置路径别名
|
||||
alias: {
|
||||
'@': resolve(__dirname, './src'),
|
||||
'~': resolve(__dirname, './src/assets')
|
||||
}
|
||||
},
|
||||
server: {
|
||||
hmr: true,
|
||||
port: 8080,
|
||||
host: true,
|
||||
open: true
|
||||
},
|
||||
css: {
|
||||
// css预处理器
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
charset: false,
|
||||
additionalData: '@import "./src/assets/scss/index.scss";'
|
||||
}
|
||||
},
|
||||
|
||||
postcss: {
|
||||
plugins: [
|
||||
postCssPxToRem({
|
||||
rootValue: 37.5,
|
||||
propList: ['*']
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user