Browse Source

init

master
kola-web 2 months ago
commit
7ffe34202a
  1. 16
      .babelrc
  2. 2
      .deployment
  3. 13
      .editorconfig
  4. 9
      .env.bak
  5. 3
      .env.production
  6. 3
      .env.test
  7. 17
      .eslintrc.js
  8. 92
      .gitignore
  9. 0
      .husky/common.sh
  10. 5
      .husky/pre-commit
  11. 100
      .prettierignore
  12. 5
      .prettierrc
  13. 30
      README.md
  14. 133
      deploy.sh
  15. 43
      dev/azure/deploy/deploy.sh
  16. 31
      dev/azure/deploy/init-repo.sh
  17. 56
      dev/azure/nuxt/package.json
  18. 10
      dev/build/cpSource.sh
  19. 62
      dev/conf/nginx.conf
  20. 77
      dev/conf/nginx/default.conf
  21. 26
      dev/conf/php/opcache.ini
  22. 11
      dev/conf/supervisor/nginx.conf
  23. 9
      dev/conf/supervisor/php-fpm.conf
  24. 12
      dev/conf/supervisor/worker.conf
  25. 74
      dev/conf/template/nginx/default.conf
  26. 4
      dev/conf/template/nginx/run-nginx.sh
  27. 21
      docker-compose.yml
  28. 12
      login-docker.sh
  29. 120
      nuxt.config.js
  30. 14411
      package-lock.json
  31. 66
      package.json
  32. 64
      package.json.bak
  33. 14228
      pnpm-lock.yaml
  34. 54
      server.js
  35. 28
      src/assets/css/_variable.scss
  36. 227
      src/assets/css/common.scss
  37. BIN
      src/assets/images/article_cover.jpg
  38. BIN
      src/assets/images/article_cover.png
  39. BIN
      src/assets/images/beans.png
  40. BIN
      src/assets/images/beans_popup_bg.png
  41. BIN
      src/assets/images/chatbot_audio_feature.png
  42. BIN
      src/assets/images/comment.png
  43. BIN
      src/assets/images/doctor_default_poster.png
  44. BIN
      src/assets/images/doctor_list_top.png
  45. BIN
      src/assets/images/doctor_pause_icon.png
  46. BIN
      src/assets/images/doctor_play_icon.png
  47. BIN
      src/assets/images/fail_page_img.png
  48. BIN
      src/assets/images/follow_qrcode.png
  49. BIN
      src/assets/images/follow_qrcode_test.png
  50. BIN
      src/assets/images/heart.png
  51. BIN
      src/assets/images/heart_gray.png
  52. BIN
      src/assets/images/home_logo.png
  53. BIN
      src/assets/images/like.png
  54. BIN
      src/assets/images/like_chosen.png
  55. BIN
      src/assets/images/logout.png
  56. BIN
      src/assets/images/mobile_doctor_arrow.png
  57. BIN
      src/assets/images/mobile_doctor_play.png
  58. BIN
      src/assets/images/mobile_doctor_title.png
  59. BIN
      src/assets/images/mobile_logo.png
  60. BIN
      src/assets/images/novocare_qrcode.png
  61. BIN
      src/assets/images/pause_chatbot_audio.png
  62. BIN
      src/assets/images/play_chatbot_audio.png
  63. BIN
      src/assets/images/share_point.png
  64. BIN
      src/assets/images/share_qq.png
  65. BIN
      src/assets/images/share_wechat.png
  66. BIN
      src/assets/images/share_weibo.png
  67. BIN
      src/assets/images/video_play_icon.png
  68. BIN
      src/assets/images/wechat_color.png
  69. BIN
      src/assets/images/wechat_white.png
  70. 508
      src/components/Barrage.vue
  71. 440
      src/components/BottomRightFixed.vue
  72. 1418
      src/components/CommonHeader.vue
  73. 101
      src/components/DoctorItem.vue
  74. 95
      src/components/DoctorVideoItem.vue
  75. 251
      src/components/MobileHeader.vue
  76. 74
      src/components/NotLogin.vue
  77. 304
      src/components/UserInfoCommon.vue
  78. 375
      src/components/VideoArticleListItem.vue
  79. 143
      src/components/WebFooter.vue
  80. 115
      src/components/WebHeader.vue
  81. 122
      src/layouts/common.vue
  82. 245
      src/layouts/detail.vue
  83. 113
      src/layouts/error.vue
  84. 6
      src/middleware/authenticated.js
  85. 102
      src/middleware/device.js
  86. 945
      src/pages/article/_id.vue
  87. 122
      src/pages/article/index.vue
  88. 406
      src/pages/doctor/_id.vue
  89. 249
      src/pages/doctor/index.vue
  90. 906
      src/pages/index.vue
  91. 397
      src/pages/more.vue
  92. 66
      src/pages/other/about.vue
  93. 322
      src/pages/other/agreement.vue
  94. 127
      src/pages/other/cookies.vue
  95. 28
      src/pages/other/corporate.vue
  96. 39
      src/pages/other/disclaimer.vue
  97. 76
      src/pages/other/link.vue
  98. 196
      src/pages/other/privacy.vue
  99. 480
      src/pages/search/index.vue
  100. 82
      src/pages/user/index.vue
  101. Some files were not shown because too many files have changed in this diff Show More

16
.babelrc

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
{
"env": {
"test": {
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
}
}

2
.deployment

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
[config]
command = bash deploy.sh

13
.editorconfig

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false

9
.env.bak

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
# 前端端口
PORT=3000
#nuxt前端和nginx代理接口地址
PROXY_URL=http://192.168.1.154:8080
# nginx中使用的前端地址
NUXT_HOST=http://192.168.1.154:3000

3
.env.production

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
NODE_ENV = 'production'
VUE_APP_TITLE = 'production'

3
.env.test

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
NODE_ENV = 'production'
VUE_APP_TITLE = 'test'

17
.eslintrc.js

@ -0,0 +1,17 @@ @@ -0,0 +1,17 @@
module.exports = {
root: true,
env: {
browser: true,
node: true
},
parserOptions: {
parser: '@babel/eslint-parser',
requireConfigFile: false
},
extends: ['@nuxtjs', 'plugin:nuxt/recommended', 'prettier'],
plugins: [],
// add your custom rules here
rules: {
'no-console': 'off'
}
};

92
.gitignore vendored

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp
.idea

0
.husky/common.sh

5
.husky/pre-commit

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
. "$(dirname "$0")/common.sh"
npx lint-staged

100
.prettierignore

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
###
# Place your Prettier ignore content here
###
# .gitignore content is duplicated here due to https://github.com/prettier/prettier/issues/8506
# Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
src/node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# Nuxt generate
dist
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless
# IDE / Editor
.idea
# Service worker
sw.*
# macOS
.DS_Store
# Vim swap files
*.swp
**/node_modules/**
.nuxt/**/*

5
.prettierrc

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "none"
}

30
README.md

@ -0,0 +1,30 @@ @@ -0,0 +1,30 @@
# 本地开发
## 使用nginx代理
1. 安装docker
2. 登录公司镜像库, user: dnurse_dev, pass:Dnurse@20171201
```shell
docker login repo.dnurse.cn
```
3. 修改.env中的环境变量为本机信息
4. 运行命令
```shell
docker-compose up -d # 启动并进入后台
docker-compose restart # 重启
docker-compose stop # 停止
docker-compose rm -f # 删除
cd src #进入代码目录
npm run start # 启动前端
```
4. 访问 http://127.0.0.1/
## 不用代理
1. 修改.env中的环境变量为本机信息
2. 运行命令
```shell
cd src #进入代码目录
npm run start # 启动前端
```
3.访问 http://127.0.0.1/

133
deploy.sh

@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
#!/bin/bash
# ----------------------
# KUDU Deployment Script
# Version: 1.0.17
# ----------------------
# Helpers
# -------
exitWithMessageOnError () {
if [ ! $? -eq 0 ]; then
echo "An error has occurred during web site deployment."
echo $1
exit 1
fi
}
# Prerequisites
# -------------
# Verify node.js installed
hash node 2>/dev/null
exitWithMessageOnError "Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment."
# Setup
# -----
SCRIPT_DIR="${BASH_SOURCE[0]%\\*}"
SCRIPT_DIR="${SCRIPT_DIR%/*}"
ARTIFACTS=$SCRIPT_DIR/../artifacts
KUDU_SYNC_CMD=${KUDU_SYNC_CMD//\"}
if [[ ! -n "$DEPLOYMENT_SOURCE" ]]; then
DEPLOYMENT_SOURCE=$SCRIPT_DIR
fi
if [[ ! -n "$NEXT_MANIFEST_PATH" ]]; then
NEXT_MANIFEST_PATH=$ARTIFACTS/manifest
if [[ ! -n "$PREVIOUS_MANIFEST_PATH" ]]; then
PREVIOUS_MANIFEST_PATH=$NEXT_MANIFEST_PATH
fi
fi
if [[ ! -n "$DEPLOYMENT_TARGET" ]]; then
DEPLOYMENT_TARGET=$ARTIFACTS/wwwroot
else
KUDU_SERVICE=true
fi
if [[ ! -n "$KUDU_SYNC_CMD" ]]; then
# Install kudu sync
echo Installing Kudu Sync
npm install kudusync -g --silent
exitWithMessageOnError "npm failed"
if [[ ! -n "$KUDU_SERVICE" ]]; then
# In case we are running locally this is the correct location of kuduSync
KUDU_SYNC_CMD=kuduSync
else
# In case we are running on kudu service this is the correct location of kuduSync
KUDU_SYNC_CMD=$APPDATA/npm/node_modules/kuduSync/bin/kuduSync
fi
fi
# Node Helpers
# ------------
selectNodeVersion () {
if [[ -n "$KUDU_SELECT_NODE_VERSION_CMD" ]]; then
SELECT_NODE_VERSION="$KUDU_SELECT_NODE_VERSION_CMD \"$DEPLOYMENT_SOURCE\" \"$DEPLOYMENT_TARGET\" \"$DEPLOYMENT_TEMP\""
eval $SELECT_NODE_VERSION
exitWithMessageOnError "select node version failed"
if [[ -e "$DEPLOYMENT_TEMP/__nodeVersion.tmp" ]]; then
NODE_EXE=`cat "$DEPLOYMENT_TEMP/__nodeVersion.tmp"`
exitWithMessageOnError "getting node version failed"
fi
if [[ -e "$DEPLOYMENT_TEMP/__npmVersion.tmp" ]]; then
NPM_JS_PATH=`cat "$DEPLOYMENT_TEMP/__npmVersion.tmp"`
exitWithMessageOnError "getting npm version failed"
fi
if [[ ! -n "$NODE_EXE" ]]; then
NODE_EXE=node
fi
NPM_CMD="\"$NODE_EXE\" \"$NPM_JS_PATH\""
else
NPM_CMD=npm
NODE_EXE=node
fi
}
##################################################################################################################################
# Deployment
# ----------
echo Handling node.js deployment.
# 1. KuduSync
if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then
"$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh"
exitWithMessageOnError "Kudu Sync failed"
fi
# 2. Select node version
selectNodeVersion
# 3. Install npm packages
if [ -e "$DEPLOYMENT_TARGET/package.json" ]; then
cd "$DEPLOYMENT_TARGET"
echo "Running $NPM_CMD install --production"
eval $NPM_CMD install --production
exitWithMessageOnError "npm failed"
cd - > /dev/null
fi
# 4. Use composer
echo "composer:$DEPLOYMENT_TARGET"
if [ -e "$DEPLOYMENT_TARGET/composer.json" ]; then
echo "Found composer.json"
echo "COMPOSER_ARGS:$COMPOSER_ARGS"
pushd "$DEPLOYMENT_TARGET"
php composer.phar install $COMPOSER_ARGS
exitWithMessageOnError "Composer install failed"
popd
fi
##################################################################################################################################
echo "Finished successfully."

43
dev/azure/deploy/deploy.sh

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
#!/usr/bin/env bash
# $1 git url
# $2 artifact path
# $3 save dir
# $4 build name
# $5 source dir
# $3 comment
GitUrl=$1
ArtifactPath=$2
SaveDir=$3
BuildName=$4
SourcePath=$5
Comment=$6
RepoDir=$(dirname ${ArtifactPath})
RepoPath="${RepoDir}/repo/${BuildName}/${SaveDir}"
ScriptDir=$(dirname ${SourcePath})
cd ${RepoPath}
pwd
echo $SourcePath
echo $RepoPath
echo $ScriptDir
echo "clean old:"
ls -A | grep -v '.git' | xargs rm -rf {}
echo "copy new:"
cp -R ${SourcePath}/src/. .
#cp -R ${SourcePath}/app_data .
#cp -R ${SourcePath}/bin .
#cp -R ${SourcePath}/ini .
#cp -R ${SourcePath}/PostDeploymentActions .
git add -f .
git commit -a -m "TFS:${Comment}"
git push -u origin --all

31
dev/azure/deploy/init-repo.sh

@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
#!/usr/bin/env bash
# $1 git url
# $2 artifact path
# $3 save dir
# $4 build name
GitUrl=$1
ArtifactPath=$2
SaveDir=$3
BuildName=$4
RepoDir=$(dirname ${ArtifactPath})
RepoPath="${RepoDir}/repo/${BuildName}"
mkdir -p ${RepoPath}
cd ${RepoPath}
if [ ! -e ${SaveDir} ]
then
git clone $1 ${SaveDir}
cd ${SaveDir}
git config user.name 'autouser'
git config user.email 'autouser@dnurse.cn'
else
cd ${SaveDir}
git reset --hard
git clean -df
git pull
fi

56
dev/azure/nuxt/package.json

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
{
"name": "novo-pc",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "cross-env process.env.VUE_APP_TITLE=test nuxt build",
"build:prod": "cross-env process.env.VUE_APP_TITLE=production nuxt build",
"start": "node ./server.js",
"start_prod": "NODE_ENV=production node ./server.js",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint:js:fix": "eslint --ext \".js,.vue\" --ignore-path .gitignore --fix .",
"format": "prettier --write \"./**/*.vue\" \"./**/*.js\" \"./**/*.scss\" ",
"format1": "prettier ",
"lint:prettier": "prettier --check \"./**/*.vue\" \"./**/*.js\"",
"lint": "npm run lint:js && npm run lint:prettier",
"lintfix": "prettier --write --list-different \"./**/*.vue\" \"./**/*.js\" && npm run lint:js:fix"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue}": "eslint --cache",
"*.**": "prettier --check --ignore-unknown"
},
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/robots": "^2.5.0",
"@nuxtjs/style-resources": "^1.2.1",
"core-js": "^3.19.3",
"element-ui": "^2.15.6",
"koa": "^2.13.4",
"moment": "^2.29.1",
"nuxt": "^2.15.8",
"vue": "^2.6.14",
"vue-qr": "^3.2.2"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^6.0.1",
"@nuxtjs/eslint-module": "^3.0.2",
"@nuxtjs/proxy": "^2.1.0",
"cross-env": "^7.0.3",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-nuxt": "^2.0.0",
"eslint-plugin-vue": "^7.12.1",
"husky": "^7.0.4",
"lint-staged": "^12.1.3",
"node-sass": "^6.0.1",
"prettier": "^2.5.1",
"qs": "^6.10.2",
"sass-loader": "^10.2.0"
}
}

10
dev/build/cpSource.sh

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
#!/usr/bin/env bash
# $1 git url
# $2 artifact path
SOURCE=$1
DEST=$2
cp -R ${SOURCE}/src ${DEST}
rm -rf ${DEST}/src/node_modules

62
dev/conf/nginx.conf

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
#user nobody;
worker_processes auto;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid run/nginx.pid;
events {
worker_connections 32768;
accept_mutex off;
multi_accept on;
use epoll;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
log_format logstash '$http_host $server_addr $remote_addr [$time_local] "$request" '
'$request_body $status $body_bytes_sent "$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time $http_x_user_sn $http_x_log_id';
#tcp_nopush on;
sendfile off;
keepalive_timeout 5;
keepalive_requests 500;
client_header_timeout 10;
client_body_timeout 10;
reset_timedout_connection on;
send_timeout 10;
limit_conn_zone $binary_remote_addr zone=addr:5m;
limit_conn addr 500;
gzip on;
gzip_disable "msie6";
gzip_proxied any;
gzip_min_length 1000;
gzip_comp_level 6;
gzip_types text/plain text/css application
#keepalive_timeout 0;
client_max_body_size 100m;
server_tokens off;
include /etc/nginx/sites-enabled/*;
}
#daemon off;

77
dev/conf/nginx/default.conf

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
server {
listen 80; ## listen for ipv4; this line is default and implied
listen [::]:80 default ipv6only=on; ## listen for ipv6
root /var/www/html/public;
index index.php index.html index.htm;
access_log /var/log/nginx/access.log logstash;
# Make site accessible from http://localhost/
server_name _;
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;
client_max_body_size 64M;
# deny access to . files, for security
#
location ~ /\. {
log_not_found off;
deny all;
}
location ^~ /.well-known {
allow all;
auth_basic off;
}
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
rewrite ^/uploadfile/ https://sancdiabeteswebn201.blob.core.chinacloudapi.cn$request_uri? permanent;
set_by_lua $api_host_env 'return os.getenv("API_HOST")';
set_by_lua $nuxt_host_env 'return os.getenv("NUXT_HOST")';
location /web {
proxy_pass ${api_host_env};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ^~ / {
proxy_pass ${nuxt_host_env};
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php$ {
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~* \.(jpg|jpeg|gif|png|css|js|ico|webp|tiff|ttf|svg)$ {
expires 5d;
}
}

26
dev/conf/php/opcache.ini

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=20000
opcache.save_comments=1
opcache.fast_shutdown=1
expose_php=false
opcache.validate_timestamps=1
opcache.file_cache=/tmp
xdebug.remote_enable=1
xdebug.remote_log=/tmp/xdebug.log
xdebug.remote_autostart=false
xdebug.remote_port=9000
xdebug.remote_host=192.168.1.37
xdebug.idekey="dnurse"
xdebug.profiler_output_dir=/var/www/html/public
xdebug.profiler_enable_trigger=1
;http 请求后加参数 XDEBUG_SESSION_START=dnurse
;cli 先设置变量 export XDEBUG_CONFIG="idekey=dnurse"
;cli 先设置变量 export PHP_IDE_CONFIG="serverName=novo-open"
;extension=xhprof.so
;xhprof.output_dir=/var/www/html/public

11
dev/conf/supervisor/nginx.conf

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
[program:nginx]
command=/var/www/template/nginx/run-nginx.sh
autostart=true
autorestart=true
priority=6
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

9
dev/conf/supervisor/php-fpm.conf

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
[program:php-fpm]
command = /usr/local/sbin/php-fpm --nodaemonize --fpm-config /usr/local/etc/php-fpm.d/www.conf
autostart=true
autorestart=true
priority=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

12
dev/conf/supervisor/worker.conf

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
[program:nginx]
command=/var/www/html/api/scripts/queue.sh
user =nginx
autostart=true
autorestart=true
priority=6
stdout_events_enabled=true
stderr_events_enabled=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

74
dev/conf/template/nginx/default.conf

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
server {
listen 80; ## listen for ipv4; this line is default and implied
listen [::]:80 default ipv6only=on; ## listen for ipv6
root /var/www/html/public;
index index.php index.html index.htm;
access_log /var/log/nginx/access.log logstash;
# Make site accessible from http://localhost/
server_name _;
# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html
sendfile off;
client_max_body_size 64M;
# deny access to . files, for security
#
location ~ /\. {
log_not_found off;
deny all;
}
location ^~ /.well-known {
allow all;
auth_basic off;
}
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
index index.html index.htm index.php;
charset utf-8;
rewrite ^/uploadfile/ https://sancdiabeteswebn201.blob.core.chinacloudapi.cn${DOLLAR}request_uri? permanent;
location /web {
proxy_pass ${PROXY_URL};
proxy_http_version 1.1;
proxy_set_header Upgrade ${DOLLAR}http_upgrade;
proxy_set_header Connection "Upgrade";
}
location ^~ / {
proxy_pass ${NUXT_HOST};
proxy_http_version 1.1;
proxy_set_header Upgrade ${DOLLAR}http_upgrade;
proxy_set_header Connection "Upgrade";
}
location = /favicon.ico { access_log off; log_not_found off; }
location = /robots.txt { access_log off; log_not_found off; }
error_page 404 /index.php;
location ~ \.php${DOLLAR} {
fastcgi_pass unix:/var/run/php-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME ${DOLLAR}realpath_root${DOLLAR}fastcgi_script_name;
include fastcgi_params;
}
location ~ /\.(?!well-known).* {
deny all;
}
location ~* \.(jpg|jpeg|gif|png|css|js|ico|webp|tiff|ttf|svg)${DOLLAR} {
expires 5d;
}
}

4
dev/conf/template/nginx/run-nginx.sh

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
#!/usr/bin/env bash
export DOLLAR='$'
envsubst < /var/www/template/nginx/default.conf > /etc/nginx/sites-available/default.conf
/usr/sbin/nginx -g "daemon off;"

21
docker-compose.yml

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
version: '2'
services:
php-fpm:
image: repo.dnurse.cn/library/nginx-php-7.4-xdebug:v1.2
ports:
- '80:80'
env_file:
- .env
environment:
SKIP_COMPOSER: 'true'
SKIP_CHOWN: 'true'
ERRORS: '1'
ENABLE_XDEBUG: '1'
volumes:
- ./dev/conf/supervisor/nginx.conf:/etc/supervisor/conf.d/nginx.conf
- ./dev/conf/supervisor/php-fpm.conf:/etc/supervisor/conf.d/php-fpm.conf
# - ./dev/conf/nginx:/etc/nginx/sites-available
- ./dev/conf/template:/var/www/template
- ./src:/var/www/html
# - ./dev/conf/nginx.conf:/etc/nginx/nginx.conf
- ./dev/conf/php/opcache.ini:/usr/local/etc/php/conf.d/opcache.ini

12
login-docker.sh

@ -0,0 +1,12 @@ @@ -0,0 +1,12 @@
#!/usr/bin/env bash
curDirName=$(basename `pwd`)
dockerId="${curDirName}_php-fpm_1"
hasPod=$(docker ps | grep ${dockerId} | wc -l)
if [[ ${hasPod} -eq 0 ]]; then
curDirName="${curDirName//-/}"
dockerId="${curDirName}_php-fpm_1"
fi
docker exec -it $dockerId bash

120
nuxt.config.js

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
/* eslint-disable nuxt/no-cjs-in-config */
module.exports = {
srcDir: './src',
loading: false,
// Global page headers: https://go.nuxtjs.dev/config-head
head: {
title: '糖尿病网 - 服务糖尿病患者的知识社区',
htmlAttrs: {
lang: 'en'
},
meta: [
{ charset: 'utf-8' },
{
name: 'viewport',
content:
'initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,width=device-width,user-scalable=no,viewport-fit=cover'
},
{
hid: 'description',
name: 'description',
content:
'糖尿病网,服务糖尿病患者的知识平台!以帮助糖尿病患者科学管理身体为目标,提供最新鲜、专业的健康管理资讯。'
},
{
hid: 'keywords',
name: 'keywords',
content:
'糖尿病,健康管理,控糖,血糖,血糖管理,科普,知识社区,专业资讯,干货,糖友,疾病,公益,有用,深度,饮食知识'
},
{ name: 'format-detection', content: 'telephone=no' }
],
link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
script: [
{
src: 'https://hm.baidu.com/hm.js?c17780012e32ae356918a39fe159755e'
}
]
},
router: {
middleware: ['device']
},
// Global CSS: https://go.nuxtjs.dev/config-css
css: [
'element-ui/lib/theme-chalk/index.css',
'video.js/dist/video-js.css',
'vue-video-player/src/custom-theme.css'
],
// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [
'@/plugins/element-ui',
'@/plugins/axios',
'@/plugins/filters',
{ src: '@/plugins/vueqr', ssr: true },
{ src: '@/plugins/video', ssr: false },
{ src: '~/plugins/swiper.js', ssr: false },
{ src: '~/plugins/wx-share.js', ssr: false },
{ src: '~/plugins/baidu.js', ssr: false }
],
// Auto import components: https://go.nuxtjs.dev/config-components
components: true,
// Modules for dev and build (recommended): https://go.nuxtjs.dev/config-modules
buildModules:
process.env.NODE_ENV !== 'production' ? ['@nuxtjs/eslint-module'] : [],
styleResources: {
scss: ['~/assets/css/common.scss']
},
// Modules: https://go.nuxtjs.dev/config-modules
modules: ['@nuxtjs/axios', '@nuxtjs/style-resources', '@nuxtjs/robots'],
axios: {
prefix: '/web',
credentials: true,
proxy: process.env.NODE_ENV !== 'production',
timeout: 2000,
debug: false
},
proxy: {
'/web/': process.env.PROXY_URL
},
robots: {
UserAgent: '*',
Disallow: '/user'
},
publicRuntimeConfig: {
axios: {
browserBaseURL:
process.env.NODE_ENV !== 'production'
? null
: process.env.BROWSER_BASE_URL + '/web'
}
},
privateRuntimeConfig: {
axios: {
baseURL:
process.env.NODE_ENV !== 'production'
? null
: process.env.PROXY_URL + '/web'
}
},
// Build Configuration: https://go.nuxtjs.dev/config-build
build: {
transpile: [/^element-ui/]
},
server: {
port: process.env.PORT || 3000, // default: 3000
// host: '0.0.0.0' // default: localhost
host: 'localhost'
},
telemetry: false,
env: {
VUE_APP_TITLE: process.env.VUE_APP_TITLE
}
};

14411
package-lock.json generated

File diff suppressed because it is too large Load Diff

66
package.json

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
{
"name": "novo-pc",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "cross-env process.env.VUE_APP_TITLE=test nuxt build",
"build:prod": "cross-env process.env.VUE_APP_TITLE=production nuxt build",
"start": "node ./server.js",
"start_prod": "NODE_ENV=production node ./server.js",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint:js:fix": "eslint --ext \".js,.vue\" --ignore-path .gitignore --fix .",
"format": "prettier --write \"./**/*.vue\" \"./**/*.js\" \"./**/*.scss\" ",
"format1": "prettier ",
"lint:prettier": "prettier --check \"./**/*.vue\" \"./**/*.js\"",
"lint": "npm run lint:js && npm run lint:prettier",
"lintfix": "prettier --write --list-different \"./**/*.vue\" \"./**/*.js\" && npm run lint:js:fix",
"prepare": "husky install",
"generate": "nuxt generate"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue}": "eslint --cache",
"*.**": "prettier --check --ignore-unknown"
},
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/robots": "^2.5.0",
"@nuxtjs/style-resources": "^1.2.1",
"core-js": "^3.19.3",
"dotenv": "^16.5.0",
"element-ui": "^2.15.6",
"koa": "^2.13.4",
"moment": "^2.29.1",
"nuxt": "^2.15.8",
"vue": "^2.6.14",
"vue-awesome-swiper": "^3.1.3",
"vue-qr": "^3.2.2",
"vue-server-renderer": "^2.6.14",
"vue-template-compiler": "^2.6.14",
"vue-video-player": "^5.0.2",
"webpack": "^4.46.0",
"weixin-js-sdk": "^1.6.0"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^6.0.1",
"@nuxtjs/eslint-module": "^3.0.2",
"@nuxtjs/proxy": "^2.1.0",
"cross-env": "^7.0.3",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-nuxt": "^2.0.0",
"eslint-plugin-vue": "^7.12.1",
"husky": "^7.0.4",
"lint-staged": "^12.1.3",
"node-sass": "^6.0.1",
"prettier": "^2.5.1",
"qs": "^6.10.2",
"sass-loader": "^10.2.0",
"video.js": "^8.23.3"
}
}

64
package.json.bak

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
{
"name": "novo-pc",
"version": "1.0.0",
"private": true,
"scripts": {
"dev": "nuxt",
"build": "cross-env process.env.VUE_APP_TITLE=test nuxt build",
"build:prod": "cross-env process.env.VUE_APP_TITLE=production nuxt build",
"start": "node ./server.js",
"start_prod": "NODE_ENV=production node ./server.js",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint:js:fix": "eslint --ext \".js,.vue\" --ignore-path .gitignore --fix .",
"format": "prettier --write \"./**/*.vue\" \"./**/*.js\" \"./**/*.scss\" ",
"format1": "prettier ",
"lint:prettier": "prettier --check \"./**/*.vue\" \"./**/*.js\"",
"lint": "npm run lint:js && npm run lint:prettier",
"lintfix": "prettier --write --list-different \"./**/*.vue\" \"./**/*.js\" && npm run lint:js:fix",
"prepare": "husky install",
"generate": "nuxt generate"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,vue}": "eslint --cache",
"*.**": "prettier --check --ignore-unknown"
},
"dependencies": {
"@nuxtjs/axios": "^5.13.6",
"@nuxtjs/robots": "^2.5.0",
"@nuxtjs/style-resources": "^1.2.1",
"core-js": "^3.19.3",
"element-ui": "^2.15.6",
"koa": "^2.13.4",
"moment": "^2.29.1",
"nuxt": "^2.15.8",
"vue": "^2.6.14",
"vue-awesome-swiper": "^3.1.3",
"vue-qr": "^3.2.2",
"vue-server-renderer": "^2.6.14",
"vue-template-compiler": "^2.6.14",
"vue-video-player": "^5.0.2",
"webpack": "^4.46.0",
"weixin-js-sdk": "^1.6.0"
},
"devDependencies": {
"@nuxtjs/eslint-config": "^6.0.1",
"@nuxtjs/eslint-module": "^3.0.2",
"@nuxtjs/proxy": "^2.1.0",
"cross-env": "^7.0.3",
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-nuxt": "^2.0.0",
"eslint-plugin-vue": "^7.12.1",
"husky": "^7.0.4",
"lint-staged": "^12.1.3",
"node-sass": "^6.0.1",
"prettier": "^2.5.1",
"qs": "^6.10.2",
"sass-loader": "^10.2.0"
}
}

14228
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

54
server.js

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
const Koa = require('koa');
const { Nuxt, Builder } = require('nuxt');
const dotenv = require('dotenv');
console.log('==========env:', process.env.NODE_ENV);
if (process.env.NODE_ENV !== 'production') {
// dotenv.config('.env');
} else {
console.log('===+++++++++++');
// dotenv.config('./.env.production');
}
dotenv.config('.env');
console.log(
'===+++++++++++',
process.env.BROWSER_BASE_URL,
process.env.PROXY_URL
);
// console.log("==========env2:", process.env);
async function start() {
const app = new Koa();
// Import and Set Nuxt.js options
const config = require('./nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
const { host, port } = config.server;
// Instantiate nuxt.js
const nuxt = new Nuxt(config);
// Build in development
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
}
app.use((ctx) => {
ctx.status = 200;
ctx.respond = false; // Mark request as handled for Koa
ctx.req.ctx = ctx; // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
nuxt.render(ctx.req, ctx.res);
});
app.listen(port, host);
console.log('Server listening on ' + host + ':' + port); // eslint-disable-line no-console
}
start();

28
src/assets/css/_variable.scss

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
$outer-width: 1258px;
$content-width: 1080px;
$body-color: #fff;
$bg-color: #f5f5f8;
$main-blue-color: #001965;
$font-color1: #292b2c;
$font-color2: #5d6269;
$font-color3: #939aa7;
$extra-blue: #005ad2;
$extra-red: #e6553f;
$extra-yellow: #eaab00;
$extra-green: #2a918b;
$extra-pink: #eea7bf;
@mixin textEllipsisMore1Line($lines) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lines;
overflow: hidden;
}
@mixin roundFun($width) {
width: $width;
height: $width;
border-radius: $width/2;
}

227
src/assets/css/common.scss

@ -0,0 +1,227 @@ @@ -0,0 +1,227 @@
@import 'variable';
* {
margin: 0;
border: none;
padding: 0;
}
html {
height: 100%;
}
body {
position: relative;
min-width: 320px;
min-height: 100%;
background: $body-color;
font-family: MicrosoftYaHei;
font-size: 0;
.outer-container {
min-width: $outer-width;
&.isMobile {
min-width: auto;
.content-container {
padding: 60px 0 62px;
width: 100%;
.left-content {
display: block !important;
margin-right: 0 !important;
width: 100% !important;
}
.footer-outer {
position: absolute !important;
min-width: auto !important;
}
.load-more {
width: 100% !important;
}
.common-flex {
display: block !important;
}
.common-other-content {
margin: 0 !important;
h6 {
margin-bottom: 10px !important;
font-size: 20px !important;
line-height: 28px !important;
}
.font-content {
p,
b,
h5,
h6 {
margin-bottom: 8px !important;
font-size: 16px !important;
line-height: 24px !important;
word-break: break-all;
}
h5 {
font-size: 18px !important;
}
}
}
}
&.hasLoginBottom {
padding-bottom: calc(constant(safe-area-inset-bottom) + 55px);
padding-bottom: calc(env(safe-area-inset-bottom) + 55px);
.bottom-outer,
.footer-outer {
bottom: calc(constant(safe-area-inset-bottom) + 55px);
bottom: calc(env(safe-area-inset-bottom) + 55px);
}
.fixed-box {
bottom: calc(constant(safe-area-inset-bottom) + 65px) !important;
bottom: calc(env(safe-area-inset-bottom) + 65px) !important;
}
}
}
}
.content-container {
margin: 0 auto;
width: $content-width;
}
img {
vertical-align: top;
-o-object-fit: cover;
object-fit: cover;
}
a {
text-decoration: none;
}
.text-ellipsis {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.left-content {
display: inline-block;
margin-right: 32px;
width: 723px;
}
.right-content {
float: right;
display: inline-block;
width: 325px;
}
.common-flex {
//display: flex;
//justify-content: space-between;
//flex-wrap: wrap;
display: grid;
grid-template-columns: repeat(4, 25%);
}
.load-more {
margin: 0 auto;
width: 357px;
height: 50px;
background: $bg-color;
border-radius: 4px;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color3;
line-height: 50px;
text-align: center;
}
.footer-outer {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
min-width: $outer-width;
background: $bg-color;
z-index: 9;
}
.common-title {
padding: 20px 0 10px;
text-align: left;
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $main-blue-color;
line-height: 33px;
a {
float: right;
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color2;
i {
color: $main-blue-color;
}
}
}
p.go-follow {
margin-bottom: 23px;
height: 50px;
background: $extra-red;
border-radius: 4px;
text-align: center;
cursor: pointer;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $body-color;
line-height: 50px;
}
.common-other-content {
margin: 44px 0;
padding: 30px;
box-sizing: border-box;
h6 {
margin-bottom: 20px;
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $main-blue-color;
line-height: 33px;
}
.font-content {
p,
b,
h5,
h6 {
margin-bottom: 12px;
font-size: 18px;
color: $font-color2;
line-height: 30px;
}
h5 {
font-size: 20px;
}
}
}
.el-message {
top: 50% !important;
i {
font-size: 16px;
}
@media screen and (max-width: 500px) {
min-width: 200px;
max-width: 80%;
}
}
.video-player-box {
.vjs-big-play-button {
top: 50%;
left: 50%;
border: none;
outline: none;
width: 68px;
height: 67px;
transform: translate(-50%, -50%);
background: url(~assets/images/video_play_icon.png) center no-repeat;
background-color: transparent !important;
background-size: 68px 67px;
.vjs-icon-placeholder::before {
display: none;
}
}
.vjs-control-bar button {
outline: none;
//outline: 2px;
}
}
.el-avatar > img {
width: 100%;
}
}

BIN
src/assets/images/article_cover.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
src/assets/images/article_cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
src/assets/images/beans.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
src/assets/images/beans_popup_bg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
src/assets/images/chatbot_audio_feature.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
src/assets/images/comment.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
src/assets/images/doctor_default_poster.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
src/assets/images/doctor_list_top.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 372 KiB

BIN
src/assets/images/doctor_pause_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
src/assets/images/doctor_play_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
src/assets/images/fail_page_img.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
src/assets/images/follow_qrcode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/images/follow_qrcode_test.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/images/heart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

BIN
src/assets/images/heart_gray.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

BIN
src/assets/images/home_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
src/assets/images/like.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/assets/images/like_chosen.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
src/assets/images/logout.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

BIN
src/assets/images/mobile_doctor_arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
src/assets/images/mobile_doctor_play.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 952 B

BIN
src/assets/images/mobile_doctor_title.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
src/assets/images/mobile_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
src/assets/images/novocare_qrcode.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
src/assets/images/pause_chatbot_audio.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
src/assets/images/play_chatbot_audio.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
src/assets/images/share_point.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
src/assets/images/share_qq.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
src/assets/images/share_wechat.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/assets/images/share_weibo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/assets/images/video_play_icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
src/assets/images/wechat_color.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
src/assets/images/wechat_white.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

508
src/components/Barrage.vue

@ -0,0 +1,508 @@ @@ -0,0 +1,508 @@
<!--
* @Author: your name
* @Date: 2020-03-18 13:26:53
* @LastEditTime: 2020-09-02 22:05:29
* @LastEditors: Please set LastEditors
* @Description: In User Settings Edit
* @FilePath: /vue-barrage/src/views/src/barrage_new.vue
-->
<template>
<div class="z_barrage-container">
<canvas
ref="canvasContainer"
:width="containerWidth"
:height="containerHeight"
style="display: none"
/>
<div class="z_container" :style="{ height: containerHeight / 2 + 'px' }">
<canvas
id="canvas"
ref="canvas"
class="z_barrage"
:width="containerWidth"
:height="containerHeight"
:style="{
width: containerWidth / 2 + 'px',
height: containerHeight / 2 + 'px'
}"
/>
</div>
</div>
</template>
<script>
// import faceMap from '../../assets/emoji'
let aniId, aniId1;
export default {
name: 'Barrage',
props: {
barrageList: {
type: Array,
default: () => []
},
speed: {
type: Number,
default: 4
},
loop: {
type: Boolean,
default: true
},
channels: {
type: Number,
default: 2
},
barrageHeight: {
type: Number,
default: 60
},
screenPercent: {
type: Number,
default: 0.3
},
borderColor: {
type: String,
default: ''
},
background: {
type: String,
default: ''
},
deviceType: {
type: String,
default: 'pc'
},
linearGradient: {
type: Object,
default: () => {
return {
startColor: '',
endColor: ''
};
}
}
},
data() {
return {
newBarrageArray: [], //
barrageArray: [],
barrageQueue: [],
containerWidth: 0,
containerHeight: 0,
channelsArray: [],
barrageChannels: 1
};
},
watch: {
barrageList(val) {
if (val.length !== 0) {
this.barrageQueue = JSON.parse(JSON.stringify(val));
this.newBarrageArray = JSON.parse(JSON.stringify(val));
this.initData();
window.cancelAnimationFrame(aniId);
aniId = window.requestAnimationFrame(this.render);
}
}
},
mounted() {
this.containerWidth =
this.deviceType === 'pc' ? 723 * 2 : document.body.clientWidth * 2;
this.containerHeight = 365 * 2; //
this.barrageChannels = this.channels; //
this.ctx = this.$refs.canvas.getContext('2d');
this.ctx1 = this.$refs.canvasContainer.getContext('2d');
this.barrageClickEvent();
},
methods: {
/**
* 数据初始化
*/
initData() {
for (let i = 0; i < this.barrageQueue.length; i++) {
// 50
let tagImg = null;
let img = null;
if (this.barrageQueue[i].icon) {
img = new Image();
img.src = this.barrageQueue[i].icon;
}
if (this.barrageQueue[i].tagImage) {
tagImg = new Image();
tagImg.src = this.barrageQueue[i].tagImage;
}
const content = this.dealStr(this.barrageQueue[i].content);
this.barrageArray.push({
id: this.barrageQueue[i].id,
content,
x: this.containerWidth + this.barrageHeight,
icon: img,
tagImage: tagImg,
width:
this.ctx1.measureText(content).width * 3.6 +
(this.barrageQueue[i].icon ? 60 : 0),
color: this.barrageQueue[i].color || '#FFFFFF',
bgColor: this.barrageQueue[i].bgColor || 'rgba(0,0,0,0.4)'
});
}
this.initChannel();
},
/**
* 初始化轨道数据
*/
initChannel() {
for (let i = 0; i < this.barrageChannels; i++) {
const item = this.barrageArray.shift();
if (item) {
this.channelsArray[i] = [item];
} else {
this.channelsArray[i] = [];
}
}
},
/**
* 渲染
*/
render() {
this.ctx.clearRect(0, 0, this.containerWidth, this.containerHeight);
this.ctx.font = '36px Microsoft YaHei';
this.draw();
window.cancelAnimationFrame(aniId1);
aniId1 = window.requestAnimationFrame(this.render);
},
draw() {
for (let i = 0; i < this.channelsArray.length; i++) {
for (let j = 0; j < this.channelsArray[i].length; j++) {
try {
const barrage = this.channelsArray[i][j];
barrage.x -= this.speed;
if (barrage.x <= this.containerWidth) {
//
this.borderColor &&
this.drawRoundRectBorder(
this.ctx,
barrage.x - this.barrageHeight / 2,
i * (this.barrageHeight + 60) + 20,
barrage.width + this.barrageHeight,
this.barrageHeight,
this.barrageHeight / 2
);
this.drawRoundRect(
this.ctx,
barrage.bgColor,
barrage.x - this.barrageHeight / 2,
i * (this.barrageHeight + 60) + 21,
barrage.width + this.barrageHeight,
this.barrageHeight - 2,
this.barrageHeight / 2
);
this.ctx.fillStyle = `${barrage.color}`;
this.ctx.fillText(
barrage.content,
barrage.x + (barrage.icon ? this.barrageHeight / 2 + 10 : -5),
i * (this.barrageHeight + 60) + this.barrageHeight - 25
);
if (barrage.icon) {
this.circleImg(
this.ctx,
barrage.icon,
barrage.x - 40,
i * (this.barrageHeight + 60) + 40,
40
);
}
if (barrage.tagImage) {
this.originImg(
this.ctx,
barrage.tagImage,
barrage.x - this.barrageHeight - 10,
i * (this.barrageHeight + 60) + 20,
this.barrageHeight,
this.barrageHeight
);
}
}
if (barrage.x < -(barrage.width + this.barrageHeight)) {
//
const arr = this.channelsArray.reduce((a, b) => a.concat(b));
if (this.loop) {
if (this.checkBarrageStatus(arr)) {
this.barrageQueue = [];
this.barrageQueue = JSON.parse(
JSON.stringify(this.newBarrageArray)
);
this.initData();
}
}
}
//
if (
barrage.x <=
Math.floor(this.containerWidth - barrage.width - 40) &&
barrage.x >=
Math.floor(
this.containerWidth - barrage.width - 40 - this.speed
) &&
j === this.channelsArray[i].length - 1 &&
this.barrageArray.length !== 0
) {
const item = this.barrageArray.shift();
this.channelsArray[i].push(item);
}
} catch (e) {
console.log(e);
}
}
}
},
/**
* 重置数据
*/
add(obj) {
const content = this.dealStr(obj.content);
let img = null;
let tagImg = null;
if (obj.icon) {
img = new Image();
img.src = obj.icon;
}
if (obj.tagImage) {
tagImg = new Image();
tagImg.src = obj.tagImage;
}
const item = {
id: obj.id,
content,
x: this.containerWidth + this.barrageHeight,
icon: obj.icon ? img : '',
tagImage: obj.tagImage ? tagImg : '',
width:
this.ctx1.measureText(content).width * 3 +
(obj.icon ? this.barrageHeight : 0),
color: obj.color || '#FFFFFF',
bgColor: obj.bgColor || 'rgba(0,0,0,0.4)'
};
const originItem = {
id: obj.id,
content: obj.content,
icon: obj.icon,
tagImage: obj.tagImage,
color: obj.color || '#FFFFFF',
bgColor: obj.bgColor || 'rgba(0,0,0,0.4)'
};
if (this.barrageArray.length === 0) {
// 0
this.newBarrageArray.unshift(originItem);
} else {
this.barrageArray.unshift(item);
const insertIndex = this.barrageList.length - this.barrageArray.length;
this.newBarrageArray.splice(insertIndex, 0, originItem);
}
},
/**
* 弹幕点击事件
*/
barrageClickEvent() {
document.getElementById('canvas').addEventListener(
'click',
(e) => {
const p = this.getEventPosition(e);
const channelIndex = Math.floor(p.y / (this.barrageHeight + 36));
const tempArray = JSON.parse(
JSON.stringify(this.channelsArray[channelIndex])
);
for (let i = 0; i < tempArray.length; i++) {
const channelItemArray = tempArray[i];
if (
p.x > channelItemArray.x &&
p.x < channelItemArray.x + channelItemArray.width
) {
if (channelItemArray.id) {
this.$emit('doLike', channelItemArray.id);
}
}
}
},
false
);
},
/**
* 获取点击位置
*/
getEventPosition(ev) {
let x, y;
if (ev.layerX || ev.layerX === 0) {
x = ev.layerX;
y = ev.layerY;
} else if (ev.offsetX || ev.offsetX === 0) {
x = ev.offsetX;
y = ev.offsetY;
}
return { x: 2 * x, y: 2 * y };
},
/**
* 判断所有的弹幕是否滚动完成
* @params arr
*/
checkBarrageStatus(arr) {
for (let i = 0; i < arr.length; i++) {
if (arr[i].x > -arr[i].width) return false;
}
return true;
},
/**
* 处理字符
*/
dealStr(str) {
return str.length > 50 ? `${str.substring(0, 50)}...` : str;
},
/**
* 获取随机颜色
*/
getColor() {
return `#${Math.floor(Math.random() * 16777215).toString(16)}`;
},
/**
* 裁剪图片
* @param ctx
* @param img
* @param x
* @param y
* @param r
*/
circleImg(ctx, img, x, y, r) {
ctx.save();
const d = 2 * r;
const cx = x + r;
const cy = y + r;
ctx.beginPath();
ctx.arc(cx, cy, r, 0, 2 * Math.PI);
ctx.clip();
ctx.drawImage(img, x, y, d, d);
ctx.restore();
ctx.closePath();
},
/**
* 绘制原始图片
* @param ctx
* @param img
* @param x
* @param y
* @param width
* @param height
*/
originImg(ctx, img, x, y, width, height) {
try {
ctx.beginPath();
ctx.drawImage(img, x, y, width, height);
ctx.closePath();
} catch (e) {
console.log(e);
}
},
/**
* 绘画圆角矩形
* @param context
* @param bgColor
* @param x
* @param y
* @param width
* @param height
* @param radius
*/
drawRoundRect(context, bgColor, x, y, width, height, radius) {
if (this.linearGradient.startColor && this.linearGradient.endColor) {
const linearGrad = context.createLinearGradient(x, y, x, y + height);
linearGrad.addColorStop(0, this.linearGradient.startColor);
linearGrad.addColorStop(1, this.linearGradient.endColor);
context.fillStyle = linearGrad || bgColor;
} else {
context.fillStyle = this.background || bgColor;
}
context.beginPath();
context.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
context.lineTo(width - radius + x, y);
context.arc(
width - radius + x,
radius + y,
radius,
(Math.PI * 3) / 2,
Math.PI * 2
);
context.lineTo(width + x, height + y - radius);
context.arc(
width - radius + x,
height - radius + y,
radius,
0,
Math.PI / 2
);
context.lineTo(radius + x, height + y);
context.arc(
radius + x,
height - radius + y,
radius,
Math.PI / 2,
Math.PI
);
context.fill();
context.closePath();
},
/**
* 绘画圆角矩形
* @param context
* @param x
* @param y
* @param width
* @param height
* @param radius 半径
*/
drawRoundRectBorder(context, x, y, width, height, radius) {
context.beginPath();
context.lineWidth = 2;
context.strokeStyle = this.borderColor;
context.arc(x + radius, y + radius, radius, Math.PI, (Math.PI * 3) / 2);
context.lineTo(width - radius + x, y);
context.arc(
width - radius + x,
radius + y,
radius,
(Math.PI * 3) / 2,
Math.PI * 2
);
context.lineTo(width + x, height + y - radius);
context.arc(
width - radius + x,
height - radius + y,
radius,
0,
Math.PI / 2
);
context.lineTo(radius + x, height + y);
context.arc(
radius + x,
height - radius + y,
radius,
Math.PI / 2,
Math.PI
);
context.stroke();
context.closePath();
}
}
};
</script>
<style lang="css" scoped>
.z_barrage-container {
pointer-events: none;
}
.z_container {
width: 100%;
overflow: hidden;
}
.z_barrage {
position: absolute;
top: 0;
left: 0;
}
</style>

440
src/components/BottomRightFixed.vue

@ -0,0 +1,440 @@ @@ -0,0 +1,440 @@
<template>
<div>
<div
class="fixed-box"
:class="{ isMobile: deviceType !== 'pc' }"
:style="{ right: right + 'px' }"
>
<div v-if="scrollTop > 200" @click="goTop">
<i class="el-icon-arrow-up"></i><span>TOP</span>
</div>
<template v-if="deviceType !== 'pc'">
<div
v-if="showCollect"
:style="{ color: isCollect == 1 ? '#EAAB00' : '' }"
@click="collectClick"
>
<i class="el-icon-star-on"></i
><span>{{ isCollect == 1 ? '已' : '' }}收藏</span>
</div>
<div v-if="showShare" @click="showSharePop">
<i class="el-icon-share"></i><span>分享</span>
</div>
<div v-if="showComment" @click="commentClick">
<img src="~/assets/images/comment.png" alt="" /><span>评论</span>
</div>
<div v-if="showLike" @click="likeClick">
<img
v-if="isLike == 1"
src="~/assets/images/like_chosen.png"
alt=""
/><img v-else src="~/assets/images/like.png" alt="" /><span
:style="{ color: isLike == 1 ? '#E6553F' : '' }"
>{{ likeNum }}</span
>
</div>
</template>
</div>
<div
v-if="deviceType === 'pc'"
class="fixed-box left-fixed"
:style="{
position: hasScroll ? 'fixed' : 'absolute',
top: hasScroll ? '236px' : '',
left: left + 'px'
}"
>
<div
v-if="showCollect"
:style="{ color: isCollect == 1 ? '#EAAB00' : '' }"
@click="collectClick"
>
<i class="el-icon-star-on"></i
><span>{{ isCollect == 1 ? '已' : '' }}收藏</span>
</div>
<el-popover
placement="right"
width="174"
:visible-arrow="false"
popper-class="share-hover-box"
trigger="hover"
>
<div class="share-box">
<el-popover
placement="top-start"
width="176"
:visible-arrow="false"
popper-class="wechat-qrcode-box"
trigger="hover"
>
<div class="qrcode-box">
<p>
<img
src="~/assets/images/wechat_color.png"
alt="扫码分享至微信"
/>
</p>
<vue-qr class="bicode" :size="220" :text="codeValue"></vue-qr>
</div>
<p slot="reference" @click="share('微信')">
<img src="~/assets/images/share_wechat.png" alt="" />
</p>
</el-popover>
<p @click="share('qq')">
<img src="~/assets/images/share_qq.png" alt="" />
</p>
<p @click="share('微博')">
<img src="~/assets/images/share_weibo.png" alt="" />
</p>
</div>
<div v-if="showShare" slot="reference" @click="pcShareClick">
<i class="el-icon-share"></i><span>分享</span>
</div>
</el-popover>
<div v-if="showComment" @click="commentClick">
<img src="~/assets/images/comment.png" alt="" /><span>评论</span>
</div>
<div v-if="showLike" @click="likeClick">
<img
v-if="isLike == 1"
src="~/assets/images/like_chosen.png"
alt=""
/><img v-else src="~/assets/images/like.png" alt="" /><span
:style="{ color: isLike == 1 ? '#E6553F' : '' }"
>{{ likeNum }}</span
>
</div>
</div>
<div
v-if="sharePopupShow"
class="share-popup"
@click="sharePopupShow = false"
>
<template v-if="env === 'wechat' || env === 'weibo' || env === 'qq'">
<img src="~/assets/images/share_point.png" />
<div>
<p>点击右上角</p>
<p>分享给朋友</p>
</div>
</template>
<template v-else>
<p>点击浏览器分享图标发送好友</p>
</template>
</div>
</div>
</template>
<script>
export default {
props: {
showCollect: Boolean,
showShare: Boolean,
showComment: Boolean,
showLike: Boolean,
isLike: {
type: Number,
default: 0
},
likeNum: {
type: Number,
default: 0
},
isCollect: {
type: Number,
default: 0
},
isLogin: {
type: Boolean,
default: false
}
},
data() {
return {
scrollTop: 0,
left: -89,
right: 0,
codeValue: '',
hasScroll: false,
sharePopupShow: false
};
},
computed: {
deviceType() {
return this.$store.state.device.deviceType;
},
env() {
return this.$store.state.device.env;
}
},
mounted() {
const that = this;
let href = window.location.href;
if (that.$route.name !== 'video-id') {
const origin =
process.env.VUE_APP_TITLE !== 'production'
? 'https://apptest.diabetes.com.cn'
: 'https://app.diabetes.com.cn';
if (that.$route.name === 'doctor-id') {
href = origin + '/expert-video';
} else {
href = origin + '/library/detail/' + that.$route.params.id;
}
}
this.codeValue = href;
if (that.deviceType !== 'pc' && that.env === 'wechat' && that.showShare) {
that.wxShare(
{
title: document.title,
desc: document
.querySelector('meta[name="description"]')
.getAttribute('content'),
url: href,
image: location.origin + require('~/assets/images/mobile_logo.png')
},
() => {
that.sharePopupShow = false;
}
);
}
this.right = (document.documentElement.offsetWidth - 1258) / 2;
this.$nextTick(() => {
window.addEventListener('scroll', function () {
that.hasScroll = true;
// scrollTop
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
const scrollLeft =
document.documentElement.scrollLeft || document.body.scrollLeft;
that.scrollTop = scrollTop;
if (document.documentElement.offsetWidth < 1258) {
that.right =
0 - (1258 - document.documentElement.offsetWidth) + scrollLeft;
that.left = 0 - scrollLeft;
} else {
that.right = (document.documentElement.offsetWidth - 1258) / 2;
that.left = (document.documentElement.offsetWidth - 1258) / 2;
}
});
});
},
methods: {
goTop() {
document.documentElement.scrollTop = 0;
document.body.scrollTop = 0;
},
commentClick() {
this.$emit('commentClick');
},
likeClick() {
this.$emit('likeClick');
},
collectClick() {
this.$emit('collectClick');
},
share(types) {
const title = document.title;
// logo
// url
const url = document.location.href;
//
const description = document
.querySelector('meta[name="description"]')
.getAttribute('content');
//
const keywords = document
.querySelector('meta[name="keywords"]')
.getAttribute('content');
if (types === '微博') {
window.open(
'http://service.weibo.com/share/share.php?url=' +
url +
'&sharesource=weibo&title=' +
title +
' ' +
description,
'_blank'
);
}
// qq
if (types === 'qq') {
window.open(
'http://connect.qq.com/widget/shareqq/index.html?url=' +
url +
'&sharesource=qq&title=' +
title +
'&summary=' +
description +
'&desc=' +
keywords,
'_blank'
);
}
if (this.$route.name !== 'article-id') {
this.baiduStat(
(this.$route.name === 'video-id' ? '视频' : '一问医答') + '详情页',
'click',
(this.$route.name === 'video-id' ? '视频' : '一问医答') +
'分享-' +
types
);
}
},
pcShareClick() {
if (this.$route.name === 'article-id') {
this.baiduStat('文章详情页', 'click', '文章内分享');
}
},
showSharePop() {
const that = this;
that.sharePopupShow = true;
this.baiduStat('移动端', 'click', '移动端-分享页面');
if (this.env !== 'wechat') {
setTimeout(() => {
that.sharePopupShow = false;
}, 3000);
}
}
}
};
</script>
<style lang="scss">
.fixed-box {
position: fixed;
bottom: 130px;
right: 0;
width: 60px;
z-index: 9;
div {
margin-top: 1px;
width: 62px;
height: 62px;
background: $bg-color;
border-radius: 4px;
text-align: center;
cursor: pointer;
color: $font-color2;
i,
span {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
line-height: 20px;
}
i {
margin-top: 12px;
font-size: 30px;
}
img {
margin-top: 10px;
width: 26px;
}
span {
display: block;
}
}
&.isMobile {
top: auto !important;
right: 15px !important;
bottom: 70px !important;
transform: scale(0.85);
transform-origin: bottom;
}
&.left-fixed {
position: absolute;
top: 150px;
bottom: auto;
left: -89px;
}
}
.share-hover-box {
border: none;
box-shadow: none;
padding: 11px 0 11px 11px;
height: 61px;
background: $bg-color;
box-sizing: border-box;
transform: translateX(-15px);
border-radius: 0 50px 50px 0;
.share-box {
font-size: 0;
& > span,
& > p {
display: inline-block;
margin-right: 12px;
background: $body-color;
cursor: pointer;
@include roundFun(40px);
img {
@include roundFun(40px);
}
}
}
&.left-show {
transform: translateX(15px) scale(0.85);
transform-origin: bottom;
border-radius: 50px 0 0 50px;
}
}
.wechat-qrcode-box {
border: 1px solid #ededed;
padding: 14px 0 16px;
background: $body-color;
box-sizing: border-box;
.qrcode-box {
text-align: center;
p {
margin-bottom: 6px;
font-size: 16px;
color: $font-color1;
line-height: 28px;
img {
width: 28px;
height: 28px;
}
}
& > img {
width: 110px;
}
}
}
.share-popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 10;
p {
font-size: 16px;
color: #fff;
text-align: center;
}
div {
text-align: right;
float: right;
position: absolute;
right: 0;
top: 60px;
width: 150px;
p {
display: block;
}
}
img {
width: 210px;
height: 124px;
float: right;
}
& > p {
position: absolute;
bottom: 20px;
width: 100%;
}
}
</style>

1418
src/components/CommonHeader.vue

File diff suppressed because it is too large Load Diff

101
src/components/DoctorItem.vue

@ -0,0 +1,101 @@ @@ -0,0 +1,101 @@
<template>
<div
:key="detail.id"
class="doctor-item"
@click="goDoctor(detail.video_list[0].id)"
>
<img :src="detail.expert_img" :alt="detail.username" />
<h5>{{ detail.username }} {{ detail.title }}</h5>
<p class="text-ellipsis">{{ detail.hospital_name }}</p>
<p class="text-ellipsis">{{ detail.department }}</p>
<div class="expert-video-list">
<template v-for="(video, index) in detail.video_list">
<p
v-if="index < 2"
:key="'live' + video.id"
@click.stop="goDoctor(video.id)"
>
<img src="~/assets/images/doctor_play_icon.png" alt="" />
<span>{{ video.title }}</span>
</p>
</template>
</div>
</div>
</template>
<script>
export default {
props: {
detail: {
type: Object,
default: null
},
columnNum: {
type: Number,
default: 1
}
},
created() {},
methods: {
goDoctor(id) {
this.$emit('addStat');
const { href } = this.$router.resolve({
path: '/doctor/' + id
});
window.open(href, '_blank');
}
}
};
</script>
<style lang="scss">
.doctor-item {
padding: 20px 10px 0;
width: 230px;
height: 385px;
background: $bg-color;
border-radius: 4px;
box-sizing: border-box;
text-align: center;
cursor: pointer;
& > img {
margin: 0 auto;
border: 1px solid #ededed;
background: $body-color;
@include roundFun(108px);
}
h5 {
margin-top: 10px;
font-size: 18px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: $main-blue-color;
line-height: 24px;
}
& > p {
font-size: 16px;
color: $font-color3;
line-height: 21px;
}
& > p:nth-child(3) {
margin: 11px 0 1px;
}
.expert-video-list {
margin-top: 21px;
p {
display: flex;
margin-bottom: 22px;
text-align: left;
align-items: center;
img {
margin-right: 10px;
@include roundFun(30px);
}
span {
@include textEllipsisMore1Line(2);
font-size: 16px;
color: $font-color1;
line-height: 21px;
}
}
}
}
</style>

95
src/components/DoctorVideoItem.vue

@ -0,0 +1,95 @@ @@ -0,0 +1,95 @@
<template>
<div class="doctor-video-box" @click="goDoctor(video.id)">
<h6>{{ video.title }}</h6>
<span>{{ video.record_num }}次播放</span>
<p class="play-video">
<img
src="~/assets/images/mobile_doctor_play.png"
alt="点击播放"
/>
</p>
</div>
</template>
<script>
export default {
props: {
video: {
type: Object,
default: null
}
},
created() {},
methods: {
goDoctor(id) {
this.$emit('addStat');
const { href } = this.$router.resolve({
path: '/doctor/' + id
});
window.location.href = href;
}
}
};
</script>
<style lang="scss">
.doctor-video-box {
position: relative;
padding: 20px 15px;
/*height: 127px;*/
background: $body-color;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.06);
box-sizing: border-box;
cursor: pointer;
h6 {
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333;
line-height: 25px;
@include textEllipsisMore1Line(2);
}
span {
display: inline-block;
margin: 11px 0 0 -15px;
padding: 0 9px 0 14px;
height: 30px;
background: #f7f7fa;
border-radius: 0 69px 69px 0;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #7d7d7d;
line-height: 30px;
}
p {
float: right;
margin-top: 10px;
width: 134px;
height: 32px;
background: linear-gradient(180deg, #fad961 0%, #f76b1c 100%);
border-radius: 20px;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #fafafc;
line-height: 32px;
text-align: center;
img {
position: relative;
top: 5px;
display: inline-block;
margin-right: 6px;
width: 21px;
height: 21px;
}
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 12px;
width: calc(100% - 24px);
height: 1px;
background: #ddd;
}
}
</style>

251
src/components/MobileHeader.vue

@ -0,0 +1,251 @@ @@ -0,0 +1,251 @@
<template>
<div
class="mobile-header"
:style="{ position: isIndex ? (needFixed ? 'fixed' : '') : 'fixed' }"
>
<div ref="mobileHeader">
<div>
<p
v-for="item in tabArr"
:key="item.name"
:class="{ chosenMobileTab: activeTab === item.name }"
@click="handleClick(item)"
>
{{ item.font }}
</p>
</div>
</div>
</div>
</template>
<script>
function getDay() {
const today = new Date();
const y = today.getFullYear();
let m = today.getMonth() + 1;
let d = today.getDate();
m = m < 10 ? '0' + m : m;
d = d < 10 ? '0' + d : d;
return y + '-' + m + '-' + d;
}
export default {
props: {
isIndex: {
type: Boolean,
default: false
},
needFixed: {
type: Boolean,
default: false
},
bannerHeight: {
type: Number,
default: 0
},
isMore: {
type: Boolean,
default: false
}
},
data() {
return {
activeTab: '',
tabArr: [
{
font: '推荐',
name: 'index',
url: '/',
statInfo: { category: '推荐', des: '推荐tab' }
},
{
font: '视频',
name: 'video',
url: '/video',
statInfo: { category: '视频', des: '视频tab' }
},
{
font: '一问医答',
name: 'doctor',
url: '/doctor',
statInfo: { category: '一问医答', des: '一问医答tab' }
},
{
font: '药物',
name: 'article-medicine',
url: '/article?labelId=-1',
statInfo: { category: '文章', des: '文章-药物tab' }
},
{
font: '饮食',
name: 'article-food',
url: '/article?labelId=2',
statInfo: { category: '文章', des: '文章-饮食tab' }
},
{
font: '运动',
name: 'article-sport',
url: '/article?labelId=3',
statInfo: { category: '文章', des: '文章-运动tab' }
},
{
font: '并发症',
name: 'article-complication',
url: '/article?labelId=17',
statInfo: { category: '文章', des: '文章-并发症tab' }
},
{
font: '监测',
name: 'article-survey',
url: '/article?labelId=-2',
statInfo: { category: '文章', des: '文章-监测tab' }
},
{
font: '心理',
name: 'article-mind',
url: '/article?labelId=18',
statInfo: { category: '文章', des: '文章-心理tab' }
}
]
};
},
created() {
let activeName = this.$route.name;
if (this.$route.name === 'article') {
const labelId = Number(this.$route.query.labelId);
let affixArticle = '';
switch (labelId) {
case -1:
affixArticle = 'medicine';
break;
case 2:
affixArticle = 'food';
break;
case 3:
affixArticle = 'sport';
break;
case 17:
affixArticle = 'complication';
break;
case -2:
affixArticle = 'survey';
break;
case 18:
affixArticle = 'mind';
break;
default:
break;
}
activeName += '-' + affixArticle;
}
this.activeTab = activeName;
},
mounted() {
if (!this.isMore) {
if (this.activeTab === 'index') {
if (!localStorage.getItem('indexTabAniDay')) {
this.animationFun();
localStorage.setItem('indexTabAniDay', getDay());
} else {
let _day = localStorage.getItem('indexTabAniDay');
if (getDay() !== _day) {
this.animationFun();
_day = getDay();
localStorage.setItem('indexTabAniDay', _day);
}
}
}
const left =
document.getElementsByClassName('chosenMobileTab')[0].offsetLeft;
const centerL =
((document.documentElement.clientWidth || document.body.clientWidth) -
70) /
2;
document
.getElementsByClassName('mobile-header')[0]
.scrollTo(left - centerL, 0);
}
},
methods: {
animationFun() {
const windowW =
document.documentElement.clientWidth || document.body.clientWidth;
const tabW = this.$refs.mobileHeader.clientWidth;
this.$refs.mobileHeader.animate(
[{ marginLeft: '0' }, { marginLeft: windowW - tabW + 'px' }],
{
duration: 1000,
delay: 1000
}
);
this.$refs.mobileHeader.animate(
[
{ marginLeft: windowW - tabW + 'px' },
{ marginLeft: windowW - tabW + 'px' }
],
{
duration: 200,
delay: 2000
}
);
this.$refs.mobileHeader.animate(
[{ marginLeft: windowW - tabW + 'px' }, { marginLeft: 0 }],
{
duration: 1000,
delay: 2200
}
);
},
handleClick(item) {
if (item.name !== this.activeTab) {
this.baiduStat(
(this.isMore ? '更多页面-' : '导航栏-') + item.statInfo.category,
'click',
item.statInfo.des
);
const { href } = this.$router.resolve({
path: item.url
});
window.location.href = href;
}
}
}
};
</script>
<style lang="scss">
.mobile-header {
overflow-x: scroll;
top: 60px;
display: flex;
border-bottom: 1px solid $bg-color;
width: 100%;
height: 48px;
background: $body-color;
white-space: nowrap;
z-index: 2;
& > div {
& > div {
display: flex;
overflow: hidden;
height: 48px;
position: relative;
p {
flex: none;
width: 70px;
font-size: 15px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color1;
line-height: 48px;
text-align: center;
cursor: pointer;
&.chosenMobileTab {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $main-blue-color;
}
}
}
}
}
</style>

74
src/components/NotLogin.vue

@ -0,0 +1,74 @@ @@ -0,0 +1,74 @@
<template>
<div class="mobile-not-login">
<i class="el-icon-error" @click="hideClick"></i>
<img src="~/assets/images/wechat_color.png" alt="" />
<p>关注公众号登录查看更多资讯</p>
<p
@click="
baiduStat('移动端-底部控件', 'click', '移动端底部登录控件');
showLoginDialog(true);
"
>
GO
</p>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapActions } = createNamespacedHelpers('user');
export default {
methods: {
...mapActions({
showLoginDialog: 'showLoginDialog'
}),
hideClick() {
this.$emit('hideClick');
}
}
};
</script>
<style lang="scss">
.mobile-not-login {
position: fixed;
bottom: 0;
left: 0;
display: flex;
padding: 10px 15px;
padding-bottom: calc(constant(safe-area-inset-bottom) + 10px);
padding-bottom: calc(env(safe-area-inset-bottom) + 10px);
width: calc(100% - 30px);
height: 35px;
background: $body-color;
align-items: center;
z-index: 9;
i {
font-size: 14px;
color: #bac0c2;
}
img {
margin: 0 2px 0 5px;
width: 26px;
height: 26px;
}
p:nth-child(3) {
font-size: 15px;
color: $font-color1;
line-height: 20px;
}
p:last-child {
position: absolute;
right: 15px;
width: 75px;
height: 35px;
background: $extra-red;
border-radius: 4px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $body-color;
line-height: 35px;
text-align: center;
}
}
</style>

304
src/components/UserInfoCommon.vue

@ -0,0 +1,304 @@ @@ -0,0 +1,304 @@
<template>
<div>
<div class="top-user-info">
<div>
<div class="left-content">
<template v-if="deviceType === 'pc'">
<el-avatar :size="100" :src="userImg"></el-avatar>
<p>{{ userName }}</p>
<NuxtLink
to="/user/setting"
target="_blank"
@click.native="
baiduStat('我的账户页', 'click', '我的账户页-点击设置')
"
><i class="el-icon-s-tools"></i>账户设置</NuxtLink
>
</template>
<a
v-else
href="/user/setting"
@click="baiduStat('我的账户页', 'click', '我的账户页-点击设置')"
>
<el-avatar :size="100" :src="userImg"></el-avatar>
<p>{{ userName }}</p>
<i class="el-icon-s-tools"></i>
</a>
</div>
<div class="right-content">
<img src="~/assets/images/beans.png" alt="" />
<p>我的金豆</p>
<h5
@click="
baiduStat('我的账户页', 'click', '我的账户页-点击金豆余额');
dialogVisible = true;
"
>
{{ beanNum }}<i class="el-icon-caret-right"></i>
</h5>
</div>
</div>
</div>
<div class="user-collect">
<h6>
我的收藏 <span>{{ total }}</span>
</h6>
<div class="common-flex">
<template v-for="item in articleList">
<VideoArticleListItem
:key="'collect' + item.id"
:detail="item"
:column-num="4"
:device-type="deviceType"
@addStat="
baiduStat('我的账户页', 'click', '我的账户页-点击收藏的文章')
"
/>
</template>
</div>
<p v-if="collectLoading" class="load-more">
加载更多<i class="el-icon-loading"></i>
</p>
</div>
<el-dialog
title=""
:visible.sync="dialogVisible"
custom-class="beans-popup"
:show-close="false"
:width="deviceType === 'pc' ? '795px' : '280px'"
>
<div class="popup-content">
<img
v-if="deviceType === 'pc'"
src="~/assets/images/beans_popup_bg.png"
alt=""
/>
<div>
<p>
<img src="~/assets/images/wechat_white.png" alt="" />扫码前往公众号
</p>
<img :src="isPro ? qrcodeUrl : qrcodeUrlTest" alt="" />
<h5>公众号内做任务赢金豆<br />海量精美礼品免费兑换</h5>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapState } = createNamespacedHelpers('user');
export default {
props: {
beanNum: {
type: Number,
default: 0
},
total: {
type: Number,
default: 0
},
articleList: {
type: Array,
default: () => []
},
hasMore: {
type: Boolean,
default: true
},
collectLoading: {
type: Boolean,
default: false
}
},
data() {
return {
dialogVisible: false,
isPro: false,
qrcodeUrl: require('~/assets/images/follow_qrcode.png'),
qrcodeUrlTest: require('~/assets/images/follow_qrcode_test.png')
};
},
computed: {
deviceType() {
return this.$store.state.device.deviceType;
},
...mapState({
userInfo: (state) => state.info
}),
userName() {
if (
this.userInfo !== null &&
this.userInfo.name !== null &&
this.userInfo.name !== ''
) {
return this.userInfo.name;
}
return '--';
},
userImg() {
if (
this.userInfo !== null &&
this.userInfo.headimg !== null &&
this.userInfo.headimg !== ''
) {
return this.userInfo.headimg;
}
return 'https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png';
}
},
created() {
this.isPro = process.env.VUE_APP_TITLE === 'production';
}
};
</script>
<style lang="scss">
.top-user-info {
position: absolute;
top: 86px;
left: 0;
width: 100%;
min-width: $outer-width;
height: 259px;
background: $bg-color;
& > div {
margin: 0 auto;
width: $content-width;
& > div {
margin-top: 36px;
height: 183px;
background: $body-color;
box-sizing: border-box;
}
.left-content {
position: relative;
padding: 82px 272px 0 153px;
& > span {
position: absolute;
top: 44px;
left: 37px;
}
& > p {
font-size: 18px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: $font-color1;
line-height: 24px;
@extend .text-ellipsis;
}
& > a {
position: absolute;
top: 69px;
right: 44px;
width: 208px;
height: 50px;
background: #3099ea;
border-radius: 4px;
text-align: center;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $body-color;
line-height: 50px;
i {
margin-right: 5px;
}
}
}
.right-content {
position: relative;
padding: 65px 0 0 159px;
& > img {
position: absolute;
top: 57px;
left: 69px;
width: 74px;
height: 74px;
}
p {
font-size: 15px;
color: $font-color3;
line-height: 20px;
}
h5 {
margin-top: 8px;
font-size: 24px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: $extra-red;
line-height: 31px;
cursor: pointer;
i {
font-size: 20px;
color: $extra-blue;
}
}
}
}
}
.user-collect {
padding-top: 50px;
& > h6 {
margin-bottom: 10px;
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $main-blue-color;
line-height: 33px;
span {
color: $extra-red;
}
}
.common-flex {
justify-content: flex-start;
.detail-list-item {
margin: 0 19.3px 20px 0;
}
.detail-list-item:nth-child(4n) {
margin-right: 0;
}
}
}
.beans-popup {
background: transparent;
box-shadow: none;
.el-dialog__header {
padding: 0;
}
.el-dialog__body {
padding: 0;
.popup-content {
position: relative;
& > img {
width: 795px;
height: 407px;
}
& > div {
position: absolute;
right: 231px;
bottom: 29px;
text-align: center;
color: $body-color;
p {
font-size: 16px;
line-height: 24px;
img {
margin-right: 8px;
width: 24px;
}
}
& > img {
margin: 12px 0 9px;
width: 143px;
height: 143px;
}
h5 {
font-size: 24px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
line-height: 36px;
}
}
}
}
}
</style>

375
src/components/VideoArticleListItem.vue

@ -0,0 +1,375 @@ @@ -0,0 +1,375 @@
<template>
<div
v-if="detail.mainModule == 38"
class="only-img detail-list-item large-width"
:class="
'one-line-columns-' + columnNum + (deviceType !== 'pc' ? ' isMobile' : '')
"
@click="goHref(detail.action_url)"
>
<img :src="detail.image_url" alt="" />
</div>
<div
v-else
class="detail-list-item"
:class="
(detail.showType == 2 ? 'large-width ' : '') +
('one-line-columns-' + columnNum) +
(deviceType !== 'pc' ? ' isMobile' : '') +
(detail.mainModule == 47 ? ' video-item' : ' article-item') +
(detail.intro && detail.intro !== '' ? ' has-intro' : ' no-intro')
"
@click="
goHref((detail.mainModule == 47 ? '/video/' : '/article/') + detail.id)
"
>
<div class="top-img-box">
<img
v-if="detail.mainModule !== 47 && !detail.thumb"
src="~/assets/images/article_cover.png"
alt=""
/>
<img
v-else
:src="detail.mainModule == 47 ? detail.video_cover_url : detail.thumb"
alt=""
/>
<p v-if="deviceType !== 'pc' && detail.intro && detail.intro !== ''">
{{ detail.intro }}
</p>
<template v-if="detail.mainModule == 47">
<span
>{{
parseInt(detail.video_time / 60) > 9
? parseInt(detail.video_time / 60)
: '0' + parseInt(detail.video_time / 60)
}}:{{
parseInt(detail.video_time % 60) > 9
? parseInt(detail.video_time % 60)
: '0' + parseInt(detail.video_time % 60)
}}</span
>
<img class="play" src="~/assets/images/video_play_icon.png" alt="" />
</template>
</div>
<p>{{ detail.title }}</p>
<div class="bottom">
<span
:style="{
display: (
detail.mainModule == 47 ? detail.tag_name : detail.labelName
)
? ''
: 'none'
}"
>{{
detail.mainModule == 47 ? detail.tag_name : detail.labelName
}}</span
>
<span
>{{ detail.mainModule == 47 ? detail.play_num : detail.hit
}}<template v-if="deviceType !== 'pc'"
>{{ detail.mainModule == 47 ? '播放' : '阅读' }}&nbsp;&nbsp;</template
><i v-else class="el-icon-view" style="margin-right: 8px"></i
>{{ detail.mainModule == 47 ? detail.like_num : detail.collect
}}<template v-if="deviceType !== 'pc'"
>{{ detail.mainModule == 47 ? '喜欢' : '收藏' }}&nbsp;&nbsp;</template
><template v-else>
<img
v-if="detail.mainModule == 47"
src="~/assets/images/heart_gray.png"
alt=""
/><i
v-else
class="el-icon-star-on"
style="margin-left: 2px; font-size: 18px"
></i> </template
></span>
</div>
</div>
</template>
<script>
export default {
props: {
detail: {
type: Object,
default: null
},
columnNum: {
type: Number,
default: 1
},
deviceType: {
type: String,
default: 'pc'
}
},
created() {},
methods: {
goHref(url) {
this.$emit('addStat');
let openNew = false;
if (this.deviceType === 'pc') {
openNew = true;
}
if (openNew) {
window.open(url, '_blank');
} else {
window.location.href = url;
}
}
}
};
</script>
<style lang="scss">
.detail-list-item {
display: block;
overflow: hidden;
position: relative;
margin-bottom: 18px;
background: $bg-color;
border-radius: 4px;
cursor: pointer;
.top-img-box {
position: relative;
height: 131px;
& > img:first-child {
width: 100%;
height: 100%;
}
span {
position: absolute;
top: 12px;
right: 12px;
padding: 0 9px;
height: 24px;
background: rgba(0, 0, 0, 0.4);
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $body-color;
line-height: 24px;
border-radius: 4px;
}
& > img.play {
position: absolute;
top: 50%;
left: 50%;
margin: -34px 0 0 -34px;
width: 68px;
height: 67px;
}
}
& > p {
padding: 12px 10px 0;
font-size: 16px;
color: $font-color2;
line-height: 26px;
@include textEllipsisMore1Line(2);
}
.bottom {
position: absolute;
bottom: 0;
left: 0;
padding: 0 10px 22px;
width: 100%;
height: 46px;
box-sizing: border-box;
span:first-child {
padding: 0 9px;
height: 24px;
border-radius: 4px;
border: 1px solid #bac0c2;
box-sizing: border-box;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color3;
line-height: 22px;
}
span:last-child {
float: right;
font-size: 16px;
font-family: Apis-Regular, Apis;
color: #bac0c2;
line-height: 24px;
i {
margin-left: 4px;
}
img {
margin: 4px 0 0 4px;
width: 16px;
}
}
}
&.one-line-columns-3 {
width: 230px;
height: 274px;
}
&.one-line-columns-4 {
width: 255px;
height: 298px;
.top-img-box {
height: 144px;
}
& > p {
padding-top: 18px;
}
.bottom {
padding-bottom: 30px;
height: 54px;
}
&.large-width {
position: relative;
width: 529px;
grid-column-start: span 2;
.top-img-box {
height: 100%;
& > img.play {
display: none;
}
}
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 120px;
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.4) 100%
);
}
& > p {
position: absolute;
bottom: 62px;
left: 12px;
padding: 0;
width: calc(100% - 24px);
@extend .text-ellipsis;
z-index: 1;
font-size: 18px;
color: $body-color;
}
.bottom {
/*padding: 0 12px 14px;*/
/*height: 38px;*/
z-index: 1;
span:first-child {
border-color: rgba(255, 255, 255, 0.6);
color: $body-color;
}
span:last-child {
color: $body-color;
}
}
&.only-img {
&::after {
display: none;
}
& > img {
width: 100%;
height: 100%;
}
}
}
}
&.isMobile {
position: relative;
display: flex;
margin-bottom: 1px !important;
padding: 15px 15px 43px;
width: 100% !important;
height: auto !important;
flex-direction: column-reverse;
background: $body-color;
box-sizing: border-box;
& > p {
position: unset !important;
display: block;
margin-bottom: 10px;
padding: 0;
width: 100% !important;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $font-color1 !important;
line-height: 25px;
white-space: normal !important;
}
.top-img-box {
height: auto;
img {
border-radius: 6px;
}
span {
top: auto;
bottom: 12px;
}
img.play {
display: block !important;
}
}
.bottom {
position: absolute;
bottom: 13px;
left: 15px;
padding: 0;
width: auto;
height: 20px;
span:first-child {
display: none;
}
span:last-child {
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #bac0c2 !important;
line-height: 20px;
}
}
&.article-item {
.top-img-box {
position: relative;
padding-right: 125px;
height: 80px;
img {
position: absolute;
top: 0;
right: 0;
width: 110px;
height: 80px;
border-radius: 6px;
}
}
&.no-intro {
& > p {
position: absolute !important;
top: 15px;
padding-right: 125px;
width: calc(100% - 30px) !important;
box-sizing: border-box;
@include textEllipsisMore1Line(3);
}
}
&.has-intro {
.top-img-box {
p {
@include textEllipsisMore1Line(3);
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color2;
line-height: 24px;
}
}
}
}
&::after {
display: none;
}
}
}
</style>

143
src/components/WebFooter.vue

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
<template>
<div class="web-footer" :class="{ isMobile: deviceType !== 'pc' }">
<template v-if="deviceType !== 'pc'">
<p>
© {{ new Date().getFullYear() }} 糖尿病网&nbsp;&nbsp;&nbsp;
<a
href="https://www.diabetes.com.cn/uploadfile/jpg/medicine_info_service_diabetes.jpg"
target="_blank"
@click="
baiduStat(
'药品医疗器械网络信息服务备案',
'click',
'药品医疗器械网络信息服务备案'
)
"
>药品医疗器械网络信息服务备案() 网药械信息备字 (2023)第00432号</a
><br />
<a
href="https://beian.miit.gov.cn/"
target="_blank"
@click="baiduStat('icp证书', 'click', 'icp证书')"
>京ICP备14012503号-4</a
><br />
<a
href="http://www.beian.gov.cn/"
target="_blank"
@click="baiduStat('公安备案', 'click', '公安备案')"
>京公网安备 11000002002023</a
>
</p>
</template>
<template v-else>
<template v-if="detailCommon">
<p>
© {{ new Date().getFullYear() }} 糖尿病网&nbsp;&nbsp;&nbsp;<a
href="https://www.diabetes.com.cn/uploadfile/jpg/medicine_info_service_diabetes.jpg"
target="_blank"
@click="
baiduStat(
'药品医疗器械网络信息服务备案',
'click',
'药品医疗器械网络信息服务备案'
)
"
>药品医疗器械网络信息服务备案() 网药械信息备字
(2023)第00432号</a
>&nbsp;&nbsp;&nbsp;<a
href="https://beian.miit.gov.cn/"
target="_blank"
@click="baiduStat('icp证书', 'click', 'icp证书')"
>京ICP备14012503号-4</a
>&nbsp;&nbsp;&nbsp;<a
href="http://www.beian.gov.cn/"
target="_blank"
@click="baiduStat('公安备案', 'click', '公安备案')"
>京公网安备 11000002002023</a
>
</p>
</template>
<template v-else>
<p>
© {{ new Date().getFullYear() }} 糖尿病网&nbsp;&nbsp;&nbsp;<a
href="https://www.diabetes.com.cn/uploadfile/jpg/medicine_info_service_diabetes.jpg"
target="_blank"
@click="
baiduStat(
'药品医疗器械网络信息服务备案',
'click',
'药品医疗器械网络信息服务备案'
)
"
>药品医疗器械网络信息服务备案() 网药械信息备字
(2023)第00432号</a
>&nbsp;&nbsp;&nbsp;<a
href="https://beian.miit.gov.cn/"
target="_blank"
@click="baiduStat('icp证书', 'click', 'icp证书')"
>京ICP备14012503号-4</a
>&nbsp;&nbsp;&nbsp;<a
href="http://www.beian.gov.cn/"
target="_blank"
@click="baiduStat('公安备案', 'click', '公安备案')"
>京公网安备 11000002002023</a
>
</p>
</template>
</template>
</div>
</template>
<script>
export default {
props: {
detailCommon: Boolean,
deviceType: {
type: String,
default: 'pc'
}
}
};
</script>
<style lang="scss">
.web-footer {
margin: 0 auto;
padding-top: 18px;
width: $content-width;
height: 66px;
text-align: left;
box-sizing: border-box;
p {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color3;
line-height: 30px;
span {
float: right;
}
a {
color: $font-color3;
}
}
&.isMobile {
padding-top: 0 !important;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
width: 100%;
height: auto;
box-sizing: border-box;
background: $main-blue-color;
text-align: center;
p {
color: $body-color !important;
a {
color: $body-color !important;
}
}
p:first-child {
padding: 15px 0;
line-height: 16px !important;
}
}
}
</style>

115
src/components/WebHeader.vue

@ -0,0 +1,115 @@ @@ -0,0 +1,115 @@
<template>
<div class="web-header">
<NuxtLink
to="/other/about"
target="_blank"
@click.native="baiduStat('关于我们', 'click', '关于我们')"
>关于我们</NuxtLink
>
<NuxtLink
to="/other/agreement"
target="_blank"
@click.native="baiduStat('用户协议', 'click', '用户协议')"
>用户协议</NuxtLink
>
<NuxtLink
to="/other/privacy"
target="_blank"
@click.native="baiduStat('隐私政策', 'click', '隐私政策')"
>隐私政策</NuxtLink
>
<NuxtLink
to="/other/disclaimer"
style="display: none"
target="_blank"
@click.native="baiduStat('免责声明', 'click', '免责声明')"
>免责声明</NuxtLink
>
<NuxtLink
to="/other/link"
style="display: none"
target="_blank"
@click.native="baiduStat('友情链接', 'click', '友情链接')"
>友情链接</NuxtLink
>
<NuxtLink
to="/other/cookies"
target="_blank"
@click.native="baiduStat('cookies政策', 'click', 'cookies政策')"
>cookies政策</NuxtLink
>
<NuxtLink
to="/other/corporate"
target="_blank"
@click.native="baiduStat('媒体合作', 'click', '媒体合作')"
>媒体合作</NuxtLink
>
<el-popover
placement="top-start"
width="420"
:visible-arrow="false"
popper-class="feedback-popover"
trigger="hover"
>
<div class="feedback-box">
<h6>意见反馈</h6>
<p>
您在使用网站中发现了哪些问题可以发送邮件至
<a
href="mailto:DHcommunications@novonordisk.com"
target="_blank"
@click="baiduStat('意见反馈', 'click', '意见反馈')"
>DHcommunications@novonordisk.com</a
>
向我们反馈
</p>
</div>
<p slot="reference">意见反馈</p>
</el-popover>
</div>
</template>
<script>
export default {};
</script>
<style lang="scss">
.web-header {
margin: 0 auto;
width: $content-width;
height: 30px;
font-size: 14px;
color: $body-color;
line-height: 30px;
a {
margin-right: 44px;
color: $body-color;
}
& > span {
float: right;
p {
display: inline-block;
cursor: pointer;
}
}
}
.feedback-popover {
padding: 30px 26px 42px;
box-sizing: border-box;
text-align: center;
.feedback-box {
h6 {
font-size: 18px;
color: $main-blue-color;
line-height: 24px;
}
p {
margin-top: 16px;
font-size: 16px;
color: $font-color1;
line-height: 24px;
a {
color: $font-color1;
}
}
}
}
</style>

122
src/layouts/common.vue

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
<template>
<div
class="outer-container"
:class="{
isMobile: deviceType !== 'pc',
hasLoginBottom: deviceType !== 'pc' && !isLogin && !hasCloseMobileNotLogin
}"
>
<div class="content-container common-page">
<CommonHeader
:device-type="deviceType"
:scroll-left="scrollLeft"
:has-sub-menu-color="true"
:tab-type="1"
:search-height="searchHeight"
@func="getMsgFormSon"
/>
<MobileHeader v-if="deviceType !== 'pc'" />
<Nuxt />
<div class="footer-outer" :style="{ left: -scrollLeft + 'px' }">
<WebFooter :device-type="deviceType" />
</div>
<BottomRightFixed />
<NotLogin
v-if="deviceType !== 'pc' && !isLogin && !hasCloseMobileNotLogin"
@hideClick="hasCloseMobileNotLogin = true"
/>
</div>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapGetters } = createNamespacedHelpers('user');
export default {
data() {
return {
scrollLeft: 0,
searchHeight: 0,
hasCloseMobileNotLogin: false
};
},
computed: {
...mapGetters({
isLogin: 'isLogin'
}),
deviceType() {
return this.$store.state.device.deviceType;
}
},
mounted() {
const that = this;
this.$nextTick(() => {
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
that.searchHeight = windowHeight - 86 - 66;
window.addEventListener('scroll', function () {
// scrollTop
const scrollLeft =
document.documentElement.scrollLeft || document.body.scrollLeft;
that.scrollLeft = scrollLeft;
});
window.addEventListener('resize', function () {
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
that.searchHeight = windowHeight - 86 - 66;
});
});
},
methods: {
// CommonHeader
getMsgFormSon(data) {
this.msgFormSon = data;
console.log(this.msgFormSon);
}
}
};
</script>
<style lang="scss">
.common-page {
padding: 86px 0 76px;
.common-header {
.top-menu {
.el-menu-item.is-active p,
.el-submenu.is-active .el-submenu__title {
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
}
.el-menu-item.is-active,
.el-submenu.is-active .el-submenu__title {
position: relative;
&::after {
position: absolute;
content: '';
bottom: 24px;
left: 50%;
margin-left: -15px;
width: 30px;
height: 4px;
background: $main-blue-color;
border-radius: 2px;
}
}
}
}
}
.isMobile {
.common-page {
padding-bottom: 0;
}
}
.active-color {
.el-menu--popup {
.el-menu-item.is-active {
p {
color: $font-color2 !important;
}
}
}
}
</style>

245
src/layouts/detail.vue

@ -0,0 +1,245 @@ @@ -0,0 +1,245 @@
<template>
<div
class="outer-container"
:class="{
isMobile: deviceType !== 'pc',
hasLoginBottom: deviceType !== 'pc' && !isLogin && !hasCloseMobileNotLogin
}"
>
<div class="content-container detail-page">
<CommonHeader
:device-type="deviceType"
:scroll-left="scrollLeft"
:tab-type="tabType"
:search-height="searchHeight"
:show-close="headShowClose"
@func="getMsgFormSon"
/>
<Nuxt />
<div
class="bottom-outer"
:style="{ minWidth: deviceType === 'pc' ? '1258px' : '' }"
>
<WebHeader v-if="deviceType === 'pc'" />
<WebFooter :device-type="deviceType" :detail-common="true" />
<div v-if="deviceType === 'pc'" class="follow-box">
<div>
<div>
<div>
<h5>
<p>
<img
src="~/assets/images/wechat_white.png"
alt="微信扫码"
/>
</p>
微信扫码
</h5>
<p>关注糖尿病网</p>
<h6>掌上资讯随时看</h6>
</div>
<img :src="isPro ? qrcodeUrl : qrcodeUrlTest" />
</div>
</div>
</div>
</div>
<NotLogin
v-if="deviceType !== 'pc' && !isLogin && !hasCloseMobileNotLogin"
@hideClick="hasCloseMobileNotLogin = true"
/>
</div>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapGetters } = createNamespacedHelpers('user');
export default {
data() {
return {
scrollLeft: 0,
tabType: 1,
searchHeight: 0,
isPro: false,
qrcodeUrl: require('~/assets/images/follow_qrcode.png'),
qrcodeUrlTest: require('~/assets/images/follow_qrcode_test.png'),
hasCloseMobileNotLogin: false,
headShowClose: false
};
},
computed: {
...mapGetters({
isLogin: 'isLogin'
}),
deviceType() {
return this.$store.state.device.deviceType;
}
},
created() {
this.isPro = process.env.VUE_APP_TITLE === 'production';
if (this.$route.path.includes('/other')) {
this.tabType = 2;
} else {
this.tabType = 1;
}
this.headShowClose = this.$route.name === 'user-setting';
},
mounted() {
const that = this;
this.$nextTick(() => {
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
that.searchHeight = windowHeight - 86;
window.addEventListener('scroll', function () {
// scrollTop
const scrollLeft =
document.documentElement.scrollLeft || document.body.scrollLeft;
that.scrollLeft = scrollLeft;
});
window.addEventListener('resize', function () {
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
that.searchHeight = windowHeight - 86;
});
});
},
methods: {
// CommonHeader
getMsgFormSon(data) {
this.msgFormSon = data;
console.log(this.msgFormSon);
}
}
};
</script>
<style lang="scss">
.detail-page {
padding: 86px 0 122px;
.common-header-outer {
border-bottom: 1px solid $bg-color;
.common-header {
.top-menu {
.el-menu-item.is-active p,
.el-submenu.is-active .el-submenu__title {
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
}
.el-menu-item.is-active,
.el-submenu.is-active .el-submenu__title {
position: relative;
&::after {
position: absolute;
content: '';
bottom: 24px;
left: 50%;
margin-left: -15px;
width: 30px;
height: 4px;
background: $main-blue-color;
border-radius: 2px;
}
}
}
}
}
.bottom-outer {
position: absolute;
bottom: 0;
left: 0;
padding-top: 20px;
width: 100%;
height: 122px;
background: $main-blue-color;
box-sizing: border-box;
.web-header {
position: relative;
z-index: 1;
& > span {
/*display: none;*/
float: none;
}
}
.web-footer {
position: relative;
padding-top: 15px;
height: auto;
z-index: 1;
p {
color: rgba(255, 255, 255, 0.4);
line-height: 17px;
a {
color: rgba(255, 255, 255, 0.4);
}
}
}
.follow-box {
position: absolute;
bottom: 20px;
width: 100%;
& > div {
margin: 0 auto;
width: $content-width;
& > div {
display: flex;
float: right;
align-items: flex-end;
& > div {
color: $body-color;
h5 {
height: 24px;
font-size: 18px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
line-height: 24px;
p {
position: relative;
top: -4px;
display: inline-block;
margin-right: 10px;
width: 28px;
height: 28px;
background: linear-gradient(180deg, #42e32d 0%, #22e034 100%);
border-radius: 4px;
img {
margin: 2px;
width: 24px;
}
}
}
& > p {
font-size: 16px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
line-height: 30px;
letter-spacing: 3px;
}
h6 {
font-size: 16px;
line-height: 21px;
}
}
& > img {
margin-left: 10px;
width: 82px;
height: 82px;
}
}
}
}
}
/*.fixed-box {*/
/*bottom: 200px;*/
/*}*/
}
.isMobile {
.detail-page {
padding-bottom: 0;
}
.bottom-outer {
/*position: unset;*/
padding: 0;
height: auto;
}
}
</style>

113
src/layouts/error.vue

@ -0,0 +1,113 @@ @@ -0,0 +1,113 @@
<template>
<div class="error">
<h4 v-if="code != -1 && code != 0">{{ code }}</h4>
<h6 v-if="code === 404">NOT FOUND</h6>
<h6 v-else-if="code === 500">服务器错误</h6>
<h6 v-else-if="code === 504">网络超时</h6>
<h6 v-else>错误</h6>
<p v-if="code === 404">糟糕网页似乎迷路了<br />请尝试返回重新寻找</p>
<p v-else-if="code === 500">紧急施工中请稍后再试</p>
<p v-else-if="code === 502">
哎呀今天的网络好拥堵<br />等等我已经在路上
</p>
<p v-else-if="code === 504">加载失败了请返回重新尝试</p>
<p v-else-if="code === -1" style="margin-top: 300px">请检查您的网络连接</p>
<p v-else-if="code === 401">您暂未登录<br />请登录后再操作</p>
<p v-else style="margin-top: 300px">加载错误请重新尝试</p>
<img src="~/assets/images/fail_page_img.png" alt="" />
</div>
</template>
<script>
export default {
layout: 'detail',
props: {
error: {
type: Object,
default: null
}
},
data() {
return {
code: 0
};
},
mounted() {
console.log(this.error);
if (this.$route.path === '/error') {
if (this.$route.query && this.$route.query.code)
this.code = Number(this.$route.query.code);
} else {
this.code = Number(this.error.statusCode);
}
} //
};
</script>
<style lang="scss">
.error {
position: relative;
padding: 30px 0;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: #e3e3e3;
text-align: center;
box-sizing: border-box;
h4 {
font-size: 200px;
line-height: 264px;
}
h6 {
margin-top: -25px;
font-size: 60px;
line-height: 79px;
}
p {
margin-top: 118px;
font-size: 24px;
font-family: MicrosoftYaHei;
color: $font-color2;
font-weight: normal;
line-height: 31px;
}
img {
position: absolute;
bottom: 128px;
left: 50%;
margin-left: -92px;
width: 184px;
height: 233px;
}
}
.isMobile {
.error {
margin-bottom: -110px;
background: $body-color;
h4 {
font-size: 100px;
line-height: 130px;
}
h6 {
margin-top: -10px;
font-size: 30px;
line-height: 40px;
}
p {
margin-top: 56px;
font-size: 18px;
line-height: 24px;
}
img {
bottom: 64px;
margin-left: -60px;
width: 120px;
height: auto;
}
& ~ .bottom-outer {
position: absolute;
}
& ~ .mobile-not-login {
display: none;
}
}
}
</style>

6
src/middleware/authenticated.js

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
export default function ({ store, redirect }) {
// If the user is not authenticated
if (store.state.user.info == null) {
return redirect('/');
}
}

102
src/middleware/device.js

@ -0,0 +1,102 @@ @@ -0,0 +1,102 @@
/**
*
* @param {*} UA ,就是userAgent
* @returns type: 设备类型
* env: 访问环境(微信/微博/qq)
* masklayer: 就是给外部拿到判断是否显示遮罩层的,一些特殊环境要引导用户到外部去打开访问
*/
function isWechat(UA) {
return /MicroMessenger/i.test(UA);
}
function isWeibo(UA) {
return /Weibo/i.test(UA);
}
function isQQ(UA) {
return /QQ/i.test(UA);
}
function isMoible(UA) {
return /(Android|webOS|iPhone|iPod|tablet|BlackBerry|Mobile)/i.test(UA);
}
function isIOS(UA) {
return /iPhone|iPad|iPod/i.test(UA);
}
function isAndroid(UA) {
return /Android/i.test(UA);
}
function deviceType(UA) {
if (isMoible(UA)) {
if (isIOS(UA)) {
if (isWechat(UA)) {
return {
type: 'ios',
env: 'wechat',
masklayer: true
};
}
if (isWeibo(UA)) {
return {
type: 'ios',
env: 'weibo',
masklayer: true
};
}
if (isQQ(UA)) {
return {
type: 'ios',
env: 'qq',
masklayer: true
};
}
return {
type: 'ios'
};
}
if (isAndroid(UA)) {
if (isWechat(UA)) {
return {
type: 'android',
env: 'wechat',
masklayer: true
};
}
if (isWeibo(UA)) {
return {
type: 'android',
env: 'weibo',
masklayer: true
};
}
if (isQQ(UA)) {
return {
type: 'android',
env: 'qq',
masklayer: true
};
}
return {
type: 'android'
};
}
return {
type: 'mobile'
};
} else {
return {
type: 'pc'
};
}
}
export default function (context) {
context.userAgent = process.server
? context.req.headers['user-agent']
: navigator.userAgent;
// 给全局上下文添加一个属性来保存我们返回的匹配信息
context.deviceType = deviceType(context.userAgent);
// 这里注入到store
context.store.commit('device/setDeviceType', context.deviceType);
}

945
src/pages/article/_id.vue

@ -0,0 +1,945 @@ @@ -0,0 +1,945 @@
<!-- eslint-disable vue/no-v-html-->
<template>
<div class="article-detail-page" style="padding: 30px 0">
<div class="left-content">
<div class="top">
<img
v-if="!articleData.thumb"
src="~/assets/images/article_cover.png"
alt=""
style="display: none"
/>
<img
v-else
:src="articleData.thumb"
:alt="articleData.title"
style="display: none"
/>
<h4>{{ articleData.title }}</h4>
</div>
<p>
{{ articleData.hit }}&nbsp;<i class="el-icon-view"></i>&nbsp;&nbsp;{{
articleData.published_at
}}
</p>
<div v-if="showNovoTeacher" class="chatbot-audio">
<img src="~/assets/images/chatbot_audio_feature.png" alt="" />
<div>
<h6>保护双眼听小诺朗读</h6>
<div>
<img
:style="{ display: audioPause ? '' : 'none' }"
src="~/assets/images/play_chatbot_audio.png"
alt=""
@click="playPauseAudio"
/>
<img
:style="{ display: !audioPause ? '' : 'none' }"
src="~/assets/images/pause_chatbot_audio.png"
alt=""
@click="playPauseAudio"
/>
<div>
<span :style="{ color: playTime != '00:00' ? '#3099EA' : '' }">{{
playTime
}}</span>
<p><span :style="{ width: playWidth + '%' }"></span></p>
<span>{{ allTime }}</span>
</div>
</div>
<audio
ref="chatbotAudio"
:src="articleData.audio_url"
@canplay="getDuration"
@timeupdate="updateTime"
@ended="audioEnd"
></audio>
</div>
</div>
<div v-if="articleData.video_url" class="video-box">
<video
x5-video-player-type="h5"
x5-video-player-fullscreen="true"
x-webkit-airplay="true"
x5-playsinline="true"
webkit-playsinline="true"
playsinline="true"
width="100%"
controls
style="object-fit: fill"
:poster="articleData.thumb"
:src="articleData.video_url"
></video>
</div>
<div class="article-content" v-html="articleData.content"></div>
<div class="bottom-box">
<p
v-if="!isLogin && deviceType === 'pc'"
class="go-follow"
@click="
baiduStat('文章详情页', 'click', '文章详情页-未登录按钮');
showLoginDialog(true);
"
>
点击关注微信公众号查看更多专业资讯
</p>
<div class="comment-box">
<h6>
<span>最新看法</span>
<span @click="getCommentCorpus(1, 1)">发表你的看法</span>
</h6>
<div class="comment-content">
<Barrage
ref="barrage"
class="barrage"
:barrage-list="barrageList"
:speed="deviceType === 'pc' ? 4 : 3"
:loop="true"
:channels="3"
:border-color="'#ededed'"
:background="'#fff'"
:barrage-height="120"
:device-type="deviceType"
/>
</div>
<p v-if="!barrageIsShow">还没有人发表看法</p>
</div>
</div>
</div>
<div
class="right-content"
:style="{ position: isFixed ? 'fixed' : 'static', right: right + 'px' }"
>
<h6 class="common-title">
猜你喜欢<img src="~/assets/images/heart.png" alt="" />
</h6>
<div>
<div
v-for="item in recommendArticles"
:key="item.id"
@click="
baiduStat(
'文章详情页',
'click',
'文章点击猜你喜欢-' + item.id + '+' + item.title
);
goHref('/article/' + item.id);
"
>
<img
v-if="!item.thumb"
src="~/assets/images/article_cover.png"
alt=""
/>
<img v-else :src="item.thumb" :alt="item.title" />
<p>{{ item.title }}</p>
<div class="bottom">
<span
>{{ item.hit
}}<i class="el-icon-view" style="margin-right: 8px"></i
>{{ item.collect }}<i class="el-icon-star-on"></i
></span>
</div>
</div>
</div>
</div>
<div v-if="commentModelPopupShow" class="comment-model-popup">
<div class="mask" @click="commentModelPopupShow = false"></div>
<div class="content">
<div
ref="commentCorpus"
:style="{ maxHeight: commentMaxHeight + 'px' }"
>
<div ref="commentCorpusContent">
<div>
<template v-for="(item, index) in commentModelList">
<div :key="'comment' + item.id">
<p
:class="item.chosen ? 'chosen' : ''"
@click="chooseCorpus(item.id, index)"
>
{{ item.content }}
</p>
</div>
</template>
</div>
<p v-if="commentLoading" class="load-more">
加载更多<i class="el-icon-loading"></i>
</p>
</div>
</div>
<p @click="commentArticle">立即发布</p>
</div>
</div>
<BottomRightFixed
:show-share="true"
:show-collect="true"
:is-login="isLogin"
:is-collect="articleData.is_collected"
:show-comment="true"
@commentClick="getCommentCorpus(1, 2)"
@collectClick="collectArticle"
/>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapActions, mapGetters } = createNamespacedHelpers('user');
export default {
name: 'ArticleDetailPage',
layout: 'detail',
validate({ params }) {
return /^\d+$/.test(params.id);
},
async asyncData({ $axios, params, req, redirect }) {
// if (!process.server) return;
const data = await $axios.$get(
'/article/detail/' + params.id,
$axios.genSSROptions(req)
);
// data.detail.articleData.audio_url = 'https://app.diabetes.com.cn/uploadfile/article/audio/8160-16395833618215.mp3';
// console.log('dafa:', data);
if (data.success === false) {
redirect('/error?code=' + 404);
}
return { ...data.detail };
},
data() {
return {
audioPause: true,
playTime: '00:00',
allTime: '00:00',
canPlayAudio: false,
playWidth: 0,
barrageIsShow: false,
barrageList: [],
commentModelPopupShow: false,
commentModelList: [],
inComment: false,
commentPage: 1,
commentLoading: false,
commentHasMore: true,
chosenCommentId: 0,
inCollect: false,
isFixed: false,
right: 89,
commentMaxHeight: 300
};
},
head() {
return {
title: this.articleData.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
]
};
},
computed: {
...mapGetters({
isLogin: 'isLogin'
}),
deviceType() {
return this.$store.state.device.deviceType;
},
env() {
return this.$store.state.device.env;
}
},
mounted() {
const that = this;
this.getComment();
this.$nextTick(() => {
if (that.deviceType === 'pc') {
window.addEventListener('scroll', function () {
that.isFixed = true;
const scrollLeft =
document.documentElement.scrollLeft || document.body.scrollLeft;
if (document.documentElement.offsetWidth < 1258) {
that.right =
89 - (1258 - document.documentElement.offsetWidth) + scrollLeft;
} else {
that.right = (document.documentElement.offsetWidth - 1080) / 2;
}
});
} else {
that.commentMaxHeight =
(document.documentElement.clientHeight ||
document.body.clientHeight) * 0.6;
if (this.deviceType === 'ios') {
if (that.env === 'wechat') {
document.addEventListener(
'WeixinJSBridgeReady',
function () {
that.$refs.chatbotAudio.play();
that.$refs.chatbotAudio.pause();
that.$refs.chatbotAudio.currentTime = 0;
},
false
);
} else {
window.onload = function () {
that.$refs.chatbotAudio.play();
that.$refs.chatbotAudio.pause();
that.$refs.chatbotAudio.currentTime = 0;
if (!isNaN(that.$refs.chatbotAudio.duration)) {
const duration = that.$refs.chatbotAudio.duration;
let minutes = parseInt(duration / 60, 10);
let seconds = parseInt(duration % 60);
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
that.allTime = minutes + ':' + seconds;
that.canPlayAudio = true;
} else {
that.canPlayAudio = true;
}
};
}
}
}
});
},
methods: {
...mapActions({
showLoginDialog: 'showLoginDialog'
}),
getDuration() {
const duration = this.$refs.chatbotAudio.duration;
let minutes = parseInt(duration / 60, 10);
let seconds = parseInt(duration % 60);
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
this.allTime = minutes + ':' + seconds;
this.canPlayAudio = true;
},
playPauseAudio() {
if (this.canPlayAudio) {
if (this.audioPause) {
this.baiduStat('文章详情页', 'click', '小诺老师语音控件');
this.$refs.chatbotAudio.play();
} else {
this.$refs.chatbotAudio.pause();
}
this.audioPause = !this.audioPause;
}
},
updateTime(e) {
const currentTime = e.target.currentTime;
const duration = e.target.duration;
let minutes = parseInt(currentTime / 60, 10);
let seconds = parseInt(currentTime % 60);
minutes = minutes < 10 ? '0' + minutes : minutes;
seconds = seconds < 10 ? '0' + seconds : seconds;
this.playWidth = (currentTime / duration) * 100;
this.playTime = minutes + ':' + seconds;
},
audioEnd() {
this.audioPause = true;
this.playTime = '00:00';
this.playWidth = 0;
},
async getComment() {
const data = await this.$axios.$get(
'/article/comment-list?aid=' + this.$route.params.id
);
for (const item of data.detail.commentList) {
item.time = Math.random() * 15 + 9;
}
this.barrageIsShow = data.detail.commentList.length > 0;
const _list = [];
for (const comment of data.detail.commentList) {
_list.push({
content: comment.comment_content,
color: '#939AA7',
icon: comment.headimgurl
});
}
this.barrageList = _list;
},
async getCommentCorpus(page, type) {
if (!this.isLogin) {
this.showLoginDialog(true);
} else {
const that = this;
if (page === 1) {
this.baiduStat(
'文章详情页',
'click',
type === 1 ? '文章内发表你的想法-点击' : '文章内评论'
);
this.chosenCommentId = 0;
this.commentModelList = [];
this.commentPage = 1;
this.commentHasMore = true;
this.commentModelPopupShow = true;
setTimeout(function () {
that.$refs.commentCorpus.addEventListener('scroll', function () {
// scrollTop
const scrollTop = that.$refs.commentCorpus.scrollTop;
// windowHeight
const contentHeight = that.$refs.commentCorpus.clientHeight;
// scrollHeight
const allHeight = that.$refs.commentCorpusContent.clientHeight;
//
if (scrollTop + contentHeight > allHeight - 20) {
// that
that.getCommentCorpus();
}
});
});
}
if (this.commentHasMore && !this.commentLoading) {
this.commentLoading = true;
const data = await this.$axios.$get(
'/article/comment-corpus?page=' + this.commentPage
);
for (const item of data.detail.list) {
item.chosen = false;
}
this.commentPage = this.commentPage + 1;
this.commentModelList = this.commentModelList.concat(
data.detail.list
);
this.commentHasMore = data.detail.hasMore;
this.commentLoading = false;
}
}
},
chooseCorpus(id, index) {
for (const item of this.commentModelList) {
item.chosen = false;
}
this.chosenCommentId = id;
this.commentModelList[index].chosen = true;
},
async commentArticle() {
if (this.chosenCommentId === 0) {
this.$message({
message: '请选择看法模版',
type: 'warning'
});
} else {
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const data = await this.$axios.$post('/article/user-comment', {
aid: this.$route.params.id,
comment_template_id: this.chosenCommentId
});
if (data && data.success) {
this.baiduStat('文章详情页', 'click', '文章内发表你的想法-发表成功');
}
console.log(data);
loading.close();
this.commentModelPopupShow = false;
this.getComment();
}
},
async collectArticle() {
if (!this.isLogin) {
this.showLoginDialog(true);
} else if (this.inCollect) {
this.$message({
message: '您的操作太快了',
type: 'warning'
});
} else {
this.baiduStat('文章详情页', 'click', '文章内收藏');
this.inCollect = true;
const data = await this.$axios.$post('/article/user-collect', {
aid: this.$route.params.id
});
console.log(data);
this.articleData.is_collected = data.detail.curState;
this.inCollect = false;
}
},
goHref(url) {
let openNew = false;
if (this.deviceType === 'pc') {
openNew = true;
}
url = this.$router.resolve({
path: url
});
if (openNew) {
window.open(url.href, '_blank');
} else {
window.location.href = url.href;
}
}
}
};
</script>
<style lang="scss">
.article-detail-page {
position: relative;
.left-content {
div.top {
margin-bottom: 20px;
border-bottom: 1px solid $bg-color;
padding-bottom: 30px;
img {
width: 100%;
height: 407px;
border-radius: 4px;
}
h4 {
/*margin-top: 30px;*/
font-size: 32px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: $font-color1;
line-height: 44px;
}
}
& > p:nth-child(2) {
margin-bottom: 20px;
font-size: 16px;
font-family: Apis-Regular, Apis;
font-weight: 400;
color: #bac0c2;
line-height: 28px;
}
.video-box {
margin-bottom: 20px;
video {
width: 100%;
}
}
.chatbot-audio {
position: relative;
margin-bottom: 20px;
padding: 16px 16px 0 85px;
width: 100%;
height: 105px;
background: $bg-color;
border-radius: 8px;
box-sizing: border-box;
& > img {
position: absolute;
top: 12px;
left: 12px;
width: 57px;
height: 87px;
}
& > div {
h6 {
font-size: 18px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #333;
line-height: 25px;
}
& > div {
position: relative;
margin-top: 25px;
padding-left: 48px;
& > img {
position: absolute;
top: -17px;
left: -10px;
width: 60px;
height: 60px;
cursor: pointer;
}
div {
display: flex;
& > span {
width: 38px;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $font-color3;
line-height: 17px;
}
p {
position: relative;
margin-top: 7px;
width: calc(100% - 76px);
height: 3px;
background: #e3e3e3;
border-radius: 2px;
span {
position: absolute;
top: 0;
left: 0;
height: 3px;
background: linear-gradient(90deg, #3b97de 0%, #38b0ff 100%);
border-radius: 2px;
}
}
& > span:last-child {
text-align: right;
}
}
}
}
}
.article-content {
border-bottom: 1px solid $bg-color;
padding: 23px 0;
font-size: 18px;
color: $font-color1;
line-height: 30px;
img {
max-width: 100%;
border-radius: 4px;
}
}
.bottom-box {
padding-top: 20px;
div.comment-box {
position: relative;
h6 {
height: 50px;
span:first-child {
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $main-blue-color;
line-height: 50px;
}
span:last-child {
float: right;
width: 208px;
height: 50px;
background: #3099ea;
border-radius: 4px;
text-align: center;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $body-color;
line-height: 50px;
cursor: pointer;
}
}
.comment-content {
position: relative;
margin-top: 24px;
height: 365px;
box-sizing: border-box;
.baberrage-item {
padding: 0;
.barrage-items {
display: inline-block;
border: 1px solid #ededed;
padding: 0 20px 0 10px;
height: 60px;
background: $body-color;
border-radius: 30px;
box-sizing: border-box;
img {
margin: 9px 10px 0 0;
@include roundFun(40px);
}
span {
font-size: 18px;
font-family: AppleColorEmoji;
color: $font-color3;
line-height: 58px;
}
}
}
}
& > p {
position: absolute;
top: 100px;
width: 100%;
font-size: 20px;
color: $font-color1;
text-align: center;
line-height: 100px;
}
}
}
}
.right-content {
position: fixed;
top: 116px;
.common-title {
padding: 0 0 8px;
font-size: 18px;
color: $font-color1;
line-height: 25px;
img {
margin: 3px 0 0 4px;
width: 20px;
height: 20px;
}
}
& > div {
& > div {
display: block;
position: relative;
border-bottom: 1px solid $bg-color;
padding: 20px 0 0 124px;
height: 120px;
box-sizing: border-box;
img {
position: absolute;
left: 0;
width: 110px;
height: 80px;
border-radius: 4px;
}
p {
font-size: 16px;
color: $font-color1;
line-height: 24px;
@include textEllipsisMore1Line(2);
}
div.bottom {
position: absolute;
right: 0;
bottom: 19px;
text-align: right;
span {
font-size: 16px;
font-family: Apis-Regular, Apis;
font-weight: 400;
color: #bac0c2;
line-height: 28px;
i {
margin-left: 3px;
}
}
}
}
}
}
.comment-model-popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9;
.mask {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.content {
position: absolute;
bottom: 0;
left: 0;
border: 1px solid #e3e3e3;
padding: 44px 0 42px;
width: 100%;
box-shadow: 0 -10px 10px 0 rgba(0, 0, 0, 0.08);
border-radius: 4px 4px 0 0;
background: $body-color;
text-align: center;
& > div {
overflow-y: scroll;
margin: 0 auto 39px;
max-width: 1044px;
max-height: 300px;
& > div {
& > div {
display: flex;
flex-wrap: wrap;
& > div {
padding: 0 14px;
p {
margin-bottom: 20px;
border: 1px solid $bg-color;
padding: 0 20px;
min-width: 320px;
height: 60px;
box-sizing: border-box;
background: $bg-color;
font-size: 16px;
font-family: AppleColorEmoji;
color: #292b2c;
line-height: 58px;
@extend .text-ellipsis;
border-radius: 30px;
cursor: pointer;
}
p.chosen {
border-color: #3099ea;
background: rgba(48, 153, 234, 0.1);
}
}
}
}
}
& > p {
margin: 0 auto;
width: 325px;
height: 50px;
background: #3099ea;
border-radius: 4px;
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: $body-color;
line-height: 50px;
cursor: pointer;
}
}
}
}
.isMobile {
.article-detail-page {
padding: 11px 15px 400px !important;
background: $body-color;
.left-content {
div.top {
margin-bottom: 10px;
padding-bottom: 10px;
h4 {
font-size: 24px;
line-height: 30px;
}
}
& > p:nth-child(2) {
margin-bottom: 10px;
font-size: 14px;
}
.chatbot-audio {
margin-top: 20px;
padding: 25px 15px 10px 70px;
& > img {
left: 10px;
top: -15px;
}
& > div {
& > div {
margin-left: -55px;
padding: 0;
width: calc(100% + 55px);
& > img {
top: -62px;
right: -13px;
left: auto;
}
}
}
}
.bottom-box {
position: absolute;
bottom: 20px;
left: 0;
padding: 0 15px;
width: 100%;
box-sizing: border-box;
div.comment-box {
h6 {
height: 40px;
span:first-child {
display: none;
}
span:last-child {
width: 150px;
height: 40px;
line-height: 40px;
}
}
.comment-content {
height: 290px;
.z_barrage {
left: -15px;
}
}
}
}
}
.right-content {
float: none;
padding-top: 10px;
width: 100%;
& > div {
& > div {
margin-bottom: 10px;
border: none;
padding: 15px 15px 12px;
height: auto;
background: #f1f1f1;
border-radius: 10px;
box-sizing: border-box;
img {
display: none;
}
div.bottom {
position: unset;
margin-top: 4px;
text-align: left;
}
}
}
}
.comment-model-popup {
z-index: 10;
.mask {
background: rgba(0, 0, 0, 0.5);
}
.content {
border: none;
padding: 62px 15px 15px;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.05);
border-radius: 20px 20px 0 0;
box-sizing: border-box;
& > div {
margin: 0;
width: 100%;
max-width: none;
& > div {
& > div {
display: block;
& > div {
padding: 0;
p {
margin-bottom: 10px;
border: none;
padding: 11px 15px;
height: auto;
min-width: auto;
background: #eef5fb;
border-radius: 10px;
box-sizing: border-box;
text-align: left;
font-size: 16px;
font-family: AppleColorEmoji;
color: $font-color1;
line-height: 26px;
&.chosen {
color: #3099ea;
}
}
}
}
}
}
& > p {
position: absolute;
top: 15px;
right: 15px;
width: 100px;
height: 32px;
line-height: 32px;
font-size: 16px;
}
}
}
}
}
</style>

122
src/pages/article/index.vue

@ -0,0 +1,122 @@ @@ -0,0 +1,122 @@
<template>
<div ref="articleList" class="article-list-page">
<div
v-if="articleList.length > 0"
:style="{ paddingTop: deviceType === 'pc' ? '30px' : '49px' }"
>
<div class="common-flex">
<template v-for="item in articleList">
<VideoArticleListItem
:key="'videolist' + item.id"
:detail="item"
:column-num="4"
:device-type="deviceType"
@addStat="
baiduStat(
'文章列表页',
'click',
(item.mainModule === 38 ? '广告' : '文章列表点击-') +
item.id +
'+' +
item.title
)
"
/>
</template>
</div>
<p v-if="articleLoading" class="load-more">
加载更多<i class="el-icon-loading"></i>
</p>
</div>
</div>
</template>
<script>
export default {
name: 'ArticleIndexPage',
layout: 'common',
async asyncData({ $axios, query }) {
if (!process.server) return;
// console.log(query);
const data = await $axios.$get(
'/article/list?labelId=' + query.labelId + '&page=1'
);
return { ...data.detail };
},
data() {
return {
articlePage: 2,
articleLoading: false
};
},
head: {
title: '糖尿病网-文章',
meta: [{ hid: 'description', name: 'description', content: '' }]
},
computed: {
deviceType() {
return this.$store.state.device.deviceType;
}
},
mounted() {
const that = this;
let category = '药物';
const labelId = Number(this.$route.query.labelId);
if (labelId === -1) {
category = '药物';
} else if (labelId === 2) {
category = '饮食';
} else if (labelId === 3) {
category = '运动';
} else if (labelId === 17) {
category = '并发症';
} else if (labelId === -2) {
category = '监测';
} else if (labelId === 18) {
category = '心理';
}
this.baiduStat('文章列表页', 'show', '文章列表-' + category);
console.log(this.$refs.articleList.offsetHeight);
const outerHeight =
document.documentElement.clientHeight || document.body.clientHeight;
if (this.$refs.articleList.offsetHeight < outerHeight - 86 - 66) {
this.getArticleListData();
}
this.$nextTick(() => {
window.addEventListener('scroll', function () {
// scrollTop
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
// windowHeight
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// scrollHeight
const scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight;
//
if (scrollTop + windowHeight > scrollHeight - 50) {
// that
that.getArticleListData();
}
});
});
},
methods: {
async getArticleListData() {
if (this.hasMore && !this.articleLoading) {
this.articleLoading = true;
const data = await this.$axios.$get(
'/article/list?labelId=' +
this.$route.query.labelId +
'&page=' +
this.articlePage
);
this.hasMore = data.detail.hasMore;
this.articleList = this.articleList.concat(data.detail.articleList);
this.articlePage = this.articlePage + 1;
this.articleLoading = false;
}
}
}
};
</script>

406
src/pages/doctor/_id.vue

@ -0,0 +1,406 @@ @@ -0,0 +1,406 @@
<template>
<div class="doctor-detail-page" style="position: relative; padding: 30px 0">
<div class="left-content">
<template v-if="deviceType === 'pc'">
<h4>{{ videoRecord.title }}</h4>
<p>
{{ videoRecord.record_num }}&nbsp;<i class="el-icon-view"></i
>&nbsp;&nbsp;{{
(Number(videoRecord.publish_at) * 1000)
| formatDate('yyyy-MM-dd hh:mm')
}}
</p>
<div class="video-box">
<video-player
:playsinline="true"
class="video-player-box"
:options="playerOptions"
@play="videoPlay"
>
</video-player>
</div>
<p
v-if="!isLogin"
class="go-follow"
@click="
baiduStat('一问医答详情页', 'click', '一问医答详情页-未登录按钮');
showLoginDialog(true);
"
>
点击关注微信公众号查看更多专业资讯
</p>
</template>
<template v-else>
<div class="video-box">
<video-player
:playsinline="true"
class="video-player-box"
:options="playerOptions"
@play="videoPlay"
>
</video-player>
</div>
<h4>{{ videoRecord.title }}</h4>
<p>
{{ videoRecord.record_num }}&nbsp;<i class="el-icon-view"></i
>&nbsp;&nbsp;{{
(Number(videoRecord.publish_at) * 1000)
| formatDate('yyyy-MM-dd hh:mm')
}}
</p>
</template>
</div>
<div class="right-content">
<div v-if="deviceType === 'pc'" class="doctor-info-box">
<img :src="expertColumnInfo.expert_img" alt="" />
<div>
<h6 class="text-ellipsis">
{{ expertColumnInfo.username }} {{ expertColumnInfo.title }}
</h6>
<p class="text-ellipsis">{{ expertColumnInfo.hospital_name }}</p>
<p class="text-ellipsis">{{ expertColumnInfo.department }}</p>
</div>
</div>
<div ref="allList" class="doctor-list-box">
<div
v-for="item in expertColumnInfo.video_list"
:key="item.id"
@click="goDoctorVideo(item.id)"
>
<template v-if="deviceType === 'pc'">
<img
v-if="item.id == videoRecord.id"
ref="activeListImg"
src="~/assets/images/doctor_pause_icon.png"
alt=""
/>
<img v-else src="~/assets/images/doctor_play_icon.png" alt="" />
</template>
<span
:style="{ color: item.id == videoRecord.id ? '#3B97DE' : '' }"
>{{ item.title }}</span
>
<div v-if="deviceType !== 'pc'" class="bottom-box">
<span>{{ item.record_num }}次播放</span>
<span :style="{ opacity: item.id == videoRecord.id ? '0.6' : '' }"
><img src="~/assets/images/mobile_doctor_play.png" alt="" />{{
item.id == videoRecord.id ? '正在' : '点击'
}}观看</span
>
</div>
</div>
</div>
</div>
<BottomRightFixed :show-share="true" />
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapGetters, mapActions } = createNamespacedHelpers('user');
export default {
name: 'DoctorDetailPage',
layout: 'detail',
validate({ params }) {
return /^\d+$/.test(params.id);
},
async asyncData({ $axios, params, redirect }) {
if (!process.server) return;
const data = await $axios.$get('/expert-video/detail?vid=' + params.id);
if (data.success === false) {
redirect('/error?code=' + 404);
}
console.log(data.detail.expertColumnInfo.video_list);
return {
...data.detail,
playerOptions: {
playbackRates: [0.7, 1.0, 1.5, 2.0], //
autoplay: false, // true,
muted: false, //
loop: false, //
preload: 'auto', // <video>auto,
language: 'en',
fluid: true, // trueVideo.js player
sources: [
{
type: '', // git
src: data.detail.videoRecord.video_url // url
}
],
aspectRatio: '16:9',
poster: require('~/assets/images/doctor_default_poster.png'),
// width: document.documentElement.clientWidth, //
notSupportedMessage: '此视频暂无法播放,请稍后再试', // Video.js
controlBar: {
timeDivider: true,
durationDisplay: true,
remainingTimeDisplay: false,
fullscreenToggle: true //
}
}
};
},
data() {
return {
playerOptions: {}
};
},
head() {
return {
title: this.videoRecord.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
]
};
},
computed: {
...mapGetters({
isLogin: 'isLogin'
}),
deviceType() {
return this.$store.state.device.deviceType;
}
},
mounted() {
if (this.deviceType === 'pc') {
this.$nextTick(() => {
this.$refs.allList.scrollTop =
this.$refs.activeListImg[0].parentNode.offsetTop -
this.$refs.allList.clientHeight / 2;
});
}
},
methods: {
...mapActions({
showLoginDialog: 'showLoginDialog'
}),
async videoPlay(e) {
if (e.cache_.currentTime === 0 || e.cache_.currentTime === undefined) {
this.baiduStat('一问医答详情页', 'click', '一问医答详情页-播放视频');
await this.$axios.$post('/expert-video/user-play', {
vid: this.$route.params.id
});
}
},
goDoctorVideo(id) {
if (Number(this.$route.params.id) !== Number(id)) {
// this.$router.push('/doctor/'+id);
this.baiduStat(
'一问医答详情页',
'click',
'一问医答详情页-切换视频列表'
);
const { href } = this.$router.resolve({
path: '/doctor/' + id
});
window.location.href = href;
}
}
}
};
</script>
<style lang="scss">
.doctor-detail-page {
position: relative;
.left-content {
& > h4 {
margin-bottom: 20px;
border-bottom: 1px solid $bg-color;
padding-bottom: 30px;
font-size: 32px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: $font-color1;
line-height: 44px;
}
& > p:nth-child(2),
& > p:nth-child(3) {
margin-bottom: 20px;
font-size: 16px;
font-family: Apis-Regular, Apis;
font-weight: 400;
color: #bac0c2;
line-height: 28px;
}
.video-box {
margin-bottom: 30px;
height: 406.69px;
}
p.go-follow {
margin-bottom: 10px;
}
}
.right-content {
position: absolute;
right: 0;
height: calc(100% - 80px);
.doctor-info-box {
position: relative;
margin-bottom: 20px;
padding: 42px 0 0 127px;
height: 168px;
background: $bg-color;
border-radius: 4px;
box-sizing: border-box;
img {
position: absolute;
top: 42px;
left: 27px;
border: 1px solid #ededed;
background: $body-color;
@include roundFun(80px);
box-sizing: border-box;
}
div {
h6 {
margin-bottom: 11px;
font-size: 18px;
font-family: MicrosoftYaHei-Bold, MicrosoftYaHei;
font-weight: bold;
color: $main-blue-color;
line-height: 24px;
}
p {
font-size: 16px;
color: $font-color3;
line-height: 21px;
}
}
}
.doctor-list-box {
position: relative;
overflow-y: scroll;
padding: 30px;
height: calc(100% - 188px);
background: #f5f5f8;
border-radius: 4px;
box-sizing: border-box;
& > div {
display: flex;
margin-bottom: 22px;
align-items: center;
cursor: pointer;
img {
margin-right: 10px;
width: 30px;
height: 30px;
}
& > span {
font-size: 16px;
color: $font-color1;
line-height: 21px;
}
}
& > div:last-child {
margin-bottom: 0;
}
}
}
}
.isMobile {
.doctor-detail-page {
padding: 0 !important;
background: $body-color;
.left-content {
.video-box {
margin: 0;
padding: 0;
height: auto;
box-shadow: none;
&::after {
display: none;
}
.video-player-box {
width: 100%;
& > div {
padding-top: 56.25%;
}
}
}
& > h4 {
margin: 0 15px 4px;
padding: 20px 5px 16px;
font-size: 18px;
line-height: 24px;
}
& > p:nth-child(3) {
margin-bottom: 20px;
padding: 0 22px;
font-size: 12px;
line-height: 17px;
}
}
.right-content {
position: unset;
float: none;
width: 100%;
height: auto;
.doctor-list-box {
padding: 0 15px;
height: auto;
background: transparent;
& > div {
display: block;
margin: 0;
border-bottom: 1px solid $bg-color;
padding: 20px 0 15px;
& > span {
display: block;
margin: 0 15px 11px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333;
line-height: 25px;
}
.bottom-box {
height: 32px;
span:first-child {
display: inline-block;
margin-top: 1px;
padding: 0 9px 0 14px;
height: 30px;
background: #f7f7fa;
border-radius: 0 15px 15px 0;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #7d7d7d;
line-height: 30px;
}
span:last-child {
float: right;
width: 134px;
height: 32px;
background: linear-gradient(180deg, #fad961 0%, #f76b1c 100%);
border-radius: 16px;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #fafafc;
line-height: 32px;
text-align: center;
img {
margin: 6px 6px 0 0;
width: 21px;
height: 21px;
}
}
}
}
}
}
}
}
</style>

249
src/pages/doctor/index.vue

@ -0,0 +1,249 @@ @@ -0,0 +1,249 @@
<template>
<div
class="doctor-list-page"
:style="{ paddingTop: deviceType === 'pc' ? '' : '49px' }"
>
<template v-if="deviceType === 'pc'">
<div class="top-box"></div>
<div v-if="expertList.length > 0">
<div class="common-flex">
<template v-for="item in expertList">
<DoctorItem
:key="'expert' + item.expert_id"
:detail="item"
:column-num="4"
:device-type="deviceType"
/>
</template>
</div>
</div>
</template>
<template v-else>
<div class="mobile-top-box">
<img src="~/assets/images/mobile_doctor_title.png" alt="一问医答" />
<h5>
邀请知名专家以一问一答短视频的形式对糖友关心的问题予以解答以期传递简单清晰的教育信息帮助糖友提高疾病认知
</h5>
</div>
<div v-if="expertList.length > 0" class="mobile-doctor-swipe">
<swiper ref="mySwiper" :options="swiperOption">
<swiper-slide
v-for="item in expertList"
:key="'expertMobile' + item.expert_id"
>
<div class="mobile-doctor-item">
<div class="expert-intro">
<img :src="item.expert_img" :alt="item.username" />
<div class="right-font">
<h3>
{{ item.username }}<span>{{ item.title }}</span>
</h3>
<h4>{{ item.hospital_name }}</h4>
<h5>{{ item.department }}</h5>
</div>
</div>
<p v-if="expertList.length > 1">
<img
src="~/assets/images/mobile_doctor_arrow.png"
alt=""
/><img
src="~/assets/images/mobile_doctor_arrow.png"
alt=""
/>
</p>
<div v-if="item.video_list.length > 0" class="video-list">
<DoctorVideoItem
v-for="video in item.video_list"
:key="'mobileExpertVideo' + video.id"
:video="video"
@addStat="
baiduStat(
'一问医答列表',
'click',
'一问医答列表点击-' + video.id + '+' + video.title
)
"
/>
</div>
</div>
</swiper-slide>
</swiper>
</div>
</template>
</div>
</template>
<script>
export default {
name: 'VideoIndexPage',
layout: 'common',
async asyncData({ $axios }) {
if (!process.server) return;
const data = await $axios.$get('/expert-video/list');
return { ...data.detail };
},
data() {
return {
swiperOption: {
loop: true,
navigation: false,
pagination: false,
autoplay: false,
autoHeight: true
}
};
},
head: {
title: '糖尿病网-一问医答',
meta: [{ hid: 'description', name: 'description', content: '' }]
},
computed: {
deviceType() {
return this.$store.state.device.deviceType;
}
},
mounted() {
this.baiduStat('一问医答列表', 'show', '一问医答列表-访问');
}
};
</script>
<style lang="scss">
.doctor-list-page {
.top-box {
position: absolute;
top: 86px;
left: 0;
width: 100%;
min-width: $outer-width;
height: 334px;
background: url(~assets/images/doctor_list_top.png) no-repeat center;
background-size: auto 334px;
}
& > div:nth-child(2) {
position: relative;
padding-top: 235px;
.common-flex {
justify-content: flex-start;
.doctor-item {
margin: 0 19.3px 30px 0;
padding: 20px 22px 0;
width: 255px;
}
.doctor-item:nth-child(4n) {
margin-right: 0;
}
}
}
.mobile-top-box {
padding: 17px 16px 64px;
background: linear-gradient(180deg, #3b97de 0%, #009fda 100%);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.06);
text-align: center;
img {
width: 102px;
height: 39px;
}
h5 {
margin: 9px 0 0;
font-size: 14px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: $body-color;
line-height: 20px;
text-align: left;
}
}
.mobile-doctor-swipe {
margin-top: -48px;
padding: 0 !important;
.mobile-doctor-item {
.expert-intro {
display: flex;
overflow: hidden;
align-items: center;
margin: 0 13px;
padding: 0 24px 0 0;
height: 119px;
background: $body-color;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.08);
border-radius: 10px;
box-sizing: border-box;
img {
margin-top: 7px;
width: 124px;
height: 112px;
}
.right-font {
margin-left: 12px;
width: calc(100% - 136px);
h3,
h4,
h5 {
@extend .text-ellipsis;
}
h3 {
margin: 0 0 3px;
font-size: 20px;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #323232;
line-height: 28px;
span {
margin-left: 8px;
font-size: 18px;
}
}
h4 {
display: inline-block;
margin: 0 0 11px;
padding: 0 6px;
max-width: 100%;
height: 27px;
background: linear-gradient(270deg, #3b97de 0%, #009fda 100%);
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.06);
border-radius: 4px;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $body-color;
line-height: 27px;
}
h5 {
margin: 0 0 0 3px;
font-size: 15px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #009fda;
line-height: 22px;
}
}
}
& > p {
margin-top: 7px;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #003279;
line-height: 20px;
text-align: center;
img {
position: relative;
top: 4px;
display: inline-block;
margin-left: 3px;
width: 13px;
height: 13px;
}
img:first-child {
margin: 0 3px 0 0;
transform: rotate(180deg);
}
}
.video-list {
margin-top: 13px;
}
}
}
}
</style>

906
src/pages/index.vue

@ -0,0 +1,906 @@ @@ -0,0 +1,906 @@
<template>
<div
class="outer-container"
:class="{
isMobile: deviceType !== 'pc',
hasLoginBottom: deviceType !== 'pc' && !isLogin && !hasCloseMobileNotLogin
}"
>
<div
class="content-container index-page"
:class="{ isMobile: deviceType !== 'pc' }"
>
<div
v-if="deviceType === 'pc'"
class="header-outer"
:style="{ top: -scrollTop + 'px', left: -scrollLeft + 'px' }"
>
<WebHeader />
</div>
<CommonHeader
:device-type="deviceType"
:has-web-header="true"
:scroll-top="scrollTop"
:scroll-left="scrollLeft"
:search-height="searchHeight"
:tab-type="1"
@func="getMsgFormSon"
/>
<div v-show="msgFormSon" class="library-page">
<div class="left-content">
<div v-if="topBanner.length > 0" class="top-banner">
<div v-if="popTopBannerShow" class="pop-banner">
<span @click="popTopBannerShow = false"
>跳过广告 {{ popTopBannerTime }}s</span
>
<img
:src="popTopBannerItem.image_url"
:alt="popTopBannerItem.title"
@click="
goTopBanner(
'top',
popTopBannerItem.id,
popTopBannerItem.title,
popTopBannerItem.action_url,
popTopBannerItem.content_type
)
"
/>
</div>
<el-carousel :height="topBannerHeight + 'px'" arrow="never">
<el-carousel-item v-for="item in topBanner" :key="item.id">
<div
class="top-banner-box"
@click="
goTopBanner(
'top',
item.id,
item.title,
item.action_url,
item.content_type
)
"
>
<img :src="item.image_url" :alt="item.title" />
<div class="mask">
<p class="text-ellipsis">{{ item.title }}</p>
</div>
</div>
</el-carousel-item>
</el-carousel>
</div>
<template v-if="deviceType === 'pc'">
<div v-if="expertList.length > 0" class="doctor-box">
<h4 class="common-title">
一问医答
<NuxtLink
to="/doctor"
target="_blank"
@click.native="
baiduStat('糖尿病网-首页', 'click', '一问医答-查看更多')
"
>查看更多<i class="el-icon-caret-right"></i
></NuxtLink>
</h4>
<div class="common-flex">
<template v-for="item in expertList">
<DoctorItem
:key="'doctor' + item.id"
:detail="item"
:column-num="3"
@addStat="
baiduStat(
'糖尿病网-首页',
'click',
'一问医答-' + item.id + '+' + item.title
)
"
/>
</template>
</div>
</div>
<div v-if="videoList.length > 0" class="hot-videos">
<h4 class="common-title">
热门视频
<NuxtLink
to="/video"
target="_blank"
@click.native="
baiduStat('糖尿病网-首页', 'click', '热门视频-查看更多')
"
>查看更多<i class="el-icon-caret-right"></i
></NuxtLink>
</h4>
<div class="common-flex">
<template v-for="item in videoList">
<VideoArticleListItem
:key="'va' + item.id"
:detail="item"
:column-num="3"
@addStat="
baiduStat(
'糖尿病网-首页',
'click',
'热门视频-' + item.id + '+' + item.title
)
"
/>
</template>
</div>
</div>
</template>
<MobileHeader
v-if="deviceType !== 'pc'"
:is-index="true"
:need-fixed="mobileTabNeedFixed"
/>
<div
class="recommend-article"
:style="{
paddingTop:
deviceType !== 'pc' && mobileTabNeedFixed ? '49px' : ''
}"
>
<h4 v-if="deviceType === 'pc'" class="common-title">推荐阅读</h4>
<div class="common-flex">
<template v-for="item in articleList">
<VideoArticleListItem
:key="'rva' + item.id"
:detail="item"
:column-num="3"
:device-type="deviceType"
@addStat="
baiduStat(
'糖尿病网-首页',
'click',
'推荐阅读-' + item.id + '+' + item.title
)
"
/>
</template>
</div>
<p v-if="articleLoading" class="load-more">
加载更多<i class="el-icon-loading"></i>
</p>
</div>
</div>
<div v-if="deviceType === 'pc'" class="right-content">
<!-- <div v-if="!isLogin" class="not-login-box"> -->
<!-- <h6>登录后内容更精彩</h6> -->
<!-- <p -->
<!-- @click=" -->
<!-- baiduStat('立即登录按钮', 'click', '立即登录按钮'); -->
<!-- gotoLogin(); -->
<!-- " -->
<!-- > -->
<!-- 立即登录 -->
<!-- </p> -->
<!-- </div> -->
<div
class="need-fixed"
:style="{
position: needFixed ? 'fixed' : 'unset',
top: needFixed ? '86px' : '',
right: needFixed ? right + 'px' : ''
}"
>
<div class="go-wechat">
<div>
<h4>糖尿病网微信版</h4>
<p>前沿专业的公益科普<br />专属糖友的知识社区</p>
</div>
<img :src="isPro ? qrcodeUrl : qrcodeUrlTest" alt="" />
</div>
</div>
<div v-if="hotArticle.length > 0" class="hot-articles-box">
<h6 class="common-title">
大家在看<span>HOT</span>
<NuxtLink
to="/article?labelId=-1"
target="_blank"
@click.native="
baiduStat('糖尿病网-首页', 'click', '大家在看-查看更多')
"
>查看更多<i class="el-icon-caret-right"></i
></NuxtLink>
</h6>
<div>
<NuxtLink
v-for="(item, index) in hotArticle"
:key="index"
:to="{ name: 'article', params: { id: item.id } }"
target="_blank"
@click.native="
baiduStat(
'糖尿病网-首页',
'click',
'大家在看-' + item.id + '+' + item.title
)
"
>
<img
v-if="!item.thumb"
src="~/assets/images/article_cover.png"
alt=""
/>
<img v-else :src="item.thumb" :alt="item.title" />
<p>
<span>{{ index + 1 }}</span
>{{ item.title }}
</p>
</NuxtLink>
</div>
</div>
<span ref="needFixed"></span>
<div
class="need-fixed"
:style="{
position: needFixed ? 'fixed' : 'unset',
top: needFixed ? '86px' : '',
right: needFixed ? right + 'px' : ''
}"
>
<div class="go-wechat">
<div>
<h4>养成健康好习惯</h4>
<p>获积分 免费换针头<br />扫码前往诺和关怀</p>
</div>
<img src="~/assets/images/novocare_qrcode.png" alt="" />
</div>
<div v-if="rightBottomAd.length > 0" class="right-banner">
<img
:src="rightBottomAd[0].image_url"
alt=""
@click="
goTopBanner(
'right',
rightBottomAd[0].id,
rightBottomAd[0].title,
rightBottomAd[0].action_url
)
"
/>
</div>
</div>
</div>
</div>
<div
v-if="deviceType === 'pc' && bottomAd.length > 0"
class="bottom-ad"
:style="{ left: -scrollLeft + 'px' }"
>
<div>
<i
class="el-icon-error"
@click="
baiduStat('底部弹层广告', 'click', '底部弹层广告-关闭');
bottomAd = [];
"
></i>
<img
:src="bottomAd[0].image_url"
alt=""
@click="
goTopBanner(
'bottom',
bottomAd[0].id,
bottomAd[0].title,
bottomAd[0].action_url
)
"
/>
</div>
</div>
<div class="footer-outer" :style="{ left: -scrollLeft + 'px' }">
<WebFooter :device-type="deviceType" />
</div>
<NotLogin
v-if="deviceType !== 'pc' && !isLogin && !hasCloseMobileNotLogin"
@hideClick="hasCloseMobileNotLogin = true"
/>
<BottomRightFixed />
</div>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapActions, mapGetters } = createNamespacedHelpers('user');
// test node
export default {
name: 'IndexPage',
// layout: 'common',
async asyncData({ req, $axios, store }) {
if (store.state.device.deviceType === 'pc') {
const data = await $axios.$get('/index-data', $axios.genSSROptions(req));
return { ...data.detail };
} else {
const [dataA, dataB] = await Promise.all([
$axios.$get('/article/list?page=1', $axios.genSSROptions(req)),
$axios.$get('/index-banner', $axios.genSSROptions(req))
]);
return {
...dataA.detail,
...dataB.detail
};
}
},
data() {
return {
scrollTop: 0,
scrollLeft: 0,
articlePage: 1,
articleList: [],
articleLoading: false,
hasMore: true,
hotArticle: [],
searchHeight: 0,
needFixed: false,
right: 89,
isPro: false,
qrcodeUrl: require('~/assets/images/follow_qrcode.png'),
qrcodeUrlTest: require('~/assets/images/follow_qrcode_test.png'),
msgFormSon: true, //
topBannerHeight: 410,
popTopBannerShow: false,
popTopBannerTime: 5,
popTopBannerItem: {},
mobileTabNeedFixed: false,
hasCloseMobileNotLogin: false
// isLogin: 0,
};
},
computed: {
...mapGetters({
isLogin: 'isLogin'
}),
deviceType() {
return this.$store.state.device.deviceType;
}
},
created() {
this.isPro = process.env.VUE_APP_TITLE === 'production';
if (this.$store.state.device.deviceType !== 'pc') {
this.topBannerHeight = (410 * (320 - 30)) / 723;
}
},
mounted() {
const that = this;
that.baiduStat('糖尿病网-首页', 'show', '首页访问');
if (that.deviceType === 'pc') {
that.getArticleListData();
that.getHotArticleData();
} else {
that.articlePage = 2;
that.topBannerHeight =
(410 *
((document.documentElement.clientWidth || document.body.clientWidth) -
30)) /
723;
}
this.$nextTick(() => {
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
that.searchHeight = windowHeight - 86 - 66 - 30;
window.addEventListener('resize', function () {
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
that.searchHeight = windowHeight - 86 - 66 - 30;
});
window.addEventListener('scroll', function () {
// scrollTop
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
const scrollLeft =
document.documentElement.scrollLeft || document.body.scrollLeft;
that.scrollTop = scrollTop;
that.scrollLeft = scrollLeft;
if (that.deviceType === 'pc') {
if (scrollTop + 86 >= that.$refs.needFixed.offsetTop) {
that.needFixed = true;
if (document.documentElement.offsetWidth < 1258) {
that.right =
89 - (1258 - document.documentElement.offsetWidth) + scrollLeft;
} else {
that.right = (document.documentElement.offsetWidth - 1080) / 2;
}
} else {
that.needFixed = false;
}
} else if (scrollTop >= that.topBannerHeight + 56) {
that.mobileTabNeedFixed = true;
} else {
that.mobileTabNeedFixed = false;
}
// windowHeight
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// scrollHeight
const scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight;
//
if (scrollTop + windowHeight > scrollHeight - 50) {
// that
that.getArticleListData();
}
});
const topBanner = [];
const clickShowBanner = localStorage.getItem('clickShowBanner')
? JSON.parse(localStorage.getItem('clickShowBanner'))
: [];
for (const banner of that.topBanner) {
if (
banner.content_type === 'top_banner' &&
!clickShowBanner.includes(banner.id)
) {
topBanner.push(banner);
}
}
if (topBanner.length > 0) {
that.popTopBannerItem =
topBanner[Math.floor(Math.random() * topBanner.length)];
that.popTopBannerShow = true;
clickShowBanner.push(that.popTopBannerItem.id);
localStorage.setItem(
'clickShowBanner',
JSON.stringify(clickShowBanner)
);
const PopBannerinterval = setInterval(() => {
that.popTopBannerTime -= 1;
if (that.popTopBannerTime === 0) {
that.popTopBannerShow = false;
clearInterval(PopBannerinterval);
}
}, 1000);
}
});
},
methods: {
...mapActions({
showLoginDialog: 'showLoginDialog'
}),
goTopBanner(position, id, title, url, type) {
let des = '';
if (position === 'top') {
if (type === 'video') {
const { href } = this.$router.resolve({
path: '/video/' + id
});
url = href;
} else if (type === 'article') {
const { href } = this.$router.resolve({
path: '/article/' + id
});
url = href;
}
if (type === 'top_banner') {
const clickShowBanner = localStorage.getItem('clickShowBanner')
? JSON.parse(localStorage.getItem('clickShowBanner'))
: [];
clickShowBanner.push(id);
localStorage.setItem(
'clickShowBanner',
JSON.stringify(clickShowBanner)
);
des = '轮播banner-' + id;
} else {
des = '轮播内容-' + id + '+' + title;
}
} else if (position === 'right') {
des = '首页右侧小banner-' + id;
} else if (position === 'bottom') {
des = '底部弹层广告-点击';
}
this.baiduStat(
position === 'bottom' ? '底部弹层广告' : '糖尿病网-首页',
'click',
des
);
if (url) {
if (this.deviceType === 'pc') {
window.open(url, '_blank');
} else {
window.location.href = url;
}
}
},
async getArticleListData() {
if (this.hasMore && !this.articleLoading) {
this.articleLoading = true;
const data = await this.$axios.$get(
'/article/list?page=' + this.articlePage
);
this.hasMore = data.detail.hasMore;
this.articleList = this.articleList.concat(data.detail.articleList);
this.articlePage = this.articlePage + 1;
this.articleLoading = false;
}
},
async getHotArticleData() {
const data = await this.$axios.$get('/article/hot');
this.hotArticle = data.detail.articleList;
},
gotoLogin() {
this.showLoginDialog(true);
},
// CommonHeader
getMsgFormSon(data) {
this.msgFormSon = data;
console.log(this.msgFormSon);
}
}
};
</script>
<style lang="scss">
.index-page {
padding: 116px 0 76px;
.header-outer {
position: fixed;
top: 0;
left: 0;
width: 100%;
min-width: $outer-width;
background: $main-blue-color;
z-index: 9;
}
.library-page {
padding-top: 30px;
.top-banner {
position: relative;
border-bottom: 1px solid $bg-color;
padding-bottom: 30px;
.pop-banner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: calc(100% - 30px);
z-index: 3;
span {
position: absolute;
top: 12px;
right: 12px;
width: 100px;
height: 25px;
border-radius: 5px;
background: rgba(0, 0, 0, 0.4);
cursor: pointer;
font-size: 16px;
color: $body-color;
line-height: 25px;
text-align: center;
}
img {
width: 100%;
height: 100%;
cursor: pointer;
}
}
.top-banner-box {
overflow: hidden;
position: relative;
height: 100%;
border-radius: 4px;
img {
width: 100%;
height: 100%;
cursor: pointer;
}
.mask {
position: absolute;
bottom: 0;
left: 0;
padding: 43px 20px 0;
width: 100%;
height: 120px;
background: linear-gradient(
180deg,
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.4) 100%
);
box-sizing: border-box;
p {
font-size: 24px;
color: $body-color;
line-height: 30px;
}
}
}
.el-carousel__indicators--horizontal {
left: 0;
padding-left: 20px;
transform: translateX(0);
.el-carousel__indicator--horizontal {
margin-right: 8px;
padding: 0 0 20px;
&:last-child {
margin-right: 0;
}
.el-carousel__button {
@include roundFun(10px);
opacity: 1;
}
&.is-active {
.el-carousel__button {
width: 34px;
}
}
}
}
}
.hot-videos {
border-bottom: 1px solid $bg-color;
padding-bottom: 10px;
}
.doctor-box {
border-bottom: 1px solid $bg-color;
padding-bottom: 20px;
}
.common-flex {
grid-template-columns: repeat(3, 33.33%);
}
.not-login-box {
margin-bottom: 20px;
padding-top: 55px;
height: 202px;
background: $bg-color;
border-radius: 4px;
box-sizing: border-box;
text-align: center;
h6 {
font-size: 24px;
color: $main-blue-color;
line-height: 31px;
font-weight: normal;
}
p {
margin: 17px auto 0;
width: 208px;
height: 50px;
background: $extra-red;
border-radius: 4px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $body-color;
line-height: 50px;
cursor: pointer;
}
}
.hot-articles-box {
margin-bottom: 30px;
.common-title {
padding: 0;
font-size: 18px;
line-height: 25px;
span {
position: relative;
top: -1px;
display: inline-block;
margin-left: 6px;
width: 30px;
height: 15px;
background: $extra-red;
text-align: center;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: $body-color;
font-size: 12px;
line-height: 15px;
border-radius: 8px;
}
}
div {
margin-top: 8px;
a {
display: block;
position: relative;
border-bottom: 1px solid $bg-color;
padding: 10px 0 9px 124px;
height: 100px;
box-sizing: border-box;
img {
position: absolute;
left: 0;
width: 110px;
height: 80px;
border-radius: 4px;
}
p {
@include textEllipsisMore1Line(3);
font-size: 16px;
color: $font-color1;
line-height: 24px;
span {
display: inline-block;
margin-right: 4px;
width: 25px;
height: 16px;
background: #ccc5bd;
border-radius: 4px;
text-align: center;
font-size: 14px;
font-family: Apis-Regular, Apis;
font-weight: 400;
color: $body-color;
line-height: 16px;
}
}
}
a:first-child,
a:nth-child(2),
a:nth-child(3) {
p {
span {
background: $extra-yellow;
}
}
}
}
}
.go-wechat {
display: flex;
margin-bottom: 30px;
padding: 20px;
width: 325px;
height: 150px;
background: $bg-color;
border-radius: 4px;
box-sizing: border-box;
align-items: center;
justify-content: space-between;
div {
h4 {
font-size: 24px;
color: $main-blue-color;
line-height: 31px;
}
p {
margin-top: 17px;
font-size: 16px;
font-family: Apis-Regular, Apis;
font-weight: 400;
color: $font-color2;
line-height: 26px;
}
}
img {
width: 110px;
height: 110px;
background: $body-color;
}
}
.right-banner {
width: 325px;
img {
width: 100%;
height: 150px;
border-radius: 4px;
cursor: pointer;
}
}
}
.bottom-ad {
position: fixed;
bottom: 66px;
width: 100%;
min-width: $outer-width;
height: 160px;
background: rgba(0, 0, 0, 0.5);
text-align: center;
z-index: 4;
& > div {
position: relative;
margin: 0 auto;
width: $content-width;
height: 160px;
cursor: pointer;
img {
width: $content-width;
height: 100%;
object-fit: inherit;
}
i {
position: absolute;
top: 0;
left: 0;
color: $body-color;
font-size: 25px;
}
}
}
&.isMobile {
padding: 0;
.library-page {
padding: 0;
background: $bg-color;
.top-banner {
padding: 15px;
border-bottom: none;
margin-bottom: 10px;
background: $body-color;
.el-carousel--horizontal {
padding-bottom: 16px;
}
.top-banner-box {
.mask {
padding: 7px 11.5px 0;
height: 62px;
p {
@include textEllipsisMore1Line(2);
font-size: 16px;
line-height: 24px;
white-space: inherit;
}
}
}
.el-carousel__indicators--horizontal {
bottom: 0;
left: 50%;
padding: 0;
transform: translateX(-50%);
.el-carousel__indicator--horizontal {
margin-right: 4px;
padding: 0;
.el-carousel__button {
width: 8px;
height: 4px;
background: #e3e3e3;
border-radius: 2px;
}
&.is-active {
.el-carousel__button {
width: 16px;
background: $main-blue-color;
}
}
}
}
}
}
}
}
</style>

397
src/pages/more.vue

@ -0,0 +1,397 @@ @@ -0,0 +1,397 @@
<template>
<div
class="outer-container"
:class="{
isMobile: deviceType !== 'pc'
}"
>
<div
class="content-container more-page"
:class="{ isMobile: deviceType !== 'pc' }"
>
<CommonHeader
:device-type="deviceType"
:has-web-header="true"
:scroll-top="scrollTop"
:scroll-left="scrollLeft"
:search-height="searchHeight"
:tab-type="1"
:show-close="true"
@func="getMsgFormSon"
/>
<div class="top-entrance-box">
<h6>栏目</h6>
<MobileHeader :is-more="true" />
<h6>关于</h6>
<div class="other-box">
<a
href="/other/about"
@click="baiduStat('关于我们', 'click', '关于我们')"
>关于我们</a
>
<a
href="/other/agreement"
@click="baiduStat('用户协议', 'click', '用户协议')"
>用户协议</a
>
<a
href="/other/privacy"
@click="baiduStat('隐私政策', 'click', '隐私政策')"
>隐私政策</a
>
<a
href="/other/cookies"
@click="baiduStat('cookies政策', 'click', 'cookies政策')"
>cookies政策</a
>
<a
href="/other/corporate"
@click="baiduStat('媒体合作', 'click', '媒体合作')"
>媒体合作</a
>
<a
style="display: none"
href="/other/disclaimer"
@click="baiduStat('免责声明', 'click', '免责声明')"
>免责声明</a
>
<a
style="display: none"
href="/other/link"
@click="baiduStat('友情链接', 'click', '友情链接')"
>友情链接</a
>
</div>
</div>
<UserInfoCommon
v-if="isLogin"
:bean-num="beanNum"
:total="total"
:has-more="hasMore"
:article-list="articleList"
:collect-loading="collectLoading"
/>
<div v-else class="not-login-box">
<div class="top-box">
<el-avatar
src="https://cube.elemecdn.com/3/7c/3ea6beec64369c2642b92c6726f1epng.png"
style="cursor: pointer"
:size="50"
></el-avatar>
<p>未登录</p>
<span>更多个性化内容登录查看</span>
</div>
<div class="bottom-box"></div>
</div>
</div>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapActions, mapGetters, mapState } = createNamespacedHelpers('user');
export default {
name: 'MorePage',
// layout: 'common',
async asyncData({ req, $axios, store }) {
if (store.state.user.info && store.state.user.info.isLogin) {
const [dataA, dataB] = await Promise.all([
$axios.$get('/user/collect-list?page=1', $axios.genSSROptions(req)),
$axios.$get('/user/bean-num', $axios.genSSROptions(req))
]);
return {
...dataA.detail,
...dataB.detail
};
}
},
data() {
return {
scrollTop: 0,
scrollLeft: 0,
searchHeight: 0,
msgFormSon: true, //
// isLogin: 0,
collectPage: 2,
collectLoading: false
};
},
computed: {
...mapGetters({
isLogin: 'isLogin'
}),
deviceType() {
return this.$store.state.device.deviceType;
},
...mapState({
loginInfo: (state) => state.loginInfo,
loginDialogVisible: (state) => state.loginDialogVisible
})
},
created() {},
mounted() {
if (!this.isLogin) {
this.showLoginDialog(true);
document.getElementsByClassName('el-dialog__wrapper')[0].style.position =
'absolute';
document.getElementsByClassName('el-dialog__wrapper')[0].style.top =
'auto';
document.getElementsByClassName('el-dialog__wrapper')[0].style.bottom =
'0';
document.getElementsByClassName('mobile-login-popup')[0].style.margin =
'0';
document.getElementsByClassName('mobile-login-popup')[0].style.width =
'100%';
} else {
const that = this;
this.$nextTick(() => {
window.addEventListener('scroll', function () {
// scrollTop
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
// windowHeight
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// scrollHeight
const scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight;
//
if (scrollTop + windowHeight > scrollHeight - 50) {
// that
that.getCollectList();
}
});
});
}
},
methods: {
...mapActions({
showLoginDialog: 'showLoginDialog'
}),
// CommonHeader
getMsgFormSon(data) {
this.msgFormSon = data;
console.log(this.msgFormSon);
},
async getCollectList() {
if (this.hasMore && !this.collectLoading) {
this.collectLoading = true;
const data = await this.$axios.$get(
'/user/collect-list?page=' + this.collectPage
);
this.hasMore = data.detail.hasMore;
this.articleList = this.articleList.concat(data.detail.articleList);
this.collectPage = this.collectPage + 1;
this.collectLoading = false;
}
}
}
};
</script>
<style lang="scss">
.more-page {
padding: 61px 0 0 !important;
.common-header-outer {
border-bottom: 1px solid $bg-color;
}
.top-entrance-box {
border-bottom: 10px solid $bg-color;
padding: 15px 16px 22px;
& > h6 {
margin-bottom: 16px;
font-size: 16px;
color: $font-color3;
line-height: 21px;
font-weight: normal;
}
.mobile-header {
overflow: hidden;
position: unset !important;
display: block;
margin-bottom: 20px;
border-bottom: none;
height: auto;
white-space: normal;
}
.mobile-header > div > div,
.other-box {
display: flex;
padding: 0 9px;
height: auto;
flex-wrap: wrap;
/*justify-content: space-between;*/
p,
a {
margin-bottom: 20px;
/*width: 100%;*/
font-size: 16px;
font-family: MicrosoftYaHei;
color: $font-color1;
line-height: 21px;
}
p:first-child {
display: none;
}
p,
a {
width: 25%;
white-space: nowrap;
box-sizing: border-box;
text-align: center;
}
a:nth-child(4n + 1),
p:nth-child(4n + 2) {
text-align: left;
}
p:nth-child(4n + 4) {
padding-left: 10%;
}
@media screen and (min-width: 375px) {
a:nth-child(4n + 3) {
/*padding-left: 5%;*/
}
}
a:nth-child(4n + 4),
p:nth-child(4n + 5) {
text-align: right;
}
}
}
.not-login-box {
margin-top: 12px;
background: $body-color;
.top-box {
border-bottom: 1px solid $bg-color;
padding: 16px 0 0 73px;
height: 81px;
position: relative;
box-sizing: border-box;
& > span:first-child {
position: absolute;
top: 15px;
left: 15px;
}
p {
margin-bottom: 5px;
font-size: 18px;
color: $font-color1;
line-height: 24px;
}
span:last-child {
font-size: 14px;
color: $font-color2;
line-height: 19px;
}
}
.bottom-box {
height: 370px;
}
}
.top-user-info {
position: unset;
min-width: 100%;
height: auto;
background: transparent;
& > div {
width: 100%;
& > div {
height: auto;
}
& > div:first-child {
margin: 0;
border-bottom: 1px solid $bg-color;
padding: 0;
height: 81px;
box-sizing: border-box;
& > a {
position: relative;
top: 0;
right: 0;
display: block;
padding: 28px 48px 0 69px;
width: 100%;
height: 80px;
box-sizing: border-box;
background: transparent;
line-height: 0;
text-align: left;
& > span:first-child {
position: absolute;
top: 15px;
left: 15px;
width: 50px !important;
height: 50px !important;
border-radius: 25px !important;
}
& > p {
font-size: 18px;
color: $font-color1;
line-height: 24px;
@extend .text-ellipsis;
}
& > i {
position: absolute;
top: 31px;
right: 15px;
width: 18px;
height: 18px;
color: $main-blue-color;
}
}
}
& > div:nth-child(2) {
float: none;
margin: 0;
padding: 0 0 0 70px;
width: 100%;
height: 80px;
& > img {
top: 15px;
left: 15px;
width: 50px;
height: 50px;
}
p {
margin-top: 30px;
}
h5 {
position: absolute;
top: 25px;
right: 15px;
margin: 0;
}
}
}
}
.user-collect {
border-top: 44px solid $bg-color;
padding: 0;
h6 {
margin-bottom: 1px;
padding-left: 15px;
background: $body-color;
font-size: 16px;
font-family: MicrosoftYaHei;
color: $main-blue-color;
font-weight: normal;
line-height: 48px;
}
}
.beans-popup {
.el-dialog__body {
.popup-content {
& > div {
position: unset;
}
}
}
}
}
</style>

66
src/pages/other/about.vue

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
<template>
<div class="common-other-content about-us-page">
<h6>关于我们</h6>
<div class="font-content">
<h5>糖尿病网 智慧相伴</h5>
<p>
糖尿病网www.diabetes.com.cn是一个专注糖尿病领域的知识服务社区致力于为每一位糖友提供专业的控糖知识及新闻资讯帮助更多人拥有健康生活远离糖尿病带来的困扰
</p>
<p>
糖尿病网目前在微信公众号\网站等渠道拥有大量用户其中微信公众号稳居糖尿病垂直领域头部阵营
</p>
<h5>我们将持续探索和创新让糖友生活更美好</h5>
</div>
<div class="qrcode-box">
<img :src="isPro ? qrcodeUrl : qrcodeUrlTest" alt="" />
<p>微信服务号</p>
</div>
</div>
</template>
<script>
export default {
name: 'OtherAboutPage',
layout: 'detail',
data() {
return {
isPro: false,
qrcodeUrl: require('~/assets/images/follow_qrcode.png'),
qrcodeUrlTest: require('~/assets/images/follow_qrcode_test.png')
};
},
head: {
title: '糖尿病网-关于我们',
meta: [{ hid: 'description', name: 'description', content: '' }]
},
created() {
console.log('process.env.VUE_APP_TITLE:', process.env.VUE_APP_TITLE);
this.isPro = process.env.VUE_APP_TITLE === 'production';
}
};
</script>
<style lang="scss">
.about-us-page {
padding-bottom: 246px !important;
.qrcode-box {
float: right;
margin-top: 44px;
padding-top: 9px;
width: 139px;
height: 174px;
background: $bg-color;
border-radius: 1px;
text-align: center;
box-sizing: border-box;
img {
width: 110px;
height: 110px;
}
p {
font-size: 18px;
color: $font-color1;
line-height: 30px;
}
}
}
</style>

322
src/pages/other/agreement.vue

@ -0,0 +1,322 @@ @@ -0,0 +1,322 @@
<template>
<div class="common-other-content user-agreement-page">
<h6>糖尿病网用户服务协议</h6>
<div class="font-content">
<p style="text-align: right">修订日期2021年11月8日</p>
<p style="text-align: right">生效日期2021年11月8日</p>
<p>
糖尿病网是由诺和诺德中国制药有限公司以下称本公司所有及运营的网站/微信公众号/平台
糖尿病网用户服务协议以下简称本协议是您或称用户指注册登录使用浏览糖尿病网的个人或组织
与本公司及其关联公司及其合作单位包括但不限于糖尿病网授权运营执行第三方之间关于糖尿病网域名为app.diabetes.com.cn简称本平台
就本平台服务所订立的协议以下简称本协议本协议包括服务协议正文及所有本平台发布的各类规则等补充协议
平台发布的各类规则规范法律声明隐私政策通知公告帮助文档温馨提示等均为本协议的补充协议为本协议不可分割的一部分
与本协议正文具有同等法律效力
</p>
<b>重要提示</b>
<p>
您在点击同意本协议之前应当认真阅读本协议请您务必审慎阅读充分理解各条款内容特别是免除或者限制责任的条款法律适用和争议解决条款
<b>特别是粗体下划线标识之处您应重点阅读</b
>如您对协议有任何疑问可向糖尿病网咨询
</p>
<p>
<b>
如我们及关联公司范围详见定义部分提供了糖尿病网的服务或产品但未设独立用户协议的则本协议同样适用于该部分服务或产品
我们及关联公司就其向您提供的产品或服务单独设立用户协议的则相应产品或服务适用相应的用户协议
</b>
</p>
<p>
<b style="text-decoration: underline">
特别提示请仔细阅读本协议尤其是加粗或者下划线内容并确认充分理解本协议全部规则和要点一旦您开始使用或在我们更新本协议后
我们会及时提示您更新的情况继续使用我们的产品和服务即视为您同意本协议含更新版本的全部内容
同意我们按本协议收集使用保存共享和处理您的相关信息
</b>
</p>
<p>
<b>特别提示如您未满14周岁请在法定监护人的陪同下阅读本协议</b>
</p>
<p>
<b
>重要提醒
本次更新主要更新了如何收集使用个人信息章节中的部分内容进一步细化明确了我们收集信息的目的方式范围以及增加了撤回同意的方式等</b
>
</p>
<p>
如您对本协议或相关事宜有任何疑问可通过邮件<a
href="mailto:china-privacy@novonordisk.com"
target="_blank"
style="text-decoration: underline"
>china-privacy@novonordisk.com</a
>联系我们
</p>
<h5>. 定义</h5>
<p>
<b>糖尿病网</b>指糖尿病网域名为app.diabetes.com.cn
网站及糖尿病网微信公众号及小程序以下称本网站或客户端
</p>
<p>
<b>糖尿病网服务提供者</b
>指糖尿病网的运营公司诺和诺德中国制药有限公司
</p>
<p>
<b>关联公司</b
>指诺和诺德中国制药有限公司丹麦诺和诺德公司及其附属关联公司
</p>
<p>
<b>个人信息</b
>指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息
</p>
<p>
<b>个人敏感信息</b
>指包括身份证件号码个人生物识别信息财产信息行踪轨迹交易信息14岁以下儿童信息等的个人信息
</p>
<p>
<b>个人信息删除</b
>指在实现日常业务功能所涉及的系统中去除个人信息的行为使其保持不可被检索访问的状态
</p>
<p><b>儿童</b>指不满十四周岁的未成年人</p>
<h5> 用户注册与使用</h5>
<p>
在您使用糖尿病网的部分服务时可能需要您先完成用户调研调研完成您即成为注册用户
</p>
<h6>1. 用户资格</h6>
<p>
您确认在您开始使用糖尿病服务前您应当具备中华人民共和国法律规定的与您行为相适应的民事行为能力
您知悉无民事行为能力人限制民事行为能力人以及无经营或特定经营资格的组织视情况
不当注册为糖尿病网用户或超过其民事权利或行为能力范围从事与糖尿病网进行交易的其与糖尿病网之间的服务协议自始无效
一经发现糖尿病网有权立即停止与该用户的交易注销该用户您及您的监护人应依照法律规定承担因此而导致的一切后果
</p>
<h6>2. 账户注册</h6>
<p>
账户注册是指用户登录糖尿病网按要求填写相关信息并确认同意履行本协议的过程糖尿病网只允许每位用户注册及使用一个糖尿病网账户
<b
>如有证据证明或糖尿病网根据相关规则判断您存在不当注册或不当使用多个糖尿病网账户的情形糖尿病网可采取冻结或关闭账户取消订单拒绝提供服务等措施如给糖尿病网及相关方造成损失的您还应承担赔偿责任</b
>
</p>
<h6>3. 账户安全</h6>
<p>
您有权使用您设置或确认的糖尿病网会员名手机号码以下简称"账户名称"登录糖尿病网
<b>您的账户为您自行设置并由您保管</b>建议您务必保管好您的账户
<b
>账户因您主动泄露或因您遭受他人攻击诈骗等行为导致的损失及后果糖尿病网并不承担责任您应通过司法行政等救济途径向侵权行为人追偿</b
>
<br />
由于您的糖尿病网平台账户关联您的个人信息及糖尿病网平台商业信息
<b
>您的糖尿病网平台账户仅限您本人使用未经糖尿病网平台同意您直接或间接授权第三方使用您糖尿病网平台账户或获取您账户项下信息的行为无效</b
>
如糖尿病网根据糖尿病网平台规则中约定的违约认定程序及标准判断您糖尿病网平台账户的使用可能危及您的账户安全及/或糖尿病网平台信息安全的糖尿病网平台可拒绝提供相应服务或终止本协议
</p>
<h6>4. 账户转让</h6>
<p>
由于用户账户关联用户信用信息仅当有法律明文规定司法裁定或经糖尿病网平台同意并符合糖尿病网平台规则规定的用户账户转让流程的情况下您可进行账户的转让
您的账户一经转让该账户项下权利义务一并转移除此外<b
>您的账户不得以任何方式转让否则糖尿病网平台有权追究您的违约责任且由此产生的责任及后果均由您自行承担</b
>
</p>
<h6>5. 实名认证</h6>
<p>
作为糖尿病网平台经营者为使您更好地使用糖尿病网服务保障您的账户安全糖尿病网平台可要求您按相关法律法规规定完成实名认证
</p>
<h6>6. 信息真实</h6>
<p>
在使用糖尿病网平台服务时您应当按糖尿病网平台页面的提示准确完整地提供您的信息包括您的姓名及电子邮件地址联系电话联系地址收货地址等以便糖尿病网或其他用户与您联系
<b
>您了解并同意您有义务保持您提供信息的真实性及有效性如您的信息发生变动您应当在变动发生后10日内及时更新您的信息</b
>
因您未及时更新信息导致未能正常使用糖尿病网服务或发生其他损失的均由您承担相关责任
</p>
<h6>7. 税费承担</h6>
<p>
您因使用糖尿病网进行交易获取有偿服务等而发生的所有您应纳的税赋由糖尿病网为您承担
</p>
<h5>商品交易</h5>
<p><b>您在糖尿病网兑换商品时必须遵守以下条款</b></p>
<p>
1.您在使用糖尿病网服务时应遵守所有适用的中国法律法规条例和地方性法律的要求您还必须确保遵守本协议及纳入本协议的所有其他条款和规则的规定
</p>
<p>
2.您决定通过糖尿病网兑换某一商品的即代表您同意受该商品描述所含的出售条件的约束只要该等出售条件不违反法律或本协议规定
</p>
<p>
3.您理解并认可糖尿病网上的订单生效规则糖尿病网展示的商品信息如商品名称价格商品描述等仅构成要约邀请
<b
>当您通过糖尿病网订购商品确定兑换并成功提交订单时订单内容应包含兑换的商品数量价格及支付方式收货人联系方式收货地址等信息即视为您向销售方发出了购买订单商品的要约糖尿病网订单出库时合同成立</b
>
</p>
<p>
4.在您下订单的同时也同时承认了您已经达到兑换所订商品的法定年龄并对您在订单中提供的所有信息的真实性负责由于糖尿病网出售部分产品的特殊性
您可能被要求进一步提供与购买使用服用订单项下商品相关的信息方能继续订单流程该等信息视情况可能包括您或使用服用商品人的个人信息或其他信息您应保证提供的信息真实有效
<b
>糖尿病网有权对该等信息进行审核并要求您做出进一步说明并视情况可能拒绝您发出的订单或取消已经生成的订单</b
>
</p>
<p>5.糖尿病网保留对您产品兑换数量频次的限制权</p>
<p>
6.您的订单确认后如发生意外情况的包括但不限于糖尿病网上显示的订单内商品和/或订单明显错误或缺货
<b
>糖尿病网将会通过站内信或电话通知您与您协商您应当及时予以回应</b
>
</p>
<p>
7.商品价格和可获性都将在糖尿病网上标明显示的每一项价格<b
>运费将另外结算糖尿病网将在结算页面通知您您应当仔细阅读</b
>
</p>
<p>
8.当您在糖尿病网平台兑换商品及/或服务时请您务必仔细确认所兑换商品的品名价格数量型号规格或服务的时间内容限制性要求等重要事项
并在下单时核实您的联系地址电话收货人等信息如您填写的收货人非您本人则该收货人的行为和意思表示产生的法律后果均由您承担
<br />
您的兑换行为应当基于真实的消费需求不得存在对商品及/或服务实施恶意兑换恶意维权等扰乱糖尿病网平台正常交易秩序的行为
<b
>基于维护糖尿病网平台交易秩序及交易安全的需要糖尿病网发现上述情形时可主动执行关闭相关交易订单等操作</b
>
</p>
<h5>您的权利和义务</h5>
<p>
1.您有权根据本协议的规定以及糖尿病网上发布的相关规则邀请关注好友参加糖尿病网的有关活动以及享受糖尿病网提供的其它信息服务
</p>
<p>
2.您应当保证在使用糖尿病网兑换商品过程中遵守诚实信用原则不在购买过程中采取不正当行为不扰乱网上交易的正常秩序
</p>
<p>3.您不得在糖尿病网发表包含以下内容的言论</p>
<p>
①煽动抗拒破坏宪法和法律行政法规实施的<br />
②煽动颠覆国家政权推翻社会主义制度的<br />
③煽动分裂国家破坏国家统一的<br />
④煽动民族仇恨民族歧视破坏民族团结的<br />
⑤任何包含对种族性别宗教地域内容等歧视的<br />
⑥捏造或者歪曲事实散布谣言扰乱社会秩序的<br />
⑦宣扬封建迷信淫秽色情赌博暴力凶杀恐怖教唆犯罪的<br />
⑧公然侮辱他人或者捏造事实诽谤他人的或者进行其他恶意攻击的<br />
⑨损害国家机关信誉的<br />
⑩其他违背社会公共利益或公共道德或依据相关糖尿病网平台协议规则的规定不适合在糖尿病网平台上发布的<br />
⑪其他违反宪法法律和行政法规的<br />
您在发表使用体验讨论图片等除遵守本条款外还应遵守国家相关法律法规<br />
本公司有权根据国家法律及相关部门的要求对您上传发布的内容进行审核有权根据相关证据结合侵权责任法网络安全法等法律法规规章及本协议对侵权信息进行处理
</p>
<h5>糖尿病网的权利和义务</h5>
<p>
1.糖尿病网有义务在现有技术上维护整个网上交易平台的正常运行并努力提升和改进技术使您得以顺利享受网站服务
</p>
<p>
2.对您在注册使用糖尿病网中所遇到的有关的问题及反映的情况糖尿病网应及时作出回复
</p>
<p>
3.<b
>对于您在糖尿病网上的不当行为或任何糖尿病网认为应当暂时中止服务的情况糖尿病网有权即刻作出暂时屏蔽相关信息中止提供服务等处理并通知您</b
>
您对糖尿病网的处理措施存在异议的可以提供相关证据予以说明若经核实您的行为确有违法违规或违反本平台用户协议交易规则的情况糖尿病网有权终止对您提供服务
</p>
<p>
4.糖尿病网将尽最大努力采取必要合理措施保障糖尿病网网络安全稳定运行糖尿病网将在业务范围内为您提供最大帮助
</p>
<h5> 通知</h5>
<p>
1.您在注册成为糖尿病网平台用户并接受糖尿病网服务时您在注册糖尿病网平台用户时生成的账户用于登录糖尿病网平台接收站内信和系统消息作为您的有效联系方式
</p>
<p>
2.糖尿病网将向您的您送达各类通知而此类通知的内容可能对您的权利义务产生重大的有利或不利影响请您务必及时关注
</p>
<p>
3.糖尿病网通过上述联系方式向您发出通知其中以电子的方式发出的书面通知包括但不限于在糖尿病网平台公告
向您提供的联系电话发送手机短信向您的账号发送系统消息以及站内信信息在发送成功后即视为送达
</p>
<h5>知识产权</h5>
<p>本平台著作权属于本公司所有</p>
<p>
1.您在使用本平台的相关服务时发表上传的文字图片及视频等应为您原创的信息或已获合法授权并且确保该内容不会侵犯任何第三方的合法权益
<b
>如果第三方提出关于知识产权的异议本公司有权根据实际情况删除相关内容并尽可能在处理之后对您进行通知由此造成的损失及后果由您自行承担</b
>
</p>
<p>
2.本公司单独拥有或与相关内容提供者共同拥有本平台/网站内所有内容包括但不限于文字图片音频视频资料及页面设计编排软件等
或通过本平台提供产品或服务所涉及到的版权和/或其他相关知识产权与本平台相关的标识为本公司的注册商标受中国法律保护
</p>
<p>
3.除非中国法律另有规定未经本公司书面许可对于诺和诺德拥有版权和或其他相关知识产权的任何内容
任何公司及个人不得复制或以其他任何方式进行使用包含但不限于抄录编辑修改及传播对于本平台标识等本公司的注册商标任何人不得擅自使用
已获得书面授权可以使用本公司拥有版权和或其他相关知识产权的任何内容及商标标识的公司及个人使用上述内容时必须注明来源
</p>
<p>
4.本平台注明来源的稿件图片及其他作品的内容均属转载本平台的转载行为是基于信息共享和传播并未进行权属真实性等问题的核实亦并不对其观点持任何立场
其他公司或个人若从本平台下载使用所转内容须保留本平台网站注明的稿件来源并自负版权等法律责任
</p>
<p>
5.本平台的内容或转载内容若涉及版权及其他任何侵权问题请版权人或其他权利人以书面形式向本公司反映并提供相应身份证明权属证明及详细的侵权情况证
</p>
<h5>个人信息保护</h5>
<p>
本公司将按照糖尿病网个人信息及隐私保护政策填写链接收集使用处理您的个人信息按照适用法律对于您的个人信息
您享有查询更正删除以及注销服务或帐号的权利请致电400-810-2299具体流程请参见隐私政策
</p>
<h5>责任条款</h5>
<p>
本公司将尽合理努力为本平台提供准确的信息但对所提供信息的准确性即时性或完整想不作任何陈述担保或保证
本公司对您由于访问或不能访问本平台或者因依赖本平台所提供的信息而产生的任何损失或损害不承担任何责任
</p>
<p>
本公司不对经您本人同意自愿并自行上传本人信息的准确性及时性及完整性承担责任请您本人在上传个人信息时仔细核对信息的准确性
如因您所上传数据有误而影响与上传信息相关的服务本公司不承担由此产生的一切责任
如本人发现上传信息有误或不更新可通过<a
href="tel:400-810-2299"
style="text-decoration: underline"
>400-810-2299</a
>更新相关信息
</p>
<p>
本平台可能含有其他平台的链接或引用或第三方提供的信息或内容但本公司对其他平台或第三方提供的内容不承担任何责任
而且对该内容所导致的损失或损害不承担任何责任所有其他平台的链接仅为方便本平台用户而提供
</p>
<p>
您同意保障和维护本公司及其他您的利益由于您使用本平台时出现内容违法不真实不正当侵犯第三方合法权益
或您违反本协议项下的任何条款而给本公司或任何其他第三人造成损失您同意承担由此造成的损害赔偿责任
</p>
<p>
如您的行为使本公司陷于任何第三人主张权利本公司可在对第三人承担金钱给付等义务后就全部损失向您追偿
</p>
<p>
本公司依法律规定承担相应义务但无法对由于信息网络设备维护连接故障电脑通讯或其他系统的故障黑客活动计算机病毒电力故障罢工
暴乱火灾洪水风暴爆炸战争政府行为司法行政机关的命令或因第三方原因而给您造成的损害结果承担责任
</p>
<h5>法律适用及管辖</h5>
<p>
本协议之订立生效解释修订补充终止执行与争议解决均适用中华人民共和国大陆地区法律
</p>
<p>
您和本公司一致同意有关本协议以及使用本平台的服务产生的争议将优先友好协商解决经友好协商仍不能解决时应向本公司住所地有管辖权的人民法院提起诉讼
</p>
<p>个人信息及隐私保护政策</p>
<p>
我们深知个人信息对您的重要性并会尽全力保护您的个人信息安全可靠我们致力于维持您对我们的信任并恪守以下原则保护您的个人信息
权责一致原则目的明确原则选择同意原则最少够用原则确保安全原则主体参与原则公开透明原则等
同时我们承诺我们将按业界成熟的安全标准采取相应的安全保护措施来保护您的个人信息鉴于此我们制定了本隐私权政策下称本政策
/
</p>
<p>
本政策适用于糖尿病网所提供的所有产品及服务如我们及关联公司范围详见定义部分的产品或服务中使用了上述提供的产品或服务但未设独立隐私权政策的
则本政策同样适用于该部分产品或服务我们及关联公司就其向您提供的产品或服务单独设立有隐私权政策的则相应产品或服务适用相应隐私权政策
需要特别说明的是本政策不适用于其他第三方向您提供的服务第三方向您提供的服务适用其向您另行说明的隐私权政策
</p>
<p>本服务的使用人在本政策中称为用户或称为</p>
<p>
请您在使用本服务的各项产品及服务前仔细阅读并充分理解本隐私保护政策协议您在点击确认/同意按钮或勾选同意后本政策即构成对双方有约束力的法律文件
即表示您同意我们按照本政策收集使用处理和存储您的相关个人信息如果您对本隐私政策有任何疑问意见或建议可通过本政策第9条提供的联系方式与我们联系
</p>
</div>
</div>
</template>
<script>
export default {
name: 'OtherAgreementPage',
layout: 'detail',
data() {
return {};
},
head: {
title: '糖尿病网-用户协议',
meta: [{ hid: 'description', name: 'description', content: '' }]
}
};
</script>

127
src/pages/other/cookies.vue

@ -0,0 +1,127 @@ @@ -0,0 +1,127 @@
<template>
<div class="common-other-content cookies-page">
<h6>cookies政策</h6>
<div class="font-content">
<p>cookies政策内容--待定</p>
<h5>1. 什么是cookies?</h5>
<p>
Cookie是网站服务器在您在自己的电脑浏览器或者移动设备上访问网站时放置的文本文件Cookie包含一个独特的代码允许糖尿病网在您访问网站时识别您的浏览器(称为对话缓存cookie)或稍后重复访问时识别您的浏览器(称为持久缓存cookie)每个cookie都是您网页浏览器独有的Cookies可能由您访问的网站服务器或由和网站合作的合作伙伴放置即所谓的第三方Cookies
</p>
<p>
Cookies通常可使使用者与网站可以更容易和快捷地进行互动并帮助使用者在网站不同部分之间进行切换Cookies也可用于使网站的内容更符合访问者的需求并使网站符合访问者的个人喜好和需要
</p>
<h5>2. [糖尿病网www.diabetes.com.cn ]对Cookies的使用</h5>
<p>
2.1 [本网站www.diabetes.com.cn]的拥有者为: 诺和诺德中国制药有限公司
</p>
<p>
2.2诺和诺德利用cookies存储用户的参数设置从而增强网站的功能
在糖尿病网网页上使用必须的cookies如下
</p>
<table>
<tr>
<td>cookies名称</td>
<td>cookies说明</td>
<td>过期时间</td>
</tr>
<tr>
<td>novo2</td>
<td>用于保存登录信息包括用户登录状态登录密码</td>
<td>15天后自动过期</td>
</tr>
<tr>
<td>xsrf-token</td>
<td>身份校验防止非法请求伪造信息</td>
<td>15天后自动过期</td>
</tr>
</table>
<p>
2.2.1
为了访问并使用某些部分这些cookies是必须的如果你拒绝这些cookies网站的一些功能将不能正常运转
</p>
<h5>3. Cookie 管理</h5>
<p>
3.1
您必须指示您是否允许将cookie放置在您的设备上或者禁止某些类型的cookie如果您不接受某些cookies的使用那么网站的某些功能将无法被使用或者您可能无法使用网站的某些服务
</p>
<p>
3.2
如果您希望限制或阻止使用cookies您必须通过网站上显示的cookie设定您的参数您也可以通过浏览器设置限制cookies的使用浏览器中的帮助功能会告诉您如何操作
</p>
<p>3.3 请注意限制cookies可能影响糖尿病网网站的功能</p>
<h5>4. 数据隐私</h5>
<p>
4.1
当您访问我们的网站时我们会自动收集与您的电脑相关的基本信息其所处位置信息以及您是跳转自哪个网站如适用这些信息不能被用于识别您的个人身份这些信息只会以聚合的形式使用以使我们知晓我们的访问者来自哪里他们都浏览了哪些内容和做了什么以及他们针对哪些内容所花费时间最多
</p>
<p>
4.2
当我们要求您提供任何可用于识别您个人身份的个人可识别信息时我们都始终会征得您的许可并针对我们为何收集如何收集如何存储以及我们计划如何处理和使用这些信息做出解释
</p>
<p>
4.3
我们将仅针对在收集过程中声明的目的收集并存储个人可识别信息在我们针对已声明目的将您的信息处理完毕后我们会将该信息删除并销毁以保护您的隐私
</p>
<p>
4.4 有些数据基于我们的正当利益通过cookies进行收集其目的包括
改善我们的网站和您的用户体验正常使用我们网站的功能以及使我们能够针对特定目的优化我们的网站
</p>
<p>
4.5
对于各类因为特定目的收集个人资料的cookie糖尿病网将只会针对您在cookie
功能管理中许可的目的收集的个人资料进行处理
</p>
<p>4.6 您所有的个人资料将会被保密处理并且只会用于以上所陈述的目的</p>
<p>
4.7糖尿病网可能会聘请第三方服务供应商他们将根据我们的委托处理您的个人信息
</p>
<p>
4.8 根据数据保护规定您有以下权力要行使这些权力请通过<a
href="mailto:china-privacy@novonordisk.com"
target="_blank"
style="text-decoration: underline"
>china-privacy@novonordisk.com</a
>联系诺和诺德 我们会在15日内对您进行回复<br />
您可查阅我们拥有您的何种个人信息<br />
您可获取一份结构化的常用机读格式个人信息副本<br />
您可要求对您的个人信息进行更新或修正<br />
您可要求您的个人信息被删除或销毁<br />
您可要求我们停止或限制对您的个人信息进行处理<br />
如您已许可我们对您的个人信息进行处理您可在任何时间撤回许可您撤回许可将不影响在您撤回许可前已进行的数据处理的合法性<br />
针对我们处理您个人信息的方式您可向数据保护主管部门提出投诉<br />
</p>
<p>
依据适用法律上述权利视数据处理活动具体情况可能会受到限制如您有与上述权利相关问题或请求请通过上述所述方式与我们取得联系
</p>
</div>
</div>
</template>
<script>
export default {
name: 'OtherCookiesPage',
layout: 'detail',
data() {
return {};
},
head: {
title: '糖尿病网-cookies政策',
meta: [{ hid: 'description', name: 'description', content: '' }]
}
};
</script>
<style lang="scss">
.cookies-page {
table {
border-spacing: 0;
border-collapse: collapse;
td {
border: 1px solid $font-color3;
padding: 0 10px;
font-size: 18px;
color: $font-color3;
line-height: 40px;
}
}
}
</style>

28
src/pages/other/corporate.vue

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
<template>
<div class="common-other-content media-corporate-page">
<h6>媒体合作</h6>
<div class="font-content">
<p>
商务合作邮箱<a
href="mailto:DHcommunications@novonordisk.com"
target="_blank"
>DHcommunications@novonordisk.com</a
>
</p>
</div>
</div>
</template>
<script>
export default {
name: 'OtherCorporatePage',
layout: 'detail',
data() {
return {};
},
head: {
title: '糖尿病网-媒体合作',
meta: [{ hid: 'description', name: 'description', content: '' }]
}
};
</script>

39
src/pages/other/disclaimer.vue

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
<template>
<div class="common-other-content disclamier-page">
<h6>免责声明</h6>
<div class="font-content">
<p>
最新糖尿病调查结果显示我国约有4亿人处在糖尿病前期但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常仅查空腹血糖会有70%的糖尿病前期被漏诊糖尿病前期是糖尿病发展的必经之路那么什么是糖尿病前期如何预防疾病进展呢
</p>
<p>
最新糖尿病调查结果显示我国约有4亿人处在糖尿病前期但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常
</p>
<p>
最新糖尿病调查结果显示我国约有4亿人处在糖尿病前期但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常仅查空腹血糖会有70%的糖尿病前期被漏诊糖尿病前期是糖尿病发展的必经之路那么什么是糖尿病前期如何预防疾病进展呢
</p>
<p>
最新糖尿病调查结果显示我国约有4亿人处在糖尿病前期但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常
</p>
<p>
最新糖尿病调查结果显示我国约有4亿人处在糖尿病前期但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常仅查空腹血糖会有70%的糖尿病前期被漏诊糖尿病前期是糖尿病发展的必经之路那么什么是糖尿病前期如何预防疾病进展呢
</p>
<p>
最新糖尿病调查结果显示我国约有4亿人处在糖尿病前期但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常但90%的人不知道自己处于糖尿病前期这是因为很多人不测血糖或只测空腹血糖殊不知空腹血糖正常不代表餐后血糖也正常
</p>
</div>
</div>
</template>
<script>
export default {
name: 'OtherDisclaimerPage',
layout: 'detail',
data() {
return {};
},
head: {
title: '糖尿病网-免责声明',
meta: [{ hid: 'description', name: 'description', content: '' }]
}
};
</script>

76
src/pages/other/link.vue

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
<template>
<div class="common-other-content other-link-page">
<h6>我们的合作伙伴</h6>
<div class="link-content">
<a href="https://www.dnurse.com">
<img src="~/assets/images/wechat_color.png" alt="" />
</a>
<a href="https://www.dnurse.com">
<img src="~/assets/images/wechat_color.png" alt="" />
</a>
<a href="https://www.dnurse.com">
<img src="~/assets/images/wechat_color.png" alt="" />
</a>
<a href="https://www.dnurse.com">
<img src="~/assets/images/wechat_color.png" alt="" />
</a>
<a href="https://www.dnurse.com">
<img src="~/assets/images/wechat_color.png" alt="" />
</a>
<a href="https://www.dnurse.com">
<img src="~/assets/images/wechat_color.png" alt="" />
</a>
</div>
<p>
申请加入友情链接请联系邮箱
<a href="mailto:DHcommunications@novonordisk.com" target="_blank"
>DHcommunications@novonordisk.com</a
>
</p>
</div>
</template>
<script>
export default {
name: 'OtherLinkPage',
layout: 'detail',
data() {
return {};
},
head: {
title: '糖尿病网-友情链接',
meta: [{ hid: 'description', name: 'description', content: '' }]
}
};
</script>
<style lang="scss">
.other-link-page {
.link-content {
a {
display: inline-block;
margin: 0 19.33px 28px 0;
width: 240px;
height: 92px;
border-radius: 1px;
border: 1px solid #ededed;
box-sizing: border-box;
text-align: center;
img {
width: auto;
height: 100%;
}
}
a:nth-child(4n) {
margin-right: 0;
}
}
p {
font-size: 15px;
color: $font-color2;
line-height: 30px;
a {
color: $font-color2;
}
}
}
</style>

196
src/pages/other/privacy.vue

@ -0,0 +1,196 @@ @@ -0,0 +1,196 @@
<template>
<div class="common-other-content privacy-policy-page">
<h6>隐私政策</h6>
<div class="font-content">
<p>
本网站为诺和诺德中国制药有限公司下称诺和诺德所有其内容为诺和诺德受版权法保护的财产并由诺和诺德对网站进行日常管理
</p>
<h5>1. 我们收集的信息</h5>
<p>
1.1
我们收集能够帮助我们改善网站的信息部分信息我们会直接询问您而另一些则会自动收集所有自动收集到的信息都会以聚合形式存储且不能被用于识别具体个人我们有时会请求您提供在某些情况下可被用于识别个人身份和隐私的信息
</p>
<p>
因此我们所收集的信息可被分为两类<br />
a) 聚合的访问者统计数据 <br />
b) 个人可识别信息 <br />
我们不会将聚合的访问者统计数据与个人可识别信息进行关联
</p>
<p>
<b>聚合的访问者统计数据</b><br />
当您访问我们的网站时我们会自动收集与您的电脑相关的基本信息其所处位置信息以及您是跳转自哪个网站如适用这些信息不能被用于识别您的个人身份这些信息只会以聚合的形式使用以使我们知晓我们的访问者来自哪里他们都浏览了哪些内容和做了什么以及他们针对哪些内容所花费时间最多
</p>
<p>
<span style="text-decoration: underline">我们自动收集的信息包括</span
><br />
第一方cookie支持无论您是否允许我们在您的电脑上放置cookie<br />
访问者ID在可能时由我们放置在您电脑上的cookie提供<br />
跳转来源即您来自哪个网站如google.com<br />
访问日期与时间<br />
区域及语言设置以确定您在哪个国家<br />
操作系统WindowsOS XLinuxiOS安卓等<br />
浏览器及浏览器版本ChromeIEFirefoxOperaSafari等<br />
屏幕分辨率1280x10241024x768等<br />
JavaScript支持<br />
Java支持<br />
IP地址电脑在因特网上的地址<br />
您所浏览网页的标题<br />
您所浏览网页的URL<br />
</p>
<p>
<b>个人可识别信息</b>
<br />有时我们需要收集与您相关的更多个人信息<br />
</p>
<p>
<i>帮助您成为我们网站的注册用户</i><br />
在您点击我们网站上的注册之后我们会弹窗提示您扫描我们的微信公众号二维码以及查看我们的隐私政策我们需要收集您的[微信Uni
ID]才能为您完成注册<br />
</p>
<p>
<i>为您提供更佳的网页体验及个性化推荐</i><br />
在您成为我们的注册用户后当您继续浏览我们的网站时我们会收集并整合您在我们网站上以及我们的微信公众号中的[点击关注评论分享搜索收藏]等浏览使用行为浏览时长和设备信息通过算法预测您的偏好特征并基于特征标签产出间接人群画像用于展示推送您可能感兴趣的信息和内容<br />
若您同时是我们的诺和关怀的注册用户我们还将这些个人信息与诺和关怀处理的个人信息进行进一步内部数据整合和分析一同用于前述算法预测以及向您进行个性化内容推荐
</p>
<p>
<b
>我们努力保障您的浏览体验如果您不想接受我们给您发送的推送内容您可通过[设置-开启/关闭个性化内容推荐]选择关闭</b
>
</p>
<p>
若我们要求您提供任何其他可用于识别您个人身份的个人可识别信息时我们都会另行针对我们为何收集如何收集如何存储以及我们计划如何处理和使用这些信息做出解释
</p>
<h5>2. 我们如何使用信息</h5>
<p>2.1 我们将仅针对在收集过程中声明的目的使用并存储个人可识别信息</p>
<h5>3. 敏感信息的收集</h5>
<p>
3.1
我们不在本网站上收集或保存与您的健康民族宗教信仰或政治信念相关的敏感信息
</p>
<h5>4. 保护儿童</h5>
<p>
4.1
保护儿童隐私非常重要诺和诺德无意在未得到儿童父母或法定监护人许可的情况下收集其个人可识别信息儿童定义为14岁以下的孩子在未征得其父母或法定监护人明确许可的情况下儿童不应向诺和诺德提交其个人可识别信息
</p>
<p>
我们会在适当的地方指示儿童不要提交其个人信息如您的孩子已提交其个人信息且您希望将其删除请联系<a
href="mailto:china-privacy@novonordisk.com"
target="_blank"
style="text-decoration: underline"
>china-privacy@novonordisk.com</a
>
</p>
<h5>5. 如何确保信息安全</h5>
<p>
5.1
本网站会在必要范围内收集个人数据在任何情况下所收集数据都不会以任何理由被出售给第三方
</p>
<h5>6. 数据在哪进行处理</h5>
<p>
6.1
我们依照适用的法律法规的规定将我们所收集的您的个人信息存储于中华人民共和国境内目前我们不会将上述信息传输至其他地区如果我们向其他地区传输我们将遵循相关国家规定征求您的同意
</p>
<h5>7. 存储期限</h5>
<p>
在您的任何个人信息已连续2未被用于本隐私政策中所列明之目的时以二者中较早者为准我们将删除相关个人信息或者将其进行匿名化但法律法规另有规定的除外
</p>
<h5>8. 关于向第三方披露</h5>
<p>
8.1
我们会聘请服务供应商依照我们的委托协助我们处理个人信息我们在任何情况下都不会向第三方转让或分享您的个人可识别信息但以下情况除外<br />
a) 在另行获得您的单独同意后向经同意的其他个人信息处理者提供<br />
b)
因合并分立解散被宣告破产等原因需要转移个人信息的我们会要求新的持有您个人信息的个人信息处理者继续受本隐私政策的约束否则我们将要求该第三方重新取得您的同意<br />
c)
我们可能会根据法律法规规定或按政府主管部门的强制性要求向有关主管部门提供您的个人信息
</p>
<h5>9. 访问者权利</h5>
<p>
9.1 依据数据保护规定您拥有如下权利欲行使这些权利请通过<a
href="mailto:china-privacy@novonordisk.com"
target="_blank"
style="text-decoration: underline"
>china-privacy@novonordisk.com</a
>与诺和诺德联系我们会在15日内对您进行回复
</p>
<p>
您可查阅我们拥有您的何种个人信息<br />
您可获取一份结构化的常用机读格式个人信息副本<br />
您可要求对您的个人信息进行更新或修正<br />
您可要求您的个人信息被删除或销毁<br />
您可要求我们停止或限制对您的个人信息进行处理<br />
如您已许可我们对您的个人信息进行处理您可在任何时间撤回许可您撤回许可将不影响在您撤回许可前已进行的信息处理的合法性<br />
针对我们处理您个人信息的方式您可向数据保护主管部门提出投诉<br />
依据适用法律上述权利视信息处理活动具体情况可能会受到限制<br />
如您有与上述权利相关问题或请求请通过上述电子邮箱与我们取得联系
</p>
<h5>10. 使用cookies</h5>
<p>
10.1 您可阅读我们的<a
href="/other/cookies"
style="text-decoration: underline"
>Cookie政策</a
>了解更多我们如何使用cookies
</p>
<h5>11. 法律免责声明</h5>
<p><b>信息目的</b></p>
<p>
诺和诺德网站展示的内容是基于信息传播目的本网站并不向您提供任何类型的意见或建议也不应被作为任何决策或行动的依据针对网站内容在任何特定领域是否适用建议您咨询相关领域的专业顾问特别是本网站任何内容均不构成投资或交易诺和诺德股票的要约或邀请此外本网站提供一些疾病及其治疗方案的特定信息此类信息不应被视为医疗建议此类信息不能替代医疗专业人士的建议如您有任何健康问题或怀疑有健康问题您应咨询全科医师或其他有资质的医疗服务提供者
</p>
<p><b>信息按原样提供</b></p>
<p>
本网站信息按原样提供诺和诺德未就其包括但不限于适销性针对某一特定目的的适宜性或非侵权性做出任何明示或默示的陈述或保证诺和诺德未就其完整性精确性及时性可及性功能性及是否符合适用法律做出任何陈述或保证使用本网站即代表您接受其信息可能不完整或不精确或不能满足您的需要或需求的风险
</p>
<p><b>免除责任</b></p>
<p>
对于您因访问或无法访问本网站或您因依赖本网站任何信息所造成的破坏或伤害诺和诺德及我们的内容提供者概不负责诺和诺德无须为直接间接偶然性后果性惩罚性及特殊或其它破坏机会丧失利润丧失或其它任何类型的损失或破坏承担任何及一切责任这一限制包括任何可能影响您电脑设备的破坏或病毒
</p>
<p><b>链接至其它网站</b></p>
<p>
如本网站包含指向其它非诺和诺德拥有或控制网站的链接请知晓我们不为这些网站的隐私政策负责对其也没有控制权本隐私声明仅适用于在本网站上收集的信息我们强烈建议您在访问每个收集个人可识别信息的网站时都阅读其隐私声明
</p>
<p><b>有本网站链接的网站</b></p>
<p>
诺和诺德不为任何链接至诺和诺德站点的网站背书诺和诺德不为这些网站的内容负责对用户可能选择向这些网站提供的信息也不负责
</p>
<p><b>变更</b></p>
<p>
诺和诺德保留在任何时间由其全权自主变更修改替换或删除任何本网站内容限制访问本网站或中止分发本网站的权利
</p>
<p><b>内容的版权及使用</b></p>
<p>
本网站内容是诺和诺德受版权法保护的财产本网站内所展示的商标服务商标商用名称标识和产品均在全球范围内受到保护未提前获得诺和诺德书面同意不得对其进行任何使用欢迎您下载本网站内容但仅供您个人进行非商业目的使用禁止对上述内容进行修改或进一步复制不得以任何其它方式复制或使用网站内容
</p>
<p><b>使用提问及评论功能</b></p>
<p>
您向本网站或诺和诺德以电子或任何其它手段所传递的任何问题评论建议或任何其它沟通手段包括任何创意发明概念技术或专业技能都不会被视为机密而且将成为诺和诺德的财产诺和诺德可不受限制地将其以任何方式用于任何目的包括产品或服务的研发制造及营销
</p>
<p><b>管辖法律</b></p>
<p>
您访问和使用本网站及其内容将受中国法律及中国缔结的国际条约管辖并据此解释
</p>
<h5>12. 联系方式</h5>
<p>
如您针对此隐私政策服务协议cookies政策及法律免责声明有任何问题如您希望深入了解网站所拥有的任何与您相关的个人信息如对诺和诺德使用您的个人信息您有任何担忧您可通过<a
href="mailto:china-privacy@novonordisk.com"
target="_blank"
style="text-decoration: underline"
>china-privacy@novonordisk.com</a
>与我们取得联系
</p>
</div>
</div>
</template>
<script>
export default {
name: 'OtherPrivacyPage',
layout: 'detail',
data() {
return {};
},
head: {
title: '糖尿病网-隐私政策',
meta: [{ hid: 'description', name: 'description', content: '' }]
}
};
</script>

480
src/pages/search/index.vue

@ -0,0 +1,480 @@ @@ -0,0 +1,480 @@
<template>
<div
class="search-header-outer"
:class="{ isMobile: deviceType !== 'pc' }"
:style="{ top: -scrollTop + 'px', left: -scrollLeft + 'px' }"
>
<div class="box">
<div class="search-header">
<a href="/">
<img
v-if="deviceType === 'pc'"
src="~/assets/images/home_logo.png"
alt="糖尿病网"
/>
<img v-else src="~/assets/images/mobile_logo.png" alt="糖尿病网" />
</a>
<div class="search-user">
<el-input
slot="reference"
v-model.trim="searchKey"
maxlength="50"
class="search-input"
:placeholder="toSouSuo"
@keyup.enter.native="enterSearch()"
@click.native="baiduStat('糖尿病网-搜索', 'click', '点击输入框')"
>
</el-input>
<i class="el-icon-error" @click="shutDown()"></i>
<i class="el-icon-search" @click="getSearch()"></i>
</div>
</div>
</div>
<div class="search-center">
<div v-show="showHid" class="search-center-content">
<h3 v-show="showHot" class="search-hot">
大家都在看
<i class="hot">HOT</i>
</h3>
<ul class="search-uls">
<li
v-for="(item, index) in topSearch"
:key="index"
class="search-lis"
@click="
baiduStat(
'移动端-搜索',
'click',
'移动端搜索页-大家在看-' + item.id + '+' + item.title
);
toTopSearch(item.id);
"
>
<span
class="search-spa"
:style="index < 3 ? colorData : colorDatas"
>
{{ index + 1 > 9 ? index + 1 : '' + (index + 1) }}
</span>
<span class="search-spas">
{{ item.title }}
</span>
</li>
</ul>
</div>
<div v-show="showHidden" class="common-flex">
<h2 v-show="showHidden" class="searchtitle">
为您找到{{ numberOf }}条包含<span class="fontcolor">{{
searchKeys
}}</span
>的内容
</h2>
<template v-for="item in searchDataContent">
<VideoArticleListItem
:key="'sea' + item.id"
:detail="item"
:column-num="3"
:device-type="deviceType"
@addStat="
baiduStat(
'糖尿病网-搜索',
'click',
'搜索结果点击-' + item.id + '+' + item.title
)
"
/>
</template>
<p v-if="searchLoading" class="load-more">
加载更多<i class="el-icon-loading"></i>
</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
searchKey: '', //
showHidden: false, //
topSearch: [], //
title: 1, //
colorData: { background: '#EAAB00FF' }, //
colorDatas: { background: '#CCC5BDFF' }, //
toSouSuo: '', //
searchHasMore: true,
searchPages: 1,
searchLoading: false, // loading
searchDataContent: [], //
showHid: true, //
scrollTop: 0,
scrollLeft: 0,
showHot: false, //
numberOf: '', //
searchKeys: ''
};
},
computed: {
deviceType() {
return this.$store.state.device.deviceType;
}
},
mounted() {
const that = this;
that.baiduStat('糖尿病网-搜索', 'show', '搜索页面');
//
that.getHotSearchKey();
that.$nextTick(() => {
if (that.deviceType !== 'pc') {
window.addEventListener('scroll', function () {
// scrollTop
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
// windowHeight
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// scrollHeight
const scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight;
//
if (
scrollTop + windowHeight > scrollHeight - 50 &&
that.searchKey !== ''
) {
// that
that.searchData();
}
});
}
});
},
methods: {
//
async getHotSearchKey() {
const data = await this.$axios.$get('/article/hot');
this.topSearch = data.detail.articleList;
this.toSouSuo = '大家都在搜';
if (data.detail.articleList.length > 0) {
this.showHot = true;
}
},
//
async searchData() {
if (!this.searchLoading && this.searchHasMore) {
if (this.searchPages === 1) {
this.baiduStat('糖尿病网-搜索', 'click', '搜索控件-开始搜索');
}
this.searchLoading = true;
const data = await this.$axios.$get(
'/search-article?keyword=' +
this.searchKey +
'&page=' +
this.searchPages
);
this.searchDataContent = this.searchDataContent.concat(
data.detail.dataList
);
this.searchHasMore = data.detail.hasMore;
this.searchPages = this.searchPages + 1;
this.searchLoading = false;
this.numberOf = data.detail.total;
this.searchKeys = this.searchKey;
}
},
//
toTopSearch(id) {
const event = this.$router.resolve({
path: `/article/` + id
});
window.location.href = event.href;
},
//
shutDown() {
window.history.go(-1);
},
//
getSearch() {
if (this.searchKey === '') {
return false;
}
this.searchHasMore = true;
this.searchPages = 1;
this.searchDataContent = [];
this.searchData();
//
this.showHidden = true;
//
this.showHid = false;
},
//
enterSearch() {
if (this.searchKey === '') {
return false;
}
this.searchHasMore = true;
this.searchPages = 1;
this.searchDataContent = [];
this.searchData();
//
this.showHidden = true;
//
this.showHid = false;
}
}
};
</script>
<style lang="scss">
.search-header-outer {
width: 100%;
height: 100%;
border-bottom: 1px solid $bg-color;
min-width: $outer-width;
background: $body-color;
z-index: 9;
.box {
width: 100%;
height: 60px;
}
.search-center {
width: 100%;
height: 100%;
position: relative;
padding-top: 14px;
.common-flex {
width: 100%;
position: absolute;
top: 0;
left: 0;
.bottom {
display: none;
}
.searchtitle {
width: 100%;
height: 60px;
line-height: 60px;
font-size: 16px;
padding-left: 14px;
box-sizing: border-box;
}
}
.search-tocenter {
width: 100%;
height: 100%;
background: red;
position: absolute;
top: 0;
left: 0;
.searchdl {
width: 100%;
height: 30px;
background: #ccc;
}
}
.search-center-content {
margin: auto 15px;
.search-history {
font-size: 16px;
color: $font-color3;
.el-icon-delete {
margin-left: 8px;
color: $font-color3;
}
}
.search-ul {
margin-top: 18px;
width: 100%;
height: 100%;
float: left;
.search-li {
font-size: 16px;
line-height: 48px;
border-radius: 26px;
margin-bottom: 21px;
margin-right: 10px;
padding-left: 26px;
padding-right: 26px;
border: 1px solid #ededed;
list-style: none;
float: left;
color: $font-color1;
}
}
.search-hot {
width: 100%;
font-size: 16px;
float: left;
color: $font-color3;
.hot {
width: 36px;
font-size: 12px;
border-radius: 25px;
text-align: center;
display: inline-block;
color: $body-color;
background: $extra-red;
}
}
.search-uls {
width: 100%;
float: left;
margin-top: 30px;
.search-lis {
font-size: 16px;
list-style: none;
margin-bottom: 10px;
width: 100%;
height: auto;
white-space: normal;
.search-spa {
width: 27px;
height: 16px;
font-size: 14px;
border-radius: 4px;
line-height: 16px;
color: $body-color;
background: #ccc5bdff;
display: inline-block;
text-align: center;
margin-right: 5px;
float: left;
margin-top: 8px;
}
.search-spas {
color: $font-color1;
padding-left: 13px;
padding-right: 15px;
@include textEllipsisMore1Line(2);
}
}
}
}
}
.search-header {
margin: 0 auto;
display: flex;
width: $content-width;
height: 86px;
align-items: center;
& > a:first-child {
width: 155px;
height: 46px;
img {
width: 100%;
}
}
.search-user {
display: flex;
width: 335px;
position: relative;
.el-icon-search {
font-size: 20px;
position: absolute;
top: 10px;
right: 56px;
}
.el-icon-error {
font-size: 24px;
margin-top: 6px;
color: $font-color3;
}
}
}
&.isMobile {
top: 0 !important;
min-width: auto;
height: 60px;
.search-header {
position: fixed;
padding: 0 15px;
width: 100%;
height: 60px;
box-sizing: border-box;
justify-content: space-between;
z-index: 999;
background: $body-color;
& > a:first-child {
flex: none;
width: 36px;
height: 36px;
}
.search-user {
width: 80%;
min-width: 233px;
.search-input {
margin-right: 20px;
width: calc(100% - 40px) !important;
.el-input__inner {
border: none;
padding: 0 50px 0 12px;
width: 100% !important;
height: 40px;
background: $bg-color;
border-radius: 20px;
box-sizing: border-box;
color: $font-color1;
font-size: 16px;
}
.el-input__suffix {
right: 16px;
.el-input__icon {
width: 24px;
font-size: 24px;
color: $main-blue-color;
}
}
}
}
}
.common-flex {
display: block !important;
.load-more {
width: 100%;
}
.detail-list-item.isMobile {
padding: 15px;
}
}
}
}
</style>

82
src/pages/user/index.vue

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
<template>
<div class="user-page">
<UserInfoCommon
:bean-num="beanNum"
:total="total"
:has-more="hasMore"
:article-list="articleList"
:collect-loading="collectLoading"
/>
<BottomRightFixed />
</div>
</template>
<script>
export default {
name: 'UserIndexPage',
layout: 'detail',
middleware: 'authenticated',
async asyncData({ req, $axios }) {
const [dataA, dataB] = await Promise.all([
$axios.$get('/user/collect-list?page=1', $axios.genSSROptions(req)),
$axios.$get('/user/bean-num', $axios.genSSROptions(req))
]);
return {
...dataA.detail,
...dataB.detail
};
},
data() {
return {
collectPage: 2,
collectLoading: false
};
},
head: {
title: '糖尿病网-个人中心',
meta: [{ hid: 'description', name: 'description', content: '' }]
},
mounted() {
const that = this;
this.baiduStat('我的账户页', 'show', '我的账户页-访问');
this.$nextTick(() => {
window.addEventListener('scroll', function () {
// scrollTop
const scrollTop =
document.documentElement.scrollTop || document.body.scrollTop;
// windowHeight
const windowHeight =
document.documentElement.clientHeight || document.body.clientHeight;
// scrollHeight
const scrollHeight =
document.documentElement.scrollHeight || document.body.scrollHeight;
//
if (scrollTop + windowHeight > scrollHeight - 50) {
// that
that.getCollectList();
}
});
});
},
methods: {
async getCollectList() {
if (this.hasMore && !this.collectLoading) {
this.collectLoading = true;
const data = await this.$axios.$get(
'/user/collect-list?page=' + this.collectPage
);
this.hasMore = data.detail.hasMore;
this.articleList = this.articleList.concat(data.detail.articleList);
this.collectPage = this.collectPage + 1;
this.collectLoading = false;
}
}
}
};
</script>
<style lang="scss">
.user-page {
padding-top: 259px;
}
</style>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save