初始化
2
.browserslistrc
Normal file
@@ -0,0 +1,2 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
6
.env
Normal file
@@ -0,0 +1,6 @@
|
||||
# env
|
||||
NODE_ENV = 'dev' // 如果是生产环境,请记得切换为production
|
||||
|
||||
# flag
|
||||
VUE_APP_FLAG='dev'
|
||||
VUE_APP_BASE='http://192.168.8.221:7000'
|
||||
7
.env.dev
Normal file
@@ -0,0 +1,7 @@
|
||||
# env
|
||||
NODE_ENV = 'dev' // 如果是生产环境,请记得切换为production
|
||||
|
||||
# flag
|
||||
VUE_APP_FLAG='dev'
|
||||
VUE_APP_BASE='http://ebiz-fooge.320.io:7015'
|
||||
|
||||
8
.env.prd
Normal file
@@ -0,0 +1,8 @@
|
||||
# env
|
||||
NODE_ENV = 'production' // 如果是生产环境,请记得切换为production
|
||||
|
||||
# flag
|
||||
VUE_APP_FLAG='prd'
|
||||
VUE_APP_BASE='http://smart.ebiz-digits.com:7115'
|
||||
VUE_APP_AI='http://smart.ebiz-digits.com:7115'
|
||||
#VUE_APP_AI='http://192.168.9.71:8015'
|
||||
4
.eslintignore
Normal file
@@ -0,0 +1,4 @@
|
||||
public
|
||||
dist
|
||||
node_modules
|
||||
src/assets/js/utils/ebizTrack.es.js
|
||||
27
.eslintrc.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { endsWith } = require("core-js/core/string")
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
extends: ['plugin:vue/essential', '@vue/prettier'],
|
||||
rules: {
|
||||
//'off'或'0':关闭 'warn'或'1':警告 "error"或者"2":报错
|
||||
// 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'error',
|
||||
'no-console': 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'error',
|
||||
'prettier/prettier': ['error',
|
||||
{
|
||||
singleQuote: true, //单引号
|
||||
semi: false, //分号
|
||||
printWidth: 120, //一行的字符数,如果超过会进行换行,默认为80
|
||||
trailingComma: 'none', //是否使用尾逗号,有三个可选值"<none|es5|all>"
|
||||
endOfLine: 'auto' //结尾是 \n \r \n\r auto win 和 mac 不一致
|
||||
}
|
||||
]
|
||||
},
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
}
|
||||
}
|
||||
31
.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
package-lock.json
|
||||
/dist
|
||||
/target
|
||||
/generate
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.settings
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
*.iml
|
||||
.classpath
|
||||
.project
|
||||
*.log
|
||||
/sale
|
||||
*.zip
|
||||
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 160,
|
||||
"endOfLine": "auto"
|
||||
}
|
||||
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
2.7.18
|
||||
183
README.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# basic-h5
|
||||
|
||||
## 介绍
|
||||
|
||||
- UI 框架使用[Vant](https://youzan.github.io/vant/#/zh-CN/intro),它是轻量、可靠的移动端 Vue 组件库
|
||||
- 组件导入方式和 viewport 适配参考:[快速上手](https://youzan.github.io/vant/#/zh-CN/quickstart)
|
||||
|
||||
### 技术栈
|
||||
|
||||
Vue2 + Vue-Router + Axios + Vuex + Vant + Vue-Loader + Vue-Cli3.0 + ...
|
||||
|
||||
### 命名规范
|
||||
|
||||
- 组件名:大驼峰
|
||||
- 方法名:小驼峰
|
||||
- class 类名:连字符(a-b)
|
||||
- 常量(const):全大写
|
||||
- 变量(let):小驼峰
|
||||
- 文件夹命名: 一律小写(只允许小写字母、中横杠-,如:a-b)
|
||||
- Vue 文件命名: 大驼峰式命名法,即每个单词的首字母大写 CamelCase.vue
|
||||
- css,js,image 等文件命名:一律小写(css/js 文件使用中横杆-,image 使用下划线\_)第三方除外
|
||||
- 如条件允许: import 进来的变量名及 export 都采用大驼峰形式命名,如 import VueRouter from 'vue-router' 及 export default Common
|
||||
|
||||
```javascript
|
||||
//什么叫“如条件允许”:比如我们需要
|
||||
import routes from './routers/router'
|
||||
const router = new VueRouter({
|
||||
routes
|
||||
})
|
||||
//这种情况下,routes可以用小写,事实上,上面的范例可以换一种写法:
|
||||
import Routes from './routers/router'
|
||||
const router = new VueRouter({
|
||||
routes: Routes
|
||||
})
|
||||
```
|
||||
|
||||
## wiki 文档
|
||||
|
||||
[wiki 参考文档](http://114.215.252.122:8090/pages/viewpage.action?pageId=13960243)
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
.
|
||||
├── node_modules // npm/yarn安装的项目依赖模块
|
||||
├── public // 静态资源目录
|
||||
│ ├── webViewBridge.js //跳转文件 与原生交互js
|
||||
|
||||
├── src // 源码目录
|
||||
│ ├── api // 网络请求文件
|
||||
│ │ ├── app // 基础工程网络请求文件
|
||||
│ │ │ ├── user.js // 用户模块api请求
|
||||
│ │ ├── example
|
||||
│ │ │ └── *** // 示例模块api请求
|
||||
│ ├── assets // 资源目录
|
||||
│ │ ├── fonts // 字体文件
|
||||
│ │ ├── images // 图片文件
|
||||
│ │ | ├── app // 基础工程图片
|
||||
│ │ | ├── 404_img // 404图片
|
||||
│ │ ├── js
|
||||
│ │ | ├── utils //工具类js
|
||||
│ │ | | ├── get-url.js //根据设置获取请求url
|
||||
│ │ | | ├── premission.js //权限控制
|
||||
│ │ | | ├── request.js //拦截器
|
||||
│ │ | | ├── validate.js //表单校验
|
||||
│ │ | ├── vendor //第三方js
|
||||
│ │ | ├── common.js //全局js方法
|
||||
│ │ | ├── business-common.js //业务相关全局js方法
|
||||
│ │ ├── sass
|
||||
│ │ | └── common.scss // 样式出口文件
|
||||
│ │ | └── vant-ui.scss // vant样式
|
||||
│ │ | └── minix.scss // 样式配置
|
||||
│ │ | └── transition.scss // 动画样式
|
||||
│ │ | └── utils.scss // 工具样式
|
||||
│ │ | └── variables.scss // 变量文件
|
||||
│ ├── components // 全局组件
|
||||
│ | ├── common //公共组件
|
||||
│ │ | └── Jump // 跳转组件
|
||||
│ ├── config // 配置文件
|
||||
│ | ├── index.js // 环境配置
|
||||
│ | ├── urlMap.js // api url配置
|
||||
│ ├── filters
|
||||
│ | ├── index.js // 过滤器
|
||||
│ ├── router
|
||||
│ | ├── app // 基础工程路由
|
||||
│ | ├── example // 示例路由
|
||||
│ | ├── index.js // 路由配置
|
||||
│ ├── store
|
||||
│ | ├── modules // 状态模块
|
||||
│ | ├── getters.js // 配置getters
|
||||
│ | ├── index.js // 引用vuex,创建store
|
||||
│ ├── views
|
||||
│ | ├── app // 基础工程页面
|
||||
│ | | ├── layout // layout:导航栏、侧边栏、主窗口等
|
||||
│ | | ├── login // 登录页面
|
||||
│ | | ├── 404.vue // 404页面
|
||||
│ | | ├── home.vue // 首页
|
||||
│ | ├── example // 示例页面,不一一列举
|
||||
│ ├── App.vue // 页面入口文件
|
||||
│ ├── main.js // 程序入口文件,加载各种公共组件
|
||||
├── tests // 测试相关文件
|
||||
├── .env.dev // 开发环境配置
|
||||
├── .env.test // 测试环境配置
|
||||
├── .env.uat // 回归环境配置
|
||||
├── .env.prd // 生产环境配置
|
||||
├── .env.staging // staging环境配置
|
||||
├── .eslintrc.js // eslint配置
|
||||
├── babel.config.js // 转码配置
|
||||
├── .prettierrc // prettier配置
|
||||
├── jest.config.js // 测试配置
|
||||
├── package.json // 安装包列表文件
|
||||
├── postcss.config.js // 样式配置
|
||||
├── vue.config.js // vue工程配置
|
||||
.
|
||||
```
|
||||
|
||||
## 项目运行
|
||||
|
||||
```bash
|
||||
git clone http://112.124.100.131/base/ebiz-base-vue-h5.git
|
||||
|
||||
cd ebiz-base-vue-h5
|
||||
|
||||
yarn install(推荐使用yarn)
|
||||
|
||||
yarn serve(开发环境)
|
||||
|
||||
```
|
||||
|
||||
## 项目打包
|
||||
|
||||
```bash
|
||||
# 测试环境打包
|
||||
yarn run build:envName(测试环境打包)
|
||||
|
||||
# 线上环境打包
|
||||
yarn run build:prd(线上环境打包)
|
||||
```
|
||||
|
||||
## 预发环境打包预览、文件检查等
|
||||
|
||||
```bash
|
||||
# 代码检查
|
||||
yarn run lint
|
||||
|
||||
# 代码检查修复
|
||||
yarn run lint:fix
|
||||
|
||||
#代码格式化
|
||||
yarn run format
|
||||
```
|
||||
|
||||
## 快速开始项目
|
||||
|
||||
1、src>>api 目录下创建项目所需 http 请求文件夹
|
||||
2、src>>assets 目录下创建项目所需的 js image 文件夹,以及 scss 文件
|
||||
3、src>>router 目录下创建所需路由文件夹
|
||||
4、src>>store>>modules 目录下创建所需状态管理文件
|
||||
|
||||
##### [注:项目开始前请先配置 vscode 代码检查、格式化](http://115.29.19.195:8090/pages/viewpage.action?pageId=13960404)
|
||||
|
||||
## 主要功能点介绍
|
||||
|
||||
- http 请求文件
|
||||
- 模块划分:app 为基础工程模块、其他项目需另建文件夹
|
||||
- 拦截器:超时设置、请求头加 token、统一处理异常、token 异常重新登录等
|
||||
- mock 数据
|
||||
- [工具 rap2](http://rap2.taobao.org/) 、[参考链接](http://115.29.19.195:8090/display/VUE/mock)
|
||||
- 通过 config 文件夹下 urlMap.js,配置 mock 接口请求
|
||||
- 环境配置
|
||||
- 在 config 文件夹下 index.js,通过 process.env.NODE_ENV 对环境进行配置
|
||||
- 在以下根目录env.xxx文件内配置相关环境变量
|
||||
- env.dev 开发环境
|
||||
- env.test 测试环境
|
||||
- env.uat 回归环境
|
||||
- env.prd 生产环境
|
||||
- 跨域问题可通过后台解决,也可设置前端代理 proxy
|
||||
- router
|
||||
- 模块划分
|
||||
- 路由拦截
|
||||
- 路由懒加载
|
||||
- 权限控制
|
||||
- 可在 permission.js 里设置黑白名单
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div class="hot-products">
|
||||
<h2>热销产品</h2>
|
||||
<ul>
|
||||
<li v-for="(product, index) in hotProducts" :key="index">
|
||||
<div class="product-card">
|
||||
<img :src="product.image" :alt="product.name" />
|
||||
<h3>{{ product.name }}</h3>
|
||||
<p>{{ product.description }}</p>
|
||||
<button>查看详情</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hot-products {
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
h2 {
|
||||
color: #2E5CA9;
|
||||
font-size: 24px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
overflow-x: auto; /* 添加横向滚动 */
|
||||
white-space: nowrap; /* 防止换行 */
|
||||
}
|
||||
|
||||
.product-card {
|
||||
width: 200px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #2E5CA9;
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: #1E4082;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
14
babel.config.js
Normal file
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
presets: [['@vue/app', { useBuiltIns: 'entry' }]],
|
||||
plugins: [
|
||||
[
|
||||
'import',
|
||||
{
|
||||
libraryName: 'vant',
|
||||
libraryDirectory: 'es',
|
||||
style: true
|
||||
},
|
||||
'vant'
|
||||
]
|
||||
]
|
||||
}
|
||||
29
index.html
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://g.alicdn.com/dingding/dingtalk-jsapi/3.0.25/dingtalk.open.js"></script>
|
||||
<title>OA智能助理</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>欢迎来到OA智能助理</h1>
|
||||
<p id="info"></p>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
dd.ready(function () {
|
||||
|
||||
dd.runtime.permission.requestAuthCode({
|
||||
corpId: "dinga483783a6e936d79", // 企业id
|
||||
onSuccess: function (info) {
|
||||
code = info.code // 通过该免登授权码可以获取用户身份
|
||||
$("#info").html(info);
|
||||
alert(JSON.stringfy(info));
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
16
jest.config.js
Normal file
@@ -0,0 +1,16 @@
|
||||
module.exports = {
|
||||
moduleFileExtensions: ['js', 'jsx', 'json', 'vue'],
|
||||
transform: {
|
||||
'^.+\\.vue$': 'vue-jest',
|
||||
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
|
||||
'^.+\\.jsx?$': 'babel-jest'
|
||||
},
|
||||
transformIgnorePatterns: ['/node_modules/'],
|
||||
moduleNameMapper: {
|
||||
'^@/(.*)$': '<rootDir>/src/$1'
|
||||
},
|
||||
snapshotSerializers: ['jest-serializer-vue'],
|
||||
testMatch: ['**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'],
|
||||
testURL: 'http://localhost/',
|
||||
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname']
|
||||
}
|
||||
60
package.json
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "basic-h5",
|
||||
"version": "1.0.0",
|
||||
"description": "h5 basic",
|
||||
"author": "ebiz-digits",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"build:dev": "vue-cli-service build --mode dev",
|
||||
"build:prd": "vue-cli-service build --mode prd",
|
||||
"dev": "vue-cli-service serve --mode dev",
|
||||
"prd": "vue-cli-service serve --mode prd",
|
||||
"lint": "vue-cli-service lint",
|
||||
"lint:fix": "eslint --fix --ext .js,.vue src",
|
||||
"format": "prettier --config ./.prettierrc --write \"src/**/*.js\" \"src/**/*.vue\"",
|
||||
"test:unit": "vue-cli-service test:unit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@better-scroll/core": "^2.5.1",
|
||||
"axios": "^0.19.0",
|
||||
"core-js": "^2.6.5",
|
||||
"dingtalk-jsapi": "^3.0.38",
|
||||
"eruda": "^2.11.3",
|
||||
"fastclick": "^1.0.6",
|
||||
"markdown-it": "^12.3.2",
|
||||
|
||||
"markdown-it-katex": "^2.0.3",
|
||||
"sass": "^1.69.3",
|
||||
"svg-sprite-loader": "^6.0.11",
|
||||
"swiper": "^5.4.5",
|
||||
"v-viewer": "^1.6.4",
|
||||
"vant": "^2.12.54",
|
||||
"vue": "^2.6.10",
|
||||
"vue-pdf": "^4.3.0",
|
||||
"vue-quill-editor": "^3.0.6",
|
||||
"vue-router": "^3.5.2",
|
||||
"vuex": "^3.0.1",
|
||||
"weixin-js-sdk": "^1.4.0-test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.9.0",
|
||||
"@vue/cli-plugin-eslint": "^3.9.0",
|
||||
"@vue/cli-plugin-unit-jest": "^3.9.0",
|
||||
"@vue/cli-service": "^3.9.0",
|
||||
"@vue/eslint-config-prettier": "^4.0.1",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-jest": "^23.6.0",
|
||||
"babel-plugin-import": "^1.12.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-prettier": "^3.1.0",
|
||||
"eslint-plugin-vue": "^5.0.0",
|
||||
"postcss-px-to-viewport": "^1.1.1",
|
||||
"sass-loader": "^7.1.0",
|
||||
"vee-validate": "^2.0.0-rc.25",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
}
|
||||
}
|
||||
5
postcss.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
212
public/css/pdfh5.css
Normal file
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
25
public/index.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script>
|
||||
this.globalThis || (this.globalThis = this)
|
||||
</script>
|
||||
<title>易商智汇</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
|
||||
<meta name="ccbpi" content="ebiz" />
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
|
||||
<link rel="stylesheet" href="css/pdfh5.css" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but h5-standard doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<script>
|
||||
</script>
|
||||
70
src/App.vue
Normal file
@@ -0,0 +1,70 @@
|
||||
<template>
|
||||
<div id="app">
|
||||
<!--实现路由切换动画-->
|
||||
<transition :name="transitionName">
|
||||
<RouterView />
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import '@/assets/js/ddSdk'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
transitionName: ''
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
watch: {
|
||||
$route(to, from) {
|
||||
//首次加载去除动画
|
||||
if (!from.name) return
|
||||
//动画方式
|
||||
if (to.meta.index > from.meta.index) {
|
||||
this.transitionName = 'slide-left'
|
||||
} else if (to.meta.index === from.meta.index) {
|
||||
this.transitionName = ''
|
||||
} else {
|
||||
this.transitionName = 'slide-right'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '@/assets/sass/common.scss';
|
||||
#app {
|
||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
color: #2c3e50;
|
||||
font-size: 14px;
|
||||
}
|
||||
.slide-left-leave-active,
|
||||
.slide-left-enter-active,
|
||||
.slide-right-leave-active,
|
||||
.slide-right-enter-active {
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.slide-left-enter,
|
||||
.slide-right-leave-to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
.slide-left-enter-to,
|
||||
.slide-left-leave,
|
||||
.slide-right-enter-to,
|
||||
.slide-right-leave {
|
||||
transform: translateX(0);
|
||||
}
|
||||
.slide-left-leave-to,
|
||||
.slide-right-enter {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
</style>
|
||||
38
src/api/generatedApi/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import request from '@/assets/js/utils/request'
|
||||
import getUrl from '@/assets/js/utils/get-url'
|
||||
export function haslProducts(data) {
|
||||
// 在售列表
|
||||
return request({
|
||||
url: getUrl('/hasl/products'),
|
||||
method: 'get',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function productDetail(params) {
|
||||
// 产品详情(百宝箱)
|
||||
return request({
|
||||
url: getUrl('/hasl/product/detail'),
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export function chatProduct(data) {
|
||||
//聊天获取产品百宝箱
|
||||
// 聊天框输入内容以“百宝箱”结尾时,调用这个接口
|
||||
return request({
|
||||
url: getUrl('/hasl/chat/product'),
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export function chat(data) {
|
||||
//聊天获取产品百宝箱
|
||||
// 聊天框输入内容以“百宝箱”结尾时,调用这个接口
|
||||
// isDeep: 是否开启深度思考 0否1是
|
||||
// isOnline: 是否开启联网搜索 0否1是
|
||||
|
||||
return getUrl('/hasl/chat')
|
||||
}
|
||||
BIN
src/assets/images/404.jpg
Normal file
|
After Width: | Height: | Size: 871 KiB |
14
src/assets/js/business-common.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 业务公用js,根据app具体场景写公用方法
|
||||
*/
|
||||
// 截取地址栏的参数值
|
||||
function getUrlParam(data, href) {
|
||||
let hrefUrl = href ? href : window.location.href
|
||||
const reg = new RegExp('(^|&)' + data + '=([^&]*)(&|$)')
|
||||
var r = hrefUrl.substr(1).match(reg)
|
||||
if (r != null) {
|
||||
return decodeURIComponent(r[2])
|
||||
}
|
||||
return ''
|
||||
}
|
||||
export default getUrlParam
|
||||
492
src/assets/js/common.js
Normal file
@@ -0,0 +1,492 @@
|
||||
/**
|
||||
* @desc 扩展对象继承
|
||||
* @param {Object} out 一个或多个对象
|
||||
* @return {Object} 对象
|
||||
*/
|
||||
Object.extend = function(out) {
|
||||
out = out || {}
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
if (!arguments[i]) continue
|
||||
|
||||
for (var key in arguments[i]) {
|
||||
if (arguments[i].hasOwnProperty(key)) out[key] = arguments[i][key]
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* 对Date的扩展,将 Date 转化为指定格式的String
|
||||
* 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符,
|
||||
* 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
|
||||
* 例子:
|
||||
* (new Date()).format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
|
||||
* (new Date()).format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
|
||||
*/
|
||||
Date.prototype.format = function(format) {
|
||||
let args = {
|
||||
'M+': this.getMonth() + 1,
|
||||
'd+': this.getDate(),
|
||||
'h+': this.getHours(),
|
||||
'm+': this.getMinutes(),
|
||||
's+': this.getSeconds(),
|
||||
'q+': Math.floor((this.getMonth() + 3) / 3), // quarter
|
||||
S: this.getMilliseconds()
|
||||
}
|
||||
if (/(y+)/.test(format)) format = format.replace(RegExp.$1, (this.getFullYear() + '').substr(4 - RegExp.$1.length))
|
||||
for (var i in args) {
|
||||
var n = args[i]
|
||||
if (new RegExp('(' + i + ')').test(format))
|
||||
format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? n : ('00' + n).substr(('' + n).length))
|
||||
}
|
||||
return format
|
||||
}
|
||||
|
||||
export default {
|
||||
/**
|
||||
* @desc 判断对象是否为空
|
||||
* @param {Object} o 对象
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isEmptyObject(o = {}) {
|
||||
let flag = true
|
||||
for (let k in o) {
|
||||
if (k) {
|
||||
flag = false
|
||||
break
|
||||
}
|
||||
}
|
||||
return flag
|
||||
},
|
||||
//获取当前url的所有参数
|
||||
getParams() {
|
||||
let url = location.href.split('?')[1]
|
||||
if (!url) {
|
||||
return {}
|
||||
}
|
||||
let params = {}
|
||||
let arr = url.indexOf('&') != -1 ? url.split('&') : [url]
|
||||
arr.map(item => {
|
||||
params[item.split('=')[0]] = item.split('=')[1]
|
||||
})
|
||||
return params
|
||||
},
|
||||
// 时间格式转化
|
||||
|
||||
formatDate(date, fmt) {
|
||||
date = date == undefined ? new Date() : date
|
||||
|
||||
date = typeof date == 'number' ? new Date(date) : date
|
||||
|
||||
fmt = fmt || 'yyyy-MM-dd HH:mm:ss'
|
||||
|
||||
var obj = {
|
||||
y: date.getFullYear(), // 年份,注意必须用getFullYear
|
||||
|
||||
M: date.getMonth() + 1, // 月份,注意是从0-11
|
||||
|
||||
d: date.getDate(), // 日期
|
||||
|
||||
q: Math.floor((date.getMonth() + 3) / 3), // 季度
|
||||
|
||||
w: date.getDay(), // 星期,注意是0-6
|
||||
|
||||
H: date.getHours(), // 24小时制
|
||||
|
||||
h: date.getHours() % 12 == 0 ? 12 : date.getHours() % 12, // 12小时制
|
||||
|
||||
m: date.getMinutes(), // 分钟
|
||||
|
||||
s: date.getSeconds(), // 秒
|
||||
|
||||
S: date.getMilliseconds() // 毫秒
|
||||
}
|
||||
|
||||
var week = ['天', '一', '二', '三', '四', '五', '六']
|
||||
|
||||
for (var i in obj) {
|
||||
fmt = fmt.replace(new RegExp(i + '+', 'g'), function(m) {
|
||||
var val = obj[i] + ''
|
||||
|
||||
if (i == 'w') return (m.length > 2 ? '星期' : '周') + week[val]
|
||||
|
||||
for (var j = 0, len = val.length; j < m.length - len; j++) val = '0' + val
|
||||
|
||||
return m.length == 1 ? val : val.substring(val.length - m.length)
|
||||
})
|
||||
}
|
||||
|
||||
return fmt
|
||||
},
|
||||
|
||||
/**
|
||||
* @description 去除前后空格
|
||||
* @param {String} 值
|
||||
* @return {String}
|
||||
*/
|
||||
trim(val) {
|
||||
return val.replace(/(^\s*)|(\s*$)/g, '')
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 获取 cookie
|
||||
* @param {String}
|
||||
* @return {*}
|
||||
*/
|
||||
getCookie(name) {
|
||||
let rs = ''
|
||||
var nameStr = name + '='
|
||||
var ca = document.cookie.split(';')
|
||||
for (var i = 0; i < ca.length; i++) {
|
||||
var c = ca[i]
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1)
|
||||
}
|
||||
if (c.indexOf(nameStr) != -1) {
|
||||
rs = this._string2json(c.substring(nameStr.length, c.length))
|
||||
}
|
||||
}
|
||||
return rs
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 设置 cookie
|
||||
* @param {String} name 名称
|
||||
* @param {*} value 值
|
||||
* @param {Number} hours 时长
|
||||
*/
|
||||
setCookie(name, value, hours) {
|
||||
let str = name + '=' + this._json2string(value)
|
||||
if (hours && hours > 0) {
|
||||
var date = new Date()
|
||||
date.setTime(date.getTime() + hours * 3600 * 1000)
|
||||
str += '; expires=' + date.toUTCString()
|
||||
}
|
||||
document.cookie = str
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 清除 cookie
|
||||
* @param {String} 名称
|
||||
*/
|
||||
delCookie(name) {
|
||||
var date = new Date()
|
||||
date.setTime(date.getTime() - 10000)
|
||||
document.cookie = name + '=a; expires=' + date.toGMTString()
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 获取 localStorage 中指定的变量
|
||||
* @param {String} name 名称
|
||||
* @return {*} rs
|
||||
*/
|
||||
getStorage(name) {
|
||||
return this._string2json(window.localStorage[name])
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 设置或添加 localStorage 中指定的变量
|
||||
* @param {String} name 名称
|
||||
*/
|
||||
setStorage(name, value) {
|
||||
window.localStorage[name] = this._json2string(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 删除 localStorage 中指定的变量
|
||||
* @param {String} name 名称
|
||||
*/
|
||||
delStorage(name) {
|
||||
window.localStorage.removeItem(name)
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc json转string
|
||||
* @param {*} value 值
|
||||
* @return {*} value 值
|
||||
*/
|
||||
_json2string(value) {
|
||||
return JSON.stringify(value)
|
||||
},
|
||||
/**
|
||||
* @desc string转json
|
||||
* @param {*} value 值
|
||||
* @return {*} value 值
|
||||
*/
|
||||
_string2json(value) {
|
||||
try {
|
||||
value = JSON.parse(value)
|
||||
} catch (e) {
|
||||
//console.log(e)
|
||||
}
|
||||
return value
|
||||
},
|
||||
/**
|
||||
*@desc 只能输入单词字符
|
||||
*@param { String } value
|
||||
*/
|
||||
character(value) {
|
||||
if (value) {
|
||||
value = value.replace(/[^A-Za-z0-9]/g, '')
|
||||
}
|
||||
return value
|
||||
},
|
||||
// 保留小数位,替代Number.toFixed()方法,针对于某些数据(16.455)不能做到四舍五入
|
||||
toFixed(value, num = 0) {
|
||||
let pos = value.toString().indexOf('.')
|
||||
let decimalPlaces = value.toString().length - pos - 1
|
||||
let intValue = value * Math.pow(10, decimalPlaces)
|
||||
let divisor1 = Math.pow(10, decimalPlaces - num)
|
||||
let divisor2 = Math.pow(10, num)
|
||||
return Math.round(intValue / divisor1) / divisor2
|
||||
},
|
||||
|
||||
/**
|
||||
* 如果是小数则保留小数位,默认两位
|
||||
* @param {[type]} value [description]
|
||||
* @param {Number} num [description]
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
toFloatFixed(value, num = 2) {
|
||||
let values = Number(value)
|
||||
if (values) {
|
||||
if (/^\d+\.\d+/.test(values)) {
|
||||
return this.toFixed(values, num)
|
||||
} else {
|
||||
return this.toFixed(values)
|
||||
}
|
||||
}
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* 获取设备类型
|
||||
*/
|
||||
device() {
|
||||
let ua = navigator.userAgent
|
||||
return {
|
||||
isChrome: ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/),
|
||||
isAndroid: ua.match(/(Android);?[\s/]+([\d.]+)?/),
|
||||
isIphone: ua.indexOf('iPhone') != -1,
|
||||
isWeixin: ua.match(/MicroMessenger/i)
|
||||
//isTraining: ua.match(/training/i) //当前app标识,按照项目来定
|
||||
}
|
||||
},
|
||||
isIOS() {
|
||||
var u = navigator.userAgent
|
||||
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) //ios终端
|
||||
return isiOS
|
||||
},
|
||||
/**
|
||||
* 设置title
|
||||
*/
|
||||
setTitle(title) {
|
||||
let device = this.device()
|
||||
if (device.isIphone && device.isWeixin) {
|
||||
document.title = title
|
||||
var i = document.createElement('iframe')
|
||||
i.src = '/favicon.ico'
|
||||
i.style.display = 'none'
|
||||
i.onload = function() {
|
||||
setTimeout(function() {
|
||||
i.remove()
|
||||
}, 9)
|
||||
}
|
||||
document.body.appendChild(i)
|
||||
} else {
|
||||
document.title = title
|
||||
}
|
||||
},
|
||||
|
||||
// 字符省略
|
||||
ellipsis(value = '', num = -1) {
|
||||
if (value) {
|
||||
let str = ''
|
||||
if (num > 0 && value.length > num) {
|
||||
str = '...'
|
||||
}
|
||||
return value.slice(0, num) + str
|
||||
}
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* 替换字符串具体位置字符
|
||||
* @param {String} str 目标字符串
|
||||
* @param {String} char 字符
|
||||
* @param {Number} start 数组开始索引
|
||||
* @param {Number} end 数组结束索引
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
replaceChar(str = '', char = '*', start = 0, end) {
|
||||
let list = []
|
||||
if (str) {
|
||||
list = str.split('')
|
||||
let len = list.length
|
||||
start = start > 0 ? (start <= len ? start : len) : 0
|
||||
end = end ? (end > start ? end : start) : len
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (i >= start && i < end) {
|
||||
list[i] = char
|
||||
}
|
||||
}
|
||||
}
|
||||
return list.join('')
|
||||
},
|
||||
/**
|
||||
* @desc:只能输入整数
|
||||
* @param
|
||||
value 操作数值
|
||||
max 最大值
|
||||
**/
|
||||
intOnly(value, max = Infinity, min = 0) {
|
||||
if (value) {
|
||||
let regular0 = /^0{1,}$/
|
||||
if (regular0.test(value)) {
|
||||
// 如果输入1个0以上,去除掉
|
||||
value = value.replace(/^0{1,}$/, '')
|
||||
}
|
||||
if (/[^0-9]*$/.test(value)) {
|
||||
value = value.replace(/\D/g, '')
|
||||
} else {
|
||||
if (value > max) value = value.substr(0, value.length - 1)
|
||||
if (value < min) value = min
|
||||
}
|
||||
}
|
||||
return value
|
||||
},
|
||||
// 数组去重
|
||||
unrepeat(arr) {
|
||||
if (arr instanceof Array) {
|
||||
let hash = {}
|
||||
let rsArr = []
|
||||
let len = arr.length
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let type = ''
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (typeof arr[i] == 'string') {
|
||||
type = 'string'
|
||||
}
|
||||
if (!hash[arr[i]]) {
|
||||
hash[arr[i]] = arr[i]
|
||||
rsArr.push(arr[i])
|
||||
}
|
||||
}
|
||||
return rsArr
|
||||
}
|
||||
},
|
||||
// 一层对象数组去重
|
||||
unrepeatObj(arr, key) {
|
||||
if (arr instanceof Array) {
|
||||
let hash = {}
|
||||
let rsArr = []
|
||||
let len = arr.length
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
let type = ''
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (typeof arr[i] == 'string') {
|
||||
type = 'string'
|
||||
}
|
||||
if (!hash[arr[i][key]]) {
|
||||
hash[arr[i][key]] = arr[i]
|
||||
rsArr.push(arr[i])
|
||||
}
|
||||
}
|
||||
return rsArr
|
||||
}
|
||||
},
|
||||
//距离转换
|
||||
m2km(value) {
|
||||
if (value < 1000) return value + 'm'
|
||||
else if (value >= 1000 && value <= 20000) return (value / 1000).toFixed(1) + 'km'
|
||||
else if (value >= 2000) return '>20km'
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* 获取App信息
|
||||
*/
|
||||
detectApp() {
|
||||
var ua = navigator.userAgent
|
||||
//RegExp.$1; RegExp.$2; RegExp.$3;
|
||||
//var info = ua.match(/(CarmeApp)\s*[v]*(\d+\.\d+\.\d+)\s*\/\s*(IOS|Android)/i)
|
||||
ua.match(/(CarmeApp)\s*[v]*(\d+\.\d+\.\d+)\s*\/\s*(IOS|Android)/i)
|
||||
return {
|
||||
appName: RegExp.$1,
|
||||
appVersion: RegExp.$2,
|
||||
appOS: RegExp.$3
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 手机号脱敏
|
||||
* @param {String} phoneNo
|
||||
* @returns
|
||||
*/
|
||||
desensitizePhoneNo(phoneNo) {
|
||||
if (!phoneNo || phoneNo.length !== 11) {
|
||||
return 'Invalid phone number'
|
||||
}
|
||||
return phoneNo.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2')
|
||||
},
|
||||
|
||||
/**
|
||||
* 证件号码脱敏
|
||||
* @param {String} idType 证件类型 B0101:, 其他:其他
|
||||
* @param {String} idNo 证件号码
|
||||
* @return {String} 脱敏后的证件号码 身份证号显示前6位、后3位,其余用*代替;其余账号显示第1位,后3位,其余用*代替
|
||||
*/
|
||||
desensitizeIdNo(idType, idNo) {
|
||||
if (!idNo) {
|
||||
return 'Invalid id number'
|
||||
}
|
||||
console.log('idType', idType)
|
||||
if (this.isIdCard(idType)) {
|
||||
// 身份证号显示前6位、后3位,其余用*代替
|
||||
return idNo.replace(/^(.{6})(?:\w+)(.{3})$/, '$1********$2')
|
||||
} else {
|
||||
// 其余账号显示第1位,后3位,其余用*代替
|
||||
return idNo.replace(/^(.)(?:\w+)(.{3})$/, '$1********$2')
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 证件类型是否是身份证
|
||||
* @param {String} idType
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isIdCard(idType) {
|
||||
return idType === 'B0101'
|
||||
},
|
||||
|
||||
/**
|
||||
* 证件类型是否是户口簿
|
||||
* @param {String} idType
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isHouseholdRegistration(idType) {
|
||||
return idType === 'B0200'
|
||||
},
|
||||
|
||||
/**
|
||||
* @desc 获取url中的参数
|
||||
* @param {String} url
|
||||
* @return {Object} 参数对象
|
||||
*/
|
||||
getParamsFromUrl(url) {
|
||||
console.log('getParamsFromUrl url', url)
|
||||
let params = {}
|
||||
if (!url) {
|
||||
return params
|
||||
}
|
||||
if (url.indexOf('?') == -1) {
|
||||
return params
|
||||
}
|
||||
if (url.indexOf('?') == url.length - 1) {
|
||||
return params
|
||||
}
|
||||
let arr = url.split('?')[1].split('&')
|
||||
console.log('getParamsFromUrl', arr)
|
||||
arr.map(item => {
|
||||
params[item.split('=')[0]] = item.split('=')[1]
|
||||
})
|
||||
return params
|
||||
}
|
||||
}
|
||||
32
src/assets/js/generatedFormat/index.js
Normal file
@@ -0,0 +1,32 @@
|
||||
export default {
|
||||
formatLabelFun: (value, dir) => {
|
||||
let arr = eval(`${dir}DataMaps`)
|
||||
let filterData = arr.filter(item => item.value == value)[0]
|
||||
if (filterData) {
|
||||
return filterData.label
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
|
||||
formatList: (dir) => {
|
||||
let maps = {}
|
||||
return maps[dir];
|
||||
},
|
||||
|
||||
// formatOptionsFun 固定生成
|
||||
// 用于转换list 增加自己想要的字段
|
||||
formatOptionsFun: function (array = [{label: 'name', formatLabel: 'label'}], data) {
|
||||
return data.map(item => {
|
||||
array.map(format => {
|
||||
if (item[format.label]) {
|
||||
item[format.formatLabel ? format.formatLabel : 'label'] = item[format.label]
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
item.children = this.formatOptionsFun(array, item.children)
|
||||
}
|
||||
})
|
||||
return item
|
||||
})
|
||||
}
|
||||
}
|
||||
24
src/assets/js/generatedValidate/generatedValidateItem.js
Normal file
@@ -0,0 +1,24 @@
|
||||
export default {
|
||||
// 默认存在的校验
|
||||
mobile: () => {
|
||||
return {
|
||||
message: () => {
|
||||
return '请输入正确的手机号'
|
||||
},
|
||||
validator: (value) => {
|
||||
return value.length === 11 && /^((12|13|14|15|16|17|18|19)[0-9]{1}\d{8})$/.test(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
required: () => {
|
||||
return {
|
||||
message: (file) => {
|
||||
return file + '不能为空'
|
||||
},
|
||||
validator: (value) => {
|
||||
//判断value 是不是array
|
||||
return Array.isArray(value) ? value.length > 0 : value !== ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/assets/js/generatedValidate/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import Vue from 'vue'
|
||||
import VeeValidate, { Validator } from 'vee-validate'
|
||||
import zh_CN from 'vee-validate/dist/locale/zh_CN'
|
||||
import validateItem from './generatedValidateItem'
|
||||
// 解决fileds重复定义报错的问题
|
||||
// 解决fileds重复定义报错的问题
|
||||
const config = {
|
||||
errorBagName: 'errorBags', // change if property conflicts.
|
||||
fieldsBagName: 'fieldBags'
|
||||
}
|
||||
|
||||
Vue.use(VeeValidate, config)
|
||||
Validator.locale = 'zh_CN'
|
||||
Validator.localize('zh_CN', {
|
||||
messages: zh_CN.messages
|
||||
})
|
||||
/*自定义校验规则*/
|
||||
for (let item in validateItem) {
|
||||
console.log(item)
|
||||
Validator.extend(item, {
|
||||
getMessage: validateItem[item]().message,
|
||||
validate: validateItem[item]().validator
|
||||
})
|
||||
}
|
||||
22
src/assets/js/utils/bus.js
Normal file
@@ -0,0 +1,22 @@
|
||||
//在组件销毁时应当删除订阅的事件,避免挂载多个事件
|
||||
class Bus {
|
||||
constructor() {
|
||||
this.callback = {}
|
||||
}
|
||||
on(name, val) {
|
||||
this.callback[name] = this.callback[name] || []
|
||||
this.callback[name].push(val)
|
||||
}
|
||||
async emit(name, args) {
|
||||
if (this.callback[name]) {
|
||||
return await Promise.all(this.callback[name].map(cb => cb(args)))
|
||||
}
|
||||
}
|
||||
off(name) {
|
||||
if (this.callback[name]) {
|
||||
delete this.callback[name]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Bus
|
||||
24
src/assets/js/utils/compatible.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import FastClick from 'fastclick'
|
||||
|
||||
//解决ios移动端input调软键盘问题
|
||||
let isIphone = navigator.userAgent.indexOf('iPhone') != -1
|
||||
if (isIphone) {
|
||||
FastClick.prototype.focus = function(targetElement) {
|
||||
let length
|
||||
if (
|
||||
isIphone &&
|
||||
targetElement.setSelectionRange &&
|
||||
targetElement.type.indexOf('date') !== 0 &&
|
||||
targetElement.type !== 'time' &&
|
||||
targetElement.type !== 'month' &&
|
||||
targetElement.type !== 'email'
|
||||
) {
|
||||
length = targetElement.value.length
|
||||
targetElement.setSelectionRange(length, length)
|
||||
/*修复bug ios 11.3不弹出键盘,这里加上聚焦代码,让其强制聚焦弹出键盘*/
|
||||
targetElement.focus()
|
||||
} else {
|
||||
targetElement.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/assets/js/utils/get-url.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import config from '@/config'
|
||||
import urlMap from '@/config/urlMap'
|
||||
|
||||
export default function getUrl(url, domainType = 'base') {
|
||||
let domain = ''
|
||||
if (domainType === 'base') {
|
||||
domain = config.base
|
||||
}
|
||||
if (domainType === 'ai') {
|
||||
domain = config.ai
|
||||
}
|
||||
return domain + url
|
||||
}
|
||||
24
src/assets/js/utils/permission.js
Normal file
@@ -0,0 +1,24 @@
|
||||
//权限控制
|
||||
import router from '@/router'
|
||||
export function permission() {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
document.title = to.meta.title
|
||||
//判断是否登录
|
||||
let token = localStorage.token
|
||||
if (!token) {
|
||||
localStorage.token = ''
|
||||
//无token,判断是否需要登录
|
||||
if (to.meta.auth) {
|
||||
next(`/login?redirect=${to.path}`)
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
if (to.path === '/login') {
|
||||
next({ path: '/' })
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
94
src/assets/js/utils/request.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import axios from 'axios'
|
||||
import { Dialog, Toast } from 'vant'
|
||||
import loading from '@/components/loading/loading.js'
|
||||
// 创建axios实例
|
||||
const service = axios.create({
|
||||
timeout: 66666666, // 请求超时时间
|
||||
})
|
||||
// request拦截器
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
console.log(config)
|
||||
if (!config.headers.noLoading) {
|
||||
Toast.loading({
|
||||
duration: 0,
|
||||
type: 'html',
|
||||
forbidClick: true,
|
||||
message: loading,
|
||||
})
|
||||
}
|
||||
config.headers['token'] = window.localStorage.getItem('token')
|
||||
return config
|
||||
},
|
||||
(error) => {
|
||||
Toast.clear()
|
||||
// Do something with request error
|
||||
Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
// respone拦截器
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
console.log(response, 23)
|
||||
if (response.config.sts) {
|
||||
return res
|
||||
}
|
||||
|
||||
// return false
|
||||
Toast.clear()
|
||||
// return false
|
||||
const res = response.data
|
||||
if (typeof res.sts !== 'undefined') {
|
||||
return res
|
||||
}
|
||||
if (res.code === '000' || res.code === '999') {
|
||||
return res
|
||||
}
|
||||
if (!res.content) {
|
||||
Toast({
|
||||
type: 'text',
|
||||
message: res.message ? res.message : '接口服务异常',
|
||||
})
|
||||
return false
|
||||
}
|
||||
res.content.code = res.content ? res.content.result : null
|
||||
if (res.content && res.content.code != 0) {
|
||||
// if(!)
|
||||
//noFail 表示不需要统一处理错误提示,需要给调用接口返回
|
||||
if (!response.config.noFail) {
|
||||
if (res.content.code == 40001) {
|
||||
Dialog.confirm({
|
||||
confirmButtonText: '重新登录',
|
||||
message: '你已被登出,可以取消继续留在该页面,或者重新登录',
|
||||
}).then(() => {
|
||||
//eslint-disable-next-line
|
||||
EWebBridge.webCallAppInJs('bridge', { flag: 'login' })
|
||||
})
|
||||
} else {
|
||||
Toast(res.content.message ? res.content.message : res.content.resultMessage ? res.content.resultMessage : '未知异常')
|
||||
}
|
||||
return Promise.reject(res)
|
||||
} else {
|
||||
return res.content
|
||||
}
|
||||
} else {
|
||||
// Toast.clear()
|
||||
if (res.content) {
|
||||
return res.content
|
||||
} else {
|
||||
Toast({
|
||||
icon: 'warning-o',
|
||||
message: '接口服务异常',
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.log(error)
|
||||
Toast(error.message ? error.message : '未知异常')
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
||||
export default service
|
||||
53
src/assets/sass/common.scss
Normal file
@@ -0,0 +1,53 @@
|
||||
@import './variables.scss';
|
||||
@import './vant-ui.scss';
|
||||
@import './mixin.scss';
|
||||
@import './utils.scss';
|
||||
@import './transition.scss';
|
||||
@import './public.scss';
|
||||
@import "./loading.scss";
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
touch-action: pan-y;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100%;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
|
||||
background-color: rgb(245, 245, 245);
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
a,
|
||||
a:focus,
|
||||
a:hover {
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
::-webkit-scrollbar{
|
||||
display: none;
|
||||
}
|
||||
// 宽度设置
|
||||
@include set-width($width-list);
|
||||
// 宽度设置
|
||||
@include set-height($width-list);
|
||||
// 间距设置
|
||||
@include set-distance($distance-class-list, $distance-list);
|
||||
//圆角弧度设置
|
||||
@include set-radius($radius);
|
||||
//透明度设置
|
||||
@include set-opacity($opacity);
|
||||
//字体设置
|
||||
@include set-fontSize($font-size-list);
|
||||
|
||||
//间距设置,遍历间距class 和 倍数, 用于padding和margin
|
||||
//@include set-distance($spacing-class-list, $spacing-multiple-list);
|
||||
206
src/assets/sass/loading.scss
Normal file
@@ -0,0 +1,206 @@
|
||||
.van-toast--html{
|
||||
background: unset!important;
|
||||
z-index: 3000!important;
|
||||
}
|
||||
.render-loading{
|
||||
.container {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
margin: auto;
|
||||
filter: url("#goo");
|
||||
animation: rotate-move 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
background-color: #000;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.dot-3 {
|
||||
background-color: #ff1717;
|
||||
animation: dot-3-move 2s ease infinite, index 6s ease infinite;
|
||||
}
|
||||
|
||||
.dot-2 {
|
||||
background-color: #0051ff;
|
||||
animation: dot-2-move 2s ease infinite, index 6s -4s ease infinite;
|
||||
}
|
||||
|
||||
.dot-1 {
|
||||
background-color: #ffc400;
|
||||
animation: dot-1-move 2s ease infinite, index 6s -2s ease infinite;
|
||||
}
|
||||
|
||||
@keyframes dot-3-move {
|
||||
20% {
|
||||
transform: scale(1);
|
||||
}
|
||||
45% {
|
||||
transform: translateY(-18px) scale(0.45);
|
||||
}
|
||||
60% {
|
||||
transform: translateY(-90px) scale(0.45);
|
||||
}
|
||||
80% {
|
||||
transform: translateY(-90px) scale(0.45);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dot-2-move {
|
||||
20% {
|
||||
transform: scale(1);
|
||||
}
|
||||
45% {
|
||||
transform: translate(-16px, 12px) scale(0.45);
|
||||
}
|
||||
60% {
|
||||
transform: translate(-80px, 60px) scale(0.45);
|
||||
}
|
||||
80% {
|
||||
transform: translate(-80px, 60px) scale(0.45);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes dot-1-move {
|
||||
20% {
|
||||
transform: scale(1);
|
||||
}
|
||||
45% {
|
||||
transform: translate(16px, 12px) scale(0.45);
|
||||
}
|
||||
60% {
|
||||
transform: translate(80px, 60px) scale(0.45);
|
||||
}
|
||||
80% {
|
||||
transform: translate(80px, 60px) scale(0.45);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0px) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotate-move {
|
||||
55% {
|
||||
transform: translate(-50%, -50%) rotate(0deg);
|
||||
}
|
||||
80% {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes index {
|
||||
0%,
|
||||
100% {
|
||||
z-index: 3;
|
||||
}
|
||||
33.3% {
|
||||
z-index: 2;
|
||||
}
|
||||
66.6% {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//speakLoading
|
||||
|
||||
.loader3 {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.bars {
|
||||
width: 10px;
|
||||
height: 20px;
|
||||
margin: 0 2px;
|
||||
border-radius: 4px;
|
||||
animation: loader3 1.2s ease-in-out infinite;
|
||||
}
|
||||
.bar1 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.8s;
|
||||
}
|
||||
.bar2 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.7s;
|
||||
}
|
||||
|
||||
.bar3 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.6s;
|
||||
}
|
||||
|
||||
.bar4 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.5s;
|
||||
}
|
||||
|
||||
.bar5 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.4s;
|
||||
}
|
||||
|
||||
.bar6 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.3s;
|
||||
}
|
||||
|
||||
.bar7 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.2s;
|
||||
}
|
||||
|
||||
.bar8 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: -0.1s;
|
||||
}
|
||||
|
||||
.bar9 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.bar10 {
|
||||
background-color: #4285F4;
|
||||
animation-delay: 0.1s;
|
||||
}
|
||||
|
||||
@keyframes loader3 {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
20% {
|
||||
transform: scale(1, 2.32);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
353
src/assets/sass/mixin.scss
Normal file
@@ -0,0 +1,353 @@
|
||||
@mixin clearfix {
|
||||
&:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin scrollBar {
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: #d3dce6;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #99a9bf;
|
||||
border-radius: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin relative {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// 设置宽度
|
||||
@mixin set-width($width-list) {
|
||||
@each $size in $width-list {
|
||||
.w#{$size} {
|
||||
width: #{$size}px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置宽度
|
||||
@mixin set-fontSize($font-size-list) {
|
||||
@each $size in $font-size-list {
|
||||
.fs#{$size} {
|
||||
font-size: #{$size}px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置高度
|
||||
@mixin set-height($width-list) {
|
||||
@each $size in $width-list {
|
||||
.h#{$size} {
|
||||
height: #{$size}px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置字体宽度
|
||||
@mixin set-height($font-weight-list) {
|
||||
@each $size in $font-weight-list {
|
||||
.fw#{$size} {
|
||||
font-weight: #{$size}px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置圆角弧度
|
||||
@mixin set-radius($radius) {
|
||||
@each $size in $radius {
|
||||
.radius#{$size} {
|
||||
border-radius: #{$size}px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 设置透明度
|
||||
@mixin set-opacity($opacity) {
|
||||
@each $size in $opacity {
|
||||
.opacity#{$size*10} {
|
||||
opacity: #{$size} !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 外边距
|
||||
@mixin margin($size) {
|
||||
margin: $size !important;
|
||||
}
|
||||
|
||||
@mixin margin-vertical($size) {
|
||||
margin-top: $size !important;
|
||||
margin-bottom: $size !important;
|
||||
}
|
||||
|
||||
@mixin margin-horizontal($size) {
|
||||
margin-left: $size !important;
|
||||
margin-right: $size !important;
|
||||
}
|
||||
|
||||
@mixin margin-top($size) {
|
||||
margin-top: $size !important;
|
||||
}
|
||||
|
||||
@mixin margin-left($size) {
|
||||
margin-left: $size !important;
|
||||
}
|
||||
|
||||
@mixin margin-bottom($size) {
|
||||
margin-bottom: $size !important;
|
||||
}
|
||||
|
||||
@mixin margin-right($size) {
|
||||
margin-right: $size !important;
|
||||
}
|
||||
|
||||
// 内边距
|
||||
@mixin padding($size) {
|
||||
padding: $size !important;
|
||||
}
|
||||
|
||||
@mixin padding-vertical($size) {
|
||||
padding-top: $size !important;
|
||||
padding-bottom: $size !important;
|
||||
}
|
||||
|
||||
@mixin padding-horizontal($size) {
|
||||
padding-left: $size !important;
|
||||
padding-right: $size !important;
|
||||
}
|
||||
|
||||
@mixin padding-top($size) {
|
||||
padding-top: $size !important;
|
||||
}
|
||||
|
||||
@mixin padding-left($size) {
|
||||
padding-left: $size !important;
|
||||
}
|
||||
|
||||
@mixin padding-bottom($size) {
|
||||
padding-bottom: $size !important;
|
||||
}
|
||||
|
||||
@mixin padding-right($size) {
|
||||
padding-right: $size !important;
|
||||
}
|
||||
|
||||
@mixin top($size) {
|
||||
top: $size !important;
|
||||
}
|
||||
|
||||
@mixin left($size) {
|
||||
left: $size !important;
|
||||
}
|
||||
|
||||
@mixin right($size) {
|
||||
right: $size !important;
|
||||
}
|
||||
|
||||
@mixin bottom($size) {
|
||||
bottom: $size !important;
|
||||
}
|
||||
|
||||
// 设置内外边距
|
||||
@mixin set-distance($distance-class-list, $distance-list) {
|
||||
@each $class-name in $distance-class-list {
|
||||
@each $size in $distance-list {
|
||||
.#{$class-name}#{$size} {
|
||||
@if $class-name == m {
|
||||
@include margin(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name == mv {
|
||||
@include margin-vertical(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==mh {
|
||||
@include margin-horizontal(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==mt {
|
||||
@include margin-top(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==ml {
|
||||
@include margin-left(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==mr {
|
||||
@include margin-right(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==mb {
|
||||
@include margin-bottom(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==p {
|
||||
@include padding(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==pv {
|
||||
@include padding-vertical(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==ph {
|
||||
@include padding-horizontal(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==pt {
|
||||
@include padding-top(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==pl {
|
||||
@include padding-left(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==pr {
|
||||
@include padding-right(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==pb {
|
||||
@include padding-bottom(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==top {
|
||||
@include top(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==left {
|
||||
@include left(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==right {
|
||||
@include right(#{$size}px);
|
||||
}
|
||||
|
||||
@if $class-name ==bottom {
|
||||
@include bottom(#{$size}px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// 设置内外边距
|
||||
//@mixin set-distance($class-list, $multiple-list){
|
||||
// @each $class in $class-list {
|
||||
// @each $multiple in $multiple-list {
|
||||
// @include generate-spacing($class, $multiple);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 根据倍数设置内外间距 class可以为 m, mv, mh, mt, ml, mr, mb, p, pv, ph, pt, pl, pr, pb
|
||||
//@mixin generate-spacing($class, $multiple) {
|
||||
// $property: null;
|
||||
// $directions: (
|
||||
// );
|
||||
//
|
||||
//// 根据class首字母判断是margin还是padding
|
||||
//@if str-index($class, 'm') {
|
||||
// $property: margin;
|
||||
//}
|
||||
//
|
||||
//@else if str-index($class, 'p') {
|
||||
// $property: padding;
|
||||
//}
|
||||
//
|
||||
//@if str-index($class, 'v') {
|
||||
// $directions: (
|
||||
// top,
|
||||
// bottom
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//@else if str-index($class, 'h') {
|
||||
// $directions: (
|
||||
// left,
|
||||
// right
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//@else if str-index($class, 't') {
|
||||
// $directions: (
|
||||
// top
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//@else if str-index($class, 'b') {
|
||||
// $directions: (
|
||||
// bottom
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//@else if str-index($class, 'l') {
|
||||
// $directions: (
|
||||
// left
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//@else if str-index($class, 'r') {
|
||||
// $directions: (
|
||||
// right
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//@else {
|
||||
// $directions: (
|
||||
// top,
|
||||
// right,
|
||||
// bottom,
|
||||
// left
|
||||
// );
|
||||
//}
|
||||
//
|
||||
//// 生成对应的 class
|
||||
//@each $direction in $directions {
|
||||
// .#{$class}-#{str-replace($multiple + 'x', '.', '_')} {
|
||||
// #{$property}-#{$direction}: $base-spacing * $multiple;
|
||||
// }
|
||||
//}
|
||||
//}
|
||||
|
||||
// 把间距class中名字中带 . 的替换成 _,避免class名字带点引起歧义和风险
|
||||
@function str-replace($string, $search, $replace: '') {
|
||||
$index: str-index($string, $search);
|
||||
|
||||
@if($index) {
|
||||
@return str-slice($string, 1, $index - 1)+'_'+str-slice($string, $index + 1);
|
||||
}
|
||||
|
||||
@return $string;
|
||||
}
|
||||
|
||||
//单行省略
|
||||
@mixin text-overflow() {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@mixin horizontal-margin($size) {
|
||||
margin-left: $size ;
|
||||
margin-right: $size
|
||||
}
|
||||
|
||||
@mixin border-radius($radius) {
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
@mixin btn-sizes($btn-height, $font-size) {
|
||||
height: $btn-height;
|
||||
font-size: $font-size;
|
||||
//@include border-radius($btn-height/2);
|
||||
}
|
||||
58
src/assets/sass/public.scss
Normal file
@@ -0,0 +1,58 @@
|
||||
body{
|
||||
background-color: #EEEEEE!important;
|
||||
}
|
||||
.van-cell-group--inset{
|
||||
margin:0 10px!important;
|
||||
}
|
||||
.van-cell-group__title--inset{
|
||||
padding:15px 15px 5px 15px!important;
|
||||
}
|
||||
.van-toast--text {
|
||||
max-width: 70vw !important;
|
||||
min-width: 60vw !important;
|
||||
}
|
||||
.button-group-container{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
#app .van-col{
|
||||
margin: 5px 0;
|
||||
}
|
||||
#app .button-group-container{
|
||||
margin: 5px!important;
|
||||
.van-button{
|
||||
margin: unset;
|
||||
}
|
||||
}
|
||||
#app .van-button + .van-button{
|
||||
margin-left: 5px;
|
||||
}
|
||||
#app .van-cell__title{
|
||||
color: #646566;
|
||||
}
|
||||
|
||||
|
||||
.flexPrice{
|
||||
display: flex; justify-content: space-between; justify-items: center; align-items: center; width: 100%
|
||||
}
|
||||
.van-tag+.van-tag{margin-left: 5px;}
|
||||
.speakLoadingToast{
|
||||
z-index: 2100!important;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.gs-color-g{
|
||||
color: #009960;
|
||||
}
|
||||
.gs-bg-color-g{
|
||||
background: #009960;
|
||||
}
|
||||
|
||||
|
||||
.chat-icon{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
48
src/assets/sass/transition.scss
Normal file
@@ -0,0 +1,48 @@
|
||||
// global transition css
|
||||
|
||||
/* fade */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.28s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.fade-transform-enter {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
/* breadcrumb transition */
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.breadcrumb-move {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
665
src/assets/sass/utils.scss
Normal file
@@ -0,0 +1,665 @@
|
||||
//浮动、清除浮动
|
||||
.clearfix {
|
||||
@include clearfix;
|
||||
}
|
||||
|
||||
.fl {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fr {
|
||||
float: right;
|
||||
}
|
||||
|
||||
//字体相关
|
||||
.fs12 {
|
||||
font-size: $font-size-small;
|
||||
}
|
||||
|
||||
.fs14 {
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
|
||||
.fs16 {
|
||||
font-size: $font-size-large;
|
||||
}
|
||||
|
||||
.fs18 {
|
||||
font-size: $font-size-xl;
|
||||
}
|
||||
|
||||
.fw600 {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.fwb {
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.ffP {
|
||||
font-family: PingFangSC-Regular, PingFang SC;
|
||||
;
|
||||
}
|
||||
|
||||
// 宽度
|
||||
.w36 {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
//布局定位
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.absolute {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.mh-auto {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.hide {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: -webkit-flex !important;
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.flex1 {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.inline-flex {
|
||||
display: -webkit-inline-flex;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.inline-b {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.justify-content-c {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-content-a {
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-content-e {
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.justify-content-fs {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.justify-content-fe {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-content-b {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.align-items-c {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-c {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.flex-column-b {
|
||||
@extend .flex-column;
|
||||
justify-content: space-between;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.flex-column-c {
|
||||
@extend .flex-column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-row-c {
|
||||
@extend .flex-row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-row-e {
|
||||
@extend .flex-row;
|
||||
//justify-content: flex-end;
|
||||
//align-items: center;
|
||||
}
|
||||
|
||||
.flex-row-b {
|
||||
@extend .flex-row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// 常用文字颜色
|
||||
.c-gray-base {
|
||||
color: $gray-base !important;
|
||||
}
|
||||
|
||||
.c-gray-dark {
|
||||
color: $gray-dark !important;
|
||||
}
|
||||
|
||||
.c-gray-darker {
|
||||
color: $gray-darker !important;
|
||||
}
|
||||
|
||||
.gray-darker-cell {
|
||||
color: $gray-darker-cell !important;
|
||||
}
|
||||
|
||||
// 文字位置设置
|
||||
.text-left {
|
||||
text-align: left !important;
|
||||
|
||||
.van-field__control {
|
||||
text-align: left !important;
|
||||
}
|
||||
}
|
||||
|
||||
.text-right {
|
||||
text-align: right !important;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.text-underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.text-indent {
|
||||
text-indent: 20px;
|
||||
}
|
||||
|
||||
// 垂直方向对齐方式
|
||||
.v-top {
|
||||
vertical-align: top !important;
|
||||
}
|
||||
|
||||
.v-middle {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.v-bottom {
|
||||
vertical-align: bottom !important;
|
||||
}
|
||||
|
||||
.v-baseline {
|
||||
vertical-align: baseline !important;
|
||||
}
|
||||
|
||||
//单行省略
|
||||
.ellipsis {
|
||||
@include text-overflow;
|
||||
}
|
||||
|
||||
//颜色
|
||||
.green {
|
||||
color: $green !important;
|
||||
}
|
||||
|
||||
.red {
|
||||
color: $red !important;
|
||||
}
|
||||
|
||||
.reddish {
|
||||
color: #E8443C !important;
|
||||
}
|
||||
|
||||
.yellow {
|
||||
color: $yellow !important;
|
||||
}
|
||||
|
||||
.gray {
|
||||
color: $gray !important;
|
||||
}
|
||||
|
||||
.white {
|
||||
color: $white !important;
|
||||
}
|
||||
|
||||
.orange {
|
||||
color: $orange !important;
|
||||
}
|
||||
|
||||
.blue {
|
||||
color: $blue !important;
|
||||
}
|
||||
|
||||
.bg-black {
|
||||
background: $black !important;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
background: $white !important;
|
||||
}
|
||||
|
||||
.bg-orange {
|
||||
background-color: $orange !important;
|
||||
}
|
||||
|
||||
.bg-none {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.bg-primary {
|
||||
background-color: $primary-color !important;
|
||||
}
|
||||
|
||||
.bg-header-notice {
|
||||
background-color: $bg-color-header-notice;
|
||||
}
|
||||
|
||||
.van-toast--text {
|
||||
padding: 12px 20px !important;
|
||||
|
||||
.van-toast__text {
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.van-toast {
|
||||
padding: 20px;
|
||||
|
||||
.van-loading__spinner {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.van-icon {
|
||||
font-size: 50px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
//.van-toast__text{
|
||||
// margin-top: 16px;
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.cancelDialog {
|
||||
.van-dialog__footer {
|
||||
.van-button {
|
||||
color: #7f3208 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 页面横向边距 16px
|
||||
// 横向间距 horizontal-spacing 16px
|
||||
.h-spacing {
|
||||
margin-left: 2 * $base-spacing;
|
||||
margin-right: 2 * $base-spacing;
|
||||
}
|
||||
|
||||
// 纵向间距 vertical-spacing 12px
|
||||
.v-spacing {
|
||||
margin-top: 1.5 * $base-spacing;
|
||||
}
|
||||
|
||||
// 主信息背景-如投保结果页-订单信息
|
||||
.bg-text-main {
|
||||
padding: 1.5 * $base-spacing 2 * $base-spacing;
|
||||
background-color: $bg-color-main;
|
||||
border-radius: $base-spacing;
|
||||
}
|
||||
|
||||
// 重要信息背景-如我的保险信息
|
||||
.bg-text-gradient {
|
||||
background: linear-gradient(to right, $bg-color-gradient-start, $bg-color-gradient-end);
|
||||
border-radius: 2 * $base-spacing;
|
||||
}
|
||||
|
||||
/* ====================
|
||||
文字
|
||||
==================== */
|
||||
|
||||
// 辅助信息(次级信息)的正文内容 12px #333 400
|
||||
.text-auxiliary {
|
||||
font-size: $font-size-auxiliary;
|
||||
color: $font-color-regular;
|
||||
font-weight: $font-weight-regular;
|
||||
}
|
||||
|
||||
.text-auxiliary-info {
|
||||
@extend .text-auxiliary;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
// 辅助信息(次级信息)的辅助信息正文内容 12px #999 400
|
||||
.text-auxiliary-auxiliary-info {
|
||||
@extend .text-auxiliary-info;
|
||||
color: $font-color-auxiliary;
|
||||
}
|
||||
|
||||
// 主信息的正文内容 14px #333 400
|
||||
.text-main {
|
||||
font-size: $font-size-main;
|
||||
color: $font-color-regular;
|
||||
font-weight: $font-weight-regular;
|
||||
line-height: $font-size-main;
|
||||
}
|
||||
|
||||
// 主信息的次级内容 14px #666 400
|
||||
.text-main-secondary {
|
||||
@extend .text-main;
|
||||
color: $font-color-secondary;
|
||||
}
|
||||
|
||||
// 文字大小 14sp
|
||||
.text-size-main {
|
||||
font-size: $font-size-main;
|
||||
}
|
||||
|
||||
// 文字大小 18sp
|
||||
.text-size-primary {
|
||||
font-size: $font-size-primary-title;
|
||||
}
|
||||
|
||||
// 主题字体颜色
|
||||
.text-color-primary {
|
||||
color: $font-color-primary !important;
|
||||
}
|
||||
|
||||
// 字体颜色 666
|
||||
.text-color-secondary {
|
||||
color: $font-color-secondary !important;
|
||||
}
|
||||
|
||||
// tag 文字
|
||||
.text-tag {
|
||||
font-size: $font-size-tag-text !important;
|
||||
}
|
||||
|
||||
.text-color-white {
|
||||
color: $reverse-white-color-regular !important;
|
||||
}
|
||||
|
||||
// 字重 400
|
||||
.text-weight-regular {
|
||||
font-weight: $font-weight-regular !important;
|
||||
}
|
||||
|
||||
// 字重 500
|
||||
.text-weight-medium {
|
||||
font-weight: $font-weight-medium !important;
|
||||
}
|
||||
|
||||
// 字重 600
|
||||
.text-weight-semi-bold {
|
||||
font-weight: $font-weight-semi-bold !important;
|
||||
}
|
||||
|
||||
// 主信息的次级内容 14px #FA6B4B 500
|
||||
.text-main-primary {
|
||||
@extend .text-main;
|
||||
@extend .text-color-primary;
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
// 主信息的辅助内容 14px #999 400
|
||||
.text-main-auxiliary {
|
||||
@extend .text-main;
|
||||
color: $font-color-auxiliary;
|
||||
}
|
||||
|
||||
// 主信息的正文内容反白颜色 14px #fff 400
|
||||
.text-main-reverse-white {
|
||||
@extend .text-main;
|
||||
color: $reverse-white-color-regular;
|
||||
}
|
||||
|
||||
// 二级标题 16px #333 500
|
||||
.text-secondary-title {
|
||||
font-size: $font-size-secondary-title;
|
||||
color: $font-color-regular;
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
// 二级标题 16px #333 400
|
||||
.text-secondary-rugular-title {
|
||||
@extend .text-secondary-title;
|
||||
font-weight: $font-weight-regular;
|
||||
}
|
||||
|
||||
// 二级标题文字大小 16sp
|
||||
.text-size-secondary-title {
|
||||
font-size: $font-size-secondary-title;
|
||||
}
|
||||
|
||||
// 第一层级内容的标题 18px #333 500
|
||||
.text-primary {
|
||||
font-size: $font-size-primary-title;
|
||||
color: $font-color-regular;
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
// 二级金额 20px #fff 600
|
||||
.text-secondary-amount {
|
||||
font-size: $font-size-prominent-title;
|
||||
color: $reverse-white-color-regular;
|
||||
font-weight: $font-weight-semi-bold;
|
||||
}
|
||||
|
||||
// 常规金额 28px #333 600
|
||||
.text-main-amount {
|
||||
font-size: $font-size-regular-amount;
|
||||
color: $font-color-regular;
|
||||
font-weight: $font-weight-semi-bold;
|
||||
}
|
||||
|
||||
.text-main-amount__white {
|
||||
@extend .text-main-amount;
|
||||
color: $reverse-white-color-regular;
|
||||
}
|
||||
|
||||
// 常规金额 28px #fff 600
|
||||
.text-regular-amount {
|
||||
font-size: $font-size-regular-amount;
|
||||
color: $reverse-white-color-regular;
|
||||
font-weight: $font-weight-semi-bold;
|
||||
}
|
||||
|
||||
// 辅助颜色 14px #1677FF 500
|
||||
.text-accessory-secondary {
|
||||
font-size: $font-size-main;
|
||||
font-weight: $font-weight-medium;
|
||||
color: $accessory-color-secondary;
|
||||
}
|
||||
|
||||
.text-top-spacing {
|
||||
margin-top: $font-spacing;
|
||||
}
|
||||
|
||||
.text-left-spacing {
|
||||
margin-left: $font-spacing;
|
||||
}
|
||||
|
||||
.text-line-height {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
// 辅助信息背景-如保单详情-状态
|
||||
.text-bg-accessory {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-width: 60px;
|
||||
height: 3 * $base-spacing;
|
||||
border-radius: 0.5 * $base-spacing;
|
||||
background: rgba(94, 152, 255, 0.08);
|
||||
}
|
||||
|
||||
/* ====================
|
||||
按钮
|
||||
==================== */
|
||||
.btn {
|
||||
// 基础样式
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
&--primary {
|
||||
background: linear-gradient(90deg, $btn-primary-bg-color-start 0%, $btn-primary-bg-color-end 100%);
|
||||
border: none;
|
||||
color: $btn-font-color-primary;
|
||||
font-weight: $font-weight-medium;
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background: linear-gradient(90deg, darken($btn-primary-bg-color-start, 10%) 0%, darken($btn-primary-bg-color-end, 10%) 100%);
|
||||
}
|
||||
}
|
||||
|
||||
&--secondary {
|
||||
min-width: 88px;
|
||||
background-color: $btn-secondary-bg-color;
|
||||
border: 1px solid $btn-secondary-border-color;
|
||||
color: $btn-font-color-secondary;
|
||||
font-weight: $font-weight-regular;
|
||||
|
||||
&:active,
|
||||
&:focus {
|
||||
background-color: $btn-secondary-pressed-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
pointer-events: none;
|
||||
opacity: $btn-disabled-opacity;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
&--large {
|
||||
@include btn-sizes($btn-height-large, $btn-font-size-large);
|
||||
}
|
||||
|
||||
&--medium {
|
||||
@include btn-sizes($btn-height-middle, $btn-font-size-middle);
|
||||
}
|
||||
|
||||
&--small {
|
||||
@include btn-sizes($btn-height-small, $btn-font-size-small);
|
||||
}
|
||||
|
||||
&--extra-small {
|
||||
@include btn-sizes($btn-height-extra-small, $btn-font-size-extra-small);
|
||||
}
|
||||
|
||||
// 是否为块级元素
|
||||
&--block {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ====================
|
||||
边框
|
||||
==================== */
|
||||
.border-top {
|
||||
border-top: $border-width-regular solid $border-color-regular;
|
||||
}
|
||||
|
||||
.border-bottom {
|
||||
border-bottom: $border-width-regular solid $border-color-regular
|
||||
}
|
||||
|
||||
/* ====================
|
||||
图标
|
||||
==================== */
|
||||
// 大图标 宽高 48px
|
||||
.icon-large {
|
||||
width: 4 * $base-spacing;
|
||||
height: 4 * $base-spacing;
|
||||
}
|
||||
|
||||
// 中图标 宽高 24px
|
||||
.icon-medium {
|
||||
width: 3 * $base-spacing;
|
||||
height: 3 * $base-spacing;
|
||||
}
|
||||
|
||||
// 右边按钮图标 宽高 16px
|
||||
.icon-arrow-right {
|
||||
width: 2 * $base-spacing;
|
||||
height: 2 * $base-spacing;
|
||||
}
|
||||
|
||||
/* ====================
|
||||
容器
|
||||
==================== */
|
||||
// 容器外边距 mh:16px mt:12px
|
||||
.container-margin {
|
||||
margin: 1.5 * $base-spacing 2 * $base-spacing 0;
|
||||
}
|
||||
|
||||
// 容器内边距 ph:32px pt:16px
|
||||
.container-padding {
|
||||
padding: 2 * $base-spacing 4 * $base-spacing;
|
||||
}
|
||||
|
||||
// 白色容器 mh:16px mt:12px ph:32px pv:16px radius: 12px
|
||||
.white-container—r1_5x {
|
||||
@extend .container-margin;
|
||||
@extend .bg-white;
|
||||
@extend .radius-1_5x;
|
||||
border: 1px solid #F1F1F1;
|
||||
}
|
||||
|
||||
// 白色块
|
||||
.white-container-r1x {
|
||||
@extend .container-margin;
|
||||
@extend .bg-white;
|
||||
border-radius: $base-spacing;
|
||||
}
|
||||
|
||||
/* ====================
|
||||
圆角
|
||||
==================== */
|
||||
.radius-1x {
|
||||
border-radius: 1 * $base-spacing;
|
||||
}
|
||||
|
||||
.radius-1_5x {
|
||||
border-radius: 1.5 * $base-spacing;
|
||||
}
|
||||
|
||||
.radius-2x {
|
||||
border-radius: 2 * $base-spacing;
|
||||
}
|
||||
|
||||
|
||||
6
src/assets/sass/vant-ui.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
.van-tab__pane, .van-tab__pane-wrapper{
|
||||
padding: 10px;
|
||||
}
|
||||
.van-tab--active{
|
||||
font-weight: 600!important;
|
||||
}
|
||||
174
src/assets/sass/variables.scss
Normal file
@@ -0,0 +1,174 @@
|
||||
//颜色
|
||||
$white: #fff !default;
|
||||
$green: #5ebc75 !default;
|
||||
$red: #ff0000 !default;
|
||||
$yellow: #f2c44d !default;
|
||||
$orange: #fa6b4b !default;
|
||||
$gray: #b1b1b1 !default;
|
||||
$black: #000 !default;
|
||||
$blue: #1989fa !default;
|
||||
|
||||
$gray-base: #999 !default;
|
||||
$gray-dark: #666 !default;
|
||||
$gray-darker: #333 !default;
|
||||
$gray-darker-cell: #969799 !default;
|
||||
|
||||
$font-size-base: 14px !default;
|
||||
$font-size-large: 16px !default;
|
||||
$font-size-small: 12px !default;
|
||||
$font-size-xl: 18px !default;
|
||||
//文字列表
|
||||
$font-size-list: 12 13 14 16 18 20 22 24 26;
|
||||
// 宽度列表
|
||||
$width-list: 16 20 22 30 32 40 50 56 60 70 80 86 100 110 120 140 150 155 160 180 192 200 220 260 280 315 325 350 400 440 445 450 550 700 1340;
|
||||
// 宽度列表
|
||||
$font-weight-list: 100 200 300 400 500 600 700 800 900 1000;
|
||||
|
||||
// 内外边距列表
|
||||
$distance-list: 0 1 2 5 6 8 9 10 12 15 16 20 24 25 28 30 35 38 33 40 45 48 50 60 65 70 80 86 90 100 110 120 130 140 150 160 170 180 190 200;
|
||||
$distance-class-list: m, mv, mh, mt, ml, mr, mb, p, pv, ph, pt, pl, pr, pb, top,
|
||||
left, right, bottom;
|
||||
|
||||
// 新内外边距
|
||||
$spacing-multiple-list: 0 0.5 1 1.5 2 2.5 3 3.5 4 4.5 6 8 9 10 12 15 16 20 24 25 28 30 35 38 33 40 45 48 50 60 65 70 80 86 90 100 110 120 130 140 150 160 170 180 190 200;
|
||||
$spacing-class-list: m, mv, mh, mt, ml, mr, mb, p, pv, ph, pt, pl, pr, pb, top,
|
||||
left, right, bottom;
|
||||
|
||||
//圆角弧度
|
||||
$radius: 1 2 3 4 5 6 7 8 9 10 12 15 18 20 50 100;
|
||||
|
||||
//透明度
|
||||
$opacity: 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9;
|
||||
|
||||
// ==================== UI 规范 ====================
|
||||
|
||||
/* ====================
|
||||
颜色
|
||||
==================== */
|
||||
|
||||
// 主题色:主要用于按钮、icon、文字、输入框、边框、背景等
|
||||
$primary-color: #3c8c93;
|
||||
|
||||
// 辅助色:主要用于icon、页面点缀以及相应的功能色,辅助色和弱打扰的颜色可自定义。
|
||||
$accessory-color-regular: #FFA200; // 常规辅助色
|
||||
$accessory-color-secondary: #1677FF; // 次要辅助色
|
||||
$accessory-color-distraction-weak: #5C74B8; // 弱打扰
|
||||
$accessory-color-error-warning: #FF4019; // 错误/警示色
|
||||
$accessory-color-success-safe: #00B33B; // 成功/安全色
|
||||
|
||||
// 文字颜色定义
|
||||
$font-color-primary: $primary-color; // 主要文字颜色
|
||||
$font-color-regular: #333; // 常规文字颜色
|
||||
$font-color-secondary: #666; // 次要文字颜色
|
||||
$font-color-auxiliary: #999; // 辅助文字颜色
|
||||
$font-color-reverse-gray: #BDBDBD; // 灰底反显文字颜色
|
||||
$font-color-reverse-white: #CCC; // 白底反显文字颜色
|
||||
$font-color-placeholder: $font-color-reverse-white; // 输入框提示文字颜色
|
||||
|
||||
// 背景颜色定义
|
||||
$bg-color-overlay: rgba(0, 0, 0, 0.4); // 弹框背景蒙层 40%透明度
|
||||
$bg-color-regular: #F5F5F5; // 常规背景色
|
||||
$bg-color-hint: #FFF3D7; // 提示语背景
|
||||
$bg-color-warning: #FFE8E3; // 警示语背景
|
||||
$bg-color-main: #F8F8F8; // 常规文字背景
|
||||
$bg-color-backup-blue: #ADCBFF; // 备用背景-蓝
|
||||
$bg-color-backup-purple: #CFC3FE; // 备用背景-紫
|
||||
|
||||
// 分割线颜色
|
||||
$border-color-regular: #F1F1F1; // 常规分割线颜色
|
||||
$border-color-weak: #F0F0F0; // 弱分割线颜色
|
||||
|
||||
// 反白颜色
|
||||
$reverse-white-color-regular: #FFF; // 常规反白颜色
|
||||
$reverse-white-color-secondary: rgba(255, 255, 255, 0.8); // 次要反白颜色,透明度80%
|
||||
|
||||
/* ====================
|
||||
文字
|
||||
==================== */
|
||||
|
||||
/* 字体:手机银行UI界面中的字体使用系统自带字体;
|
||||
iOS: 中/英字体 苹方/Helvetica Neue;
|
||||
Android: 中/英字体 思源黑体/Roboto;
|
||||
备选:中/英字体 微软雅黑/Noto Sans */
|
||||
|
||||
// 字号
|
||||
$font-size-base: 16px; // 基础字号
|
||||
// 常规字号:用于界面中各个层级的文本内容,
|
||||
$font-size-tag-text: 10px; // 标签文字
|
||||
$font-size-auxiliary: 12px; // 辅助信息;次级信息的正文
|
||||
$font-size-main: 14px; // 主要信息的正文
|
||||
$font-size-highlighted: 15px; // 突出显示的正文内容
|
||||
$font-size-secondary-title: 16px; // 次级内容标题;页面主流程操作
|
||||
$font-size-main-process: 16px; // 页面主流程操作
|
||||
$font-size-primary-title: 18px; // 导航栏标题;第一层级内容的标题
|
||||
|
||||
// 特殊数字或图形化字符:字号较大,通常用于数字的输入,或需要着重强调的内容
|
||||
$font-size-prominent-title: 20px; // 醒目的内容大标题
|
||||
$font-size-name-card: 24px; // 名片的姓名显示
|
||||
$font-size-regular-amount: 28px; // 常规的金额数字显示
|
||||
$font-size-graphic-decoration: 32px; // 图形化装饰性字符
|
||||
|
||||
// 行高倍数:建议单行文本的行高倍数=1,如弹框标题;多行文本的行高倍数=1.4,英文的行高倍数=1.2,如正文;选取行高时尽量选取基本单位8的增量或相邻偶数。
|
||||
$font-line-height-single: 1; // 单行文本
|
||||
$font-line-height-multi-chinese: 1.4; // 多行文本-中文
|
||||
$font-line-height-multi-english: 1.2; // 多行文本-英文
|
||||
|
||||
// 字重
|
||||
$font-weight-regular: 400; // 常规
|
||||
$font-weight-medium: 500; // 中黑
|
||||
$font-weight-semi-bold: 600; // 中粗
|
||||
|
||||
/* ====================
|
||||
按钮
|
||||
==================== */
|
||||
|
||||
// 按钮文字颜色
|
||||
$btn-font-color-primary: #FFFFFF;
|
||||
$btn-font-color-secondary: $primary-color;
|
||||
|
||||
// 按钮背景渐变色
|
||||
$btn-primary-bg-color-start: #FF8850;
|
||||
$btn-primary-bg-color-end: #FF5505;
|
||||
|
||||
$btn-secondary-bg-color: #FFFFFF;
|
||||
$btn-secondary-border-color: $primary-color;
|
||||
$btn-secondary-pressed-bg-color: rgba(250, 107, 75, 0.1);
|
||||
|
||||
$btn-disabled-opacity: 0.2;
|
||||
// 按钮高度
|
||||
$btn-height-large: 44px;
|
||||
$btn-height-middle: 40px;
|
||||
$btn-height-small: 28px;
|
||||
$btn-height-extra-small: 22px;
|
||||
|
||||
// 按钮文字
|
||||
$btn-font-size-large: 16px;
|
||||
$btn-font-size-middle: 14px;
|
||||
$btn-font-size-small: 12px;
|
||||
$btn-font-size-extra-small: 12px;
|
||||
|
||||
/* ====================
|
||||
栅格布局
|
||||
==================== */
|
||||
/* 用户追求高效、便捷,所以栅格的设置不能太拥挤也不能太松散,因此使用经典的栏式网格更符合手机银行的整体调性
|
||||
界面中的间距应尽量以基本单位「8」整数倍的增量来定义,以保证界面清晰有秩序的层次结构。特殊情况下,可使用0.5或1.5等倍数定义间距。*/
|
||||
$base-spacing: 8px; // 最小单位/基础间距:
|
||||
$grid-column-count: 6; // 栅格栏数
|
||||
|
||||
// ==================== 自定义 start ====================
|
||||
// 边框宽度
|
||||
$border-width-regular: 1px;
|
||||
|
||||
// 背景色-我的
|
||||
$bg-color-gradient-start: #FF9E59;
|
||||
$bg-color-gradient-end: $primary-color;
|
||||
|
||||
$bg-color-header-notice: #FFF6F4;
|
||||
|
||||
// 文字间距
|
||||
$font-spacing: 1 * $base-spacing;
|
||||
|
||||
// 高度
|
||||
$height-cell-regular: 50px;
|
||||
$height-cell-title: 44px;
|
||||
|
||||
30
src/components/loading/loading.js
Normal file
@@ -0,0 +1,30 @@
|
||||
const loadingTemp = `<div class='render-loading'>
|
||||
<!--以下内容替换-->
|
||||
<!--替换完成之后修改loading.scss 保留render-loading-->
|
||||
<div class="container">
|
||||
<div class="dot dot-1"></div>
|
||||
<div class="dot dot-2"></div>
|
||||
<div class="dot dot-3"></div>
|
||||
</div>
|
||||
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<filter id="goo">
|
||||
<feGaussianBlur
|
||||
result="blur"
|
||||
stdDeviation="10"
|
||||
in="SourceGraphic"
|
||||
></feGaussianBlur>
|
||||
<feColorMatrix
|
||||
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -7"
|
||||
mode="matrix"
|
||||
in="blur"
|
||||
></feColorMatrix>
|
||||
</filter>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
|
||||
<!--以上内容替换-->
|
||||
</div>`
|
||||
export default loadingTemp
|
||||
13
src/components/loading/speakLoading.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const speakLoading = `<div class="loader3">
|
||||
<div class="bars bar1"></div>
|
||||
<div class="bars bar2"></div>
|
||||
<div class="bars bar3"></div>
|
||||
<div class="bars bar4"></div>
|
||||
<div class="bars bar5"></div>
|
||||
<div class="bars bar6"></div>
|
||||
<div class="bars bar7"></div>
|
||||
<div class="bars bar8"></div>
|
||||
<div class="bars bar9"></div>
|
||||
<div class="bars bar10"></div>
|
||||
</div>`
|
||||
export default speakLoading
|
||||
49
src/components/svg-icon/index.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true">
|
||||
<use :xlink:href="iconName"></use>
|
||||
<title v-if="!!title">{{title}}</title>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'SvgIcon',
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
return `#icon-${this.iconClass}`
|
||||
},
|
||||
svgClass() {
|
||||
if (this.className) {
|
||||
return 'svg-icon ' + this.className
|
||||
} else {
|
||||
return 'svg-icon'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.svg-icon {
|
||||
//width: 1.1em;
|
||||
//height: 1.1em;
|
||||
margin-left: 0.35em;
|
||||
margin-right: 0.35em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
7
src/config/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
let envInfo = process.env
|
||||
let [base,ai] = [envInfo.VUE_APP_BASE,envInfo.VUE_APP_AI]
|
||||
|
||||
export default {
|
||||
base,
|
||||
ai
|
||||
}
|
||||
9
src/config/urlMap.js
Normal file
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* @desc 远程接口地址和本地mock地址映射表
|
||||
* key:接口地址
|
||||
* value:本地地址
|
||||
*/
|
||||
const mockBaseUrl = 'http://rap2.ebiz-digits.com:8083/app/mock/67/'
|
||||
export default {
|
||||
mockBaseUrl
|
||||
}
|
||||
16
src/directive/no-more-click/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* @describe // 全局防重复点击
|
||||
* @example v-no-more-click="1000"
|
||||
*/
|
||||
export default {
|
||||
inserted(el, binding) {
|
||||
el.addEventListener('click', () => {
|
||||
el.classList.add('is-disabled')
|
||||
el.disabled = true
|
||||
setTimeout(() => {
|
||||
el.disabled = false
|
||||
el.classList.remove('is-disabled')
|
||||
}, binding.value || 1000)
|
||||
})
|
||||
}
|
||||
}
|
||||
102
src/filters/index.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import Common from '@/assets/js/common'
|
||||
|
||||
/*
|
||||
* 把其他filter放到index.js里面一起引入
|
||||
*/
|
||||
export default {
|
||||
m2km(value) {
|
||||
return Common.m2km(value)
|
||||
},
|
||||
|
||||
/**
|
||||
* 除法
|
||||
* @param {Number} dividend 被除数
|
||||
* @param {Number} divisor 除数
|
||||
* @return {Number} 值
|
||||
*/
|
||||
divide(dividend, divisor) {
|
||||
return Common.divide(dividend, divisor)
|
||||
},
|
||||
|
||||
/**
|
||||
* 保留小数位,替代Number.toFixed()方法,针对于某些数据(16.455)不能做到四舍五入
|
||||
* @param {[type]} value 数值
|
||||
* @param {[type]} num 几位小数
|
||||
* @return {[type]} 值
|
||||
*/
|
||||
toFixed(value, num) {
|
||||
return Common.toFixed(value, num)
|
||||
},
|
||||
|
||||
/**
|
||||
* 如果是小数则保留小数位,默认两位
|
||||
* @param {[type]} value 数值
|
||||
* @param {Number} num 几位小数
|
||||
* @return {[type]} 值
|
||||
*/
|
||||
toFloatFixed(value, num) {
|
||||
Common.toFloatFixed(value, num)
|
||||
},
|
||||
|
||||
/**
|
||||
* 转化成工作时间
|
||||
* @param {[type]} value 值
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
businessHour(value) {
|
||||
let time = ''
|
||||
if (value) {
|
||||
const arr = value.split(';')
|
||||
time =
|
||||
'星期' +
|
||||
Common.getWeek(arr[0]) +
|
||||
' ~ 星期' +
|
||||
Common.getWeek(arr[1]) +
|
||||
' ' +
|
||||
Common.getTime(arr[2]) +
|
||||
':' +
|
||||
Common.getTime(arr[3]) +
|
||||
' ~ ' +
|
||||
Common.getTime(arr[4]) +
|
||||
':' +
|
||||
Common.getTime(arr[5])
|
||||
}
|
||||
return time
|
||||
},
|
||||
|
||||
isEmptyObject(o) {
|
||||
return Common.isEmptyObject(o)
|
||||
},
|
||||
wan(value) {
|
||||
const isNumber = typeof value === 'number'
|
||||
return isNumber && value / 10000
|
||||
},
|
||||
// 除以100保留小数位
|
||||
divide100(value, num = 2, isFill) {
|
||||
let values = Number(value)
|
||||
const regular = /^\d+\.\d+/
|
||||
if (values) {
|
||||
values = values / 100
|
||||
if (regular.test(values)) {
|
||||
return Common.toFixed(values, num)
|
||||
} else {
|
||||
return Common.toFixed(values)
|
||||
}
|
||||
} else {
|
||||
if (isFill) {
|
||||
// 是否需要填充
|
||||
value = 0
|
||||
}
|
||||
}
|
||||
return value
|
||||
},
|
||||
discount(value, position = 0) {
|
||||
let ret = value.split('.')
|
||||
if (position == 0) {
|
||||
ret = ret[0]
|
||||
} else if (position == 1) {
|
||||
ret = ret[1]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
7
src/generatedComponents/index.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const requireComponent = require.context('./', true, /\w+\.vue$/) //遍历当前目录和子目录
|
||||
let comps = {}
|
||||
requireComponent.keys().map(fileName => {
|
||||
let comp = requireComponent(fileName).default;
|
||||
comps[comp.name] = comp
|
||||
})
|
||||
export default comps
|
||||
3
src/icons/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const requireAll = requireContext => requireContext.keys().map(requireContext)
|
||||
const req = require.context('./svg', false, /\.svg$/)
|
||||
requireAll(req)
|
||||
1
src/icons/svg/add.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1748936132297" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9417" width="20" height="20"><path d="M511.602535 1023.001001a511.473535 511.473535 0 1 1 360.628673-148.846865A509.475537 509.475537 0 0 1 511.601535 1024z m0-941.031145c-109.8869 0-220.772799 41.956962-304.686723 125.869885-167.826848 168.826847-167.826848 441.545599 0 609.373447s440.5466 167.826848 609.372447 0c167.827848-167.827848 167.827848-440.5466 0-609.373447-83.912924-82.913925-194.799823-124.870887-304.685724-124.870886zM844.259233 845.184162z" fill="#ffffff" p-id="9418"></path><path d="M780.325291 552.485428H241.87978a39.958964 39.958964 0 0 1 0-79.917927h538.445511a39.958964 39.958964 0 0 1 0 79.917927z" fill="#ffffff" p-id="9419"></path><path d="M511.602535 822.208183a39.958964 39.958964 0 0 1-39.959963-39.959963V243.803709a39.958964 39.958964 0 1 1 79.918927 0V782.25022a39.958964 39.958964 0 0 1-39.959964 39.958963z" fill="#ffffff" p-id="9420"></path></svg>
|
||||
|
After Width: | Height: | Size: 1000 B |
1
src/icons/svg/dislike.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1748933578894" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8133" width="16" height="16"><path d="M939.314286 487.2c4.114286-13.714286 6.171429-27.885714 6.171428-42.285714 0-32.342857-10.628571-63.428571-29.828571-88.8 4.114286-13.714286 6.171429-27.885714 6.171428-42.285715 0-32.342857-10.628571-63.428571-29.828571-88.8 4.114286-13.714286 6.171429-27.885714 6.171429-42.285714 0-58.971429-35.085714-112.114286-89.485715-135.314286a75.542857 75.542857 0 0 0-30.285714-6.171428H91.428571c-20.228571 0-36.571429 16.342857-36.571428 36.571428v416c0 20.228571 16.342857 36.571429 36.571428 36.571429h147.771429l98.057143 355.2C353.028571 942.857143 405.6 982.857143 465.028571 982.857143c33.942857 0 65.6-13.485714 89.028572-38.171429 23.428571-24.571429 35.428571-56.8 33.714286-90.742857l-6.857143-140.457143h274.171428c13.828571 0 27.314286-3.657143 39.2-10.628571 46.171429-26.857143 74.857143-75.542857 74.857143-126.857143 0-32.342857-10.628571-63.428571-29.828571-88.8zM137.142857 448V123.428571h92.571429v324.571429h-92.571429z m716.8 183.314286H494.628571l10.971429 226.742857c0.685714 13.6-5.371429 26.4-16.685714 34.857143-6.971429 5.142857-15.542857 7.771429-24.114286 7.657143a50.605714 50.605714 0 0 1-48.228571-36.914286L302.857143 451.657143V123.428571h474.742857a64.971429 64.971429 0 0 1 38.4 59.2c0 11.085714-2.628571 21.6-7.885714 31.2l-15.885715 29.028572 25.028572 21.714286a64.868571 64.868571 0 0 1 22.4 49.142857c0 11.085714-2.628571 21.6-7.885714 31.2l-15.885715 29.028571 25.028572 21.714286a64.868571 64.868571 0 0 1 22.4 49.142857c0 11.085714-2.628571 21.6-7.885715 31.2l-16 29.142857 25.028572 21.714286a64.868571 64.868571 0 0 1 22.4 49.142857c0 21.828571-12.571429 42.857143-32.914286 55.314286z" p-id="8134" fill="#F6AA21"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
1
src/icons/svg/earth.svg
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
1
src/icons/svg/fillDislike.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1748934863167" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3146" width="20" height="20"><path d="M885.902222 490.296889a128.682667 128.682667 0 0 0-20.707555-114.688 128.682667 128.682667 0 0 0-20.707556-114.688 128.568889 128.568889 0 0 0-72.903111-155.392 66.104889 66.104889 0 0 0-26.481778-5.404445H273.009778v427.975112h0.284444l85.816889 310.812444a116.252444 116.252444 0 0 0 111.786667 85.077333 106.382222 106.382222 0 0 0 77.909333-33.393777 106.268444 106.268444 0 0 0 29.496889-79.388445l-6.001778-122.908444h239.900445c12.088889 0 23.893333-3.214222 34.304-9.301334 40.391111-23.495111 65.479111-66.104889 65.479111-110.990222 0-28.302222-9.272889-55.495111-26.083556-77.710222zM112.014222 132.010667v363.975111c0 17.720889 14.279111 32 31.971556 32h65.024V100.039111H144.014222c-17.720889 0-32 14.279111-32 32z" fill="#F6AA21" p-id="3147"></path></svg>
|
||||
|
After Width: | Height: | Size: 923 B |
1
src/icons/svg/fillLike.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1748935008505" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5853" width="20" height="20"><path d="M184.021333 448H136.021333c-39.808 0-72.021333 32.213333-72.021333 72.021333v367.957334c0 39.808 32.213333 72.021333 72.021333 72.021333h47.957334C223.786667 960 256 927.786667 256 887.978667v-368c0-39.765333-32.213333-71.978667-72.021333-71.978667z m773.162667 26.112c-6.698667-26.112-21.077333-46.592-46.976-55.722667a131.925333 131.925333 0 0 0-41.813333-6.570666c-75.306667-0.597333-150.613333-0.213333-225.877334-0.213334-9.728 0-10.026667-0.597333-8.533333-9.898666 4.693333-27.52 8.618667-55.125333 14.037333-82.389334 7.253333-37.034667 8.192-73.813333-4.138666-109.738666-11.093333-32.298667-23.68-64.170667-35.797334-96.170667-6.784-18.090667-18.176-31.317333-36.48-38.912-34.517333-14.506667-68.608-14.208-101.717333 2.986667-21.077333 11.093333-33.493333 28.714667-32.768 53.802666 0.981333 35.413333 1.194667 70.826667 2.688 106.24a58.026667 58.026667 0 0 1-7.808 32.554667c-27.306667 46.933333-47.104 83.413333-75.605333 129.621333-5.290667 8.533333-21.376 24.789333-28.288 32.085334-20.394667 21.504-30.890667 35.498667-31.018667 59.093333-0.085333 85.930667-0.298667 275.029333-0.682667 396.8a71.936 71.936 0 0 0 72.106667 72.234667c105.173333-0.128 296.277333-0.298667 389.973333-0.298667 22.4 0 44.416-1.408 66.005334-8.405333 42.794667-13.994667 69.717333-47.232 73.088-92.032 1.322667-16.981333 0.512-33.877333-5.546667-50.261334-0.938667-2.304 0.554667-6.314667 2.261333-8.618666 16.896-22.613333 27.008-47.616 25.173334-76.117334-0.554667-9.813333-3.669333-19.584-6.656-29.098666-1.621333-5.12-1.621333-8.490667 1.877333-12.8 16.128-20.181333 25.898667-43.178667 25.130667-69.162667-0.426667-12.330667-4.906667-24.405333-7.253334-36.608-0.554667-2.901333-0.981333-7.210667 0.64-9.002667 13.781333-15.402667 21.504-33.621333 25.514667-53.717333a5.546667 5.546667 0 0 1 1.365333-2.005333v-28.288c-0.981333-3.114667-2.005333-6.186667-2.901333-9.386667z" fill="#2e5cA9" p-id="5854"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
1
src/icons/svg/input.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1730875483884" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8599" width="200" height="200"><path d="M287.165217 427.408696 287.165217 427.408696c-13.356522 0-22.26087-8.904348-22.26087-20.034783L264.904348 333.913043c0-11.130435 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C309.426087 418.504348 300.521739 427.408696 287.165217 427.408696z" p-id="8600"></path><path d="M287.165217 621.078261 287.165217 621.078261c-13.356522 0-22.26087-8.904348-22.26087-20.034783l0-73.46087c0-8.904348 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C309.426087 612.173913 300.521739 621.078261 287.165217 621.078261z" p-id="8601"></path><path d="M440.765217 621.078261 440.765217 621.078261c-13.356522 0-22.26087-8.904348-22.26087-20.034783l0-73.46087c0-8.904348 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C463.026087 612.173913 451.895652 621.078261 440.765217 621.078261z" p-id="8602"></path><path d="M592.13913 621.078261 592.13913 621.078261c-13.356522 0-22.26087-8.904348-22.26087-20.034783l0-73.46087c0-8.904348 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C614.4 612.173913 605.495652 621.078261 592.13913 621.078261z" p-id="8603"></path><path d="M736.834783 621.078261 736.834783 621.078261c-13.356522 0-22.26087-8.904348-22.26087-20.034783l0-73.46087c0-8.904348 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C759.095652 612.173913 750.191304 621.078261 736.834783 621.078261z" p-id="8604"></path><path d="M736.834783 427.408696 736.834783 427.408696c-13.356522 0-22.26087-8.904348-22.26087-20.034783L714.573913 333.913043c0-11.130435 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C759.095652 418.504348 750.191304 427.408696 736.834783 427.408696z" p-id="8605"></path><path d="M592.13913 427.408696 592.13913 427.408696c-13.356522 0-22.26087-8.904348-22.26087-20.034783L569.878261 333.913043c0-11.130435 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C614.4 418.504348 605.495652 427.408696 592.13913 427.408696z" p-id="8606"></path><path d="M440.765217 427.408696 440.765217 427.408696c-13.356522 0-22.26087-8.904348-22.26087-20.034783L418.504348 333.913043c0-11.130435 8.904348-20.034783 22.26087-20.034783l0 0c11.130435 0 22.26087 8.904348 22.26087 20.034783l0 73.46087C463.026087 418.504348 454.121739 427.408696 440.765217 427.408696z" p-id="8607"></path><path d="M776.904348 747.965217 260.452174 747.965217c-11.130435 0-20.034783-11.130435-20.034783-22.26087l0 0c0-11.130435 8.904348-22.26087 20.034783-22.26087l516.452174 0c8.904348 0 20.034783 11.130435 20.034783 22.26087l0 0C796.93913 739.06087 790.26087 747.965217 776.904348 747.965217z" p-id="8608"></path><path d="M518.678261 1001.73913C253.773913 1001.73913 35.617391 785.808696 35.617391 518.678261S251.547826 35.617391 518.678261 35.617391C783.582609 35.617391 1001.73913 251.547826 1001.73913 518.678261S783.582609 1001.73913 518.678261 1001.73913zM518.678261 80.13913c-240.417391 0-438.53913 195.895652-438.53913 438.53913C80.13913 759.095652 276.034783 957.217391 518.678261 957.217391 759.095652 957.217391 957.217391 761.321739 957.217391 518.678261 957.217391 278.26087 759.095652 80.13913 518.678261 80.13913z" p-id="8609"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
1
src/icons/svg/like.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1748933405919" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2313" width="16" height="16"><path d="M273 495.9v428l0.3-428z m538.2-88.3H496.8l9.6-198.4c0.6-11.9-4.7-23.1-14.6-30.5-6.1-4.5-13.6-6.8-21.1-6.7-19.6 0.1-36.9 13.4-42.2 32.3-37.1 134.4-64.9 235.2-83.5 302.5V852h399.4a56.85 56.85 0 0 0 33.6-51.8c0-9.7-2.3-18.9-6.9-27.3l-13.9-25.4 21.9-19a56.76 56.76 0 0 0 19.6-43c0-9.7-2.3-18.9-6.9-27.3l-13.9-25.4 21.9-19a56.76 56.76 0 0 0 19.6-43c0-9.7-2.3-18.9-6.9-27.3l-14-25.5 21.9-19a56.76 56.76 0 0 0 19.6-43c0-19.1-11-37.5-28.8-48.4z" fill="#ffffff" p-id="2314"></path><path d="M112 528v364c0 17.7 14.3 32 32 32h65V496h-65c-17.7 0-32 14.3-32 32z m773.9 5.7c16.8-22.2 26.1-49.4 26.1-77.7 0-44.9-25.1-87.5-65.5-111a67.67 67.67 0 0 0-34.3-9.3H572.3l6-122.9c1.5-29.7-9-57.9-29.5-79.4a106.4 106.4 0 0 0-77.9-33.4c-52 0-98 35-111.8 85.1l-85.8 310.8-0.3 428h472.1c9.3 0 18.2-1.8 26.5-5.4 47.6-20.3 78.3-66.8 78.3-118.4 0-12.6-1.8-25-5.4-37 16.8-22.2 26.1-49.4 26.1-77.7 0-12.6-1.8-25-5.4-37 16.8-22.2 26.1-49.4 26.1-77.7 0-12.6-1.8-25-5.4-37zM820.4 499l-21.9 19 14 25.5a56.2 56.2 0 0 1 6.9 27.3c0 16.5-7.1 32.2-19.6 43l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 16.5-7.1 32.2-19.6 43l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 22.4-13.2 42.6-33.6 51.8H345V506.8c18.6-67.2 46.4-168 83.5-302.5a44.28 44.28 0 0 1 42.2-32.3c7.5-0.1 15 2.2 21.1 6.7 9.9 7.4 15.2 18.6 14.6 30.5l-9.6 198.4h314.4C829 418.5 840 436.9 840 456c0 16.5-7.1 32.2-19.6 43z" p-id="2315" fill="#2E5CA9"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
src/icons/svg/magic.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1732168644539" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5075" width="20" height="20"><path d="M446.350222 939.235556a471.267556 471.267556 0 0 1-33.166222 24.234666c-66.673778 44.373333-144.497778 36.295111-206.108444 14.222222a379.790222 379.790222 0 0 1-82.545778-41.642666c-22.528-15.132444-41.528889-31.687111-52.679111-46.478222a28.444444 28.444444 0 1 1 45.511111-34.133334c5.688889 7.623111 18.944 20.024889 38.855111 33.393778 19.456 13.084444 43.690667 25.941333 69.973333 35.328 53.475556 19.114667 110.08 22.186667 155.420445-8.021333 4.721778-3.128889 9.216-6.257778 13.653333-9.443556a209.92 209.92 0 0 1-21.333333-20.195555 174.08 174.08 0 0 1-46.933334-115.313778 194.218667 194.218667 0 0 1 27.648-103.082667c16.384-27.249778 40.903111-51.541333 69.518223-58.709333 40.675556-10.183111 78.051556-3.242667 105.358222 20.252444 26.737778 22.983111 39.253333 58.083556 39.253333 95.687111 0 53.304889-24.689778 115.086222-77.084444 171.804445 13.937778 5.575111 29.582222 10.638222 47.217777 15.075555 130.958222 32.711111 222.378667 31.175111 313.685334-29.752889a28.444444 28.444444 0 1 1 31.573333 47.331556c-110.478222 73.671111-220.956444 72.135111-359.082667 37.546667a393.557333 393.557333 0 0 1-78.734222-28.103111z m-6.656-70.030223c51.939556-51.484444 72.248889-105.016889 72.248889-143.928889 0-25.486222-8.419556-42.951111-19.569778-52.508444-10.524444-9.102222-27.875556-14.791111-54.442666-8.192-7.850667 1.991111-21.902222 11.889778-34.531556 32.881778-12.003556 19.911111-20.081778 45.795556-19.512889 72.476444 0.568889 26.168889 9.500444 53.532444 32.028445 78.165334 6.542222 7.168 14.449778 14.222222 23.779555 21.048888z" p-id="5076" fill="#1989fa"></path><path d="M828.643556 69.063111l140.003555 37.546667a28.444444 28.444444 0 0 1 20.252445 34.247111l-116.792889 474.908444-194.958223-52.280889 116.508445-473.770666a28.444444 28.444444 0 0 1 34.986667-20.650667z m27.420444 612.067556l-195.015111-52.280889-10.979556 44.828444a28.444444 28.444444 0 0 0-0.284444 12.515556l32.597333 159.118222a28.444444 28.444444 0 0 0 49.152 13.141333l107.292445-121.002666a28.444444 28.444444 0 0 0 6.371555-12.117334l10.808889-44.202666zM365.738667 92.16a11.377778 11.377778 0 0 1 21.731555 0l35.896889 116.394667a11.377778 11.377778 0 0 0 7.509333 7.509333l116.394667 35.84a11.377778 11.377778 0 0 1 0 21.788444l-116.394667 35.896889a11.377778 11.377778 0 0 0-7.509333 7.509334l-35.84 116.394666a11.377778 11.377778 0 0 1-21.788444 0l-35.896889-116.394666a11.377778 11.377778 0 0 0-7.509334-7.509334l-116.394666-35.84a11.377778 11.377778 0 0 1 0-21.788444l116.394666-35.896889a11.377778 11.377778 0 0 0 7.509334-7.509333l35.896889-116.394667zM155.363556 548.864a11.377778 11.377778 0 0 1 20.992-5.745778l38.343111 60.017778a11.377778 11.377778 0 0 0 9.272889 5.233778l71.224888 2.104889a11.377778 11.377778 0 0 1 5.802667 20.992l-60.074667 38.343111a11.377778 11.377778 0 0 0-5.233777 9.272889l-2.104889 71.224889a11.377778 11.377778 0 0 1-20.992 5.802666l-38.343111-60.074666a11.377778 11.377778 0 0 0-9.272889-5.233778l-71.224889-2.104889a11.377778 11.377778 0 0 1-5.802667-20.992l60.074667-38.343111a11.377778 11.377778 0 0 0 5.233778-9.272889l2.104889-71.224889z" p-id="5077" fill="#1989fa"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
1
src/icons/svg/message.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1731381325647" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="36033" width="20" height="20"><path d="M192 42.666667a42.666667 42.666667 0 0 1 42.666667 42.666666v64H298.666667a42.666667 42.666667 0 0 1 0 85.333334H234.666667V298.666667a42.666667 42.666667 0 0 1-85.333334 0V234.666667H85.333333a42.666667 42.666667 0 0 1 0-85.333334h64V85.333333a42.666667 42.666667 0 0 1 42.666667-42.666666zM554.666667 85.333333a42.666667 42.666667 0 0 1 39.808 27.349334l74.026666 192.384c12.8 33.28 16.810667 42.922667 22.314667 50.688 5.546667 7.765333 12.373333 14.549333 20.096 20.053333 7.765333 5.546667 17.365333 9.557333 50.688 22.357333l192.384 74.026667a42.666667 42.666667 0 0 1 0 79.616l-192.384 74.026667c-33.28 12.8-42.922667 16.810667-50.688 22.314666-7.765333 5.546667-14.549333 12.373333-20.053333 20.096-5.546667 7.765333-9.557333 17.365333-22.357334 50.688l-74.026666 192.384a42.666667 42.666667 0 0 1-79.616 0l-74.026667-192.384c-12.8-33.28-16.810667-42.922667-22.314667-50.688a85.376 85.376 0 0 0-20.096-20.053333c-7.765333-5.546667-17.365333-9.557333-50.688-22.357333L155.306667 551.808a42.666667 42.666667 0 0 1 0-79.616L347.733333 398.165333c33.28-12.8 42.922667-16.810667 50.688-22.314666a85.333333 85.333333 0 0 0 20.053334-20.096c5.546667-7.765333 9.557333-17.365333 22.357333-50.688l74.026667-192.384A42.666667 42.666667 0 0 1 554.666667 85.333333z m0 161.536l-34.133334 88.832-1.706666 4.352c-10.410667 27.050667-18.261333 47.488-30.805334 65.152a170.666667 170.666667 0 0 1-40.149333 40.149334c-17.664 12.544-38.101333 20.394667-65.152 30.805333l-4.352 1.706667-88.832 34.133333 88.832 34.133333 4.352 1.706667c27.050667 10.410667 47.488 18.261333 65.152 30.805333a170.666667 170.666667 0 0 1 40.149333 40.149334c12.544 17.664 20.394667 38.101333 30.805334 65.152l1.706666 4.352 34.133334 88.832 34.133333-88.832 1.706667-4.352c10.410667-27.050667 18.261333-47.488 30.805333-65.152a170.666667 170.666667 0 0 1 40.149333-40.149334c17.664-12.544 38.101333-20.394667 65.152-30.805333l4.352-1.706667 88.832-34.133333-88.832-34.133333a3388.373333 3388.373333 0 0 0-4.352-1.706667c-27.050667-10.410667-47.488-18.261333-65.152-30.805333a170.666667 170.666667 0 0 1-40.149333-40.149334c-12.544-17.664-20.394667-38.101333-30.805333-65.152a3177.557333 3177.557333 0 0 0-1.706667-4.352L554.666667 246.869333zM192 682.666667a42.666667 42.666667 0 0 1 42.666667 42.666666v64H298.666667a42.666667 42.666667 0 1 1 0 85.333334H234.666667V938.666667a42.666667 42.666667 0 1 1-85.333334 0v-64H85.333333a42.666667 42.666667 0 1 1 0-85.333334h64V725.333333a42.666667 42.666667 0 0 1 42.666667-42.666666z" p-id="36034"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
1
src/icons/svg/product.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1749014951268" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9268" width="20" height="20"><path d="M0 102.4m51.2 0l278.016 0q51.2 0 51.2 51.2l0 275.712q0 51.2-51.2 51.2l-278.016 0q-51.2 0-51.2-51.2l0-275.712q0-51.2 51.2-51.2Z" fill="#ffffff" opacity=".99" p-id="9269"></path><path d="M431.1552 153.6m51.2 0l490.4448 0q51.2 0 51.2 51.2l0 0q0 51.2-51.2 51.2l-490.4448 0q-51.2 0-51.2-51.2l0 0q0-51.2 51.2-51.2Z" fill="#ffffff" opacity=".3" p-id="9270"></path><path d="M431.1552 342.6304m51.2 0l220.9792 0q51.2 0 51.2 51.2l0 0q0 51.2-51.2 51.2l-220.9792 0q-51.2 0-51.2-51.2l0 0q0-51.2 51.2-51.2Z" fill="#ffffff" opacity=".3" p-id="9271"></path><path d="M0 543.488m51.2 0l278.016 0q51.2 0 51.2 51.2l0 275.712q0 51.2-51.2 51.2l-278.016 0q-51.2 0-51.2-51.2l0-275.712q0-51.2 51.2-51.2Z" fill="#ffffff" opacity=".99" p-id="9272"></path><path d="M431.1552 563.2m51.2 0l490.4448 0q51.2 0 51.2 51.2l0 0q0 51.2-51.2 51.2l-490.4448 0q-51.2 0-51.2-51.2l0 0q0-51.2 51.2-51.2Z" fill="#ffffff" opacity=".3" p-id="9273"></path><path d="M431.1552 716.8m51.2 0l220.9792 0q51.2 0 51.2 51.2l0 0q0 51.2-51.2 51.2l-220.9792 0q-51.2 0-51.2-51.2l0 0q0-51.2 51.2-51.2Z" fill="#ffffff" opacity=".3" p-id="9274"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
1
src/icons/svg/sale.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1749015288763" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11211" width="20" height="20"><path d="M840 619.2q35.2-31.2 57.6-32.8 21.6 0 38.4 30.4Q748 864 623.2 864L73.6 881.6q-12.8 0-21.6 9.6-9.6 11.2-9.6 26.4t9.6 24.8q9.6 11.2 21.6 11.2l549.6-18.4q159.2 0 376-290.4l12.8-17.6-8.8-20q-31.2-79.2-85.6-91.2-53.6-10.4-118.4 48-35.2 32-95.2 52l-45.6 14.4q-4.8-28.8-23.2-40-20.8-13.6-77.6-16l-34.4-1.6q-32-4.8-46.4-13.6-23.2-14.4-46.4-38.4l-7.2-7.2q-39.2-36.8-68.8-52.8-32-17.6-79.2-27.2-120.8-22.4-252 132.8l-8 9.6-3.2 204q-0.8 13.6 8 25.6 11.2 11.2 23.2 11.2 12.8 0 22.4-10.4 10.4-12 10.4-25.6l1.6-174.4q101.6-117.6 187.2-101.6 35.2 5.6 62.4 20.8 22.4 13.6 53.6 42.4l8 7.2q34.4 33.6 58.4 48 31.2 16 70.4 20.8l36.8 2.4q28 1.6 40.8 4.8 0 31.2-19.2 48.8-19.2 18.4-60 26.4-59.2 12-208.8-65.6-12.8-6.4-24-1.6-12 4.8-18.4 18.4-4.8 14.4-1.6 28 6.4 14.4 17.6 20 164.8 86.4 246.4 72 64-12.8 98.4-50.4 16-17.6 24-40.8l73.6-22.4q76-26.4 116.8-64.8m20-524q-19.2-21.6-44.8-21.6h-608q-27.2 0-46.4 21.6-17.6 20-17.6 50.4v164q0 16 8.8 25.6 8 10.4 23.2 10.4 13.6 0 21.6-10.4 10.4-10.4 10.4-25.6V145.6h608v215.2h-344q-11.2 0-22.4 11.2-9.6 12-9.6 24.8 0 14.4 9.6 26.4 9.6 9.6 22.4 9.6h344q25.6 0 44.8-20.8 19.2-20 19.2-51.2V145.6q0-29.6-19.2-50.4z" p-id="11212" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
src/icons/svg/service.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1749014667641" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4408" width="20" height="20"><path d="M237.661973 0.056884a237.661973 237.661973 0 0 0 0 475.323945h237.775741V237.718857A237.718857 237.718857 0 0 0 237.718857 0.056884z m786.366469 237.661973a237.661973 237.661973 0 0 0-475.437713 0v237.661972h237.77574a237.661973 237.661973 0 0 0 237.661973-237.661972zM0 786.309585a237.661973 237.661973 0 0 0 475.380829 0V548.647613H237.661973A237.661973 237.661973 0 0 0 0 786.309585z m786.252701-237.661972h-237.775741v237.661972a237.718857 237.718857 0 0 0 475.437714 0 237.548204 237.548204 0 0 0-237.661973-237.661972z" fill="#ffffff" p-id="4409"></path></svg>
|
||||
|
After Width: | Height: | Size: 720 B |
1
src/icons/svg/stars.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1731053354695" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4163" width="20" height="20"><path d="M183.296 552.277333c22.186667-1.024 40.96-9.557333 56.32-25.6 15.36-16.042667 23.210667-35.157333 23.210667-57.344 0 22.186667 7.509333 41.301333 22.869333 57.344 15.36 16.042667 34.133333 24.576 56.32 25.6-22.186667 1.024-40.96 9.557333-56.32 25.6a79.872 79.872 0 0 0-22.869333 57.344c0-22.186667-7.509333-41.301333-23.210667-57.344-15.36-16.042667-34.133333-24.576-56.32-25.6z m79.530667-245.76c43.008-2.048 79.530667-18.773333 109.568-50.176 30.037333-31.402667 45.056-68.608 45.056-111.957333 0 43.349333 15.018667 80.554667 45.056 111.957333s66.56 47.786667 109.909333 50.176c-28.330667 1.365333-54.272 9.216-78.165333 24.234667-23.552 14.677333-42.325333 34.474667-56.32 59.050667-13.653333 24.576-20.48 50.858667-20.48 79.530666 0-43.349333-15.018667-80.896-45.056-112.298666-30.037333-31.744-66.56-48.469333-109.568-50.517334z m113.322666 367.616c32.426667-1.365333 60.074667-13.994667 82.602667-37.546666 22.528-23.552 33.792-51.541333 33.792-84.309334 0 32.768 11.264 60.757333 33.792 84.309334 22.528 23.552 49.834667 36.181333 82.261333 37.546666-32.426667 1.365333-59.733333 13.994667-82.261333 37.546667-22.528 23.552-33.792 51.541333-33.792 84.309333 0-32.768-11.264-60.757333-33.792-84.309333a119.569067 119.569067 0 0 0-82.602667-37.546667z m232.448-161.792c32.426667-1.365333 59.733333-13.994667 82.261334-37.546666 22.528-23.552 33.450667-51.541333 33.450666-84.650667 0 32.768 11.264 60.757333 33.792 84.309333s50.176 36.181333 82.602667 37.546667c-32.426667 1.365333-60.074667 13.994667-82.602667 37.546667-22.528 23.552-33.792 51.541333-33.792 84.309333 0-32.768-11.264-60.757333-33.450666-84.309333-22.528-23.210667-49.834667-35.84-82.261334-37.205334z" p-id="4164"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
1
src/icons/svg/stopSale.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1749015353759" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12273" width="20" height="20"><path d="M512 5.12c279.9616 0 506.88 226.9184 506.88 506.88s-226.9184 506.88-506.88 506.88S5.12 791.9616 5.12 512 232.0384 5.12 512 5.12z m339.712 237.312L789.0944 304.128l0.0512 8.9088-9.0112-0.0512-52.5312 51.8144h30.3616v40.448H686.592l-50.3808 49.664h123.136v40.448h-164.1472l-21.8112 21.504v31.744h227.1232v43.264H497.3056L441.1392 647.168h313.9584v207.2576h-50.3808v-34.816H328.6016v34.7648h-50.3808l-0.0512-46.5408-40.4992 39.9872A433.664 433.664 0 0 0 851.712 242.3808v0.0512zM512 78.336a433.664 433.664 0 0 0-327.0144 718.5408l93.184-91.8528v-57.856H336.896l56.1664-55.3472H278.2208V385.3312a783.872 783.872 0 0 1-83.7632 83.7632l-15.5648-50.432c73.728-67.3792 126.3104-141.1584 156.8256-220.672l49.664 9.9328c-8.5504 21.2992-17.7152 41.8304-28.3648 62.464h175.9232a470.3232 470.3232 0 0 0-34.7136-65.3312l52.48-8.448c11.3664 21.9648 22.016 46.7456 31.8976 73.7792l136.448-0.0512 82.432-81.2032A432.0256 432.0256 0 0 0 512 78.336z m192.7168 612.864H396.544l-67.9936 66.9696v16.0768h376.1664v-83.1488 0.0512zM490.9056 495.2064H327.168v53.248h109.7728l53.9648-53.248z m32.1536-90.112H327.168v49.7664h195.8912v-49.7664z m59.2384 0H573.44l-0.0512 8.8064 8.9088-8.8064z m41.0624-40.448L675.84 312.8832h-102.5024v51.8144h49.9712z m-100.352-51.8144H332.1856a47.7696 47.7696 0 0 0-4.9664 7.8848v43.9296h195.8912V312.8832z" fill="#ffffff" p-id="12274"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
1
src/icons/svg/think.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1748932783928" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1290" width="16" height="16"><path d="M507.947 42.667c106.069 0 189.866 134.229 217.642 329.941l1.152 8.661 9.984 6.571a897.707 897.707 0 0 1 60.672 43.861l11.862 9.686c36.949-48.299 51.072-88.363 40.789-113.622l-2.09-4.266c-10.07-18.048-33.494-30.166-69.163-34.262a40.619 40.619 0 0 1-28.075-15.872 43.093 43.093 0 0 1 38.955-68.693c61.61 7.083 107.52 32.853 131.626 75.819 34.603 61.696 9.088 144.128-63.061 230.528a67.413 67.413 0 0 1-19.84 23.466 862.25 862.25 0 0 1-95.147 89.046l-14.72 11.648-2.389 20.394a752.213 752.213 0 0 1-13.867 73.6l-3.157 12.16 8.79 0.683c63.914 4.053 110.506-7.637 127.615-31.317l2.475-3.84c10.24-18.304 8.661-45.654-6.059-80a42.581 42.581 0 0 1-0.725-32.427 41.387 41.387 0 0 1 22.528-23.296 42.539 42.539 0 0 1 55.21 22.997c25.174 58.624 26.54 112.598 2.39 155.734-53.12 94.549-229.333 101.888-417.195 27.946l-8.533-3.413-15.403 6.4c-19.456 7.723-38.741 14.592-57.77 20.48l-5.248 1.536 1.536 3.499c24.32 52.48 54.186 85.333 84.65 88.362l4.566 0.256c20.949 0 43.946-16.085 65.92-48.213a40.15 40.15 0 0 1 58.24-9.77c18.261 14.08 22.314 40.789 9.13 59.989-37.376 54.528-83.328 84.394-133.29 84.394-103.382 0-184.534-127.829-214.614-309.632l-2.304-14.72-1.877-1.408c-28.587-21.802-55.04-44.8-78.933-68.522l-5.035-5.12-4.523 7.082c-28.416 45.739-37.717 85.12-26.794 110.848l2.261 4.694c10.88 19.328 37.035 31.872 76.757 34.986a40.533 40.533 0 0 1 28.587 14.891 43.093 43.093 0 0 1-36.523 69.973c-66.901-5.333-116.608-31.445-142.08-76.842C48.171 650.112 127.02 502.74 282.496 380.5l7.68-5.973 0.853-6.272c3.84-26.24 8.832-51.627 14.806-75.947l1.194-4.778-7.04-0.726c-59.306-5.418-103.466 5.248-121.344 30.55l-2.56 4.096c-11.605 20.608-8.106 52.906 11.99 92.885 5.12 9.941 6.144 21.632 2.73 32.341a41.557 41.557 0 0 1-21.034 24.704 42.41 42.41 0 0 1-56.448-19.541c-32.854-65.45-37.291-125.824-10.624-173.397 39.253-69.846 134.4-92.16 253.866-69.291l10.582 2.133 1.493 0.214 1.536 0.426 9.003 2.048c34.432 8.022 70.57 19.584 107.776 34.646l8.106 3.413 13.483-6.059a657.92 657.92 0 0 1 83.157-30.464l3.67-1.066-3.67-7.51c-24.064-47.189-52.181-74.496-78.762-77.568l-4.95-0.298c-22.485 0-47.274 18.56-70.57 55.21a40.235 40.235 0 0 1-57.984 11.904 44.416 44.416 0 0 1-11.051-59.562c38.443-60.587 86.656-93.952 139.563-93.952zM633.472 701.78l-9.813 6.144-15.744 9.515-6.315 3.67 15.787 4.266 9.557 2.304 1.365-4.693c1.28-4.822 2.56-9.728 3.712-14.678l1.451-6.528zM496.512 344.96l-9.856 5.077c-19.37 10.112-39.552 21.376-60.587 33.878-17.194 10.24-33.834 20.906-49.877 32l-8.32 5.845-0.896 9.387a897.664 897.664 0 0 0-3.285 60.373l-0.256 20.48a900.66 900.66 0 0 0 4.352 88.405l0.981 9.344 0.768 0.555a897.792 897.792 0 0 0 34.603 22.827l17.92 10.965c18.688 11.093 37.973 21.376 57.685 30.848l15.488 7.168 14.933-7.125a909.431 909.431 0 0 0 37.718-19.968l18.474-10.71a1112.576 1112.576 0 0 0 65.92-42.368l17.366-12.373 1.066-14.933c0.768-13.398 1.28-27.136 1.536-41.216l0.128-21.419c0-21.973-0.768-43.648-2.261-64.939l-1.323-16.042-36.01-21.76L570.112 384a853.205 853.205 0 0 0-58.24-31.701l-15.445-7.339z m11.435 123.819c22.997 0 41.386 19.498 41.386 43.221 0 23.723-18.389 43.221-41.386 43.221-22.998 0-41.387-19.498-41.387-43.221 0-23.723 18.39-43.221 41.387-43.221z m-227.072 24.32l-5.504 5.29-12.288 12.374-5.078 5.376 5.974 6.186 13.354 13.142 3.712 3.413-0.256-12.885-0.042-13.995 0.17-18.901zM624.64 294.272l-4.608 1.152-14.592 4.181-8.363 2.688 14.72 8.534 20.139 12.032-4.352-17.92-2.944-10.667z" p-id="1291" fill="#ffffff"></path></svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
1
src/icons/svg/voice.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg t="1730875233854" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7506" width="30" height="30"><path d="M512 938.666667C277.333333 938.666667 85.333333 746.666667 85.333333 512S277.333333 85.333333 512 85.333333s426.666667 192 426.666667 426.666667-192 426.666667-426.666667 426.666667z m0-768c-187.733333 0-341.333333 153.6-341.333333 341.333333s153.6 341.333333 341.333333 341.333333 341.333333-153.6 341.333333-341.333333-153.6-341.333333-341.333333-341.333333z" fill="#707070" p-id="7507"></path><path d="M512 256c46.933333 0 85.333333 38.4 85.333333 85.333333v170.666667c0 46.933333-38.4 85.333333-85.333333 85.333333s-85.333333-38.4-85.333333-85.333333V341.333333c0-46.933333 38.4-85.333333 85.333333-85.333333z" fill="#707070" p-id="7508"></path><path d="M512 704c-106.666667 0-192-85.333333-192-192 0-12.8 8.533333-21.333333 21.333333-21.333333s21.333333 8.533333 21.333334 21.333333c0 81.066667 68.266667 149.333333 149.333333 149.333333s149.333333-68.266667 149.333333-149.333333c0-12.8 8.533333-21.333333 21.333334-21.333333s21.333333 8.533333 21.333333 21.333333c0 106.666667-85.333333 192-192 192z" fill="#707070" p-id="7509"></path><path d="M490.666667 682.666667h42.666666v170.666666h-42.666666z" fill="#707070" p-id="7510"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
130
src/main.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
import Router from './router'
|
||||
import Store from './store'
|
||||
import Filters from '@/filters'
|
||||
import utils from '@/assets/js/common'
|
||||
import './icons'
|
||||
import FastClick from 'fastclick'
|
||||
import '@/assets/js/generatedValidate/index' //表单校验
|
||||
import validatorItem from '@/assets/js/generatedValidate/generatedValidateItem' //表单校验
|
||||
import '@utils/compatible' //兼容性代码
|
||||
import NoMoreClick from '@/directive/no-more-click'
|
||||
import eruda from 'eruda'
|
||||
//全局注册vant常用组件
|
||||
import 'vant/lib/index.css'
|
||||
import 'viewerjs/dist/viewer.css'
|
||||
import 'quill/dist/quill.snow.css'
|
||||
import Bus from '@utils/bus'
|
||||
import { Toast, Form, Loading, Lazyload, Notify, Image, Button, Dialog } from 'vant'
|
||||
import generatedFormat from '@/assets/js/generatedFormat/index'
|
||||
import generatedApi from '@/api/generatedApi/index'
|
||||
import generatedComponents from './generatedComponents'
|
||||
for (let item in generatedComponents) {
|
||||
Vue.component(item, generatedComponents[item])
|
||||
}
|
||||
Vue.use(Image)
|
||||
Vue.use(Dialog)
|
||||
Vue.use(Button)
|
||||
Vue.use(Toast)
|
||||
Vue.use(Form)
|
||||
Vue.use(Loading)
|
||||
Vue.use(Lazyload)
|
||||
Vue.use(Notify)
|
||||
Vue.use(Lazyload, {
|
||||
lazyComponent: true
|
||||
})
|
||||
//router or bridge jump
|
||||
Vue.prototype.$bus = new Bus()
|
||||
Vue.prototype.$generatedFormat = generatedFormat
|
||||
Vue.prototype.$generatedApi = generatedApi
|
||||
Vue.prototype.$generatedDictList = generatedFormat.formatList
|
||||
Vue.prototype.$validatorItem = { ...validatorItem }
|
||||
Vue.prototype.$computeNumber = function computeNumber(a, type, b) {
|
||||
/**
|
||||
* 获取数字小数点的长度
|
||||
* @param {number} n 数字
|
||||
*/
|
||||
function getDecimalLength(n) {
|
||||
const decimal = n.toString().split('.')[1]
|
||||
return decimal ? decimal.length : 0
|
||||
}
|
||||
/**
|
||||
* 修正小数点
|
||||
* @description 防止出现 `33.33333*100000 = 3333332.9999999995` && `33.33*10 = 333.29999999999995` 这类情况做的处理
|
||||
* @param {number} n
|
||||
*/
|
||||
const amend = (n, precision = 15) => parseFloat(Number(n).toPrecision(precision))
|
||||
const power = Math.pow(10, Math.max(getDecimalLength(a), getDecimalLength(b)))
|
||||
let result = 0
|
||||
|
||||
a = amend(a * power)
|
||||
b = amend(b * power)
|
||||
|
||||
switch (type) {
|
||||
case '+':
|
||||
result = (a + b) / power
|
||||
break
|
||||
case '-':
|
||||
result = (a - b) / power
|
||||
break
|
||||
case '*':
|
||||
result = (a * b) / (power * power)
|
||||
break
|
||||
case '/':
|
||||
result = a / b
|
||||
break
|
||||
}
|
||||
|
||||
result = amend(result)
|
||||
|
||||
return {
|
||||
/** 计算结果 */
|
||||
result,
|
||||
/**
|
||||
* 继续计算
|
||||
* @param {"+"|"-"|"*"|"/"} nextType 继续计算方式
|
||||
* @param {number} nextValue 继续计算的值
|
||||
*/
|
||||
next(nextType, nextValue) {
|
||||
return computeNumber(result, nextType, nextValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 全局 防重复点击
|
||||
Vue.directive('no-more-click', NoMoreClick)
|
||||
|
||||
Vue.prototype.$utils = utils
|
||||
//混合开发调试工具
|
||||
// 注册过滤器
|
||||
Object.keys(Filters).forEach(function (k) {
|
||||
Vue.filter(k, Filters[k])
|
||||
})
|
||||
let envFlag = process.env.VUE_APP_FLAG
|
||||
if (envFlag != 'prd') {
|
||||
// eslint-disable-next-line no-undef
|
||||
// eruda.init()
|
||||
Vue.config.devtools = true
|
||||
} else {
|
||||
Vue.config.devtools = false
|
||||
// eslint-disable-next-line no-undef
|
||||
// eruda.init() //TODO 生产环境验证,打开调试工具
|
||||
}
|
||||
//权限控制
|
||||
import { permission } from '@/assets/js/utils/permission'
|
||||
permission()
|
||||
//ios点击300毫秒时延
|
||||
FastClick.attach(document.body)
|
||||
// components下的文件全部转化成组件
|
||||
const files = require.context('@/components/', true, /\.vue$/)
|
||||
files.keys().map((item) => {
|
||||
Vue.component(files(item).default.name, files(item).default)
|
||||
})
|
||||
Vue.config.productionTip = false
|
||||
const vm = new Vue({
|
||||
router: Router,
|
||||
store: Store,
|
||||
render: (h) => h(App)
|
||||
}).$mount('#app')
|
||||
window.page = vm
|
||||
38
src/router/app/index.js
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* @name:
|
||||
* @author: Zhang Weiwei
|
||||
* @created_date: Do not edit
|
||||
* @description:
|
||||
*/
|
||||
// 用途:app模块路由配置
|
||||
//定义相关组件
|
||||
const notFound = () => import('@/views/app/404')
|
||||
const home = () => import('@/views/app/Home.vue')
|
||||
|
||||
export default [
|
||||
{
|
||||
path: '/404',
|
||||
component: notFound,
|
||||
meta: {
|
||||
title: '404'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
redirect: '/404'
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home',
|
||||
meta: {
|
||||
title: 'OA智能助理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/home',
|
||||
component: home,
|
||||
meta: {
|
||||
title: 'OA智能助理'
|
||||
}
|
||||
}
|
||||
]
|
||||
10
src/router/generatedRouter/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export default [
|
||||
{
|
||||
path: '/chatPage',
|
||||
name: 'chatPage',
|
||||
component: () => import('@/views/AI/index.vue'),
|
||||
meta: {
|
||||
title: 'chatPage '
|
||||
}
|
||||
},
|
||||
]
|
||||
14
src/router/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
import App from './app/index'
|
||||
import generatedRouter from './generatedRouter/index'
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
const constantRouterMap = [...App, ...generatedRouter] //后续可以删减模块
|
||||
|
||||
export default new Router({
|
||||
mode: 'hash', //路由模式
|
||||
routes: constantRouterMap,
|
||||
scrollBehavior: () => ({ y: 0 })
|
||||
})
|
||||
2
src/store/actions.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const actions = {}
|
||||
export default actions
|
||||
4
src/store/getters.js
Normal file
@@ -0,0 +1,4 @@
|
||||
const getters = {
|
||||
networkState: state => state.app.networkState
|
||||
}
|
||||
export default getters
|
||||
14
src/store/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
import getters from './getters'
|
||||
import mutations from './mutations'
|
||||
import actions from './actions'
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
export default new Vuex.Store({
|
||||
mutations,
|
||||
actions,
|
||||
getters,
|
||||
modules: {}
|
||||
})
|
||||
2
src/store/mutations.js
Normal file
@@ -0,0 +1,2 @@
|
||||
const mutations = {}
|
||||
export default mutations
|
||||
25
src/store/user.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @name: 用户信息数据
|
||||
* @author: Zhang Weiwei
|
||||
* @created_date: 2023.10.08
|
||||
* @description: 用户信息和sessionId
|
||||
*/
|
||||
|
||||
const state = {
|
||||
// 投保信息录入
|
||||
userInfo: {
|
||||
customerNo: '00000000134'
|
||||
},
|
||||
sessionId: ''
|
||||
}
|
||||
|
||||
const getters = {
|
||||
userInfo: state => state.userInfo,
|
||||
sessionId: state => state.sessionId
|
||||
}
|
||||
|
||||
export default {
|
||||
namespaced: true, // 启用命名空间
|
||||
state,
|
||||
getters
|
||||
}
|
||||
41
src/views/AI/components/Announcement.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="announcement" >
|
||||
<van-notice-bar
|
||||
background='#2e5ca9'
|
||||
left-icon="volume-o"
|
||||
color='#fff'
|
||||
:text="announcements[0].content"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { NoticeBar } from 'vant';
|
||||
export default {
|
||||
name: 'Announcement',
|
||||
components: {
|
||||
[NoticeBar.name]:NoticeBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
announcements: [
|
||||
{
|
||||
title: '新功能上线',
|
||||
content: '我们很高兴地宣布,全新的“智能问答”功能现已上线!您可以体验更高效、更智能的问答服务。',
|
||||
date: '2023-09-25'
|
||||
},
|
||||
]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.announcement {
|
||||
//padding: 20px;
|
||||
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
//box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
96
src/views/AI/components/HotProducts.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="hot-products">
|
||||
<h3>最新产品</h3>
|
||||
<van-cell-group>
|
||||
<van-cell v-for="(product, index) in displayedProducts" :key="index" :title="product.productName" @click="cellClick(product)" />
|
||||
</van-cell-group>
|
||||
<div style="text-align: right; color: #f6aa21" class="mt10 fs14 fw600" @click="toggleShowAll">
|
||||
{{ showAll ? '收起' : '更多' }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Cell, CellGroup } from 'vant'
|
||||
import { haslProducts } from '@/api/generatedApi'
|
||||
|
||||
export default {
|
||||
name: 'HotProducts',
|
||||
props: {
|
||||
messagesList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
hotProducts: [
|
||||
// {
|
||||
// productName: '中邮智多星保险产品计划百宝箱',
|
||||
// },
|
||||
// {
|
||||
// productName: '中邮挚爱一生(分红型)百宝箱',
|
||||
// },
|
||||
// {
|
||||
// productName: '中邮富富余财富嘉6号两全保险保险箱',
|
||||
// },
|
||||
// {
|
||||
// productName: '中邮富富余财富嘉7号两全保险保险箱',
|
||||
// },
|
||||
// {
|
||||
// productName: '中邮富富余财富嘉8号两全保险保险箱',
|
||||
// },
|
||||
// {
|
||||
// productName: '中邮富富余财富嘉9号两全保险保险箱',
|
||||
// },
|
||||
],
|
||||
showAll: false, // 控制是否展示全部数据
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getHotProducts()
|
||||
},
|
||||
computed: {
|
||||
displayedProducts() {
|
||||
return this.showAll ? this.hotProducts : this.hotProducts.slice(0, 3)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
cellClick(item) {
|
||||
this.$emit('cellClick')
|
||||
this.messagesList.push({
|
||||
type: 'box',
|
||||
text: item.productName,
|
||||
})
|
||||
this.$emit('update:messageList', this.messagesList)
|
||||
console.log(this.messagesList)
|
||||
},
|
||||
toggleShowAll() {
|
||||
this.showAll = !this.showAll
|
||||
},
|
||||
|
||||
async getHotProducts() {
|
||||
const res = await haslProducts()
|
||||
this.hotProducts = res.content
|
||||
},
|
||||
},
|
||||
components: {
|
||||
[Cell.name]: Cell,
|
||||
[CellGroup.name]: CellGroup,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hot-products {
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
overflow: scroll;
|
||||
h3 {
|
||||
color: #2e5ca9;
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
87
src/views/AI/components/NavigationList.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<van-swipe class="my-swipe" >
|
||||
<van-swipe-item class='item'>
|
||||
<div v-for='item in navigationItems'>
|
||||
<div class='icon-contact mt20'>
|
||||
<svg-icon :icon-class='item.icon' class-name='icon '></svg-icon>
|
||||
</div>
|
||||
<div class='nav-title mb10'>{{item.title}}</div>
|
||||
</div>
|
||||
</van-swipe-item>
|
||||
<!-- <van-swipe-item>2</van-swipe-item>-->
|
||||
</van-swipe>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Swipe,SwipeItem} from 'vant'
|
||||
import SvgIcon from '@/components/svg-icon/index.vue'
|
||||
|
||||
export default {
|
||||
name: 'NavigationList',
|
||||
components: {
|
||||
SvgIcon,
|
||||
[Swipe.name]: Swipe,
|
||||
[SwipeItem.name]:SwipeItem,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
navigationItems: [
|
||||
{ title: '产品中心', icon: 'product' },
|
||||
{ title: '在售产品', icon: 'sale' },
|
||||
{ title: '停售产品', icon: 'stopSale' },
|
||||
{ title: '服务中心', icon: 'service' },
|
||||
],
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 主题颜色定义
|
||||
$primary-color: #2E5CA9;
|
||||
$primary-text-color: #F6AA21;
|
||||
$primary-trans-color: #87A2D0;
|
||||
|
||||
.my-swipe{
|
||||
|
||||
background: $primary-color;
|
||||
border-radius: 5px;
|
||||
.item{
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
text-align: center;
|
||||
div{
|
||||
flex:1;
|
||||
padding:10px 5px;
|
||||
text-align: center;
|
||||
.icon-contact{
|
||||
padding: 5px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
margin: 0 auto;
|
||||
background: $primary-trans-color;
|
||||
text-align: center;
|
||||
width: 35px;
|
||||
height:35px;
|
||||
display: flex;
|
||||
//justify-items: center;
|
||||
align-items: center;
|
||||
justify-items: center;
|
||||
.icon{
|
||||
flex: 1;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
.nav-title{
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color:#fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
61
src/views/AI/components/TabBox.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div >
|
||||
<div class='tab-box' v-for='item in 2'>
|
||||
<div class='box-title'>
|
||||
这个标题
|
||||
</div>
|
||||
<div class='box-container'>
|
||||
<div v-for='item in 5'>
|
||||
内容
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'box',
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
props: {},
|
||||
watch: {},
|
||||
components: {},
|
||||
filters: {},
|
||||
methods: {},
|
||||
created() {
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
computed: {}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang='scss'>
|
||||
// 主题颜色定义
|
||||
$primary-color: #2E5CA9; // 修复了 SCSS 变量的定义方式
|
||||
$primary-text-color: #F6AA21; // 修复了 SCSS 变量的定义方式
|
||||
$primary-trans-color: rgba(135, 162, 208, 0.5); // 使用rgba定义颜色,透明度为0.8
|
||||
.tab-box{
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
background: #fff;
|
||||
border: 1px solid $primary-trans-color;
|
||||
.box-title{
|
||||
padding: 10px 15px;
|
||||
background:$primary-trans-color ;
|
||||
color : #2E5CA9; // 修复了 SCSS 变量的定义方式
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
|
||||
}
|
||||
.box-container{
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
//justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
222
src/views/AI/components/message.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<section>
|
||||
<div v-for="(message, index) in messagesList" :key="index" class="message-item">
|
||||
<!--用户消息-->
|
||||
<div v-if="message.type === 'user'" class="user-message">
|
||||
<p>{{ message.text }}</p>
|
||||
</div>
|
||||
<!--机器人消息-->
|
||||
<div v-else-if="message.type === 'bot'" class="bot-message">
|
||||
<span v-if="message.isThink">
|
||||
<!-- loading-->
|
||||
<span class="speakLoadingToast pv10">
|
||||
<van-loading type="spinner" color="#2e5ca9" size="20px" v-if="!message.text" />
|
||||
<span class="loading-text">{{ message.text ? '思考完成' : '思考中...' }}</span>
|
||||
<van-icon name="arrow-up" :class="message.showThink ? 'rate360' : 'rate180'" @click="showThink(message)" v-if="message.think" />
|
||||
</span>
|
||||
<!--开启思考-->
|
||||
<p v-html="md.render(message.think)" v-if="message.think && message.showThink" class="thinkText" />
|
||||
</span>
|
||||
<div>
|
||||
<p v-html="md.render(message.text)" v-if="message.text"></p>
|
||||
<span class="speakLoadingToast pv10" v-else>
|
||||
<van-loading type="spinner" color="#2e5ca9" size="20px" v-if="!message.text" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!--百宝箱-->
|
||||
<div v-else>
|
||||
<treasure-box :item="message"></treasure-box>
|
||||
</div>
|
||||
<!-- 新增点赞和踩按钮 -->
|
||||
<div class="reaction-buttons" v-if="message.type !== 'user'">
|
||||
<button @click="handleReaction(message, 'like')" class="like">
|
||||
<svg-icon :icon-class="message.isLike ? 'fillLike' : 'like'" class-name="chat-icon"></svg-icon> 有用
|
||||
</button>
|
||||
<button @click="handleReaction(message, 'dislike')" class="dislike">
|
||||
<svg-icon :icon-class="message.isDisLike ? 'fillDislike' : 'dislike'" style="width: 15px; height: 15px"></svg-icon>无用
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// icon
|
||||
import { Icon } from 'vant'
|
||||
import TreasureBox from '@/views/AI/components/treasureBox.vue'
|
||||
import MarkdownIt from 'markdown-it'
|
||||
import markdownItKatex from 'markdown-it-katex'
|
||||
// import mermaidItMarkdown from 'mermaid-it-markdown'
|
||||
|
||||
console.log(mermaidItMarkdown)
|
||||
const md = new MarkdownIt({
|
||||
html: true,
|
||||
linkify: true,
|
||||
typographer: true,
|
||||
})
|
||||
.use(markdownItKatex)
|
||||
// .use(mermaidItMarkdown)
|
||||
export default {
|
||||
name: 'message',
|
||||
components: { TreasureBox, [Icon.name]: Icon },
|
||||
props: {
|
||||
messagesList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
isDeep: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
md,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showThink(message) {
|
||||
this.$set(message, 'showThink', !message.showThink)
|
||||
|
||||
console.log(message.showThink)
|
||||
},
|
||||
// 处理点赞和踩的逻辑
|
||||
handleReaction(message, type) {
|
||||
if (type === 'like') {
|
||||
this.$set(message, 'isLike', !message.isLike)
|
||||
this.$set(message, 'isDisLike', false)
|
||||
} else if (type === 'dislike') {
|
||||
this.$set(message, 'isDisLike', !message.isDisLike)
|
||||
this.$set(message, 'isLike', false)
|
||||
}
|
||||
// 触发父组件的更新事件
|
||||
this.$emit('update-message', { ...message })
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 主题颜色定义
|
||||
$primary-color: #2e5ca9; // 修复了 SCSS 变量的定义方式
|
||||
$primary-text-color: #f6aa21; // 修复了 SCSS 变量的定义方式
|
||||
$primary-trans-color: rgba(135, 162, 208, 0.5); // 使用rgba定义颜色,透明度为0.8
|
||||
|
||||
.message-item {
|
||||
all: initial;
|
||||
margin-bottom: 10px;
|
||||
width: 100%;
|
||||
font-size: unset;
|
||||
|
||||
.user-message,
|
||||
.bot-message {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
background-color: $primary-color;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
//max-width: 70%;
|
||||
//max-width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.user-message {
|
||||
justify-content: end;
|
||||
}
|
||||
.bot-message {
|
||||
justify-content: start;
|
||||
background-color: #fff;
|
||||
border-radius: 5px;
|
||||
max-width: calc(80% + 20px);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.bot-avatar {
|
||||
margin-left: 10px;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.thinkText {
|
||||
color: $primary-trans-color;
|
||||
padding-top: 0;
|
||||
}
|
||||
.speakLoadingToast {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: $primary-text-color;
|
||||
margin-left: 10px;
|
||||
.loading-text {
|
||||
color: $primary-trans-color;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
background-color: #fff;
|
||||
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
// 新增样式:点赞和踩按钮
|
||||
.reaction-buttons {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-top: 5px;
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
padding: 2px 3px;
|
||||
border-radius: 5px;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
.dislike {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
.rate360 {
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: rate360;
|
||||
}
|
||||
.rate180 {
|
||||
animation-duration: 0.5s;
|
||||
animation-fill-mode: both;
|
||||
animation-name: rate180;
|
||||
}
|
||||
|
||||
@keyframes rate360 {
|
||||
0% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
@keyframes rate180 {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
86
src/views/AI/components/treasureBox.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="treasure-box">
|
||||
<h3>{{ item.text.indexOf('工具箱') !== -1 ? item.text : item.text + '工具箱' }}</h3>
|
||||
<van-tabs v-model="active" color="#2E5CA9" title-active-color="#2E5CA9" line-width="20">
|
||||
<van-tab title="常用工具">
|
||||
<TabBox></TabBox>
|
||||
</van-tab>
|
||||
<van-tab title="爆款图文">
|
||||
<TabBox></TabBox>
|
||||
</van-tab>
|
||||
<van-tab title="产品知识">
|
||||
<TabBox></TabBox>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
<!-- 在这里添加百宝箱的具体内容 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Tabs, Tab } from 'vant'
|
||||
import TabBox from '@/views/AI/components/TabBox.vue'
|
||||
import { productDetail } from '@/api/generatedApi'
|
||||
export default {
|
||||
name: 'TreasureBox',
|
||||
props: {
|
||||
item: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
components: {
|
||||
[Tabs.name]: Tabs,
|
||||
[Tab.name]: Tab,
|
||||
TabBox,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
active: 0,
|
||||
// 可以在这里添加百宝箱相关的数据
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'item.text': {
|
||||
handler(newValue, oldValue) {
|
||||
console.log(this.item)
|
||||
this.getTreasureBox()
|
||||
},
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 可以在这里添加百宝箱相关的功能方法
|
||||
async getTreasureBox() {
|
||||
productDetail({ query: this.item.text }).then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 主题颜色定义
|
||||
$primary-color: #2e5ca9;
|
||||
$primary-text-color: #f6aa21;
|
||||
$primary-trans-color: #87a2d0;
|
||||
|
||||
.treasure-box {
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
//box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
h3 {
|
||||
color: $primary-color;
|
||||
font-size: 18px;
|
||||
//margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
327
src/views/AI/index.vue
Normal file
@@ -0,0 +1,327 @@
|
||||
<template>
|
||||
<div class="chat-page">
|
||||
<main class="chat-main">
|
||||
<div class="chat-content">
|
||||
<div class="message-area" ref="messageArea" @scroll="handleScroll">
|
||||
<HotProducts class="mb10" :messagesList.sync="messages"></HotProducts>
|
||||
<messageComponent :messagesList="messages" :is-deep="isDeep" :is-search="isSearching"></messageComponent>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<section class="section">
|
||||
<button @click="searchInternet" :class="{ active: isSearching }">
|
||||
<svg-icon icon-class="earth" class-name="chat-icon"></svg-icon>
|
||||
联网搜索
|
||||
</button>
|
||||
<button @click="deepInternet" :class="{ active: isDeep }">
|
||||
<svg-icon icon-class="think" class-name="chat-icon"></svg-icon>
|
||||
深度思考
|
||||
</button>
|
||||
<button @click="startNewConversation" class="active">
|
||||
<svg-icon icon-class="add" class-name="chat-icon"></svg-icon>
|
||||
新建会话
|
||||
</button>
|
||||
</section>
|
||||
<footer class="chat-footer">
|
||||
<input type="text" v-model="newMessage" placeholder="请简短描述您的问题" @keyup.enter="sendMessage" />
|
||||
<button @click="sendMessage" :disabled="messageStatus === 'send'" :class="{ disabled: messageStatus === 'send' }">发送</button>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Icon } from 'vant'
|
||||
import messageComponent from './components/message.vue'
|
||||
import SvgIcon from '@/components/svg-icon/index.vue'
|
||||
import HotProducts from '@/views/AI/components/HotProducts.vue'
|
||||
import { chat, chatProduct } from '@/api/generatedApi'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SvgIcon,
|
||||
[Icon.name]: Icon,
|
||||
messageComponent,
|
||||
HotProducts,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
conversationId: '',
|
||||
currentMessage: null,
|
||||
messageStatus: 'stop',
|
||||
isThink: false,
|
||||
newMessage: '',
|
||||
messages: [],
|
||||
isSearching: false,
|
||||
isDeep: false,
|
||||
autoScrollEnabled: true,
|
||||
scrollPosition: 0,
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
deepInternet() {
|
||||
this.isDeep = !this.isDeep
|
||||
},
|
||||
searchInternet() {
|
||||
this.isSearching = !this.isSearching
|
||||
},
|
||||
startNewConversation() {
|
||||
this.messages = []
|
||||
this.conversationId = ''
|
||||
},
|
||||
|
||||
hasTreasureBox() {
|
||||
chatProduct({ query: this.newMessage }).then((res) => {
|
||||
if (res) {
|
||||
this.messageStatus = 'stop'
|
||||
this.messages.push({ type: 'box', text: this.newMessage })
|
||||
this.newMessage = ''
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
sendMessage() {
|
||||
if (this.newMessage.trim() === '') return
|
||||
|
||||
this.messages.push({ type: 'user', text: this.newMessage })
|
||||
this.messageStatus = 'send'
|
||||
|
||||
if (this.newMessage.includes('工具箱')) {
|
||||
this.hasTreasureBox()
|
||||
return
|
||||
}
|
||||
|
||||
this.axiosGetAiChat()
|
||||
},
|
||||
|
||||
throttle(fn, delay = 50) {
|
||||
let lastCall = 0
|
||||
return (...args) => {
|
||||
const now = Date.now()
|
||||
if (now - lastCall >= delay) {
|
||||
lastCall = now
|
||||
fn.apply(this, args)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
scrollToBottom: function () {
|
||||
if (!this.autoScrollEnabled) return
|
||||
|
||||
this.$nextTick(() => {
|
||||
const messageArea = this.$refs.messageArea
|
||||
if (messageArea) {
|
||||
messageArea.scrollTop = messageArea.scrollHeight
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
handleScroll() {
|
||||
const messageArea = this.$refs.messageArea
|
||||
if (!messageArea) return
|
||||
|
||||
const threshold = 10
|
||||
const isAtBottom = messageArea.scrollHeight - messageArea.clientHeight <= messageArea.scrollTop + threshold
|
||||
this.autoScrollEnabled = isAtBottom
|
||||
this.scrollPosition = messageArea.scrollTop
|
||||
},
|
||||
|
||||
axiosGetAiChat() {
|
||||
const abortController = new AbortController()
|
||||
|
||||
this.currentMessage = JSON.parse(
|
||||
JSON.stringify({
|
||||
type: 'bot',
|
||||
text: '',
|
||||
isThink: this.isDeep,
|
||||
showThink: true,
|
||||
think: '',
|
||||
isLike: false,
|
||||
isDisLike: false,
|
||||
})
|
||||
)
|
||||
this.messages.push(this.currentMessage)
|
||||
|
||||
const params = {
|
||||
query: this.newMessage,
|
||||
isDeep: this.isDeep ? 1 : 0,
|
||||
isOnline: this.isSearching ? 1 : 0,
|
||||
user: 'chenyuda',
|
||||
conversationId: this.conversationId,
|
||||
}
|
||||
|
||||
fetch(chat(), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
signal: abortController.signal,
|
||||
body: JSON.stringify(params),
|
||||
})
|
||||
.then((res) => this.processStreamResponse(res))
|
||||
.catch((err) => console.error('请求错误:', err))
|
||||
},
|
||||
|
||||
async processStreamResponse(response) {
|
||||
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`)
|
||||
|
||||
if (!response.body) {
|
||||
console.error('响应体不存在:', response)
|
||||
return
|
||||
}
|
||||
|
||||
const reader = response.body.getReader()
|
||||
let buffer = ''
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
const { done, value } = await reader.read()
|
||||
if (done) break
|
||||
|
||||
buffer += new TextDecoder().decode(value)
|
||||
const lines = buffer.split('\n')
|
||||
|
||||
lines.slice(0, -1).forEach((line) => {
|
||||
const parsed = this.parseStreamLine(line)
|
||||
if (parsed) this.updateMessageContent(parsed)
|
||||
})
|
||||
|
||||
buffer = lines[lines.length - 1] || ''
|
||||
} catch (error) {
|
||||
console.error('读取流数据时发生错误:', error)
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
parseStreamLine(line) {
|
||||
try {
|
||||
const cleanLine = line.replace(/^data:\s*/, '')
|
||||
if (!cleanLine) return null
|
||||
const data = JSON.parse(cleanLine)
|
||||
console.log(data)
|
||||
this.updateConversationState(data)
|
||||
return data
|
||||
} catch (error) {
|
||||
console.error('流数据解析失败:', error)
|
||||
return null
|
||||
}
|
||||
},
|
||||
updateConversationState(data) {
|
||||
this.conversationId = data.conversation_id || conversationId.value
|
||||
if (data.answer && data.answer.indexOf('<think>') !== -1) {
|
||||
this.isThink = true
|
||||
}
|
||||
|
||||
if (data.answer && data.answer.indexOf('</think>') !== -1) {
|
||||
this.isThink = false
|
||||
}
|
||||
},
|
||||
updateMessageContent({ answer }) {
|
||||
if (!this.currentMessage || !answer) return
|
||||
|
||||
// 使用 requestAnimationFrame 控制文本逐步显示
|
||||
|
||||
if (answer) {
|
||||
if (this.isThink) {
|
||||
this.currentMessage.think += answer
|
||||
} else {
|
||||
this.currentMessage.text += answer
|
||||
}
|
||||
}
|
||||
this.scrollToBottom()
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
messages: {
|
||||
handler() {
|
||||
this.$nextTick(() => this.scrollToBottom())
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$primary-color: #2e5ca9;
|
||||
$primary-text-color: #f6aa21;
|
||||
$primary-trans-color: rgba(135, 162, 208, 0.5);
|
||||
|
||||
.chat-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
|
||||
.chat-main {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 10px;
|
||||
background: #f7f8fa;
|
||||
|
||||
.chat-content {
|
||||
height: 100%;
|
||||
.message-area {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 10px 0 10px;
|
||||
background-color: #fff;
|
||||
gap: 10px;
|
||||
|
||||
button {
|
||||
padding: 5px 12px;
|
||||
border: none;
|
||||
background-color: $primary-trans-color;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.active {
|
||||
background-color: $primary-color;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
|
||||
input {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border: none;
|
||||
background: #f5f5f5;
|
||||
border-radius: 5px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
outline: none;
|
||||
color: $primary-text-color;
|
||||
border-radius: 5px;
|
||||
background: transparent;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.disabled {
|
||||
pointer-events: none;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
47
src/views/TreasureBox.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="treasure-box">
|
||||
<h1>百宝箱</h1>
|
||||
<p>欢迎来到百宝箱!这里为您提供各种实用工具和功能。</p>
|
||||
<!-- 在这里添加百宝箱的具体内容 -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TreasureBox',
|
||||
data() {
|
||||
return {
|
||||
// 可以在这里添加百宝箱相关的数据
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
// 可以在这里添加百宝箱相关的功能方法
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// 主题颜色定义
|
||||
$primary-color: #2E5CA9;
|
||||
$primary-text-color: #F6AA21;
|
||||
$primary-trans-color: #87A2D0;
|
||||
|
||||
.treasure-box {
|
||||
padding: 20px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
|
||||
h1 {
|
||||
color: $primary-color;
|
||||
font-size: 24px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
38
src/views/app/404.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<!--
|
||||
Codes Generated By ebiz-lowcode:
|
||||
http://www.ebiz-interactive.com/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="container" style='height: 100vh;'>
|
||||
<van-empty style='height: 100%' description='这里什么都没有' :image='notFoundImg' image-size='100vw' />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import notFoundImg from '@/assets/images/404.jpg'
|
||||
import {Empty} from 'vant'
|
||||
export default {
|
||||
components: {
|
||||
[Empty.name]:Empty
|
||||
},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
notFoundImg,
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
// H5提交函数
|
||||
submitForm() {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
38
src/views/app/Home.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<!--
|
||||
Codes Generated By ebiz-lowcode:
|
||||
http://www.ebiz-interactive.com/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {
|
||||
},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
formData: {},
|
||||
mtab37656ActiveTab: 'tab1',
|
||||
mtab49216ActiveTab: 'tab1'
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
created() {},
|
||||
mounted() {},
|
||||
methods: {
|
||||
// H5提交函数
|
||||
submitForm() {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
50
src/views/app/Login.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="login-container ph10">
|
||||
<h3 class="text-center pv30">欢迎登录</h3>
|
||||
<van-cell-group cl>
|
||||
<van-field v-model="username" clearable label="用户名" placeholder="请输入用户名" left-icon="contact" />
|
||||
<van-field v-model="password" type="password" label="密码" placeholder="请输入密码" left-icon="bag-o" />
|
||||
</van-cell-group>
|
||||
<van-button type="info" size="large" class="mt30" @click="login" :loading="loading" loading-text="登录中...">登录</van-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Field, CellGroup } from 'vant'
|
||||
import { login } from '@/api/app/user'
|
||||
export default {
|
||||
name: 'login',
|
||||
components: {
|
||||
[Field.name]: Field,
|
||||
[CellGroup.name]: CellGroup
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
username: 'admin',
|
||||
password: '123456',
|
||||
loading: false,
|
||||
redirect: this.$route.query.redirect
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
login() {
|
||||
if (this.username == '' || this.password == '') {
|
||||
this.$toast('账号或密码不能为空')
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
//登录
|
||||
login()
|
||||
.then(res => {
|
||||
localStorage.token = res.token
|
||||
this.$router.push({ path: this.redirect || '/' })
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err)
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
9
src/views/app/layout/Layout.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div class="layout-container">我是layout</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'layout'
|
||||
}
|
||||
</script>
|
||||
5
tests/unit/.eslintrc.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
env: {
|
||||
jest: true
|
||||
}
|
||||
}
|
||||
12
tests/unit/example.spec.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import { shallowMount } from '@vue/test-utils'
|
||||
import HelloWorld from '@/components/HelloWorld.vue'
|
||||
|
||||
describe('HelloWorld.vue', () => {
|
||||
it('renders props.msg when passed', () => {
|
||||
const msg = 'new message'
|
||||
const wrapper = shallowMount(HelloWorld, {
|
||||
propsData: { msg }
|
||||
})
|
||||
expect(wrapper.text()).toMatch(msg)
|
||||
})
|
||||
})
|
||||
73
vue.config.js
Normal file
@@ -0,0 +1,73 @@
|
||||
const autoprefixer = require('autoprefixer')
|
||||
const pxtoviewport = require('postcss-px-to-viewport')
|
||||
const path = require('path')
|
||||
function resolve(dir) {
|
||||
return path.join(__dirname, dir)
|
||||
}
|
||||
module.exports = {
|
||||
publicPath: '/',
|
||||
lintOnSave: false, //是否开启代码检查
|
||||
outputDir: 'dist', //打包输出目录
|
||||
productionSourceMap: false,
|
||||
devServer: {
|
||||
https: false,
|
||||
host: "0.0.0.0",
|
||||
disableHostCheck: true
|
||||
},
|
||||
css: {
|
||||
sourceMap: true, // 查看css属于哪个css文件
|
||||
loaderOptions: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
autoprefixer(),
|
||||
pxtoviewport({
|
||||
viewportWidth: 375,
|
||||
// 该项仅在使用 Circle 组件时需要
|
||||
// 原因参见 https://github.com/youzan/vant/issues/1948
|
||||
selectorBlackList: ['van-circle__layer']
|
||||
})
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
chainWebpack: (config) => {
|
||||
// 移除 prefetch 插件
|
||||
config.resolve.alias.set('@utils', resolve('./src/assets/js/utils'))
|
||||
|
||||
// config.plugins.delete('prefetch')
|
||||
/* 配置svg图标自动加载 begin */
|
||||
config.module.rule('svg').exclude.add(resolve('src/icons')).end()
|
||||
config.module
|
||||
.rule('icons')
|
||||
.test(/\.svg$/)
|
||||
.include.add(resolve('src/icons'))
|
||||
.end()
|
||||
.use('svg-sprite-loader')
|
||||
.loader('svg-sprite-loader')
|
||||
.options({
|
||||
symbolId: 'icon-[name]'
|
||||
})
|
||||
config.module
|
||||
.rule('mjs')
|
||||
.test(/\.mjs$/)
|
||||
.include.add(/node_modules/)
|
||||
.end()
|
||||
.use('babel-loader')
|
||||
.loader('babel-loader');
|
||||
//设置路径别名
|
||||
},
|
||||
configureWebpack: (config) => {
|
||||
;(config.devtool = 'source-map'), // 调试js
|
||||
(config.performance = {
|
||||
hints: 'error',
|
||||
//入口起点的最大体积 700kb
|
||||
maxEntrypointSize: 7168000,
|
||||
//生成文件的最大体积 700kb
|
||||
maxAssetSize: 7168000,
|
||||
//只给出 js 文件的性能提示
|
||||
assetFilter: function (assetFilename) {
|
||||
return assetFilename.endsWith('.js')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
23
vue编码规范.md
Normal file
@@ -0,0 +1,23 @@
|
||||
- Vue 文件命名: 大驼峰式命名法,即每个单词的首字母大写 CamelCase.vue,2-3 个单词用具体意义,不要过于简写。
|
||||
|
||||
- 定义变量使用 let ,定义常量使用 const,使用 export,import 模块化。
|
||||
|
||||
- 基于模块方式开发,每个 Vue 文件等同于一个模块,模块应该专注于解决单一问题,是独立的可服用的。
|
||||
|
||||
- 行内表达式尽量简化,太复杂了不宜阅读和维护,可以考虑使用 method 或是 computed 属性来替代其功能;如获取年:(new Date()).getFullYear(),不要写在行内,使用 computed 来实现。
|
||||
|
||||
- 组件 props 尽量使用原始类型(字符串、数字、布尔值),这样清晰直观,便于理解。尽量避免使用复杂的对象。使用 prop 验证,代码更严谨。具体请参考:prop 验证
|
||||
|
||||
- 使用 ES6 的箭头函数,这样就不用切换上下文,不用编写类似 let self = this 这样的代码。
|
||||
|
||||
- 组件自定义事件命名使用中横岗,对应组件外的一组意义操作。
|
||||
|
||||
- 避免使用:this.\$parent
|
||||
|
||||
- 谨慎使用:this.\$refs
|
||||
|
||||
- 样式作用域空间:<style scoped></style>
|
||||
|
||||
- 代码校验:[ESLint](https://eslint.org/)
|
||||
|
||||
- 其他请参考:[官方风格指南](https://cn.vuejs.org/v2/style-guide/)
|
||||
814
开发规范.md
Normal file
@@ -0,0 +1,814 @@
|
||||
# 一、文件命名规范
|
||||
|
||||
## 1.1 文件夹以及文件命名
|
||||
|
||||
规则:用简短有意义的`英文`(不能出现中文命名)来命名。
|
||||
|
||||
1、文件夹名称全部小写。如需连接可使用中横线“-”连字符。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
2、vue文件采用大驼峰形式。即第一个单词首字母大写,之后每个单词首字母大写。也可views —> 文件夹名称 —> Index.vue(文件夹可多级)
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
3、html、css、js文件命全部小写。如需连接可使用中横线“-”连字符。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
4、图片命名规则:全部小写,如需要可用下划线“_”连字符。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
## 1.2 vue文件内css的命名规则
|
||||
|
||||
规则:用简短有意义的英文来命名。如需连接可使用中横线“-”连字符。
|
||||
|
||||
1、规定html/vue的每一个容器都要加入对应的名字 ps(container一个页面只能有一个,如需所有的container有公共样式可如下图,如不需要可只保留对应的名字+container类名)
|
||||
|
||||

|
||||
|
||||
2、需加入scoped防止污染全局,如需覆盖框架样式加入::v-deep 即可。
|
||||
|
||||

|
||||
|
||||
## 1.3 vue文件内js的命名规则
|
||||
|
||||
1、常量命名有固定指意的全部大写,并用下划线分隔单词。无特定指意可采用小驼峰形式。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
2、变量命名采用小驼峰且使用let。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
3、Class、构造函数命名采用大驼峰。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
4、函数命名采用小驼峰。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
推荐:
|
||||
|
||||
| 动词 | 含义 | 返回值 |
|
||||
| --- | ----------- | ----------------------- |
|
||||
| can | 判断是否可执行某个动作 | true---可执行,false---不可执行 |
|
||||
| has | 判断是否含有某个值 | true---含有此值,false---无此值 |
|
||||
| is | 判断是否为某个值 | true---是此值,false---不是此值 |
|
||||
| get | 获取某个或某些值 | ----- |
|
||||
| set | 设置某个或某些值 | ----- |
|
||||
|
||||
5、组件命名采用大驼峰。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
6、import及export进来的变量名采用大驼峰。
|
||||
|
||||
正例:
|
||||
|
||||

|
||||
|
||||
反例:
|
||||
|
||||

|
||||
|
||||
# 二、编码规范
|
||||
|
||||
## 2.1 sass 规范
|
||||
|
||||
### 2.1.1 Sass 目录规范
|
||||
|
||||

|
||||
|
||||
### 2.1.2 目录说明
|
||||
|
||||
1、common.scss
|
||||
|
||||
初始化样式、样式出口文件,要在入口文件App.vue引入。
|
||||
|
||||

|
||||
|
||||
2、vant-ui.scss
|
||||
|
||||
如果vant某些样式不是你需要的,在 vant-ui.scss 里可以重写来覆盖vant样式。
|
||||
|
||||
如果你只想重写当前组件的样式,可在直接在组件里重写,如设置了style的scoped属性导致修改样式无效,可以使用::v-deep ,方法如下:
|
||||
|
||||

|
||||
|
||||
3、mixin.scss
|
||||
|
||||
@mixin 指令允许我们定义一个可以在整个样式表中重复使用的样式。
|
||||
|
||||
@include 指令可以将混入(定义的@mixin,可理解为sass模块)引入到文档中,实例如下:
|
||||
|
||||

|
||||
|
||||
可向混入传递变量,实例如下:
|
||||
|
||||

|
||||
|
||||
常用内外边距及类名缩写列表
|
||||
|
||||

|
||||
|
||||
常用边距模块
|
||||
|
||||

|
||||
|
||||
配合循环语句和判断语句来匹配并引入。
|
||||
|
||||

|
||||
|
||||
CSS输出
|
||||
|
||||

|
||||
|
||||
4、transition.scss
|
||||
|
||||
如果你需要在多处使用同一个固定CSS3动画,统一写在 transition.scss,如:
|
||||
|
||||

|
||||
|
||||
5、utils.scss
|
||||
|
||||
工具样式文件,如果你需要在多处使用同一个固定样式时可以写在此文件中,如:
|
||||
|
||||

|
||||
|
||||
6、variables.scss
|
||||
|
||||
如需声明变量,统一写在 variables.scss,用 $ 加变量名 声明,如:
|
||||
|
||||

|
||||
|
||||
### 2.1.3 全局CSS样式
|
||||
|
||||
1、浮动、清除浮动
|
||||
|
||||
- .fl 左浮动
|
||||
|
||||
- .fr 右浮动
|
||||
|
||||
- .chearfix 清除浮动
|
||||
|
||||
2、字体样式
|
||||
|
||||
- .fs12 字体尺寸 12px
|
||||
|
||||
- .fs14 字体尺寸 14px
|
||||
|
||||
- .fs16 字体尺寸 16px
|
||||
|
||||
- .fs18 字体尺寸 18px
|
||||
|
||||
- .fw600 字体粗细 600
|
||||
|
||||
- .fwb 字体粗细 bold
|
||||
|
||||
3、布局定位
|
||||
|
||||
- .relative 相对于其正常位置进行定位
|
||||
|
||||
- .absolute 相对于 static 定位以外的第一个父元素进行定位
|
||||
|
||||
- .fixed 相对于浏览器窗口定位
|
||||
|
||||
- .mh-auto 块级元素水平居中
|
||||
|
||||
- .hide 元素隐藏,但仍占用空间
|
||||
|
||||
- .hidden 元素隐藏,不占用空间
|
||||
|
||||
- .flex 弹性盒模型布局,块级元素
|
||||
|
||||
- .inline-flex 弹性盒模型布局,内联块元素
|
||||
|
||||
- .inline-b 内联块元素,表现为同行显示并可修改宽高内外边距等属性
|
||||
|
||||
- .justify-content-c 弹性项目居中填充
|
||||
|
||||
- .align-items-c 元素位于容器的中心。弹性盒子元素在该行的侧轴(纵轴)上居中放置
|
||||
|
||||
4、常用文字颜色
|
||||
|
||||
- .c-gray-base 文字颜色 #999
|
||||
|
||||
- .c-gray-dark 文字颜色 #666
|
||||
|
||||
- .c-gray-darker 文字颜色 #333
|
||||
|
||||
- .green 文字颜色 green
|
||||
|
||||
- .red 文字颜色 red
|
||||
|
||||
- .yellow 文字颜色 yellow
|
||||
|
||||
- .gray 文字颜色 gray
|
||||
|
||||
- .white 文字颜色 白色
|
||||
|
||||
4、背景颜色
|
||||
|
||||
- .bg-black 背景颜色 黑色 black
|
||||
|
||||
- .bg-white 背景颜色 白色 white
|
||||
|
||||
- .bg-none 背景颜色 透明 transparent
|
||||
|
||||
5、文字位置设置
|
||||
|
||||
- .text-left 左对齐
|
||||
|
||||
- .text-right 右对齐
|
||||
|
||||
- .text-center 居中对其
|
||||
|
||||
- .text-underline 文字下划线
|
||||
|
||||
6、垂直方向对齐方式
|
||||
|
||||
- .v-top 把元素的顶端与行中最高元素的顶端对齐 vertical-align: top
|
||||
|
||||
- .v-middle 把此元素放置在父元素的中部 vertical-align: middle
|
||||
|
||||
- .v-bottom 把元素的顶端与行中最低的元素的顶端对齐 vertical-align: bottom
|
||||
|
||||
- .v-baseline 默认,元素放置在父元素的基线上 vertical-align: baseline
|
||||
|
||||

|
||||
|
||||
7、单行省略
|
||||
|
||||
.ellipsis 单行省略
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
可根据宽度自适应显示文字长度
|
||||
|
||||

|
||||
|
||||
8、宽度、高度、内外边距、圆角弧度、透明度
|
||||
|
||||
- 我们在 variables.scss 文件里声明了** 宽度、高度、内外边距、圆角弧度、透明度** 常用的一些数值,以及一些类名缩写
|
||||
|
||||

|
||||
|
||||
- 在 mixin.scss 文件定义了混入函数,通过传参的方式,根据类名缩写及数值来匹配相应的样式。
|
||||
|
||||

|
||||
|
||||
- 并在样式出口文件 common.scss中引入,这样全局都可使用这些类名缩写来使用这些样式。
|
||||
|
||||

|
||||
|
||||
文档如下:
|
||||
|
||||
**宽度 、高度**
|
||||
|
||||
类名缩写:w,h
|
||||
|
||||
常用数值:40 50 60 70 80 86 100 110 120 140 150 155 160 180 192 200 220 260 280 315 325 350 400 440 445 450 550 700 1340
|
||||
|
||||
使用方法:类名 + 数值
|
||||
|
||||
例如:
|
||||
|
||||
宽度为100:<div class="w100"></div>
|
||||
|
||||
高度为100:<div class="h100"></div>
|
||||
|
||||
**圆角弧度**
|
||||
|
||||
类名缩写:radius
|
||||
|
||||
常用数值:1 2 3 4 5 6 7 8 9 10 12 15 18 20 50 100
|
||||
|
||||
使用方法:类名 + 数值
|
||||
|
||||
例如:
|
||||
|
||||
圆角弧度为10:<div class="radius10"></div>
|
||||
|
||||
**透明度**
|
||||
|
||||
类名缩写:opacity
|
||||
|
||||
常用数值:0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9
|
||||
|
||||
使用方法:类名 + 数值
|
||||
|
||||
例如:
|
||||
|
||||
透明度为0.9:<div class="opacity0.9"></div>
|
||||
|
||||
**内外边距**
|
||||
|
||||
类名缩写:m, mv, mh, mt, ml, mr, mb, p, pv, ph, pt, pl, pr, pb, top, left, right, bottom
|
||||
|
||||
- m margin 外边距
|
||||
|
||||
- mv margin-top margin-bottom 上下外边距
|
||||
|
||||
- mh margin-left margin-right 左右外边距
|
||||
|
||||
- mt margin-top 上外边距
|
||||
|
||||
- ml margin-left 左外边距
|
||||
|
||||
- mr margin-right 右外边距
|
||||
|
||||
- mb margin-bottom 下外边距
|
||||
|
||||
- p padding 内边距
|
||||
|
||||
- pv padding-top padding-bottom 上下内边距
|
||||
|
||||
- ph padding-left padding-right 左右内边距
|
||||
|
||||
- pt padding-top 上内边距
|
||||
|
||||
- pl padding-left 左内边距
|
||||
|
||||
- pr padding-right 右内边距
|
||||
|
||||
- pb padding-bottom 下内边距
|
||||
|
||||
- top 定位属性 top
|
||||
|
||||
- left 定位属性 left
|
||||
|
||||
- right 定位属性 right
|
||||
|
||||
- bottom 定位属性 bottom
|
||||
|
||||
常用数值:0 1 2 5 6 8 9 10 12 15 20 25 30 35 40 45 50 60 80 86 90 100
|
||||
|
||||
使用方法:类名 + 数值
|
||||
|
||||
例如:
|
||||
|
||||
左右外边距为30:<div class="mh30"></div>
|
||||
|
||||
上下内边距为30:<div class="pv30"></div>
|
||||
|
||||
定位属性top为30:<div class="top30"></div>
|
||||
|
||||
### 2.1.4 使用实例
|
||||
|
||||
代码为:
|
||||
|
||||

|
||||
|
||||
输出为:
|
||||
|
||||

|
||||
|
||||
代码为:
|
||||
|
||||

|
||||
|
||||
输出为:
|
||||
|
||||

|
||||
|
||||
## 2.2 vue 编码规范
|
||||
|
||||
### 2.2.1 vue 文件基本结构
|
||||
|
||||
组件顶级元素顺序按 `<template>`、`<script>`、`<style> `的顺序放置。
|
||||
|
||||

|
||||
|
||||
### 2.2.2 template
|
||||
|
||||
在template里的最外层放置容器,类名为 **组件名+container**,例如:
|
||||
|
||||

|
||||
|
||||
### 2.2.3 script
|
||||
|
||||
各个属性、周期,需按照如下顺序放置name、components、props、data、周期函数、methods、computed、watch、filter,其他规范如下:
|
||||
|
||||
- **name**:组件命名必填,和组件文件名保持一致,始终是 PascalCase,在DOM模板中应该是kebab-case
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
- **props**:定义需指定其类型,命名应该始终使用 camelCase,而在模板和 JSX 中应该始终使用 kebab-case
|
||||
|
||||

|
||||
|
||||
- **data:**变量注释规范 在变量后单行注释说明
|
||||
|
||||

|
||||
|
||||
- **methods**
|
||||
|
||||
建议使用扩展运算符拷贝对象
|
||||
|
||||

|
||||
|
||||
提交数据时不可直接使用页面渲染对象,推荐使用扩展运算符
|
||||
|
||||

|
||||
|
||||
- **computed:**复杂计算应使用计算属性 **computed**。
|
||||
|
||||
理论上,computed 所有实现可以使用 methods 完全替换。computed 只有在与它相关或者需要的数据发生改变时才会重新求值。
|
||||
|
||||

|
||||
|
||||
调用computed的方法时不需要加**()**。
|
||||
|
||||

|
||||
|
||||
- **watch**:当需要在数据变化时执行异步或开销较大的操作时应使用侦听器 **watch**。
|
||||
|
||||

|
||||
|
||||
上图中初始 fullName是没有值的,watch默认不会执行,只有当侦听的数据发生改变时才会执行,通过声明 **immediate **选项为** true**,可以立即执行一次 handler。
|
||||
|
||||

|
||||
|
||||
不使用 deep 时,当我们改变 obj.a 的值时,watch不能监听到数据变化,**默认情况下,handler 只监听属性引用的变化,也就是只监听了一层,但修改对象内部的属性是监听不到的**。
|
||||
|
||||
通过使用 **deep: true** 进行深入观察,这时,我们监听 obj,会把 obj 下面的属性层层遍历,都加上监听事件,这样做,**性能开销也会变大**,只要修改 obj 中任意属性值,都会触发 handler。
|
||||
|
||||

|
||||
|
||||
### 2.2.4 style
|
||||
|
||||
声明语言 **lang="scss"** ,为了避免样式冲突,添加 **scoped **属性,并避免出现元素选择器。
|
||||
|
||||
以嵌套的方式在父容器类里写你需要的样式,因为我们声明了很多公用的全局CSS样式,所以组件里的样式尽量不超过20行。
|
||||
|
||||
如果你想重写当前组件vant的样式,可使用** ::v-deep ** 重写,示例如下
|
||||
|
||||

|
||||
|
||||
### 2.2.5 指令规范
|
||||
|
||||
指令有缩写一律采用缩写形式,如v-bind、v-on简写成 **: **、**@**。
|
||||
|
||||

|
||||
|
||||
v-for 循环必须加上 **key** 属性,在整个 for 循环中 key 需要 **唯一** 避免 v-if 和 v-for 同时用在一个元素上(性能问题),可把 v-if 放在父容器上。
|
||||
|
||||

|
||||
|
||||
### 2.2.6 注释规范
|
||||
|
||||
代码注释在一个项目的后期维护中显的尤为重要,使用注释是良好的编程实践,它可以帮助他人理解代码的意图和功能,也方便自己在以后阅读代码时理解思路。
|
||||
|
||||
注释的内容主要包含这样三个方面:**做什么、为什么、怎么做**。对于一些复杂的组件和接口,我们可能还需要写明“如何用”。
|
||||
|
||||
文件和函数一定要写注释,而且要写得尽可能全面详细。函数内部的注释要相对少一些,一般都是靠好的命名、提炼函数、解释性变量、总结性注释来提高代码可读性。
|
||||
|
||||
**单行注释:**可以在语句之前和之后使用
|
||||
|
||||
```javascript
|
||||
alert('Hello, JavaScript'); // 输出Hello, JavaScript
|
||||
```
|
||||
|
||||
**多行注释:**用于跨多行注释代码片段
|
||||
|
||||
```javascript
|
||||
/*
|
||||
This is a multi-line comment.
|
||||
It can span across multiple lines.
|
||||
*/
|
||||
```
|
||||
|
||||
.**html/.js/.vue/.css文件注释**
|
||||
|
||||
文件顶部必须包含文件注释,用 @name 标识文件说明。标识符冒号与内容之间必须保留一个空格。新增的情况
|
||||
|
||||
当该业务项目主要由固定的一个或多个人负责时,需要添加**@author**标识,一方面是尊重劳动成果,另一方面方便在需要时快速定位责任人。
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* @name 文件名或模块名
|
||||
* @author 作者姓名
|
||||
* @created_date 创建时间 2018.07.05
|
||||
* @description 文件或模块描述
|
||||
*/
|
||||
```
|
||||
|
||||
- 修该的情况(追加)
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* @modified by 修改者姓名
|
||||
* @modified_date 修该时间2018.07.05
|
||||
* @description 修该点
|
||||
*/
|
||||
```
|
||||
|
||||
**常量**:每个常量都需要添加说明注释
|
||||
|
||||
**data**: data 数据必须添加单行注释
|
||||
|
||||
```javascript
|
||||
data () {
|
||||
return {
|
||||
name: 'zhangsan', // 姓名
|
||||
age: 18, // 年龄
|
||||
sex: '男', // 性别
|
||||
address: '北京市' // 地址
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**公共配置文件中的部分函数/方法/过滤器**等(css 或者样式选填)
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* @name 函数名
|
||||
* @author 作者姓名
|
||||
* @created_date 创建时间 2018.07.05
|
||||
* @description 函数功能和使用描述
|
||||
* @param {参数类型} 参数名称,如: date 字符串日期 “20200101”
|
||||
* @param {参数类型} 参数名称
|
||||
* @return 没有返回信息写 void / 有返回信息 {返回类型} 描述信息
|
||||
*/
|
||||
```
|
||||
|
||||
|
||||
|
||||
修该的情况(追加)
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* @modified by 修改者姓名
|
||||
* @modified_date 修该时间2018.07.05
|
||||
* @description 修该点
|
||||
*/
|
||||
```
|
||||
|
||||
**函数:**每个 js 函数添加注释,js函数的关键逻辑(流程、判断、变量处理方式等)添加注释说明
|
||||
|
||||
```javascript
|
||||
/*
|
||||
* 函数名
|
||||
* @description 函数功能和使用描述
|
||||
* @param {参数类型} 参数名称,如: date 字符串日期 “20200101”
|
||||
* @param {参数类型} 参数名称
|
||||
* @return 没有返回信息写 void / 有返回信息 {返回类型} 描述信息
|
||||
*/
|
||||
```
|
||||
|
||||
```text
|
||||
// 判断 xxxx, 如果xxxx,则xxxx
|
||||
if(xxxx) {
|
||||
|
||||
} else {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
**TODO 标记**
|
||||
|
||||
如果某段代码有功能未实现,或者有待完善,必须添加 **TODO **标记,**TODO **后应留一个**空格**。例如:
|
||||
|
||||
```javascript
|
||||
submit () {
|
||||
// TODO 今天偷懒了,明天补上
|
||||
}
|
||||
```
|
||||
|
||||
开发中不允许写死数据,如需测试数据,要添加 **TODO **标记备注,如:
|
||||
|
||||
```text
|
||||
userName: '小明' // TODO 测试数据,待修改
|
||||
```
|
||||
|
||||
## 2.3 样式规范
|
||||
|
||||
### 2.3.1 数值与单位
|
||||
|
||||
- 当属性值或颜色参数为 0 - 1 之间的数时,省略小数点前的 0
|
||||
|
||||
`~~color: rgba(255, 255, 255, 0.5)~~`
|
||||
|
||||
`color: rgba(255, 255, 255, .5);`
|
||||
|
||||
- 当长度值为 0 时省略单位。
|
||||
|
||||
`~~margin: 0px auto left: 0px;~~`~~ ~~
|
||||
|
||||
`margin: 0 auto; left: 0;`
|
||||
|
||||
- 十六进制的颜色属性值使用小写和尽量简写。
|
||||
|
||||
`~~color: #ffcc00~~`
|
||||
|
||||
`color: #fc0;`
|
||||
|
||||
### 2.3.2 样式属性顺序
|
||||
|
||||
单个样式规则下的属性在书写时,应按功能进行分组,并以 **Positioning Model > Box Model > Typographic > Visual** 的顺序书写,提高代码的可读性。
|
||||
|
||||
- 如果包含 **content **属性,应放在最前面;
|
||||
|
||||
- **Positioning Model **布局方式、位置,相关属性包括:**position / top / right / bottom / left / z-index / display / float / ...**
|
||||
|
||||
- **Box Model** 盒模型,相关属性包括:**width / height / padding / margin / border / overflow / ...**
|
||||
|
||||
- **Typographic **文本排版,相关属性包括:**font / line-height / text-align / word-wrap / ...**
|
||||
|
||||
- **Visual** 视觉外观,相关属性包括:**color / background / list-style / transform / animation / transition / ...**
|
||||
|
||||
**Positioning** 处在第一位,因为他可以使一个元素脱离正常文本流,并且覆盖盒模型相关的样式。盒模型紧跟其后,因为他决定了一个组件的大小和位置。其他属性只在组件内部起作用或者不会对前面两种情况的结果产生影响,所以他们排在后面。
|
||||
|
||||
### 2.3.3 合理使用使用引号
|
||||
|
||||
在某些样式中,会出现一些含有空格的关键字或者中文关键字。
|
||||
|
||||
- **font-family** 内使用引号
|
||||
|
||||
当字体名字中间有空格,中文名字体及 Unicode 字符编码表示的中文字体,为了保证兼容性,都建议在字体两端添加单引号:
|
||||
|
||||

|
||||
|
||||
- **background-image** 的 url内使用引号
|
||||
|
||||
如果路径里面有空格,旧版 IE 是无法识别的,会导致路径失效,所以不管是否有空格,都加上单引号:
|
||||
|
||||

|
||||
|
||||
### 2.3.4 避免使用!important
|
||||
|
||||
`!important` 的存在会给后期维护以及多人协作带来噩梦般的影响。当存在样式覆盖层叠时,如果你发现新定义的一个样式无法覆盖一个旧的样式,只有加上 `!important` 才能生效时,是因为你新定义的选择器的优先级不够旧样式选择器的优先级高。所以,合理的书写新样式选择器,是完全可以规避一些看似需要使用 `!important` 的情况的。
|
||||
|
||||
### 2.3.5 避免使用行内样式
|
||||
|
||||
行内样式指的是将CSS代码直接写在标签的style属性中,这种写法虽然简单,但是可能会造成代码混乱,难以维护。
|
||||
|
||||

|
||||
|
||||
### 2.3.6 避免不同样式应用方法混杂
|
||||
|
||||
在一个页面中保证只使用一种方法进行样式引入,以保证代码的整洁和一致,提高代码的可读性和可维护性
|
||||
|
||||
### 2.3.7 避免使用 Id 选择器进行样式绑定
|
||||
|
||||
在 Vue.js 和其他现代前端框架中,通常推荐以组件为单位进行样式的定义和管理,而组件的特性是可重用和可复制的,但是 ID 在一个 HTML 文档中必须是唯一的,所以如果你在组件的样式中使用 ID 选择器,当这个组件被重复使用时,就会造成 ID 冲突。
|
||||
|
||||
因此,应该尽量避免在 Vue.js 组件的样式中使用 ID 选择器。取而代之的是,你可以使用类选择器或属性选择器来定义样式,这样既可以保证样式的优先级不会过高,也可以避免 ID 冲突的问题。
|
||||
|
||||
## 2.4 JavaScrpit 规范
|
||||
|
||||
- 变量声明、表达式、return、throw、break、continue、do-while结束要加分号。
|
||||
|
||||
- 用'===', '!=='代替'==', '!=';
|
||||
|
||||
- 不要在同个作用域下声明同名变量;不要使用未声明的变量;不要声明了变量却不使用。
|
||||
|
||||
# 三、注意事项
|
||||
|
||||
## 3.1 通用事项
|
||||
|
||||
1. 开发时添加注释,新增或修改.html/.js/.vue/.css 文件等,添加注释,注意格式(参照2.2.6)
|
||||
|
||||
1. 调用对象方法/CSS3/HTML5 考虑兼容性,防止页面布局错乱(`document.body.clientWidth `||` document.documentElement.clientWidth`)
|
||||
|
||||
1. 调用接口时,注意上送参数格式(如金额、日期)
|
||||
|
||||
1. 开发过程中,页面中 `data` 里的变量必须添加注释
|
||||
|
||||
1. 前端获取重要数据在页面展示时,防止后端数据未返回,前端需考虑对此字段做缺省值处理,非判空处理
|
||||
|
||||
1. `new Date(date).getTime()`方法获取时间戳,在苹果手机上不兼容,返回 NAN,原因:ios 下只能解析反斜杠/而无法解析-导致的,解决办法:`new Date(date.replace(/-/g,'/')).getTime() `
|
||||
|
||||
1. 神策埋点 事件名(可不一致),属性名一致情况下,该属性值类型必须保持一致,否则导致后者埋点失败,若提供的神策埋点存在此问题,及时与模块负责人同步
|
||||
|
||||
1. 开发、测试、回归环境合并代码若存在冲突处理,再冲突处理完成后进行代码比对
|
||||
|
||||
1. 判空处理需与提供方(后端-接口、客户端-插件)确认为空返回的数据格式,切勿自行认定
|
||||
|
||||
1. 多考虑不同场景,做好影响性分析,切勿影响存量逻辑,或修改通用规则,全量排查后进行修改,切勿遗漏
|
||||
|
||||
1. 未登录场景调用接口,需与后端确认接口是否支持未登录调用
|
||||
|
||||
1. 接口涉及上送金额,统一使用公共方法(`$Fw.util.Format.formatAmt`)做金额格式转换接口金额字段要求:字符串、保留 2 位小数、反格式化
|
||||
|
||||
## 3.2 手机银行事项
|
||||
|
||||
### 3.2.1 公共 js
|
||||
|
||||
1. 涉及金额计算,请使用公共方法(`$Fw.add`,`$Fw.Numsub`,`$Fw.mul`,`$Fw.division`)进行加减乘除-**避免精度问题**
|
||||
|
||||
1. 通过公共方法(`$Fw.util.Format.getToday`)进行获取前几日、几月、几年-**页面中若使用 getToday 获取前几日/天/年方法,存在问题,对于特殊月份 2 月取值计算有误**
|
||||
|
||||
1. 接口涉及上送金额,统一使用公共方法(`$Fw.util.Format.formatAmt`)做金额格式转换 接口金额字段要求:字符串、保留 2 位小数、反格式化-**所有接口涉及上送金额字段,需要保留两位小数上送,如客户输入 100,接口上送时需要上送 100.00,否则导致接口报错**
|
||||
|
||||
### 3.2.2 页面渲染
|
||||
|
||||
1. 账号、金额等涉及接口上送字段,在页面使用过滤器进行格式化处理,不改变原有值-**js 中进行格式化处理,在上送时需反格式化处理,易遗漏**
|
||||
|
||||
1. 若需展示人民币符号,请复制粘贴"¥"此符号进行替换-**手动输入为错误符号¥,安卓机型可复现问题**
|
||||
|
||||
1. 调用接口,若字段为空,返回数据中无此字段,需做判空处理-**若返回无此字段,页面展示undefined**
|
||||
|
||||
### 3.2.3 代码规范
|
||||
|
||||
1. 删除页面多余的代码(模拟数据、方法)
|
||||
|
||||
1. 代码中判断条件语句不要用中文作为判断依据
|
||||
|
||||
1. 严格按照 eslint 规范,类型判断是 string 还是 number 类型要区分清楚
|
||||
|
||||
1. css、js、文件命名统一按照规范文档命名
|
||||
|
||||
### 3.2.4 功能逻辑
|
||||
|
||||
1. **交易完成后跳转结果页面,结果页需返回至首页,不可返回上页**(根据功能区分),不可出现死循环跳转
|
||||
|
||||
1. 添加埋点时,调用接口 fail 回调中加埋点变量时,需加报错弹窗 -**fail 自定义设置后,会默认不提示错误信息,需手动弹窗提示**
|
||||
|
||||
1. 调用接口若有 loading 加载框,fail 回调中需主动关闭 loading
|
||||
|
||||
1. 功能需求修改,需分析游客,非游客,登录,非登录,强弱认证以及当前页面接口数据是否返回为空对页面加载是否有影响-**不同条件会影响页面加载**
|
||||
|
||||
1. 新增功能需考虑是否支持未登录、游客身份客户,是否支持村行,功能优化需考虑是否涉及温情版改造
|
||||
|
||||
1. 功能界面若需登录/或游客认证,应考虑是否存在营销广告位、在线客服等不可控登录/游客入口进入
|
||||
|
||||
### 3.2.5 交互设计
|
||||
|
||||
1. 首次查询时不应展示上拉加载组件,应加判断条件,接口查询出数据后展示
|
||||
|
||||
1. 点击范围区域扩大
|
||||
|
||||
1. 功能首页涉及内容较多建议分组件开发、维护,各组件的生命周期中写对应方法—局部加载
|
||||
|
||||
1. tab 页面,仅首次点击时需做查询,无需每次点击时反复请求,非首次切换后,**客户可手动下拉刷新**,上拉加载;点击同一个 tab 页签,不做刷新处理
|
||||
|
||||
1. 页面涉及多接口调用,可自定义控制 loading 展示以及关闭,避免加载断层
|
||||
|
||||
1. 涉及**下一步展示密码弹窗场景,需自动弹起键盘,无需客户手动再次点击**,此项纳入概要设计、代码检查规范中
|
||||
|
||||