初始化
This commit is contained in:
commit
f32d7b0493
3
.browserslistrc
Normal file
3
.browserslistrc
Normal file
@ -0,0 +1,3 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not dead
|
||||
23
.env.development
Normal file
23
.env.development
Normal file
@ -0,0 +1,23 @@
|
||||
# 只在开发模式中被载入
|
||||
|
||||
# 网站前缀
|
||||
VITE_BASE_URL = /
|
||||
|
||||
# base api
|
||||
|
||||
VITE_BASE_API_VERSION = '1.0.002'
|
||||
|
||||
VITE_BASE_API_go = 'https://health.ufutx.com/go/api'
|
||||
|
||||
VITE_BASE_API = 'https://health.ufutx.com/api'
|
||||
|
||||
VITE_BASE_API_APP = '//app.health.ufutx.com/api'
|
||||
|
||||
# 是否兼容旧的浏览器
|
||||
VITE_LEGACY = false
|
||||
|
||||
# mock api
|
||||
VITE_MOCK_API = '/mock-api/'
|
||||
|
||||
# 是否删除console
|
||||
VITE_DROP_CONSOLE = false
|
||||
22
.env.production
Normal file
22
.env.production
Normal file
@ -0,0 +1,22 @@
|
||||
# 只在开发模式中被载入
|
||||
|
||||
# 网站前缀
|
||||
VITE_BASE_URL = /work/
|
||||
|
||||
# base api
|
||||
|
||||
VITE_BASE_API_VERSION = '1.0.002'
|
||||
|
||||
VITE_BASE_API_go = '//health.ufutx.com/go/api'
|
||||
|
||||
VITE_BASE_API = '//health.ufutx.com/api'
|
||||
|
||||
VITE_BASE_API_APP = '//health.ufutx.com/api'
|
||||
|
||||
# 是否兼容旧的浏览器
|
||||
VITE_LEGACY = false
|
||||
|
||||
# mock api
|
||||
VITE_MOCK_API = '/mock-api/'
|
||||
# 是否删除console
|
||||
VITE_DROP_CONSOLE = true
|
||||
16
.eslintignore
Normal file
16
.eslintignore
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
*.sh
|
||||
node_modules
|
||||
*.md
|
||||
*.woff
|
||||
*.ttf
|
||||
.vscode
|
||||
.idea
|
||||
dist
|
||||
/public
|
||||
/docs
|
||||
.husky
|
||||
.local
|
||||
/bin
|
||||
/src/mock
|
||||
Dockerfile
|
||||
108
.eslintrc.js
Normal file
108
.eslintrc.js
Normal file
@ -0,0 +1,108 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parser: 'vue-eslint-parser',
|
||||
parserOptions: {
|
||||
parser: '@typescript-eslint/parser',
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
jsxPragma: 'React',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
tsx: true,
|
||||
},
|
||||
},
|
||||
plugins: ['@typescript-eslint', 'prettier', 'import'],
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:vue/vue3-recommended', 'prettier'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.vue', '*d.ts'],
|
||||
rules: {
|
||||
'prefer-const': 0,
|
||||
'no-undef': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
// js/ts
|
||||
// 'no-console': ['warn', { allow: ['error'] }],
|
||||
'no-restricted-syntax': ['error', 'LabeledStatement', 'WithStatement'],
|
||||
camelcase: ['error', { properties: 'never' }],
|
||||
|
||||
'no-var': 'error',
|
||||
'no-empty': ['error', { allowEmptyCatch: true }],
|
||||
'no-void': 'error',
|
||||
'prefer-const': ['warn', { destructuring: 'all', ignoreReadBeforeAssign: true }],
|
||||
'prefer-template': 'error',
|
||||
'object-shorthand': ['error', 'always', { ignoreConstructors: false, avoidQuotes: true }],
|
||||
'block-scoped-var': 'error',
|
||||
'no-constant-condition': ['error', { checkLoops: false }],
|
||||
|
||||
'no-redeclare': 'off',
|
||||
'@typescript-eslint/no-redeclare': 'error',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
|
||||
// '@typescript-eslint/consistent-type-imports': ['error', { disallowTypeAnnotations: false }],
|
||||
'@typescript-eslint/no-var-requires': 'off',
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
'no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
},
|
||||
],
|
||||
|
||||
// vue
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/require-default-prop': 'off',
|
||||
'vue/require-explicit-emits': 'off',
|
||||
'vue/multi-word-component-names': 'off',
|
||||
|
||||
// prettier
|
||||
'prettier/prettier': 'error',
|
||||
|
||||
// import
|
||||
'import/first': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
|
||||
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: 'vue',
|
||||
group: 'external',
|
||||
position: 'before',
|
||||
},
|
||||
{
|
||||
pattern: '@vue/**',
|
||||
group: 'external',
|
||||
position: 'before',
|
||||
},
|
||||
{
|
||||
pattern: 'ant-design-vue',
|
||||
group: 'internal',
|
||||
},
|
||||
],
|
||||
pathGroupsExcludedImportTypes: ['type'],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
6
.gitattributes
vendored
Normal file
6
.gitattributes
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
* text=auto eol=lf
|
||||
*.ts linguist-detectable=false
|
||||
*.css linguist-detectable=false
|
||||
*.scss linguist-detectable=false
|
||||
*.js linguist-detectable=true
|
||||
*.vue linguist-detectable=true
|
||||
57
.github/workflows/gh-pages.yml
vendored
Normal file
57
.github/workflows/gh-pages.yml
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
name: syncToGitee
|
||||
env:
|
||||
# 7 GiB by default on GitHub, setting to 6 GiB
|
||||
# https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#supported-runners-and-hardware-resources
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
jobs:
|
||||
repo-sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js v14.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '14.x'
|
||||
|
||||
- name: Install
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
|
||||
- name: Deploy
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
publish_dir: ./dist
|
||||
personal_token: ${{ secrets.PERSONAL_TOKEN }}
|
||||
commit_message: Update ghPages
|
||||
force_orphan: true
|
||||
|
||||
- name: Mirror the Github organization repos to Gitee.
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: 'github/buqiyuan'
|
||||
dst: 'gitee/buqiyuan'
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
static_list: 'vite-vue3-h5'
|
||||
force_update: true
|
||||
debug: true
|
||||
|
||||
- name: Build Gitee Pages
|
||||
uses: yanglbme/gitee-pages-action@main
|
||||
with:
|
||||
# 注意替换为你的 Gitee 用户名
|
||||
gitee-username: buqiyuan
|
||||
# 注意在 Settings->Secrets 配置 GITEE_PASSWORD
|
||||
gitee-password: ${{ secrets.GITEE_PASSWORD }}
|
||||
# 注意替换为你的 Gitee 仓库,仓库名严格区分大小写,请准确填写,否则会出错
|
||||
gitee-repo: buqiyuan/vite-vue3-h5
|
||||
# 是否强制使用 HTTPS
|
||||
https: false
|
||||
# 要部署的分支,默认是 master,若是其他分支,则需要指定(指定的分支必须存在)
|
||||
branch: gh-pages
|
||||
32
.gitignore
vendored
Normal file
32
.gitignore
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
node_modules
|
||||
components.d.ts
|
||||
.DS_Store
|
||||
dist
|
||||
.npmrc
|
||||
.cache
|
||||
|
||||
public/version.json
|
||||
|
||||
tests/server/static
|
||||
tests/server/static/upload
|
||||
|
||||
.local
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
.eslintcache
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
*pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
# .vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
11
.prettierignore
Normal file
11
.prettierignore
Normal file
@ -0,0 +1,11 @@
|
||||
/dist/*
|
||||
.local
|
||||
.output.js
|
||||
/node_modules/**
|
||||
|
||||
.yarnrc
|
||||
|
||||
**/*.svg
|
||||
**/*.sh
|
||||
|
||||
/public/*
|
||||
2
.stylelintignore
Normal file
2
.stylelintignore
Normal file
@ -0,0 +1,2 @@
|
||||
/dist/*
|
||||
/public/*
|
||||
13
.vscode/extensions.json
vendored
Normal file
13
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"vue.volar",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"stylelint.vscode-stylelint",
|
||||
"esbenp.prettier-vscode",
|
||||
"mrmlnc.vscode-less",
|
||||
"lokalise.i18n-ally",
|
||||
"antfu.iconify",
|
||||
"mikestead.dotenv",
|
||||
"heybourn.headwind"
|
||||
]
|
||||
}
|
||||
13
.vscode/launch.json
vendored
Normal file
13
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome",
|
||||
"url": "http://localhost:3100",
|
||||
"webRoot": "${workspaceFolder}/src",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
}
|
||||
104
.vscode/settings.json
vendored
Normal file
104
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
{
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"volar.tsPlugin": true,
|
||||
"volar.tsPluginStatus": false,
|
||||
"npm.packageManager": "yarn",
|
||||
"editor.tabSize": 2,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"files.eol": "\n",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
"**/*.log": true,
|
||||
"**/*.log*": true,
|
||||
"**/bower_components": true,
|
||||
"**/dist": true,
|
||||
"**/elehukouben": true,
|
||||
"**/.git": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.svn": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/.idea": true,
|
||||
"**/.vscode": false,
|
||||
"**/yarn.lock": true,
|
||||
"**/tmp": true,
|
||||
"out": true,
|
||||
"dist": true,
|
||||
"node_modules": true,
|
||||
"CHANGELOG.md": true,
|
||||
"examples": true,
|
||||
"res": true,
|
||||
"screenshots": true,
|
||||
"yarn-error.log": true,
|
||||
"**/.yarn": true
|
||||
},
|
||||
"files.exclude": {
|
||||
"**/.cache": true,
|
||||
"**/.editorconfig": true,
|
||||
"**/.eslintcache": true,
|
||||
"**/bower_components": true,
|
||||
"**/.idea": true,
|
||||
"**/tmp": true,
|
||||
"**/.git": true,
|
||||
"**/.svn": true,
|
||||
"**/.hg": true,
|
||||
"**/CVS": true,
|
||||
"**/.DS_Store": true
|
||||
},
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/.vscode/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/tmp/**": true,
|
||||
"**/bower_components/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/yarn.lock": true
|
||||
},
|
||||
"stylelint.enable": true,
|
||||
"stylelint.packageManager": "yarn",
|
||||
"path-intellisense.mappings": {
|
||||
"@/": "${workspaceRoot}/src"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[less]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true,
|
||||
"source.fixAll.stylelint": true
|
||||
}
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/locales/lang"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"]
|
||||
}
|
||||
8
.yarnrc
Normal file
8
.yarnrc
Normal file
@ -0,0 +1,8 @@
|
||||
registry "https://registry.npmmirror.com"
|
||||
|
||||
sass_binary_site "https://npmmirror.com/mirrors/node-sass/"
|
||||
phantomjs_cdnurl "http://cnpmjs.org/downloads"
|
||||
electron_mirror "https://npmmirror.com/mirrors/electron/"
|
||||
sqlite3_binary_host_mirror "https://foxgis.oss-cn-shanghai.aliyuncs.com/"
|
||||
profiler_binary_host_mirror "https://npmmirror.com/mirrors/node-inspector/"
|
||||
chromedriver_cdnurl "https://cdn.npm.taobao.org/dist/chromedriver"
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 bqy
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
39
README.md
Normal file
39
README.md
Normal file
@ -0,0 +1,39 @@
|
||||
## 移动端布局方案
|
||||
|
||||
移动端 vant3.x + vue3.x + vite2.x + ts.4.x + REM 布局 + Viewport (VW) 布局的实例运用
|
||||
|
||||
## 在线预览
|
||||
|
||||
http://buqiyuan.gitee.io/vite-vue3-h5
|
||||
|
||||
提供三个布局方案
|
||||
|
||||
**1. REM 布局**
|
||||
|
||||
使用 js 动态设置 html 的 font-size,css 使用 rem 单位,文本大小可选择使用 px
|
||||
|
||||
js 设置 viewport 的 scale 以支持高清设备的 1px
|
||||
|
||||
可设置页面最大最小宽度
|
||||
|
||||
**2. VW 布局**
|
||||
|
||||
css 使用 vw 单位,文本大小可选择使用 px
|
||||
|
||||
使用 transform 以支持高清设备的边框 1px(包括圆角),使用 @mixin `./vw/scss/_border.scss`
|
||||
|
||||
可设置容器固定纵横比,不可设置页面最大最小宽度
|
||||
|
||||
**3. REM + VW 布局**
|
||||
|
||||
html 的 font-size 使用 vw 单位,css 使用 rem 单位,文本大小可选择使用 px
|
||||
|
||||
使用 transform 以支持高清设备的边框 1px(包括圆角),使用 @mixin `./vw-rem/scss/_border.scss`
|
||||
|
||||
可设置容器固定纵横比,可设置页面最大最小宽度
|
||||
|
||||
## 使用
|
||||
|
||||
1. yarn dev
|
||||
|
||||
2. 业务代码中样式的调用方式可参考 `./rem/scss/rem.scss` 及 `./vw/scss/vw.scss` 及 `./vw-rem/scss/vw-rem.scss` 三个文件;可在 html 文件相应位置配置 `data-content-max` 属性来限制容器最大最小宽
|
||||
4
auto-imports.d.ts
vendored
Normal file
4
auto-imports.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
// Generated by 'unplugin-auto-import'
|
||||
// We suggest you to commit this file into source control
|
||||
declare global {}
|
||||
export {};
|
||||
13
commitlint.config.js
Normal file
13
commitlint.config.js
Normal file
@ -0,0 +1,13 @@
|
||||
module.exports = {
|
||||
ignores: [(commit) => commit.includes('init')],
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
'body-leading-blank': [2, 'always'],
|
||||
'footer-leading-blank': [1, 'always'],
|
||||
'header-max-length': [2, 'always', 108],
|
||||
'subject-empty': [2, 'never'],
|
||||
'type-empty': [2, 'never'],
|
||||
'subject-case': [0],
|
||||
'type-enum': [2, 'always', ['feat', 'fix', 'perf', 'style', 'docs', 'test', 'refactor', 'build', 'ci', 'chore', 'revert', 'wip', 'workflow', 'types', 'release']],
|
||||
},
|
||||
};
|
||||
51
index.html
Normal file
51
index.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-content-max>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="stylesheet" href="./src/styles/common.scss" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<title>友福同享</title>
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
<body data-content-max>
|
||||
<div id="app"></div>
|
||||
<!-- 企业微信开发 引入 jwxwork sdk -->
|
||||
<script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js" referrerpolicy="origin"></script>
|
||||
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js" referrerpolicy="origin"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.2/echarts.min.js" referrerpolicy="origin"></script>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
<script>
|
||||
(function () {
|
||||
// if (location.href.includes('localhost')) {
|
||||
// return;
|
||||
// }
|
||||
// ('use strict');
|
||||
// const SCRIPT_URL = 'https://dldir1.qq.com/WechatWebDev/devPlatform/px.min.js';
|
||||
// const param = {
|
||||
// maskMode: 'no-mask', // 隐私策略, all-mask 或 no-mask, 详见:https://www.npmjs.com/package/@wxobs/miniprogram-sdk#%E6%96%87%E6%A1%A3
|
||||
// recordCanvas: false, // 若要采集canvas, 设为true
|
||||
// projectId: 'wxee293b036a1af0c2-DiDfLdsVO-JsRI7',
|
||||
// attrs: {}, // 对回放添加自定义属性,在管理端可通过属性筛选回放 (对象中 key 和 value 类型必须为 string),
|
||||
// iframe: false, // 是否采集 iframe 页面
|
||||
// };
|
||||
// const scriptEle = document.createElement('script');
|
||||
// scriptEle.type = 'text/javascript';
|
||||
// scriptEle.async = true;
|
||||
// scriptEle.onload = function () {
|
||||
// window.__startPX(param); // 启动采集,返回 Promise<{ sessionId: string }>
|
||||
// };
|
||||
// scriptEle.src = SCRIPT_URL;
|
||||
// document.getElementsByTagName('script')[0].parentNode.appendChild(scriptEle);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
20
mock/_createProductionServer.ts
Normal file
20
mock/_createProductionServer.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
|
||||
|
||||
const modules = import.meta.globEager('./**/*.ts');
|
||||
|
||||
const mockModules: any[] = [];
|
||||
Object.keys(modules).forEach((key) => {
|
||||
if (key.includes('/_')) {
|
||||
return;
|
||||
}
|
||||
mockModules.push(...modules[key].default);
|
||||
});
|
||||
|
||||
/**
|
||||
* Used in a production environment. Need to manually import all modules
|
||||
*/
|
||||
export function setupProdMockServer() {
|
||||
console.log('mockModules', mockModules);
|
||||
|
||||
createProdMockServer(mockModules);
|
||||
}
|
||||
56
mock/_util.ts
Normal file
56
mock/_util.ts
Normal file
@ -0,0 +1,56 @@
|
||||
// Interface data format used to return a unified format
|
||||
|
||||
export function resultSuccess<T = Recordable>(data: T, { message = 'ok' } = {}) {
|
||||
return {
|
||||
code: 200,
|
||||
data,
|
||||
message,
|
||||
type: 'success',
|
||||
};
|
||||
}
|
||||
|
||||
export function resultPageSuccess<T = any>(page: number, pageSize: number, list: T[], { message = 'ok' } = {}) {
|
||||
const pageData = pagination(page, pageSize, list);
|
||||
|
||||
return {
|
||||
...resultSuccess({
|
||||
list: pageData,
|
||||
pagination: {
|
||||
page: ~~page,
|
||||
size: ~~pageSize,
|
||||
total: list.length,
|
||||
},
|
||||
}),
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
export function resultError(message = 'Request failed', { code = -1, result = null } = {}) {
|
||||
return {
|
||||
code,
|
||||
result,
|
||||
message,
|
||||
type: 'error',
|
||||
};
|
||||
}
|
||||
|
||||
export function pagination<T = any>(page: number, pageSize: number, array: T[]): T[] {
|
||||
const offset = (page - 1) * Number(pageSize);
|
||||
const ret = offset + Number(pageSize) >= array.length ? array.slice(offset, array.length) : array.slice(offset, offset + Number(pageSize));
|
||||
return ret;
|
||||
}
|
||||
|
||||
export interface requestParams {
|
||||
method: string;
|
||||
body: any;
|
||||
headers?: { authorization?: string };
|
||||
query: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
|
||||
*
|
||||
*/
|
||||
export function getRequestToken({ headers }: requestParams): string | undefined {
|
||||
return headers?.authorization;
|
||||
}
|
||||
12453
mock/log/_cateList.json
Normal file
12453
mock/log/_cateList.json
Normal file
File diff suppressed because it is too large
Load Diff
17
mock/log/index.ts
Normal file
17
mock/log/index.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { MockMethod } from 'vite-plugin-mock';
|
||||
import { resultSuccess } from '../_util';
|
||||
|
||||
import data from './_cateList.json';
|
||||
|
||||
export default [
|
||||
{
|
||||
url: '/mock-api/category/list',
|
||||
timeout: 0,
|
||||
method: 'get',
|
||||
response: ({ query }) => {
|
||||
console.log('query', query);
|
||||
|
||||
return resultSuccess(data);
|
||||
},
|
||||
},
|
||||
] as MockMethod[];
|
||||
11585
package-lock.json
generated
Normal file
11585
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
117
package.json
Normal file
117
package.json
Normal file
@ -0,0 +1,117 @@
|
||||
{
|
||||
"name": "vite-vue3-h5",
|
||||
"version": "0.0.0",
|
||||
"homepage": "git@buqiyuan.github.io/vite-vue3-h5",
|
||||
"scripts": {
|
||||
"serve": "npm run dev",
|
||||
"dev": "vite --host=0.0.0.0",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"deploy": "gh-pages -d dist",
|
||||
"format": "prettier --write ./src",
|
||||
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
|
||||
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
|
||||
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,less,postcss,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
|
||||
"lint:lint-staged": "lint-staged",
|
||||
"postversion": "git push && git push origin --tags",
|
||||
"reinstall": "rimraf yarn.lock && rimraf package.lock.json && rimraf node_modules && npm run dev",
|
||||
"version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
|
||||
"test:gzip": "npx http-server dist --cors --gzip -c-1",
|
||||
"test:br": "npx http-server dist --cors --brotli -c-1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vant/area-data": "^1.3.1",
|
||||
"@vant/touch-emulator": "^1.3.2",
|
||||
"@vueuse/core": "^8.7.5",
|
||||
"axios": "^0.27.2",
|
||||
"dayjs": "^1.11.3",
|
||||
"js-md5": "^0.7.3",
|
||||
"lodash-es": "^4.17.21",
|
||||
"pinia": "^2.0.13",
|
||||
"qs": "^6.11.0",
|
||||
"vant": "^4.0.0-alpha.4",
|
||||
"vconsole": "^3.14.6",
|
||||
"vue": "^3.2.37",
|
||||
"vue-router": "^4.0.16",
|
||||
"weixin-js-sdk": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.0.3",
|
||||
"@commitlint/config-conventional": "^17.0.3",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/node": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.0",
|
||||
"@typescript-eslint/parser": "^5.30.0",
|
||||
"@vitejs/plugin-legacy": "^1.8.2",
|
||||
"@vitejs/plugin-vue": "^2.3.3",
|
||||
"@vitejs/plugin-vue-jsx": "^1.3.10",
|
||||
"@vue/compiler-sfc": "3.2.33",
|
||||
"@vue/eslint-config-typescript": "^11.0.0",
|
||||
"commitizen": "^4.2.4",
|
||||
"conventional-changelog-cli": "^2.2.2",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"eslint": "^8.18.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.1.0",
|
||||
"eslint-plugin-vue": "^9.1.1",
|
||||
"gh-pages": "^4.0.0",
|
||||
"lint-staged": "^13.0.3",
|
||||
"mockjs": "^1.1.0",
|
||||
"postcss": "^8.4.14",
|
||||
"postcss-html": "^1.4.1",
|
||||
"postcss-scss": "^4.0.4",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.53.0",
|
||||
"stylelint": "^14.9.1",
|
||||
"stylelint-config-html": "^1.0.0",
|
||||
"stylelint-config-prettier": "^9.0.3",
|
||||
"stylelint-config-recommended": "^8.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.4.0",
|
||||
"stylelint-config-standard": "^26.0.0",
|
||||
"stylelint-order": "^5.0.0",
|
||||
"stylelint-scss": "^4.2.0",
|
||||
"typescript": "^4.7.4",
|
||||
"unplugin-vue-components": "^0.20.1",
|
||||
"unplugin-vue-define-options": "^0.6.1",
|
||||
"vite": "^2.9.13",
|
||||
"vite-plugin-checker": "^0.4.6",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vite-plugin-style-import": "^1.4.1",
|
||||
"vue-eslint-parser": "^9.0.3",
|
||||
"vue-tsc": "^0.38.2"
|
||||
},
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12 || >=14"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./src/**/*.{js,jsx,ts,tsx,vue}": [
|
||||
"eslint --fix",
|
||||
"git add ."
|
||||
],
|
||||
"*.{js,jsx,ts,tsx}": [
|
||||
"eslint --fix",
|
||||
"prettier --write"
|
||||
],
|
||||
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
|
||||
"prettier --write"
|
||||
],
|
||||
"package.json": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.vue": [
|
||||
"eslint --fix",
|
||||
"prettier --write",
|
||||
"stylelint --fix"
|
||||
],
|
||||
"*.{scss,less,styl,html}": [
|
||||
"stylelint --fix",
|
||||
"prettier --write"
|
||||
],
|
||||
"*.md": [
|
||||
"prettier --write"
|
||||
]
|
||||
}
|
||||
}
|
||||
11
prettier.config.js
Normal file
11
prettier.config.js
Normal file
@ -0,0 +1,11 @@
|
||||
module.exports = {
|
||||
printWidth: 800,
|
||||
semi: true,
|
||||
vueIndentScriptAndStyle: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
proseWrap: 'never',
|
||||
htmlWhitespaceSensitivity: 'strict',
|
||||
endOfLine: 'auto',
|
||||
useTabs: false,
|
||||
};
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
50
src/App.vue
Normal file
50
src/App.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="ui-app">
|
||||
<router-view #="{ Component }">
|
||||
<keep-alive :include="include">
|
||||
<component :is="Component"></component>
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
// 需要缓存的页面
|
||||
const include = ref<any[]>(['DiscussList', 'WorkOrder', 'Assess', 'Remarks', 'Surveys', 'GroupOrders', 'MeasurementRecord', 'DiscussList', 'WineRecords']);
|
||||
|
||||
onMounted(() => {
|
||||
console.log('11');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html {
|
||||
@include root-font-size();
|
||||
}
|
||||
|
||||
.ui-app {
|
||||
--container-height: 100vh;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.van-pull-refresh {
|
||||
min-height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.ui-bubble {
|
||||
overflow: initial !important;
|
||||
background: none !important;
|
||||
}
|
||||
</style>
|
||||
54
src/api/demo.ts
Normal file
54
src/api/demo.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import weChat from '@/utils/weChat';
|
||||
|
||||
// 登录
|
||||
export function getLogin() {
|
||||
let data = { url: window.location.href.split('#')[0] };
|
||||
return weChat({
|
||||
hideLoading: true,
|
||||
data,
|
||||
url: '/h5/wechat/work/login',
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 获取版本号
|
||||
export function getVersion() {
|
||||
return weChat({
|
||||
hideLoading: true,
|
||||
url: 'get/version?type=2',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// 微信、企业微信config配置
|
||||
export function getWxConfig() {
|
||||
let data = { url: window.location.href.split('#')[0] };
|
||||
return weChat({
|
||||
hideLoading: true,
|
||||
data,
|
||||
url: '/h5/work/js/sdk/config/v2',
|
||||
method: 'post',
|
||||
});
|
||||
}
|
||||
|
||||
// 阿里云上传配置
|
||||
export function getALiYun(token) {
|
||||
let data = token;
|
||||
return weChat({
|
||||
data,
|
||||
hideLoading: true,
|
||||
url: 'get/oss/config',
|
||||
method: 'get',
|
||||
});
|
||||
}
|
||||
|
||||
// // 企业微信config配置
|
||||
// export function getAuthTicket() {
|
||||
// let data = { url: window.location.href.split('#')[0], type: 'app' };
|
||||
// return weChat({
|
||||
// hideLoading: true,
|
||||
// data,
|
||||
// url: '/h5/work/js/sdk/config',
|
||||
// method: 'post',
|
||||
// });
|
||||
// }
|
||||
14
src/api/models/userModel.ts
Normal file
14
src/api/models/userModel.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// 用户登录参数
|
||||
export interface LoginParams {
|
||||
account: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 登录成功返回结果
|
||||
*/
|
||||
export interface LoginResult {
|
||||
username: string;
|
||||
avatar: string;
|
||||
token: string;
|
||||
}
|
||||
15
src/api/user.ts
Normal file
15
src/api/user.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// import { LoginParams, LoginResult } from './models/userModel';
|
||||
// import request from '@/utils/request';
|
||||
|
||||
// enum Api {
|
||||
// getUserId = '/base/appLogin', // 获取用户userid
|
||||
// }
|
||||
|
||||
// 根据code获取userid / token
|
||||
// export function getUserId(data: LoginParams) {
|
||||
// return request<LoginResult>({
|
||||
// url: Api.getUserId,
|
||||
// method: 'post',
|
||||
// data,
|
||||
// });
|
||||
// }
|
||||
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
326
src/components/menuSettingTop.vue
Normal file
326
src/components/menuSettingTop.vue
Normal file
@ -0,0 +1,326 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
detail: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
id: 0,
|
||||
name: '',
|
||||
mode: '',
|
||||
date: '',
|
||||
avatar: '',
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:date', 'update:mode']);
|
||||
|
||||
const activeModal = ref(-1);
|
||||
const initialDate = ref('--');
|
||||
const date = ref('--');
|
||||
const showDate = ref(false);
|
||||
|
||||
const dateValues = ref<any[]>([`${new Date().getFullYear()}`, `${new Date().getMonth() + 1 < 10 ? `0${new Date().getMonth() + 1}` : new Date().getMonth() + 1}`, `${new Date().getDate()}`]);
|
||||
|
||||
const minDate = new Date(new Date().getFullYear() - 120, 0, 1);
|
||||
const maxDate = (() => {
|
||||
const d = new Date();
|
||||
d.setDate(1);
|
||||
d.setMonth(d.getMonth() + 1);
|
||||
d.setDate(Math.min(new Date().getDate(), new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate()));
|
||||
d.setHours(0, 0, 0, 0);
|
||||
return d;
|
||||
})();
|
||||
|
||||
// 模式文本映射
|
||||
const getModalText = (mode: number) => {
|
||||
if (mode === 1) return '减肥型';
|
||||
if (mode === 2) return '其他型';
|
||||
return '--';
|
||||
};
|
||||
|
||||
const modalText = ref('未选择');
|
||||
let hasUserChangedMode = false;
|
||||
let hasUserChangedDate = false;
|
||||
const showChange = ref(false);
|
||||
const showModalText = ref('');
|
||||
|
||||
// 切换模式
|
||||
const changeModal = (type) => {
|
||||
activeModal.value = type;
|
||||
modalText.value = getModalText(type);
|
||||
hasUserChangedMode = true;
|
||||
maybeEmitMode();
|
||||
showModal.value = false;
|
||||
};
|
||||
|
||||
let initialModeValue = '';
|
||||
let initialDateValue = '';
|
||||
|
||||
// 监听详情赋值
|
||||
watch(
|
||||
() => props.detail,
|
||||
(newDetail) => {
|
||||
if (!newDetail) return;
|
||||
|
||||
// 优先级:log.id 存在则用 log.model,否则用外层 model
|
||||
let realModel = 0;
|
||||
if (newDetail.log && newDetail.log.id > 0) {
|
||||
realModel = newDetail.log.model ?? 0;
|
||||
} else {
|
||||
realModel = newDetail.model ?? 0;
|
||||
}
|
||||
|
||||
activeModal.value = realModel;
|
||||
modalText.value = getModalText(realModel);
|
||||
initialModeValue = modalText.value;
|
||||
|
||||
// 同步日期
|
||||
const startDate = newDetail.log?.start_date_str || '--';
|
||||
date.value = startDate;
|
||||
initialDateValue = startDate;
|
||||
|
||||
if (startDate !== '--') {
|
||||
const [year, month, day] = startDate.split('-').map(Number);
|
||||
dateValues.value = [String(year), String(month).padStart(2, '0'), String(day).padStart(2, '0')];
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
// 条件 emit
|
||||
const maybeEmitMode = () => {
|
||||
if (modalText.value !== initialModeValue) {
|
||||
emit('update:mode', modalText.value, date.value);
|
||||
}
|
||||
};
|
||||
|
||||
const maybeEmitDate = () => {
|
||||
if (date.value !== initialDateValue) {
|
||||
emit('update:date', date.value, modalText.value);
|
||||
}
|
||||
};
|
||||
|
||||
const toShowDate = () => {};
|
||||
|
||||
// 日期选择确认
|
||||
const onConfirm = ({ selectedValues }) => {
|
||||
date.value = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
showDate.value = false;
|
||||
hasUserChangedDate = true;
|
||||
maybeEmitDate();
|
||||
};
|
||||
|
||||
const showModal = ref(false);
|
||||
|
||||
// 已执行天数
|
||||
const executedDays = computed(() => {
|
||||
const startDateStr = props.detail?.log?.start_date_str;
|
||||
if (!startDateStr || startDateStr === '--') return 0;
|
||||
|
||||
const startDate = new Date(startDateStr);
|
||||
const today = new Date();
|
||||
startDate.setHours(0, 0, 0, 0);
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const diffTime = today.getTime() - startDate.getTime();
|
||||
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
||||
return Math.max(0, diffDays);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="menu-setting-top" @click="showModal = false">
|
||||
<div class="menu-setting-card">
|
||||
<img class="menu-setting-card-bg" src="https://images.health.ufutx.com/202511/18/fb38114f13f3065021c3bd6843d06989.png" alt="" />
|
||||
<div class="menu-setting-message-box">
|
||||
<img :src="detail.avatar || 'https://image.fulllinkai.com/202203/09/cc1c73eb1a4941fef25a15cd1ff2f9df.png'" alt="" class="menu-setting-message-avatar" />
|
||||
<div class="menu-setting-message-name text-center">{{ detail.name || '未知用户' }}</div>
|
||||
<div class="menu-setting-message-sub">ID· {{ 52330000 + detail.id }}</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-setting-message-list">
|
||||
<div class="menu-setting-message-item" @click.stop="showModal = !showModal">
|
||||
<div>当前模式</div>
|
||||
<div class="menu-setting-message-item-right">
|
||||
<div>{{ modalText }}</div>
|
||||
<svg class="menu-setting-message-item-r-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="9" viewBox="0 0 14 9" fill="none">
|
||||
<path d="M12 2L7 7L2 2" stroke="#66676C" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div v-if="showModal" class="menu-setting-message-modal" @click.stop>
|
||||
<div class="menu-setting-message-m-item" :class="{ 'menu-setting-message-m-i-active': activeModal === 1 }" @click.stop="changeModal(1)">
|
||||
<div class="menu-setting-message-m-i-title">
|
||||
<div>减肥型</div>
|
||||
<svg v-if="activeModal === 1" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none">
|
||||
<path d="M5.2998 12L9.16254 15.8627L16.5498 8.47547" stroke="#18CA6E" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="menu-setting-message-m-i-sub">适用减脂需求,强化热量缺口与代谢调节</div>
|
||||
<div v-if="activeModal === 1" class="menu-setting-message-m-i-default">当前选中</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-setting-message-m-item" :class="{ 'menu-setting-message-m-i-active': activeModal === 2 }" @click.stop="changeModal(2)">
|
||||
<div class="menu-setting-message-m-i-title">
|
||||
<div>其他型</div>
|
||||
<svg v-if="activeModal === 2" xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 22 22" fill="none">
|
||||
<path d="M5.2998 12L9.16254 15.8627L16.5498 8.47547" stroke="#18CA6E" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="menu-setting-message-m-i-sub">按照特殊规则在一定范围内生成</div>
|
||||
<div v-if="activeModal === 2" class="menu-setting-message-m-i-default">当前选中</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-setting-message-item">
|
||||
<div>方案周期</div>
|
||||
<div class="menu-setting-message-i-two">共49天 已执行{{ executedDays }}天</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-setting-message-item" @click="showDate = true">
|
||||
<div>开始时间</div>
|
||||
<div class="menu-setting-message-item-right" @click="toShowDate">
|
||||
<div>{{ date }}</div>
|
||||
<svg class="menu-setting-message-item-r-icon" xmlns="http://www.w3.org/2000/svg" width="14" height="9" viewBox="0 0 14 9" fill="none">
|
||||
<path d="M12 2L7 7L2 2" stroke="#66676C" stroke-width="1.5" stroke-linecap="round" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<van-popup v-model:show="showDate" round position="bottom" :duration="0.5">
|
||||
<van-date-picker v-model="dateValues" title="选择日期" :min-date="minDate" :max-date="maxDate" @cancel="showDate = false" @confirm="onConfirm" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.menu-setting-top {
|
||||
border-radius: 0 0 16px 16px;
|
||||
background: #fff;
|
||||
}
|
||||
.menu-setting-card {
|
||||
position: relative;
|
||||
margin: 51px 10px 0;
|
||||
z-index: 10;
|
||||
flex-shrink: 0;
|
||||
.menu-setting-card-bg {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
.menu-setting-message-box {
|
||||
padding: 40px 6px 0;
|
||||
}
|
||||
.menu-setting-message-avatar {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -30px;
|
||||
transform: translateX(-50%);
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 30px;
|
||||
border: 2px solid #fff;
|
||||
}
|
||||
.menu-setting-message-name {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 3px;
|
||||
color: #0e0e0e;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.menu-setting-message-sub {
|
||||
margin-bottom: 20px;
|
||||
color: #66676c;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
.menu-setting-message-list {
|
||||
padding: 10px 16px 16px;
|
||||
.menu-setting-message-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 0;
|
||||
color: #0e0e0e;
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #f2f2f2;
|
||||
.menu-setting-message-item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.menu-setting-message-item-r-icon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.menu-setting-message-i-two {
|
||||
color: #18ca6e;
|
||||
font-weight: bold;
|
||||
}
|
||||
.menu-setting-message-modal {
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
width: 311px;
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
box-shadow: 0 3px 20px 0 rgba(0, 0, 0, 0.25);
|
||||
z-index: 1;
|
||||
.menu-setting-message-m-item {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
border-radius: 10px;
|
||||
background: #f8f8f8;
|
||||
.menu-setting-message-m-i-title {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.menu-setting-message-m-i-sub {
|
||||
color: #66676c;
|
||||
font-size: 14px;
|
||||
}
|
||||
.menu-setting-message-m-i-default {
|
||||
padding: 3px 6px;
|
||||
color: #18ca6e;
|
||||
font-size: 14px;
|
||||
border-radius: 6px;
|
||||
background: #e8faf1;
|
||||
}
|
||||
}
|
||||
.menu-setting-message-m-i-active {
|
||||
background: #fff;
|
||||
border: 0.3px solid #18ca6e;
|
||||
}
|
||||
}
|
||||
}
|
||||
.menu-setting-message-item:nth-child(1) {
|
||||
padding-top: 0;
|
||||
}
|
||||
.menu-setting-message-item:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
113
src/components/uploadPicture.vue
Normal file
113
src/components/uploadPicture.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<van-uploader :after-read="afterRead" :multiple="multiple" :max-count="maxCount" accept="image/*">
|
||||
<slot></slot>
|
||||
</van-uploader>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import md5 from 'js-md5';
|
||||
import service from '@/utils/service';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { getALiYun } from '@/api/demo';
|
||||
|
||||
const props = defineProps({
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
maxCount: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const state = ref(false);
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
|
||||
const emit = defineEmits(['onSuccess']);
|
||||
|
||||
const afterRead = (file) => {
|
||||
state.value = false;
|
||||
if (file && file.length > 1) {
|
||||
file.forEach((item) => {
|
||||
if (item.status === 'failed') {
|
||||
showToast('网络异常,请重试');
|
||||
state.value = true;
|
||||
}
|
||||
if (item.file.size >= 10400 * 1024) {
|
||||
state.value = true;
|
||||
showToast('文件大小不能超过 10M');
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (!state.value) {
|
||||
file.forEach((item) => {
|
||||
submit(item.file);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (file.status === 'failed') {
|
||||
showToast('网络异常,请重试');
|
||||
}
|
||||
if (file.file.size >= 10400 * 1024) {
|
||||
showToast('文件大小不能超过 10M');
|
||||
return;
|
||||
}
|
||||
submit(file.file);
|
||||
}
|
||||
};
|
||||
|
||||
const submit = (file) => {
|
||||
const uploadData = userStore.uploadData;
|
||||
if (uploadData) {
|
||||
const formData = new FormData();
|
||||
const fileName = `${md5(file.name) + Math.random()}.${file.type.split('/').pop().toLowerCase()}`;
|
||||
const filePath = `${uploadData.host}/${uploadData.dir}${fileName}`;
|
||||
formData.append('name', uploadData.dir + fileName);
|
||||
formData.append('key', uploadData.dir + fileName);
|
||||
formData.append('policy', uploadData.policy);
|
||||
formData.append('OSSAccessKeyId', uploadData.access_id);
|
||||
formData.append('success_action_status', '200');
|
||||
formData.append('signature', uploadData.signature);
|
||||
formData.append('file', file);
|
||||
formData.append('filename', file.name);
|
||||
service
|
||||
.post(uploadData.host, formData, { headers: { 'Content-Type': 'multipart/form-data' } })
|
||||
.then((data) => {
|
||||
console.log(data);
|
||||
console.log(props.type, filePath, '7777777');
|
||||
if (!props.type) {
|
||||
emit('onSuccess', filePath);
|
||||
} else {
|
||||
emit('onSuccess', filePath, props.type);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
showToast('网络异常,请重试');
|
||||
console.log(err);
|
||||
});
|
||||
} else {
|
||||
showToast('网络环境异常,请稍后重试');
|
||||
getALiYun(userStore.token).then((value) => {
|
||||
if (value.code == 0) {
|
||||
userStore.uploadData = value.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.custom-image .van-empty__image {
|
||||
width: px2rem(180);
|
||||
height: px2rem(180);
|
||||
}
|
||||
</style>
|
||||
10
src/definition.d.ts
vendored
Normal file
10
src/definition.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
declare global {
|
||||
interface Window {
|
||||
getIosToken: any; // 苹果全局变量名
|
||||
getAndroidPhone: any; // 安卓全局变量名
|
||||
webAppInterface: any; // 安卓全局变量名
|
||||
webkit: any; // ios app全局变量名
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
49
src/layout/index.vue
Normal file
49
src/layout/index.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<van-sticky ref="stickyRef">
|
||||
<van-nav-bar :title="$route.meta?.title">
|
||||
<template #left>
|
||||
<van-icon name="wap-nav" size="18" />
|
||||
</template>
|
||||
<template #right>
|
||||
<van-icon name="search" size="18" />
|
||||
</template>
|
||||
</van-nav-bar>
|
||||
</van-sticky>
|
||||
<div class="container">
|
||||
<router-view #="{ Component }">
|
||||
<keep-alive>
|
||||
<component :is="Component"></component>
|
||||
</keep-alive>
|
||||
</router-view>
|
||||
</div>
|
||||
<van-tabbar ref="tabbarRef" route>
|
||||
<template v-for="item in main" :key="item.name">
|
||||
<van-tabbar-item :to="item.path" :icon="item.meta?.icon"> {{ item.meta?.title }} </van-tabbar-item>
|
||||
</template>
|
||||
</van-tabbar>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, nextTick } from 'vue';
|
||||
import { Sticky, Tabbar } from 'vant';
|
||||
import { main } from '@/router/modules/main';
|
||||
|
||||
defineOptions({ name: 'Layout' });
|
||||
|
||||
const stickyRef = ref<InstanceType<typeof Sticky>>();
|
||||
const tabbarRef = ref<InstanceType<typeof Tabbar>>();
|
||||
const containerHeight = ref('');
|
||||
|
||||
nextTick(() => {
|
||||
containerHeight.value = `${stickyRef.value?.$el?.offsetHeight + tabbarRef.value?.$el.offsetHeight}px`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
--height: v-bind('containerHeight');
|
||||
--container-height: calc(100vh - var(--height));
|
||||
height: calc(100vh - var(--height));
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
16
src/main.ts
Normal file
16
src/main.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import { setupPlugins } from './plugins';
|
||||
import { setupStore } from './store';
|
||||
import { setupRouter } from './router';
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 安装插件(vant-ui等),若使用了 vite-plugin-components 插件,则需要手动引入组件
|
||||
setupPlugins(app);
|
||||
// 安装vuex
|
||||
setupStore(app);
|
||||
// 安装router
|
||||
setupRouter(app);
|
||||
|
||||
app.mount('#app');
|
||||
39
src/plugins/compress.ts
Normal file
39
src/plugins/compress.ts
Normal file
@ -0,0 +1,39 @@
|
||||
// 图片压缩
|
||||
const fileToDataURL = (file: Blob): Promise<any> => {
|
||||
return new Promise((resolve) => {
|
||||
const reader = new FileReader();
|
||||
reader.onloadend = (e) => resolve((e.target as FileReader).result);
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
};
|
||||
|
||||
const dataURLToImage = (dataURL: string): Promise<HTMLImageElement> => {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.onload = () => resolve(img);
|
||||
img.src = dataURL;
|
||||
});
|
||||
};
|
||||
|
||||
const canvastoFile = (canvas: HTMLCanvasElement, type: string, quality: number): Promise<Blob | null> => {
|
||||
return new Promise((resolve) => canvas.toBlob((blob) => resolve(blob), type, quality));
|
||||
};
|
||||
|
||||
export const compressionFile = async (file, type = 'image/jpeg', quality = 0.5) => {
|
||||
const fileName = file.name;
|
||||
const canvas = document.createElement('canvas');
|
||||
const context = canvas.getContext('2d') as CanvasRenderingContext2D;
|
||||
const base64 = await fileToDataURL(file);
|
||||
const img = await dataURLToImage(base64);
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
context.clearRect(0, 0, img.width, img.height);
|
||||
context.drawImage(img, 0, 0, img.width, img.height);
|
||||
const blob = (await canvastoFile(canvas, type, quality)) as Blob; // quality:0.5可根据实际状况核算
|
||||
const newFile = await new File([blob], fileName, {
|
||||
type,
|
||||
});
|
||||
return newFile;
|
||||
};
|
||||
|
||||
export default compressionFile;
|
||||
8
src/plugins/index.ts
Normal file
8
src/plugins/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { App } from 'vue';
|
||||
import './vant';
|
||||
// import { vantPlugins } from './vant';
|
||||
|
||||
export const setupPlugins = (app: App) => {
|
||||
// app.use(vantPlugins);
|
||||
console.log('app', app);
|
||||
};
|
||||
232
src/plugins/public.ts
Normal file
232
src/plugins/public.ts
Normal file
@ -0,0 +1,232 @@
|
||||
import { showToast } from 'vant';
|
||||
import { getALiYun } from '@/api/demo';
|
||||
// import wx from 'weixin-js-sdk';
|
||||
// import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
// 回填登录数据
|
||||
export const backFillLoginData = (result, userStore) => {
|
||||
localStorage.setItem('workToken', result.user ? result.user.token : '');
|
||||
localStorage.setItem('work_id', result.user ? result.user.work_id : '');
|
||||
userStore.token = result.user ? result.user.token : '';
|
||||
userStore.name = result.user ? result.user.name : '';
|
||||
userStore.mobile = result.user ? result.user.mobile : '';
|
||||
userStore.avatar = result.user ? result.user.avatar : 'http://images.ufutx.com/201905/13/599151d27fc07ba1bc4cc57a291525e5.jpeg';
|
||||
userStore.wxConfig = result.work_config;
|
||||
userStore.agentConfig = result.app_config;
|
||||
localStorage.setItem('userStore', JSON.stringify(result));
|
||||
|
||||
if (userStore.uploadData && userStore.uploadData.access_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
getALiYun('53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen').then((value) => {
|
||||
if (value.code == 0) {
|
||||
userStore.uploadData = value.data;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 获取当前日期
|
||||
export const currentDate = () => {
|
||||
let year = new Date().getFullYear();
|
||||
let month = new Date().getMonth() + 1;
|
||||
let day = new Date().getDate();
|
||||
month = (month < 10 ? `0${month}` : month) as any;
|
||||
day = (day < 10 ? `0${day}` : day) as any;
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
// 日期转换
|
||||
export const formatData = (time) => {
|
||||
let timestamp = time.replace(/-/g, '/');
|
||||
timestamp = new Date(timestamp).getTime().toString(); // 补全为13位
|
||||
const minute = 1000 * 60;
|
||||
const hour = minute * 60;
|
||||
const day = hour * 24;
|
||||
const month = day * 30;
|
||||
const now = new Date().getTime();
|
||||
const diffValue = now - timestamp;
|
||||
// 计算差异的时间
|
||||
const monthC = diffValue / month;
|
||||
const weekC = diffValue / (7 * day);
|
||||
const dayC = diffValue / day;
|
||||
const hourC = diffValue / hour;
|
||||
const minC = diffValue / minute;
|
||||
// 数值补0方法
|
||||
const zero = function (value) {
|
||||
if (value < 10) {
|
||||
return `0${value}`;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
// 超过1年,直接显示年月日
|
||||
if (monthC > 12) {
|
||||
return (function () {
|
||||
const date = new Date(time);
|
||||
return `${date.getFullYear()}年${zero(date.getMonth() + 1)}月${zero(date.getDate())}日`;
|
||||
})();
|
||||
} else if (monthC >= 1) {
|
||||
return `${parseInt(monthC)}月前`;
|
||||
} else if (weekC >= 1) {
|
||||
return `${parseInt(weekC)}周前`;
|
||||
} else if (dayC >= 1) {
|
||||
return `${parseInt(dayC)}天前`;
|
||||
} else if (hourC >= 1) {
|
||||
return `${parseInt(hourC)}小时前`;
|
||||
} else if (minC >= 1) {
|
||||
return `${parseInt(minC)}分钟前`;
|
||||
}
|
||||
return '刚刚';
|
||||
};
|
||||
|
||||
// 倒计时
|
||||
export const countDown = (time) => {
|
||||
let startDateV2 = new Date(); // 开始时间
|
||||
let endTimeArrV2 = time.replace(/-/g, '/');
|
||||
let endDateV2 = new Date(endTimeArrV2); // 结束时间
|
||||
let t = endDateV2.getTime() - startDateV2.getTime(); // 时间差
|
||||
let d = 0;
|
||||
let h = 0;
|
||||
let m = 0;
|
||||
let s = 0;
|
||||
if (t >= 0) {
|
||||
d = Math.floor(t / 1000 / 3600 / 24);
|
||||
h = Math.floor((t / 1000 / 60 / 60) % 24);
|
||||
m = Math.floor((t / 1000 / 60) % 60);
|
||||
s = Math.floor((t / 1000) % 60);
|
||||
}
|
||||
// 数值补0方法
|
||||
const zero = function (value) {
|
||||
if (value < 10) {
|
||||
return `0${value}`;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
// 修改小时格式
|
||||
if (h >= 0 && h <= 9) {
|
||||
h = zero(h);
|
||||
}
|
||||
// 修改分钟格式
|
||||
if (m >= 0 && m <= 9) {
|
||||
m = zero(m);
|
||||
}
|
||||
// 修改秒格式
|
||||
if (s >= 0 && s <= 9) {
|
||||
s = zero(s);
|
||||
}
|
||||
return { d, h, m, s };
|
||||
};
|
||||
|
||||
export const timestamp = (time) => {
|
||||
let date = new Date(time * 1000) as any;
|
||||
let year = date.getFullYear();
|
||||
let month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
let day = String(date.getDate()).padStart(2, '0');
|
||||
let hours = String(date.getHours()).padStart(2, '0');
|
||||
let minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
let seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
// 复制内容
|
||||
export const copy = async (val, state) => {
|
||||
//创建input标签
|
||||
const input = document.createElement('input');
|
||||
//将input的值设置为需要复制的内容
|
||||
input.value = val;
|
||||
//添加input标签
|
||||
document.body.appendChild(input);
|
||||
//选中input标签
|
||||
input.select();
|
||||
//执行复制
|
||||
document.execCommand('copy');
|
||||
if (!state) {
|
||||
if (document.execCommand('copy')) {
|
||||
showToast('复制成功');
|
||||
} else {
|
||||
showToast('该浏览器暂不支持复制');
|
||||
}
|
||||
}
|
||||
//移除input标签
|
||||
document.body.removeChild(input);
|
||||
};
|
||||
|
||||
// // 微信分享
|
||||
// export const weXinShare = async (img, link, title, desc) => {
|
||||
// const wxConfig = useUserStore();
|
||||
// wx.config(wxConfig.wxConfig);
|
||||
// wx.ready(function () {
|
||||
// wx.hideMenuItems({
|
||||
// menuList: ['menuItem:copyUrl'], // 屏蔽复制链接
|
||||
// });
|
||||
// wx.updateAppMessageShareData({
|
||||
// title, // 分享标题
|
||||
// desc, // 分享描述
|
||||
// link, // 分享链接
|
||||
// img, // 分享图标
|
||||
// success() {
|
||||
// console.log('分享成功');
|
||||
// },
|
||||
// cancel() {
|
||||
// console.log('分享失败');
|
||||
// },
|
||||
// });
|
||||
// // 微信分享菜单测试
|
||||
// wx.updateTimelineShareData({
|
||||
// title, // 分享标题
|
||||
// desc, // 分享描述
|
||||
// link, // 分享链接
|
||||
// img, // 分享图标
|
||||
// success() {
|
||||
// console.log('分享成功');
|
||||
// },
|
||||
// cancel() {
|
||||
// console.log('分享失败');
|
||||
// },
|
||||
// });
|
||||
// wx.onMenuShareAppMessage({
|
||||
// title, // 分享标题
|
||||
// desc, // 分享描述
|
||||
// link, // 分享链接
|
||||
// img, // 分享图标
|
||||
// success() {
|
||||
// console.log('分享成功-企业'); // 用户确认分享后执行的回调函数
|
||||
// },
|
||||
// cancel() {
|
||||
// // 用户取消分享后执行的回调函数
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
// wx.error(function (err) {
|
||||
// console.log(JSON.stringify(err), '7777777777777');
|
||||
// });
|
||||
// };
|
||||
|
||||
//封装防抖
|
||||
export const debounce = (fn, delay) => {
|
||||
let timer: null | ReturnType<typeof setTimeout> = null;
|
||||
return function () {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(function () {
|
||||
fn.apply(1);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
//封装节流
|
||||
export const throttle = (fn, delay) => {
|
||||
let valid = true;
|
||||
return function () {
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
valid = false;
|
||||
setTimeout(() => {
|
||||
fn.apply(2);
|
||||
valid = true;
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
11
src/plugins/vant.ts
Normal file
11
src/plugins/vant.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// https://youzan.github.io/vant/v4/#/zh-CN/quickstart#4.-yin-ru-han-shu-zu-jian-de-yang-shi
|
||||
// Toast
|
||||
import 'vant/es/toast/style';
|
||||
// Dialog
|
||||
import 'vant/es/dialog/style';
|
||||
// Notify
|
||||
import 'vant/es/notify/style';
|
||||
// ImagePreview
|
||||
import 'vant/es/image-preview/style';
|
||||
|
||||
import '@vant/touch-emulator'; // 桌面端touch适配
|
||||
676
src/router/index.ts
Normal file
676
src/router/index.ts
Normal file
@ -0,0 +1,676 @@
|
||||
import { App } from 'vue';
|
||||
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';
|
||||
import { createRouterGuards } from './routerGuards';
|
||||
import { main } from './modules/main';
|
||||
|
||||
export const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'boundEnterprise',
|
||||
redirect: '/h5/boundEnterprise',
|
||||
children: [
|
||||
...main,
|
||||
// 企业微信友福客服中心
|
||||
{
|
||||
path: '/h5/serviceCentre',
|
||||
name: 'serviceCentre',
|
||||
component: () => import('@/views/weChatPage/serviceCentre.vue'),
|
||||
meta: {
|
||||
title: '友福客服',
|
||||
},
|
||||
},
|
||||
// 企业微信查看用户信息
|
||||
{
|
||||
path: '/h5/viewUserInfo',
|
||||
name: 'viewUserInfo',
|
||||
component: () => import('@/views/weChatPage/viewUserInfo.vue'),
|
||||
meta: {
|
||||
title: '查看信息',
|
||||
},
|
||||
},
|
||||
// 企业微信查看用户信息
|
||||
{
|
||||
path: '/h5/viewUserInfoV2',
|
||||
name: 'viewUserInfoV2',
|
||||
component: () => import('@/views/weChatPage/viewUserInfoV2.vue'),
|
||||
meta: {
|
||||
title: '查看信息',
|
||||
},
|
||||
},
|
||||
// 企业微信报告补充资料
|
||||
{
|
||||
path: '/h5/replenishData',
|
||||
name: 'replenishData',
|
||||
component: () => import('@/views/weChatPage/replenishData.vue'),
|
||||
meta: {
|
||||
title: '补充资料',
|
||||
},
|
||||
},
|
||||
// 企业微信用户报告上传
|
||||
{
|
||||
path: '/h5/uploadReport',
|
||||
name: 'uploadReport',
|
||||
component: () => import('@/views/weChatPage/uploadReport.vue'),
|
||||
meta: {
|
||||
title: '报告上传',
|
||||
},
|
||||
},
|
||||
// 企业微信用户测量记录
|
||||
{
|
||||
path: '/h5/measurementRecord',
|
||||
name: 'measurementRecord',
|
||||
component: () => import('@/views/weChatPage/measurementRecord.vue'),
|
||||
meta: {
|
||||
title: '测量记录',
|
||||
},
|
||||
},
|
||||
// 企业微信用户测量记录详情
|
||||
{
|
||||
path: '/h5/measuringRecordDetail',
|
||||
name: 'measuringRecordDetail',
|
||||
component: () => import('@/views/weChatPage/measuringRecordDetail.vue'),
|
||||
meta: {
|
||||
title: '记录详情',
|
||||
},
|
||||
},
|
||||
// 企业微信测量记录图表统计
|
||||
{
|
||||
path: '/h5/statistics',
|
||||
name: 'statistics',
|
||||
component: () => import('@/views/weChatPage/statistics.vue'),
|
||||
meta: {
|
||||
title: '图表统计',
|
||||
},
|
||||
},
|
||||
// 企业微信备注记录
|
||||
{
|
||||
path: '/h5/remarks',
|
||||
name: 'remarks',
|
||||
component: () => import('@/views/weChatPage/remarks.vue'),
|
||||
meta: {
|
||||
title: '备注列表',
|
||||
},
|
||||
},
|
||||
// 企业微信备注详情
|
||||
{
|
||||
path: '/h5/remarkDetail',
|
||||
name: 'remarkDetail',
|
||||
component: () => import('@/views/weChatPage/remarkDetail.vue'),
|
||||
meta: {
|
||||
title: '备注详情',
|
||||
},
|
||||
},
|
||||
// 企业微信未绑定订单
|
||||
{
|
||||
path: '/h5/groupOrders',
|
||||
name: 'groupOrders',
|
||||
component: () => import('@/views/weChatPage/groupOrders.vue'),
|
||||
meta: {
|
||||
title: '订单列表',
|
||||
},
|
||||
},
|
||||
// 企业微信创建虚拟订单
|
||||
{
|
||||
path: '/h5/addOrder',
|
||||
name: 'addOrder',
|
||||
component: () => import('@/views/weChatPage/addOrder.vue'),
|
||||
meta: {
|
||||
title: '创建订单',
|
||||
},
|
||||
},
|
||||
// 企业微信评估人员列表
|
||||
{
|
||||
path: '/h5/assess',
|
||||
name: 'assess',
|
||||
component: () => import('@/views/weChatPage/assess.vue'),
|
||||
meta: {
|
||||
title: '评估人员',
|
||||
},
|
||||
},
|
||||
// 企业微信评估详情
|
||||
{
|
||||
path: '/h5/assessDetail',
|
||||
name: 'assessDetail',
|
||||
component: () => import('@/views/weChatPage/assessDetail.vue'),
|
||||
meta: {
|
||||
title: '评估详情',
|
||||
},
|
||||
},
|
||||
// 企业微信用户餐单
|
||||
{
|
||||
path: '/h5/userMenu',
|
||||
name: 'userMenu',
|
||||
component: () => import('@/views/weChatPage/userMenu.vue'),
|
||||
meta: {
|
||||
title: '用户餐单',
|
||||
},
|
||||
},
|
||||
// 企业微信用户餐单营养元素余量
|
||||
{
|
||||
path: '/h5/nutrientElement',
|
||||
name: 'nutrientElement',
|
||||
component: () => import('@/views/weChatPage/nutrientElement.vue'),
|
||||
meta: {
|
||||
title: '营养元素',
|
||||
},
|
||||
},
|
||||
// 企业微信工单
|
||||
{
|
||||
path: '/h5/workOrder',
|
||||
name: 'workOrder',
|
||||
component: () => import('@/views/weChatPage/workOrder.vue'),
|
||||
meta: {
|
||||
title: '工单',
|
||||
},
|
||||
},
|
||||
// 企业微信工单
|
||||
{
|
||||
path: '/h5/addWorkOrder',
|
||||
name: 'addWorkOrder',
|
||||
component: () => import('@/views/weChatPage/addWorkOrder.vue'),
|
||||
meta: {
|
||||
title: '创建工单',
|
||||
},
|
||||
},
|
||||
// 企业微信调查问卷列表
|
||||
{
|
||||
path: '/h5/surveys',
|
||||
name: 'surveys',
|
||||
component: () => import('@/views/weChatPage/surveys.vue'),
|
||||
meta: {
|
||||
title: '问卷列表',
|
||||
},
|
||||
},
|
||||
// 企业微信填写调查问卷
|
||||
{
|
||||
path: '/h5/questionnaire',
|
||||
name: 'questionnaire',
|
||||
component: () => import('@/views/weChatPage/questionnaire.vue'),
|
||||
meta: {
|
||||
title: '调查问卷',
|
||||
},
|
||||
},
|
||||
// 企业微信订单切换用户
|
||||
{
|
||||
path: '/h5/switchUser',
|
||||
name: 'switchUser',
|
||||
component: () => import('@/views/weChatPage/switchUser.vue'),
|
||||
meta: {
|
||||
title: '切换用户',
|
||||
},
|
||||
},
|
||||
// 企业微信测试页面
|
||||
{
|
||||
path: '/h5/weChatTest',
|
||||
name: 'weChatTest',
|
||||
component: () => import('@/views/weChatPage/weChatTest.vue'),
|
||||
meta: {
|
||||
title: '测试页面',
|
||||
},
|
||||
},
|
||||
// 企业微信跳转小程序中转
|
||||
{
|
||||
path: '/h5/enterpriseTransfer',
|
||||
name: 'enterpriseTransfer',
|
||||
component: () => import('@/views/weChatPage/enterpriseTransfer.vue'),
|
||||
meta: {
|
||||
title: '小程序中转',
|
||||
},
|
||||
},
|
||||
// 企业微信跳转小程序中转
|
||||
{
|
||||
path: '/h5/serviceTransfer',
|
||||
name: 'serviceTransfer',
|
||||
component: () => import('@/views/weChatPage/serviceTransfer.vue'),
|
||||
meta: {
|
||||
title: '小程序中转',
|
||||
},
|
||||
},
|
||||
// APP跳转中转
|
||||
{
|
||||
path: '/h5/bindEnterprise',
|
||||
name: 'bindEnterprise',
|
||||
component: () => import('@/views/appDir/bindEnterprise.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
// APP跳转个人中心
|
||||
{
|
||||
path: '/h5/personalCenter',
|
||||
name: 'personalCenter',
|
||||
component: () => import('@/views/appDir/personalCenter.vue'),
|
||||
meta: {
|
||||
title: '友福客服',
|
||||
},
|
||||
},
|
||||
// APP跳转查看用户信息
|
||||
{
|
||||
path: '/h5/appViewUserInfo',
|
||||
name: 'appViewUserInfo',
|
||||
component: () => import('@/views/appDir/appViewUserInfo.vue'),
|
||||
meta: {
|
||||
title: '查看信息',
|
||||
},
|
||||
},
|
||||
// APP跳转报告上传
|
||||
{
|
||||
path: '/h5/appReplenishData',
|
||||
name: 'appReplenishData',
|
||||
component: () => import('@/views/appDir/appReplenishData.vue'),
|
||||
meta: {
|
||||
title: '补充资料',
|
||||
},
|
||||
},
|
||||
// APP跳转报告上传
|
||||
{
|
||||
path: '/h5/appUploadReport',
|
||||
name: 'appUploadReport',
|
||||
component: () => import('@/views/appDir/appUploadReport.vue'),
|
||||
meta: {
|
||||
title: '报告上传',
|
||||
},
|
||||
},
|
||||
// APP跳转查看用户测量记录
|
||||
{
|
||||
path: '/h5/appMeasurementRecord',
|
||||
name: 'appMeasurementRecord',
|
||||
component: () => import('@/views/appDir/appMeasurementRecord.vue'),
|
||||
meta: {
|
||||
title: '测量记录',
|
||||
},
|
||||
},
|
||||
// APP跳转查看用户测量记录
|
||||
{
|
||||
path: '/h5/appMeasuringRecordDetail',
|
||||
name: 'appMeasuringRecordDetail',
|
||||
component: () => import('@/views/appDir/appMeasuringRecordDetail.vue'),
|
||||
meta: {
|
||||
title: '记录详情',
|
||||
},
|
||||
},
|
||||
// APP跳转调查问卷
|
||||
{
|
||||
path: '/h5/appSurveys',
|
||||
name: 'appSurveys',
|
||||
component: () => import('@/views/appDir/appSurveys.vue'),
|
||||
meta: {
|
||||
title: '问卷列表',
|
||||
},
|
||||
},
|
||||
// APP跳转调查问卷
|
||||
{
|
||||
path: '/h5/appQuestionnaire',
|
||||
name: 'appQuestionnaire',
|
||||
component: () => import('@/views/appDir/appQuestionnaire.vue'),
|
||||
meta: {
|
||||
title: '方案后服务问卷',
|
||||
},
|
||||
},
|
||||
// APP跳转图片统计
|
||||
{
|
||||
path: '/h5/appStatistics',
|
||||
name: 'appStatistics',
|
||||
component: () => import('@/views/appDir/appStatistics.vue'),
|
||||
meta: {
|
||||
title: '图表统计',
|
||||
},
|
||||
},
|
||||
// APP跳转用户备注管理
|
||||
{
|
||||
path: '/h5/appRemarks',
|
||||
name: 'appRemarks',
|
||||
component: () => import('@/views/appDir/appRemarks.vue'),
|
||||
meta: {
|
||||
title: '备注列表',
|
||||
},
|
||||
},
|
||||
// APP跳转用户备注详情
|
||||
{
|
||||
path: '/h5/appRemarkDetail',
|
||||
name: 'appRemarkDetail',
|
||||
component: () => import('@/views/appDir/appRemarkDetail.vue'),
|
||||
meta: {
|
||||
title: '备注详情',
|
||||
},
|
||||
},
|
||||
// APP跳转复盘评估表
|
||||
{
|
||||
path: '/h5/appAssess',
|
||||
name: 'appAssess',
|
||||
component: () => import('@/views/appDir/appAssess.vue'),
|
||||
meta: {
|
||||
title: '评估人员',
|
||||
},
|
||||
},
|
||||
// APP跳转评估详情
|
||||
{
|
||||
path: '/h5/appAssessDetail',
|
||||
name: 'appAssessDetail',
|
||||
component: () => import('@/views/appDir/appAssessDetail.vue'),
|
||||
meta: {
|
||||
title: '评估详情',
|
||||
},
|
||||
},
|
||||
// APP跳转切换用户
|
||||
{
|
||||
path: '/h5/appSwitchUser',
|
||||
name: 'appSwitchUser',
|
||||
component: () => import('@/views/appDir/appSwitchUser.vue'),
|
||||
meta: {
|
||||
title: '切换用户',
|
||||
},
|
||||
},
|
||||
// APP跳转查看用户餐单
|
||||
{
|
||||
path: '/h5/appUserMenu',
|
||||
name: 'appUserMenu',
|
||||
component: () => import('@/views/appDir/appUserMenu.vue'),
|
||||
meta: {
|
||||
title: '用户餐单',
|
||||
},
|
||||
},
|
||||
// APP跳转预设用户餐单日历
|
||||
{
|
||||
path: '/h5/appUserMenuAll',
|
||||
name: 'appUserMenuAll',
|
||||
component: () => import('@/views/appDir/appUserMenuAll.vue'),
|
||||
meta: {
|
||||
title: '用户餐单',
|
||||
},
|
||||
},
|
||||
// APP跳转设置餐单
|
||||
{
|
||||
path: '/h5/appEditUserMenu',
|
||||
name: 'appEditUserMenu',
|
||||
component: () => import('@/views/appDir/appEditUserMenu.vue'),
|
||||
meta: {
|
||||
title: '设置餐单',
|
||||
},
|
||||
},
|
||||
// APP跳转服务流程
|
||||
{
|
||||
path: '/h5/appServiceFlow',
|
||||
name: 'appServiceFlow',
|
||||
component: () => import('@/views/appDir/appServiceFlow.vue'),
|
||||
meta: {
|
||||
title: '服务流程',
|
||||
},
|
||||
},
|
||||
// APP跳转工单
|
||||
{
|
||||
path: '/h5/appWorkOrder',
|
||||
name: 'appWorkOrder',
|
||||
component: () => import('@/views/appDir/appWorkOrder.vue'),
|
||||
meta: {
|
||||
title: '工单',
|
||||
},
|
||||
},
|
||||
// APP跳转创建工单
|
||||
{
|
||||
path: '/h5/appAddWorkOrder',
|
||||
name: 'appAddWorkOrder',
|
||||
component: () => import('@/views/appDir/appAddWorkOrder.vue'),
|
||||
meta: {
|
||||
title: '创建工单',
|
||||
},
|
||||
},
|
||||
// APP跳转订单列表
|
||||
{
|
||||
path: '/h5/appGroupOrders',
|
||||
name: 'appGroupOrders',
|
||||
component: () => import('@/views/appDir/appGroupOrders.vue'),
|
||||
meta: {
|
||||
title: '订单列表',
|
||||
},
|
||||
},
|
||||
// APP跳转创建订单
|
||||
{
|
||||
path: '/h5/appAddOrder',
|
||||
name: 'appAddOrder',
|
||||
component: () => import('@/views/appDir/appAddOrder.vue'),
|
||||
meta: {
|
||||
title: '创建订单',
|
||||
},
|
||||
},
|
||||
// APP跳转创建订单
|
||||
{
|
||||
path: '/h5/appSleepStatistics',
|
||||
name: 'appSleepStatistics',
|
||||
component: () => import('@/views/appDir/appSleepStatistics.vue'),
|
||||
meta: {
|
||||
title: '睡眠统计',
|
||||
},
|
||||
},
|
||||
// 小程序webview数据统计页面
|
||||
{
|
||||
path: '/h5/mpStatistics',
|
||||
name: 'mpStatistics',
|
||||
component: () => import('@/views/otherDir/mpStatistics.vue'),
|
||||
meta: {
|
||||
title: '数据统计',
|
||||
},
|
||||
},
|
||||
// 客服绑定酒
|
||||
{
|
||||
path: '/h5/bindingWine',
|
||||
name: 'bindingWine',
|
||||
component: () => import('@/views/otherDir/bindingWine.vue'),
|
||||
meta: {
|
||||
title: '友福渣酒',
|
||||
},
|
||||
},
|
||||
// 客服绑定酒的详情
|
||||
{
|
||||
path: '/h5/wineDetail',
|
||||
name: 'wineDetail',
|
||||
component: () => import('@/views/otherDir/wineDetail.vue'),
|
||||
meta: {
|
||||
title: '友福渣酒',
|
||||
},
|
||||
},
|
||||
// 客服绑定酒记录
|
||||
{
|
||||
path: '/h5/wineRecords',
|
||||
name: 'wineRecords',
|
||||
component: () => import('@/views/otherDir/wineRecords.vue'),
|
||||
meta: {
|
||||
title: '友福渣酒',
|
||||
},
|
||||
},
|
||||
// 友福邀约
|
||||
{
|
||||
path: '/h5/invite',
|
||||
name: 'invite',
|
||||
component: () => import('@/views/otherDir/invite.vue'),
|
||||
meta: {
|
||||
title: '友福邀约',
|
||||
},
|
||||
},
|
||||
// 查看友福邀约全部
|
||||
{
|
||||
path: '/h5/viewInvite',
|
||||
name: 'viewInvite',
|
||||
component: () => import('@/views/otherDir/viewInvite.vue'),
|
||||
meta: {
|
||||
title: '友福邀约',
|
||||
},
|
||||
},
|
||||
// 友福邀约 v2
|
||||
{
|
||||
path: '/h5/inviteV2',
|
||||
name: 'inviteV2',
|
||||
component: () => import('@/views/otherDir/inviteV2.vue'),
|
||||
meta: {
|
||||
title: '年三十围炉火锅团聚夜',
|
||||
},
|
||||
},
|
||||
// 查看友福邀约全部 v2
|
||||
{
|
||||
path: '/h5/viewInviteV2',
|
||||
name: 'viewInviteV2',
|
||||
component: () => import('@/views/otherDir/viewInviteV2.vue'),
|
||||
meta: {
|
||||
title: '年三十火锅人员',
|
||||
},
|
||||
},
|
||||
// 友福邀约 v3
|
||||
{
|
||||
path: '/h5/inviteV3',
|
||||
name: 'inviteV3',
|
||||
component: () => import('@/views/otherDir/inviteV3.vue'),
|
||||
meta: {
|
||||
title: '友福批发商大会',
|
||||
},
|
||||
},
|
||||
// 查看友福邀约全部 v3
|
||||
{
|
||||
path: '/h5/viewInviteV3',
|
||||
name: 'viewInviteV3',
|
||||
component: () => import('@/views/otherDir/viewInviteV3.vue'),
|
||||
meta: {
|
||||
title: '友福批发商大会',
|
||||
},
|
||||
},
|
||||
// 友福邀约
|
||||
{
|
||||
path: '/h5/invite',
|
||||
name: 'invite',
|
||||
component: () => import('@/views/otherDir/invite.vue'),
|
||||
meta: {
|
||||
title: '友福邀约',
|
||||
},
|
||||
},
|
||||
// 查看友福邀约全部
|
||||
{
|
||||
path: '/h5/viewInvite',
|
||||
name: 'viewInvite',
|
||||
component: () => import('@/views/otherDir/viewInvite.vue'),
|
||||
meta: {
|
||||
title: '友福邀约',
|
||||
},
|
||||
},
|
||||
// 友福批发商联谊会
|
||||
{
|
||||
path: '/h5/inviteV4',
|
||||
name: 'inviteV4',
|
||||
component: () => import('@/views/otherDir/inviteV4.vue'),
|
||||
meta: {
|
||||
title: '批发商联谊会',
|
||||
},
|
||||
},
|
||||
// 查看友福批发商联谊会全部
|
||||
{
|
||||
path: '/h5/viewInviteV4',
|
||||
name: 'viewInviteV4',
|
||||
component: () => import('@/views/otherDir/viewInviteV4.vue'),
|
||||
meta: {
|
||||
title: '批发商联谊会',
|
||||
},
|
||||
},
|
||||
// 友福邀约 配置性 - 暂没用上
|
||||
{
|
||||
path: '/h5/inviteMerchant',
|
||||
name: 'inviteMerchant',
|
||||
component: () => import('@/views/otherDir/inviteMerchant.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
// 查看友福邀约全部 配置性 - 暂没用上
|
||||
{
|
||||
path: '/h5/inviteMerchantList',
|
||||
name: 'inviteMerchantList',
|
||||
component: () => import('@/views/otherDir/inviteMerchantList.vue'),
|
||||
meta: {
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
// 友福年会
|
||||
{
|
||||
path: '/h5/addAnnualMeeting',
|
||||
name: 'addAnnualMeeting',
|
||||
component: () => import('@/views/otherDir/addAnnualMeeting.vue'),
|
||||
meta: {
|
||||
title: '友福年会',
|
||||
},
|
||||
},
|
||||
// 查看友福年会邀约全部
|
||||
{
|
||||
path: '/h5/allAnnualMeeting',
|
||||
name: 'allAnnualMeeting',
|
||||
component: () => import('@/views/otherDir/allAnnualMeeting.vue'),
|
||||
meta: {
|
||||
title: '友福年会',
|
||||
},
|
||||
},
|
||||
// saas活动订单接龙
|
||||
{
|
||||
path: '/h5/activityOrder',
|
||||
name: 'activityOrder',
|
||||
component: () => import('@/views/otherDir/activityOrder.vue'),
|
||||
meta: {
|
||||
title: '活动订单',
|
||||
},
|
||||
},
|
||||
// 健康活动订单接龙
|
||||
{
|
||||
path: '/h5/activityOrderV2',
|
||||
name: 'activityOrderV2',
|
||||
component: () => import('@/views/otherDir/activityOrderV2.vue'),
|
||||
meta: {
|
||||
title: '活动订单',
|
||||
},
|
||||
},
|
||||
// 讨论组话题列表
|
||||
{
|
||||
path: '/h5/discussList',
|
||||
name: 'discussList',
|
||||
component: () => import('@/views/otherDir/discussList.vue'),
|
||||
meta: {
|
||||
title: '话题列表',
|
||||
},
|
||||
},
|
||||
// 讨论组话题详情
|
||||
{
|
||||
path: '/h5/discussDetail',
|
||||
name: 'discussDetail',
|
||||
component: () => import('@/views/otherDir/discussDetail.vue'),
|
||||
meta: {
|
||||
title: '话题详情',
|
||||
},
|
||||
},
|
||||
// 404页面未找到
|
||||
{
|
||||
path: '/h5/notFound',
|
||||
name: 'notFound',
|
||||
component: () => import('@/views/otherDir/notFound.vue'),
|
||||
meta: {
|
||||
title: 'Not Found',
|
||||
},
|
||||
},
|
||||
// 测试
|
||||
{
|
||||
path: '/h5/test',
|
||||
name: 'test',
|
||||
component: () => import('@/views/otherDir/test.vue'),
|
||||
meta: {
|
||||
title: '测试',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const router = createRouter({
|
||||
// process.env.BASE_URL
|
||||
history: createWebHashHistory(''),
|
||||
routes,
|
||||
});
|
||||
|
||||
export function setupRouter(app: App) {
|
||||
app.use(router);
|
||||
// 创建路由守卫
|
||||
createRouterGuards(router);
|
||||
}
|
||||
export default router;
|
||||
15
src/router/modules/main.ts
Normal file
15
src/router/modules/main.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
|
||||
export const main: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/h5/boundEnterprise',
|
||||
name: 'boundEnterprise',
|
||||
component: () => import('@/views/weChatPage/boundEnterprise.vue'),
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
icon: 'home-o',
|
||||
active: 'https://image.fulllinkai.com/202304/14/7c216ca4337d8ebcd84279f8002c20dd.png',
|
||||
inactive: 'https://image.fulllinkai.com/202304/14/ffb35939710ce4c98062f85392a2c005.png',
|
||||
},
|
||||
},
|
||||
];
|
||||
81
src/router/routerGuards.ts
Normal file
81
src/router/routerGuards.ts
Normal file
@ -0,0 +1,81 @@
|
||||
import { Router } from 'vue-router';
|
||||
import { getALiYun, getLogin } from '@/api/demo';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
let pathUrls = ['invite', 'viewInvite', 'inviteV4', 'viewInviteV4', 'inviteMerchant', 'inviteMerchantList', 'addAnnualMeeting', 'allAnnualMeeting', 'enterpriseTransfer', 'questionnaire', 'activityOrder', 'activityOrderV2', 'mpStatistics', 'appSurveys', 'appQuestionnaire', 'appUserMenu', 'appEditUserMenu', 'test', 'appSleepStatistics']; // 不需要登录的页面
|
||||
|
||||
let initLogin = 1;
|
||||
|
||||
export function createRouterGuards(router: Router) {
|
||||
router.beforeEach((to, _from, next) => {
|
||||
document.title = (to?.meta?.title as string) || document.title;
|
||||
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 判断是否需要登录的页面
|
||||
let through = false;
|
||||
pathUrls.forEach((item) => {
|
||||
if (to.path.includes(item)) {
|
||||
through = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (to.query.service_user_id) {
|
||||
userStore.serviceUserId = to.query.service_user_id as any;
|
||||
}
|
||||
|
||||
if (to.query.work_id) {
|
||||
localStorage.removeItem('work_id');
|
||||
localStorage.setItem('work_id', to.query.work_id as any);
|
||||
}
|
||||
|
||||
if (through) {
|
||||
next();
|
||||
} else if (to.path.includes('bindEnterprise')) {
|
||||
getALiYun('53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen').then((value) => {
|
||||
if (value.code == 0) {
|
||||
userStore.uploadData = value.data;
|
||||
}
|
||||
});
|
||||
next();
|
||||
} else {
|
||||
if (initLogin <= 1) {
|
||||
// 获取微信、企业微信凭证、token
|
||||
getLogin().then((res) => {
|
||||
if (res.code === 0) {
|
||||
let result = res.data;
|
||||
initLogin++;
|
||||
if (result.user && result.user.work_id) {
|
||||
localStorage.setItem('work_id', result.user ? result.user.work_id : '');
|
||||
}
|
||||
localStorage.setItem('workToken', result.user ? result.user.token : '');
|
||||
userStore.token = result.user ? result.user.token : '';
|
||||
userStore.name = result.user ? result.user.name : '';
|
||||
userStore.mobile = result.user ? result.user.mobile : '';
|
||||
userStore.userID = result.user ? result.user.id : '';
|
||||
userStore.avatar = result.user ? result.user.avatar : 'http://images.ufutx.com/201905/13/599151d27fc07ba1bc4cc57a291525e5.jpeg';
|
||||
userStore.wxConfig = result.work_config;
|
||||
userStore.agentConfig = result.app_config;
|
||||
getALiYun('53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen').then((value) => {
|
||||
if (value.code == 0) {
|
||||
userStore.uploadData = value.data;
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
next();
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.onError((error) => {
|
||||
const fetchResourcesErrors = ['Failed to fetch dynamically imported module', 'Importing a module script failed'];
|
||||
if (fetchResourcesErrors.some((item) => error?.message && error.message?.includes(item))) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
10
src/store/index.ts
Normal file
10
src/store/index.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { createPinia } from 'pinia';
|
||||
import type { App } from 'vue';
|
||||
|
||||
const store = createPinia();
|
||||
|
||||
export function setupStore(app: App<Element>) {
|
||||
app.use(store);
|
||||
}
|
||||
|
||||
export { store };
|
||||
48
src/store/modules/user.ts
Normal file
48
src/store/modules/user.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { defineStore } from 'pinia';
|
||||
|
||||
interface UserState {
|
||||
init: number;
|
||||
token: string;
|
||||
name: string;
|
||||
avatar: string;
|
||||
mobile: string;
|
||||
userID: string;
|
||||
weChatBindState: string;
|
||||
chatId: string;
|
||||
serviceUserId: string;
|
||||
dataState: string;
|
||||
orderState: string;
|
||||
service: string;
|
||||
coach: string;
|
||||
chiefCoach: string;
|
||||
uploadData: object;
|
||||
wxConfig: object;
|
||||
agentConfig: object;
|
||||
signature: object;
|
||||
}
|
||||
|
||||
export const useUserStore = defineStore({
|
||||
id: 'user',
|
||||
state: (): UserState => ({
|
||||
init: 0,
|
||||
token: '',
|
||||
name: '',
|
||||
avatar: '',
|
||||
mobile: '',
|
||||
userID: '',
|
||||
weChatBindState: '',
|
||||
chatId: '', // 群ID
|
||||
serviceUserId: '', // 客服人员ID
|
||||
dataState: '', // 服务状态
|
||||
orderState: '', // 订单状态
|
||||
service: '', // 是否客服
|
||||
coach: '', // 是否副教练
|
||||
chiefCoach: '', // 是否主教练
|
||||
uploadData: {}, // 阿里云上传配置信息
|
||||
wxConfig: { init: 'true' }, // 储存微信凭证
|
||||
agentConfig: {}, // 储存企业微信凭证
|
||||
signature: {}, // 储存上传凭证
|
||||
}),
|
||||
getters: {},
|
||||
actions: {},
|
||||
});
|
||||
472
src/styles/common.scss
Normal file
472
src/styles/common.scss
Normal file
@ -0,0 +1,472 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@use './rem/scss/util';
|
||||
|
||||
.text-right { text-align: right; }
|
||||
|
||||
.text-center { text-align: center; }
|
||||
|
||||
.text-left { text-align: left; }
|
||||
|
||||
.break-all {
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.f-fbc {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.f-fcl {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.f-fl {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.f-fcc {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.f-fc {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.f-fcr {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.f-fr {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.f-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ellipsis_1 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ellipsis_2 {
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
}
|
||||
|
||||
.ellipsis_3 {
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
}
|
||||
|
||||
.backCover {
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.ui-relative { position: relative; }
|
||||
|
||||
.ui-overflow { overflow: hidden; }
|
||||
|
||||
.flo_l { float: left; }
|
||||
|
||||
.flo_r { float: right; }
|
||||
|
||||
.bold { font-weight: bold; }
|
||||
|
||||
.color0 {
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.color0E {
|
||||
color: #0e0e0e !important;
|
||||
}
|
||||
|
||||
.color054 {
|
||||
color: #054725 !important;
|
||||
}
|
||||
|
||||
.color08 {
|
||||
color: #080f1e !important;
|
||||
}
|
||||
|
||||
.color76c {
|
||||
color: #66676c !important;
|
||||
}
|
||||
|
||||
.color76 {
|
||||
color: #767676 !important;
|
||||
}
|
||||
|
||||
.color73 {
|
||||
color: #734236 !important;
|
||||
}
|
||||
|
||||
.color043 {
|
||||
color: #043d20 !important;
|
||||
}
|
||||
|
||||
.colorB2 {
|
||||
color: #b2b3b5 !important;
|
||||
}
|
||||
|
||||
.colorF { color: #fff; }
|
||||
|
||||
.color3 { color: #333; }
|
||||
|
||||
.color6 { color: #666; }
|
||||
|
||||
.color9 { color: #999; }
|
||||
|
||||
.colorF5 { color: #f5f5f5; }
|
||||
|
||||
.colorF8 { color: #f8f8f8; }
|
||||
|
||||
.colorC2 { color: #c2c2c2; }
|
||||
|
||||
.colorTheme { color: #5ac7a0; }
|
||||
|
||||
.colorPrice { color: #ff5959; }
|
||||
|
||||
.bold100 {
|
||||
font-weight: 100 !important;
|
||||
}
|
||||
|
||||
.bold200 {
|
||||
font-weight: 200 !important;
|
||||
}
|
||||
|
||||
.bold400 {
|
||||
font-weight: 400 !important;
|
||||
}
|
||||
|
||||
.bold500 {
|
||||
font-weight: 500 !important;
|
||||
}
|
||||
|
||||
.bold600 {
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.bold700 {
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.bcTheme { background: linear-gradient(90deg, #8c9bff 0%, #707ffa 100%); }
|
||||
|
||||
.font_20 {
|
||||
font-size: px2rem(20);
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
|
||||
.font_22 { font-size: px2rem(22); }
|
||||
|
||||
.font_24 { font-size: px2rem(24); }
|
||||
|
||||
.font_26 { font-size: px2rem(26); }
|
||||
|
||||
.font_28 { font-size: px2rem(28); }
|
||||
|
||||
.font_30 { font-size: px2rem(30); }
|
||||
|
||||
.font_32 { font-size: px2rem(32); }
|
||||
|
||||
.font_34 { font-size: px2rem(34); }
|
||||
|
||||
.font_36 { font-size: px2rem(36); }
|
||||
|
||||
.font_38 { font-size: px2rem(38); }
|
||||
|
||||
.font_40 { font-size: px2rem(40); }
|
||||
|
||||
.font_42 { font-size: px2rem(42); }
|
||||
|
||||
.font_44 { font-size: px2rem(44); }
|
||||
|
||||
.font_46 { font-size: px2rem(46); }
|
||||
|
||||
.font_48 { font-size: px2rem(48); }
|
||||
|
||||
.ui-pt-4 { padding-top: px2rem(4); }
|
||||
|
||||
.ui-pt-6 { padding-top: px2rem(6); }
|
||||
|
||||
.ui-pt-8 { padding-top: px2rem(8); }
|
||||
|
||||
.ui-pt-10 { padding-top: px2rem(10); }
|
||||
|
||||
.ui-pt-12 { padding-top: px2rem(12); }
|
||||
|
||||
.ui-pt-14 { padding-top: px2rem(14); }
|
||||
|
||||
.ui-pt-16 { padding-top: px2rem(16); }
|
||||
|
||||
.ui-pt-18 { padding-top: px2rem(18); }
|
||||
|
||||
.ui-pt-20 { padding-top: px2rem(20); }
|
||||
|
||||
.ui-pt-24 { padding-top: px2rem(24); }
|
||||
|
||||
.ui-pt-26 { padding-top: px2rem(26); }
|
||||
|
||||
.ui-pt-28 { padding-top: px2rem(28); }
|
||||
|
||||
.ui-pt-30 { padding-top: px2rem(30); }
|
||||
|
||||
.ui-pt-32 { padding-top: px2rem(32); }
|
||||
|
||||
.ui-pt-36 { padding-top: px2rem(36); }
|
||||
|
||||
.ui-pt-40 { padding-top: px2rem(40); }
|
||||
|
||||
.ui-pr-4 { padding-right: px2rem(4); }
|
||||
|
||||
.ui-pr-6 { padding-right: px2rem(6); }
|
||||
|
||||
.ui-pr-8 { padding-right: px2rem(8); }
|
||||
|
||||
.ui-pr-10 { padding-right: px2rem(10); }
|
||||
|
||||
.ui-pr-12 { padding-right: px2rem(12); }
|
||||
|
||||
.ui-pr-14 { padding-right: px2rem(14); }
|
||||
|
||||
.ui-pr-16 { padding-right: px2rem(16); }
|
||||
|
||||
.ui-pr-20 { padding-right: px2rem(20); }
|
||||
|
||||
.ui-pr-24 { padding-right: px2rem(24); }
|
||||
|
||||
.ui-pr-26 { padding-right: px2rem(26); }
|
||||
|
||||
.ui-pr-28 { padding-right: px2rem(28); }
|
||||
|
||||
.ui-pr-30 { padding-right: px2rem(30); }
|
||||
|
||||
.ui-pr-32 { padding-right: px2rem(32); }
|
||||
|
||||
.ui-pr-36 { padding-right: px2rem(36); }
|
||||
|
||||
.ui-pr-40 { padding-right: px2rem(40); }
|
||||
|
||||
.ui-pb-4 { padding-bottom: px2rem(4); }
|
||||
|
||||
.ui-pb-6 { padding-bottom: px2rem(6); }
|
||||
|
||||
.ui-pb-8 { padding-bottom: px2rem(8); }
|
||||
|
||||
.ui-pb-10 { padding-bottom: px2rem(10); }
|
||||
|
||||
.ui-pb-12 { padding-bottom: px2rem(12); }
|
||||
|
||||
.ui-pb-14 { padding-bottom: px2rem(14); }
|
||||
|
||||
.ui-pb-16 { padding-bottom: px2rem(16); }
|
||||
|
||||
.ui-pb-20 { padding-bottom: px2rem(20); }
|
||||
|
||||
.ui-pb-24 { padding-bottom: px2rem(24); }
|
||||
|
||||
.ui-pb-26 { padding-bottom: px2rem(26); }
|
||||
|
||||
.ui-pb-28 { padding-bottom: px2rem(28); }
|
||||
|
||||
.ui-pb-30 { padding-bottom: px2rem(30); }
|
||||
|
||||
.ui-pb-32 { padding-bottom: px2rem(32); }
|
||||
|
||||
.ui-pb-36 { padding-bottom: px2rem(36); }
|
||||
|
||||
.ui-pb-40 { padding-bottom: px2rem(40); }
|
||||
|
||||
.ui-pl-4 { padding-left: px2rem(4); }
|
||||
|
||||
.ui-pl-6 { padding-left: px2rem(6); }
|
||||
|
||||
.ui-pl-8 { padding-left: px2rem(8); }
|
||||
|
||||
.ui-pl-10 { padding-left: px2rem(10); }
|
||||
|
||||
.ui-pl-12 { padding-left: px2rem(12); }
|
||||
|
||||
.ui-pl-14 { padding-left: px2rem(14); }
|
||||
|
||||
.ui-pl-16 { padding-left: px2rem(16); }
|
||||
|
||||
.ui-pl-20 { padding-left: px2rem(20); }
|
||||
|
||||
.ui-pl-24 { padding-left: px2rem(24); }
|
||||
|
||||
.ui-pl-26 { padding-left: px2rem(26); }
|
||||
|
||||
.ui-pl-28 { padding-left: px2rem(28); }
|
||||
|
||||
.ui-pl-30 { padding-left: px2rem(30); }
|
||||
|
||||
.ui-pl-32 { padding-left: px2rem(32); }
|
||||
|
||||
.ui-pl-36 { padding-left: px2rem(36); }
|
||||
|
||||
.ui-pl-40 { padding-left: px2rem(40); }
|
||||
|
||||
.ui-mt-4 { margin-top: px2rem(4); }
|
||||
|
||||
.ui-mt-6 { margin-top: px2rem(6); }
|
||||
|
||||
.ui-mt-8 { margin-top: px2rem(8); }
|
||||
|
||||
.ui-mt-10 { margin-top: px2rem(10); }
|
||||
|
||||
.ui-mt-12 { margin-top: px2rem(12); }
|
||||
|
||||
.ui-mt-14 { margin-top: px2rem(14); }
|
||||
|
||||
.ui-mt-16 { margin-top: px2rem(16); }
|
||||
|
||||
.ui-mt-20 { margin-top: px2rem(20); }
|
||||
|
||||
.ui-mt-24 { margin-top: px2rem(24); }
|
||||
|
||||
.ui-mt-26 { margin-top: px2rem(26); }
|
||||
|
||||
.ui-mt-28 { margin-top: px2rem(28); }
|
||||
|
||||
.ui-mt-30 { margin-top: px2rem(30); }
|
||||
|
||||
.ui-mt-32 { margin-top: px2rem(32); }
|
||||
|
||||
.ui-mt-36 { margin-top: px2rem(36); }
|
||||
|
||||
.ui-mt-40 { margin-top: px2rem(40); }
|
||||
|
||||
.ui-mr-4 { margin-right: px2rem(4); }
|
||||
|
||||
.ui-mr-6 { margin-right: px2rem(6); }
|
||||
|
||||
.ui-mr-8 { margin-right: px2rem(8); }
|
||||
|
||||
.ui-mr-10 { margin-right: px2rem(10); }
|
||||
|
||||
.ui-mr-12 { margin-right: px2rem(12); }
|
||||
|
||||
.ui-mr-14 { margin-right: px2rem(14); }
|
||||
|
||||
.ui-mr-16 { margin-right: px2rem(16); }
|
||||
|
||||
.ui-mr-20 { margin-right: px2rem(20); }
|
||||
|
||||
.ui-mr-24 { margin-right: px2rem(24); }
|
||||
|
||||
.ui-mr-26 { margin-right: px2rem(26); }
|
||||
|
||||
.ui-mr-28 { margin-right: px2rem(28); }
|
||||
|
||||
.ui-mr-30 { margin-right: px2rem(30); }
|
||||
|
||||
.ui-mr-32 { margin-right: px2rem(32); }
|
||||
|
||||
.ui-mr-36 { margin-right: px2rem(36); }
|
||||
|
||||
.ui-mr-40 { margin-right: px2rem(40); }
|
||||
|
||||
.ui-mb-4 { margin-bottom: px2rem(4); }
|
||||
|
||||
.ui-mb-6 { margin-bottom: px2rem(6); }
|
||||
|
||||
.ui-mb-8 { margin-bottom: px2rem(8); }
|
||||
|
||||
.ui-mb-10 { margin-bottom: px2rem(10); }
|
||||
|
||||
.ui-mb-12 { margin-bottom: px2rem(12); }
|
||||
|
||||
.ui-mb-14 { margin-bottom: px2rem(14); }
|
||||
|
||||
.ui-mb-16 { margin-bottom: px2rem(16); }
|
||||
|
||||
.ui-mb-20 { margin-bottom: px2rem(20); }
|
||||
|
||||
.ui-mb-24 { margin-bottom: px2rem(24); }
|
||||
|
||||
.ui-mb-26 { margin-bottom: px2rem(26); }
|
||||
|
||||
.ui-mb-28 { margin-bottom: px2rem(28); }
|
||||
|
||||
.ui-mb-30 { margin-bottom: px2rem(30); }
|
||||
|
||||
.ui-mb-32 { margin-bottom: px2rem(32); }
|
||||
|
||||
.ui-mb-36 { margin-bottom: px2rem(36); }
|
||||
|
||||
.ui-mb-40 { margin-bottom: px2rem(40); }
|
||||
|
||||
.ui-ml-4 { margin-left: px2rem(4); }
|
||||
|
||||
.ui-ml-6 { margin-left: px2rem(6); }
|
||||
|
||||
.ui-ml-8 { margin-left: px2rem(8); }
|
||||
|
||||
.ui-ml-10 { margin-left: px2rem(10); }
|
||||
|
||||
.ui-ml-12 { margin-left: px2rem(12); }
|
||||
|
||||
.ui-ml-14 { margin-left: px2rem(14); }
|
||||
|
||||
.ui-ml-16 { margin-left: px2rem(16); }
|
||||
|
||||
.ui-ml-20 { margin-left: px2rem(20); }
|
||||
|
||||
.ui-ml-24 { margin-left: px2rem(24); }
|
||||
|
||||
.ui-ml-26 { margin-left: px2rem(26); }
|
||||
|
||||
.ui-ml-28 { margin-left: px2rem(28); }
|
||||
|
||||
.ui-ml-30 { margin-left: px2rem(30); }
|
||||
|
||||
.ui-ml-32 { margin-left: px2rem(32); }
|
||||
|
||||
.ui-ml-36 { margin-left: px2rem(36); }
|
||||
|
||||
.ui-ml-40 { margin-left: px2rem(40); }
|
||||
|
||||
.ui-mgt-entrance {
|
||||
width: px2rem(140);
|
||||
height: px2rem(140);
|
||||
display: block;
|
||||
margin-top: px2rem(20);
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100vh;
|
||||
transform: translate(0);
|
||||
}
|
||||
|
||||
/* 滚动条凹槽的颜色,还可以设置边框属性 */
|
||||
*::-webkit-scrollbar-track-piece {
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 2em;
|
||||
}
|
||||
|
||||
/* 滚动条 */
|
||||
*::-webkit-scrollbar {
|
||||
width: 0;
|
||||
}
|
||||
129
src/styles/func.scss
Normal file
129
src/styles/func.scss
Normal file
@ -0,0 +1,129 @@
|
||||
@use 'sass:meta';
|
||||
|
||||
@function rpx($value) {
|
||||
@return $value * 1rpx;
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击文本状态
|
||||
*/
|
||||
@mixin click-text-active($color: rgba(0, 0, 0, 0.2)) {
|
||||
&:active {
|
||||
color: $color;
|
||||
transition: color 0.01s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $color
|
||||
* 点击背景状态
|
||||
*/
|
||||
@mixin click-bg-active($color: rgba(0, 0, 0, 0.06)) {
|
||||
&:active {
|
||||
background-color: $color;
|
||||
transition: background-color 0.01s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $rpx
|
||||
* 将元素设置成圆形
|
||||
*/
|
||||
@mixin el-to-circle($rpx) {
|
||||
width: rpx($rpx);
|
||||
height: rpx($rpx);
|
||||
border-radius: rpx($rpx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 绝对定位居中显示
|
||||
*/
|
||||
@mixin position-center() {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description flex布局垂直水平居中
|
||||
*/
|
||||
@mixin flex-center($args...) {
|
||||
@each $key, $value in meta.keywords($args) {
|
||||
& {
|
||||
#{$key}: $value;
|
||||
}
|
||||
}
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 多行文本省略号
|
||||
*/
|
||||
@mixin text-ellipsis($line: 1) {
|
||||
@if ($line == 1) {
|
||||
word-break: break-all;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: $line;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 三角气泡框
|
||||
*/
|
||||
@mixin popover($placement, $args...) {
|
||||
@each $key, $value in meta.keywords($args) {
|
||||
&::after {
|
||||
#{$key}: $value;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
color: #fff;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
content: '';
|
||||
}
|
||||
|
||||
&::after {
|
||||
@if ($placement == 'top') {
|
||||
border-bottom-width: 0;
|
||||
border-top-color: currentColor;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 100%);
|
||||
}
|
||||
@if ($placement == 'bottom') {
|
||||
border-top-width: 0;
|
||||
border-bottom-color: currentColor;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -100%);
|
||||
}
|
||||
@if ($placement == 'left') {
|
||||
border-right-width: 0;
|
||||
border-left-color: currentColor;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translate(100%, -50%);
|
||||
}
|
||||
@if ($placement == 'right') {
|
||||
border-left-width: 0;
|
||||
border-right-color: currentColor;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translate(-100%, -50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
168
src/styles/rem/css/rem.css
Normal file
168
src/styles/rem/css/rem.css
Normal file
@ -0,0 +1,168 @@
|
||||
.f-p-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html body {
|
||||
min-width: 320px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
@media (-webkit-device-pixel-ratio: 2) {
|
||||
html body {
|
||||
min-width: 640px;
|
||||
}
|
||||
}
|
||||
@media (-webkit-device-pixel-ratio: 3) {
|
||||
html body {
|
||||
min-width: 960px;
|
||||
}
|
||||
}
|
||||
|
||||
html body[data-content-max] {
|
||||
max-width: 540px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
@media (-webkit-device-pixel-ratio: 2) {
|
||||
html body[data-content-max] {
|
||||
max-width: 1080px;
|
||||
}
|
||||
}
|
||||
@media (-webkit-device-pixel-ratio: 3) {
|
||||
html body[data-content-max] {
|
||||
max-width: 1620px;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-dpr='1'] body {
|
||||
min-width: 320px;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 0.3733333333rem;
|
||||
background-color: #f8f8f8;
|
||||
border-width: 45px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 4rem;
|
||||
line-height: 4rem;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 2.6666666667rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul .icon {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
margin-bottom: 0.2666666667rem;
|
||||
line-height: 1.6rem;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 0.2666666667rem;
|
||||
}
|
||||
|
||||
main h3 {
|
||||
position: relative;
|
||||
margin-top: 0.6666666667rem;
|
||||
margin-left: 0.3466666667rem;
|
||||
font-size: 0.4rem;
|
||||
}
|
||||
|
||||
main h3::before {
|
||||
position: absolute;
|
||||
left: -0.2666666667rem;
|
||||
width: 0.16rem;
|
||||
height: 100%;
|
||||
background-color: #fc8200;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.info-items {
|
||||
margin-top: 0.2666666667rem;
|
||||
margin-bottom: 0.2666666667rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
padding: 0.4rem;
|
||||
padding-left: 0;
|
||||
margin-top: 0.2666666667rem;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.info-item span {
|
||||
min-width: 1.6rem;
|
||||
text-align: center;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.info-item input {
|
||||
width: 100%;
|
||||
font-size: 0.3733333333rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
.info-item textarea {
|
||||
width: 100%;
|
||||
height: 3.3333333333rem;
|
||||
padding: 0.2666666667rem;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 0.3733333333rem;
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
border: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
.info-confirm {
|
||||
margin-bottom: 0.5333333333rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-confirm__btn {
|
||||
display: inline-block;
|
||||
width: 2.6666666667rem;
|
||||
height: 1.0666666667rem;
|
||||
margin-top: 1.0666666667rem;
|
||||
line-height: 1.0666666667rem;
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
text-decoration: none !important;
|
||||
background-color: #fc8200;
|
||||
}
|
||||
|
||||
footer {
|
||||
height: 2rem;
|
||||
line-height: 2rem;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
7
src/styles/rem/css/rem.css.map
Normal file
7
src/styles/rem/css/rem.css.map
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"version": 3,
|
||||
"mappings": "AAIA,MAAO,CACH,OAAO,CAAE,YAAY,CAGzB,SACK,CACD,MAAM,CAAE,CAAC,CACT,OAAO,CAAE,CAAC,CCsBV,SAAK,CAgBL,YAAY,CAAE,IAAI,CAClB,WAAW,CAAE,IAAI,CACjB,SAAS,CA1CM,KAAK,CA4CpB,sCAAuC,CApBvC,SAAK,CAqBD,SAAS,CAAE,KAAqB,EAGpC,sCAAuC,CAxBvC,SAAK,CAyBD,SAAS,CAAE,KAAqB,EAtBhC,2BAAoB,CA4BxB,YAAY,CAAE,IAAI,CAClB,WAAW,CAAE,IAAI,CACjB,SAAS,CAvDM,KAAK,CAyDpB,sCAAuC,CAhCnC,2BAAoB,CAiCpB,SAAS,CAAE,MAAqB,EAGpC,sCAAuC,CApCnC,2BAAoB,CAqCpB,SAAS,CAAE,MAAqB,EA/BpC,uBAAqB,CACjB,SAAS,CAlCE,KAAK,CDSxB,IAAK,CAED,YAAY,CCQJ,IAAmC,CDN3C,gBAAgB,CAAE,OAAO,CACzB,SAAS,CCAD,cAAmC,CDC3C,WAAW,CAAE,iBAAiB,CAGlC,UAAW,CACP,gBAAgB,CAAE,IAAI,CAG1B,MAAO,CACH,MAAM,CCTE,IAAmC,CDU3C,WAAW,CCVH,IAAmC,CDW3C,UAAU,CAAE,MAAM,CAClB,gBAAgB,CAAE,OAAO,CAG7B,MAAO,CACH,OAAO,CAAE,IAAI,CACb,eAAe,CAAE,YAAY,CAC7B,OAAO,CAAE,CAAC,CAEV,SAAG,CACC,OAAO,CAAE,IAAI,CACb,SAAS,CAAE,IAAI,CACf,KAAK,CCvBD,eAAmC,CDwBvC,eAAe,CAAE,MAAM,CAG3B,YAAM,CACF,aAAa,CC5BT,cAAmC,CD6BvC,KAAK,CC7BD,MAAmC,CD8BvC,MAAM,CC9BF,MAAmC,CD+BvC,WAAW,CC/BP,MAAmC,CDgCvC,UAAU,CAAE,MAAM,CAClB,gBAAgB,CAAE,OAAO,CAIjC,IAAK,CACD,OAAO,CCtCC,cAAmC,CDwC3C,OAAG,CACC,QAAQ,CAAE,QAAQ,CAClB,UAAU,CC1CN,cAAmC,CD2CvC,WAAW,CC3CP,cAAmC,CD4CvC,SAAS,CC5CL,KAAmC,CDkDvC,cAAS,CACL,OAAO,CAAE,EAAE,CACX,QAAQ,CAAE,QAAQ,CAClB,IAAI,CCrDJ,eAAmC,CDsDnC,KAAK,CCtDL,MAAmC,CDuDnC,MAAM,CAAE,IAAI,CACZ,gBAAgB,CAAE,OAAO,CAKrC,WAAY,CACR,UAAU,CC9DF,cAAmC,CD+D3C,aAAa,CC/DL,cAAmC,CDkE/C,UAAW,CACP,UAAU,CCnEF,cAAmC,CDoE3C,OAAO,CCpEC,KAAmC,CDqE3C,YAAY,CAAE,CAAC,CACf,MAAM,CAAE,cAAc,CAEtB,OAAO,CAAE,IAAI,CAEb,eAAK,CACD,SAAS,CC3EL,MAAmC,CD4EvC,UAAU,CAAE,MAAM,CAClB,YAAY,CAAE,cAAc,CAGhC,gBAAM,CACF,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,SAAS,CCnFL,cAAmC,CDoFvC,WAAW,CAAE,OAAO,CACpB,OAAO,CAAE,IAAI,CAGjB,mBAAS,CACL,OAAO,CCzFH,cAAmC,CD0FvC,KAAK,CAAE,IAAI,CACX,MAAM,CAAE,IAAI,CACZ,MAAM,CC5FF,eAAmC,CD6FvC,SAAS,CC7FL,cAAmC,CD8FvC,WAAW,CAAE,iBAAiB,CAC9B,WAAW,CAAE,OAAO,CACpB,wBAAwB,CAAE,IAAI,CAC9B,gBAAgB,CAAE,IAAI,CAI9B,aAAc,CACV,aAAa,CCtGL,cAAmC,CDuG3C,UAAU,CAAE,MAAM,CAElB,kBAAO,CACH,OAAO,CAAE,YAAY,CACrB,UAAU,CC3GN,eAAmC,CD4GvC,KAAK,CC5GD,eAAmC,CD6GvC,MAAM,CC7GF,eAAmC,CD8GvC,WAAW,CC9GP,eAAmC,CD+GvC,UAAU,CAAE,MAAM,CAClB,gBAAgB,CAAE,OAAO,CACzB,eAAe,CAAE,eAAe,CAChC,KAAK,CAAE,eAAe,CAK9B,MAAO,CACH,MAAM,CCxHE,IAAmC,CDyH3C,WAAW,CCzHH,IAAmC,CD0H3C,UAAU,CAAE,MAAM,CAClB,gBAAgB,CAAE,OAAO",
|
||||
"sources": ["../scss/rem.scss","../scss/_util.scss"],
|
||||
"names": [],
|
||||
"file": "rem.css"
|
||||
}
|
||||
63
src/styles/rem/index.html
Normal file
63
src/styles/rem/index.html
Normal file
@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>REM布局</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="zh-CN" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<link rel="stylesheet" href="./css/rem.css" />
|
||||
<script src="./js/rem.min.js"></script>
|
||||
<!-- <script src="http://g.tbcdn.cn/mtb/lib-flexible/0.3.4/??flexible_css.js,flexible.js"></script> -->
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
<body data-content-max>
|
||||
<section class="container">
|
||||
<header>375 * 150</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<h3>填写信息</h3>
|
||||
<div class="info-items">
|
||||
<p class="info-item">
|
||||
<span>姓名</span>
|
||||
<input type="text" class="info-item__name" placeholder="请填写姓名" />
|
||||
</p>
|
||||
<p class="info-item">
|
||||
<span>手机</span>
|
||||
<input type="number" class="info-item__tel" placeholder="请填写手机号" />
|
||||
</p>
|
||||
</div>
|
||||
<h3>个人介绍</h3>
|
||||
<div class="info-items">
|
||||
<p class="info-item f-p-0">
|
||||
<textarea class="info-item__intro" placeholder="请填写一段简要的自我介绍"></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<div class="info-confirm">
|
||||
<a href="javascript:;" class="info-confirm__btn">确认</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer>375 * 75</footer>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
70
src/styles/rem/js/rem.js
Normal file
70
src/styles/rem/js/rem.js
Normal file
@ -0,0 +1,70 @@
|
||||
!(function () {
|
||||
let docElem = document.documentElement,
|
||||
metaElem = document.querySelector('meta[name="viewport"]'),
|
||||
dpr = window.devicePixelRatio || 1,
|
||||
// 将页面分为10块
|
||||
blocks = 10,
|
||||
// 需要限制的最小宽度
|
||||
defaultMinWidth = 320,
|
||||
// 需要限制的最大宽度
|
||||
defaultMaxWidth = 540,
|
||||
// 计算的基准值
|
||||
calcMaxWidth = 9999999;
|
||||
|
||||
if (!metaElem) {
|
||||
metaElem = initMetaViewport();
|
||||
}
|
||||
|
||||
if (metaElem.getAttribute('data-content-max') !== null) {
|
||||
calcMaxWidth = defaultMaxWidth;
|
||||
}
|
||||
|
||||
// 确保meta[name="viewport"]存在
|
||||
function initMetaViewport() {
|
||||
const meta = document.createElement('meta');
|
||||
|
||||
meta.setAttribute('name', 'viewport');
|
||||
meta.setAttribute('content', 'width=device-width,initial-scale=1,user-scalable=no');
|
||||
document.head.appendChild(meta);
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
// 大部分dpr为2以下的安卓机型不识别scale,需设置不缩放
|
||||
if (navigator.appVersion.match(/android/gi) && dpr <= 2) {
|
||||
dpr = 1;
|
||||
}
|
||||
|
||||
setScale(dpr);
|
||||
|
||||
// 企业QQ设置了scale后,不能完全识别scale(此时clientWidth未收到缩放的影响而翻倍),需设置不缩放
|
||||
if (navigator.appVersion.match(/qq\//gi) && docElem.clientWidth <= 360) {
|
||||
dpr = 1;
|
||||
setScale(dpr);
|
||||
}
|
||||
|
||||
docElem.setAttribute('data-dpr', dpr);
|
||||
|
||||
// 设置缩放
|
||||
function setScale(dpr) {
|
||||
metaElem.setAttribute('content', `initial-scale=${1 / dpr},maximum-scale=${1 / dpr},minimum-scale=${1 / dpr},user-scalable=no`);
|
||||
}
|
||||
|
||||
// 设置docElem字体大小
|
||||
function setFontSize() {
|
||||
let clientWidth = docElem.clientWidth;
|
||||
|
||||
clientWidth = Math.max(clientWidth, defaultMinWidth * dpr);
|
||||
|
||||
// 调整计算基准值
|
||||
if (calcMaxWidth === defaultMaxWidth) {
|
||||
clientWidth = Math.min(clientWidth, defaultMaxWidth * dpr);
|
||||
}
|
||||
|
||||
docElem.style.fontSize = `${clientWidth / blocks}px`;
|
||||
}
|
||||
|
||||
setFontSize();
|
||||
|
||||
window.addEventListener(window.orientationchange ? 'orientationchange' : 'resize', setFontSize, false);
|
||||
})();
|
||||
44
src/styles/rem/js/rem.min.js
vendored
Normal file
44
src/styles/rem/js/rem.min.js
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
!(function () {
|
||||
let c = document.documentElement,
|
||||
j = document.querySelector('meta[name="viewport"]'),
|
||||
h = window.devicePixelRatio || 1,
|
||||
a = 10,
|
||||
f = 320,
|
||||
b = 540,
|
||||
i = 9999999;
|
||||
if (!j) {
|
||||
j = e();
|
||||
}
|
||||
if (j.getAttribute('data-content-max') !== null) {
|
||||
i = b;
|
||||
}
|
||||
function e() {
|
||||
const k = document.createElement('meta');
|
||||
k.setAttribute('name', 'viewport');
|
||||
k.setAttribute('content', 'width=device-width,initial-scale=1,user-scalable=no');
|
||||
document.head.appendChild(k);
|
||||
return k;
|
||||
}
|
||||
if (navigator.appVersion.match(/android/gi) && h <= 2) {
|
||||
h = 1;
|
||||
}
|
||||
g(h);
|
||||
if (navigator.appVersion.match(/qq\//gi) && c.clientWidth <= 360) {
|
||||
h = 1;
|
||||
g(h);
|
||||
}
|
||||
c.setAttribute('data-dpr', h);
|
||||
function g(k) {
|
||||
j.setAttribute('content', `initial-scale=${1 / k},maximum-scale=${1 / k},minimum-scale=${1 / k},user-scalable=no`);
|
||||
}
|
||||
function d() {
|
||||
let k = c.clientWidth;
|
||||
k = Math.max(k, f * h);
|
||||
if (i === b) {
|
||||
k = Math.min(k, b * h);
|
||||
}
|
||||
c.style.fontSize = `${k / a}px`;
|
||||
}
|
||||
d();
|
||||
window.addEventListener(window.orientationchange ? 'orientationchange' : 'resize', d, false);
|
||||
})();
|
||||
95
src/styles/rem/scss/_util.scss
Normal file
95
src/styles/rem/scss/_util.scss
Normal file
@ -0,0 +1,95 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@use 'sass:math';
|
||||
|
||||
/* 移动端页面设计稿宽度 */
|
||||
$design-width: 750;
|
||||
|
||||
/* 移动端页面设计稿dpr基准值 */
|
||||
$design-dpr: 2;
|
||||
|
||||
/* 将移动端页面分为10块 */
|
||||
$blocks: 10;
|
||||
|
||||
/* 缩放所支持的设备最小宽度 */
|
||||
$min-device-width: 320px;
|
||||
|
||||
/* 缩放所支持的设备最大宽度 */
|
||||
$max-device-width: 540px;
|
||||
|
||||
/*
|
||||
rem与px对应关系,1rem代表在JS中设置的html font-size值(为一块的宽度),$rem即为$px对应占多少块
|
||||
|
||||
$px $rem
|
||||
------------- === ------------
|
||||
$design-width $blocks
|
||||
*/
|
||||
|
||||
/* 单位px转化为rem */
|
||||
@function px2rem($px) {
|
||||
@return #{math.div($px, $design-width) * $blocks}rem;
|
||||
}
|
||||
|
||||
/* 单位rem转化为px,可用于根据rem单位快速计算原px */
|
||||
@function rem2px($rem) {
|
||||
@return #{math.div($rem, $blocks) * $design-width}px;
|
||||
}
|
||||
|
||||
/* html根的宽度定义 */
|
||||
@mixin root-width() {
|
||||
body {
|
||||
@include container-min-width();
|
||||
|
||||
&[data-content-max] {
|
||||
@include container-max-width();
|
||||
}
|
||||
}
|
||||
|
||||
/* 某些机型虽然设备dpr大于1,但识别不了scale缩放,这里需要重新设置最小宽度防止出现横向滚动条 */
|
||||
&[data-dpr='1'] body {
|
||||
min-width: $min-device-width;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置容器拉伸的最小宽度 */
|
||||
@mixin container-min-width() {
|
||||
min-width: $min-device-width;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
||||
@media (-webkit-device-pixel-ratio: 2) {
|
||||
min-width: $min-device-width * 2;
|
||||
}
|
||||
|
||||
@media (-webkit-device-pixel-ratio: 3) {
|
||||
min-width: $min-device-width * 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置容器拉伸的最大宽度 */
|
||||
@mixin container-max-width() {
|
||||
max-width: $max-device-width;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
|
||||
@media (-webkit-device-pixel-ratio: 2) {
|
||||
max-width: $max-device-width * 2;
|
||||
}
|
||||
|
||||
@media (-webkit-device-pixel-ratio: 3) {
|
||||
max-width: $max-device-width * 3;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置字体大小,不使用rem单位, 根据dpr值分段调整 */
|
||||
@mixin font-size($fontSize) {
|
||||
font-size: $fontSize / $design-dpr;
|
||||
|
||||
[data-dpr='2'] & {
|
||||
font-size: $fontSize / $design-dpr * 2;
|
||||
}
|
||||
|
||||
[data-dpr='3'] & {
|
||||
font-size: $fontSize / $design-dpr * 3;
|
||||
}
|
||||
}
|
||||
146
src/styles/rem/scss/rem.scss
Normal file
146
src/styles/rem/scss/rem.scss
Normal file
@ -0,0 +1,146 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import './util';
|
||||
|
||||
.f-p-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
@include root-width();
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: px2rem(28);
|
||||
background-color: #f8f8f8;
|
||||
|
||||
/* rem2px的使用方式,仅用于临时计算 */
|
||||
border-width: rem2px(0.6);
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
header {
|
||||
height: px2rem(300);
|
||||
line-height: px2rem(300);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: px2rem(200);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: px2rem(120);
|
||||
height: px2rem(120);
|
||||
margin-bottom: px2rem(20);
|
||||
line-height: px2rem(120);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: px2rem(20);
|
||||
|
||||
h3 {
|
||||
position: relative;
|
||||
margin-top: px2rem(50);
|
||||
margin-left: px2rem(26);
|
||||
font-size: px2rem(30);
|
||||
|
||||
/* 字体也可以选择不使用rem
|
||||
@include font-size(30px);
|
||||
*/
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: px2rem(-20);
|
||||
width: px2rem(12);
|
||||
height: 100%;
|
||||
background-color: #fc8200;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-items {
|
||||
margin-top: px2rem(20);
|
||||
margin-bottom: px2rem(20);
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
padding: px2rem(30);
|
||||
padding-left: 0;
|
||||
margin-top: px2rem(20);
|
||||
border: 1px solid #ddd;
|
||||
|
||||
span {
|
||||
min-width: px2rem(120);
|
||||
text-align: center;
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
font-size: px2rem(28);
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: px2rem(250);
|
||||
padding: px2rem(20);
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: px2rem(28);
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
border: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
}
|
||||
|
||||
.info-confirm {
|
||||
margin-bottom: px2rem(40);
|
||||
text-align: center;
|
||||
|
||||
&__btn {
|
||||
display: inline-block;
|
||||
width: px2rem(200);
|
||||
height: px2rem(80);
|
||||
margin-top: px2rem(80);
|
||||
line-height: px2rem(80);
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
text-decoration: none !important;
|
||||
background-color: #fc8200;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
height: px2rem(150);
|
||||
line-height: px2rem(150);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
230
src/styles/vw-rem/_border.scss
Normal file
230
src/styles/vw-rem/_border.scss
Normal file
@ -0,0 +1,230 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/**
|
||||
* 获取边框某项对应的值
|
||||
* @example getBorderItemValue(10px, 2)
|
||||
* @param {string|list} $item 某一项或多个项的列表
|
||||
* @param {number} $index 下标
|
||||
* @return {string} 项值
|
||||
*/
|
||||
@function getBorderItemValue($item, $index) {
|
||||
@if (type-of($item) == list) {
|
||||
@if ($index > length($item)) {
|
||||
$index: 1;
|
||||
}
|
||||
|
||||
@return nth($item, $index);
|
||||
} @else {
|
||||
@return $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为百分比
|
||||
* @param {number} $value 值
|
||||
* @return {boolean} 是否为百分比
|
||||
*/
|
||||
@function is-percentage($value) {
|
||||
@return type-of($value) == number and unit($value) == '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* 边框圆角,支持单个值与多个值,在高清设备下px圆角加倍
|
||||
* @param {number|list} $radius 圆角值
|
||||
* @param {number} $ratio 设备像素比
|
||||
*/
|
||||
@mixin border-radius($radius: 0, $ratio: 1) {
|
||||
$border-radius-corner: (top-left, top-right, bottom-right, bottom-left);
|
||||
|
||||
/* 列表 按照四个角的顺序匹配 */
|
||||
@if (type-of($radius) == list) {
|
||||
@for $i from 1 through length($radius) {
|
||||
$item: nth($radius, $i);
|
||||
$corner: nth($border-radius-corner, $i);
|
||||
|
||||
/* 普通设备,或者为百分比则直接使用圆角值 */
|
||||
@if $ratio == 1 or is-percentage($item) {
|
||||
border-#{$corner}-radius: $item;
|
||||
}
|
||||
|
||||
/* 否则翻$ratio倍 */
|
||||
@else {
|
||||
border-#{$corner}-radius: $item * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 单个值 */
|
||||
@else {
|
||||
@if $ratio == 1 or is-percentage($radius) {
|
||||
border-radius: $radius;
|
||||
} @else {
|
||||
border-radius: $radius * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素边框
|
||||
* @param {string|list} $direction: all 为all或列表时表示多个方向的边框,否则为单个边框
|
||||
* @param {string|list} $size: 1px 边框尺寸,为列表时表将按照direction的顺序取值
|
||||
* @param {string|list} $style: solid 边框样式,高清设备下仅支持solid,同上
|
||||
* @param {string|list} $color: #ddd 边框颜色,同上
|
||||
* @param {string} $position: relative 元素定位方式,一般为relative即可
|
||||
* @param {string} $radius: 0 边框圆角
|
||||
*/
|
||||
@mixin border(
|
||||
$direction: all,
|
||||
$size: 1px,
|
||||
$style: solid,
|
||||
$color: #ddd,
|
||||
$position: relative,
|
||||
$radius: 0
|
||||
) {
|
||||
/* 多个边框 */
|
||||
@if $direction == all or type-of($direction) == list {
|
||||
/* 普通设备 */
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
@include border-radius($radius);
|
||||
|
||||
@if $direction == all {
|
||||
border: $size $style $color;
|
||||
} @else {
|
||||
@for $i from 1 through length($direction) {
|
||||
$item: nth($direction, $i);
|
||||
|
||||
border-#{$item}: getborderitemvalue($size, $i)
|
||||
getborderitemvalue($style, $i)
|
||||
getborderitemvalue($color, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 高清设备 */
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
@include border-multiple(
|
||||
$direction: $direction,
|
||||
$size: $size,
|
||||
$color: $color,
|
||||
$position: $position,
|
||||
$radius: $radius
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* 单个边框 */
|
||||
@else {
|
||||
/* 普通设备 */
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
border-#{$direction}: $size $style $color;
|
||||
}
|
||||
|
||||
/* 高清设备 */
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
@include border-single(
|
||||
$direction: $direction,
|
||||
$size: $size,
|
||||
$color: $color,
|
||||
$position: $position
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 实现1物理像素的单条边框线 */
|
||||
@mixin border-single($direction: bottom, $size: 1px, $color: #ddd, $position: relative) {
|
||||
position: $position;
|
||||
|
||||
&::after {
|
||||
/* 上下 */
|
||||
@if ($direction == top or $direction == bottom) {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: $size;
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 2) {
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 3) {
|
||||
-webkit-transform: scaleY(0.333333333333);
|
||||
transform: scaleY(0.333333333333);
|
||||
}
|
||||
}
|
||||
|
||||
/* 左右 */
|
||||
@else if ($direction == left or $direction == right) {
|
||||
top: 0;
|
||||
width: $size;
|
||||
height: 100%;
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 2) {
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 3) {
|
||||
-webkit-transform: scaleX(0.333333333333);
|
||||
transform: scaleX(0.333333333333);
|
||||
}
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
background-color: $color;
|
||||
content: '';
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
#{$direction}: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 实现1物理像素的多条边框线 */
|
||||
@mixin border-multiple($direction: all, $size: 1px, $color: #ddd, $position: relative, $radius: 0) {
|
||||
@include border-radius($radius);
|
||||
|
||||
position: $position;
|
||||
|
||||
&::after {
|
||||
@if $direction == all {
|
||||
border: $size solid $color;
|
||||
} @else {
|
||||
@for $i from 1 through length($direction) {
|
||||
$item: nth($direction, $i);
|
||||
|
||||
border-#{$item}: getborderitemvalue($size, $i) solid getborderitemvalue($color, $i);
|
||||
}
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
transform-origin: top left;
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 2) {
|
||||
@include border-radius($radius, 2);
|
||||
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 3) {
|
||||
@include border-radius($radius, 3);
|
||||
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
-webkit-transform: scale(0.333333333333, 0.333333333333);
|
||||
transform: scale(0.333333333333, 0.333333333333);
|
||||
}
|
||||
}
|
||||
}
|
||||
115
src/styles/vw-rem/_util.scss
Normal file
115
src/styles/vw-rem/_util.scss
Normal file
@ -0,0 +1,115 @@
|
||||
@charset "UTF-8";
|
||||
@use 'sass:math';
|
||||
|
||||
/* 移动端页面设计稿宽度 */
|
||||
$design-width: 750;
|
||||
|
||||
/* 移动端页面设计稿dpr基准值 */
|
||||
$design-dpr: 2;
|
||||
|
||||
/* 将移动端页面分为10块 */
|
||||
$blocks: 10;
|
||||
|
||||
/* 缩放所支持的设备最小宽度 */
|
||||
$min-device-width: 320px;
|
||||
|
||||
/* 缩放所支持的设备最大宽度 */
|
||||
$max-device-width: 540px;
|
||||
|
||||
/*
|
||||
rem与px对应关系,1rem代表html font-size值(为一块的宽度),$rem即为$px对应占多少块
|
||||
|
||||
$px $rem
|
||||
------------- === ------------
|
||||
$design-width $blocks
|
||||
*/
|
||||
|
||||
/* html根元素的font-size定义,简单地将页面分为$blocks块,方便计算 */
|
||||
@mixin root-font-size() {
|
||||
font-size: math.div(100vw, $blocks);
|
||||
|
||||
/* 最小宽度定义 */
|
||||
@media screen and (max-width: $min-device-width) {
|
||||
font-size: math.div($min-device-width, $blocks);
|
||||
}
|
||||
|
||||
body {
|
||||
@include container-min-width();
|
||||
}
|
||||
|
||||
/* 最大宽度定义 */
|
||||
&[data-content-max] {
|
||||
@media screen and (min-width: $max-device-width) {
|
||||
font-size: math.div($max-device-width, $blocks);
|
||||
}
|
||||
|
||||
body[data-content-max] {
|
||||
@include container-max-width();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 单位px转化为rem */
|
||||
@function px2rem($px) {
|
||||
@return #{math.div($px, $design-width) * $blocks}rem;
|
||||
}
|
||||
|
||||
/* 单位rem转化为px,可用于根据rem单位快速计算原px */
|
||||
@function rem2px($rem) {
|
||||
@return #{math.div($px, $design-width) * $design-width}px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现固定宽高比
|
||||
* @param {string} $position: relative 定位方式
|
||||
* @param {string} $width: 100% 容器宽度
|
||||
* @param {string} $sub: null 容器的目标子元素
|
||||
* @param {number} $aspectX: 1 容器宽
|
||||
* @param {number} $aspectY: 1 容器高
|
||||
*/
|
||||
@mixin aspect-ratio($position: relative, $width: 100%, $sub: null, $aspectX: 1, $aspectY: 1) {
|
||||
@if $sub == null {
|
||||
$sub: '*';
|
||||
}
|
||||
|
||||
position: $position;
|
||||
width: $width;
|
||||
height: 0;
|
||||
padding-top: percentage($aspectY / $aspectX);
|
||||
overflow: hidden;
|
||||
|
||||
& > #{$sub} {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置容器拉伸的最小宽度 */
|
||||
@mixin container-min-width() {
|
||||
min-width: $min-device-width;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 设置容器拉伸的最大宽度 */
|
||||
@mixin container-max-width() {
|
||||
max-width: $max-device-width;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 设置字体大小,不使用rem单位, 根据dpr值分段调整 */
|
||||
@mixin font-size($fontSize) {
|
||||
font-size: $fontSize / $design-dpr;
|
||||
|
||||
[data-dpr='2'] & {
|
||||
font-size: $fontSize / $design-dpr * 2;
|
||||
}
|
||||
|
||||
[data-dpr='3'] & {
|
||||
font-size: $fontSize / $design-dpr * 3;
|
||||
}
|
||||
}
|
||||
577
src/styles/vw-rem/css/vw-rem.css
Normal file
577
src/styles/vw-rem/css/vw-rem.css
Normal file
@ -0,0 +1,577 @@
|
||||
.f-p-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.f-border::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.f-border::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.f-border::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-bottom {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-bottom {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.f-border-bottom::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
pointer-events: none;
|
||||
background-color: #ddd;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-bottom::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.f-border-bottom::after {
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.f-border-bottom::after {
|
||||
-webkit-transform: scaleY(0.3333333333);
|
||||
transform: scaleY(0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-radius {
|
||||
border: 1px solid #ddd;
|
||||
border-top-right-radius: 20px;
|
||||
border-bottom-right-radius: 30px;
|
||||
border-bottom-left-radius: 40px;
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-radius {
|
||||
position: relative;
|
||||
border-top-right-radius: 20px;
|
||||
border-bottom-right-radius: 30px;
|
||||
border-bottom-left-radius: 40px;
|
||||
border-top-left-radius: 10px;
|
||||
}
|
||||
|
||||
.f-border-radius::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.f-border-radius::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-top-right-radius: 40px;
|
||||
border-bottom-right-radius: 60px;
|
||||
border-bottom-left-radius: 80px;
|
||||
border-top-left-radius: 20px;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.f-border-radius::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-top-right-radius: 60px;
|
||||
border-bottom-right-radius: 90px;
|
||||
border-bottom-left-radius: 120px;
|
||||
border-top-left-radius: 30px;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 10vw;
|
||||
}
|
||||
|
||||
html body {
|
||||
min-width: 320px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 320px) {
|
||||
html {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
html[data-content-max] body[data-content-max] {
|
||||
max-width: 540px;
|
||||
margin-right: auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 540px) {
|
||||
html[data-content-max] {
|
||||
font-size: 54px;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 0.3733333333rem;
|
||||
background-color: #f8f8f8;
|
||||
border-width: 45px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 4rem;
|
||||
line-height: 4rem;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 2.6666666667rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul .icon {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
margin-bottom: 0.2666666667rem;
|
||||
line-height: 1.6rem;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 0.2666666667rem;
|
||||
}
|
||||
|
||||
main h3 {
|
||||
position: relative;
|
||||
margin-top: 0.6666666667rem;
|
||||
margin-left: 0.3466666667rem;
|
||||
font-size: 0.4rem;
|
||||
}
|
||||
|
||||
main h3::before {
|
||||
position: absolute;
|
||||
left: -0.2666666667rem;
|
||||
width: 0.16rem;
|
||||
height: 100%;
|
||||
background-color: #fc8200;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.info-items {
|
||||
margin-top: 0.2666666667rem;
|
||||
margin-bottom: 0.2666666667rem;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
padding: 0.4rem;
|
||||
padding-left: 0;
|
||||
margin-top: 0.2666666667rem;
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:not(.info-item__tel) {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:not(.info-item__tel) {
|
||||
position: relative;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.info-item:not(.info-item__tel)::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item:not(.info-item__tel)::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 100px;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item:not(.info-item__tel)::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 150px;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item.info-item__tel::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
pointer-events: none;
|
||||
background-color: #ddd;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel::after {
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item.info-item__tel::after {
|
||||
-webkit-transform: scaleY(0.3333333333);
|
||||
transform: scaleY(0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:only-of-type {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:only-of-type {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.info-item:only-of-type::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item:only-of-type::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item:only-of-type::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.hover {
|
||||
border-top: 3px dotted #0f0;
|
||||
border-right: 2px dotted #ddd;
|
||||
border-bottom: 1px dotted #0f0;
|
||||
border-left: 3px dotted #0f0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.hover {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.info-item.hover::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border-top: 3px solid #0f0;
|
||||
border-right: 2px solid #ddd;
|
||||
border-bottom: 1px solid #0f0;
|
||||
border-left: 3px solid #0f0;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item.hover::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item.hover::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
.info-item span {
|
||||
min-width: 1.6rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item span::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background-color: #ddd;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item span::after {
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item span::after {
|
||||
-webkit-transform: scaleX(0.3333333333);
|
||||
transform: scaleX(0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span.hover {
|
||||
border-right: 5px solid #0f0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span.hover {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item span.hover::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background-color: #0f0;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span.hover::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item span.hover::after {
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item span.hover::after {
|
||||
-webkit-transform: scaleX(0.3333333333);
|
||||
transform: scaleX(0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
.info-item input {
|
||||
width: 100%;
|
||||
font-size: 0.3733333333rem;
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
.info-item textarea {
|
||||
width: 100%;
|
||||
height: 3.3333333333rem;
|
||||
padding: 0.2666666667rem;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 0.3733333333rem;
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
border: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
.info-confirm {
|
||||
margin-bottom: 0.5333333333rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-confirm__btn {
|
||||
display: inline-block;
|
||||
width: 2.6666666667rem;
|
||||
height: 1.0666666667rem;
|
||||
margin-top: 1.0666666667rem;
|
||||
line-height: 1.0666666667rem;
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
text-decoration: none !important;
|
||||
background-color: #fc8200;
|
||||
}
|
||||
|
||||
footer {
|
||||
height: 2rem;
|
||||
line-height: 2rem;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
7
src/styles/vw-rem/css/vw-rem.css.map
Normal file
7
src/styles/vw-rem/css/vw-rem.css.map
Normal file
File diff suppressed because one or more lines are too long
64
src/styles/vw-rem/index.html
Normal file
64
src/styles/vw-rem/index.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html data-content-max>
|
||||
<head>
|
||||
<title>VW-REM布局</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="zh-CN" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<link rel="stylesheet" href="./css/vw-rem.css" />
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
<body data-content-max>
|
||||
<section class="container">
|
||||
<!-- 此处为固定宽高比的例子 -->
|
||||
<header class="header">
|
||||
<div class="header-content">375 * 150</div>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon f-border-radius">圆角</span>
|
||||
<span class="f-border">导航入口</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<h3>填写信息</h3>
|
||||
<div class="info-items">
|
||||
<p class="info-item">
|
||||
<span>姓名</span>
|
||||
<input type="text" class="info-item__name" placeholder="请填写姓名" />
|
||||
</p>
|
||||
<p class="info-item info-item__tel">
|
||||
<span>手机</span>
|
||||
<input type="number" class="info-item__tel" placeholder="请填写手机号" />
|
||||
</p>
|
||||
</div>
|
||||
<h3>个人介绍</h3>
|
||||
<div class="info-items">
|
||||
<p class="info-item f-p-0">
|
||||
<textarea class="info-item__intro" placeholder="请填写一段简要的自我介绍"></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<div class="info-confirm">
|
||||
<a href="javascript:;" class="info-confirm__btn">确认</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer>375 * 75</footer>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
222
src/styles/vw-rem/vw-rem.scss
Normal file
222
src/styles/vw-rem/vw-rem.scss
Normal file
@ -0,0 +1,222 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import 'util';
|
||||
@import 'border';
|
||||
|
||||
.f-p-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.f-border {
|
||||
@include border($direction: all, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
.f-border-bottom {
|
||||
@include border($direction: bottom, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
/* 圆角边框自定义多个角,顺序 */
|
||||
.f-border-radius {
|
||||
@include border(
|
||||
$radius: (
|
||||
10px,
|
||||
20px,
|
||||
30px,
|
||||
40px,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
@include root-font-size();
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: px2rem(28);
|
||||
background-color: #f8f8f8;
|
||||
|
||||
/* rem2px的使用方式,仅用于临时计算 */
|
||||
border-width: rem2px(0.6);
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
header {
|
||||
height: px2rem(300);
|
||||
line-height: px2rem(300);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
/* 容器宽高比 */
|
||||
// .header {
|
||||
// @include aspect-ratio(
|
||||
// // $width: px2rem(600),
|
||||
// // $sub: ".header-content",
|
||||
// $aspectX: 375,
|
||||
// $aspectY: 150
|
||||
// )
|
||||
// }
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: px2rem(200);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: px2rem(120);
|
||||
height: px2rem(120);
|
||||
margin-bottom: px2rem(20);
|
||||
line-height: px2rem(120);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: px2rem(20);
|
||||
|
||||
h3 {
|
||||
position: relative;
|
||||
margin-top: px2rem(50);
|
||||
margin-left: px2rem(26);
|
||||
font-size: px2rem(30);
|
||||
|
||||
/* 字体也可以选择不使用rem
|
||||
@include font-size(30px);
|
||||
*/
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: px2rem(-20);
|
||||
width: px2rem(12);
|
||||
height: 100%;
|
||||
background-color: #fc8200;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-items {
|
||||
margin-top: px2rem(20);
|
||||
margin-bottom: px2rem(20);
|
||||
}
|
||||
|
||||
.info-item {
|
||||
// border: 1px solid #ddd;
|
||||
|
||||
display: flex;
|
||||
padding: px2rem(30);
|
||||
padding-left: 0;
|
||||
margin-top: px2rem(20);
|
||||
|
||||
/* 多个边框调用 */
|
||||
&:not(.info-item__tel) {
|
||||
@include border($direction: all, $size: 1px, $color: #ddd, $style: solid, $radius: 50px);
|
||||
}
|
||||
|
||||
&.info-item__tel {
|
||||
@include border($direction: bottom, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
&:only-of-type {
|
||||
@include border($direction: all, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
/* 多个边框的动态更新 */
|
||||
&.hover {
|
||||
@include border(
|
||||
$direction: (
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
),
|
||||
$size: (
|
||||
3px,
|
||||
2px,
|
||||
1px,
|
||||
),
|
||||
$color: (
|
||||
#0f0,
|
||||
#ddd,
|
||||
),
|
||||
$style: dotted
|
||||
);
|
||||
}
|
||||
|
||||
span {
|
||||
/* 单个边框调用 */
|
||||
@include border($direction: right);
|
||||
|
||||
min-width: px2rem(120);
|
||||
text-align: center;
|
||||
|
||||
/* 单个边框的动态更新 */
|
||||
&.hover {
|
||||
@include border($direction: right, $size: 5px, $color: #0f0);
|
||||
}
|
||||
|
||||
// border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
font-size: px2rem(28);
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: px2rem(250);
|
||||
padding: px2rem(20);
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: px2rem(28);
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
border: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
}
|
||||
|
||||
.info-confirm {
|
||||
margin-bottom: px2rem(40);
|
||||
text-align: center;
|
||||
|
||||
&__btn {
|
||||
display: inline-block;
|
||||
width: px2rem(200);
|
||||
height: px2rem(80);
|
||||
margin-top: px2rem(80);
|
||||
line-height: px2rem(80);
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
text-decoration: none !important;
|
||||
background-color: #fc8200;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
height: px2rem(150);
|
||||
line-height: px2rem(150);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
513
src/styles/vw/css/vw.css
Normal file
513
src/styles/vw/css/vw.css
Normal file
@ -0,0 +1,513 @@
|
||||
.f-p-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.f-border::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.f-border::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.f-border::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-bottom {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-bottom {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.f-border-bottom::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
pointer-events: none;
|
||||
background-color: #ddd;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-bottom::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.f-border-bottom::after {
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.f-border-bottom::after {
|
||||
-webkit-transform: scaleY(0.3333333333);
|
||||
transform: scaleY(0.3333333333);
|
||||
}
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-radius {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.f-border-radius {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.f-border-radius::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.f-border-radius::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 50%;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.f-border-radius::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 50%;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 3.7333333333vw;
|
||||
background-color: #f8f8f8;
|
||||
border-width: 120px;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
header {
|
||||
height: 40vw;
|
||||
line-height: 40vw;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
padding-top: 40%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header > * {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: 26.6666666667vw;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
nav ul .icon {
|
||||
width: 16vw;
|
||||
height: 16vw;
|
||||
margin-bottom: 2.6666666667vw;
|
||||
line-height: 16vw;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 2.6666666667vw;
|
||||
}
|
||||
|
||||
main h3 {
|
||||
position: relative;
|
||||
margin-top: 6.6666666667vw;
|
||||
margin-left: 3.4666666667vw;
|
||||
font-size: 4vw;
|
||||
}
|
||||
|
||||
main h3::before {
|
||||
position: absolute;
|
||||
left: -2.6666666667vw;
|
||||
width: 1.6vw;
|
||||
height: 100%;
|
||||
background-color: #fc8200;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.info-items {
|
||||
margin-top: 2.6666666667vw;
|
||||
margin-bottom: 2.6666666667vw;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
padding: 4vw;
|
||||
padding-left: 0;
|
||||
margin-top: 2.6666666667vw;
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:not(.info-item__tel) {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 50px;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:not(.info-item__tel) {
|
||||
position: relative;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.info-item:not(.info-item__tel)::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item:not(.info-item__tel)::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 100px;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item:not(.info-item__tel)::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 150px;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel {
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item.info-item__tel::after {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
pointer-events: none;
|
||||
background-color: #ddd;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item.info-item__tel::after {
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item.info-item__tel::after {
|
||||
-webkit-transform: scaleY(0.3333333333);
|
||||
transform: scaleY(0.3333333333);
|
||||
}
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:only-of-type {
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item:only-of-type {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.info-item:only-of-type::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border: 1px solid #ddd;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item:only-of-type::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item:only-of-type::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.hover {
|
||||
border-top: 3px dotted #0f0;
|
||||
border-right: 2px dotted #ddd;
|
||||
border-bottom: 1px dotted #0f0;
|
||||
border-left: 3px dotted #0f0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item.hover {
|
||||
position: relative;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.info-item.hover::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
border-top: 3px solid #0f0;
|
||||
border-right: 2px solid #ddd;
|
||||
border-bottom: 1px solid #0f0;
|
||||
border-left: 3px solid #0f0;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item.hover::after {
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item.hover::after {
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
border-radius: 0;
|
||||
-webkit-transform: scale(0.3333333333, 0.3333333333);
|
||||
transform: scale(0.3333333333, 0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
.info-item span {
|
||||
min-width: 16vw;
|
||||
text-align: center;
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item span::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background-color: #ddd;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item span::after {
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item span::after {
|
||||
-webkit-transform: scaleX(0.3333333333);
|
||||
transform: scaleX(0.3333333333);
|
||||
}
|
||||
}
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span.hover {
|
||||
border-right: 5px solid #0f0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span.hover {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.info-item span.hover::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 5px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
background-color: #0f0;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-min-device-pixel-ratio: 2) {
|
||||
.info-item span.hover::after {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 2) {
|
||||
.info-item span.hover::after {
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
}
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) and (-webkit-device-pixel-ratio: 3) {
|
||||
.info-item span.hover::after {
|
||||
-webkit-transform: scaleX(0.3333333333);
|
||||
transform: scaleX(0.3333333333);
|
||||
}
|
||||
}
|
||||
|
||||
.info-item input {
|
||||
width: 100%;
|
||||
font-size: 3.7333333333vw;
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
.info-item textarea {
|
||||
width: 100%;
|
||||
height: 33.3333333333vw;
|
||||
padding: 2.6666666667vw;
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: 3.7333333333vw;
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
border: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
.info-confirm {
|
||||
margin-bottom: 5.3333333333vw;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.info-confirm__btn {
|
||||
display: inline-block;
|
||||
width: 26.6666666667vw;
|
||||
height: 10.6666666667vw;
|
||||
margin-top: 10.6666666667vw;
|
||||
line-height: 10.6666666667vw;
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
text-decoration: none !important;
|
||||
background-color: #fc8200;
|
||||
}
|
||||
|
||||
footer {
|
||||
height: 20vw;
|
||||
line-height: 20vw;
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
7
src/styles/vw/css/vw.css.map
Normal file
7
src/styles/vw/css/vw.css.map
Normal file
File diff suppressed because one or more lines are too long
64
src/styles/vw/index.html
Normal file
64
src/styles/vw/index.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>VW布局</title>
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="zh-CN" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<link rel="stylesheet" href="./css/vw.css" />
|
||||
</head>
|
||||
<style>
|
||||
body {
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<section class="container">
|
||||
<!-- 此处为固定宽高比的例子 -->
|
||||
<header class="header">
|
||||
<div class="header-content">固定纵横比 375 * 150</div>
|
||||
</header>
|
||||
<nav>
|
||||
<ul>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon">60 * 60</span>
|
||||
<span>导航入口</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="icon f-border-radius">圆角</span>
|
||||
<span class="f-border">导航入口</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<main>
|
||||
<h3>填写信息</h3>
|
||||
<div class="info-items">
|
||||
<p class="info-item">
|
||||
<span>姓名</span>
|
||||
<input type="text" class="info-item__name" placeholder="请填写姓名" />
|
||||
</p>
|
||||
<p class="info-item info-item__tel">
|
||||
<span>手机</span>
|
||||
<input type="number" class="info-item__tel" placeholder="请填写手机号" />
|
||||
</p>
|
||||
</div>
|
||||
<h3>个人介绍</h3>
|
||||
<div class="info-items">
|
||||
<p class="info-item f-p-0">
|
||||
<textarea class="info-item__intro" placeholder="请填写一段简要的自我介绍"></textarea>
|
||||
</p>
|
||||
</div>
|
||||
<div class="info-confirm">
|
||||
<a href="javascript:;" class="info-confirm__btn">确认</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer>375 * 75</footer>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
230
src/styles/vw/scss/_border.scss
Normal file
230
src/styles/vw/scss/_border.scss
Normal file
@ -0,0 +1,230 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/**
|
||||
* 获取边框某项对应的值
|
||||
* @example getBorderItemValue(10px, 2)
|
||||
* @param {string|list} $item 某一项或多个项的列表
|
||||
* @param {number} $index 下标
|
||||
* @return {string} 项值
|
||||
*/
|
||||
@function getBorderItemValue($item, $index) {
|
||||
@if (type-of($item) == list) {
|
||||
@if ($index > length($item)) {
|
||||
$index: 1;
|
||||
}
|
||||
|
||||
@return nth($item, $index);
|
||||
} @else {
|
||||
@return $item;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为百分比
|
||||
* @param {number} $value 值
|
||||
* @return {boolean} 是否为百分比
|
||||
*/
|
||||
@function is-percentage($value) {
|
||||
@return type-of($value) == number and unit($value) == '%';
|
||||
}
|
||||
|
||||
/**
|
||||
* 边框圆角,支持单个值与多个值,在高清设备下px圆角加倍
|
||||
* @param {number|list} $radius 圆角值
|
||||
* @param {number} $ratio 设备像素比
|
||||
*/
|
||||
@mixin border-radius($radius: 0, $ratio: 1) {
|
||||
$border-radius-corner: (top-left, top-right, bottom-right, bottom-left);
|
||||
|
||||
/* 列表 按照四个角的顺序匹配 */
|
||||
@if (type-of($radius) == list) {
|
||||
@for $i from 1 through length($radius) {
|
||||
$item: nth($radius, $i);
|
||||
$corner: nth($border-radius-corner, $i);
|
||||
|
||||
/* 普通设备,或者为百分比则直接使用圆角值 */
|
||||
@if $ratio == 1 or is-percentage($item) {
|
||||
border-#{$corner}-radius: $item;
|
||||
}
|
||||
|
||||
/* 否则翻$ratio倍 */
|
||||
@else {
|
||||
border-#{$corner}-radius: $item * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 单个值 */
|
||||
@else {
|
||||
@if $ratio == 1 or is-percentage($radius) {
|
||||
border-radius: $radius;
|
||||
} @else {
|
||||
border-radius: $radius * $ratio;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 元素边框
|
||||
* @param {string|list} $direction: all 为all或列表时表示多个方向的边框,否则为单个边框
|
||||
* @param {string|list} $size: 1px 边框尺寸,为列表时表将按照direction的顺序取值
|
||||
* @param {string|list} $style: solid 边框样式,高清设备下仅支持solid,同上
|
||||
* @param {string|list} $color: #ddd 边框颜色,同上
|
||||
* @param {string} $position: relative 元素定位方式,一般为relative即可
|
||||
* @param {string} $radius: 0 边框圆角
|
||||
*/
|
||||
@mixin border(
|
||||
$direction: all,
|
||||
$size: 1px,
|
||||
$style: solid,
|
||||
$color: #ddd,
|
||||
$position: relative,
|
||||
$radius: 0
|
||||
) {
|
||||
/* 多个边框 */
|
||||
@if $direction == all or type-of($direction) == list {
|
||||
/* 普通设备 */
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
@include border-radius($radius);
|
||||
|
||||
@if $direction == all {
|
||||
border: $size $style $color;
|
||||
} @else {
|
||||
@for $i from 1 through length($direction) {
|
||||
$item: nth($direction, $i);
|
||||
|
||||
border-#{$item}: getborderitemvalue($size, $i)
|
||||
getborderitemvalue($style, $i)
|
||||
getborderitemvalue($color, $i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 高清设备 */
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
@include border-multiple(
|
||||
$direction: $direction,
|
||||
$size: $size,
|
||||
$color: $color,
|
||||
$position: $position,
|
||||
$radius: $radius
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* 单个边框 */
|
||||
@else {
|
||||
/* 普通设备 */
|
||||
@media not screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
border-#{$direction}: $size $style $color;
|
||||
}
|
||||
|
||||
/* 高清设备 */
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
@include border-single(
|
||||
$direction: $direction,
|
||||
$size: $size,
|
||||
$color: $color,
|
||||
$position: $position
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 实现1物理像素的单条边框线 */
|
||||
@mixin border-single($direction: bottom, $size: 1px, $color: #ddd, $position: relative) {
|
||||
position: $position;
|
||||
|
||||
&::after {
|
||||
/* 上下 */
|
||||
@if ($direction == top or $direction == bottom) {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: $size;
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 2) {
|
||||
-webkit-transform: scaleY(0.5);
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 3) {
|
||||
-webkit-transform: scaleY(0.333333333333);
|
||||
transform: scaleY(0.333333333333);
|
||||
}
|
||||
}
|
||||
|
||||
/* 左右 */
|
||||
@else if ($direction == left or $direction == right) {
|
||||
top: 0;
|
||||
width: $size;
|
||||
height: 100%;
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 2) {
|
||||
-webkit-transform: scaleX(0.5);
|
||||
transform: scaleX(0.5);
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 3) {
|
||||
-webkit-transform: scaleX(0.333333333333);
|
||||
transform: scaleX(0.333333333333);
|
||||
}
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
background-color: $color;
|
||||
content: '';
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
|
||||
-webkit-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
#{$direction}: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* 实现1物理像素的多条边框线 */
|
||||
@mixin border-multiple($direction: all, $size: 1px, $color: #ddd, $position: relative, $radius: 0) {
|
||||
@include border-radius($radius);
|
||||
|
||||
position: $position;
|
||||
|
||||
&::after {
|
||||
@if $direction == all {
|
||||
border: $size solid $color;
|
||||
} @else {
|
||||
@for $i from 1 through length($direction) {
|
||||
$item: nth($direction, $i);
|
||||
|
||||
border-#{$item}: getborderitemvalue($size, $i) solid getborderitemvalue($color, $i);
|
||||
}
|
||||
}
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
content: '';
|
||||
box-sizing: border-box;
|
||||
-webkit-transform-origin: top left;
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 2) {
|
||||
@include border-radius($radius, 2);
|
||||
|
||||
width: 200%;
|
||||
height: 200%;
|
||||
-webkit-transform: scale(0.5, 0.5);
|
||||
transform: scale(0.5, 0.5);
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-device-pixel-ratio: 3) {
|
||||
@include border-radius($radius, 3);
|
||||
|
||||
width: 300%;
|
||||
height: 300%;
|
||||
-webkit-transform: scale(0.333333333333, 0.333333333333);
|
||||
transform: scale(0.333333333333, 0.333333333333);
|
||||
}
|
||||
}
|
||||
}
|
||||
66
src/styles/vw/scss/_util.scss
Normal file
66
src/styles/vw/scss/_util.scss
Normal file
@ -0,0 +1,66 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
/* 移动端页面设计稿宽度 */
|
||||
$design-width: 750;
|
||||
|
||||
/* 移动端页面设计稿dpr基准值 */
|
||||
$design-dpr: 2;
|
||||
|
||||
/*
|
||||
vw与px对应关系,100vw为视窗宽度,$vw即为$px对应占多宽
|
||||
|
||||
$px $vw
|
||||
------------- === ------------
|
||||
$design-width 100vw
|
||||
*/
|
||||
|
||||
/* 单位px转化为vw */
|
||||
@function px2vw($px) {
|
||||
@return ($px / $design-width) * 100vw;
|
||||
}
|
||||
|
||||
/* 单位vw转化为px,可用于根据vw单位快速计算原px */
|
||||
@function vw2px($vw) {
|
||||
@return #{($vw / 100) * $design-width}px;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实现固定宽高比
|
||||
* @param {string} $position: relative 定位方式
|
||||
* @param {string} $width: 100% 容器宽度
|
||||
* @param {string} $sub: null 容器的目标子元素
|
||||
* @param {number} $aspectX: 1 容器宽
|
||||
* @param {number} $aspectY: 1 容器高
|
||||
*/
|
||||
@mixin aspect-ratio($position: relative, $width: 100%, $sub: null, $aspectX: 1, $aspectY: 1) {
|
||||
@if $sub == null {
|
||||
$sub: '*';
|
||||
}
|
||||
|
||||
position: $position;
|
||||
width: $width;
|
||||
height: 0;
|
||||
padding-top: percentage($aspectY / $aspectX);
|
||||
overflow: hidden;
|
||||
|
||||
& > #{$sub} {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 设置字体大小,不使用rem单位, 根据dpr值分段调整 */
|
||||
@mixin font-size($fontSize) {
|
||||
font-size: $fontSize / $design-dpr;
|
||||
|
||||
[data-dpr='2'] & {
|
||||
font-size: $fontSize / $design-dpr * 2;
|
||||
}
|
||||
|
||||
[data-dpr='3'] & {
|
||||
font-size: $fontSize / $design-dpr * 3;
|
||||
}
|
||||
}
|
||||
211
src/styles/vw/scss/vw.scss
Normal file
211
src/styles/vw/scss/vw.scss
Normal file
@ -0,0 +1,211 @@
|
||||
@charset "UTF-8";
|
||||
|
||||
@import './util';
|
||||
@import './border';
|
||||
|
||||
.f-p-0 {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.f-border {
|
||||
@include border($direction: all, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
.f-border-bottom {
|
||||
@include border($direction: bottom, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
/* 圆角边框百分比 */
|
||||
.f-border-radius {
|
||||
@include border($direction: all, $radius: 50%);
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: px2vw(28);
|
||||
background-color: #f8f8f8;
|
||||
|
||||
/* vw2px的使用方式,仅用于临时计算 */
|
||||
border-width: vw2px(16);
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
header {
|
||||
height: px2vw(300);
|
||||
line-height: px2vw(300);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
/* 容器宽高比 */
|
||||
.header {
|
||||
@include aspect-ratio(
|
||||
// $width: px2vw(600),
|
||||
// $sub: ".header-content",
|
||||
$aspectX: 375,
|
||||
$aspectY: 150
|
||||
);
|
||||
}
|
||||
|
||||
nav ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
width: px2vw(200);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: px2vw(120);
|
||||
height: px2vw(120);
|
||||
margin-bottom: px2vw(20);
|
||||
line-height: px2vw(120);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
padding: px2vw(20);
|
||||
|
||||
h3 {
|
||||
position: relative;
|
||||
margin-top: px2vw(50);
|
||||
margin-left: px2vw(26);
|
||||
font-size: px2vw(30);
|
||||
|
||||
/* 字体也可以选择不使用rem
|
||||
@include font-size(30px);
|
||||
*/
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
left: px2vw(-20);
|
||||
width: px2vw(12);
|
||||
height: 100%;
|
||||
background-color: #fc8200;
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-items {
|
||||
margin-top: px2vw(20);
|
||||
margin-bottom: px2vw(20);
|
||||
}
|
||||
|
||||
.info-item {
|
||||
// border: 1px solid #ddd;
|
||||
|
||||
display: flex;
|
||||
padding: px2vw(30);
|
||||
padding-left: 0;
|
||||
margin-top: px2vw(20);
|
||||
|
||||
/* 多个边框调用 */
|
||||
&:not(.info-item__tel) {
|
||||
@include border($direction: all, $size: 1px, $color: #ddd, $style: solid, $radius: 50px);
|
||||
}
|
||||
|
||||
&.info-item__tel {
|
||||
@include border($direction: bottom, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
&:only-of-type {
|
||||
@include border($direction: all, $size: 1px, $color: #ddd, $style: solid);
|
||||
}
|
||||
|
||||
/* 多个边框的动态更新 */
|
||||
&.hover {
|
||||
@include border(
|
||||
$direction: (
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
),
|
||||
$size: (
|
||||
3px,
|
||||
2px,
|
||||
1px,
|
||||
),
|
||||
$color: (
|
||||
#0f0,
|
||||
#ddd,
|
||||
),
|
||||
$style: dotted
|
||||
);
|
||||
}
|
||||
|
||||
span {
|
||||
/* 单个边框调用 */
|
||||
@include border($direction: right);
|
||||
|
||||
min-width: px2vw(120);
|
||||
text-align: center;
|
||||
|
||||
/* 单个边框的动态更新 */
|
||||
&.hover {
|
||||
@include border($direction: right, $size: 5px, $color: #0f0);
|
||||
}
|
||||
|
||||
// border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
font-size: px2vw(28);
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
height: px2vw(250);
|
||||
padding: px2vw(20);
|
||||
font-family: 'Microsoft YaHei';
|
||||
font-size: px2vw(28);
|
||||
-webkit-text-size-adjust: none;
|
||||
text-size-adjust: none;
|
||||
border: none;
|
||||
caret-color: #fc8200;
|
||||
}
|
||||
}
|
||||
|
||||
.info-confirm {
|
||||
margin-bottom: px2vw(40);
|
||||
text-align: center;
|
||||
|
||||
&__btn {
|
||||
display: inline-block;
|
||||
width: px2vw(200);
|
||||
height: px2vw(80);
|
||||
margin-top: px2vw(80);
|
||||
line-height: px2vw(80);
|
||||
color: #fff !important;
|
||||
text-align: center;
|
||||
text-decoration: none !important;
|
||||
background-color: #fc8200;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
height: px2vw(150);
|
||||
line-height: px2vw(150);
|
||||
text-align: center;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
128
src/utils/Storage.ts
Normal file
128
src/utils/Storage.ts
Normal file
@ -0,0 +1,128 @@
|
||||
// 默认缓存期限为7天
|
||||
const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7;
|
||||
|
||||
/**
|
||||
* 创建本地缓存对象
|
||||
* @param {string=} prefixKey -
|
||||
* @param {Object} [storage=localStorage] - sessionStorage | localStorage
|
||||
*/
|
||||
export const createStorage = ({ prefixKey = '', storage = localStorage } = {}) => {
|
||||
/**
|
||||
* 本地缓存类
|
||||
* @class Storage
|
||||
*/
|
||||
const Storage = class {
|
||||
private storage = storage;
|
||||
private prefixKey?: string = prefixKey;
|
||||
|
||||
private getKey(key: string) {
|
||||
return `${this.prefixKey}${key}`.toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 设置缓存
|
||||
* @param {string} key 缓存键
|
||||
* @param {*} value 缓存值
|
||||
* @param expire
|
||||
*/
|
||||
set(key: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
||||
const stringData = JSON.stringify({
|
||||
value,
|
||||
expire: expire !== null ? new Date().getTime() + expire * 1000 : null,
|
||||
});
|
||||
this.storage.setItem(this.getKey(key), stringData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取缓存
|
||||
* @param {string} key 缓存键
|
||||
* @param {*=} def 默认值
|
||||
*/
|
||||
get<T = any>(key: string, def: any = null): T {
|
||||
const item = this.storage.getItem(this.getKey(key));
|
||||
if (item) {
|
||||
try {
|
||||
const data = JSON.parse(item);
|
||||
const { value, expire } = data;
|
||||
// 在有效期内直接返回
|
||||
if (expire === null || expire >= Date.now()) {
|
||||
return value;
|
||||
}
|
||||
this.remove(this.getKey(key));
|
||||
} catch (e) {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从缓存删除某项
|
||||
* @param {string} key
|
||||
*/
|
||||
remove(key: string) {
|
||||
console.log(key, '搜索');
|
||||
this.storage.removeItem(this.getKey(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空所有缓存
|
||||
* @memberOf Cache
|
||||
*/
|
||||
clear(): void {
|
||||
this.storage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置cookie
|
||||
* @param {string} name cookie 名称
|
||||
* @param {*} value cookie 值
|
||||
* @param {number=} expire 过期时间
|
||||
* 如果过期时间为设置,默认关闭浏览器自动删除
|
||||
* @example
|
||||
*/
|
||||
setCookie(name: string, value: any, expire: number | null = DEFAULT_CACHE_TIME) {
|
||||
document.cookie = `${this.getKey(name)}=${value}; Max-Age=${expire}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名字获取cookie值
|
||||
* @param name
|
||||
*/
|
||||
getCookie(name: string): string {
|
||||
const cookieArr = document.cookie.split('; ');
|
||||
for (let i = 0, length = cookieArr.length; i < length; i++) {
|
||||
const kv = cookieArr[i].split('=');
|
||||
if (kv[0] === this.getKey(name)) {
|
||||
return kv[1];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名字删除指定的cookie
|
||||
* @param {string} key
|
||||
*/
|
||||
removeCookie(key: string) {
|
||||
this.setCookie(key, 1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空cookie,使所有cookie失效
|
||||
*/
|
||||
clearCookie(): void {
|
||||
const keys = document.cookie.match(/[^ =;]+(?==)/g);
|
||||
if (keys) {
|
||||
for (let i = keys.length; i--; ) {
|
||||
document.cookie = `${keys[i]}=0;expire=${new Date(0).toUTCString()}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return new Storage();
|
||||
};
|
||||
|
||||
export const Storage = createStorage();
|
||||
|
||||
export default Storage;
|
||||
32
src/utils/httpEnum.ts
Normal file
32
src/utils/httpEnum.ts
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @description: 请求结果集
|
||||
*/
|
||||
export enum ResultEnum {
|
||||
SUCCESS = 0,
|
||||
ERROR = 7,
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 请求方法
|
||||
*/
|
||||
export enum RequestEnum {
|
||||
GET = 'GET',
|
||||
POST = 'POST',
|
||||
PATCH = 'PATCH',
|
||||
PUT = 'PUT',
|
||||
DELETE = 'DELETE',
|
||||
}
|
||||
|
||||
/**
|
||||
* @description: 常用的contentTyp类型
|
||||
*/
|
||||
export enum ContentTypeEnum {
|
||||
// json
|
||||
JSON = 'application/json;charset=UTF-8',
|
||||
// json
|
||||
TEXT = 'text/plain;charset=UTF-8',
|
||||
// form-data 一般配合qs
|
||||
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
|
||||
// form-data 上传
|
||||
FORM_DATA = 'multipart/form-data;charset=UTF-8',
|
||||
}
|
||||
13
src/utils/importAll.ts
Normal file
13
src/utils/importAll.ts
Normal file
@ -0,0 +1,13 @@
|
||||
/**
|
||||
* @description 按照一定路径规则批量导入文件
|
||||
* @param pathRule eg: pathRule = './modules/*.ts'
|
||||
*/
|
||||
export const importAll = (pathRule: string) => {
|
||||
const allModules = import.meta.globEager(pathRule);
|
||||
const modules = {} as any;
|
||||
Object.keys(allModules).forEach((path) => {
|
||||
const fileName = path.replace(/(.*\/)*([^.]+).*/gi, '$2');
|
||||
modules[fileName] = allModules[path].default;
|
||||
});
|
||||
return modules;
|
||||
};
|
||||
153
src/utils/request.ts
Normal file
153
src/utils/request.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import { ref } from 'vue';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
import { showToast, showLoadingToast, closeToast } from 'vant';
|
||||
import { ContentTypeEnum } from './httpEnum';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { getLogin } from '@/api/demo';
|
||||
// import router from '@/router';
|
||||
|
||||
// 超文本传输协议
|
||||
let href = ref('http:');
|
||||
if (location.href.includes('https:')) {
|
||||
href.value = 'https:';
|
||||
}
|
||||
|
||||
// 创建一个axios实例
|
||||
const service = axios.create({
|
||||
baseURL: `${href.value}${import.meta.env.VITE_BASE_API as string}`,
|
||||
withCredentials: false, // 当跨域请求时发送cookie
|
||||
timeout: 15000, // 请求超时时间
|
||||
});
|
||||
|
||||
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
|
||||
hideLoading?: boolean;
|
||||
}
|
||||
|
||||
interface BaseResponse<T = any> {
|
||||
code: number;
|
||||
data: T;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
// request请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: CustomAxiosRequestConfig) => {
|
||||
// 不传递默认开启loading
|
||||
if (!config.hideLoading) {
|
||||
showLoadingToast({
|
||||
message: '加载中...',
|
||||
forbidClick: true,
|
||||
});
|
||||
}
|
||||
const userStore = useUserStore() as any;
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.chatId = 1;
|
||||
}
|
||||
if ((localStorage.getItem('workToken') || userStore.token) && config.headers) {
|
||||
config.headers['Authorization'] = `Bearer ${localStorage.getItem('workToken') || userStore.token}`;
|
||||
}
|
||||
if (config.headers) {
|
||||
config.headers['Version'] = `${import.meta.env.VITE_BASE_API_VERSION}`;
|
||||
}
|
||||
if (config.url?.includes('?')) {
|
||||
config.url = `${config.url}&work_id=${localStorage.getItem('work_id') || ''}`;
|
||||
} else {
|
||||
config.url = `${config.url}?work_id=${localStorage.getItem('work_id') || ''}`;
|
||||
}
|
||||
const contentType = config.headers?.['content-type'] || config.headers?.['Content-Type'];
|
||||
const data = config.data;
|
||||
if (config.method?.toLocaleUpperCase() == 'POST' && data) {
|
||||
if (ContentTypeEnum.FORM_DATA == contentType) {
|
||||
const fd = new FormData();
|
||||
Object.keys(data).forEach((key) => fd.append(key, data[key]));
|
||||
config.data = fd;
|
||||
} else if (ContentTypeEnum.FORM_URLENCODED == contentType) {
|
||||
config.data = qs.stringify(config.data);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 处理请求错误
|
||||
console.log(error);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// response相应拦截器
|
||||
const error = ref(1);
|
||||
const errorV3 = ref(1);
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
closeToast();
|
||||
const userStore = useUserStore();
|
||||
const res = response.data;
|
||||
if (res.code === 1) {
|
||||
// 服务器访问出错了~
|
||||
showToast(res.message);
|
||||
return Promise.reject(res.message || 'code: 1');
|
||||
} else if (res.code === 2) {
|
||||
// 登录超时,重新登录
|
||||
error.value += 1;
|
||||
if (error.value <= 2) {
|
||||
// 本地环境
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.token = '53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen';
|
||||
} else {
|
||||
localStorage.removeItem('workToken');
|
||||
localStorage.removeItem('work_id');
|
||||
getLogin().then((res) => {
|
||||
let result = res.data;
|
||||
localStorage.setItem('workToken', result.user ? result.user.token : '');
|
||||
localStorage.setItem('work_id', result.user ? result.user.work_id : '');
|
||||
userStore.token = result.user ? result.user.token : '';
|
||||
userStore.name = result.user ? result.user.name : '';
|
||||
userStore.mobile = result.user ? result.user.mobile : '';
|
||||
userStore.avatar = result.user ? result.user.avatar : 'http://images.ufutx.com/201905/13/599151d27fc07ba1bc4cc57a291525e5.jpeg';
|
||||
setTimeout(() => {
|
||||
window.location.replace(location.href);
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.reject(res.message);
|
||||
} else if (res.code === 3) {
|
||||
errorV3.value += 1;
|
||||
if (errorV3.value <= 2) {
|
||||
// 本地环境
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.token = '53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen';
|
||||
} else {
|
||||
// master环境
|
||||
if (location.href.split('/#/')[1]) {
|
||||
window.location.replace(`https://health.ufutx.com/api/h5/order/bound/enterprise/auth/v2?target_path=/work/#/${location.href.split('/#/')[1]}`);
|
||||
} else {
|
||||
window.location.replace(`https://health.ufutx.com/api/h5/order/bound/enterprise/auth/v2?target_path=/work/#/h5/boundEnterprise`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
} else {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
if (error.message?.includes('timeout')) {
|
||||
showToast('请求超时!');
|
||||
}
|
||||
console.log(`err${error}`);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
const request = <T = any>(config: CustomAxiosRequestConfig): Promise<BaseResponse<T>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
service
|
||||
.request<BaseResponse<T>>(config)
|
||||
.then((res) => resolve(res.data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export default request;
|
||||
151
src/utils/requestApp.ts
Normal file
151
src/utils/requestApp.ts
Normal file
@ -0,0 +1,151 @@
|
||||
import { ref } from 'vue';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
import { showToast, showLoadingToast, closeToast } from 'vant';
|
||||
import { ContentTypeEnum } from './httpEnum';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { getLogin } from '@/api/demo';
|
||||
// import router from '@/router';
|
||||
|
||||
// 超文本传输协议
|
||||
let href = ref('http:');
|
||||
if (location.href.includes('https:')) {
|
||||
href.value = 'https:';
|
||||
}
|
||||
|
||||
// 创建一个axios实例
|
||||
const service = axios.create({
|
||||
baseURL: `${href.value}${import.meta.env.VITE_BASE_API_APP as string}`,
|
||||
withCredentials: false, // 当跨域请求时发送cookie
|
||||
timeout: 15000, // 请求超时时间
|
||||
});
|
||||
|
||||
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
|
||||
hideLoading?: boolean;
|
||||
}
|
||||
|
||||
interface BaseResponse<T = any> {
|
||||
code: number;
|
||||
data: T;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
// request请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: CustomAxiosRequestConfig) => {
|
||||
// 不传递默认开启loading
|
||||
if (!config.hideLoading) {
|
||||
showLoadingToast({
|
||||
message: '加载中...',
|
||||
forbidClick: true,
|
||||
});
|
||||
}
|
||||
const userStore = useUserStore() as any;
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.chatId = 1;
|
||||
}
|
||||
if (localStorage.getItem('appToken') && config.headers) {
|
||||
config.headers['Authorization'] = `Bearer ${localStorage.getItem('appToken')}`;
|
||||
}
|
||||
if (config.headers) {
|
||||
config.headers['Version'] = `${import.meta.env.VITE_BASE_API_VERSION}`;
|
||||
}
|
||||
const contentType = config.headers?.['content-type'] || config.headers?.['Content-Type'];
|
||||
const data = config.data;
|
||||
if (config.method?.toLocaleUpperCase() == 'POST' && data) {
|
||||
if (ContentTypeEnum.FORM_DATA == contentType) {
|
||||
const fd = new FormData();
|
||||
Object.keys(data).forEach((key) => fd.append(key, data[key]));
|
||||
config.data = fd;
|
||||
} else if (ContentTypeEnum.FORM_URLENCODED == contentType) {
|
||||
config.data = qs.stringify(config.data);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 处理请求错误
|
||||
console.log(error);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// response相应拦截器
|
||||
const error = ref(1);
|
||||
const errorV3 = ref(1);
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
const userStore = useUserStore();
|
||||
const res = response.data;
|
||||
if (res.code === 1) {
|
||||
closeToast();
|
||||
// 服务器访问出错了~
|
||||
showToast(res.message);
|
||||
return Promise.reject(res.message || 'code: 1');
|
||||
} else if (res.code === 2) {
|
||||
// 登录超时,重新登录
|
||||
closeToast();
|
||||
error.value += 1;
|
||||
if (error.value <= 2) {
|
||||
// 本地环境
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.token = '53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen';
|
||||
} else {
|
||||
localStorage.removeItem('workToken');
|
||||
localStorage.removeItem('work_id');
|
||||
getLogin().then((res) => {
|
||||
let result = res.data;
|
||||
localStorage.setItem('workToken', result.user ? result.user.token : '');
|
||||
localStorage.setItem('work_id', result.user ? result.user.work_id : '');
|
||||
userStore.token = result.user ? result.user.token : '';
|
||||
userStore.name = result.user ? result.user.name : '';
|
||||
userStore.mobile = result.user ? result.user.mobile : '';
|
||||
userStore.avatar = result.user ? result.user.avatar : 'http://images.ufutx.com/201905/13/599151d27fc07ba1bc4cc57a291525e5.jpeg';
|
||||
setTimeout(() => {
|
||||
window.location.replace(location.href);
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.reject(res.message);
|
||||
} else if (res.code === 3) {
|
||||
closeToast();
|
||||
errorV3.value += 1;
|
||||
if (errorV3.value <= 2) {
|
||||
// 本地环境
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.token = '53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen';
|
||||
} else {
|
||||
// master环境
|
||||
if (location.href.split('/#/')[1]) {
|
||||
window.location.replace(`https://health.ufutx.com/api/h5/order/bound/enterprise/auth/v2?target_path=/work/#/${location.href.split('/#/')[1]}`);
|
||||
} else {
|
||||
window.location.replace(`https://health.ufutx.com/api/h5/order/bound/enterprise/auth/v2?target_path=/work/#/h5/boundEnterprise`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
} else {
|
||||
closeToast();
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
if (error.message?.includes('timeout')) {
|
||||
showToast('请求超时!');
|
||||
}
|
||||
console.log(`err${error}`);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
const request = <T = any>(config: CustomAxiosRequestConfig): Promise<BaseResponse<T>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
service
|
||||
.request<BaseResponse<T>>(config)
|
||||
.then((res) => resolve(res.data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export default request;
|
||||
111
src/utils/requestGo.ts
Normal file
111
src/utils/requestGo.ts
Normal file
@ -0,0 +1,111 @@
|
||||
import { ref } from 'vue';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
import { showToast, showLoadingToast, closeToast } from 'vant';
|
||||
import { ContentTypeEnum } from './httpEnum';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
// 超文本传输协议
|
||||
let href = ref('http:');
|
||||
if (location.href.includes('https:')) {
|
||||
href.value = 'https:';
|
||||
}
|
||||
|
||||
// 创建一个axios实例
|
||||
const service = axios.create({
|
||||
baseURL: `${import.meta.env.VITE_BASE_API_go as string}`,
|
||||
withCredentials: false, // 当跨域请求时发送cookie
|
||||
timeout: 50000, // 请求超时时间
|
||||
});
|
||||
|
||||
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
|
||||
hideLoading?: boolean;
|
||||
}
|
||||
|
||||
interface BaseResponse<T = any> {
|
||||
code: number;
|
||||
data: T;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
// request请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: CustomAxiosRequestConfig) => {
|
||||
// 不传递默认开启loading
|
||||
if (!config.hideLoading) {
|
||||
showLoadingToast({
|
||||
message: '加载中...',
|
||||
duration: 0,
|
||||
overlayClass: 'ui-van-overlay',
|
||||
overlay: true,
|
||||
});
|
||||
}
|
||||
const userStore = useUserStore() as any;
|
||||
// if (import.meta.env.MODE === 'development') {
|
||||
// userStore.chatId = 31146624805;
|
||||
// userStore.serviceUserId = 31;
|
||||
// }
|
||||
if ((localStorage.getItem('workToken') || userStore.token) && config.headers) {
|
||||
config.headers['Authorization'] = `Bearer ${localStorage.getItem('workToken') || userStore.token}`;
|
||||
}
|
||||
if (config.headers) {
|
||||
config.headers['Version'] = `${import.meta.env.VITE_BASE_API_VERSION}`;
|
||||
}
|
||||
if (config.url?.includes('?')) {
|
||||
config.url = `${config.url}&work_id=${localStorage.getItem('work_id') || ''}&service_user_id=${userStore.serviceUserId || ''}`;
|
||||
} else {
|
||||
config.url = `${config.url}?work_id=${localStorage.getItem('work_id') || ''}&service_user_id=${userStore.serviceUserId || ''}`;
|
||||
}
|
||||
const contentType = config.headers?.['content-type'] || config.headers?.['Content-Type'];
|
||||
const data = config.data;
|
||||
if (config.method?.toLocaleUpperCase() == 'POST' && data) {
|
||||
if (ContentTypeEnum.FORM_DATA == contentType) {
|
||||
const fd = new FormData();
|
||||
Object.keys(data).forEach((key) => fd.append(key, data[key]));
|
||||
config.data = fd;
|
||||
} else if (ContentTypeEnum.FORM_URLENCODED == contentType) {
|
||||
config.data = qs.stringify(config.data);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 处理请求错误
|
||||
console.log(error, '*--------*');
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// response相应拦截器
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
const res = response.data;
|
||||
if (res.code === 1) {
|
||||
closeToast();
|
||||
// 服务器访问出错了~
|
||||
showToast(res.message);
|
||||
return Promise.reject(res.message || 'code: 1');
|
||||
} else {
|
||||
closeToast();
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
if (error.message?.includes('timeout')) {
|
||||
showToast('网络异常,请稍后重试!');
|
||||
}
|
||||
console.log(`err${error}`);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
const request = <T = any>(config: CustomAxiosRequestConfig): Promise<BaseResponse<T>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
service
|
||||
.request<BaseResponse<T>>(config)
|
||||
.then((res) => resolve(res.data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export default request;
|
||||
86
src/utils/service.ts
Normal file
86
src/utils/service.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { ref } from 'vue';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
import { showToast, showLoadingToast, closeToast } from 'vant';
|
||||
import { ContentTypeEnum } from './httpEnum';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const service = axios.create({
|
||||
// VITE_API_URL || VITE_BASE_API
|
||||
baseURL: import.meta.env.VITE_BASE_API as string, // url = base api url + request url
|
||||
withCredentials: false, // send cookies when cross-domain requests
|
||||
timeout: 15000, // request timeout
|
||||
});
|
||||
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
|
||||
hideLoading?: boolean;
|
||||
}
|
||||
|
||||
// request拦截器 request interceptor
|
||||
service.interceptors.request.use(
|
||||
(config: CustomAxiosRequestConfig) => {
|
||||
// 不传递默认开启loading
|
||||
if (!config.hideLoading) {
|
||||
showLoadingToast({
|
||||
message: '上传中...',
|
||||
forbidClick: true,
|
||||
duration: 0,
|
||||
});
|
||||
}
|
||||
const userStore = useUserStore();
|
||||
if (userStore.token && config.headers) {
|
||||
config.headers['Authorization'] = `Bearer ${userStore.token}`;
|
||||
}
|
||||
if (config.headers) {
|
||||
config.headers['Version'] = `${import.meta.env.VITE_BASE_API_VERSION}`;
|
||||
}
|
||||
const contentType = config.headers?.['content-type'] || config.headers?.['Content-Type'];
|
||||
const data = config.data;
|
||||
if (config.method?.toLocaleUpperCase() == 'POST' && data) {
|
||||
if (ContentTypeEnum.FORM_DATA == contentType) {
|
||||
const fd = new FormData();
|
||||
Object.keys(data).forEach((key) => fd.append(key, data[key]));
|
||||
config.data = fd;
|
||||
} else if (ContentTypeEnum.FORM_URLENCODED == contentType) {
|
||||
config.data = qs.stringify(config.data);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// do something with request error
|
||||
console.log(error); // for debug
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
const error = ref(1);
|
||||
// respone拦截器
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
closeToast();
|
||||
const res = response.data;
|
||||
if (res.code === 1) {
|
||||
// 服务器访问出错了~
|
||||
showToast(res.message);
|
||||
return Promise.reject(res.message || 'code: 1');
|
||||
} else if (res.code === 2) {
|
||||
// 登录超时,重新登录
|
||||
error.value += 1;
|
||||
if (error.value <= 2) {
|
||||
console.log(error.value, '登录失效,重新登录');
|
||||
}
|
||||
return Promise.reject(res.message);
|
||||
} else {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
if (error.message?.includes('timeout')) {
|
||||
showToast('请求超时!');
|
||||
}
|
||||
console.log(`err${error}`); // for debug
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
export default service;
|
||||
176
src/utils/weChat.ts
Normal file
176
src/utils/weChat.ts
Normal file
@ -0,0 +1,176 @@
|
||||
import { ref } from 'vue';
|
||||
import axios, { AxiosRequestConfig } from 'axios';
|
||||
import qs from 'qs';
|
||||
import { showLoadingToast, closeToast, showDialog } from 'vant';
|
||||
import { ContentTypeEnum } from './httpEnum';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { getLogin } from '@/api/demo';
|
||||
import router from '@/router';
|
||||
|
||||
// 超文本传输协议
|
||||
let href = ref('http:');
|
||||
if (location.href.includes('https:')) {
|
||||
href.value = 'https:';
|
||||
}
|
||||
|
||||
// 创建一个axios实例
|
||||
const service = axios.create({
|
||||
baseURL: `${import.meta.env.VITE_BASE_API as string}`,
|
||||
withCredentials: false, // 当跨域请求时发送cookie
|
||||
timeout: 15000, // 请求超时时间
|
||||
});
|
||||
|
||||
interface CustomAxiosRequestConfig extends AxiosRequestConfig {
|
||||
hideLoading?: boolean;
|
||||
}
|
||||
|
||||
interface BaseResponse<T = any> {
|
||||
code: number;
|
||||
data: T;
|
||||
content: T;
|
||||
msg: string;
|
||||
}
|
||||
|
||||
// request请求拦截器
|
||||
service.interceptors.request.use(
|
||||
(config: CustomAxiosRequestConfig) => {
|
||||
// 不传递默认开启loading
|
||||
if (!config.hideLoading) {
|
||||
showLoadingToast({
|
||||
message: '加载中...',
|
||||
forbidClick: true,
|
||||
});
|
||||
}
|
||||
// 192.168.31.30:8090
|
||||
const userStore = useUserStore() as any;
|
||||
// if (import.meta.env.MODE === 'development') {
|
||||
// userStore.chatId = 31146624805;
|
||||
// userStore.serviceUserId = 31;
|
||||
// }
|
||||
if (userStore.token && config.headers) {
|
||||
config.headers['Authorization'] = `Bearer ${localStorage.getItem('workToken') || userStore.token}`;
|
||||
}
|
||||
if (config.headers) {
|
||||
config.headers['Version'] = `${import.meta.env.VITE_BASE_API_VERSION}`;
|
||||
}
|
||||
if (config.url?.includes('?')) {
|
||||
config.url = `${config.url}&work_id=${localStorage.getItem('work_id') || ''}&service_user_id=${userStore.serviceUserId || ''}`;
|
||||
} else {
|
||||
config.url = `${config.url}?work_id=${localStorage.getItem('work_id') || ''}&service_user_id=${userStore.serviceUserId || ''}`;
|
||||
}
|
||||
const contentType = config.headers?.['content-type'] || config.headers?.['Content-Type'];
|
||||
const data = config.data;
|
||||
if (config.method?.toLocaleUpperCase() == 'POST' && data) {
|
||||
if (ContentTypeEnum.FORM_DATA == contentType) {
|
||||
const fd = new FormData();
|
||||
Object.keys(data).forEach((key) => fd.append(key, data[key]));
|
||||
config.data = fd;
|
||||
} else if (ContentTypeEnum.FORM_URLENCODED == contentType) {
|
||||
config.data = qs.stringify(config.data);
|
||||
}
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
// 处理请求错误
|
||||
console.log(error);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// response相应拦截器
|
||||
const error = ref(1);
|
||||
const errorV3 = ref(1);
|
||||
service.interceptors.response.use(
|
||||
(response) => {
|
||||
closeToast();
|
||||
const userStore = useUserStore();
|
||||
const res = response.data;
|
||||
if (res.code === 1) {
|
||||
// console.log(router.currentRoute.value.name, `${res.message}222`, '77777777777777777');
|
||||
// 服务器访问出错了~
|
||||
if (res.message == 'data is empty') {
|
||||
router.replace({
|
||||
name: 'notFound',
|
||||
});
|
||||
} else {
|
||||
showDialog({
|
||||
title: '温馨提示',
|
||||
overlayStyle: { background: '#333333', opacity: 0.8 },
|
||||
message: res.message,
|
||||
}).then(() => {});
|
||||
}
|
||||
return Promise.reject(res.message || 'code: 1');
|
||||
} else if (res.code === 2) {
|
||||
// 登录超时,重新登录
|
||||
error.value += 1;
|
||||
if (error.value <= 2) {
|
||||
// 本地环境
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.token = '53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen';
|
||||
} else {
|
||||
localStorage.removeItem('workToken');
|
||||
localStorage.removeItem('work_id');
|
||||
getLogin().then((res) => {
|
||||
let result = res.data;
|
||||
localStorage.setItem('workToken', result.user ? result.user.token : '');
|
||||
localStorage.setItem('work_id', result.user ? result.user.work_id : '');
|
||||
userStore.token = result.user ? result.user.token : '';
|
||||
userStore.name = result.user ? result.user.name : '';
|
||||
userStore.mobile = result.user ? result.user.mobile : '';
|
||||
userStore.avatar = result.user ? result.user.avatar : 'http://images.ufutx.com/201905/13/599151d27fc07ba1bc4cc57a291525e5.jpeg';
|
||||
setTimeout(() => {
|
||||
window.location.replace(location.href);
|
||||
}, 200);
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.reject(res.message);
|
||||
} else if (res.code === 3) {
|
||||
errorV3.value += 1;
|
||||
if (errorV3.value <= 2) {
|
||||
// 本地环境
|
||||
if (import.meta.env.MODE === 'development') {
|
||||
userStore.token = '53|SPv38Y7yBN7AFglo82Cze3hU1qVqJPL8QUG4UDen';
|
||||
} else {
|
||||
// master环境
|
||||
if (location.href.split('?')[1]) {
|
||||
window.location.replace(`https://health.ufutx.com/api/h5/talk/auth?${location.href.split('?')[1]}`);
|
||||
} else {
|
||||
window.location.replace(`https://health.ufutx.com/api/h5/order/bound/enterprise/auth/v2?target_path=boundEnterprise`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(response);
|
||||
} else {
|
||||
return Promise.resolve(response);
|
||||
}
|
||||
},
|
||||
(error: Error) => {
|
||||
if (error.message?.includes('timeout')) {
|
||||
showDialog({
|
||||
title: '温馨提示',
|
||||
overlayStyle: { background: '#333333', opacity: 0.8 },
|
||||
message: '网络环境异常,请稍后重试!',
|
||||
}).then(() => {
|
||||
router.replace({
|
||||
name: 'boundEnterprise',
|
||||
});
|
||||
router.go(-1);
|
||||
});
|
||||
}
|
||||
console.log(`err${error}`);
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
const request = <T = any>(config: CustomAxiosRequestConfig): Promise<BaseResponse<T>> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
service
|
||||
.request<BaseResponse<T>>(config)
|
||||
.then((res) => resolve(res.data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
};
|
||||
|
||||
export default request;
|
||||
225
src/views/appDir/appAddOrder.vue
Normal file
225
src/views/appDir/appAddOrder.vue
Normal file
@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="ui-appAddOrder">
|
||||
<div class="ui-data-box">
|
||||
<div class="font_28 color3 ui-pl-20 ui-pb-20">昵称</div>
|
||||
<van-field v-model="name" class="ui-data-input f-fcc font_30 color3 bold" placeholder="请填写昵称" />
|
||||
<div class="font_28 color3 ui-pl-20 ui-pb-20 ui-pt-40">联系电话</div>
|
||||
<div class="ui-area-code-box f-fcl">
|
||||
<div class="f-fcl ui-area-code" @click.stop="switchChoose">
|
||||
<div class="font_26 color3">{{ AreaValue }}</div>
|
||||
<img v-if="showChooseArea" class="Angle_icon" src="https://image.fulllinkai.com/202112/10/697cbb5933196bbc21165cb974f2e343.png" alt="" />
|
||||
<img v-else class="Angle_icon" src="https://image.fulllinkai.com/202112/10/02d4c797f6de6494643f506c5d2b85b7.png" alt="" />
|
||||
</div>
|
||||
<van-field v-model="mobile" type="tel" class="ui-mobile-input f-fcc font_30 color3" placeholder="请填写联系电话" />
|
||||
<div v-if="showChooseArea" class="ui-relative area_code_choose_box">
|
||||
<div class="area_code_choose_list">
|
||||
<div v-for="(item, index) in areaList" :key="index" class="alignment area_code_choose" @click="selectedArea(item, index)">
|
||||
<div class="font_32 color3 area_code_choose_item" :class="AreaIndex == index ? 'colorPrice' : ''">{{ item.area_code }} {{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showChooseArea" class="ui-area-mask" @click="showChooseArea = false"></div>
|
||||
</div>
|
||||
<div class="ui-btn font_30 colorF bold f-fcc" @click="addOrder">确定创建</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import router from '@/router';
|
||||
import requestGo from '@/utils/requestGo';
|
||||
|
||||
defineOptions({ name: 'AppAddOrder' });
|
||||
|
||||
const userStore = useUserStore(); // pinia状态缓存数据
|
||||
const throttle = ref(true);
|
||||
const name = ref('');
|
||||
const mobile = ref('');
|
||||
const areaList = ref<any[]>([{ area_code: 86, label: '中国大陆' }]);
|
||||
const AreaValue = ref('中国大陆 86');
|
||||
const showChooseArea = ref(false);
|
||||
const AreaIndex = ref(0);
|
||||
|
||||
const addOrder = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
name: name.value,
|
||||
area_code: areaList.value[AreaIndex.value].area_code,
|
||||
mobile: mobile.value,
|
||||
};
|
||||
if (!name.value) {
|
||||
showToast('请填写昵称');
|
||||
return;
|
||||
}
|
||||
if (!mobile.value) {
|
||||
showToast('请填写联系电话');
|
||||
return;
|
||||
}
|
||||
if (AreaIndex.value == 0 && !/^1(3|4|5|6|7|8|9)\d{9}$/.test(mobile.value)) {
|
||||
showToast('电话号码格式错误');
|
||||
return;
|
||||
}
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/offline/order`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showToast('创建成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'personalCenter',
|
||||
});
|
||||
router.go(-2);
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getAreaCode = () => {
|
||||
requestGo({ url: `/h5/v2/user/areacode/list`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
areaList.value = result;
|
||||
console.log(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 选择国家区号
|
||||
const selectedArea = (e, index) => {
|
||||
AreaValue.value = `${e.label} ${e.area_code}`;
|
||||
AreaIndex.value = index;
|
||||
showChooseArea.value = !showChooseArea.value;
|
||||
};
|
||||
|
||||
// 国家区号弹框
|
||||
const switchChoose = () => {
|
||||
showChooseArea.value = !showChooseArea.value;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getAreaCode();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appAddOrder {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ui-data-box {
|
||||
padding: px2rem(40);
|
||||
|
||||
// 去除默认下划线
|
||||
::v-deep(.van-cell:after) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ui-data-input {
|
||||
width: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(16);
|
||||
height: px2rem(102);
|
||||
}
|
||||
|
||||
.ui-area-code-box {
|
||||
width: 100%;
|
||||
height: px2rem(102);
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
|
||||
.ui-area-code {
|
||||
padding-left: px2rem(30);
|
||||
padding-right: px2rem(20);
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.Angle_icon {
|
||||
width: px2rem(20);
|
||||
height: px2rem(12);
|
||||
display: block;
|
||||
margin-left: px2rem(10);
|
||||
}
|
||||
|
||||
.ui-mobile-input {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.ui-area-mask {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 21;
|
||||
}
|
||||
|
||||
.area_code_choose_box {
|
||||
position: absolute;
|
||||
left: px2rem(66);
|
||||
top: px2rem(86);
|
||||
background: #ffffff;
|
||||
box-shadow: 0 px2rem(4) px2rem(28) 0 rgba(0, 0, 0, 0.08);
|
||||
border-radius: px2rem(8);
|
||||
z-index: 22;
|
||||
|
||||
.area_code_choose_list {
|
||||
max-height: px2rem(420);
|
||||
overflow-y: scroll;
|
||||
|
||||
.area_code_choose {
|
||||
padding: px2rem(24) px2rem(30) 0 px2rem(30);
|
||||
|
||||
.area_code_choose_item {
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.selected_icon {
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.area_code_choose:last-child {
|
||||
padding-bottom: px2rem(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.area_code_choose_box:before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: px2rem(-24);
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
border-top: solid px2rem(12) transparent;
|
||||
border-left: solid px2rem(12) transparent;
|
||||
border-right: solid px2rem(12) transparent;
|
||||
border-bottom: solid px2rem(12) #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
width: px2rem(560);
|
||||
height: px2rem(80);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
margin: px2rem(140) auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
171
src/views/appDir/appAddWorkOrder.vue
Normal file
171
src/views/appDir/appAddWorkOrder.vue
Normal file
@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="ui-appAddWorkOrder">
|
||||
<div class="font_30 color333 bold f-fcl">添加工单</div>
|
||||
<div class="ui-describe-box">
|
||||
<van-field v-model="describe" class="ui-desc-input font_30 color3" rows="5" type="textarea" maxlength="500" show-word-limit placeholder="请输入工单描述内容" autosize />
|
||||
</div>
|
||||
<div class="ui-upload-box">
|
||||
<div v-for="(item, index) in pics" :key="index" class="ui-upload-icon-box flo_l">
|
||||
<img class="ui-upload-icon" :src="item" mode="aspectFill" alt="" @click="ImagePreview(pics, index)" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" mode="widthFix" alt="" @click="clearPic(index)" />
|
||||
</div>
|
||||
<div v-if="pics.length < 9">
|
||||
<uploadPicture :multiple="true" :max-count="9" @on-success="onSuccess" @click="takePhone">
|
||||
<div class="ui-upload">
|
||||
<img class="ui-upload-icon flo_l" src="https://image.fulllinkai.com/202301/07/6c9bc853bed42c9871f56156d1b8f31c.png" alt="" />
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-next-btn font_30 f-fcc colorF" @click="changeData">保存</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { showImagePreview, showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AddWorkOrder' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const throttle = ref(true);
|
||||
const describe = ref<any>('');
|
||||
const pics = ref<any[]>([]);
|
||||
|
||||
// 删除某个已上传的图片
|
||||
const clearPic = (index) => {
|
||||
pics.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const changeData = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
images: pics.value,
|
||||
desc: describe.value,
|
||||
};
|
||||
if (!describe.value) {
|
||||
showToast('请输入工单描述内容');
|
||||
return;
|
||||
}
|
||||
if (pics.value.length === 0) {
|
||||
showToast('请上传最少一张图片');
|
||||
return;
|
||||
}
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
weChat({ url: `/h5/add/work/order`, data, method: 'post' })
|
||||
.then(() => {
|
||||
throttle.value = true;
|
||||
showToast('保存成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'appWorkOrder',
|
||||
query: { state: 1 },
|
||||
});
|
||||
router.go(-1);
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const ImagePreview = (e, index) => {
|
||||
showImagePreview({
|
||||
images: e,
|
||||
showIndex: false,
|
||||
startPosition: index,
|
||||
loop: false,
|
||||
});
|
||||
};
|
||||
|
||||
// 上传图片
|
||||
const onSuccess = (val) => {
|
||||
if (pics.value.length < 9) {
|
||||
pics.value.push(val);
|
||||
}
|
||||
};
|
||||
|
||||
window.getAndroidPhone = (type, e) => {
|
||||
if (e && e.length > 0) {
|
||||
e.forEach((item) => {
|
||||
if (pics.value && pics.value.length < 9) {
|
||||
pics.value.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const takePhone = () => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.takePhoto('multiple', 9);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appAddWorkOrder {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
padding: px2rem(30);
|
||||
}
|
||||
|
||||
.ui-describe-box {
|
||||
padding-bottom: px2rem(40);
|
||||
|
||||
.ui-desc-input {
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
margin-top: px2rem(20);
|
||||
}
|
||||
|
||||
.inputColor {
|
||||
color: #c2c2c2;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-box {
|
||||
overflow: hidden;
|
||||
margin-bottom: px2rem(120);
|
||||
|
||||
.ui-upload-icon-box {
|
||||
position: relative;
|
||||
|
||||
.ui-upload-clear-icon {
|
||||
position: absolute;
|
||||
right: px2rem(30);
|
||||
top: px2rem(10);
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-icon {
|
||||
width: px2rem(200);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
border-radius: px2rem(16);
|
||||
margin-right: px2rem(20);
|
||||
margin-bottom: px2rem(20);
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-next-btn {
|
||||
width: px2rem(560);
|
||||
height: px2rem(80);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
170
src/views/appDir/appAssess.vue
Normal file
170
src/views/appDir/appAssess.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<div class="ui-appAssess">
|
||||
<div v-if="!loadingState && list.length == 0">
|
||||
<img class="ui-empty-data-icon" src="https://image.fulllinkai.com/202306/07/247d8ae6b90334457d1b39129cd5c490.png" alt="" />
|
||||
<div v-if="roles.length > 0" class="color6 font_30 text-center">
|
||||
当前职责为
|
||||
<span v-for="(item, index) in roles" :key="index">{{ item }}<span v-if="index + 1 != roles.length">、</span></span>
|
||||
,暂无可评估人员
|
||||
</div>
|
||||
<div v-else-if="roles.length == 0" class="color6 font_30 text-center">
|
||||
当前并未绑定职责,请
|
||||
<span class="colorTheme" @click="contactService">联系</span>
|
||||
工作人员
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="ui-list-box">
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-list-item f-fbc">
|
||||
<div class="">
|
||||
<div class="ui-name font_30 color3 bold ellipsis_1">{{ item.service_user.name }}</div>
|
||||
<div class="ui-responsibility f-fcl">
|
||||
<img v-if="item.role.name == '主教练'" class="ui-icon" src="https://image.fulllinkai.com/202306/25/103763ce22be9a1bd964080a6b45bdf0.png" alt="" />
|
||||
<img v-else-if="item.role.name == '副教练'" class="ui-icon" src="https://image.fulllinkai.com/202306/25/b73e57901f943fa31eec78b21c8e8eac.png" alt="" />
|
||||
<img v-else class="ui-icon" src="https://image.fulllinkai.com/202306/25/6a96db6ec2a77503b84e6213f9d905a6.png" alt="" />
|
||||
<div class="font_26 color6">{{ item.role.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.reappraises_count == 1" class="ui-assess-btn-v2 font_26 f-fcc" @click="jumpPath(item)">查看详情</div>
|
||||
<div v-else class="ui-assess-btn font_26 f-fcc" @click="jumpPath(item)">立即评估</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onActivated } from 'vue';
|
||||
import { closeToast } from 'vant';
|
||||
import router from '@/router';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppAssess' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const roles = ref<any[]>([]); // 当前角色职责
|
||||
const loadingState = ref(true); // 加载状态
|
||||
|
||||
// 获取评估人员列表
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/order/other/roles?chat_id=${userStore.chatId}&is_im=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
list.value = result;
|
||||
if (list.value && list.value.length === 0) {
|
||||
loadingState.value = false;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取角色职责和是否绑定企业微信
|
||||
const getRoles = () => {
|
||||
weChat({ url: `/h5/work/group/roles?chat_id=${userStore.chatId}&is_im=1`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
roles.value = result.roles;
|
||||
console.log(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
closeToast();
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const contactService = () => {
|
||||
window.location.href = `https://work.weixin.qq.com/kfid/kfc967090f765f69300`;
|
||||
};
|
||||
|
||||
const jumpPath = (e) => {
|
||||
let typeName;
|
||||
if (e.role.name === '主教练') {
|
||||
typeName = 1;
|
||||
} else if (e.role.name === '副教练') {
|
||||
typeName = 2;
|
||||
} else {
|
||||
typeName = 3;
|
||||
}
|
||||
router.push({
|
||||
name: 'appAssessDetail',
|
||||
query: { ID: e.id, state: e.reappraises_count, type: typeName },
|
||||
});
|
||||
};
|
||||
|
||||
onActivated(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
// 评估后重新请求接口
|
||||
if (route.state) {
|
||||
getList();
|
||||
getRoles();
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
console.log('222');
|
||||
let route = router.currentRoute.value.query;
|
||||
if (route.chat_id) {
|
||||
userStore.chatId = route.chat_id;
|
||||
}
|
||||
getList();
|
||||
getRoles();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appAssess {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-empty-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: 25vh auto px2rem(10) auto;
|
||||
}
|
||||
|
||||
.ui-list-box {
|
||||
padding-top: px2rem(30);
|
||||
padding-bottom: 16vh;
|
||||
|
||||
.ui-list-item {
|
||||
margin: 0 px2rem(30) px2rem(30) px2rem(30);
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(16);
|
||||
|
||||
.ui-name {
|
||||
max-width: px2rem(420);
|
||||
}
|
||||
|
||||
.ui-responsibility {
|
||||
padding-top: px2rem(20);
|
||||
|
||||
.ui-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
display: block;
|
||||
margin-right: px2rem(8);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-assess-btn,
|
||||
.ui-assess-btn-v2 {
|
||||
width: px2rem(144);
|
||||
height: px2rem(56);
|
||||
border-radius: px2rem(30);
|
||||
border: px2rem(2) solid #ffe3c1;
|
||||
color: #ffa438;
|
||||
}
|
||||
|
||||
.ui-assess-btn-v2 {
|
||||
color: #5ac7a0;
|
||||
border: px2rem(2) solid #b2e3d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
354
src/views/appDir/appAssessDetail.vue
Normal file
354
src/views/appDir/appAssessDetail.vue
Normal file
@ -0,0 +1,354 @@
|
||||
<template>
|
||||
<div class="ui-appAssessDetail">
|
||||
<div class="ui-assess-data-box">
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">1. 用户方案配合度</div>
|
||||
<div class="f-fbc ui-item-rate">
|
||||
<van-rate v-model="coordinate" gutter="10" :size="21" :readonly="readonly" color="#ffc629" void-icon="star" void-color="#e8e8e8" allow-half />
|
||||
<div class="font_28 color6">{{ coordinate * 2 }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">2. {{ firstProblem }}</div>
|
||||
<div class="f-fbc ui-item-rate">
|
||||
<van-rate v-model="firstAnswer" gutter="10" :size="21" :readonly="readonly" color="#ffc629" void-icon="star" void-color="#e8e8e8" allow-half />
|
||||
<div class="font_28 color6">{{ firstAnswer * 2 }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">3. {{ secondProblem }}</div>
|
||||
<div class="f-fbc ui-item-rate">
|
||||
<van-rate v-model="secondAnswer" gutter="10" :size="21" :readonly="readonly" color="#ffc629" void-icon="star" void-color="#e8e8e8" allow-half />
|
||||
<div class="font_28 color6">{{ secondAnswer * 2 }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">4. {{ thirdProblem }}</div>
|
||||
<div class="f-fbc ui-item-rate">
|
||||
<van-rate v-model="thirdAnswer" gutter="10" :size="21" :readonly="readonly" color="#ffc629" void-icon="star" void-color="#e8e8e8" allow-half />
|
||||
<div class="font_28 color6">{{ thirdAnswer * 2 }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">5. {{ fourthProblem }}</div>
|
||||
<div class="f-fbc ui-item-rate">
|
||||
<van-rate v-model="fourthAnswer" gutter="10" :size="21" :readonly="readonly" color="#ffc629" void-icon="star" void-color="#e8e8e8" allow-half />
|
||||
<div class="font_28 color6">{{ fourthAnswer * 2 }}分</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">6. 你会将该工作人员推荐给下一位用户吗?</div>
|
||||
<div class="f-fcl">
|
||||
<div v-for="(item, index) in selectList" :key="index" class="f-fcl ui-item-select" @click="selectChange(item)">
|
||||
<img v-show="selectValue != item" class="ui-item-select-icon" src="https://image.fulllinkai.com/202306/25/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-show="selectValue == item" class="ui-item-select-icon" src="https://image.fulllinkai.com/202306/25/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_28 color3">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-item-reason">
|
||||
<van-field v-model="recommendReason" :readonly="readonly" class="ui-item-data-input font_30 color3" rows="5" type="textarea" placeholder="请描述你的理由" autosize />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">7. 本次服务优点表现</div>
|
||||
<van-field v-model="advantage" :readonly="readonly" class="ui-item-data-input font_30 color3" rows="5" type="textarea" placeholder="请描述本次服务优点表现" autosize />
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">8. 需要改进与建议</div>
|
||||
<!-- <div class="f-fbc ui-item-rate">-->
|
||||
<!-- <van-rate v-model="rateNum" gutter="10" :size="21" :readonly="readonly" color="#ffc629" void-icon="star" void-color="#e8e8e8" allow-half />-->
|
||||
<!-- <div class="font_28 color6">{{ rateNum * 2 }}分</div>-->
|
||||
<!-- </div>-->
|
||||
<van-field v-model="suggest" :readonly="readonly" class="ui-item-data-input font_30 color3" rows="5" type="textarea" placeholder="请留下你的建议" autosize />
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">9. 特殊案例与解决方案分享</div>
|
||||
<van-field v-model="shareCase" :readonly="readonly" class="ui-item-data-input font_30 color3" rows="5" type="textarea" placeholder="请描述解决方案分享" autosize maxlength="500" />
|
||||
</div>
|
||||
<div class="ui-assess-data-item">
|
||||
<div class="font_30 color3 bold ui-item-title">10. 请分享您的想法与改善建议,以便我们更好的服务</div>
|
||||
<van-field v-model="shareSuggest" :readonly="readonly" class="ui-item-data-input font_30 color3" rows="5" type="textarea" placeholder="请描述您的想法与改善建议" autosize maxlength="500" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="state != 1" class="ui-save-btn f-fcc colorF font_30 bold" @click="submitCheck">提交</div>
|
||||
<van-popup v-model:show="showTips" round :close-on-click-overlay="false" :lock-scroll="true" :duration="0.5">
|
||||
<div class="ui-tips-box">
|
||||
<div class="color3 font_32 text-center ui-tips-text">提交后不可修改,确定提交该评估表吗?</div>
|
||||
<div class="ui-btn-box f-fbc">
|
||||
<div class="ui-btn font_32 f-fcc color6" @click="showTips = false">取消</div>
|
||||
<div class="ui-btn-v2 font_32 f-fcc colorF" @click="save">确定</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
|
||||
defineOptions({ name: 'AppAssessDetail' });
|
||||
|
||||
const ID = ref<any>('');
|
||||
const isApp = ref<any>('');
|
||||
const isIOS = ref<any>(true);
|
||||
const coordinate = ref(0); // 用户方案配合度
|
||||
const firstProblem = ref<any>(''); // 第一个问题
|
||||
const firstAnswer = ref<any>(0); // 第一个答案
|
||||
const secondProblem = ref<any>(''); // 第二个问题
|
||||
const secondAnswer = ref<any>(0); // 第二个答案
|
||||
const thirdProblem = ref<any>(''); // 第三个问题
|
||||
const thirdAnswer = ref<any>(0); // 第三个答案
|
||||
const fourthProblem = ref<any>(''); // 第四个问题
|
||||
const fourthAnswer = ref<any>(0); // 第四个答案
|
||||
const selectValue = ref<any>(''); // 推荐给下一个用户选择
|
||||
const recommendReason = ref<any>(''); // 推荐给下一个用户理由
|
||||
const selectList = ref<any[]>(['推荐', '不推荐', '建议学习升级后']);
|
||||
const shareSuggest = ref<any>(''); // 分享改善建议
|
||||
const shareCase = ref<any>(''); // 分享案例
|
||||
const state = ref<any>(''); // 判断是否已经提交过评估
|
||||
const type = ref<any>(''); // 角色type:1主教练,2副教练,3客服
|
||||
const readonly = ref<any>(false); // 评分是否只读
|
||||
const advantage = ref<any>(''); // 优点
|
||||
const rateNum = ref<any>(0); // 评分数
|
||||
const suggest = ref<any>(''); // 建议
|
||||
const throttle = ref(true);
|
||||
const showTips = ref(false);
|
||||
|
||||
// 获取详情数据
|
||||
const getDetail = () => {
|
||||
weChat({ url: `h5/roles/${ID.value}/reappraise?is_im=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
coordinate.value = result.content.coordinate || 0;
|
||||
firstAnswer.value = result.content.firstAnswer; // 第一个问题分数
|
||||
secondAnswer.value = result.content.secondAnswer; // 第二个问题分数
|
||||
thirdAnswer.value = result.content.thirdAnswer; // 第三个问题分数
|
||||
fourthAnswer.value = result.content.fourthAnswer; // 第四个问题分数
|
||||
selectValue.value = result.content.selectValue; // 第五个问题选择
|
||||
recommendReason.value = result.content.recommendReason; // 第五个问题理由
|
||||
advantage.value = result.content.advantage; // 第六个问题优点
|
||||
rateNum.value = result.content.rateNum; // 评分
|
||||
suggest.value = result.content.suggest; // 建议
|
||||
shareSuggest.value = result.content.shareSuggest || ''; // 分享改善建议
|
||||
shareCase.value = result.content.shareCase || ''; // 分享案例
|
||||
console.log(result, '77');
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 提交前信息校验
|
||||
const submitCheck = () => {
|
||||
if (!selectValue.value) {
|
||||
showToast('请选择是否推荐给下一位用户');
|
||||
return;
|
||||
}
|
||||
if (!recommendReason.value) {
|
||||
showToast('请描述是否推荐给下一位用户理由');
|
||||
return;
|
||||
}
|
||||
if (!advantage.value) {
|
||||
showToast('请描述本次服务优点表现');
|
||||
return;
|
||||
}
|
||||
if (!suggest.value) {
|
||||
showToast('请留下你的建议');
|
||||
return;
|
||||
}
|
||||
if (!shareCase.value) {
|
||||
showToast('请描述解决方案分享');
|
||||
return;
|
||||
}
|
||||
if (!shareSuggest.value) {
|
||||
showToast('请描述您的想法与改善建议');
|
||||
return;
|
||||
}
|
||||
showTips.value = true;
|
||||
};
|
||||
|
||||
// 提交
|
||||
const save = () => {
|
||||
if (throttle.value) {
|
||||
let data = {
|
||||
is_im: 1,
|
||||
content: { firstAnswer: firstAnswer.value, secondAnswer: secondAnswer.value, thirdAnswer: thirdAnswer.value, fourthAnswer: fourthAnswer.value, coordinate: coordinate.value, selectValue: selectValue.value, recommendReason: recommendReason.value, advantage: advantage.value, rateNum: rateNum.value, suggest: suggest.value, shareSuggest: shareSuggest.value, shareCase: shareCase.value },
|
||||
};
|
||||
console.log(data);
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/reappraise/roles/${ID.value}/v2`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showTips.value = false;
|
||||
showToast('提交成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
// 如果是app进入,提交后关闭当前页面
|
||||
if (isApp.value) {
|
||||
close();
|
||||
} else {
|
||||
router.replace({
|
||||
name: 'appAssess',
|
||||
query: { state: 1 },
|
||||
});
|
||||
router.go(-1);
|
||||
}
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const selectChange = (e) => {
|
||||
if (state.value * 1 === 1) {
|
||||
return;
|
||||
}
|
||||
selectValue.value = e;
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
// 调用ios app方法
|
||||
if (isIOS.value) {
|
||||
window.webkit.messageHandlers.goBack.postMessage(null);
|
||||
} else {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.goBack();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
isIOS.value = false;
|
||||
} else {
|
||||
isIOS.value = true;
|
||||
}
|
||||
ID.value = route.ID;
|
||||
state.value = route.state;
|
||||
type.value = route.type;
|
||||
if (route.isApp) {
|
||||
isApp.value = route.isApp;
|
||||
}
|
||||
// type 1主教练 2副教练 3客服
|
||||
if (type.value * 1 === 1) {
|
||||
firstProblem.value = '积极主动带领服务团队帮助客户顺利执行方案(0-10分)';
|
||||
secondProblem.value = '认真指导副教练学习并积极主动带领副教练完成服务工作(0-10分)';
|
||||
thirdProblem.value = '认真审核餐单,及时解决服务过程中的突发疑难问题(0-10分)';
|
||||
fourthProblem.value = '着装整洁,体态标准,仪容端庄,言谈得体(0-10分)';
|
||||
} else if (type.value * 1 === 2) {
|
||||
firstProblem.value = '明确工作岗位职责,熟练服务流程,每天按时发餐单,正确指导客户使用餐单(0-10分)';
|
||||
secondProblem.value = '积极主动与主教练、客服沟通、商讨客户遇到的问题并及时反馈,并遵行主教练指导(0-10分)';
|
||||
thirdProblem.value = '每天主动关心客户,生活化服务,关注数据变化并及时沟通,及时回复客户信息(0-10分)';
|
||||
fourthProblem.value = '着装整洁,体态标准,仪容端庄,言谈得体(0-10分)';
|
||||
} else {
|
||||
firstProblem.value = '及时收集客户资料,跟进客户合同签订、方案设计进程,按时发放餐单(0-10分)';
|
||||
secondProblem.value = '主动协助主教练指导副教练服务工作(0-10分)';
|
||||
thirdProblem.value = '积极主动提醒或者辅助回复服务群客户的信息问答(0-10分)';
|
||||
fourthProblem.value = '着装整洁,体态标准,仪容端庄,言谈得体(0-10分)';
|
||||
}
|
||||
// state 1已复评 0未复评
|
||||
if (state.value * 1 === 1) {
|
||||
readonly.value = true;
|
||||
getDetail();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appAssessDetail {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-assess-data-box {
|
||||
padding: px2rem(30) px2rem(30) px2rem(180) px2rem(30);
|
||||
|
||||
.ui-assess-data-item {
|
||||
padding-bottom: px2rem(60);
|
||||
|
||||
.ui-item-title {
|
||||
padding-bottom: px2rem(30);
|
||||
}
|
||||
|
||||
.ui-item-select {
|
||||
margin-right: px2rem(68);
|
||||
|
||||
.ui-item-select-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
display: block;
|
||||
margin-right: px2rem(16);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-item-reason {
|
||||
padding-top: px2rem(20);
|
||||
}
|
||||
|
||||
.ui-item-rate {
|
||||
padding-bottom: px2rem(24);
|
||||
}
|
||||
|
||||
.ui-item-data-input {
|
||||
padding: px2rem(30);
|
||||
background: #f8f8f8;
|
||||
border-radius: px2rem(16);
|
||||
line-height: px2rem(40);
|
||||
}
|
||||
|
||||
textarea::-webkit-input-placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-save-btn {
|
||||
width: px2rem(560);
|
||||
height: px2rem(80);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
position: fixed;
|
||||
bottom: 6vh;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
.ui-tips-box {
|
||||
position: relative;
|
||||
width: px2rem(520);
|
||||
padding: px2rem(92) px2rem(40) px2rem(50) px2rem(40);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-tips-text {
|
||||
padding: 0 px2rem(60);
|
||||
}
|
||||
|
||||
.ui-btn-box {
|
||||
padding: 0 px2rem(36);
|
||||
|
||||
.ui-btn,
|
||||
.ui-btn-v2 {
|
||||
width: px2rem(192);
|
||||
height: px2rem(68);
|
||||
border-radius: px2rem(34);
|
||||
margin-top: px2rem(52);
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
border: px2rem(2) solid #d8d8d8;
|
||||
}
|
||||
|
||||
.ui-btn-v2 {
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(34);
|
||||
border: px2rem(2) solid #5ac7a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
1200
src/views/appDir/appEditUserMenu.vue
Normal file
1200
src/views/appDir/appEditUserMenu.vue
Normal file
File diff suppressed because it is too large
Load Diff
334
src/views/appDir/appGroupOrders.vue
Normal file
334
src/views/appDir/appGroupOrders.vue
Normal file
@ -0,0 +1,334 @@
|
||||
<template>
|
||||
<div ref="scrollDistance" class="ui-appGroupOrders" @scroll="handleScroll">
|
||||
<div class="ui-top-box">
|
||||
<div class="ui-search-box">
|
||||
<van-field v-model="searchValue" class="ui-search-input font_28 color3 f-fcc" placeholder="搜索订单" />
|
||||
<img class="ui-search-icon" src="https://image.fulllinkai.com/202304/07/38e32f1948ad951b1f66b404380648f3.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-placeholder"></div>
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="getList">
|
||||
<div v-if="loadingState && list.length == 0">
|
||||
<img class="ui-empty-data-icon" src="https://image.fulllinkai.com/202306/07/247d8ae6b90334457d1b39129cd5c490.png" alt="" />
|
||||
<div class="color6 font_30 text-center">暂无订单</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="ui-list-box">
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-list-item">
|
||||
<div class="f-fbc">
|
||||
<div class="ui-name font_30 color3 ellipsis_1">下单人昵称:{{ item.name }}</div>
|
||||
<div class="ui-btn f-fcc font_26 colorTheme" @click="(showTips = true), (orderId = item.id)">绑定此订单</div>
|
||||
</div>
|
||||
<div class="ui-line-between"></div>
|
||||
<div class="ui-data color6 font_28">
|
||||
联系电话:<span class="color3">{{ item.mobile }}</span>
|
||||
</div>
|
||||
<div class="ui-data color6 font_28">
|
||||
订单金额:<span class="colorPrice">¥{{ item.price }}</span>
|
||||
</div>
|
||||
<div class="ui-data color6 font_28">
|
||||
订单编号:<span class="color3">{{ item.trade_no }}</span>
|
||||
</div>
|
||||
<div class="ui-data color6 font_28">
|
||||
订单描述:<span class="color3">{{ item.desc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<div class="ui-noBind-btn font_32 colorF f-fcc" @click="unbind">暂不绑定订单</div>
|
||||
<img class="ui-add-order" src="https://image.fulllinkai.com/202307/04/c124672b8eae0ecf8ce62a94c30c69df.png" alt="" @click="jumpPath" />
|
||||
<van-popup v-model:show="showTips" round :close-on-click-overlay="false" :lock-scroll="true" :duration="0.5">
|
||||
<div class="ui-tips-box">
|
||||
<div class="color3 font_32 text-center">确定绑定此订单吗?</div>
|
||||
<div class="ui-btn-box f-fbc">
|
||||
<div class="ui-btn font_32 f-fcc color6" @click="showTips = false">取消</div>
|
||||
<div class="ui-btn-v2 font_32 f-fcc colorF" @click="bindOrder">确认</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { watch, ref, onMounted, onActivated } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppGroupOrders' });
|
||||
|
||||
const userStore = useUserStore(); // pinia状态缓存数据
|
||||
const loadingState = ref<any>(false); // 页面数据加载完成判断
|
||||
const throttle = ref(true);
|
||||
const orderId = ref<any>(''); // 选中的订单ID
|
||||
const showTips = ref(false); // 选中绑定订单弹框提示
|
||||
const timer = ref<any>(null);
|
||||
const searchValue = ref<any>('');
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const noMore = ref(false); // 没有更多数据
|
||||
const refreshing = ref(false); // 上拉刷新false表示加载完成
|
||||
const finished = ref(false); // true表示数据全部加载完成
|
||||
const loading = ref(false); // false表示数据加载完成
|
||||
const page = ref(1); // 数据分页
|
||||
|
||||
const scrollValue = ref(0); // 记录页面列表的滚动距离
|
||||
const scrollDistance = ref<any>(null);
|
||||
|
||||
// 暂不绑定订单(后端绑定虚拟订单)
|
||||
const unbind = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
};
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/unbind/work/group/orders`, data, method: 'post' })
|
||||
.then(() => {
|
||||
userStore.weChatBindState = '3';
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'personalCenter',
|
||||
});
|
||||
router.go(-1);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 获取订单列表
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/no/group/orders?page=${page.value}&keyword=${searchValue.value}`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
if (list.value.length === 0 || page.value === 1) {
|
||||
list.value = result.data;
|
||||
} else if (list.value.length >= 15) {
|
||||
result.data.forEach((item) => {
|
||||
list.value.push(item);
|
||||
});
|
||||
}
|
||||
refreshing.value = false;
|
||||
loading.value = false;
|
||||
if (list.value.length < 15 || result.data.length < 15) {
|
||||
finished.value = true;
|
||||
noMore.value = true;
|
||||
}
|
||||
loadingState.value = true;
|
||||
page.value++;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 企业群绑定订单
|
||||
const bindOrder = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
};
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/bind/work/group/orders/${orderId.value}`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showToast('绑定成功');
|
||||
userStore.weChatBindState = '2';
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'personalCenter',
|
||||
});
|
||||
router.go(-1);
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化机构列表
|
||||
const initList = () => {
|
||||
page.value = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 上拉初始化数据
|
||||
const onRefresh = () => {
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
finished.value = false;
|
||||
loading.value = true;
|
||||
getList();
|
||||
};
|
||||
|
||||
const jumpPath = () => {
|
||||
router.push({
|
||||
name: 'appAddOrder',
|
||||
});
|
||||
};
|
||||
|
||||
// 监听搜索值变化重新获取数据
|
||||
watch(searchValue, () => {
|
||||
clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => {
|
||||
initList();
|
||||
}, 800);
|
||||
});
|
||||
|
||||
// 监听页面滚动距离
|
||||
const handleScroll = (event) => {
|
||||
scrollValue.value = event.target.scrollTop;
|
||||
};
|
||||
|
||||
onActivated(() => {
|
||||
scrollDistance.value.scrollTop = scrollValue.value;
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appGroupOrders {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-top-box {
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99;
|
||||
background: #f8f8f8;
|
||||
|
||||
.ui-search-box {
|
||||
max-width: 100%;
|
||||
padding: px2rem(30);
|
||||
height: px2rem(80);
|
||||
background: #f8f8f8;
|
||||
position: relative;
|
||||
|
||||
.ui-search-input {
|
||||
width: 100%;
|
||||
padding: 0 px2rem(70);
|
||||
height: px2rem(80);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(40);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
::v-deep(input::-webkit-input-placeholder) {
|
||||
font-size: px2rem(28);
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.ui-search-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
position: absolute;
|
||||
top: px2rem(56);
|
||||
left: px2rem(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-placeholder {
|
||||
width: 100vw;
|
||||
height: px2rem(140);
|
||||
}
|
||||
|
||||
.ui-empty-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: 25vh auto 0 auto;
|
||||
}
|
||||
|
||||
.ui-list-box {
|
||||
padding-bottom: 16vh;
|
||||
|
||||
.ui-list-item {
|
||||
margin: 0 px2rem(30) px2rem(30) px2rem(30);
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(30);
|
||||
|
||||
.ui-name {
|
||||
max-width: px2rem(300);
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
width: px2rem(164);
|
||||
height: px2rem(60);
|
||||
border-radius: px2rem(30);
|
||||
border: px2rem(2) solid #91dcc2;
|
||||
}
|
||||
|
||||
.ui-line-between {
|
||||
width: 100%;
|
||||
height: px2rem(2);
|
||||
background: #f5f5f5;
|
||||
margin: px2rem(20) 0 px2rem(4) 0;
|
||||
}
|
||||
|
||||
.ui-data {
|
||||
padding-top: px2rem(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tips-box {
|
||||
position: relative;
|
||||
width: px2rem(520);
|
||||
padding: px2rem(92) px2rem(40) px2rem(50) px2rem(40);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-btn-box {
|
||||
padding: 0 px2rem(36);
|
||||
|
||||
.ui-btn,
|
||||
.ui-btn-v2 {
|
||||
width: px2rem(192);
|
||||
height: px2rem(68);
|
||||
border-radius: px2rem(34);
|
||||
margin-top: px2rem(80);
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
border: px2rem(2) solid #d8d8d8;
|
||||
}
|
||||
|
||||
.ui-btn-v2 {
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(34);
|
||||
border: px2rem(2) solid #5ac7a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-noBind-btn {
|
||||
position: fixed;
|
||||
bottom: 8vh;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: px2rem(480);
|
||||
height: px2rem(76);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
}
|
||||
|
||||
.ui-add-order {
|
||||
position: fixed;
|
||||
bottom: 16vh;
|
||||
right: px2rem(10);
|
||||
width: px2rem(140);
|
||||
height: px2rem(140);
|
||||
}
|
||||
</style>
|
||||
239
src/views/appDir/appMeasurementRecord.vue
Normal file
239
src/views/appDir/appMeasurementRecord.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div ref="scrollDistance" class="ui-appMeasurementRecord" @scroll="handleScroll">
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="getList">
|
||||
<div v-if="!refreshing && list.length == 0">
|
||||
<div v-if="loadingState" class="text-center">
|
||||
<img class="ui-no-data-icon" src="https://image.fulllinkai.com/202212/01/f06371b76ab686752486c8b99fa7669a.png" alt="" />
|
||||
<div class="color6 font_30">还没有测量数据哦~</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-record-list">
|
||||
<div class="font_28 color6 ui-record-date">{{ item.yearMonth }}</div>
|
||||
<div v-for="(itemSub, indexSub) in item.subList" :key="indexSub" style="background: #ffffff" @click="jumpPath('appMeasuringRecordDetail', itemSub)">
|
||||
<div class="ui-record-item">
|
||||
<div class="ui-content-box {{itemSub.isTouchMove ? 'shopCon-active' : ''}}">
|
||||
<div class="f-fbc ui-content">
|
||||
<div>
|
||||
<div class="color3 font_30">{{ itemSub.score }}{{ itemSub.unit }}</div>
|
||||
<div class="color6 font_24 ui-pt-6">体脂秤类型:{{ itemSub.type == 1 ? '新版秤' : '旧版秤' }}</div>
|
||||
</div>
|
||||
<div class="f-fcr">
|
||||
<div class="font_30 color9">{{ itemSub.dayHour }}</div>
|
||||
<img class="ui-record-item-icon" src="https://image.fulllinkai.com/202211/22/8cf3979193db3796fe4ca3b3b9be00f7.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="indexSub + 1 != item.subList.length" class="ui-record-item-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onActivated, onMounted, ref } from 'vue';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppMeasurementRecord' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const loadingState = ref(false);
|
||||
const cacheList = ref<any[]>([]); // 数据存储
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const noMore = ref(false); // 没有更多数据
|
||||
const refreshing = ref(true); // 上拉刷新false表示加载完成
|
||||
const finished = ref(false); // true表示数据全部加载完成
|
||||
const loading = ref(false); // false表示数据加载完成
|
||||
const page = ref(1); // 数据分页
|
||||
|
||||
const scrollValue = ref(0); // 记录页面列表的滚动距离
|
||||
const scrollDistance = ref<any>(null);
|
||||
|
||||
// 获取备注列表
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/get/fat/logs?page=${page.value}&chat_id=${userStore.chatId}`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
if (cacheList.value.length === 0 || page.value === 1) {
|
||||
cacheList.value = result.data;
|
||||
setTimeout(() => {
|
||||
list.value = transition(cacheList.value);
|
||||
}, 100);
|
||||
} else if (cacheList.value.length >= 15) {
|
||||
result.data.forEach((item) => {
|
||||
cacheList.value.push(item);
|
||||
});
|
||||
setTimeout(() => {
|
||||
list.value = transition(cacheList.value);
|
||||
}, 100);
|
||||
}
|
||||
refreshing.value = false;
|
||||
loading.value = false;
|
||||
if (cacheList.value.length < 15 || result.data.length < 15) {
|
||||
finished.value = true;
|
||||
noMore.value = true;
|
||||
}
|
||||
page.value++;
|
||||
setTimeout(() => {
|
||||
loadingState.value = true;
|
||||
}, 200);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 上拉初始化数据
|
||||
const onRefresh = () => {
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
finished.value = false;
|
||||
loading.value = true;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 根据不同的年月放置不同的数组
|
||||
const transition = (list) => {
|
||||
let Arr = [] as any;
|
||||
list.forEach((e) => {
|
||||
let index = -1;
|
||||
let state = Arr.some((item, i) => {
|
||||
if (e.yearMonth == item.yearMonth) {
|
||||
index = i;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
if (!state) {
|
||||
Arr.push({ yearMonth: e.yearMonth, subList: [{ dayHour: e.dayHour, e_name: e.fat_name || e.e_name, id: e.id, isTouchMove: false, score: e.fat_data || e.score, created_at: e.created_at, unit: e.unit, yearMonth: e.yearMonth, type: e.type }] });
|
||||
} else {
|
||||
Arr[index].subList.push({ dayHour: e.dayHour, e_name: e.fat_name || e.e_name, id: e.id, isTouchMove: false, score: e.fat_data || e.score, created_at: e.created_at, unit: e.unit, yearMonth: e.yearMonth, type: e.type });
|
||||
}
|
||||
});
|
||||
return Arr;
|
||||
};
|
||||
|
||||
const jumpPath = (url, e) => {
|
||||
router.push({
|
||||
name: url,
|
||||
query: { tested_at: e.created_at, type: e.type, id: e.id },
|
||||
});
|
||||
};
|
||||
|
||||
// 监听页面滚动距离
|
||||
const handleScroll = (event) => {
|
||||
scrollValue.value = event.target.scrollTop;
|
||||
};
|
||||
|
||||
onActivated(() => {
|
||||
scrollDistance.value.scrollTop = scrollValue.value;
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appMeasurementRecord {
|
||||
background: #f6f7f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-tabBar-box {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 8;
|
||||
width: 100vw;
|
||||
height: px2rem(90);
|
||||
background: #f8f8f8;
|
||||
|
||||
.ui-tabBar-list {
|
||||
padding: px2rem(24) px2rem(180) px2rem(0) px2rem(180);
|
||||
}
|
||||
|
||||
.ui-tabBar-line {
|
||||
position: absolute;
|
||||
bottom: px2rem(2);
|
||||
width: 100%;
|
||||
height: px2rem(10);
|
||||
background: linear-gradient(90deg, rgba(95, 226, 175, 0.1) 0%, #41d5a9 100%);
|
||||
border-radius: px2rem(5);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-placeholder {
|
||||
width: 100vw;
|
||||
height: px2rem(70);
|
||||
}
|
||||
|
||||
.ui-no-data-icon {
|
||||
width: px2rem(500);
|
||||
height: px2rem(360);
|
||||
border-radius: 50%;
|
||||
margin-top: 20vh;
|
||||
}
|
||||
|
||||
.ui-record-list {
|
||||
.ui-record-date {
|
||||
padding: px2rem(20) px2rem(30);
|
||||
}
|
||||
|
||||
.ui-record-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
.ui-content-box {
|
||||
width: 100%;
|
||||
padding: px2rem(24) 0 px2rem(24) px2rem(30);
|
||||
margin-right: 0;
|
||||
transition: all 0.4s;
|
||||
display: flex;
|
||||
position: relative;
|
||||
left: 0;
|
||||
|
||||
.ui-content {
|
||||
width: 100%;
|
||||
|
||||
.ui-record-item-icon {
|
||||
width: px2rem(16);
|
||||
display: block;
|
||||
margin: 0 px2rem(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-del {
|
||||
background-color: #f1013d;
|
||||
width: px2rem(180);
|
||||
height: 100%;
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
z-index: 222;
|
||||
right: px2rem(-180);
|
||||
top: 0;
|
||||
transition: all 0.4s;
|
||||
}
|
||||
}
|
||||
|
||||
.shopCon-active {
|
||||
left: px2rem(-180);
|
||||
}
|
||||
|
||||
.shopDel-active {
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
.ui-record-item-line {
|
||||
width: 100%;
|
||||
height: px2rem(2);
|
||||
background: #f7f7f7;
|
||||
margin-left: px2rem(30);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
300
src/views/appDir/appMeasuringRecordDetail.vue
Normal file
300
src/views/appDir/appMeasuringRecordDetail.vue
Normal file
@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="ui-appMeasuringRecordDetail">
|
||||
<div class="f-fbc ui-date">
|
||||
<div class="color6 font_30">{{ showTime }}</div>
|
||||
<div class="colorTheme font_26">体脂秤类型:{{ type == 1 ? '新版秤' : '旧版秤' }}</div>
|
||||
</div>
|
||||
<div v-if="type == 1">
|
||||
<div class="ui-indicators-box">
|
||||
<div v-for="(item, index) in list" :key="index">
|
||||
<div @click="switchIndicators(index)">
|
||||
<div class="ui-indicators-item f-fbc">
|
||||
<div class="f-fcl">
|
||||
<img class="ui-indicators-icon" :src="item.icon" alt="" />
|
||||
<div class="color333 font_28 ui-indicators-name">{{ item.fat_name }}</div>
|
||||
<div v-if="item.fat_name != '体型'" class="font_32 color333 ui-indicators-value text-center bold"
|
||||
>{{ item.fat_data }}<span class="font_20">{{ item.unit }}</span></div
|
||||
>
|
||||
</div>
|
||||
<div class="f-fcr ui-indicators-r-icon">
|
||||
<div v-if="item.level" class="ui-indicators-item-icon font_20 colorF f-fcc" :style="{ background: item.level.color }">{{ item.level.name }}</div>
|
||||
<img class="ui-triangle-icon" :class="indicatorsIndex == index ? 'ui-triangle-active' : ''" src="https://image.fulllinkai.com/202211/28/6c196ecbe7899a15320fd6c3ab48b64b.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.control" class="font_26 color999 ui-indicators-sub-name">{{ item.control }}</div>
|
||||
<div v-if="indicatorsIndex == index" class="ui-indicators-view color666 font_24">
|
||||
<div v-if="item.boundaries.length != 0 && item.levels.length != 0" class="f-fcl ui-relative">
|
||||
<div v-for="(itemV2, indexV2) in item.levels" :key="indexV2" class="ui-indicators-data-box text-center" :style="{ width: 100 / item.levels.length + '%' }">
|
||||
<div v-if="item.boundaries.length != 0">
|
||||
<div v-if="indexV2 == 0" class="color333 font_20"> </div>
|
||||
<div v-else class="ui-indicators-num color333 font_20">{{ item.boundaries[indexV2 - 1] }}</div>
|
||||
</div>
|
||||
<div class="ui-indicators-color" :class="[indexV2 == 0 ? 'ui-indicators-radius' : '', indexV2 + 1 == item.levels.length ? 'ui-indicators-radiusV2' : '']" :style="{ background: itemV2.color }"></div>
|
||||
<div class="color666 font_22 ui-indicators-text" :class="item.level.name == itemV2.name ? 'bold' : ''" :style="{ color: item.level.name == itemV2.name ? item.level.color : '' }">{{ itemV2.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.level" class="ui-indicators-desc color999 font_24">{{ item.level.desc || '' }}</div>
|
||||
</div>
|
||||
<div class="ui-indicators-line" :class="indicatorsIndex == index ? 'ui-indicators-active-line' : ''"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ui-indicators-box">
|
||||
<div v-for="(item, index) in list" :key="index">
|
||||
<div class="ui-indicators-item f-fbc">
|
||||
<div class="f-fcl">
|
||||
<img class="ui-indicators-icon" :src="item.icon" alt="" />
|
||||
<div class="color3 font_28 ui-indicators-name">{{ item.fat_name }}</div>
|
||||
<div class="font_32 color3 ui-indicators-value text-center bold">
|
||||
{{ item.fat_data }}
|
||||
<span class="font_20">{{ item.unit || '' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="f-fcr ui-indicators-r-icon">
|
||||
<div v-if="item.fat_state" class="ui-indicators-item-icon font_20 colorF f-fcc" :style="{ background: item.color }">{{ item.fat_state }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.control" class="font_26 color9 ui-indicators-sub-name">{{ item.control }}</div>
|
||||
<div v-if="index + 1 != list.length" class="ui-indicators-line"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import router from '@/router';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppMeasuringRecordDetail' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const type = ref<any>('');
|
||||
const time = ref<any>('');
|
||||
const id = ref<any>('');
|
||||
const indicatorsIndex = ref<any>(null);
|
||||
const showTime = ref<any>('');
|
||||
const list = ref<any[]>([]);
|
||||
|
||||
// 获取数据
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/get/fat/log/detail?chat_id=${userStore.chatId}&id=${id.value}`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
list.value = result;
|
||||
if (type.value == 0) {
|
||||
if (list.value && list.value.length > 0) {
|
||||
list.value.forEach((item) => {
|
||||
if (item.fat_name === '体重' || item.fat_name === 'BMI' || item.fat_name === '脂肪占比' || item.fat_name === '脂肪控制' || item.fat_name === '体脂重量' || item.fat_name === '肥胖度') {
|
||||
if (item.fat_state === '偏瘦') {
|
||||
item.color = '#00C1E4';
|
||||
} else if (item.fat_state === '标准') {
|
||||
item.color = '#A7CB40';
|
||||
} else if (item.fat_state === '偏胖') {
|
||||
item.color = '#FBC13D';
|
||||
} else {
|
||||
item.color = '#F74142';
|
||||
}
|
||||
} else if (item.fat_name === '体脂率' || item.fat_name === '蛋白质' || item.fat_name === '蛋白质率') {
|
||||
if (item.fat_state === '偏低') {
|
||||
item.color = '#00C1E4';
|
||||
} else if (item.fat_state === '标准') {
|
||||
item.color = '#A7CB40';
|
||||
} else if (item.fat_state === '偏高') {
|
||||
item.color = '#FBC13D';
|
||||
} else {
|
||||
item.color = '#F74142';
|
||||
}
|
||||
} else if (item.fat_name === '去脂体重') {
|
||||
if (item.fat_state === '偏弱') {
|
||||
item.color = '#00C1E4';
|
||||
} else {
|
||||
item.color = '#A7CB40';
|
||||
}
|
||||
} else if (item.fat_name === '肌肉占比' || item.fat_name === '肌肉重量' || item.fat_name === '体内水分' || item.fat_name === '水含量' || item.fat_name === '骨量') {
|
||||
if (item.fat_state === '不足') {
|
||||
item.color = '#00C1E4';
|
||||
} else if (item.fat_state === '标准') {
|
||||
item.color = '#A7CB40';
|
||||
} else {
|
||||
item.color = '#A7CB40';
|
||||
}
|
||||
} else if (item.fat_name === '基础代谢') {
|
||||
if (item.fat_state === '偏低') {
|
||||
item.color = '#00C1E4';
|
||||
} else {
|
||||
item.color = '#A7CB40';
|
||||
}
|
||||
} else if (item.fat_name === '内脏脂肪') {
|
||||
if (item.fat_state === '标准') {
|
||||
item.color = '#A7CB40';
|
||||
} else if (item.fat_state === '警惕') {
|
||||
item.color = '#FBC13D';
|
||||
} else {
|
||||
item.color = '#F74142';
|
||||
}
|
||||
} else if (item.fat_name === '身体年龄') {
|
||||
if (item.fat_state === '偏大') {
|
||||
item.color = '#FBC13D';
|
||||
} else if (item.fat_state === '标准') {
|
||||
item.color = '#A7CB40';
|
||||
} else {
|
||||
item.color = '#A7CB40';
|
||||
}
|
||||
} else {
|
||||
item.color = '#A7CB40';
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const switchIndicators = (index) => {
|
||||
console.log(index);
|
||||
if (indicatorsIndex.value === index) {
|
||||
indicatorsIndex.value = null;
|
||||
return;
|
||||
}
|
||||
indicatorsIndex.value = index;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// userStore.chatId = 1;
|
||||
let route = router.currentRoute.value.query;
|
||||
type.value = route.type;
|
||||
id.value = route.id;
|
||||
if (route.tested_at) {
|
||||
time.value = route.tested_at;
|
||||
showTime.value = `${time.value.split(/[- ' ' :]/)[0]}年${time.value.split(/[- ' ' :]/)[1]}月${time.value.split(/[- ' ' :]/)[2]}日` + ` ${time.value.split(/[- ' ' :]/)[3]}:${time.value.split(/[- ' ' :]/)[4]}`;
|
||||
getList();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appMeasuringRecordDetail {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-date {
|
||||
background: #f4f4f4;
|
||||
padding: px2rem(20) px2rem(30);
|
||||
}
|
||||
|
||||
.ui-indicators-box {
|
||||
position: relative;
|
||||
padding-bottom: px2rem(140);
|
||||
|
||||
.ui-indicators-item {
|
||||
padding: px2rem(20) px2rem(30);
|
||||
|
||||
.ui-indicators-r-icon {
|
||||
//margin-bottom: px2rem(-10);
|
||||
|
||||
.ui-indicators-item-icon {
|
||||
padding: 0 px2rem(18);
|
||||
height: px2rem(36);
|
||||
border-radius: px2rem(6);
|
||||
margin-right: px2rem(16);
|
||||
}
|
||||
|
||||
.ui-triangle-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(16);
|
||||
display: block;
|
||||
transition: all 0.4s;
|
||||
}
|
||||
|
||||
.ui-triangle-active {
|
||||
transform: rotate(-180deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-indicators-name {
|
||||
width: px2rem(270);
|
||||
margin-left: px2rem(20);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ui-indicators-value {
|
||||
width: px2rem(140);
|
||||
}
|
||||
|
||||
.ui-indicators-icon {
|
||||
width: px2rem(44);
|
||||
height: px2rem(44);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-indicators-view {
|
||||
//width: 100%;
|
||||
padding: px2rem(10) px2rem(18) px2rem(20) px2rem(18);
|
||||
position: relative;
|
||||
|
||||
.ui-indicators-data-box {
|
||||
margin: px2rem(10) 0 px2rem(16) 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ui-indicators-color {
|
||||
width: 100%;
|
||||
height: px2rem(10);
|
||||
margin-bottom: px2rem(4);
|
||||
}
|
||||
|
||||
.ui-indicators-text {
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
.ui-indicators-radius {
|
||||
border-radius: px2rem(20) 0 0 px2rem(20);
|
||||
}
|
||||
|
||||
.ui-indicators-radiusV2 {
|
||||
border-radius: 0 px2rem(20) px2rem(20) 0;
|
||||
}
|
||||
|
||||
.ui-indicators-num {
|
||||
position: relative;
|
||||
right: 50%;
|
||||
}
|
||||
|
||||
.ui-indicators-desc {
|
||||
word-break: break-all;
|
||||
padding: 0 px2rem(30);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-indicators-sub-name {
|
||||
margin-left: px2rem(94);
|
||||
margin-bottom: px2rem(16);
|
||||
}
|
||||
|
||||
.ui-indicators-line,
|
||||
.ui-indicators-active-line {
|
||||
height: px2rem(2);
|
||||
margin: 0 px2rem(8) px2rem(6) px2rem(94);
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.ui-indicators-active-line {
|
||||
margin: 0 px2rem(20) px2rem(6) px2rem(20);
|
||||
}
|
||||
|
||||
.ui-view-depth-report {
|
||||
width: px2rem(600);
|
||||
height: px2rem(82);
|
||||
border-radius: px2rem(50);
|
||||
margin: px2rem(60) auto 0 auto;
|
||||
background: #5ac7a0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
293
src/views/appDir/appQuestionnaire.vue
Normal file
293
src/views/appDir/appQuestionnaire.vue
Normal file
@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<div class="ui-appQuestionnaire">
|
||||
<img class="ui-top-bg" src="https://images.health.ufutx.com/202412/12/4148e5295233d207e97ef6bfa7c92f6f.jpg" alt="" />
|
||||
<div class="ui-data-fill-in-box">
|
||||
<div v-for="(item, index) in list" :key="index" :class="index + 1 != list.length ? 'ui-data-fill-in-item' : ''">
|
||||
<div class="font_30 color3 bold">{{ index + 1 }}.{{ item.title }}</div>
|
||||
<div v-if="item.choice == 0">
|
||||
<van-radio-group v-model="answerList[index].reply[0]">
|
||||
<van-radio v-for="(itemV2, indexV2) in item.answer" :key="indexV2" :name="itemV2" class="ui-relative ui-radio-box" :disabled="readonly" @click="changeRadio(index)">
|
||||
<div class="font_28 color6">{{ itemV2 }}</div>
|
||||
<template #icon="props">
|
||||
<img class="img-icon" :src="props.checked ? activeIcon : inactiveIcon" />
|
||||
</template>
|
||||
</van-radio>
|
||||
<div v-if="item.other_answer_title">
|
||||
<van-radio :name="item.other_answer_title" class="ui-relative ui-radio-box" :disabled="readonly">
|
||||
<div class="font_28 color6">{{ item.other_answer_title }}</div>
|
||||
<template #icon="props">
|
||||
<img class="img-icon" :src="props.checked ? activeIcon : inactiveIcon" />
|
||||
</template>
|
||||
</van-radio>
|
||||
</div>
|
||||
</van-radio-group>
|
||||
<div v-if="item.other_answer_title && answerList[index].reply[0] == item.other_answer_title">
|
||||
<van-field v-model="answerList[index].other_answer_text" :readonly="readonly" class="ui-data-input font_28 color3" rows="5" type="textarea" :placeholder="`请填写${item.other_answer_title}内容`" autosize />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.choice == 1">
|
||||
<van-checkbox-group v-model="answerList[index].reply" @change="changeCheckBox(index, item.other_answer_title)">
|
||||
<van-checkbox v-for="(itemV2, indexV2) in item.answer" :key="indexV2" :name="itemV2" :disabled="readonly" class="ui-radio-box ui-relative">
|
||||
<div class="font_28 color6">{{ itemV2 }}</div>
|
||||
<template #icon="props">
|
||||
<img class="img-icon" :src="props.checked ? activeIcon : inactiveIcon" />
|
||||
</template>
|
||||
</van-checkbox>
|
||||
<div v-if="item.other_answer_title">
|
||||
<van-checkbox :name="item.other_answer_title" :disabled="readonly" class="ui-radio-box ui-relative">
|
||||
<div class="font_28 color6">{{ item.other_answer_title }}</div>
|
||||
<template #icon="props">
|
||||
<img class="img-icon" :src="props.checked ? activeIcon : inactiveIcon" />
|
||||
</template>
|
||||
</van-checkbox>
|
||||
</div>
|
||||
</van-checkbox-group>
|
||||
<div v-if="answerList[index].reply.includes(item.other_answer_title)">
|
||||
<van-field v-model="answerList[index].other_answer_text" :readonly="readonly" class="ui-data-input font_28 color3" rows="5" type="textarea" :placeholder="`请填写${item.other_answer_title}内容`" autosize />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.choice == 2">
|
||||
<van-field v-model="answerList[index].reply[0]" :readonly="readonly" class="ui-data-input font_28 color3" rows="5" type="textarea" :placeholder="`请填写${item.title}`" autosize />
|
||||
</div>
|
||||
<div v-if="item.choice == 3" style="line-height: 0">
|
||||
<van-rate v-model="answerList[index].reply[0]" :readonly="readonly" class="ui-pt-20" gutter="10" :size="21" color="#ffc629" void-icon="star" void-color="#e8e8e8" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!readonly" class="ui-btn f-fcc font_32 colorF" @click="submit">提交</div>
|
||||
</div>
|
||||
<van-popup v-model:show="showTips" round :close-on-click-overlay="false" :lock-scroll="true" :duration="0.5">
|
||||
<div class="ui-tips-box">
|
||||
<div class="color3 font_32 text-center">感谢您的填写</div>
|
||||
<div class="ui-tips-btn-box f-fbc">
|
||||
<div class="ui-tips-btn font_32 f-fcc color6" @click="showTips = false">取消</div>
|
||||
<div class="ui-tips-btn-v2 font_32 f-fcc colorTheme" @click="close">返回聊天</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<img class="ui-close-icon" src="https://image.fulllinkai.com/202306/21/3cbfe438ad3792436d26443920878ff4.png" alt="" @click="close" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
|
||||
defineOptions({ name: 'AppQuestionnaire' });
|
||||
|
||||
const chatId = ref<any>('');
|
||||
const loading = ref(false);
|
||||
const surveyId = ref<any>('');
|
||||
const role = ref<any>('');
|
||||
const isIOS = ref<any>(true);
|
||||
const readonly = ref<any>(false);
|
||||
const showTips = ref<any>(false);
|
||||
const throttle = ref(true);
|
||||
const list = ref<any[]>([]);
|
||||
const answerList = ref<any[]>([]);
|
||||
const activeIcon = ref('https://image.fulllinkai.com/202306/07/c270666b969b9e844496f05b43e3a29d.png');
|
||||
const inactiveIcon = ref('https://image.fulllinkai.com/202306/07/3cb0e29392208a1a28363ff25ca14169.png');
|
||||
|
||||
// 获取备注数据
|
||||
const getDetail = () => {
|
||||
weChat({ url: `h5/order/surveys/${surveyId.value}?chat_id=${chatId.value}&is_im=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
list.value = result.content;
|
||||
if (list.value && list.value[0].reply) {
|
||||
readonly.value = true;
|
||||
}
|
||||
if (role.value != 0) {
|
||||
showToast('该问卷暂不支持工作人员填写');
|
||||
readonly.value = true;
|
||||
}
|
||||
for (let i = 0; i < list.value.length; i++) {
|
||||
answerList.value.push({
|
||||
title: list.value[i].title,
|
||||
choice: list.value[i].choice,
|
||||
answer: list.value[i].answer,
|
||||
other_answer_title: list.value[i].other_answer_title || '',
|
||||
other_answer_text: list.value[i].other_answer_text || '',
|
||||
reply: list.value[i].reply ? list.value[i].reply : [],
|
||||
});
|
||||
}
|
||||
if (list.value && list.value.length > 0) {
|
||||
setTimeout(() => {
|
||||
loading.value = true;
|
||||
});
|
||||
}
|
||||
console.log(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
let state = false;
|
||||
let data = {
|
||||
chat_id: chatId.value,
|
||||
content: answerList.value,
|
||||
is_im: 1,
|
||||
};
|
||||
if (!loading.value) {
|
||||
return;
|
||||
}
|
||||
answerList.value.forEach((item) => {
|
||||
if (!item.reply || !item.reply[0]) {
|
||||
state = true;
|
||||
}
|
||||
// 选择了其他选项又没有填写内容
|
||||
if (item.other_answer_title && !item.other_answer_text && item.reply.includes(item.other_answer_title)) {
|
||||
state = true;
|
||||
console.log(item.other_answer_title, !item.reply.includes(item.other_answer_title), '77');
|
||||
}
|
||||
});
|
||||
if (state) {
|
||||
showToast('请完善调查问卷后提交');
|
||||
return;
|
||||
}
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
}
|
||||
weChat({ url: `h5/order/survey/${surveyId.value}`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showTips.value = true;
|
||||
readonly.value = true;
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 单选监听,不是选中其他的清空输入框内容
|
||||
const changeRadio = (index) => {
|
||||
answerList.value[index].other_answer_text = '';
|
||||
};
|
||||
|
||||
// 多选监听,其他的选项未选中,清空输入框内容
|
||||
const changeCheckBox = (index, e) => {
|
||||
if (!answerList.value[index].reply.includes(e)) {
|
||||
answerList.value[index].other_answer_text = '';
|
||||
}
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
// 调用ios app方法
|
||||
if (isIOS.value) {
|
||||
window.webkit.messageHandlers.goBack.postMessage(null);
|
||||
} else {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.goBack();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
isIOS.value = false;
|
||||
} else {
|
||||
isIOS.value = true;
|
||||
}
|
||||
let route = router.currentRoute.value.query;
|
||||
if (route.chat_id) {
|
||||
chatId.value = route.chat_id;
|
||||
}
|
||||
role.value = route.role;
|
||||
if (route.survey_id) {
|
||||
surveyId.value = route.survey_id;
|
||||
getDetail();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appQuestionnaire {
|
||||
background: #005cf9;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ui-top-bg {
|
||||
width: 100vw;
|
||||
height: px2rem(1176);
|
||||
}
|
||||
|
||||
.ui-data-fill-in-box {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: px2rem(-766) px2rem(30) px2rem(120) px2rem(30);
|
||||
padding: px2rem(40) px2rem(30) px2rem(50) px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-data-fill-in-item {
|
||||
padding-bottom: px2rem(80);
|
||||
}
|
||||
|
||||
.ui-data-input {
|
||||
margin-top: px2rem(20);
|
||||
padding: px2rem(16);
|
||||
background: #f6f9ff;
|
||||
border-radius: px2rem(16);
|
||||
}
|
||||
|
||||
.ui-radio-box {
|
||||
padding-top: px2rem(20);
|
||||
|
||||
.img-icon {
|
||||
position: absolute;
|
||||
top: px2rem(26);
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
}
|
||||
|
||||
::v-deep(.van-radio__label),
|
||||
::v-deep(.van-checkbox__label) {
|
||||
margin-left: px2rem(40);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
width: px2rem(292);
|
||||
height: px2rem(76);
|
||||
background: linear-gradient(90deg, #7bafff 0%, #556cfd 100%);
|
||||
border-radius: px2rem(38);
|
||||
margin: px2rem(80) auto 0 auto;
|
||||
}
|
||||
|
||||
.ui-close-icon {
|
||||
width: px2rem(160);
|
||||
height: px2rem(160);
|
||||
position: fixed;
|
||||
right: px2rem(-10);
|
||||
bottom: 10vh;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.ui-tips-box {
|
||||
position: relative;
|
||||
width: px2rem(600);
|
||||
padding-top: px2rem(62);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-tips-btn-box {
|
||||
.ui-tips-btn,
|
||||
.ui-tips-btn-v2 {
|
||||
border-top: px2rem(2) solid #f5f5f5;
|
||||
width: 49.9%;
|
||||
height: px2rem(90);
|
||||
margin-top: px2rem(80);
|
||||
}
|
||||
|
||||
.ui-tips-btn-v2 {
|
||||
border-left: px2rem(2) solid #f5f5f5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
279
src/views/appDir/appRemarkDetail.vue
Normal file
279
src/views/appDir/appRemarkDetail.vue
Normal file
@ -0,0 +1,279 @@
|
||||
<template>
|
||||
<div class="ui-appRemarkDetail">
|
||||
<div class="ui-remark-input-box">
|
||||
<div class="ui-pb-28">
|
||||
<div class="font_30 color3 bold ui-pb-20">备注类型</div>
|
||||
<van-field v-model="selectType" readonly class="ui-user-input f-fcc font_28 color3 bold" placeholder="请选择备注类型" @click="showType = true" />
|
||||
</div>
|
||||
<div class="font_30 color3 bold">{{ commentId ? '编辑' : '填写' }}备注</div>
|
||||
<div class="ui-upload-box">
|
||||
<div v-for="(item, index) in pics" :key="index" class="ui-upload-icon-box flo_l">
|
||||
<img class="ui-upload-icon" :src="item" mode="aspectFill" alt="" @click="ImagePreview(pics, index)" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" mode="widthFix" alt="" @click="clearPic(index)" />
|
||||
</div>
|
||||
<div v-if="pics.length < 9">
|
||||
<uploadPicture :multiple="true" :max-count="9" @on-success="onSuccess" @click="takePhone">
|
||||
<div class="ui-upload">
|
||||
<img class="ui-upload-icon flo_l" src="https://image.fulllinkai.com/202301/07/6c9bc853bed42c9871f56156d1b8f31c.png" alt="" />
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-remark-data">
|
||||
<van-field v-model="remark" class="ui-remark-input font_30 color3" rows="8" type="textarea" maxlength="500" show-word-limit placeholder="请您填写备注内容,记录每一位用户" autosize />
|
||||
</div>
|
||||
<div class="ui-btn font_32 colorF f-fcc" @click="save">保存</div>
|
||||
</div>
|
||||
<van-popup v-model:show="showType" round position="bottom" :duration="0.5">
|
||||
<van-picker v-model="TypeValues" title="选择备注类型" :columns="columns" @confirm="onConfirm" @cancel="showType = false" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { showImagePreview, showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
|
||||
defineOptions({ name: 'AppRemarkDetail' });
|
||||
|
||||
const throttle = ref(true);
|
||||
const pics = ref<any[]>([]);
|
||||
const chatId = ref<any>('');
|
||||
const commentId = ref<any>('');
|
||||
const remark = ref<any>('');
|
||||
|
||||
const showType = ref(false);
|
||||
const selectType = ref<any>('');
|
||||
const selectTypeId = ref<any>('');
|
||||
const columns = ref<any[]>([]); // 类型选择框数据
|
||||
const TypeValues = ref<any[]>([]); // 类型选择框默认值
|
||||
|
||||
// 获取备注数据
|
||||
const getDetail = () => {
|
||||
weChat({ url: `h5/order/comments/${commentId.value}?is_im=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
remark.value = result.comment;
|
||||
pics.value = result.images || [];
|
||||
if (result.type_id) {
|
||||
TypeValues.value = [result.type_id * 1];
|
||||
selectType.value = result.type_name;
|
||||
selectTypeId.value = result.type_id;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取备注类型
|
||||
const getType = () => {
|
||||
weChat({ url: `h5/get/comment/type?is_im=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
if (result && result.length > 0) {
|
||||
columns.value = [];
|
||||
result.forEach((item) => {
|
||||
columns.value.push({
|
||||
text: item.name,
|
||||
value: item.id,
|
||||
});
|
||||
});
|
||||
}
|
||||
if (commentId.value) {
|
||||
getDetail();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const save = () => {
|
||||
if (throttle.value) {
|
||||
let data = {
|
||||
images: pics.value,
|
||||
comment: remark.value,
|
||||
chat_id: chatId.value,
|
||||
type_id: selectTypeId.value,
|
||||
is_im: 1,
|
||||
};
|
||||
if (!selectTypeId.value) {
|
||||
showToast('请选择备注类型');
|
||||
return;
|
||||
}
|
||||
if (!remark.value) {
|
||||
showToast('请填写备注内容');
|
||||
return;
|
||||
}
|
||||
throttle.value = false;
|
||||
if (commentId.value) {
|
||||
weChat({ url: `h5/order/comments/${commentId.value}`, data, method: 'put' })
|
||||
.then(() => {
|
||||
showToast('编辑成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'appRemarks',
|
||||
query: { state: 1 },
|
||||
});
|
||||
router.go(-1);
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
} else {
|
||||
weChat({ url: `h5/comment/orders`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showToast('保存成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'appRemarks',
|
||||
query: { state: 1 },
|
||||
});
|
||||
router.go(-1);
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 上传报告
|
||||
const onSuccess = (val) => {
|
||||
if (pics.value.length < 9) {
|
||||
pics.value.push(val);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除某个已上传的图片
|
||||
const clearPic = (index) => {
|
||||
pics.value.splice(index, 1);
|
||||
};
|
||||
|
||||
window.getAndroidPhone = (type, e) => {
|
||||
if (e && e.length > 0) {
|
||||
e.forEach((item) => {
|
||||
if (pics.value && pics.value.length < 9) {
|
||||
pics.value.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const takePhone = () => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.takePhoto('multiple', 9);
|
||||
}
|
||||
};
|
||||
|
||||
const ImagePreview = (e, index) => {
|
||||
showImagePreview({
|
||||
images: e,
|
||||
showIndex: false,
|
||||
startPosition: index,
|
||||
loop: false,
|
||||
});
|
||||
};
|
||||
|
||||
// 选择备注类型态
|
||||
const onConfirm = ({ selectedOptions }) => {
|
||||
showType.value = false;
|
||||
selectType.value = `${selectedOptions[0].text}`;
|
||||
selectTypeId.value = `${selectedOptions[0].value}`;
|
||||
console.log(selectType.value);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
if (route.chatId) {
|
||||
chatId.value = route.chatId;
|
||||
}
|
||||
if (route.comment_id) {
|
||||
commentId.value = route.comment_id;
|
||||
document.title = '编辑备注';
|
||||
} else {
|
||||
document.title = '填写备注';
|
||||
}
|
||||
getType();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appRemarkDetail {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-remark-input-box {
|
||||
padding: px2rem(30);
|
||||
|
||||
.ui-user-input {
|
||||
box-sizing: border-box;
|
||||
border: px2rem(2) solid #f5f5f5;
|
||||
border-radius: px2rem(16);
|
||||
padding: px2rem(16) px2rem(30);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-box {
|
||||
overflow: hidden;
|
||||
border-radius: px2rem(16);
|
||||
|
||||
.ui-upload-icon-box {
|
||||
position: relative;
|
||||
|
||||
.ui-upload-clear-icon {
|
||||
position: absolute;
|
||||
right: px2rem(30);
|
||||
top: px2rem(30);
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-icon {
|
||||
width: px2rem(200);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
border-radius: px2rem(16);
|
||||
margin-right: px2rem(20);
|
||||
margin-top: px2rem(20);
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-remark-data {
|
||||
background: #ffffff;
|
||||
margin-top: px2rem(20);
|
||||
border-radius: px2rem(26);
|
||||
|
||||
.ui-remark-input {
|
||||
padding: px2rem(20) px2rem(24);
|
||||
border-radius: px2rem(30);
|
||||
line-height: px2rem(40);
|
||||
}
|
||||
|
||||
textarea::-webkit-input-placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
width: px2rem(480);
|
||||
height: px2rem(76);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
margin: px2rem(150) auto;
|
||||
}
|
||||
</style>
|
||||
191
src/views/appDir/appRemarks.vue
Normal file
191
src/views/appDir/appRemarks.vue
Normal file
@ -0,0 +1,191 @@
|
||||
<template>
|
||||
<div ref="scrollDistance" class="ui-appRemarks" @scroll="handleScroll">
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="getList">
|
||||
<div v-if="!refreshing && list.length == 0">
|
||||
<img class="ui-empty-data-icon" src="https://image.fulllinkai.com/202306/07/247d8ae6b90334457d1b39129cd5c490.png" alt="" />
|
||||
<div class="color6 font_30 text-center">暂无备注</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="ui-remarks-box">
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-remarks-item">
|
||||
<div v-if="item.images && item.images.length > 0" class="f-fcl ui-remarks-pic-box">
|
||||
<img v-for="(itemV2, indexV2) in item.images" :key="indexV2" class="ui-remarks-pic" :src="itemV2" alt="" @click="ImagePreview(item.images, indexV2)" />
|
||||
</div>
|
||||
<div class="font_30 bold color3">{{ item.comment }}</div>
|
||||
<div class="ui-line-between"></div>
|
||||
<div class="font_30 color6 ui-pb-8">
|
||||
备注人:<span class="color3">{{ item.user ? item.user.name || '--' : '--' }}</span>
|
||||
</div>
|
||||
<div class="font_30 color6 ui-pb-8">
|
||||
备注类型:<span class="color3">{{ item.type_name || '--' }}</span>
|
||||
</div>
|
||||
<div class="f-fbc">
|
||||
<div class="color6 font_30">创建时间:{{ item.created_at }}</div>
|
||||
<div class="font_30 colorTheme" @click="jumpPath(`appRemarkDetail`, item.id)">编辑</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
<div class="ui-btn font_32 colorF f-fcc" @click="jumpPath(`appRemarkDetail`, '')">填写备注</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onActivated, onMounted, ref } from 'vue';
|
||||
import { showImagePreview } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppRemarks' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const noMore = ref(false); // 没有更多数据
|
||||
const refreshing = ref(true); // 上拉刷新false表示加载完成
|
||||
const finished = ref(false); // true表示数据全部加载完成
|
||||
const loading = ref(false); // false表示数据加载完成
|
||||
const page = ref(1); // 数据分页
|
||||
|
||||
const scrollValue = ref(0); // 记录页面列表的滚动距离
|
||||
const scrollDistance = ref<any>(null);
|
||||
|
||||
// 获取备注列表
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/order/comments?page=${page.value}&chat_id=${userStore.chatId}&status=${userStore.weChatBindState}&is_im=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
if (list.value.length === 0 || page.value === 1) {
|
||||
list.value = result.data;
|
||||
} else if (list.value.length >= 15) {
|
||||
result.data.forEach((item) => {
|
||||
list.value.push(item);
|
||||
});
|
||||
}
|
||||
refreshing.value = false;
|
||||
loading.value = false;
|
||||
if (list.value.length < 15 || result.data.length < 15) {
|
||||
finished.value = true;
|
||||
noMore.value = true;
|
||||
}
|
||||
page.value++;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 上拉初始化数据
|
||||
const onRefresh = () => {
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
finished.value = false;
|
||||
loading.value = true;
|
||||
getList();
|
||||
};
|
||||
|
||||
const ImagePreview = (e, index) => {
|
||||
showImagePreview({
|
||||
images: e,
|
||||
showIndex: false,
|
||||
startPosition: index,
|
||||
loop: false,
|
||||
});
|
||||
};
|
||||
|
||||
const jumpPath = (url, id) => {
|
||||
router.push({
|
||||
name: url,
|
||||
query: { chatId: userStore.chatId, comment_id: id },
|
||||
});
|
||||
};
|
||||
|
||||
// 监听页面滚动距离
|
||||
const handleScroll = (event) => {
|
||||
scrollValue.value = event.target.scrollTop;
|
||||
};
|
||||
|
||||
onActivated(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
// 备注后重新请求接口
|
||||
if (route.state) {
|
||||
onRefresh();
|
||||
} else {
|
||||
// 赋值离开时的滚动距离
|
||||
scrollDistance.value.scrollTop = scrollValue.value;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
if (route.chat_id) {
|
||||
userStore.chatId = route.chat_id;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appRemarks {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-empty-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: 25vh auto 0 auto;
|
||||
}
|
||||
|
||||
.ui-remarks-box {
|
||||
padding-bottom: 16vh;
|
||||
|
||||
.ui-remarks-item {
|
||||
margin: px2rem(30);
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(30);
|
||||
|
||||
.ui-remarks-pic-box {
|
||||
flex-flow: wrap;
|
||||
|
||||
.ui-remarks-pic {
|
||||
width: px2rem(186);
|
||||
height: px2rem(186);
|
||||
border: px2rem(2) solid #f8f8f8;
|
||||
border-radius: px2rem(16);
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
margin-right: px2rem(20);
|
||||
margin-bottom: px2rem(20);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-remarks-pic-box::after {
|
||||
width: px2rem(140);
|
||||
content: '';
|
||||
}
|
||||
|
||||
.ui-line-between {
|
||||
width: 100%;
|
||||
height: px2rem(2);
|
||||
background: #f5f5f5;
|
||||
margin: px2rem(20) 0 px2rem(18) 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
position: fixed;
|
||||
bottom: 8vh;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: px2rem(480);
|
||||
height: px2rem(76);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
}
|
||||
</style>
|
||||
177
src/views/appDir/appReplenishData.vue
Normal file
177
src/views/appDir/appReplenishData.vue
Normal file
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="ui-appReplenishData">
|
||||
<div class="font_30 color333 bold f-fcl">补充资料</div>
|
||||
<div class="ui-describe-box">
|
||||
<van-field v-model="describe" class="ui-desc-input font_30 color3" rows="5" type="textarea" maxlength="500" show-word-limit placeholder="请输入补充资料内容" autosize />
|
||||
</div>
|
||||
<div class="ui-upload-box">
|
||||
<div v-for="(item, index) in pics" :key="index" class="ui-upload-icon-box flo_l">
|
||||
<img class="ui-upload-icon" :src="item" mode="aspectFill" alt="" @click="ImagePreview(pics, index)" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" mode="widthFix" alt="" @click="clearPic(index)" />
|
||||
</div>
|
||||
<div v-if="pics.length < 9">
|
||||
<uploadPicture :multiple="true" :max-count="9" @on-success="onSuccess" @click="takePhone">
|
||||
<div class="ui-upload">
|
||||
<img class="ui-upload-icon flo_l" src="https://image.fulllinkai.com/202301/07/6c9bc853bed42c9871f56156d1b8f31c.png" alt="" />
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-next-btn font_30 f-fcc colorF" @click="changeData">保存</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { showImagePreview, showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppReplenishData' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const throttle = ref(true);
|
||||
const describe = ref<any>('');
|
||||
const reportType = ref<any>('0');
|
||||
const pics = ref<any[]>([]);
|
||||
|
||||
// 删除某个已上传的图片
|
||||
const clearPic = (index) => {
|
||||
pics.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const changeData = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
images: pics.value,
|
||||
type: reportType.value,
|
||||
text: describe.value,
|
||||
};
|
||||
if (!describe.value) {
|
||||
showToast('请输入补充资料内容');
|
||||
return;
|
||||
}
|
||||
if (pics.value.length === 0) {
|
||||
showToast('请上传最少一张图片');
|
||||
return;
|
||||
}
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/add/other/health/file`, data, method: 'post' })
|
||||
.then(() => {
|
||||
throttle.value = true;
|
||||
showToast('保存成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'appViewUserInfo',
|
||||
query: { currentTab: 2 },
|
||||
});
|
||||
router.go(-1);
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const ImagePreview = (e, index) => {
|
||||
showImagePreview({
|
||||
images: e,
|
||||
showIndex: false,
|
||||
startPosition: index,
|
||||
loop: false,
|
||||
});
|
||||
};
|
||||
|
||||
// 上传图片
|
||||
const onSuccess = (val) => {
|
||||
if (pics.value.length < 9) {
|
||||
pics.value.push(val);
|
||||
}
|
||||
};
|
||||
|
||||
window.getAndroidPhone = (e) => {
|
||||
if (e && e.length > 0) {
|
||||
e.forEach((item) => {
|
||||
if (pics.value && pics.value.length < 9) {
|
||||
pics.value.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const takePhone = () => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.takePhoto();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
reportType.value = route.reportType;
|
||||
// userStore.chatId = 1;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appReplenishData {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
padding: px2rem(30);
|
||||
}
|
||||
|
||||
.ui-describe-box {
|
||||
padding-bottom: px2rem(40);
|
||||
|
||||
.ui-desc-input {
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
margin-top: px2rem(20);
|
||||
}
|
||||
|
||||
.inputColor {
|
||||
color: #c2c2c2;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-box {
|
||||
overflow: hidden;
|
||||
margin-bottom: px2rem(120);
|
||||
|
||||
.ui-upload-icon-box {
|
||||
position: relative;
|
||||
|
||||
.ui-upload-clear-icon {
|
||||
position: absolute;
|
||||
right: px2rem(30);
|
||||
top: px2rem(10);
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-icon {
|
||||
width: px2rem(200);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
border-radius: px2rem(16);
|
||||
margin-right: px2rem(20);
|
||||
margin-bottom: px2rem(20);
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-next-btn {
|
||||
width: px2rem(560);
|
||||
height: px2rem(80);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
margin: 0 auto;
|
||||
}
|
||||
</style>
|
||||
120
src/views/appDir/appServiceFlow.vue
Normal file
120
src/views/appDir/appServiceFlow.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="ui-appServiceFlow">
|
||||
<img class="ui-top-bg" src="https://image.fulllinkai.com/202410/28/6cf27a143e98c2a20be56f06772feea8.png" alt="" />
|
||||
<div class="ui-container-box">
|
||||
<img class="ui-before-icon" src="https://image.fulllinkai.com/202410/28/4526365c9f10d3e26441858b23be9302.png" alt="" />
|
||||
<div class="f-fbc f-wrap">
|
||||
<div v-for="(item, index) in beforeList" :key="index" class="ui-before-item f-fbc" :class="item.state ? 'ui-active' : ''">
|
||||
<div>
|
||||
<div class="font_28 color0E bold400">
|
||||
<span class="bold500">{{ index + 1 }}</span>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="font_22 color76c ui-pt-8 bold400">{{ item.subTitle }}</div>
|
||||
</div>
|
||||
<img v-if="!item.state" class="ui-state-icon" src="https://image.fulllinkai.com/202410/28/c1ce13747114460dd4cb8eb838f9dbd6.png" alt="" />
|
||||
<img v-else class="ui-state-icon" src="https://image.fulllinkai.com/202410/28/4df1d6292a1a3a499f279d8625948cf9.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<img class="ui-middle-icon" src="https://image.fulllinkai.com/202410/28/4526365c9f10d3e26441858b23be9302.png" alt="" />
|
||||
<div class="f-fbc f-wrap">
|
||||
<div v-for="(item, index) in middleList" :key="index" class="ui-before-item f-fbc" :class="item.state ? 'ui-active' : ''">
|
||||
<div>
|
||||
<div class="font_28 color0E bold400">
|
||||
<span class="bold500">{{ index + 1 }}</span>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="font_22 color76c ui-pt-8 bold400">{{ item.subTitle }}</div>
|
||||
</div>
|
||||
<img v-if="!item.state" class="ui-state-icon" src="https://image.fulllinkai.com/202410/28/c1ce13747114460dd4cb8eb838f9dbd6.png" alt="" />
|
||||
<img v-else class="ui-state-icon" src="https://image.fulllinkai.com/202410/28/4df1d6292a1a3a499f279d8625948cf9.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<img class="ui-after-icon" src="https://image.fulllinkai.com/202410/28/4526365c9f10d3e26441858b23be9302.png" alt="" />
|
||||
<div class="f-fbc f-wrap">
|
||||
<div v-for="(item, index) in afterList" :key="index" class="ui-before-item f-fbc" :class="item.state ? 'ui-active' : ''">
|
||||
<div>
|
||||
<div class="font_28 color0E bold400">
|
||||
<span class="bold500">{{ index + 1 }}</span>
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="font_22 color76c ui-pt-8 bold400">{{ item.subTitle }}</div>
|
||||
</div>
|
||||
<img v-if="!item.state" class="ui-state-icon" src="https://image.fulllinkai.com/202410/28/c1ce13747114460dd4cb8eb838f9dbd6.png" alt="" />
|
||||
<img v-else class="ui-state-icon" src="https://image.fulllinkai.com/202410/28/4df1d6292a1a3a499f279d8625948cf9.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
defineOptions({ name: 'Test' });
|
||||
|
||||
const beforeList = ref<any[]>([
|
||||
{ title: '填写问卷', subTitle: '方案前健康调查问卷', state: 1 },
|
||||
{ title: '缴费成功', subTitle: '系统审核', state: 1 },
|
||||
{ title: '签署合同', subTitle: '依法成立的合同', state: 1 },
|
||||
{ title: '健康档案', subTitle: '填写健康档案', state: 1 },
|
||||
{ title: '确认收货', subTitle: '未开始', state: 0 },
|
||||
]);
|
||||
|
||||
const middleList = ref<any[]>([{ title: '查看餐单', subTitle: '未开始', state: 0 }]);
|
||||
|
||||
const afterList = ref<any[]>([{ title: '调查问卷', subTitle: '未开始', state: 0 }]);
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appServiceFlow {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ui-top-bg {
|
||||
width: 100vw;
|
||||
height: px2rem(514);
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
margin-top: px2rem(-176);
|
||||
}
|
||||
|
||||
.ui-container-box {
|
||||
margin-top: px2rem(-100);
|
||||
padding: px2rem(12) px2rem(20) px2rem(100) px2rem(20);
|
||||
border-radius: px2rem(40) px2rem(40) 0 0;
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
.ui-before-icon,
|
||||
.ui-middle-icon,
|
||||
.ui-after-icon {
|
||||
width: px2rem(164);
|
||||
height: px2rem(74);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ui-before-item {
|
||||
width: px2rem(344);
|
||||
padding: px2rem(40) px2rem(12) px2rem(40) px2rem(20);
|
||||
box-sizing: border-box;
|
||||
background: #fcfcfc;
|
||||
border-radius: px2rem(20);
|
||||
margin-bottom: px2rem(12);
|
||||
|
||||
.ui-state-icon {
|
||||
width: px2rem(96);
|
||||
height: px2rem(96);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-active {
|
||||
background: #f8fcf9;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
663
src/views/appDir/appSleepStatistics.vue
Normal file
663
src/views/appDir/appSleepStatistics.vue
Normal file
@ -0,0 +1,663 @@
|
||||
<template>
|
||||
<div class="ui-appSleepStatistics">
|
||||
<div class="ui-placeholder"></div>
|
||||
<div class="ui-echarts-data">
|
||||
<div class="f-fbc ui-pb-32">
|
||||
<img class="ui-triangle-icon" src="https://image.fulllinkai.com/202405/14/30bdbac6230ebcbe36731049a8d43936.png" alt="" @click="prevDate" />
|
||||
<div class="font_30 bold color3 f-fcc">
|
||||
<!-- <div class="ui-pr-36 ui-pl-36">{{ startDate }}</div>-->
|
||||
<!-- <img class="ui-triangle-icon" src="https://image.fulllinkai.com/202405/14/27dd2272bf0fa82980c7bd58789c42df.png" alt="" />-->
|
||||
<div class="ui-pl-36 ui-pr-36">{{ endDate }}</div>
|
||||
</div>
|
||||
<img class="ui-triangle-icon" src="https://image.fulllinkai.com/202405/14/e813aafcc67964766a29d1ce2d33a805.png" alt="" @click="nextDate" />
|
||||
</div>
|
||||
<div v-if="selectData.seriesName && initArr && initArr.length > 0" class="ui-top-show-data colorF font_28 text-center bold" :style="{ background: initArr[selectIndex].color }">
|
||||
<div>{{ initArr[selectIndex].date }}</div>
|
||||
<div class="ui-pt-10">{{ selectData.seriesName }}</div>
|
||||
</div>
|
||||
<div class="ui-echarts-box">
|
||||
<div ref="main" class="ui-echarts"></div>
|
||||
</div>
|
||||
<div class="f-fbc">
|
||||
<div class="font_24 bold color3 ui-startTime">{{ showStartTime }}</div>
|
||||
<div class="font_24 bold color3 ui-endTime">{{ showEndTime }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-echarts-data">
|
||||
<div class="ui-type-box">
|
||||
<div v-if="sleepTotalTime" class="f-fbc color3 bold ui-pb-10">
|
||||
<div class="font_28">睡眠时长 {{ sleepTotalTime }}</div>
|
||||
<div class="ui-score">{{ sleepTotalScore }}分</div>
|
||||
</div>
|
||||
<div v-for="(item, index) in typeList" :key="index" class="f-fbc ui-mt-32">
|
||||
<div class="ui-type-item" :style="{ background: item.bgColor }">
|
||||
<div class="ui-type-item-label" :style="{ width: item.percent + '%', background: item.color }"></div>
|
||||
</div>
|
||||
<div class="text-right color3 bold">
|
||||
<div class="font_24">{{ item.name }} {{ item.value }}</div>
|
||||
<div class="font_26 ui-pt-6">{{ item.percent }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="ui-sleep-data-box">-->
|
||||
<!-- <div class="f-fcl ui-pb-24">-->
|
||||
<!-- <div class="ui-title-line"></div>-->
|
||||
<!-- <div class="font_30 bold">详细数据</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-if="initArr && initArr.length > 0" ref="scrollContainer" class="ui-detail-box">-->
|
||||
<!-- <div v-for="(item, index) in initArr" v-show="item.value !== -1" :key="index" ref="setItemRef" class="f-fbc ui-pb-16 ui-pt-16 ui-pl-28 ui-pr-28" :class="index == selectIndex ? 'ui-select-detail' : ''">-->
|
||||
<!-- <div class="f-fcl">-->
|
||||
<!-- <div class="ui-detail-label" :style="{ background: item.color }"></div>-->
|
||||
<!-- <div class="font_30 color3">{{ item.date }}</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="font_28 bold color3">{{ item.seriesName }}({{ item.origin_value || '' }})</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-else>-->
|
||||
<!-- <img class="ui-empty-data-icon" src="https://image.fulllinkai.com/202301/08/939c529ca16a91e3ee3b22bef2fe92ca.png" alt="" />-->
|
||||
<!-- <div class="color9 font_28 text-center ui-mt-16">暂无数据</div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, shallowRef } from 'vue';
|
||||
import { showToast, closeToast } from 'vant';
|
||||
// import vConsole from 'vconsole';
|
||||
import requestApp from '@/utils/requestApp';
|
||||
import { timestamp } from '@/plugins/public';
|
||||
|
||||
defineOptions({ name: 'AppSleepStatistics' });
|
||||
|
||||
const main = ref<any>(null);
|
||||
const myChart = shallowRef<any>(null);
|
||||
|
||||
// const setItemRef = ref<any>(null);
|
||||
// const scrollContainer = ref<any>(null); // 滚动的位置
|
||||
const selectData = ref<any>({ seriesName: '' }); // 选中的数据
|
||||
const selectIndex = ref<any>(0); // 选中的数据下标
|
||||
const typeList = ref<any[]>([
|
||||
{ name: '清醒', value: '0', percent: '0.0', color: '#FFCB9B', bgColor: '#FEE9D6' },
|
||||
{ name: '快速', value: '0', percent: '0.0', color: '#D6C4F8', bgColor: '#EFE7FE' },
|
||||
{ name: '浅睡', value: '0', percent: '0.0', color: '#B08BF4', bgColor: '#DFD0F9' },
|
||||
{ name: '深睡', value: '0', percent: '0.0', color: '#7148EF', bgColor: '#C5B4F8' },
|
||||
]);
|
||||
|
||||
const objectTime = ref<any>(420);
|
||||
const token = ref<any>('');
|
||||
|
||||
const startDate = ref<any>(''); // 前一天日期
|
||||
const endDate = ref<any>(''); // 今天的日期
|
||||
const startTime = ref<any>('18:00:00'); // 开始时间
|
||||
const endTime = ref<any>(''); // 结束时间
|
||||
const currentDate = ref<any>(''); // 当天的日期
|
||||
|
||||
const showStartTime = ref<any>(''); // 展示数据第一条的开始时间
|
||||
const showEndTime = ref<any>(''); // 展示数据第一条的结束时间
|
||||
|
||||
const initArr = ref<any[]>([]);
|
||||
|
||||
const sleepTotalTime = ref<any>('');
|
||||
const sleepTotalScore = ref<any>('');
|
||||
|
||||
const lightTime = ref<any>(0); // 快速的总睡眠时间
|
||||
const lightIdeal = ref<any>(0); // 快速的理想分
|
||||
const lightScore = ref<any>(0); // 快速的得分
|
||||
const remTime = ref<any>(0); // 浅睡的总睡眠时间
|
||||
const remIdeal = ref<any>(0); // 浅睡的理想分
|
||||
const remScore = ref<any>(0); // 浅睡的得分
|
||||
const deepTime = ref<any>(0); // 深睡的总睡眠时间
|
||||
const deepIdeal = ref<any>(0); // 深睡的理想分
|
||||
const deepScore = ref<any>(0); // 深睡的得分
|
||||
|
||||
const loading = ref(false);
|
||||
const throttle = ref<any>(false);
|
||||
const timer = ref<any>(null);
|
||||
|
||||
const one = ref<any>([]); // 清醒的数据
|
||||
const two = ref<any>([]); // 快速的数据
|
||||
const three = ref<any>([]); // 浅睡的数据
|
||||
const four = ref<any>([]); // 深睡的数据
|
||||
const echartsArr = ref<any>([]);
|
||||
|
||||
const getAppSleepData = () => {
|
||||
throttle.value = false;
|
||||
|
||||
if (myChart.value) {
|
||||
myChart.value.dispose();
|
||||
}
|
||||
selectData.value = { seriesName: '' };
|
||||
selectIndex.value = 0;
|
||||
// scrollContainer.value = null;
|
||||
showStartTime.value = '';
|
||||
showEndTime.value = '';
|
||||
loading.value = false;
|
||||
requestApp({ url: `/app/daily/sleep?start_time=${startDate.value} ${startTime.value}&end_time=${endDate.value} ${endTime.value}`, method: 'get' })
|
||||
// requestApp({ url: `/app/daily/sleep?start_time=2024-05-10 18:00:00&end_time=2024-05-11 18:00:00`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
|
||||
let oneTimeTotal = 0; // 清醒的总时间
|
||||
let oneDataTotal = 0; // 清醒的总数据条数
|
||||
let twoTimeTotal = 0; // 快速的总时间
|
||||
let twoDataTotal = 0; // 快速的总数据条数
|
||||
let threeTimeTotal = 0; // 浅睡的总时间
|
||||
let threeDataTotal = 0; // 浅睡的总数据条数
|
||||
let fourTimeTotal = 0; // 深睡的总时间
|
||||
let fourDataTotal = 0; // 深睡的总数据条数
|
||||
|
||||
one.value = [];
|
||||
two.value = [];
|
||||
three.value = [];
|
||||
four.value = [];
|
||||
echartsArr.value = [];
|
||||
typeList.value = [
|
||||
{ name: '清醒', value: '0', percent: '0.0', color: '#FFCB9B', bgColor: '#FEE9D6' },
|
||||
{ name: '快速', value: '0', percent: '0.0', color: '#D6C4F8', bgColor: '#EFE7FE' },
|
||||
{ name: '浅睡', value: '0', percent: '0.0', color: '#B08BF4', bgColor: '#DFD0F9' },
|
||||
{ name: '深睡', value: '0', percent: '0.0', color: '#7148EF', bgColor: '#C5B4F8' },
|
||||
];
|
||||
|
||||
let arr = result;
|
||||
if (arr && arr.length > 0) {
|
||||
for (let index = 0; index < arr.length; index++) {
|
||||
if (index > 0 && index + 1 != arr.length) {
|
||||
const date1 = new Date(result[index].date.replace(/-/g, '/')) as any;
|
||||
const date2 = new Date(result[index + 1].date.replace(/-/g, '/')) as any;
|
||||
const differenceInMilliseconds = Math.abs(date2 - date1);
|
||||
if (differenceInMilliseconds > 90000) {
|
||||
result.splice(index + 1, 0, {
|
||||
date: timestamp(Date.parse(date1) / 1000 + 60),
|
||||
value: -1,
|
||||
seriesName: '',
|
||||
origin_value: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
if (result && result.length > 0) {
|
||||
result.forEach((item) => {
|
||||
if (item.value == 0) {
|
||||
one.value.push(50);
|
||||
two.value.push('-');
|
||||
three.value.push('-');
|
||||
four.value.push('-');
|
||||
echartsArr.value.push(150);
|
||||
item.seriesName = '清醒';
|
||||
item.color = '#FFCB9B';
|
||||
oneTimeTotal += item.unit;
|
||||
oneDataTotal++;
|
||||
} else if (item.value == 1) {
|
||||
one.value.push('-');
|
||||
two.value.push(50);
|
||||
three.value.push('-');
|
||||
four.value.push('-');
|
||||
echartsArr.value.push(100);
|
||||
item.seriesName = '快速';
|
||||
item.color = '#D6C4F8';
|
||||
twoTimeTotal += item.unit;
|
||||
twoDataTotal++;
|
||||
} else if (item.value == 2) {
|
||||
one.value.push('-');
|
||||
two.value.push('-');
|
||||
three.value.push(50);
|
||||
four.value.push('-');
|
||||
echartsArr.value.push(50);
|
||||
item.seriesName = '浅睡';
|
||||
item.color = '#B08BF4';
|
||||
threeTimeTotal += item.unit;
|
||||
threeDataTotal++;
|
||||
} else if (item.value == 3) {
|
||||
one.value.push('-');
|
||||
two.value.push('-');
|
||||
three.value.push('-');
|
||||
four.value.push(50);
|
||||
echartsArr.value.push(0);
|
||||
item.seriesName = '深睡';
|
||||
item.color = '#7148EF';
|
||||
fourTimeTotal += item.unit;
|
||||
fourDataTotal++;
|
||||
} else {
|
||||
one.value.push('-');
|
||||
two.value.push('-');
|
||||
three.value.push('-');
|
||||
four.value.push('-');
|
||||
echartsArr.value.push(200);
|
||||
item.seriesName = '';
|
||||
}
|
||||
});
|
||||
|
||||
initArr.value = result;
|
||||
console.log(initArr.value, '7777');
|
||||
loading.value = true;
|
||||
|
||||
setTimeout(() => {
|
||||
// selectData.value.seriesName = initArr.value[selectIndex.value].seriesName;
|
||||
showStartTime.value = `${initArr.value[selectIndex.value].date.split(/[: ' ' ]/)[1]}:${initArr.value[selectIndex.value].date.split(/[: ' ' ]/)[2]}`;
|
||||
showEndTime.value = `${initArr.value[initArr.value.length - 1].date.split(/[: ' ']/)[1]}:${initArr.value[initArr.value.length - 1].date.split(/[: ' ']/)[2]}`;
|
||||
|
||||
remTime.value = twoTimeTotal;
|
||||
remIdeal.value = Math.floor(objectTime.value * 0.2);
|
||||
remScore.value = (remTime.value / remIdeal.value) * 100 > 100 ? 100 : Math.floor((remTime.value / remIdeal.value) * 100);
|
||||
|
||||
lightTime.value = threeTimeTotal;
|
||||
lightIdeal.value = Math.floor(objectTime.value * 0.5);
|
||||
lightScore.value = (lightTime.value / lightIdeal.value) * 100 > 100 ? 100 : Math.floor((lightTime.value / lightIdeal.value) * 100);
|
||||
|
||||
deepTime.value = fourTimeTotal;
|
||||
deepIdeal.value = Math.floor(objectTime.value * 0.2);
|
||||
let deep = (deepTime.value / deepIdeal.value) * 100 > 150 ? 150 : Math.floor((deepTime.value / deepIdeal.value) * 100);
|
||||
deepScore.value = deep <= 100 ? Math.floor(deep * 0.9) : Math.floor((deep - 100) / 5) + 90;
|
||||
|
||||
let score = Math.floor(remScore.value * 0.25) + Math.floor(lightScore.value * 0.25) + Math.floor(deepScore.value * 0.5); // 综合分
|
||||
// console.log(Math.floor(deepScore.value * 0.5), Math.floor(lightScore.value * 0.25), Math.floor(remScore.value * 0.25), '88');
|
||||
|
||||
let base = ((oneTimeTotal + twoTimeTotal + threeTimeTotal + fourTimeTotal) / objectTime.value) * 100 > 100 ? 100 : Math.floor(((oneTimeTotal + twoTimeTotal + threeTimeTotal + fourTimeTotal) / objectTime.value) * 100); // 基础分
|
||||
|
||||
sleepTotalScore.value = Math.floor(0.4 * base) + Math.floor(0.6 * score);
|
||||
|
||||
sleepTotalTime.value = TimeConversion(oneTimeTotal + twoTimeTotal + threeTimeTotal + fourTimeTotal);
|
||||
|
||||
// console.log(remTime.value, remIdeal.value, remScore.value, '1');
|
||||
// console.log(lightTime.value, lightIdeal.value, lightScore.value, '2');
|
||||
// console.log(deepTime.value, deepIdeal.value, deepScore.value, '3');
|
||||
// console.log(deep, score, base, sleepTotalScore.value, '4');
|
||||
|
||||
// 图表下方统计数据计算
|
||||
typeList.value[0].value = TimeConversion(oneTimeTotal);
|
||||
typeList.value[0].percent = `${(numFilter(oneDataTotal / initArr.value.length) * 100).toFixed(2)}`;
|
||||
typeList.value[1].value = TimeConversion(twoTimeTotal);
|
||||
typeList.value[1].percent = `${(numFilter(twoDataTotal / initArr.value.length) * 100).toFixed(2)}`;
|
||||
typeList.value[2].value = TimeConversion(threeTimeTotal);
|
||||
typeList.value[2].percent = `${(numFilter(threeDataTotal / initArr.value.length) * 100).toFixed(2)}`;
|
||||
typeList.value[3].value = TimeConversion(fourTimeTotal);
|
||||
typeList.value[3].percent = `${(numFilter(fourDataTotal / initArr.value.length) * 100).toFixed(2)}`;
|
||||
|
||||
// 百分比总和不足为1,最后一个百分比不等于0的补齐
|
||||
let scale = 0;
|
||||
let scaleIndex = 0;
|
||||
typeList.value.forEach((item, index) => {
|
||||
scale += item.percent * 1;
|
||||
if (item.percent * 1) {
|
||||
scaleIndex = index;
|
||||
}
|
||||
});
|
||||
if (scale < 100) {
|
||||
typeList.value[scaleIndex].percent = typeList.value[scaleIndex].percent * 1 + (100 - scale);
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
sleepTotalTime.value = '';
|
||||
sleepTotalScore.value = '';
|
||||
initArr.value = [];
|
||||
}
|
||||
}, 100);
|
||||
|
||||
setTimeout(() => {
|
||||
initEcharts();
|
||||
}, 200);
|
||||
})
|
||||
.catch((err) => {
|
||||
closeToast();
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const initEcharts = () => {
|
||||
myChart.value = echarts.init(main.value);
|
||||
let option = {
|
||||
grid: {
|
||||
top: '10%',
|
||||
left: '-30px',
|
||||
right: '0',
|
||||
bottom: '-8px',
|
||||
containLabel: true,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
color: '#5ac7a0',
|
||||
},
|
||||
},
|
||||
formatter(params) {
|
||||
let tar;
|
||||
if (!loading.value) {
|
||||
return;
|
||||
}
|
||||
tar = params.filter((item) => {
|
||||
return item.data != '-';
|
||||
});
|
||||
throttle.value = false;
|
||||
clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => {
|
||||
throttle.value = true;
|
||||
}, 500);
|
||||
if (tar.length > 1) {
|
||||
selectData.value = tar[1];
|
||||
selectIndex.value = tar[1].dataIndex;
|
||||
console.log(selectData.value, '7777');
|
||||
// scrollContainer.value.scrollTop = setItemRef.value[selectIndex.value].offsetTop - scrollContainer.value.offsetTop - 80;
|
||||
}
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: (function () {
|
||||
let list = [] as any;
|
||||
for (let i = 1; i <= echartsArr.value.length; i++) {
|
||||
list.push(`${echartsArr.value[i]}`);
|
||||
}
|
||||
return list;
|
||||
})(),
|
||||
show: false,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
show: false,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: 'Placeholder',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
silent: false,
|
||||
itemStyle: {
|
||||
borderColor: 'transparent',
|
||||
color: 'transparent',
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
borderColor: 'transparent',
|
||||
color: 'transparent',
|
||||
},
|
||||
},
|
||||
data: echartsArr.value,
|
||||
},
|
||||
{
|
||||
name: '清醒',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
itemStyle: {
|
||||
color: '#FFCB9B',
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
data: one.value,
|
||||
},
|
||||
{
|
||||
name: '快速',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
itemStyle: {
|
||||
color: '#D6C4F8',
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
data: two.value,
|
||||
},
|
||||
{
|
||||
name: '浅睡',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
itemStyle: {
|
||||
color: '#B08BF4',
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
data: three.value,
|
||||
},
|
||||
{
|
||||
name: '深睡',
|
||||
type: 'bar',
|
||||
stack: 'Total',
|
||||
itemStyle: {
|
||||
color: '#7148EF',
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
data: four.value,
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.value.setOption(option);
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
}, 200);
|
||||
};
|
||||
|
||||
// 计算百分比
|
||||
const numFilter = (value) => {
|
||||
let realVal;
|
||||
let tempVal = parseFloat(value).toFixed(3);
|
||||
realVal = tempVal.substring(0, tempVal.length - 1);
|
||||
return realVal;
|
||||
};
|
||||
|
||||
// 计算统计图下方时间转换
|
||||
const TimeConversion = (time) => {
|
||||
let h;
|
||||
if (time >= 60) {
|
||||
h = Math.floor(time / 60);
|
||||
time -= h * 60;
|
||||
return `${h}时${time == 0 ? '' : `${time}分`}`;
|
||||
} else {
|
||||
return `${time}分`;
|
||||
}
|
||||
};
|
||||
|
||||
//前一天
|
||||
const prevDate = () => {
|
||||
if (!throttle.value) {
|
||||
return;
|
||||
}
|
||||
let odata = new Date(new Date(startDate.value.replace(/-/g, '/')).getTime() - 24 * 60 * 60 * 1000); //计算当前日期 -1
|
||||
endDate.value = startDate.value;
|
||||
startDate.value = transferTime(odata);
|
||||
endTime.value = '18:00:00';
|
||||
getAppSleepData();
|
||||
};
|
||||
|
||||
//后一天
|
||||
const nextDate = () => {
|
||||
if (endDate.value == currentDate.value) {
|
||||
showToast('请先睡个好觉,明天再来查看吧');
|
||||
return;
|
||||
}
|
||||
if (!throttle.value) {
|
||||
return;
|
||||
}
|
||||
let h = new Date().getHours();
|
||||
let m = new Date().getMinutes();
|
||||
let s = new Date().getSeconds();
|
||||
h = (h < 10 ? `0${h}` : h) as any;
|
||||
m = (m < 10 ? `0${m}` : m) as any;
|
||||
s = (s < 10 ? `0${s}` : s) as any;
|
||||
let odata = new Date(new Date(endDate.value.replace(/-/g, '/')).getTime() + 24 * 60 * 60 * 1000); //计算当前日期 +1
|
||||
startDate.value = endDate.value;
|
||||
endDate.value = transferTime(odata);
|
||||
if (endDate.value == currentDate.value) {
|
||||
endTime.value = `${h}:${m}:${s}`;
|
||||
}
|
||||
getAppSleepData();
|
||||
};
|
||||
|
||||
//转换时间格式
|
||||
const transferTime = (e) => {
|
||||
let date = new Date(e);
|
||||
let y = date.getFullYear();
|
||||
let m = (date.getMonth() + 1) as any;
|
||||
let d = date.getDate() as any;
|
||||
m = m < 10 ? `0${m}` : m;
|
||||
d = d < 10 ? `0${d}` : d;
|
||||
return `${y}-${m}-${d}`;
|
||||
};
|
||||
|
||||
// 获取从ios app过来的token数据
|
||||
window.getIosToken = (e) => {
|
||||
objectTime.value = e.time * 1;
|
||||
token.value = e.token;
|
||||
localStorage.setItem('appToken', token.value);
|
||||
|
||||
let year = new Date().getFullYear();
|
||||
let month = new Date().getMonth() + 1;
|
||||
let day = new Date().getDate();
|
||||
let h = new Date().getHours();
|
||||
let m = new Date().getMinutes();
|
||||
let s = new Date().getSeconds();
|
||||
let c = `${year}-${month}-${day}`;
|
||||
let yesterday = new Date(new Date(c.replace(/-/g, '/')).getTime() - 24 * 60 * 60 * 1000);
|
||||
month = (month < 10 ? `0${month}` : month) as any;
|
||||
day = (day < 10 ? `0${day}` : day) as any;
|
||||
h = (h < 10 ? `0${h}` : h) as any;
|
||||
m = (m < 10 ? `0${m}` : m) as any;
|
||||
s = (s < 10 ? `0${s}` : s) as any;
|
||||
startDate.value = transferTime(yesterday);
|
||||
endDate.value = `${year}-${month}-${day}`;
|
||||
currentDate.value = `${year}-${month}-${day}`;
|
||||
endTime.value = `${h}:${m}:${s}`;
|
||||
setTimeout(() => {
|
||||
getAppSleepData();
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// new vConsole();
|
||||
window.webkit.messageHandlers.getCurTokenWithUid.postMessage(null);
|
||||
// localStorage.setItem('appToken', 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJDcmVhdGVkQXQiOjE3MTUxMzQxNjcsIkV4cGlyZWRBdCI6MTcxNzcyNjE2NywiVmVyc2lvbiI6MTAwLCJVc2VyaWQiOjEwMywiZXhwIjoyNTc5MTM0MTY3fQ.3CjxKG-yC1u8xgEIkoqz0R3FDTVHNCw4x_uOhpagzPg');
|
||||
// let year = new Date().getFullYear();
|
||||
// let month = new Date().getMonth() + 1;
|
||||
// let day = new Date().getDate();
|
||||
// let h = new Date().getHours();
|
||||
// let m = new Date().getMinutes();
|
||||
// let s = new Date().getSeconds();
|
||||
// let c = `${year}-${month}-${day}`;
|
||||
// let yesterday = new Date(new Date(c.replace(/-/g, '/')).getTime() - 24 * 60 * 60 * 1000);
|
||||
// month = (month < 10 ? `0${month}` : month) as any;
|
||||
// day = (day < 10 ? `0${day}` : day) as any;
|
||||
// h = (h < 10 ? `0${h}` : h) as any;
|
||||
// m = (m < 10 ? `0${m}` : m) as any;
|
||||
// s = (s < 10 ? `0${s}` : s) as any;
|
||||
// startDate.value = transferTime(yesterday);
|
||||
// endDate.value = `${year}-${month}-${day}`;
|
||||
// currentDate.value = `${year}-${month}-${day}`;
|
||||
// endTime.value = `${h}:${m}:${s}`;
|
||||
// setTimeout(() => {
|
||||
// getAppSleepData();
|
||||
// });
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appSleepStatistics {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ui-placeholder {
|
||||
width: 100vw;
|
||||
height: px2rem(30);
|
||||
}
|
||||
|
||||
.ui-echarts-data {
|
||||
background: #ffffff;
|
||||
padding: px2rem(30);
|
||||
margin: 0 px2rem(30) px2rem(24) px2rem(30);
|
||||
border-radius: px2rem(12);
|
||||
|
||||
.ui-triangle-icon {
|
||||
width: px2rem(32);
|
||||
height: px2rem(32);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ui-top-show-data {
|
||||
width: fit-content;
|
||||
padding: px2rem(20) px2rem(50);
|
||||
margin: 0 auto;
|
||||
border-radius: px2rem(20);
|
||||
}
|
||||
|
||||
.ui-echarts-box {
|
||||
height: px2rem(260);
|
||||
|
||||
.ui-echarts {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-startTime {
|
||||
margin-left: px2rem(-2);
|
||||
}
|
||||
|
||||
.ui-endTime {
|
||||
margin-right: px2rem(-2);
|
||||
}
|
||||
|
||||
.ui-type-box {
|
||||
.ui-score {
|
||||
font-size: px2rem(48);
|
||||
}
|
||||
|
||||
.ui-type-item {
|
||||
width: 56%;
|
||||
height: px2rem(34);
|
||||
border-radius: px2rem(30);
|
||||
overflow: hidden;
|
||||
|
||||
.ui-type-item-label {
|
||||
height: px2rem(34);
|
||||
border-radius: px2rem(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-sleep-data-box {
|
||||
margin: px2rem(30);
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(12);
|
||||
|
||||
.ui-title-line {
|
||||
width: px2rem(8);
|
||||
height: px2rem(32);
|
||||
border-radius: px2rem(20);
|
||||
background: #5ac7a0;
|
||||
margin-right: px2rem(24);
|
||||
}
|
||||
|
||||
.ui-detail-box {
|
||||
height: px2rem(440);
|
||||
overflow-y: scroll;
|
||||
|
||||
.ui-select-detail {
|
||||
border: px2rem(2) solid #5ac7a0;
|
||||
border-radius: px2rem(6);
|
||||
}
|
||||
|
||||
.ui-detail-label {
|
||||
width: px2rem(42);
|
||||
height: px2rem(22);
|
||||
border-radius: px2rem(4);
|
||||
margin-right: px2rem(20);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-empty-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: px2rem(30) auto 0 auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
343
src/views/appDir/appStatistics.vue
Normal file
343
src/views/appDir/appStatistics.vue
Normal file
@ -0,0 +1,343 @@
|
||||
<template>
|
||||
<div class="ui-appStatistics">
|
||||
<div class="ui-label-box">
|
||||
<div v-for="(item, index) in labelList" :key="index" @click="selectLabel(item, index)">
|
||||
<div class="text-center ui-label-item" :class="index == labelIndex ? 'ui-active-label' : ''">
|
||||
<img class="ui-label-icon" :src="index == labelIndex ? item.h_icon : item.icon" alt="" />
|
||||
<div class="font_24" :class="index == labelIndex ? 'colorTheme' : 'color6'">{{ item.fat_name || item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-screening-time">
|
||||
<div class="color3 font_30 bold">时间筛选</div>
|
||||
<div class="f-fbc">
|
||||
<div class="ui-examine-time" @click="showDate = true">
|
||||
<van-field v-model="date" class="ui-date-input f-fcc font_30 color3" readonly placeholder="选择开始时间" />
|
||||
<img class="ui-time-icon" src="https://image.fulllinkai.com/202301/07/b2ac8981bfc3ab790f4ffabff8887988.png" alt="" />
|
||||
</div>
|
||||
<div class="font_30 ui-ml-12 ui-mr-12">~</div>
|
||||
<div class="ui-examine-time" @click="showEndDate = true">
|
||||
<van-field v-model="dateEnd" class="ui-date-input f-fcc font_30 color3" readonly placeholder="选择结束时间" />
|
||||
<img class="ui-time-icon" src="https://image.fulllinkai.com/202301/07/b2ac8981bfc3ab790f4ffabff8887988.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<view class="ui-unit color6 font_28 f-fbc">
|
||||
<div class="f-fcl">
|
||||
<div class="ui-vertical-line"></div>
|
||||
<div>单位:{{ unit || '--' }}</div>
|
||||
</div>
|
||||
<div class="colorTheme font_24">体脂秤类型:{{ fatDevice == 1 ? '新版秤' : '旧版秤' }}</div>
|
||||
</view>
|
||||
<div class="ui-echarts-box">
|
||||
<div ref="main" class="ui-echarts"></div>
|
||||
</div>
|
||||
<van-popup v-model:show="showDate" round position="bottom" :duration="0.5">
|
||||
<van-date-picker v-model="dateValues" title="选择日期" :min-date="minDate" :max-date="maxDate" @cancel="(showDate = false), (date = '')" @confirm="onConfirm" />
|
||||
</van-popup>
|
||||
<van-popup v-model:show="showEndDate" round position="bottom" :duration="0.5">
|
||||
<van-date-picker v-model="dateEndValues" title="选择日期" :min-date="minDate" :max-date="maxDate" @cancel="(showEndDate = false), (dateEnd = '')" @confirm="onEndConfirm" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, shallowRef } from 'vue';
|
||||
// import * as echarts from 'echarts';
|
||||
import { closeToast, showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppStatistics' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const main = ref();
|
||||
const fatDevice = ref<any>('');
|
||||
const myChart = shallowRef<any>(null);
|
||||
const showDate = ref(false);
|
||||
const showEndDate = ref(false);
|
||||
const date = ref<any>('');
|
||||
const dateEnd = ref<any>('');
|
||||
const minDate = new Date(new Date().getFullYear() - 120, 0, 1);
|
||||
const maxDate = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate());
|
||||
const dateValues = ref<any[]>([`${new Date().getFullYear()}`, `${new Date().getMonth() + 1 < 10 ? `0${new Date().getMonth() + 1}` : new Date().getMonth()}`, `${new Date().getDate()}`]); // 开始日期选择框默认值
|
||||
const dateEndValues = ref<any[]>([`${new Date().getFullYear()}`, `${new Date().getMonth() < 10 ? `0${new Date().getMonth() + 1}` : new Date().getMonth()}`, `${new Date().getDate()}`]); // 开始日期选择框默认值
|
||||
const chartTime = ref<any[]>([]);
|
||||
const chartData = ref<any[]>([]);
|
||||
const chartMinData = ref<any[]>([]);
|
||||
const labelList = ref<any[]>([]);
|
||||
const labelIndex = ref(0);
|
||||
const chartName = ref<any>('体重');
|
||||
const chartNameV2 = ref<any>('Weight');
|
||||
const unit = ref<any>('--');
|
||||
|
||||
const getIcon = () => {
|
||||
weChat({ url: `/h5/get/fat/kinds?chat_id=${userStore.chatId}`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
labelList.value = result;
|
||||
if (labelList.value && labelList.value.length > 0) {
|
||||
unit.value = labelList.value[0].unit;
|
||||
}
|
||||
console.log(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
closeToast();
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const getDate = () => {
|
||||
weChat({ url: `/h5/get/fat/stat?chat_id=${userStore.chatId}&fat_name=${chartName.value}&start_date=${date.value}&end_date=${dateEnd.value}`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
fatDevice.value = result.fat_device;
|
||||
chartTime.value = result.x_arr;
|
||||
chartData.value = result.y_arr;
|
||||
chartMinData.value = result.z_arr;
|
||||
console.log(result);
|
||||
myChart.value = null;
|
||||
setTimeout(() => {
|
||||
initEcharts();
|
||||
}, 100);
|
||||
})
|
||||
.catch((err) => {
|
||||
closeToast();
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const initEcharts = () => {
|
||||
myChart.value = echarts.init(main.value);
|
||||
let option = {
|
||||
grid: {
|
||||
top: '12%',
|
||||
left: '5%',
|
||||
right: '7%',
|
||||
bottom: '18%',
|
||||
containLabel: true,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(0,0,0,0.56)',
|
||||
borderColor: 'gray',
|
||||
textStyle: {
|
||||
color: '#ffffff',
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
data: chartTime.value,
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
},
|
||||
dataZoom: [
|
||||
{
|
||||
type: 'inside',
|
||||
moveOnMouseWheel: false,
|
||||
preventDefaultMouseMove: false,
|
||||
xAxisIndex: [0],
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
{
|
||||
xAxisIndex: [0],
|
||||
start: 0,
|
||||
end: 100,
|
||||
width: '88.6%',
|
||||
left: '5%',
|
||||
height: 20,
|
||||
handleSize: 24,
|
||||
borderColor: 'none',
|
||||
handleIcon: 'M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5M36.9,35.8h-1.3z M27.8,35.8 h-1.3H27L27.8,35.8L27.8,35.8z',
|
||||
backgroundColor: '#e5f7f1',
|
||||
color: '#BFCCE3',
|
||||
showDetail: false,
|
||||
// moveOnMouseWheel: false,
|
||||
// preventDefaultMouseMove: false,
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '测量最小值',
|
||||
type: 'line',
|
||||
symbol: 'none',
|
||||
lineStyle: {
|
||||
color: '#f3aa20',
|
||||
},
|
||||
data: chartMinData.value,
|
||||
},
|
||||
{
|
||||
name: '测量最大值',
|
||||
type: 'line',
|
||||
symbol: 'none',
|
||||
lineStyle: {
|
||||
color: '#5ac7a0',
|
||||
},
|
||||
data: chartData.value,
|
||||
},
|
||||
],
|
||||
};
|
||||
myChart.value.setOption(option);
|
||||
};
|
||||
|
||||
// 选择开始日期
|
||||
const onConfirm = ({ selectedValues }) => {
|
||||
let event = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
if (event && dateEnd.value) {
|
||||
if ((Date.parse(dateEnd.value) || Date.parse(dateEnd.value.replace(/-/g, '/'))) < (Date.parse(event) || Date.parse(event.replace(/-/g, '/')))) {
|
||||
showToast('开始时间不能大于结束时间');
|
||||
return;
|
||||
}
|
||||
date.value = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
getDate();
|
||||
}
|
||||
showDate.value = false;
|
||||
date.value = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
};
|
||||
|
||||
// 选择结束日期
|
||||
const onEndConfirm = ({ selectedValues }) => {
|
||||
let event = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
if (date.value && event) {
|
||||
if ((Date.parse(event) || Date.parse(event.replace(/-/g, '/'))) < (Date.parse(date.value) || Date.parse(date.value.replace(/-/g, '/')))) {
|
||||
showToast('开始时间不能大于结束时间');
|
||||
return;
|
||||
}
|
||||
dateEnd.value = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
getDate();
|
||||
}
|
||||
showEndDate.value = false;
|
||||
dateEnd.value = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
};
|
||||
|
||||
const selectLabel = (e, index) => {
|
||||
if (labelIndex.value === index) {
|
||||
return;
|
||||
}
|
||||
labelIndex.value = index;
|
||||
chartName.value = e.fat_name;
|
||||
chartNameV2.value = e.e_name;
|
||||
unit.value = e.unit;
|
||||
getDate();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// userStore.chatId = 1;
|
||||
getIcon();
|
||||
getDate();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appStatistics {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ui-tabBar-box {
|
||||
width: 100vw;
|
||||
height: px2rem(90);
|
||||
background: #f8f8f8;
|
||||
|
||||
.ui-tabBar-list {
|
||||
padding: px2rem(24) px2rem(180) px2rem(0) px2rem(180);
|
||||
}
|
||||
|
||||
.ui-tabBar-line {
|
||||
position: absolute;
|
||||
bottom: px2rem(2);
|
||||
width: 100%;
|
||||
height: px2rem(10);
|
||||
background: linear-gradient(90deg, rgba(95, 226, 175, 0.1) 0%, #41d5a9 100%);
|
||||
border-radius: px2rem(5);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-label-box {
|
||||
padding: px2rem(30) 0 0 px2rem(30);
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
overflow-x: scroll;
|
||||
white-space: nowrap;
|
||||
|
||||
.ui-label-item {
|
||||
min-width: px2rem(120);
|
||||
height: px2rem(112);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(8);
|
||||
margin-right: px2rem(10);
|
||||
padding-top: px2rem(22);
|
||||
|
||||
.ui-label-icon {
|
||||
width: px2rem(48);
|
||||
height: px2rem(48);
|
||||
display: block;
|
||||
margin: 0 auto px2rem(8) auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-active-label {
|
||||
background: #e8fff7;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-label-box::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ui-screening-time {
|
||||
padding: px2rem(40) px2rem(30) 0 px2rem(30);
|
||||
|
||||
.ui-examine-time {
|
||||
padding: 0 px2rem(30);
|
||||
margin: px2rem(20) 0 px2rem(30) 0;
|
||||
border-radius: px2rem(24);
|
||||
background: #ffffff;
|
||||
position: relative;
|
||||
|
||||
.ui-date-input {
|
||||
//width: px2rem(500);
|
||||
height: px2rem(80);
|
||||
padding: initial;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.ui-time-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
display: block;
|
||||
position: absolute;
|
||||
right: px2rem(30);
|
||||
top: px2rem(26);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-unit {
|
||||
padding: px2rem(30) px2rem(30) 0 px2rem(30);
|
||||
margin: px2rem(20) px2rem(30) 0 px2rem(30);
|
||||
border-radius: px2rem(24) px2rem(24) 0 0;
|
||||
background: #ffffff;
|
||||
|
||||
.ui-vertical-line {
|
||||
width: px2rem(4);
|
||||
height: px2rem(20);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(4);
|
||||
margin-top: px2rem(-4);
|
||||
margin-right: px2rem(10);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-echarts-box {
|
||||
margin: 0 px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: 0 0 px2rem(24) px2rem(24);
|
||||
height: px2rem(560);
|
||||
|
||||
.ui-echarts {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
267
src/views/appDir/appSurveys.vue
Normal file
267
src/views/appDir/appSurveys.vue
Normal file
@ -0,0 +1,267 @@
|
||||
<template>
|
||||
<div ref="scrollDistance" class="ui-appSurveys" @scroll="handleScroll">
|
||||
<div class="ui-top-box">
|
||||
<div class="ui-transmit-tips font_26 colorTheme f-fcl">转发说明:转发到群聊,用户可填写或查看调查问卷</div>
|
||||
<div class="ui-search-box">
|
||||
<van-field v-model="searchValue" class="ui-search-input font_28 color3 f-fcc" placeholder="搜索问卷" />
|
||||
<img class="ui-search-icon" src="https://image.fulllinkai.com/202304/07/38e32f1948ad951b1f66b404380648f3.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-placeholder"></div>
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="getList">
|
||||
<div v-if="!refreshing && list.length == 0">
|
||||
<img class="ui-empty-data-icon" src="https://image.fulllinkai.com/202306/07/247d8ae6b90334457d1b39129cd5c490.png" alt="" />
|
||||
<div class="color6 font_30 text-center">暂无问卷</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="ui-surveys-box">
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-surveys-item f-fbc" @click="jumpPath('appQuestionnaire', item.id)">
|
||||
<div class="ui-surveys-title-box">
|
||||
<img v-if="!item.is_write" class="ui-write-icon" src="https://image.fulllinkai.com/202306/07/ac2c34d999de6e46272cd62499559816.png" alt="" />
|
||||
<img v-else class="ui-write-icon" src="https://image.fulllinkai.com/202306/07/ef51a24dd282f1bb00027eccb687cfa8.png" alt="" />
|
||||
<div class="font_30 bold color3 ui-surveys-title">{{ item.title }}</div>
|
||||
</div>
|
||||
<div class="ui-btn font_26 colorTheme f-fcc" @click.stop="retransmission(item.id)">转发</div>
|
||||
<!--<div v-else class="ui-btn font_26 colorTheme f-fcc" @click.stop="jumpPath('appQuestionnaire', item.id)">查看</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onActivated, onMounted, ref, watch } from 'vue';
|
||||
import { showConfirmDialog, showToast } from 'vant';
|
||||
import router from '@/router';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppSurveys' });
|
||||
|
||||
const userStore = useUserStore();
|
||||
const isIOS = ref<any>(null);
|
||||
const searchValue = ref<any>('');
|
||||
const timer = ref<any>(null);
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const noMore = ref(false); // 没有更多数据
|
||||
const refreshing = ref(true); // 上拉刷新false表示加载完成
|
||||
const finished = ref(false); // true表示数据全部加载完成
|
||||
const loading = ref(false); // false表示数据加载完成
|
||||
const page = ref(1); // 数据分页
|
||||
|
||||
const scrollValue = ref(0); // 记录页面列表的滚动距离
|
||||
const scrollDistance = ref<any>(null);
|
||||
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/surveys?page=${page.value}&chat_id=${userStore.chatId}&keyword=${searchValue.value}`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
if (list.value.length === 0 || page.value === 1) {
|
||||
list.value = result.data;
|
||||
} else if (list.value.length >= 15) {
|
||||
result.data.forEach((item) => {
|
||||
list.value.push(item);
|
||||
});
|
||||
}
|
||||
refreshing.value = false;
|
||||
loading.value = false;
|
||||
if (list.value.length < 15 || result.data.length < 15) {
|
||||
finished.value = true;
|
||||
noMore.value = true;
|
||||
}
|
||||
page.value++;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化机构列表
|
||||
const initList = () => {
|
||||
page.value = 1;
|
||||
getList();
|
||||
};
|
||||
|
||||
const onRefresh = () => {
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
finished.value = false;
|
||||
loading.value = true;
|
||||
getList();
|
||||
};
|
||||
|
||||
// 转发
|
||||
const retransmission = (id) => {
|
||||
showConfirmDialog({
|
||||
title: '',
|
||||
message: '是否确认转发调查问卷到群聊天?',
|
||||
})
|
||||
.then(() => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
type: 'survey',
|
||||
is_im: 1,
|
||||
};
|
||||
weChat({ url: `send/im/msg`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showToast('通知已发送');
|
||||
setTimeout(() => {
|
||||
close();
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
|
||||
const jumpPath = (url, id) => {
|
||||
console.log(userStore.chatId, '777777777777');
|
||||
router.push({
|
||||
name: url,
|
||||
query: { chat_id: userStore.chatId, survey_id: id },
|
||||
});
|
||||
};
|
||||
|
||||
const close = () => {
|
||||
// 调用ios app方法
|
||||
if (isIOS.value) {
|
||||
window.webkit.messageHandlers.goBack.postMessage(null);
|
||||
} else {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.goBack();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听搜索值变化重新获取数据
|
||||
watch(searchValue, () => {
|
||||
clearTimeout(timer.value);
|
||||
timer.value = setTimeout(() => {
|
||||
initList();
|
||||
}, 800);
|
||||
});
|
||||
|
||||
// 监听页面滚动距离
|
||||
const handleScroll = (event) => {
|
||||
scrollValue.value = event.target.scrollTop;
|
||||
};
|
||||
|
||||
onActivated(() => {
|
||||
scrollDistance.value.scrollTop = scrollValue.value;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
isIOS.value = false;
|
||||
} else {
|
||||
isIOS.value = true;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appSurveys {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-top-box {
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 101;
|
||||
background: #f8f8f8;
|
||||
|
||||
.ui-transmit-tips {
|
||||
padding: 0 px2rem(30);
|
||||
height: px2rem(80);
|
||||
background: #e8fff7;
|
||||
}
|
||||
|
||||
.ui-search-box {
|
||||
max-width: 100%;
|
||||
padding: px2rem(20) px2rem(30) px2rem(30) px2rem(30);
|
||||
height: px2rem(80);
|
||||
background: #f8f8f8;
|
||||
position: relative;
|
||||
|
||||
.ui-search-input {
|
||||
width: 100%;
|
||||
padding: 0 px2rem(70);
|
||||
height: px2rem(80);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(40);
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
::v-deep(input::-webkit-input-placeholder) {
|
||||
font-size: px2rem(28);
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.ui-search-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
position: absolute;
|
||||
top: px2rem(46);
|
||||
left: px2rem(60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-placeholder {
|
||||
width: 100vw;
|
||||
height: px2rem(210);
|
||||
}
|
||||
|
||||
.ui-empty-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: 25vh auto 0 auto;
|
||||
}
|
||||
|
||||
.ui-surveys-box {
|
||||
padding-bottom: 16vh;
|
||||
|
||||
.ui-surveys-item {
|
||||
margin: 0 px2rem(30) px2rem(30) px2rem(30);
|
||||
padding: px2rem(34) px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(16);
|
||||
|
||||
.ui-surveys-title-box {
|
||||
position: relative;
|
||||
max-width: px2rem(474);
|
||||
|
||||
.ui-write-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: px2rem(84);
|
||||
height: px2rem(40);
|
||||
}
|
||||
|
||||
.ui-surveys-title {
|
||||
text-indent: px2rem(94);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
width: px2rem(126);
|
||||
height: px2rem(52);
|
||||
border-radius: px2rem(30);
|
||||
border: px2rem(2) solid #b2e3d2;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
269
src/views/appDir/appSwitchUser.vue
Normal file
269
src/views/appDir/appSwitchUser.vue
Normal file
@ -0,0 +1,269 @@
|
||||
<template>
|
||||
<div class="ui-appSwitchUser">
|
||||
<div>
|
||||
<div class="ui-title font_48 color3 bold">切换订单用户</div>
|
||||
<div class="ui-mobile f-fcl">
|
||||
<div class="f-fcl ui-area-code" @click.stop="switchChoose">
|
||||
<div class="font_30 color3">{{ AreaValue }}</div>
|
||||
<img v-if="showChooseArea" class="Angle_icon" src="https://image.fulllinkai.com/202112/10/697cbb5933196bbc21165cb974f2e343.png" alt="" />
|
||||
<img v-else class="Angle_icon" src="https://image.fulllinkai.com/202112/10/02d4c797f6de6494643f506c5d2b85b7.png" alt="" />
|
||||
</div>
|
||||
<van-field v-model="mobile" type="tel" class="ui-user-input font_32 color3" placeholder="请输入手机号" />
|
||||
<div v-if="showChooseArea" class="ui-relative area_code_choose_box">
|
||||
<div class="area_code_choose_list">
|
||||
<div v-for="(item, index) in areaList" :key="index" class="alignment area_code_choose" @click="selectedArea(item, index)">
|
||||
<div class="font_32 color3 area_code_choose_item" :class="AreaIndex == index ? 'colorPrice' : ''">{{ item.area_code }} {{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showChooseArea" class="ui-area-mask" @click="showChooseArea = false"></div>
|
||||
</div>
|
||||
<div class="ui-btn f-fcc font_30 colorF bold" @click="save">确认绑定</div>
|
||||
<div class="font_24 color9 text-center">绑定后可使用该帐号</div>
|
||||
</div>
|
||||
<img class="ui-icon" src="https://image.fulllinkai.com/202306/26/05b70e1b55dfed082b28fda935653343.png" alt="" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import router from '@/router';
|
||||
import requestGo from '@/utils/requestGo';
|
||||
|
||||
defineOptions({ name: 'AppSwitchUser' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const mobile = ref<any>('');
|
||||
const throttle = ref<any>(true);
|
||||
const showChooseArea = ref(false);
|
||||
const AreaIndex = ref<any>(0);
|
||||
const areaList = ref<any[]>([{ area_code: 86, label: '中国大陆' }]);
|
||||
const AreaValue = ref<any>('中国大陆 86');
|
||||
|
||||
// 切换用户
|
||||
const save = () => {
|
||||
if (throttle.value) {
|
||||
let data = {
|
||||
mobile: mobile.value,
|
||||
chat_id: userStore.chatId,
|
||||
area_code: areaList.value[AreaIndex.value].area_code,
|
||||
};
|
||||
if (!mobile.value) {
|
||||
showToast('请输入手机号');
|
||||
return;
|
||||
}
|
||||
if (AreaIndex.value == 0 && !/^1(3|4|5|6|7|8|9)\d{9}$/.test(mobile.value)) {
|
||||
showToast('手机号码格式错误');
|
||||
return;
|
||||
}
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/orders/update/user`, data, method: 'post' })
|
||||
.then(() => {
|
||||
throttle.value = true;
|
||||
showToast('订单用户切换成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'personalCenter',
|
||||
});
|
||||
router.go(-1);
|
||||
}, 2000);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getAreaCode = () => {
|
||||
requestGo({ url: `/h5/v2/user/areacode/list`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
areaList.value = result;
|
||||
console.log(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 国家区号弹窗
|
||||
const switchChoose = () => {
|
||||
showChooseArea.value = !showChooseArea.value;
|
||||
};
|
||||
|
||||
// 选择国家区号
|
||||
const selectedArea = (e, index) => {
|
||||
AreaValue.value = `${e.label} ${e.area_code}`;
|
||||
AreaIndex.value = index;
|
||||
showChooseArea.value = !showChooseArea.value;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
getAreaCode();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appSwitchUser {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-title {
|
||||
padding: px2rem(120) px2rem(0) px2rem(70) px2rem(50);
|
||||
}
|
||||
|
||||
.ui-item-select {
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
padding: 0 px2rem(30);
|
||||
|
||||
.ui-duty-icon {
|
||||
width: px2rem(40);
|
||||
height: px2rem(40);
|
||||
display: block;
|
||||
margin-right: px2rem(4);
|
||||
}
|
||||
|
||||
.ui-item-select-icon {
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
display: block;
|
||||
margin-right: px2rem(10);
|
||||
}
|
||||
|
||||
.ui-item-select-text {
|
||||
line-height: px2rem(40);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-mobile {
|
||||
position: relative;
|
||||
z-index: 10001;
|
||||
margin: px2rem(0) px2rem(50);
|
||||
border-bottom: px2rem(2) solid #d8d8d8;
|
||||
|
||||
.ui-area-code {
|
||||
padding-left: px2rem(30);
|
||||
padding-right: px2rem(20);
|
||||
white-space: nowrap;
|
||||
word-break: break-all;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.Angle_icon {
|
||||
width: px2rem(20);
|
||||
height: px2rem(12);
|
||||
display: block;
|
||||
margin-left: px2rem(10);
|
||||
}
|
||||
|
||||
.ui-area-mask {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 21;
|
||||
}
|
||||
|
||||
.area_code_choose_box {
|
||||
position: absolute;
|
||||
left: px2rem(66);
|
||||
top: px2rem(82);
|
||||
background: #ffffff;
|
||||
box-shadow: 0 px2rem(4) px2rem(28) 0 rgba(0, 0, 0, 0.08);
|
||||
border-radius: px2rem(8);
|
||||
z-index: 22;
|
||||
|
||||
.area_code_choose_list {
|
||||
max-height: px2rem(420);
|
||||
overflow-y: scroll;
|
||||
|
||||
.area_code_choose {
|
||||
padding: px2rem(24) px2rem(30) 0 px2rem(30);
|
||||
|
||||
.area_code_choose_item {
|
||||
word-break: break-all;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.selected_icon {
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.area_code_choose:last-child {
|
||||
padding-bottom: px2rem(30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.area_code_choose_box:before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: px2rem(-24);
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
border-top: solid px2rem(12) transparent;
|
||||
border-left: solid px2rem(12) transparent;
|
||||
border-right: solid px2rem(12) transparent;
|
||||
border-bottom: solid px2rem(12) #ffffff;
|
||||
}
|
||||
|
||||
.ui-mobile-text {
|
||||
line-height: px2rem(44);
|
||||
}
|
||||
|
||||
.ui-user-input {
|
||||
width: px2rem(480);
|
||||
font-size: px2rem(32);
|
||||
padding: px2rem(24) px2rem(12);
|
||||
border-radius: px2rem(16);
|
||||
background: initial;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
// 去除默认下划线
|
||||
::v-deep(.van-cell:after) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::v-deep(input::-webkit-input-placeholder) {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-no-role-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: px2rem(40) auto px2rem(20) auto;
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
position: relative;
|
||||
z-index: 33;
|
||||
width: px2rem(650);
|
||||
height: px2rem(88);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
margin: px2rem(100) auto px2rem(30) auto;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
width: 100vw;
|
||||
height: px2rem(946);
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
688
src/views/appDir/appUploadReport.vue
Normal file
688
src/views/appDir/appUploadReport.vue
Normal file
@ -0,0 +1,688 @@
|
||||
<template>
|
||||
<div class="ui-appUploadReport">
|
||||
<div class="font_30 color333 bold f-fcl">上传{{ reportType == 0 ? '体检' : '复检' }}报告</div>
|
||||
<div class="ui-report-upload-box">
|
||||
<div v-for="(item, index) in pics" :key="index" class="ui-upload-icon-box flo_l">
|
||||
<img class="ui-upload-icon" :src="item" mode="aspectFill" alt="" @click="ImagePreview(pics, index)" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" mode="widthFix" alt="" @click="clearPic(index)" />
|
||||
</div>
|
||||
<div v-if="pics.length < 9">
|
||||
<uploadPicture :multiple="true" :max-count="9" @on-success="onSuccess" @click="takePhone">
|
||||
<div class="ui-upload">
|
||||
<img class="ui-upload-icon flo_l" src="https://image.fulllinkai.com/202301/07/6c9bc853bed42c9871f56156d1b8f31c.png" alt="" />
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
</div>
|
||||
<div class="font_30 color333 bold f-fcl">上传时间</div>
|
||||
<div class="ui-examine-time" @click="showTime = true">
|
||||
<div class="m_basLst f-fbc">
|
||||
<div class="font_28 color3 f-fcl">
|
||||
<span v-if="time">{{ time }}</span>
|
||||
<span v-else class="color9">请选择你的{{ reportType == 0 ? '体检' : '报告' }}时间</span>
|
||||
</div>
|
||||
<img class="ui-triangle-icon" src="https://image.fulllinkai.com/202301/06/d491373f21f5a0c5810f2167f7c961f0.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="type == '1'">
|
||||
<div class="ui-body-container">
|
||||
<div class="font_30 color3 bold ui-body-container-title">请上传全身照(正、侧面各一张)</div>
|
||||
<div class="ui-body-container-item">
|
||||
<div v-if="!bodyPic.bodyFront" class="ui-upload-box">
|
||||
<uploadPicture :max-count="1" :type="'1'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">正面照</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-else class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.bodyFront, bodyPic.bodyProfile], 0)">
|
||||
<img class="ui-body-pic" :src="bodyPic.bodyFront" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('1', 0, '正面照')" />
|
||||
</div>
|
||||
<div v-if="!bodyPic.bodyProfile" class="ui-upload-box">
|
||||
<uploadPicture :max-count="1" :type="'2'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">侧面照</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-else class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.bodyProfile, bodyPic.bodyFront], 0)">
|
||||
<img class="ui-body-pic" :src="bodyPic.bodyProfile" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('2', 0, '侧面照')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="font_30 color3 bold ui-body-container-title">请上传大头照(正面一张)</div>
|
||||
<div class="ui-body-container-item">
|
||||
<div v-if="!bodyPic.bigHead" class="ui-upload-box">
|
||||
<uploadPicture :max-count="1" :type="'3'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">大头照</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-else class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.bigHead], 0)">
|
||||
<img class="ui-body-pic" :src="bodyPic.bigHead" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('3', 0, '大头照')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="font_30 color3 bold ui-body-container-title">请上传手掌照(左右各一张)</div>
|
||||
<div class="ui-body-container-item">
|
||||
<div v-if="!bodyPic.leftPalm" class="ui-upload-box">
|
||||
<uploadPicture :max-count="1" :type="'4'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">左手掌</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-else class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.leftPalm, bodyPic.rightPalm], 0)">
|
||||
<img class="ui-body-pic" :src="bodyPic.leftPalm" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('4', 0, '左手掌照')" />
|
||||
</div>
|
||||
<div v-if="!bodyPic.rightPalm" class="ui-upload-box">
|
||||
<uploadPicture :max-count="1" :type="'5'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">右手掌</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-else class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.rightPalm, bodyPic.leftPalm], 0)">
|
||||
<img class="ui-body-pic" :src="bodyPic.rightPalm" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('5', 0, '右手掌照')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="font_30 color3 bold ui-body-container-title">请上传伸舌照(正脸伸舌头一张)</div>
|
||||
<div class="ui-body-container-item">
|
||||
<div v-if="!bodyPic.tongue" class="ui-upload-box">
|
||||
<uploadPicture :max-count="1" :type="'6'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">伸舌照</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-else class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.tongue], 0)">
|
||||
<img class="ui-body-pic" :src="bodyPic.tongue" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('6', 0, '伸舌照')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="font_30 color3 bold ui-body-container-title">身体其他问题照(最多九张)</div>
|
||||
<div class="ui-body-container-item f-wrap">
|
||||
<div v-if="bodyPic.other_img.length < 9" class="ui-upload-box">
|
||||
<uploadPicture :max-count="9 - bodyPic.other_img.length" :type="'7'" @on-success="bodyOnSuccess">
|
||||
<div class="ui-add-box">
|
||||
<img class="ui-add-icon" src="https://image.fulllinkai.com/202310/30/c8524b7eb04ac3d525a37aabe283d2eb.png" alt="" />
|
||||
<div class="font_24 color9">其他</div>
|
||||
</div>
|
||||
</uploadPicture>
|
||||
</div>
|
||||
<div v-for="(item, index) in bodyPic.other_img" :key="index" class="ui-body-pic ui-relative" @click="ImagePreview([bodyPic.other_img], index)">
|
||||
<img class="ui-body-pic" :src="item" alt="" />
|
||||
<img class="ui-upload-clear-icon" src="https://image.fulllinkai.com/202301/07/a069d2437562e00d298a9bcd253a86bd.png" alt="" @click.stop="clearBodyPic('7', index, '其他照')" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="color3 font_30 bold">标记异常项</div>
|
||||
<div class="ui-symptom-box">
|
||||
<div class="font_28 color3 bold ui-symptom-title">{{ bloodPressure.title }}</div>
|
||||
<div v-for="(item, index) in bloodPressure.list" :key="index" class="ui-symptom-item" @click="selectSymptom('bloodPressure', 'pitchOnBloodPressure', item, index)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.state == 0" class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-else class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_26 color3">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-symptom-box">
|
||||
<div class="font_28 color3 bold ui-symptom-title">{{ sugar.title }}</div>
|
||||
<div v-for="(item, index) in sugar.list" :key="index" class="ui-symptom-item" @click="selectSymptom('sugar', 'pitchOnSugar', item, index)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.state == 0" class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-else class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_26 color3">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-symptom-box">
|
||||
<div class="font_28 color3 bold ui-symptom-title">{{ renal.title }}</div>
|
||||
<div v-for="(item, index) in renal.list" :key="index" class="ui-symptom-item" @click="selectSymptom('renal', 'pitchOnRenal', item, index)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.state == 0" class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-else class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_26 color3">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-symptom-box">
|
||||
<div class="font_28 color3 bold ui-symptom-title">{{ bloodFat.title }}</div>
|
||||
<div v-for="(item, index) in bloodFat.list" :key="index" class="ui-symptom-item" @click="selectSymptom('bloodFat', 'pitchOnBloodFat', item, index)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.state == 0" class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-else class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_26 color3">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-symptom-box">
|
||||
<div class="font_28 color3 bold ui-symptom-title">{{ bloodRoutine.title }}</div>
|
||||
<div v-for="(item, index) in bloodRoutine.list" :key="index" class="ui-symptom-item" @click="selectSymptom('bloodRoutine', 'pitchOnBloodRoutine', item, index)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.state == 0" class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-else class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_26 color3">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-symptom-box">
|
||||
<div class="font_28 color3 bold ui-symptom-title">{{ liver.title }}</div>
|
||||
<div v-for="(item, index) in liver.list" :key="index" class="ui-symptom-item" @click="selectSymptom('liver', 'pitchOnLiver', item, index)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.state == 0" class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-else class="ui-symptom-icon" src="https://image.fulllinkai.com/202301/07/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
<div class="font_26 color3">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-describe-box">
|
||||
<div class="f-fbc">
|
||||
<div class="font_30 color3 bold">补充描述</div>
|
||||
<div class="font_26 color9">{{ describe.length }}/220</div>
|
||||
</div>
|
||||
<van-field v-model="describe" class="ui-desc-input font_30 color3" rows="5" type="textarea" maxlength="220" show-word-limit placeholder="请您详细描述内容,我们会用心服务每一位用户" autosize />
|
||||
</div>
|
||||
<div class="ui-next-btn font_30 f-fcc colorF" @click="changeData">保存</div>
|
||||
<van-popup v-model:show="showTime" round position="bottom" :duration="0.5">
|
||||
<van-date-picker v-model="timeValues" title="请选择你的体检时间" :min-date="minDate" :max-date="maxDate" @cancel="showTime = false" @confirm="onConfirm" />
|
||||
</van-popup>
|
||||
<van-popup v-model:show="showTips" round :duration="0.5">
|
||||
<div class="ui-tips-box">
|
||||
<div class="color3 font_32 bold text-center">系统扫描体检报告</div>
|
||||
<img class="ui-tips-icon" src="https://image.fulllinkai.com/202305/08/7ef114a85a9476bcbff02d3123246941.png" alt="" />
|
||||
<div class="color6 font_30">系统扫描会自动标记异常项,系统识别需较长时间,完成后会通知您</div>
|
||||
<div class="f-fbc ui-tips-btn-box">
|
||||
<div class="ui-tips-btn font_32 color6 f-fcc" @click="(type = '1'), (showTips = false)">手动标记</div>
|
||||
<div class="ui-tips-btn-v2 font_32 colorF f-fcc" @click="(type = '2'), (showTips = false)">系统识别</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { showConfirmDialog, showImagePreview, showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'AppUploadReport' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const throttle = ref(true);
|
||||
const pics = ref<any[]>([]);
|
||||
const minDate = new Date(new Date().getFullYear() - 120, 0, 1);
|
||||
const maxDate = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate());
|
||||
const showTime = ref(false); // 时间选择弹框key
|
||||
const time = ref('');
|
||||
const timeValues = ref<any[]>([]); // 时间选择框默认值
|
||||
const showTips = ref(false);
|
||||
const num = ref(1);
|
||||
const type = ref('1');
|
||||
const reportType = ref<any>('0'); // 0体检报告,1复检报告
|
||||
const bodyPic = ref<any>({
|
||||
bodyFront: '',
|
||||
bodyProfile: '',
|
||||
bigHead: '',
|
||||
leftPalm: '',
|
||||
rightPalm: '',
|
||||
tongue: '',
|
||||
other_img: [],
|
||||
}); // 身体照
|
||||
|
||||
const pitchOnBloodPressure = ref<any[]>([]);
|
||||
const bloodPressure = ref({
|
||||
title: '血压',
|
||||
list: [
|
||||
{ name: '收缩压', state: 0, value: 'sbp' },
|
||||
{ name: '舒张压', state: 0, value: 'dbp' },
|
||||
],
|
||||
});
|
||||
|
||||
const pitchOnSugar = ref<any[]>([]);
|
||||
const sugar = ref({
|
||||
title: '糖检测',
|
||||
list: [{ name: '空腹血糖', state: 0, value: 'fbg' }],
|
||||
});
|
||||
|
||||
const pitchOnRenal = ref<any[]>([]);
|
||||
const renal = ref({
|
||||
title: '肾功能检测',
|
||||
list: [
|
||||
{ name: '尿素', state: 0, value: 'urea' },
|
||||
{ name: '肌酐', state: 0, value: 'cre' },
|
||||
{ name: '尿酸', state: 0, value: 'ua' },
|
||||
{ name: '胱抑素C', state: 0, value: 'cvsc' },
|
||||
],
|
||||
});
|
||||
|
||||
const pitchOnBloodFat = ref<any[]>([]);
|
||||
const bloodFat = ref({
|
||||
title: '血脂四项检查',
|
||||
list: [
|
||||
{ name: '总胆固醇', state: 0, value: 'tc' },
|
||||
{ name: '甘油三脂', state: 0, value: 'tg' },
|
||||
{ name: '高密度脂蛋白', state: 0, value: 'hdl' },
|
||||
{ name: '低密度脂蛋白', state: 0, value: 'ldl' },
|
||||
],
|
||||
});
|
||||
|
||||
const pitchOnBloodRoutine = ref<any[]>([]);
|
||||
const bloodRoutine = ref({
|
||||
title: '血常规/五分类',
|
||||
list: [
|
||||
{ name: '白细胞计数', state: 0, value: 'wbc' },
|
||||
{ name: '平均RBC血红蛋白量', state: 0, value: 'mch' },
|
||||
{ name: '红细胞计数', state: 0, value: 'rbc' },
|
||||
{ name: 'RBC分布宽度标准差', state: 0, value: 'rdw' },
|
||||
{ name: '红细胞比积', state: 0, value: 'hct' },
|
||||
{ name: '平均RBC血红蛋白浓度', state: 0, value: 'mchc' },
|
||||
{ name: '血小板计数', state: 0, value: 'plt' },
|
||||
{ name: 'RBC分布宽度变异系数', state: 0, value: 'rdwcv' },
|
||||
{ name: '血小板比积', state: 0, value: 'pct' },
|
||||
{ name: '血小板体积分布宽带', state: 0, value: 'pdw' },
|
||||
{ name: '淋巴细胞计数', state: 0, value: 'ly' },
|
||||
{ name: '嗜酸粒细胞计数', state: 0, value: 'eos' },
|
||||
{ name: '大血小板比率', state: 0, value: 'plcr' },
|
||||
{ name: '嗜酸睡粒细胞比值', state: 0, value: 'eos_p' },
|
||||
{ name: '单核细胞计数', state: 0, value: 'mono' },
|
||||
{ name: '中性粒细胞计数', state: 0, value: 'gr' },
|
||||
{ name: '单核细胞比值', state: 0, value: 'mono_p' },
|
||||
{ name: '中性粒细胞比值', state: 0, value: 'gr_p' },
|
||||
{ name: '淋巴细胞比值', state: 0, value: 'ly_p' },
|
||||
{ name: '嗜碱性粒细胞计数', state: 0, value: 'baso' },
|
||||
{ name: '平均RBC体积', state: 0, value: 'mcv' },
|
||||
{ name: '嗜碱性粒细胞比值', state: 0, value: 'baso_p' },
|
||||
{ name: '血红蛋白', state: 0, value: 'hgb' },
|
||||
{ name: '平均血小板体积', state: 0, value: 'mpv' },
|
||||
],
|
||||
});
|
||||
|
||||
const pitchOnLiver = ref<any[]>([]);
|
||||
const liver = ref({
|
||||
title: '肝功三项',
|
||||
list: [
|
||||
{ name: '谷丙转氨酶', state: 0, value: 'alt' },
|
||||
{ name: '谷草转氨酶', state: 0, value: 'ast' },
|
||||
{ name: '总胆红素', state: 0, value: 'tbil' },
|
||||
{ name: '直接胆红素', state: 0, value: 'dbil' },
|
||||
],
|
||||
});
|
||||
|
||||
const obj = reactive({ bloodPressure, pitchOnBloodPressure, sugar, pitchOnSugar, renal, pitchOnRenal, bloodFat, pitchOnBloodFat, bloodRoutine, pitchOnBloodRoutine, liver, pitchOnLiver });
|
||||
|
||||
const describe = ref('');
|
||||
|
||||
const changeData = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
medical_report: pics.value,
|
||||
medical_date: time.value,
|
||||
desc: describe.value,
|
||||
body_images: bodyPic.value,
|
||||
type: reportType.value,
|
||||
anomaly_type: type.value,
|
||||
anomaly: type.value === '1' ? [pitchOnBloodPressure.value, pitchOnSugar.value, pitchOnRenal.value, pitchOnBloodFat.value, pitchOnBloodRoutine.value, pitchOnLiver.value] : [[], [], [], [], [], []],
|
||||
};
|
||||
if (pics.value.length === 0) {
|
||||
showToast('请上传最少一张报告');
|
||||
return;
|
||||
}
|
||||
if (!time.value) {
|
||||
showToast('请选择体检时间');
|
||||
return;
|
||||
}
|
||||
console.log(data, '7777');
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/order/user/health/report`, data, method: 'post' })
|
||||
.then(() => {
|
||||
throttle.value = true;
|
||||
showToast('编辑成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'appViewUserInfo',
|
||||
query: { currentTab: reportType.value == 0 ? 3 : 4 },
|
||||
});
|
||||
router.go(-1);
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 上传报告
|
||||
const onSuccess = (val) => {
|
||||
if (pics.value.length < 9) {
|
||||
pics.value.push(val);
|
||||
}
|
||||
if (num.value === 1) {
|
||||
num.value++;
|
||||
showTips.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
// 删除某个已上传的图片
|
||||
const clearPic = (index) => {
|
||||
pics.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const ImagePreview = (e, index) => {
|
||||
showImagePreview({
|
||||
images: e,
|
||||
showIndex: false,
|
||||
startPosition: index,
|
||||
loop: false,
|
||||
});
|
||||
};
|
||||
|
||||
// 选择异常项
|
||||
const selectSymptom = (type, store, e, index) => {
|
||||
if (e.state === 0) {
|
||||
obj[store].push(e.value);
|
||||
obj[type].list[index].state = 1;
|
||||
} else {
|
||||
obj[store] = obj[store].filter((item) => {
|
||||
return item !== e.value;
|
||||
});
|
||||
obj[type].list[index].state = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// 选择时间
|
||||
const onConfirm = ({ selectedValues }) => {
|
||||
showTime.value = false;
|
||||
time.value = `${selectedValues[0]}-${selectedValues[1]}-${selectedValues[2]}`;
|
||||
};
|
||||
|
||||
// 上传图片
|
||||
const bodyOnSuccess = (val, type) => {
|
||||
console.log(val, type);
|
||||
if (type == 1) {
|
||||
bodyPic.value.bodyFront = val;
|
||||
} else if (type == 2) {
|
||||
bodyPic.value.bodyProfile = val;
|
||||
} else if (type == 3) {
|
||||
bodyPic.value.bigHead = val;
|
||||
} else if (type == 4) {
|
||||
bodyPic.value.leftPalm = val;
|
||||
} else if (type == 5) {
|
||||
bodyPic.value.rightPalm = val;
|
||||
} else if (type == 6) {
|
||||
bodyPic.value.tongue = val;
|
||||
} else {
|
||||
if (bodyPic.value.other_img.length < 9) {
|
||||
bodyPic.value.other_img.push(val);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 删除某个已上传的身体照
|
||||
const clearBodyPic = (type, index, tips) => {
|
||||
showConfirmDialog({
|
||||
title: '温馨提示',
|
||||
message: `是否确认删除${tips}?`,
|
||||
})
|
||||
.then(() => {
|
||||
// on confirm
|
||||
if (type == 1) {
|
||||
bodyPic.value.bodyFront = '';
|
||||
} else if (type == 2) {
|
||||
bodyPic.value.bodyProfile = '';
|
||||
} else if (type == 3) {
|
||||
bodyPic.value.bigHead = '';
|
||||
} else if (type == 4) {
|
||||
bodyPic.value.leftPalm = '';
|
||||
} else if (type == 5) {
|
||||
bodyPic.value.rightPalm = '';
|
||||
} else if (type == 6) {
|
||||
bodyPic.value.tongue = '';
|
||||
} else {
|
||||
bodyPic.value.other_img.splice(index, 1);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
|
||||
window.getAndroidPhone = (e) => {
|
||||
if (e && e.length > 0) {
|
||||
e.forEach((item) => {
|
||||
if (pics.value && pics.value.length < 9) {
|
||||
pics.value.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const takePhone = () => {
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.takePhoto();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
reportType.value = route.reportType;
|
||||
let date = new Date();
|
||||
let m = date.getMonth() > 9 ? `${date.getMonth() + 1}` : `0${date.getMonth() + 1}`;
|
||||
let d = date.getDate() > 9 ? `${date.getDate()}` : `0${date.getDate()}`;
|
||||
timeValues.value = [`${date.getFullYear()}`, m, d];
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appUploadReport {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
padding: px2rem(30);
|
||||
}
|
||||
|
||||
.ui-report-upload-box {
|
||||
overflow: hidden;
|
||||
margin-bottom: px2rem(30);
|
||||
|
||||
.ui-upload-icon-box {
|
||||
position: relative;
|
||||
|
||||
.ui-upload-clear-icon {
|
||||
position: absolute;
|
||||
right: px2rem(30);
|
||||
top: px2rem(30);
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-icon {
|
||||
width: px2rem(180);
|
||||
height: px2rem(180);
|
||||
display: block;
|
||||
border-radius: px2rem(16);
|
||||
margin-right: px2rem(20);
|
||||
margin-top: px2rem(20);
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-examine-time {
|
||||
padding: 0 px2rem(30);
|
||||
margin-bottom: px2rem(50);
|
||||
border-radius: px2rem(24);
|
||||
background: #ffffff;
|
||||
|
||||
.m_basLst {
|
||||
height: px2rem(100);
|
||||
margin-top: px2rem(20);
|
||||
|
||||
.u_name_inp {
|
||||
height: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.inputColor {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.ui-triangle-icon {
|
||||
width: px2rem(10);
|
||||
height: px2rem(20);
|
||||
display: block;
|
||||
margin-left: px2rem(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-body-container {
|
||||
.ui-body-container-title {
|
||||
padding-bottom: px2rem(20);
|
||||
}
|
||||
|
||||
.ui-body-container-item {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
padding-bottom: px2rem(32);
|
||||
|
||||
.ui-upload-box {
|
||||
overflow: hidden;
|
||||
flex-flow: wrap;
|
||||
}
|
||||
|
||||
.ui-body-pic,
|
||||
.ui-add-box {
|
||||
width: px2rem(180);
|
||||
height: px2rem(180);
|
||||
display: block;
|
||||
border: px2rem(2) dashed #dadada;
|
||||
border-radius: px2rem(24);
|
||||
margin-right: px2rem(24);
|
||||
margin-bottom: px2rem(18);
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
}
|
||||
|
||||
.ui-add-box {
|
||||
background: #ffffff;
|
||||
text-align: center;
|
||||
|
||||
.ui-add-icon {
|
||||
width: px2rem(44);
|
||||
height: px2rem(44);
|
||||
display: block;
|
||||
margin: px2rem(42) auto px2rem(14) auto;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-upload-clear-icon {
|
||||
position: absolute;
|
||||
right: px2rem(12);
|
||||
top: px2rem(12);
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
z-index: 99;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-symptom-box {
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
margin: px2rem(20) 0 px2rem(30) 0;
|
||||
|
||||
.ui-symptom-title {
|
||||
margin-bottom: px2rem(-14);
|
||||
}
|
||||
|
||||
.ui-symptom-item {
|
||||
display: inline-block;
|
||||
margin-top: px2rem(36);
|
||||
margin-right: px2rem(40);
|
||||
|
||||
.ui-symptom-icon {
|
||||
width: px2rem(28);
|
||||
height: px2rem(28);
|
||||
display: block;
|
||||
margin-right: px2rem(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-describe-box {
|
||||
padding: px2rem(20) 0 px2rem(200) 0;
|
||||
|
||||
.ui-desc-input {
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
margin-top: px2rem(20);
|
||||
}
|
||||
|
||||
.inputColor {
|
||||
color: #c2c2c2;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-next-btn {
|
||||
position: fixed;
|
||||
bottom: px2rem(90);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: px2rem(560);
|
||||
height: px2rem(80);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.ui-tips-box {
|
||||
width: px2rem(476);
|
||||
padding: px2rem(60) px2rem(62) px2rem(50) px2rem(62);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-tips-icon {
|
||||
width: px2rem(192);
|
||||
height: px2rem(192);
|
||||
display: block;
|
||||
margin: px2rem(30) auto px2rem(40) auto;
|
||||
}
|
||||
|
||||
.ui-tips-btn-box {
|
||||
padding: px2rem(50) px2rem(20) 0 px2rem(20);
|
||||
|
||||
.ui-tips-btn,
|
||||
.ui-tips-btn-v2 {
|
||||
width: px2rem(200);
|
||||
height: px2rem(80);
|
||||
border-radius: px2rem(40);
|
||||
border: px2rem(2) solid #c2c2c2;
|
||||
}
|
||||
|
||||
.ui-tips-btn-v2 {
|
||||
background: #5ac7a0;
|
||||
border: px2rem(2) solid #5ac7a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
2567
src/views/appDir/appUserMenu.vue
Normal file
2567
src/views/appDir/appUserMenu.vue
Normal file
File diff suppressed because it is too large
Load Diff
771
src/views/appDir/appUserMenuAll.vue
Normal file
771
src/views/appDir/appUserMenuAll.vue
Normal file
@ -0,0 +1,771 @@
|
||||
<template>
|
||||
<div class="ui-appUserMenuAll">
|
||||
<div style="background: #f8f8f8">
|
||||
<menuSettingTop :detail="topDetail" @update:date="handleDateChange" @update:mode="handleModeChange"></menuSettingTop>
|
||||
</div>
|
||||
<div class="font_28 color76c ui-pt-16 ui-pb-16 text-center ui-menu-hint">请选择餐单类型后,再点击日期设置餐单*</div>
|
||||
<div class="ui-menu-date-box">
|
||||
<div class="ui-select-box f-fcc">
|
||||
<div v-for="(item, index) in typeList" :key="index" class="ui-select-item f-fcc" :style="{ background: index == typeIndex ? item.bg : '#ffffff' }">
|
||||
<div class="ui-select-li ui-relative colorF" :class="[index === 0 ? 'ui-select-li-one' : index === 1 ? 'ui-select-li-two' : 'ui-select-li-three', index == typeIndex ? item.class : '']" @click="changeType(item, index)">
|
||||
<div class="ui-pl-20 bold" :class="index == typeIndex ? 'ui-pt-28 font_32' : 'ui-pt-32 font_28'">{{ item.name }}</div>
|
||||
<div class="font_24 ui-pl-20 ui-pt-24">已设置:{{ item.day }}天</div>
|
||||
<img v-if="index == typeIndex && typeIndex == 0" class="ui-select-icon" src="https://image.fulllinkai.com/202411/05/ac350a6d384e13515829a50e92809632.png" alt="" />
|
||||
<img v-if="index == typeIndex && typeIndex == 1" class="ui-select-icon" src="https://image.fulllinkai.com/202411/07/0ecb164256dd97c0eddc3a0e73528fc3.png" alt="" />
|
||||
<img v-if="index == typeIndex && typeIndex == 2" class="ui-select-icon" src="https://image.fulllinkai.com/202411/07/0a75f467ed97ff30277eac153f8d3d4b.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-calendar-box">
|
||||
<div class="f-fbc">
|
||||
<div class="font_36 color0E bold">{{ typeData.name }}</div>
|
||||
<div class="font_28 color76c">{{ typeData.day }}天</div>
|
||||
</div>
|
||||
<div class="ui-calendar-date">
|
||||
<div class="calendar_title f-fbc">
|
||||
<img class="triangle_icon" src="https://image.fulllinkai.com/202411/05/fd04e833fb45ca7c14d25a877d4c9581.png" alt="" @click="upMonth" />
|
||||
<div class="font_28 color0E">{{ year }}年{{ month }}月</div>
|
||||
<img class="triangle_icon_v2" src="https://image.fulllinkai.com/202411/05/358000854fe0ad24249b85f64640f9a4.png" alt="" @click="nextMonth" />
|
||||
</div>
|
||||
<div class="week_box">
|
||||
<div v-for="(item, index) in weekList" :key="index" class="font_28 text-center week_num">{{ item }} </div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="calendar_day">
|
||||
<div v-for="index in nbsp" :key="index + '-only'" class="day_number font_28 colorF">空</div>
|
||||
<div v-for="(item, index) in signInList" :key="index" class="day_number">
|
||||
<div v-if="item == 1 || item == 2" class="number_box menu text-center" @click.stop="getMenu(item, index + 1)">
|
||||
<div class="bold font_32 ui-pt-10">{{ index + 1 }}</div>
|
||||
<div class="font_20 ui-text-pt">准备</div>
|
||||
</div>
|
||||
<div v-else-if="item == 3" class="number_box menuV2 text-center" @click.stop="getMenu(item, index + 1)">
|
||||
<div class="bold font_32 ui-pt-10">{{ index + 1 }}</div>
|
||||
<div class="font_20 ui-text-pt">装修</div>
|
||||
</div>
|
||||
<div v-else-if="item == 4" class="number_box menuV3 text-center" @click.stop="getMenu(item, index + 1)">
|
||||
<div class="bold font_32 ui-pt-10">{{ index + 1 }}</div>
|
||||
<div class="font_20 ui-text-pt">清洁</div>
|
||||
</div>
|
||||
<div v-else class="number_box text-center" @click.stop="getMenu(item, index + 1)">
|
||||
<div class="bold font_32 ui-pt-10">{{ index + 1 }}</div>
|
||||
<div class="font_20 colorF ui-text-pt">--</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-btn-box">
|
||||
<div v-if="!modelText && !modeDate" class="ui-save-btn font_32 f-fcc colorF" @click="addMenuAffirm(1)">保存</div>
|
||||
<div v-else class="ui-save-btn font_32 f-fcc colorF" @click="addMenuModal(1)">保存模式并重新生成餐单</div>
|
||||
<div class="ui-save-list" @click="toCheckList">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
|
||||
<path d="M8.88867 1.55566C12.9388 1.55566 16.2217 4.83858 16.2217 8.88867C16.2217 12.9388 12.9388 16.2217 8.88867 16.2217C4.83858 16.2217 1.55566 12.9388 1.55566 8.88867C1.55566 4.83858 4.83858 1.55566 8.88867 1.55566Z" stroke="#66676C" stroke-width="1.33333" />
|
||||
<path d="M8.88867 5.77783V9.33339L10.6664 11.1112" stroke="#66676C" stroke-width="1.33333" stroke-linecap="round" />
|
||||
</svg>
|
||||
<div>变更历史</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-popup v-model:show="showCheck" round :duration="0.5">
|
||||
<div class="menu-setting-change-modal-v2">
|
||||
<div class="menu-setting-change-m-title">模式变更历史</div>
|
||||
<div class="menu-setting-change-m-big-card">
|
||||
<div v-for="(item, index) in operatorList" :key="index" class="menu-setting-change-m-card">
|
||||
<div class="menu-setting-change-m-c-title">修改为「{{ item.model == 1 ? '3+4模式' : '1+6模式' }}」</div>
|
||||
<div class="menu-setting-change-m-c-operator">操作人:{{ item.service_user_name }}</div>
|
||||
<div class="menu-setting-change-m-c-operator">时间:{{ item.create_time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<svg class="close-box" xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 26 26" fill="none" @click="showCheck = false">
|
||||
<g clip-path="url(#clip0_9806_26105)">
|
||||
<path opacity="0.2" d="M13 26C20.1797 26 26 20.1797 26 13C26 5.8203 20.1797 0 13 0C5.8203 0 0 5.8203 0 13C0 20.1797 5.8203 26 13 26Z" fill="white" />
|
||||
<path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M7.64777 7.61515C7.821 7.45127 8.09291 7.45406 8.26273 7.62147L18.4382 17.6524C18.6005 17.8124 18.6024 18.0738 18.4424 18.2362C18.439 18.2396 18.4356 18.2429 18.4321 18.2462C18.2588 18.4101 17.9869 18.4073 17.8171 18.2399L7.64166 8.20903C7.4793 8.04897 7.47743 7.78759 7.63749 7.62522C7.64086 7.62181 7.64428 7.61845 7.64777 7.61515Z" fill="white" stroke="white" stroke-width="0.3" />
|
||||
<path opacity="0.8" fill-rule="evenodd" clip-rule="evenodd" d="M18.3542 7.61515C18.181 7.45127 17.909 7.45406 17.7392 7.62147L7.56378 17.6524C7.40141 17.8124 7.39954 18.0738 7.5596 18.2362C7.56297 18.2396 7.5664 18.2429 7.56988 18.2462C7.74311 18.4101 8.01502 18.4073 8.18485 18.2399L18.3603 8.20903C18.5227 8.04897 18.5245 7.78759 18.3645 7.62522C18.3611 7.62181 18.3577 7.61845 18.3542 7.61515Z" fill="white" stroke="white" stroke-width="0.3" />
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_9806_26105">
|
||||
<rect width="26" height="26" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-popup v-model:show="showChange" round :duration="0.5">
|
||||
<div class="menu-setting-change-modal">
|
||||
<div class="menu-setting-change-m-title">确认生成新餐单</div>
|
||||
<div class="menu-setting-change-m-sub">切换至「{{ modelText }}」后,历史的餐单记录会被更新。</div>
|
||||
<div class="f-fbc color76c ui-examine-box">
|
||||
<div class="ui-examine-btn f-fcc" @click="showChange = false">取消</div>
|
||||
<div class="ui-examine-btn-v2 f-fcc" @click="modalSave()">确认通过</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { showLoadingToast, showToast, showSuccessToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import router from '@/router';
|
||||
import menuSettingTop from '@/components/menuSettingTop.vue';
|
||||
import requestGo from '@/utils/requestGo';
|
||||
|
||||
defineOptions({ name: 'AppUserMenuAll' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const role = ref<any>(0);
|
||||
const isEdit = ref<any>(false);
|
||||
const chiefCoach = ref(false); // 是否主教练身份
|
||||
const viceCoach = ref(false); // 是否副教练身份
|
||||
const service = ref(false); // 是否客服身份
|
||||
const schemeStatus = ref<any>(''); // 方案是否已设置
|
||||
const throttle = ref(true);
|
||||
const isAudit = ref<any>(false); // 用户的餐单模板是否审核
|
||||
const loading = ref(false);
|
||||
const typeList = ref<any>([
|
||||
{ name: '准备日', class: 'ui-select-li-one-act', bg: '#ffeedb', type: 0, day: 0 },
|
||||
{ name: '清洁日', class: 'ui-select-li-two-act', bg: '#e2fcee', type: 2, day: 0 },
|
||||
{ name: '装修日', class: 'ui-select-li-three-act', bg: '#edeaff', type: 1, day: 0 },
|
||||
]);
|
||||
const typeData = ref<any>(typeList.value[0]);
|
||||
const typeIndex = ref<any>(0);
|
||||
const selectList = ref<any[]>([]);
|
||||
const signInList = ref<any[]>([]); // 日历日期
|
||||
const weekList = ref<any[]>(['日', '一', '二', '三', '四', '五', '六']);
|
||||
const currentDate = ref<any>(''); // 当前时间
|
||||
const year = ref<any>(''); // 年
|
||||
const day = ref<any>(''); // 天
|
||||
const month = ref<any>(''); // 月份
|
||||
const date = ref<any>(''); // 日期
|
||||
const nbsp = ref<any>([]); // 空格
|
||||
|
||||
// 获取每日食谱所需数据
|
||||
const getRecipe = () => {
|
||||
signInList.value = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
||||
if (!loading.value) {
|
||||
loading.value = true;
|
||||
weChat({ url: `/h5/user/guides?date=${year.value}-${month.value}&chat_id=${userStore.chatId}&is_im=1&role=1`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
signInList.value = result.date_arr;
|
||||
schemeStatus.value = result.scheme_status;
|
||||
isAudit.value = result.template_is_check;
|
||||
if (result.guides && result.guides.length > 0) {
|
||||
result.guides.forEach((item) => {
|
||||
JSON.parse(
|
||||
JSON.stringify(
|
||||
selectList.value.push({
|
||||
date: item.phase_date,
|
||||
type: item.phase_title == '准备日' ? 0 : item.phase_title == '装修日' ? 1 : 2,
|
||||
}),
|
||||
),
|
||||
);
|
||||
signInList.value.forEach((j, index) => {
|
||||
if (item.phase_date) {
|
||||
if (index == item.phase_date.split('-')[2] * 1 - 1) {
|
||||
if (item.phase_title == '准备日') {
|
||||
signInList.value[index] = 1;
|
||||
} else if (item.phase_title == '装修日') {
|
||||
signInList.value[index] = 3;
|
||||
} else if (item.phase_title == '清洁日') {
|
||||
signInList.value[index] = 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
let arr = selectList.value.map((item) => {
|
||||
return item.date;
|
||||
});
|
||||
selectList.value = selectList.value.filter((item, index) => {
|
||||
return arr.indexOf(item.date) == index;
|
||||
});
|
||||
loading.value = false;
|
||||
})
|
||||
.catch((err) => {
|
||||
loading.value = false;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getSettings = () => {
|
||||
weChat({ url: `/h5/user/guides?date=${year.value}-${month.value}&chat_id=${userStore.chatId}&is_im=1&role=1`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
typeList.value[0].day = result.prepare_count;
|
||||
typeList.value[1].day = result.clean_count;
|
||||
typeList.value[2].day = result.trim_count;
|
||||
typeData.value = typeList.value[typeIndex.value];
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
const addMenuAffirm = (state) => {
|
||||
let m = month.value > 9 ? month.value : `0${month.value}`;
|
||||
let date = `${year.value}-${m}`;
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
guide_arr: selectList.value,
|
||||
month: date,
|
||||
is_send: isEdit.value ? state : 0,
|
||||
};
|
||||
if (throttle.value) {
|
||||
throttle.value = false;
|
||||
if (state) {
|
||||
showLoadingToast('保存中...');
|
||||
}
|
||||
weChat({ url: `h5/batch/set/user/guide`, data, hideLoading: true, method: 'post' })
|
||||
.then(() => {
|
||||
throttle.value = true;
|
||||
if (state) {
|
||||
showToast('保存成功');
|
||||
isEdit.value = false;
|
||||
}
|
||||
getSettings();
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化日期
|
||||
const initCalendar = () => {
|
||||
currentDate.value = new Date(); // 本地时间
|
||||
year.value = currentDate.value.getFullYear(); // 年
|
||||
month.value = currentDate.value.getMonth() + 1; // 月
|
||||
date.value = currentDate.value.getDate(); // 今日
|
||||
day.value = currentDate.value.getDay() + 1; // 天
|
||||
let store = `${year.value}-${month.value}-01`;
|
||||
day.value = new Date(store.replace(/-/g, '/')).getDay(); // 当前月份1号在星期几
|
||||
if (day.value == 0) {
|
||||
nbsp.value = 0;
|
||||
} else {
|
||||
nbsp.value = day.value;
|
||||
}
|
||||
getRecipe();
|
||||
getSettings();
|
||||
};
|
||||
|
||||
// 上一月
|
||||
const upMonth = () => {
|
||||
if (loading.value) {
|
||||
showToast('操作频率过快');
|
||||
return;
|
||||
}
|
||||
year.value = month.value < 2 ? year.value - 1 : year.value;
|
||||
month.value = month.value < 2 ? 12 : month.value - 1;
|
||||
currentDate.value = new Date(year.value, month.value - 1, date.value);
|
||||
let store = `${year.value}-${month.value}-01`;
|
||||
day.value = new Date(store.replace(/-/g, '/')).getDay(); // 当前月份1号在星期几
|
||||
if (day.value == 0) {
|
||||
nbsp.value = 0;
|
||||
} else {
|
||||
nbsp.value = day.value;
|
||||
}
|
||||
getRecipe();
|
||||
getSettings();
|
||||
};
|
||||
|
||||
// 下一月
|
||||
const nextMonth = () => {
|
||||
if (loading.value) {
|
||||
showToast('操作频率过快');
|
||||
return;
|
||||
}
|
||||
year.value = month.value > 11 ? year.value + 1 : year.value;
|
||||
month.value = month.value > 11 ? 1 : month.value + 1;
|
||||
currentDate.value = new Date(year.value, month.value - 1, date.value);
|
||||
day.value = currentDate.value.getDay() + 1;
|
||||
let store = `${year.value}-${month.value}-01`;
|
||||
day.value = new Date(store.replace(/-/g, '/')).getDay(); // 当前月份1号在星期几
|
||||
if (day.value == 0) {
|
||||
nbsp.value = 0;
|
||||
} else {
|
||||
nbsp.value = day.value;
|
||||
}
|
||||
getRecipe();
|
||||
getSettings();
|
||||
};
|
||||
|
||||
// 选择日期获取对应餐单
|
||||
const getMenu = (item, index) => {
|
||||
let m = month.value > 9 ? month.value : `0${month.value}`;
|
||||
console.log(month.value, '-==');
|
||||
let d = index > 9 ? index : `0${index}`;
|
||||
let t = `${year.value}-${m}-${d}`;
|
||||
// 选择相同日期并且类型相同时取消选中
|
||||
if ((signInList.value[index - 1] == 1 || signInList.value[index - 1] == 2) && typeIndex.value == 0) {
|
||||
signInList.value[index - 1] = 0;
|
||||
} else if (signInList.value[index - 1] == 3 && typeIndex.value == 2) {
|
||||
signInList.value[index - 1] = 0;
|
||||
} else if (signInList.value[index - 1] == 4 && typeIndex.value == 1) {
|
||||
signInList.value[index - 1] = 0;
|
||||
} else {
|
||||
if (typeIndex.value == 0) {
|
||||
signInList.value[index - 1] = 1;
|
||||
} else if (typeIndex.value == 1) {
|
||||
signInList.value[index - 1] = 4;
|
||||
} else {
|
||||
signInList.value[index - 1] = 3;
|
||||
}
|
||||
let state = false;
|
||||
// 选中已有的日期时,不同类型直接更改已有数据类型
|
||||
selectList.value.forEach((item, idx) => {
|
||||
if (item.date == t) {
|
||||
state = true;
|
||||
selectList.value[idx] = { date: t, type: typeIndex.value == 0 ? typeIndex.value : typeIndex.value == 1 ? 2 : 1 };
|
||||
}
|
||||
});
|
||||
// 选中已没有的日期时,新增数据
|
||||
if (!state) {
|
||||
selectList.value.push({ date: t, type: typeIndex.value == 0 ? typeIndex.value : typeIndex.value == 1 ? 2 : 1 });
|
||||
}
|
||||
if (item != signInList.value[index - 1]) {
|
||||
isEdit.value = true;
|
||||
}
|
||||
addMenuAffirm(0);
|
||||
return;
|
||||
}
|
||||
if (item != signInList.value[index - 1]) {
|
||||
isEdit.value = true;
|
||||
}
|
||||
// 选择相同日期并且类型相同时去除数据
|
||||
selectList.value.forEach((item, index) => {
|
||||
if (item.date == t) {
|
||||
selectList.value.splice(index, 1);
|
||||
}
|
||||
});
|
||||
addMenuAffirm(0);
|
||||
};
|
||||
|
||||
const changeType = (e, index) => {
|
||||
typeData.value = e;
|
||||
typeIndex.value = index;
|
||||
};
|
||||
|
||||
// 查看历史记录
|
||||
const showCheck = ref(false);
|
||||
const toCheckList = () => {
|
||||
showCheck.value = true;
|
||||
};
|
||||
|
||||
const modelText = ref('');
|
||||
const modeDate = ref('');
|
||||
const showChange = ref(false); // 展示确认弹窗
|
||||
const handleDateChange = (e, e2) => {
|
||||
console.log(e, 'e1--');
|
||||
modeDate.value = e;
|
||||
modelText.value = e2;
|
||||
};
|
||||
const handleModeChange = (e, e2) => {
|
||||
console.log(e, 'e2--');
|
||||
modelText.value = e;
|
||||
modeDate.value = e2;
|
||||
};
|
||||
const addMenuModal = () => {
|
||||
showChange.value = true;
|
||||
};
|
||||
const modalSave = async () => {
|
||||
if (!modelText.value) {
|
||||
showToast('请选择模式');
|
||||
return;
|
||||
}
|
||||
if (!modeDate.value) {
|
||||
showToast('请选择日期');
|
||||
return;
|
||||
}
|
||||
let data = {
|
||||
model: modelText.value == '减肥型' ? 1 : 2,
|
||||
start_date: modeDate.value,
|
||||
service_user_id: userStore.serviceUserId - 0,
|
||||
};
|
||||
console.log(data, 'd===');
|
||||
let res = await requestGo({ url: `app/server/model/set/guide?chat_id=${userStore.chatId}`, method: 'post', data });
|
||||
if (res.code == 0) {
|
||||
showSuccessToast('修改成功');
|
||||
showChange.value = false;
|
||||
initCalendar();
|
||||
}
|
||||
};
|
||||
|
||||
const topDetail = ref();
|
||||
const operatorList = ref([]);
|
||||
|
||||
const getUserDetail = async () => {
|
||||
let res = await requestGo({ url: `app/server/model/guide/info?chat_id=${userStore.chatId}` });
|
||||
topDetail.value = res.data;
|
||||
};
|
||||
|
||||
const getOperatorList = async () => {
|
||||
let res = await requestGo({ url: `app/server/model/guide/log?chat_id=${userStore.chatId}` });
|
||||
console.log(res, 'rrr===');
|
||||
operatorList.value = res.data;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
if (role.value) {
|
||||
role.value.split(',').forEach((item) => {
|
||||
if (item == '1') {
|
||||
chiefCoach.value = true;
|
||||
}
|
||||
if (item == '2') {
|
||||
viceCoach.value = true;
|
||||
}
|
||||
if (item == '3') {
|
||||
service.value = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (route.chat_id) {
|
||||
userStore.chatId = route.chat_id;
|
||||
}
|
||||
initCalendar();
|
||||
getUserDetail();
|
||||
getOperatorList();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.ui-appUserMenuAll {
|
||||
.van-popup {
|
||||
overflow-y: initial;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appUserMenuAll {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-menu-hint {
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.ui-menu-date-box {
|
||||
padding: px2rem(32) 0 px2rem(260) 0;
|
||||
background: #ffffff;
|
||||
|
||||
.ui-select-box {
|
||||
.ui-select-item {
|
||||
width: px2rem(236);
|
||||
height: px2rem(198);
|
||||
border-radius: px2rem(28) px2rem(92) px2rem(28) px2rem(28);
|
||||
background: #ffffff;
|
||||
|
||||
.ui-select-li {
|
||||
width: px2rem(196);
|
||||
height: px2rem(160);
|
||||
border-radius: px2rem(20) px2rem(72) px2rem(20) px2rem(20);
|
||||
|
||||
.ui-select-icon {
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-select-li-one {
|
||||
background: #ffa949;
|
||||
}
|
||||
|
||||
.ui-select-li-two {
|
||||
background: #18ca6e;
|
||||
}
|
||||
|
||||
.ui-select-li-three {
|
||||
background: #9679ff;
|
||||
}
|
||||
|
||||
.ui-select-li-one-act:before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
bottom: px2rem(-26);
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
border-top: solid px2rem(14) #ffa949;
|
||||
border-left: solid px2rem(14) transparent;
|
||||
border-right: solid px2rem(14) transparent;
|
||||
border-bottom: solid px2rem(14) transparent;
|
||||
}
|
||||
|
||||
.ui-select-li-two-act:before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
bottom: px2rem(-26);
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
border-top: solid px2rem(14) #18ca6e;
|
||||
border-left: solid px2rem(14) transparent;
|
||||
border-right: solid px2rem(14) transparent;
|
||||
border-bottom: solid px2rem(14) transparent;
|
||||
}
|
||||
|
||||
.ui-select-li-three-act:before {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
bottom: px2rem(-26);
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
border-top: solid px2rem(14) #9679ff;
|
||||
border-left: solid px2rem(14) transparent;
|
||||
border-right: solid px2rem(14) transparent;
|
||||
border-bottom: solid px2rem(14) transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-title {
|
||||
color: #fdae18;
|
||||
}
|
||||
|
||||
.ui-title-v2 {
|
||||
color: #148a5f;
|
||||
}
|
||||
|
||||
.ui-title-v3 {
|
||||
color: #00c897;
|
||||
}
|
||||
|
||||
.ui-calendar-box {
|
||||
height: 100%;
|
||||
padding: 0 px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24) px2rem(24) 0 0;
|
||||
margin-top: px2rem(40);
|
||||
|
||||
.ui-calendar-date {
|
||||
background: linear-gradient(to bottom, #f8f8f8 0%, #ffffff 100%);
|
||||
height: px2rem(174);
|
||||
border-radius: px2rem(32) px2rem(32) 0 0;
|
||||
margin: px2rem(20) 0 px2rem(40) 0;
|
||||
|
||||
.calendar_title {
|
||||
padding: 0 px2rem(20);
|
||||
height: px2rem(94);
|
||||
|
||||
.triangle_icon,
|
||||
.triangle_icon_v2 {
|
||||
width: px2rem(54);
|
||||
height: px2rem(54);
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.week_box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
padding-top: px2rem(40);
|
||||
|
||||
.week_num {
|
||||
width: 14.2%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.calendar_day {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.day_number {
|
||||
width: 14.2%;
|
||||
padding-bottom: px2rem(20);
|
||||
|
||||
.number_box {
|
||||
width: px2rem(76);
|
||||
height: px2rem(88);
|
||||
border-radius: px2rem(12);
|
||||
background: #ffffff;
|
||||
margin: 0 auto;
|
||||
|
||||
.ui-text-pt {
|
||||
padding-top: px2rem(2);
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
color: #ffa949;
|
||||
background: #ffeedb;
|
||||
}
|
||||
|
||||
.menuV2 {
|
||||
color: #8670ff;
|
||||
background: #f3f1ff;
|
||||
}
|
||||
|
||||
.menuV3 {
|
||||
color: #18ca6e;
|
||||
background: #e2fcee;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.calendar_day:after {
|
||||
content: '';
|
||||
flex: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-save-btn {
|
||||
width: px2rem(686);
|
||||
height: px2rem(100);
|
||||
border-radius: px2rem(50);
|
||||
background: #18ca6e;
|
||||
}
|
||||
.ui-btn-box {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: -webkit-fill-available;
|
||||
display: flex;
|
||||
padding: 10px 10px 30px 10px;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: #fff;
|
||||
}
|
||||
.ui-save-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
color: #66676c;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.menu-setting-change-modal {
|
||||
width: 284px;
|
||||
padding-top: 26px;
|
||||
gap: 20px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
backdrop-filter: blur(18px);
|
||||
|
||||
.menu-setting-change-m-title {
|
||||
margin-bottom: 12px;
|
||||
text-align: center;
|
||||
color: #0e0e0e;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
line-height: normal;
|
||||
}
|
||||
.menu-setting-change-m-sub {
|
||||
padding: 0 12px 20px;
|
||||
color: #0e0e0e;
|
||||
text-align: justify;
|
||||
font-size: 15px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 25px;
|
||||
}
|
||||
.ui-examine-box {
|
||||
height: 50px; /* 原px2rem(100) */
|
||||
border-top: 1px solid #eeeeee; /* 原px2rem(2) */
|
||||
font-size: 16px;
|
||||
|
||||
.ui-examine-btn {
|
||||
position: relative;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.ui-examine-btn:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
width: 1px; /* 原px2rem(2) */
|
||||
height: 50px; /* 原px2rem(100) */
|
||||
background: #eeeeee;
|
||||
}
|
||||
.ui-examine-btn-v2 {
|
||||
width: 50%;
|
||||
color: #16ca6e;
|
||||
}
|
||||
|
||||
.ui-examine-btn-v3 {
|
||||
width: 50%;
|
||||
color: #ff2946;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-setting-change-modal-v2 {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 26px 20px;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 284px;
|
||||
|
||||
.menu-setting-change-m-title {
|
||||
margin-bottom: 12px;
|
||||
color: #0e0e0e;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
.menu-setting-change-m-big-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
gap: 16px;
|
||||
max-height: 400px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.menu-setting-change-m-card {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
align-self: stretch;
|
||||
border-radius: 10px;
|
||||
border: 0.3px solid #ddd;
|
||||
.menu-setting-change-m-c-title {
|
||||
color: #0e0e0e;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: normal;
|
||||
}
|
||||
.menu-setting-change-m-c-operator {
|
||||
color: #66676c;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.close-box {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 50%;
|
||||
transform: translate(-50%);
|
||||
}
|
||||
</style>
|
||||
1923
src/views/appDir/appViewUserInfo.vue
Normal file
1923
src/views/appDir/appViewUserInfo.vue
Normal file
File diff suppressed because it is too large
Load Diff
196
src/views/appDir/appWorkOrder.vue
Normal file
196
src/views/appDir/appWorkOrder.vue
Normal file
@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div ref="scrollDistance" class="ui-appWorkOrder" @scroll="handleScroll">
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="getList">
|
||||
<div v-if="loadingState && list.length == 0">
|
||||
<img class="ui-no-data-icon" src="https://image.fulllinkai.com/202306/07/247d8ae6b90334457d1b39129cd5c490.png" alt="" />
|
||||
<div class="color6 font_30 text-center">暂无工单</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-work-order-box">
|
||||
<div class="ui-work-order-item">
|
||||
<div class="f-fbc">
|
||||
<div class="color3 font_26">工单号:{{ item.work_order }}</div>
|
||||
<div v-if="item.status == 0" class="font_26 f-fcc colorTheme">已提交</div>
|
||||
<div v-else-if="item.status == 1" class="font_26 f-fcc color3">处理中</div>
|
||||
<div v-else-if="item.status == 2" class="font_26 f-fcc colorTheme">已处理</div>
|
||||
<div v-else class="font_26 f-fcc colorPrice">已取消</div>
|
||||
</div>
|
||||
<div class="ui-line-between"></div>
|
||||
<div class="ui-container">
|
||||
<div class="font_28 color3 bold">工单说明</div>
|
||||
<div class="ui-explain-text font_28 color3">{{ item.desc }}</div>
|
||||
<div v-if="item.images && item.images.length > 0" class="ui-explain-pic-box f-fcl">
|
||||
<img v-for="(itemV2, indexV2) in item.images" :key="indexV2" class="ui-explain-pic" :src="itemV2" alt="" @click="ImagePreview(item.images, indexV2)" />
|
||||
</div>
|
||||
<div v-if="item.deal_remark">
|
||||
<div class="font_28 color3 bold ui-pt-40">解决方案</div>
|
||||
<div class="ui-explain-text font_28 color3">{{ item.deal_remark }}</div>
|
||||
<div v-if="item.deal_images && item.deal_images.length > 0" class="ui-explain-pic-box f-fcl">
|
||||
<img v-for="(itemV2, indexV2) in item.deal_images" :key="indexV2" class="ui-explain-pic" :src="itemV2" alt="" @click="ImagePreview(item.deal_images, indexV2)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="noMore" class="ui-no-more font_24 color6 text-center">我也是有底线的</div>
|
||||
</div>
|
||||
<div class="ui-add-btn f-fcc colorF font_30 bold" @click="jumpPath('appAddWorkOrder')">添加工单</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, onActivated } from 'vue';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
|
||||
defineOptions({ name: 'AppWorkOrder' });
|
||||
|
||||
const userStore = useUserStore() as any; // pinia状态缓存数据
|
||||
const loadingState = ref<any>(false); // 页面数据加载完成判断
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const noMore = ref(false); // 没有更多数据
|
||||
const refreshing = ref(false); // 上拉刷新false表示加载完成
|
||||
const finished = ref(false); // true表示数据全部加载完成
|
||||
const loading = ref(false); // false表示数据加载完成
|
||||
const page = ref(1); // 数据分页
|
||||
|
||||
const scrollValue = ref(0); // 记录页面列表的滚动距离
|
||||
const scrollDistance = ref<any>(null);
|
||||
|
||||
// 获取工单列表
|
||||
const getList = () => {
|
||||
weChat({ url: `/h5/get/work/order?page=${page.value}&chat_id=${userStore.chatId}`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
if (list.value.length === 0 || page.value === 1) {
|
||||
list.value = result.data;
|
||||
} else if (list.value.length >= 15) {
|
||||
result.data.forEach((item) => {
|
||||
list.value.push(item);
|
||||
});
|
||||
}
|
||||
refreshing.value = false;
|
||||
loading.value = false;
|
||||
if (list.value.length < 15 || result.data.length < 15) {
|
||||
finished.value = true;
|
||||
noMore.value = true;
|
||||
}
|
||||
loadingState.value = true;
|
||||
page.value++;
|
||||
})
|
||||
.catch((err) => {
|
||||
loadingState.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 上拉初始化数据
|
||||
const onRefresh = () => {
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
finished.value = false;
|
||||
loading.value = true;
|
||||
getList();
|
||||
};
|
||||
|
||||
const jumpPath = (url) => {
|
||||
router.push({
|
||||
name: url,
|
||||
});
|
||||
};
|
||||
|
||||
// 监听页面滚动距离
|
||||
const handleScroll = (event) => {
|
||||
scrollValue.value = event.target.scrollTop;
|
||||
};
|
||||
|
||||
onActivated(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
// 提交工单后重新请求接口
|
||||
if (route.state) {
|
||||
onRefresh();
|
||||
} else {
|
||||
// 赋值离开时的滚动距离
|
||||
scrollDistance.value.scrollTop = scrollValue.value;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-appWorkOrder {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-work-order-box {
|
||||
margin: px2rem(24) px2rem(30) px2rem(30) px2rem(30);
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-work-order-item {
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-line-between {
|
||||
width: 100%;
|
||||
height: px2rem(2);
|
||||
background: #f8f8f8;
|
||||
margin-top: px2rem(24);
|
||||
}
|
||||
|
||||
.ui-container {
|
||||
padding-top: px2rem(30);
|
||||
|
||||
.ui-explain-text {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
word-break: normal;
|
||||
padding-top: px2rem(16);
|
||||
}
|
||||
|
||||
.ui-explain-pic-box {
|
||||
flex-flow: wrap;
|
||||
|
||||
.ui-explain-pic {
|
||||
width: px2rem(140);
|
||||
height: px2rem(140);
|
||||
display: block;
|
||||
object-fit: cover;
|
||||
object-position: center;
|
||||
margin-right: px2rem(16);
|
||||
margin-top: px2rem(16);
|
||||
border-radius: px2rem(12);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-no-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: 24vh auto px2rem(20) auto;
|
||||
}
|
||||
|
||||
.ui-no-more {
|
||||
padding: px2rem(20) px2rem(20) px2rem(220) px2rem(20);
|
||||
}
|
||||
|
||||
.ui-add-btn {
|
||||
position: fixed;
|
||||
bottom: px2rem(80);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: px2rem(560);
|
||||
height: px2rem(80);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
}
|
||||
</style>
|
||||
289
src/views/appDir/bindEnterprise.vue
Normal file
289
src/views/appDir/bindEnterprise.vue
Normal file
@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<div v-if="loading" class="ui-boundEnterprise">
|
||||
<div>
|
||||
<div class="ui-title font_48 color3 bold">绑定订单职责</div>
|
||||
<div class="font_28 color3 ui-pl-32 ui-pb-32">当前可绑定职责:</div>
|
||||
<div v-if="typeList && typeList.length > 0" class="ui-item-select">
|
||||
<div v-for="(item, index) in typeList" :key="index" class="f-fbc ui-pb-36" @click="selectChange(item)">
|
||||
<div class="f-fcl">
|
||||
<img v-if="item.name == '主教练'" class="ui-duty-icon" src="https://image.fulllinkai.com/202305/06/103763ce22be9a1bd964080a6b45bdf0.png" alt="" />
|
||||
<img v-else-if="item.name == '副教练'" class="ui-duty-icon" src="https://image.fulllinkai.com/202305/06/b73e57901f943fa31eec78b21c8e8eac.png" alt="" />
|
||||
<img v-else-if="item.name == '客服'" class="ui-duty-icon" src="https://image.fulllinkai.com/202305/06/6a96db6ec2a77503b84e6213f9d905a6.png" alt="" />
|
||||
<img v-else class="ui-duty-icon" src="https://image.fulllinkai.com/202305/06/697595eb7b086798bef1706563ce3b92.png" alt="" />
|
||||
<div class="font_32 color3 ui-item-select-text">{{ item.name }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<img v-show="!item.state" class="ui-item-select-icon" src="https://image.fulllinkai.com/202306/25/ba1565f2cf6d59945ec6f522c8caaa5d.png" alt="" />
|
||||
<img v-show="item.state" class="ui-item-select-icon" src="https://image.fulllinkai.com/202306/25/1c57dd3dd3d4062255c159d73a437b83.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-btn f-fcc font_30 colorF bold" @click="saveRole">确认绑定</div>
|
||||
<div class="font_24 color9 text-center">绑定后可使用该职责</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<img class="ui-no-role-icon" src="https://image.fulllinkai.com/202306/07/247d8ae6b90334457d1b39129cd5c490.png" alt="" />
|
||||
<div class="font_30 color9 text-center">暂无可绑定的职责</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ui-skeleton">
|
||||
<div class="f-fcl">
|
||||
<div class="ui-skeleton-photo"></div>
|
||||
<div>
|
||||
<div class="ui-skeleton-name"></div>
|
||||
<div class="ui-skeleton-name-v2"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui-skeleton-line"></div>
|
||||
<div v-for="(item, index) in skeleton" :key="index" class="f-fcl">
|
||||
<div class="ui-skeleton-item">{{ item }}</div>
|
||||
<div class="ui-skeleton-item-v2"></div>
|
||||
</div>
|
||||
<div class="font_28 color9 text-center">-- 加载中 --</div>
|
||||
</div>
|
||||
<img class="ui-icon" src="https://image.fulllinkai.com/202306/26/05b70e1b55dfed082b28fda935653343.png" alt="" />
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { showToast } from 'vant';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
defineOptions({ name: 'BoundEnterprise' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const loading = ref(false);
|
||||
const isBind = ref<any>(''); // 是否绑定企业微信
|
||||
const isRole = ref<any>(''); // 群是否绑定职责
|
||||
const isRoleIdList = ref<any[]>([]); // 选择跟群绑定的职责
|
||||
const typeList = ref<any[]>([]);
|
||||
const throttle = ref<any>(true);
|
||||
const skeleton = ref<any[]>(['', '', '', '', '', '']);
|
||||
|
||||
// 获取是否绑定企业微信 is_bind: true, false. 是否绑定职责is_role: true, false. 可绑定的职责列表 role_list
|
||||
const getState = () => {
|
||||
weChat({ url: `/h5/work/${userStore.chatId}/group/orders/status`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
userStore.weChatBindState = result.status;
|
||||
userStore.chiefCoach = result.is_main_coach;
|
||||
userStore.coach = result.is_coach;
|
||||
userStore.service = result.is_customer;
|
||||
isBind.value = result.is_bind;
|
||||
isRole.value = result.is_role;
|
||||
if (isRole.value) {
|
||||
router.replace({
|
||||
name: 'personalCenter',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (result.role_list && result.role_list.length > 0) {
|
||||
result.role_list.forEach((item) => {
|
||||
typeList.value.push({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
state: 0,
|
||||
});
|
||||
});
|
||||
}
|
||||
loading.value = true;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 为群绑定职责
|
||||
const saveRole = () => {
|
||||
if (throttle.value) {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
role_ids: isRoleIdList.value,
|
||||
};
|
||||
if (isRoleIdList.value.length === 0) {
|
||||
showToast('请选择需要绑定的职责');
|
||||
return;
|
||||
}
|
||||
throttle.value = false;
|
||||
weChat({ url: `h5/order/bind/role`, data, method: 'post' })
|
||||
.then((res) => {
|
||||
let result = res.data;
|
||||
console.log(result, '7777');
|
||||
showToast('绑定成功');
|
||||
setTimeout(() => {
|
||||
throttle.value = true;
|
||||
router.replace({
|
||||
name: 'personalCenter',
|
||||
});
|
||||
}, 1000);
|
||||
})
|
||||
.catch((err) => {
|
||||
throttle.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const selectChange = (e) => {
|
||||
console.log(e, '7777');
|
||||
if (e.state === 0) {
|
||||
isRoleIdList.value.push(e.id);
|
||||
e.state = 1;
|
||||
} else {
|
||||
isRoleIdList.value = isRoleIdList.value.filter((id) => {
|
||||
return id !== e.id;
|
||||
});
|
||||
e.state = 0;
|
||||
}
|
||||
console.log(isRoleIdList.value);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
console.log(route);
|
||||
if (route.chat_id) {
|
||||
userStore.chatId = route.chat_id;
|
||||
}
|
||||
userStore.serviceUserId = route.service_user_id;
|
||||
getState();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-boundEnterprise {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-title {
|
||||
padding: px2rem(120) px2rem(0) px2rem(70) px2rem(50);
|
||||
}
|
||||
|
||||
.ui-item-select {
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
padding: 0 px2rem(30);
|
||||
|
||||
.ui-duty-icon {
|
||||
width: px2rem(40);
|
||||
height: px2rem(40);
|
||||
display: block;
|
||||
margin-right: px2rem(4);
|
||||
}
|
||||
|
||||
.ui-item-select-icon {
|
||||
width: px2rem(36);
|
||||
height: px2rem(36);
|
||||
display: block;
|
||||
margin-right: px2rem(10);
|
||||
}
|
||||
|
||||
.ui-item-select-text {
|
||||
line-height: px2rem(40);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-mobile {
|
||||
position: relative;
|
||||
z-index: 33;
|
||||
margin: px2rem(0) px2rem(50);
|
||||
border-bottom: px2rem(2) solid #d8d8d8;
|
||||
|
||||
.ui-mobile-text {
|
||||
line-height: px2rem(44);
|
||||
}
|
||||
|
||||
.ui-user-input {
|
||||
width: px2rem(480);
|
||||
font-size: px2rem(32);
|
||||
padding: px2rem(24) px2rem(12);
|
||||
border-radius: px2rem(16);
|
||||
background: initial;
|
||||
line-height: initial;
|
||||
}
|
||||
|
||||
// 去除默认下划线
|
||||
::v-deep(.van-cell:after) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
::v-deep(input::-webkit-input-placeholder) {
|
||||
color: #999999;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-no-role-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: px2rem(40) auto px2rem(20) auto;
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
position: relative;
|
||||
z-index: 33;
|
||||
width: px2rem(650);
|
||||
height: px2rem(88);
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(40);
|
||||
margin: px2rem(100) auto px2rem(30) auto;
|
||||
}
|
||||
|
||||
.ui-icon {
|
||||
width: 100vw;
|
||||
height: px2rem(946);
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ui-skeleton {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: px2rem(60);
|
||||
|
||||
.ui-skeleton-photo {
|
||||
width: px2rem(120);
|
||||
height: px2rem(120);
|
||||
border-radius: 50%;
|
||||
background: #f5f5f5;
|
||||
display: block;
|
||||
margin-right: px2rem(40);
|
||||
}
|
||||
|
||||
.ui-skeleton-name,
|
||||
.ui-skeleton-name-v2 {
|
||||
width: px2rem(160);
|
||||
height: px2rem(30);
|
||||
border-radius: px2rem(16);
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.ui-skeleton-name-v2 {
|
||||
width: px2rem(240);
|
||||
margin-top: px2rem(16);
|
||||
}
|
||||
|
||||
.ui-skeleton-line {
|
||||
width: 100%;
|
||||
height: px2rem(2);
|
||||
background: #f5f5f5;
|
||||
margin: px2rem(30) 0 px2rem(60) 0;
|
||||
}
|
||||
|
||||
.ui-skeleton-item,
|
||||
.ui-skeleton-item-v2 {
|
||||
width: px2rem(52);
|
||||
height: px2rem(52);
|
||||
border-radius: px2rem(26);
|
||||
background: #f5f5f5;
|
||||
margin-bottom: px2rem(60);
|
||||
}
|
||||
|
||||
.ui-skeleton-item-v2 {
|
||||
width: px2rem(480);
|
||||
margin-left: px2rem(22);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
460
src/views/appDir/personalCenter.vue
Normal file
460
src/views/appDir/personalCenter.vue
Normal file
@ -0,0 +1,460 @@
|
||||
<template>
|
||||
<div class="ui-serviceCentre">
|
||||
<div class="ui-service-list-box">
|
||||
<div class="ui-service-top">
|
||||
<div v-if="!name && roles.length == 0" class="f-fcl">
|
||||
<img class="ui-logo-icon" src="https://image.fulllinkai.com/202306/26/b1e72421e6ff68eacbdb43816794048c.png" alt="" />
|
||||
<div class="font_32 color3 bold">还未绑定角色,请<span class="colorTheme" @click="contactService">联系</span>管理员</div>
|
||||
</div>
|
||||
<div v-else-if="!name">
|
||||
<div class="f-fcl">
|
||||
<img class="ui-logo-icon" src="https://image.fulllinkai.com/202306/26/b1e72421e6ff68eacbdb43816794048c.png" alt="" />
|
||||
<div>
|
||||
<div class="font_32 color3 bold">友福同享 客服系统</div>
|
||||
<div v-if="roles && roles.length > 0" class="f-fcl">
|
||||
<div v-for="(item, index) in roles" :key="index" class="ui-duty-box f-fcl">
|
||||
<img v-if="item == '主教练'" class="ui-duty-icon" src="https://image.fulllinkai.com/202306/26/103763ce22be9a1bd964080a6b45bdf0.png" alt="" />
|
||||
<img v-else-if="item == '副教练'" class="ui-duty-icon" src="https://image.fulllinkai.com/202306/26/b73e57901f943fa31eec78b21c8e8eac.png" alt="" />
|
||||
<img v-else class="ui-duty-icon" src="https://image.fulllinkai.com/202306/26/6a96db6ec2a77503b84e6213f9d905a6.png" alt="" />
|
||||
<div class="font_24 color6">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="userStore.weChatBindState == 2" class="font_22 color9 ui-pt-8">订单号:{{ orderNum || '--' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="ui-user-box">
|
||||
<img class="ui-logo-icon" src="https://image.fulllinkai.com/202306/26/b1e72421e6ff68eacbdb43816794048c.png" alt="" />
|
||||
<div>
|
||||
<div class="font_32 bold color3">{{ name }}</div>
|
||||
<div class="font_28 color6 ui-pt-6">手机号:{{ mobile }}</div>
|
||||
<div v-if="roles && roles.length > 0" class="f-fcl">
|
||||
<div v-for="(item, index) in roles" :key="index" class="ui-duty-box f-fcl">
|
||||
<img v-if="item == '主教练'" class="ui-duty-icon" src="https://image.fulllinkai.com/202306/26/103763ce22be9a1bd964080a6b45bdf0.png" alt="" />
|
||||
<img v-else-if="item == '副教练'" class="ui-duty-icon" src="https://image.fulllinkai.com/202306/26/b73e57901f943fa31eec78b21c8e8eac.png" alt="" />
|
||||
<img v-else class="ui-duty-icon" src="https://image.fulllinkai.com/202306/26/6a96db6ec2a77503b84e6213f9d905a6.png" alt="" />
|
||||
<div class="font_24 color6">{{ item }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="font_24 color9 ui-pt-6">未绑定角色,请<span class="colorTheme" @click="contactService">联系</span>管理员</div>
|
||||
<div v-if="userStore.weChatBindState == 2" class="font_22 color9 ui-pt-8">订单号:{{ orderNum || '--' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item, index) in list" :key="index" class="ui-service-item f-fbc" @click="jumpPath(item)">
|
||||
<div class="f-fcl">
|
||||
<img class="ui-service-item-icon" :src="item.icon" alt="" />
|
||||
<div class="font_30 color3">{{ index + 1 }}. {{ item.name }}</div>
|
||||
</div>
|
||||
<div class="f-fcr">
|
||||
<div v-if="item.name == '查看用户信息' && dataState" class="font_26 color6 ui-jump-mp-text">
|
||||
<span v-if="dataState == 'NOINFO'">信息未完善</span>
|
||||
<span v-else-if="dataState == 'COMPLETEINFO'">方案待设置</span>
|
||||
<span v-else-if="dataState == 'NOSCHEME'">方案待设置</span>
|
||||
<span v-else-if="dataState == 'SCHEME'">配送待完成</span>
|
||||
<span v-else-if="dataState == 'FINISHED'">配送已完成</span>
|
||||
</div>
|
||||
<div v-if="isNew && item.name == '用户备注管理'" class="font_26 colorPrice ui-jump-mp-text">有{{ isNew }}条新备注</div>
|
||||
<img class="ui-service-item-triangle-icon" src="https://image.fulllinkai.com/202306/13/d491373f21f5a0c5810f2167f7c961f0.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<van-popup v-model:show="showTips" round :close-on-click-overlay="false" :lock-scroll="true" :duration="0.5">
|
||||
<div class="ui-tips-box">
|
||||
<div class="color3 font_32 text-center">请先绑定{{ orderReal }}订单后操作</div>
|
||||
<div class="ui-btn-box f-fbc">
|
||||
<div class="ui-btn font_32 f-fcc color6" @click="showTips = false">取消</div>
|
||||
<div class="ui-btn-v2 font_32 f-fcc colorF" @click="jumpBindPath('groupOrders')">去绑定</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-popup v-model:show="showTipsV2" round :close-on-click-overlay="false" :lock-scroll="true" :duration="0.5">
|
||||
<div class="ui-tips-box">
|
||||
<div class="color3 font_32 text-center">请先绑定企业微信后操作</div>
|
||||
<div class="ui-btn-box f-fbc">
|
||||
<div class="ui-btn font_32 f-fcc color6" @click="showTipsV2 = false">取消</div>
|
||||
<div class="ui-btn-v2 font_32 f-fcc colorF" @click="jumpBindPath('bindEnterprise')">去绑定</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-popup>
|
||||
<van-popup v-model:show="showOrderState" round position="bottom" :duration="0.5">
|
||||
<van-picker v-model="orderStateValues" title="标记订单状态" :columns="orderStateColumns" @confirm="onConfirmOrderState" @cancel="showOrderState = false" />
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { showConfirmDialog, showToast } from 'vant';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import router from '@/router';
|
||||
import weChat from '@/utils/weChat';
|
||||
|
||||
defineOptions({ name: 'ServiceCentre' });
|
||||
|
||||
const userStore = useUserStore() as any;
|
||||
const loading = ref(false);
|
||||
const isIOS = ref<any>(null);
|
||||
const isNew = ref<any>(null);
|
||||
const roles = ref<any[]>([]); // 角色职责数据
|
||||
const orderNum = ref<any>('');
|
||||
const dataState = ref<any>(''); // 订单资料状态
|
||||
const name = ref<any>(''); // 姓名
|
||||
const mobile = ref<any>(''); // 手机
|
||||
const orderReal = ref<any>(''); // 订单的真实性
|
||||
const isBind = ref<any>(''); // 是否绑定企业微信
|
||||
const list = ref<any[]>([
|
||||
{ name: '邀请用户完善信息', icon: 'https://image.fulllinkai.com/202306/13/854c78986ae76f992f65e77f77b261b0.png', path: '' },
|
||||
{ name: '查看用户信息', icon: 'https://image.fulllinkai.com/202306/13/f94eaf7e474fde2c0b486d0d95c64d01.png', path: 'appViewUserInfo' },
|
||||
{ name: '查看用户测量记录(随时可查看)', icon: 'https://image.fulllinkai.com/202306/13/856424fce58d0862b88a5d3b4adb0bfa.png', path: 'appMeasurementRecord' },
|
||||
{ name: '图表统计', icon: 'https://image.fulllinkai.com/202306/29/e730b3fa925f819e94dab2c32dcdf0a9.png', path: 'appStatistics' },
|
||||
{ name: '方案结束后问卷调查', icon: 'https://image.fulllinkai.com/202306/13/70e934d1992521dd2ec76d6a948351d1.png', path: 'appSurveys' },
|
||||
{ name: '用户备注管理', icon: 'https://image.fulllinkai.com/202306/13/4e14f3d00b326fca0b8a2737c956902b.png', path: 'appRemarks' },
|
||||
{ name: '切换用户', icon: 'https://image.fulllinkai.com/202308/16/84f881b246a0d2755b53a80db1b4ba24.png', path: 'appSwitchUser' },
|
||||
{ name: '复盘评估表', icon: 'https://image.fulllinkai.com/202306/25/3369eb56357331fdfd450750033ad7fe.png', path: 'appAssess' },
|
||||
{ name: '设置用户餐单', icon: 'https://image.fulllinkai.com/202306/25/40b9d5fdfa39d1fd0fa761ece211d59e.png', path: 'appEditUserMenu' },
|
||||
{ name: '设置餐单日期', icon: 'https://image.fulllinkai.com/202306/25/40b9d5fdfa39d1fd0fa761ece211d59e.png', path: 'appUserMenu' },
|
||||
{ name: '工单', icon: 'https://image.fulllinkai.com/202308/16/397ce39edf70f2a05daadff6acbbd50c.png', path: 'appWorkOrder' },
|
||||
]);
|
||||
const showTips = ref(false);
|
||||
const showTipsV2 = ref(false);
|
||||
|
||||
const selectOrderState = ref<any>('');
|
||||
const orderStateValues = ref<any[]>([]); // 订单状态选择框默认值
|
||||
const orderStateColumns = ref<any[]>([
|
||||
{ text: '进行中', value: 'STARTING' },
|
||||
{ text: '暂停中', value: 'SUSPEND' },
|
||||
{ text: '已结束', value: 'FINISHED' },
|
||||
]); // 订单状态选择框数据
|
||||
const showOrderState = ref(false);
|
||||
|
||||
// 获取当前企业群是否绑定订单,status:1未绑定, 2已绑定,3暂不绑定(虚拟订单)
|
||||
const getState = () => {
|
||||
weChat({ url: `/h5/work/${userStore.chatId}/group/orders/status`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
userStore.weChatBindState = result.status;
|
||||
userStore.chiefCoach = result.is_main_coach;
|
||||
userStore.coach = result.is_coach;
|
||||
userStore.service = result.is_customer;
|
||||
isBind.value = result.is_bind;
|
||||
isNew.value = result.is_comment;
|
||||
// 绑定了订单后才能获取角色职责
|
||||
if (result.status * 1 === 2 || result.status * 1 === 3) {
|
||||
getDataState();
|
||||
// getRoles();
|
||||
}
|
||||
loading.value = true;
|
||||
if (result.status * 1 === 1 || result.status * 1 === 3) {
|
||||
list.value.splice(11, 0, {
|
||||
path: 'appGroupOrders',
|
||||
icon: 'https://image.fulllinkai.com/202306/13/60898757ebb4a400cd8be6d2b1519aa4.png',
|
||||
name: '绑定订单',
|
||||
});
|
||||
}
|
||||
if ((result.status * 1 === 1 || result.status * 1 === 3) && !result.is_bind) {
|
||||
list.value.splice(12, 0, {
|
||||
path: 'boundEnterprise',
|
||||
icon: 'https://image.fulllinkai.com/202306/26/f4de8b69f3eb31a9cfe0ec56d9647232.png',
|
||||
name: '绑定企业微信',
|
||||
});
|
||||
} else if (!result.is_bind) {
|
||||
list.value.splice(11, 0, {
|
||||
path: 'boundEnterprise',
|
||||
icon: 'https://image.fulllinkai.com/202306/26/f4de8b69f3eb31a9cfe0ec56d9647232.png',
|
||||
name: '绑定企业微信',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
loading.value = true;
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取该群绑定的角色职责和客服信息
|
||||
const getRoles = () => {
|
||||
weChat({ url: `/h5/work/group/roles?chat_id=${userStore.chatId}`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
roles.value = result.roles;
|
||||
name.value = result.service_user ? result.service_user.name : '';
|
||||
mobile.value = result.service_user ? result.service_user.mobile : '';
|
||||
console.log(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取当前用户餐单是否开始的状态
|
||||
const getDataState = () => {
|
||||
weChat({ url: `/h5/order/user/service/status?chat_id=${userStore.chatId}`, hideLoading: true, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
orderNum.value = result.order_id;
|
||||
dataState.value = result.status;
|
||||
userStore.dataState = result.status;
|
||||
// 服务状态为完成时可标记用户订单状态
|
||||
if (result.status == 'FINISHED') {
|
||||
list.value.splice(list.value.length + 1, 0, {
|
||||
path: '',
|
||||
icon: 'https://image.fulllinkai.com/202306/13/70e934d1992521dd2ec76d6a948351d1.png',
|
||||
name: '标记订单状态',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 转发
|
||||
const retransmission = () => {
|
||||
showConfirmDialog({
|
||||
title: '',
|
||||
message: '是否确认转发邀请用户完善信息到群聊天?',
|
||||
})
|
||||
.then(() => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
type: 'health',
|
||||
is_im: 1,
|
||||
};
|
||||
weChat({ url: `/send/im/msg`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showToast('通知已发送');
|
||||
setTimeout(() => {
|
||||
// 调用ios app方法
|
||||
if (isIOS.value) {
|
||||
window.webkit.messageHandlers.goBack.postMessage(null);
|
||||
} else {
|
||||
// 调用安卓 app方法
|
||||
window.webAppInterface.goBack();
|
||||
}
|
||||
}, 1200);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
|
||||
// 标记是否方案已结束
|
||||
const markersOption = () => {
|
||||
let data = {
|
||||
chat_id: userStore.chatId,
|
||||
order_status: selectOrderState.value,
|
||||
};
|
||||
showConfirmDialog({
|
||||
title: '温馨提示',
|
||||
message: `是标记订单为${selectOrderState.value == 'STARTING' ? '进行中' : selectOrderState.value == 'SUSPEND' ? '暂停中' : '已结束'}?`,
|
||||
})
|
||||
.then(() => {
|
||||
// on confirm
|
||||
weChat({ url: `h5/mark/user/order/status`, data, method: 'post' })
|
||||
.then(() => {
|
||||
showToast('标记成功');
|
||||
userStore.orderState = selectOrderState.value;
|
||||
if (selectOrderState.value == 'FINISHED') {
|
||||
// list.value.splice(list.value.length - 1, 1);
|
||||
list.value.splice(1, 0, {
|
||||
path: '',
|
||||
icon: 'https://image.fulllinkai.com/202306/13/70e934d1992521dd2ec76d6a948351d1.png',
|
||||
name: '邀请用户上传方案后体检报告',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
};
|
||||
|
||||
const jumpPath = (e) => {
|
||||
if (!loading.value) {
|
||||
return;
|
||||
}
|
||||
if (e.name == '标记订单状态') {
|
||||
if (userStore.chiefCoach == 1) {
|
||||
showOrderState.value = true;
|
||||
} else {
|
||||
showToast('非常抱歉,该功能只限主教练');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (e.name == '邀请用户完善信息') {
|
||||
retransmission();
|
||||
return;
|
||||
}
|
||||
if (dataState.value != 'SCHEME' && dataState.value != 'FINISHED' && (e.name == '设置餐单日期' || e.name == '设置用户餐单')) {
|
||||
showToast('用户餐单方案待设置');
|
||||
return;
|
||||
}
|
||||
if (e.name !== '绑定订单' && e.name !== '绑定企业微信' && e.name !== '绑定企业微信') {
|
||||
if (userStore.weChatBindState * 1 === 1) {
|
||||
showTips.value = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (userStore.weChatBindState * 1 === 3 && (e.name == '查看用户信息' || e.name == '查看用户测量记录(随时可查看)' || e.name == '图表统计' || e.name == '切换用户' || e.name == '设置餐单日期' || e.name == '设置用户餐单' || e.name == '工单')) {
|
||||
orderReal.value = '真实';
|
||||
showTips.value = true;
|
||||
return;
|
||||
}
|
||||
if (!isBind.value && (e.name == '用户备注管理' || e.name == '复盘评估表')) {
|
||||
showTipsV2.value = true;
|
||||
return;
|
||||
}
|
||||
let roleId = '' as any;
|
||||
if (userStore.chiefCoach) {
|
||||
roleId = 1;
|
||||
} else if (userStore.service) {
|
||||
roleId = 3;
|
||||
} else if (userStore.coach) {
|
||||
roleId = 2;
|
||||
}
|
||||
router.push({
|
||||
name: e.path,
|
||||
query: { role: roleId },
|
||||
});
|
||||
};
|
||||
|
||||
const jumpBindPath = (url) => {
|
||||
router.push({
|
||||
name: url,
|
||||
});
|
||||
};
|
||||
|
||||
const contactService = () => {
|
||||
window.location.href = `https://work.weixin.qq.com/kfid/kfc967090f765f69300`;
|
||||
};
|
||||
|
||||
// 选择标记订单状态
|
||||
const onConfirmOrderState = ({ selectedOptions }) => {
|
||||
showOrderState.value = false;
|
||||
selectOrderState.value = `${selectedOptions[0].value}`;
|
||||
console.log(selectOrderState.value);
|
||||
markersOption();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
let ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.indexOf('Android') > -1 || ua.indexOf('Adr') > -1 || ua.indexOf('android') != -1) {
|
||||
isIOS.value = false;
|
||||
} else {
|
||||
isIOS.value = true;
|
||||
}
|
||||
if (route.chat_id) {
|
||||
userStore.chatId = route.chat_id;
|
||||
}
|
||||
getState();
|
||||
getRoles();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-serviceCentre {
|
||||
background: #f8f8f8;
|
||||
overflow-y: auto;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.ui-service-list-box {
|
||||
margin: px2rem(30);
|
||||
padding: px2rem(30);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
}
|
||||
|
||||
.ui-service-top {
|
||||
padding-bottom: px2rem(30);
|
||||
margin-bottom: px2rem(-30);
|
||||
border-bottom: px2rem(2) solid #f5f5f5;
|
||||
|
||||
.ui-logo-icon {
|
||||
width: px2rem(120);
|
||||
height: px2rem(120);
|
||||
display: block;
|
||||
margin-right: px2rem(18);
|
||||
}
|
||||
|
||||
.ui-user-box {
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.ui-duty-box {
|
||||
width: fit-content;
|
||||
padding: px2rem(4) px2rem(16) px2rem(6) px2rem(12);
|
||||
border-radius: px2rem(24);
|
||||
border: px2rem(2) solid #d8d8d8;
|
||||
margin-top: px2rem(12);
|
||||
margin-right: px2rem(20);
|
||||
|
||||
.ui-duty-icon {
|
||||
width: px2rem(24);
|
||||
height: px2rem(24);
|
||||
display: block;
|
||||
margin-right: px2rem(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-service-item {
|
||||
margin-top: px2rem(60);
|
||||
|
||||
.ui-service-item-icon {
|
||||
width: px2rem(40);
|
||||
height: px2rem(40);
|
||||
display: block;
|
||||
margin-right: px2rem(20);
|
||||
}
|
||||
|
||||
.ui-jump-mp-text {
|
||||
line-height: px2rem(36);
|
||||
}
|
||||
|
||||
.ui-service-item-triangle-icon {
|
||||
width: px2rem(10);
|
||||
height: px2rem(20);
|
||||
display: block;
|
||||
margin-left: px2rem(16);
|
||||
}
|
||||
}
|
||||
|
||||
.ui-tips-box {
|
||||
position: relative;
|
||||
width: px2rem(520);
|
||||
padding: px2rem(92) px2rem(40) px2rem(50) px2rem(40);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(24);
|
||||
|
||||
.ui-btn-box {
|
||||
padding: 0 px2rem(36);
|
||||
|
||||
.ui-btn,
|
||||
.ui-btn-v2 {
|
||||
width: px2rem(192);
|
||||
height: px2rem(68);
|
||||
border-radius: px2rem(34);
|
||||
margin-top: px2rem(80);
|
||||
}
|
||||
|
||||
.ui-btn {
|
||||
border: px2rem(2) solid #d8d8d8;
|
||||
}
|
||||
|
||||
.ui-btn-v2 {
|
||||
background: #5ac7a0;
|
||||
border-radius: px2rem(34);
|
||||
border: px2rem(2) solid #5ac7a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
162
src/views/otherDir/activityOrder.vue
Normal file
162
src/views/otherDir/activityOrder.vue
Normal file
@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<div class="ui-activityOrder">
|
||||
<div class="ui-placeholder"></div>
|
||||
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
|
||||
<van-list v-model:loading="loading" :finished="finished" @load="getList">
|
||||
<div v-if="loadingState && list.length == 0" class="ui-empty-data-box">
|
||||
<img class="ui-empty-data-icon" src="https://image.fulllinkai.com/202301/08/939c529ca16a91e3ee3b22bef2fe92ca.png" alt="" />
|
||||
<div class="color6 font_30 text-center">暂无数据</div>
|
||||
</div>
|
||||
<div v-else-if="loadingState" class="ui-container">
|
||||
<div class="ui-packaging">
|
||||
<div v-for="(item, index) in list" :key="index" class="font_28 color3 f-fbc ui-mb-20 ui-mt-20">
|
||||
<div class="ui-input">
|
||||
<div class="f-fcl font_30 color3">{{ index + 1 }}、{{ item.desc }}</div>
|
||||
<div class="ui-mt-12 f-fbc">
|
||||
<div>购买数量:</div>
|
||||
<div class="color3 bold">x{{ item.linkmen_count }}</div>
|
||||
</div>
|
||||
<div class="ui-mt-12 f-fbc">
|
||||
<div>购买时间:</div>
|
||||
<div class="color3 bold">{{ item.created_at }}</div>
|
||||
</div>
|
||||
<div class="ui-mt-12 f-fbc">
|
||||
<div class="font_28">用户姓名:</div>
|
||||
<div class="color3 bold ui-name ellipsis_1">{{ item.name }}</div>
|
||||
</div>
|
||||
<div class="ui-mt-12 f-fbc">
|
||||
<div>推荐人:</div>
|
||||
<div v-if="item.introduce_name" class="color3 bold">{{ item.introduce_name || '无推荐人' }}</div>
|
||||
<div v-else class="color9 bold">无推荐人</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="noMore" class="ui-no-more font_24 color6 text-center">我也是有底线的</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import weChat from '@/utils/weChat';
|
||||
import router from '@/router';
|
||||
|
||||
defineOptions({ name: 'ActivityOrder' });
|
||||
|
||||
const loadingState = ref(false); // 接口是否加载完成
|
||||
const id = ref<any>('');
|
||||
const list = ref<any[]>([]); // 数据存储
|
||||
const noMore = ref(false); // 没有更多数据
|
||||
const refreshing = ref(true); // 上拉刷新false表示加载完成
|
||||
const finished = ref(false); // true表示数据全部加载完成
|
||||
const loading = ref(false); // false表示数据加载完成
|
||||
const page = ref(1); // 数据分页
|
||||
|
||||
// 获取列表
|
||||
const getList = () => {
|
||||
weChat({ url: `h5/activities/${id.value}/orders?page=${page.value}`, method: 'get' })
|
||||
.then((res) => {
|
||||
const result = res.data;
|
||||
if (list.value.length === 0 || page.value === 1) {
|
||||
list.value = result.data;
|
||||
} else if (list.value.length >= 15) {
|
||||
result.data.forEach((item) => {
|
||||
list.value.push(item);
|
||||
});
|
||||
}
|
||||
refreshing.value = false;
|
||||
loading.value = false;
|
||||
if (list.value.length < 15 || result.data.length < 15) {
|
||||
finished.value = true;
|
||||
noMore.value = true;
|
||||
}
|
||||
loadingState.value = true;
|
||||
page.value++;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
};
|
||||
|
||||
// 上拉初始化数据
|
||||
const onRefresh = () => {
|
||||
page.value = 1;
|
||||
noMore.value = false;
|
||||
finished.value = false;
|
||||
loading.value = true;
|
||||
getList();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
let route = router.currentRoute.value.query;
|
||||
id.value = route.id;
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ui-activityOrder {
|
||||
background: #ffffff;
|
||||
overflow-y: auto;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.ui-title-box {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: #ffffff;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 20;
|
||||
padding: px2rem(30);
|
||||
}
|
||||
|
||||
.ui-placeholder {
|
||||
width: 100%;
|
||||
height: px2rem(20);
|
||||
}
|
||||
|
||||
.ui-empty-data-box {
|
||||
position: relative;
|
||||
z-index: 15;
|
||||
}
|
||||
|
||||
.ui-empty-data-icon {
|
||||
width: px2rem(270);
|
||||
height: px2rem(200);
|
||||
display: block;
|
||||
margin: 26vh auto px2rem(20) auto;
|
||||
}
|
||||
|
||||
.ui-name {
|
||||
max-width: px2rem(420);
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.ui-container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0 px2rem(30);
|
||||
background: #ffffff;
|
||||
|
||||
.ui-packaging {
|
||||
padding: px2rem(2) px2rem(24);
|
||||
background: #f8f8f8;
|
||||
border-radius: px2rem(24);
|
||||
margin-bottom: px2rem(30);
|
||||
|
||||
.ui-input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: px2rem(24);
|
||||
background: #ffffff;
|
||||
border-radius: px2rem(16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ui-no-more {
|
||||
padding: px2rem(10) px2rem(0) px2rem(120) px2rem(0);
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user