Commit 30abb7cf authored by 朱国瑞's avatar 朱国瑞

初始化BFC demo

parents
Pipeline #264 canceled with stages
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
{
"recommendations": ["Vue.volar"]
}
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
const path = require('path')
module.exports = {
/**
* 在生产中服务时的基本公共路径。
* @default '/'
*/
base: './',
/**
* 与“根”相关的目录
* @default 'dist'
*/
outDir: 'dist',
port: 9528,
// 是否自动在浏览器打开
open: true,
// 是否开启 https
https: false,
// 服务端渲染
ssr: false,
// 引入第三方的配置
optimizeDeps: {
include: ["axios"]
},
resolve: {
alias: {
'@': path.resolve(__dirname, '../src')
},
},
proxy: {
'/byte-sdk-api/': {
target: 'https://bfai-service-uat.d-lab-services.danone.com', // 后端服务实际地址
changeOrigin: true,
rewrite: path => path.replace(/^\/ar/, '')
}
}
};
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Breast Feeding AI Demo</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "breast-feeding-ai-demo",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"amfe-flexible": "^2.2.1",
"autoprefixer": "^10.4.7",
"axios": "^0.21.1",
"vue": "^3.2.25",
"vue-i18n": "^9.1.10",
"vue-router": "^4.0.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^2.3.3",
"@vue/compiler-sfc": "^3.0.5",
"postcss-pxtorem": "^6.0.0",
"sass": "^1.34.0",
"sass-loader": "^11.1.1",
"vite": "^2.9.9",
"vite-plugin-style-import": "^0.10.1"
}
}
<template>
<router-view></router-view>
</template>
<script>
import { getCurrentInstance } from "vue";
export default {
name: "App",
setup() {
const { proxy } = getCurrentInstance();
function change(type) {
proxy.$i18n.locale = type;
}
return { change };
},
data() {
return {};
}
};
</script>
<style rel="stylesheet/scss" lang="scss">
// element-ui警告弹框对移动端优化
@media screen and (max-width: 450px) {
.el-message {
min-width: 80%;
}
}
</style>
export function calcAdapt() {
const maxScale = 2.16
const minScale = 1.78
const dot = 2
const { scale } = getSystemInfo()
return {
calcCoord: function ({ minTop, maxTop }, currentScale = scale) {
if (!currentScale) return {
top: maxTop
}
const s = +((currentScale - minScale) / (maxScale - minScale)).toFixed(dot)
const top = (maxTop - minTop) * s + minTop
return {
top
}
},
calc: function ({
minWidth, minHeight, maxWidth, maxHeight
}, currentScale = scale) {
if (!currentScale) return
// 1.78
// 2.16
let width, height;
// (minWidth / maxWidth)
// 比例因子
const s = +((currentScale - minScale) / (maxScale - minScale)).toFixed(dot)
width = (maxWidth - minWidth) * s + minWidth
height = (maxHeight - minHeight) * s + minHeight
return {
width,
height,
}
}
}
}
export const isBigScreen = () => {
let width = 0;
let height = 0;
if (document.body.clientWidth > 750) {
width = 750;
height = document.body.clientHeight;
} else {
width = document.body.clientWidth;
height = document.body.clientHeight;
}
if (height === 0) {
height = window.innerHeight;
}
let scale = height / width;
let k = +scale.toFixed(2);
if (k >= 2.16) {
k = 2.16;
} else if (k <= 1.78) {
k = 1.78;
}
if ((scale | 0) > 1) return true
return false
}
export const getSystemInfo = () => {
let width = 0;
let height = 0;
if (document.body.clientWidth > 750) {
width = 750;
} else {
width = document.body.clientWidth;
}
height = document.body.clientHeight;
if (height === 0) {
height = window.innerHeight;
}
let scale = height / width;
return {
windowWidth: width,
windowHeight: height,
screenWidth: width,
screenHeight: height,
scale: scale
}
}
export const authCamera = (lying) => {
const that = this;
return new Promise((resolve, reject) => {
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
if (navigator.mediaDevices.getUserMedia === undefined) {
navigator.mediaDevices.getUserMedia = function (constraints) {
// 首先获取现存的getUserMedia(如果存在)
let getUserMedia =
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.getUserMedia;
if (!getUserMedia) {
return Promise.reject(
new Error("getUserMedia is not implemented in this browser")
);
}
return new Promise(function (resolve, reject) {
getUserMedia.call(navigator, constraints, resolve, reject);
});
};
}
const constraints = {
audio: false,
video: {
transform: "scaleX(-1)"
}
};
navigator.mediaDevices
.getUserMedia(constraints)
.then(function (stream) {
resolve({ camera: 1, lying })
})
.catch(err => {
console.log("没有开启摄像头权限或浏览器版本不兼容");
console.log('获取用户授权信息失败')
});
})
}
\ No newline at end of file
<template>
<div class="btn-container">
<img class="btn-img" src="../assets/images/gesture/light-btn-bg.png" />
<div class="btn-text light-button-text">{{text}}</div>
<slot name="body"></slot>
</div>
</template>
<script >
import { defineProps, reactive } from "vue";
// defineProps({
// msg: String
// });
export default {
data() {
return {};
},
props: ["text"],
methods: {
confirm() {
this.$emit("update:confirm", { type: "confirm" });
},
cancel() {
this.$emit("update:confirm", { type: "cancel" });
}
}
};
</script>
<style lang="scss" scoped>
@media only screen and (min-width: 750px) {
.btn-container {
width: 436px;
height: 77.7px;
line-height: 77.7px;
}
.btn-text {
width: 100%;
text-align: center;
font-size: 44px;
letter-spacing: 0.2em;
font-weight: bold;
}
.btn-img {
width: 100%;
height: 100%;
}
.btn-container {
position: relative;
color: #ffffff;
img,
div,
button {
position: absolute;
left: 0;
top: 0;
}
}
.light-button-text {
letter-spacing: 0.2em;
}
}
@media screen and (max-width: 750px) {
.btn-container {
width: 5.8133rem;
height: 1.036rem;
line-height: 1.036rem;
}
.btn-text {
width: 100%;
text-align: center;
font-size: 0.5867rem;
letter-spacing: 0.2em;
font-weight: bold;
}
.btn-img {
width: 100%;
height: 100%;
}
.btn-container {
position: relative;
color: #ffffff;
img,
div,
button {
position: absolute;
left: 0;
top: 0;
}
}
.light-button-text {
letter-spacing: 0.2em;
}
}
</style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
\ No newline at end of file
export default {
strings: {
homeTitle: "Breastfeeding posture AI coach",
homeText1: "Put your device on a stable surface",
homeText2:
'Keep one meter away from the camera, and fit your body into the guide line. Session starts in 5 seconds after you click "Start Now" button.',
homeText3:
"Camera permission is needed for AI coach. Your clothes will not affect the AI coach.",
homeText4: "Cradle hold breastfeeding",
homeText5: "常见哺乳姿势,宝宝自主寻乳,\r\n适合哺乳配合好的宝宝和妈妈。",
homeButtonText: "Start Now",
homeTips:
"Your images and videos will only be used for AI coach recognition. They will not be stored.",
completeText: "Congratulations! You have completed the session!",
cameraText: "Camera not detected. Try again?",
cameraButtonText: "Try Again",
}
}
export default {
strings: {
homeTitle : "Asistente de IA de postura de lactancia",
homeText1 : "Coloque su dispositivo en una superficie estable",
homeText2 :
'Manténgase a un metro de distancia de la cámara y coloque su cuerpo en la línea. La sesión comienza en 5 segundos después de hacer clic en el botón "Comenzar ahora".',
homeText3 :
"Se necesita permiso para acceder a la cámara para el asistente de IA. Su ropa no afectará al asistente de IA.",
homeText4 : "Lactancia en posición de cuna (acunando)",
homeText5 : "常见哺乳姿势,宝宝自主寻乳,\r\n适合哺乳配合好的宝宝和妈妈。",
homeButtonText : "Empezar ahora",
homeTips :
"Sus imágenes y videos solo se utilizarán para el reconocimiento del asistente de IA. No se almacenarán.",
completeText : "¡Felicidades! ¡Has completado la sesión!",
cameraText : "Cámara no detectada. ¿Volver a Intentar otra vez?",
cameraButtonText : "Intentar otra vez",
}
}
export default {
strings: {
homeTitle : "Coach IA en posture d'allaitement",
homeText1 : "Placez votre appareil sur une surface stable",
homeText2 :
'Tenez-vous à un mètre de la caméra et placez votre corps dans la ligne de guidage. La session démarre dans 5 secondes après avoir cliqué sur le bouton "Démarrer maintenant".',
homeText3 :
"L'autorisation de la caméra est nécessaire pour l'entraîneur AI. Vos vêtements n'affecteront pas l'entraîneur AI.",
homeText4 : "Berceau d'allaitement",
homeText5 : "常见哺乳姿势,宝宝自主寻乳,\r\n适合哺乳配合好的宝宝和妈妈。",
homeButtonText : "Commencez maintenant",
homeTips :
"Vos images et vidéos ne seront utilisées que pour la reconnaissance des coachs IA. Elles ne seront pas stockés.",
completeText :
"Toutes nos félicitations! Vous avez terminé la session !",
cameraText : "Caméra non détectée. Réessayer?",
cameraButtonText : "Réessayer",
}
}
import { createI18n } from 'vue-i18n' //引入vue-i18n组件
import messages from './index'
const language = (
(navigator.language ? navigator.language : navigator.userLanguage) || "zh"
).toLowerCase();
const i18n = createI18n({
fallbackLocale: 'zh',
globalInjection:true,
legacy: false, // you must specify 'legacy: false' option
locale: language.split("-")[0] || "zh",
messages,
});
export default i18n
import en from './en'
import zh from './zh'
import es from './es'
import fr from './fr'
import pl from './pl'
export default {
en,
zh,
es,
fr,
pl
}
export default {
strings: {
homeTitle: "AI哺乳姿势矫正",
homeText1: "调整手机角度并摆放平稳,您可以使用手机支架",
homeText2: "请与屏幕保持1米距离\n直到把身体摆入框中。\n点击立即开始您有5秒钟的准备时间。",
homeText3: "本教学需要您授权使用摄像头\n请在安全环境安全设备使用本功能\n请您放心,穿着衣物不会影响AI识别。",
homeText4: "摇篮式哺乳",
homeText5: "常见哺乳姿势,宝宝自主寻乳,\r\n适合哺乳配合好的宝宝和妈妈。",
homeButtonText: "立即开始",
homeTips: "在使用过程中,我们不会存储您的图像。",
completeText: "恭喜你!完成了本次教学~",
cameraText: "未识别到您的摄像头,是否重新识别?",
cameraButtonText: "立即重识",
}
}
export default {
strings: {
homeTitle: "AI哺乳姿势矫正",
homeText1: "调整手机角度并摆放平稳,您可以使用手机支架",
homeText2: "请与屏幕保持1米距离\n直到把身体摆入框中。\n点击立即开始您有5秒钟的准备时间。",
homeText3: "本教学需要您授权使用摄像头\n请在安全环境安全设备使用本功能\n请您放心,穿着衣物不会影响AI识别。",
homeText4: "摇篮式哺乳",
homeText5: "常见哺乳姿势,宝宝自主寻乳,\n适合哺乳配合好的宝宝和妈妈。",
homeButtonText: "立即开始",
homeTips: "在使用过程中,我们不会存储您的图像。",
completeText: "恭喜你!完成了本次教学~",
cameraText: "未识别到您的摄像头,是否重新识别?",
cameraButtonText: "立即重识",
}
}
\ No newline at end of file
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import './styles/index.scss' // global css
import router from './router'
import 'amfe-flexible'
import i18n from './lang/i18n.js'
const app = createApp(App); // 挂载
import axios from 'axios';
// axios.defaults.baseURL = config.api_url; // 设置了主域名则接口就不需要+了
axios.defaults.withCredentials = false; // 跨域设置,false忽略跨域cookies(Access-Control-Allow-Headers:*)
app.use(router)
app.use(i18n)
app.mount('#app')
import {
createRouter,
createWebHistory,
createWebHashHistory
} from 'vue-router'
// 开启历史模式
// vue2中使用 mode: history 实现
// const routerHistory = createWebHistory("/sdk/");
const routerHashHistory = createWebHashHistory()
const router = createRouter({
history: routerHashHistory,
routes: [{
path: '/',
component: () => import('../views/index.vue')
},
{
path: '/complete',
component: () => import('../views/complete.vue')
}
]
})
export default router
\ No newline at end of file
body {
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
margin: 0;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
div:focus {
outline: none;
}
a:focus,
a:active {
outline: none;
}
a,
a:focus,
a:hover {
cursor: pointer;
color: inherit;
text-decoration: none;
}
.clearfix {
&:after {
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
}
}
//main-container全局样式
.app-main {
min-height: 100%
}
.app-container {
padding: 20px;
}
\ No newline at end of file
<template>
<div class="page">
<div class="main_content">
<img class="page-bg" src="../assets/images/gesture/massage-entry-fullbg.png" alt srcset />
<div class="massage-complete-modal" :class="{'noBaby':finalBabyStatus == 3 || finalBabyStatus == -1}">
<div class="nickname">
Hey,
<span>{{nikeName}}</span>
</div>
<div class="title">
<!-- <image src="{{images.title}}"></image> -->
<img class="icon" src="../assets/images/gesture/massage-complete-title-icon.png" />
{{$t('strings.completeText')}}
</div>
<div class="record" v-if="finalBabyStatus != 3 && finalBabyStatus != -1">
<div>您已记录: {{time}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { calcAdapt } from "../common/util";
let calcs = calcAdapt();
let scale;
const calcCoord = function () {
let width = 0;
let height = 0;
if (document.body.clientWidth > 750) {
width = 750;
height = document.body.clientHeight;
} else {
width = document.body.clientWidth;
height = document.body.clientHeight;
}
if (height === 0) {
height = window.innerHeight;
}
scale = height / width;
let k = +scale.toFixed(2);
if (k >= 2.16) {
k = 2.16;
} else if (k <= 1.78) {
k = 1.78;
}
return calcs.calcCoord.call(null, ...arguments, k);
};
export default {
data() {
return {
nikeName: "William",
finalBabyStatus: 0,
lying: 0,
time: 0,
total: 0,
modalCalc: {
top: ""
}
};
},
methods: {
},
created() {
let finalBabyStatus = this.$route.query.finalBabyStatus;
let lying = this.$route.query.lying;
let time = this.$route.query.time;
let total = this.$route.query.time;
this.finalBabyStatus = finalBabyStatus;
this.lying = lying;
this.time = time;
this.total = total;
},
mounted() {}
};
</script>
<style lang="scss" scoped>
.page {
background: #000;
display: flex;
justify-content: center;
height: 100vh;
}
@media only screen and (min-width: 750px) {
.page {
.main_content {
width: 750px;
height: 100%;
background: #ffffff;
position: relative;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
.page-bg {
position: absolute;
left: 0;
top: 0;
width: 750px;
z-index: 0;
}
}
.massage-complete-modal {
box-sizing: border-box;
width: 660px;
height: 620px;
padding: 80px 0 125px;
background: #fff;
display: flex;
flex-direction: column;
align-items: center;
border-radius: 14px 14px;
margin: 0 auto;
position: relative;
z-index: 1;
&.noBaby {
height: 420px;
}
.nickname {
font-size: 46px;
color: #fb7c76;
font-family: "Gotham-Book";
text-shadow: 2px 2px 4px #ffffff;
span {
font-size: 60px;
}
}
.title {
height: 53px;
margin-top: 41px;
color: #fb7c76;
font-size: 46px;
font-weight: bolder;
.icon {
width: 69px;
height: 45px;
}
}
.subTitle {
width: 100%;
text-align: center;
color: #fb7c76;
font-size: 26px;
margin-top: 20px;
font-weight: bolder;
&.underline {
text-decoration: underline;
}
}
.record {
margin-top: 100px;
margin-bottom: 100px;
width: 559px;
height: 96px;
line-height: 96px;
border-radius: 48px 48px;
font-size: 34.67px;
color: #999999;
background-color: #f5f5f5;
text-align: center;
text-shadow: 2px 2px 4px #ffffff;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}
}
}
}
@media screen and (max-width: 750px) {
.page {
.main_content {
}
}
}
</style>
\ No newline at end of file
This diff is collapsed.
import {
defineConfig
} from 'vite'
import vue from '@vitejs/plugin-vue'
const config = require('./config')
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
base: config.base,
resolve: config.resolve,
server: {
proxy: config.proxy,
port: config.port
},
})
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment