Browse Source

feat: 🚀 数据对接

pull/2/head
姜鑫 2 years ago
parent
commit
73338364d5
  1. 56
      package-lock.json
  2. 1
      package.json
  3. 1
      public/static/offline/JSON/getBackTime.json
  4. 16
      public/static/offline/JSON/getCustomerQr.json
  5. 2
      public/static/offline/JSON/getDevCoordinateByIP.json
  6. 43
      src/App.vue
  7. 5
      src/assets/images/a.svg
  8. 4
      src/assets/images/q.svg
  9. 53
      src/base/AutoBackNotification/AutoBackNotification.vue
  10. 8
      src/components/List/List.vue
  11. 56
      src/components/ListItem/ListItem.vue
  12. 68
      src/composables/useHandleScreen.ts
  13. 19
      src/composables/useInitBaseData.ts
  14. 16
      src/http/api.ts
  15. 6
      src/http/http.ts
  16. 5
      src/main.ts
  17. 7
      src/stores/index.ts
  18. 26
      src/stores/root/actions.ts
  19. 18
      src/stores/root/getters.ts
  20. 14
      src/stores/root/index.ts
  21. 13
      src/stores/root/state.ts
  22. 11
      src/stores/types.ts
  23. 6
      src/types/base.d.ts
  24. 6
      src/types/config.d.ts
  25. 44
      src/types/customer.d.ts
  26. 30
      src/types/device.d.ts
  27. 55
      src/types/native.d.ts

56
package-lock.json

@ -13,6 +13,7 @@
"@better-scroll/scroll-bar": "^2.5.1", "@better-scroll/scroll-bar": "^2.5.1",
"axios": "^1.6.7", "axios": "^1.6.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"pinia": "^2.2.0",
"swiper": "^11.1.1", "swiper": "^11.1.1",
"vue": "^3.4.15" "vue": "^3.4.15"
}, },
@ -1931,6 +1932,11 @@
"@vue/shared": "3.4.19" "@vue/shared": "3.4.19"
} }
}, },
"node_modules/@vue/devtools-api": {
"version": "6.6.3",
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.3.tgz",
"integrity": "sha512-0MiMsFma/HqA6g3KLKn+AGpL1kgKhFWszC9U29NfpWK5LE7bjeXxySWJrOJ77hBz+TBrBQ7o4QJqbPbqbs8rJw=="
},
"node_modules/@vue/eslint-config-prettier": { "node_modules/@vue/eslint-config-prettier": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz",
@ -6579,12 +6585,62 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/pinia": {
"version": "2.2.0",
"resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.2.0.tgz",
"integrity": "sha512-iPrIh26GMqfpUlMOGyxuDowGmYousTecbTHFwT0xZ1zJvh23oQ+Cj99ZoPQA1TnUPhU6AuRPv6/drkTCJ0VHQA==",
"dependencies": {
"@vue/devtools-api": "^6.6.3",
"vue-demi": "^0.14.8"
},
"funding": {
"url": "https://github.com/sponsors/posva"
},
"peerDependencies": {
"@vue/composition-api": "^1.4.0",
"typescript": ">=4.4.4",
"vue": "^2.6.14 || ^3.3.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
},
"typescript": {
"optional": true
}
}
},
"node_modules/pinia-logger": { "node_modules/pinia-logger": {
"version": "1.3.12", "version": "1.3.12",
"resolved": "https://registry.npmjs.org/pinia-logger/-/pinia-logger-1.3.12.tgz", "resolved": "https://registry.npmjs.org/pinia-logger/-/pinia-logger-1.3.12.tgz",
"integrity": "sha512-0qY41Bh6iYN7mncwOGCaiS02OGujf9NK1kLVzNTRLw/L88xbBOCA6r4bO1PD3KRTiPtXc5u3nnnqKR+kTIIdsA==", "integrity": "sha512-0qY41Bh6iYN7mncwOGCaiS02OGujf9NK1kLVzNTRLw/L88xbBOCA6r4bO1PD3KRTiPtXc5u3nnnqKR+kTIIdsA==",
"dev": true "dev": true
}, },
"node_modules/pinia/node_modules/vue-demi": {
"version": "0.14.10",
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/pinkie": { "node_modules/pinkie": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",

1
package.json

@ -24,6 +24,7 @@
"@better-scroll/scroll-bar": "^2.5.1", "@better-scroll/scroll-bar": "^2.5.1",
"axios": "^1.6.7", "axios": "^1.6.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"pinia": "^2.2.0",
"swiper": "^11.1.1", "swiper": "^11.1.1",
"vue": "^3.4.15" "vue": "^3.4.15"
}, },

1
public/static/offline/JSON/getBackTime.json

@ -0,0 +1 @@
{"code":200,"msg":"操作成功","data":[60,0]}

16
public/static/offline/JSON/getCustomerQr.json

@ -0,0 +1,16 @@
{
"code": 200,
"msg": "操作成功",
"data": [
{
"id": 1476,
"entryCode": "ogMDNkoxrQrFIXWP06T6y",
"title": "顾客心声二维码",
"content": {
"qrUrl": [
"/iotFile/project-bg9aktmmvxxfvi6vya0dua/20240715/ZR8QBUPdCyn2qZIszWaT2.jpg"
]
}
}
]
}

2
public/static/offline/JSON/getDevCoordinateByIP.json

@ -17,7 +17,7 @@
"mac": "30B49EC47D5E", "mac": "30B49EC47D5E",
"location": "51", "location": "51",
"angle": "0", "angle": "0",
"projectCode": "project-o99mwit8jby-qb_xrffk2a",
"projectCode": "project-200",
"regionCode": "", "regionCode": "",
"lensCoordinate": "", "lensCoordinate": "",
"orientationCoordinate": "", "orientationCoordinate": "",

43
src/App.vue

@ -3,12 +3,51 @@
<Header /> <Header />
<List :customer-list="list" /> <List :customer-list="list" />
</div> </div>
<!-- 倒计时返回首页提示 -->
<AutoBackNotification v-if="showCountDownDialog" :title="title" :delay="toWallpaperTime" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'
import { ref, defineAsyncComponent, computed, onMounted } from 'vue'
import { storeToRefs } from 'pinia'
import { useRootStore } from './stores/root'
import { getCustomerList } from './http/api'
import { HTTP_CODE } from './enums'
import { useHandleScreen } from '@/composables/useHandleScreen'
import Header from './components/Header/Header.vue' import Header from './components/Header/Header.vue'
import List from './components/List/List.vue' import List from './components/List/List.vue'
const list = ref<Customer[]>([])
const AutoBackNotification = defineAsyncComponent(() => import('@/base/AutoBackNotification/AutoBackNotification.vue'))
const store = useRootStore()
const { device } = storeToRefs(store)
const customerInfo = ref<Customer | null>(null)
const list = computed(() => {
return customerInfo.value?.proposalList
? customerInfo.value?.proposalList.map(item => ({
...item,
managerSignature: customerInfo.value?.managerSignature,
sealUrl: customerInfo.value?.sealUrl
}))
: []
})
getCustomerList(device.value.projectCode).then(res => {
if (res.code === HTTP_CODE.ERR_OK) {
customerInfo.value = res.data
}
})
const { checkHandleScreen, showCountDownDialog, title, toWallpaperTime } = useHandleScreen(handleScreen)
//
function handleScreen() {
//TODO
// router.push('/transfer')
}
onMounted(async () => {
window.addEventListener('touchend', checkHandleScreen)
})
</script> </script>

5
src/assets/images/a.svg

@ -0,0 +1,5 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.4375" y="0.4375" width="27.125" height="27.125" rx="6.5625" fill="#F5F5F5"/>
<rect x="0.4375" y="0.4375" width="27.125" height="27.125" rx="6.5625" stroke="#808080" stroke-width="0.875"/>
<path d="M7.46875 21.7385L12.5928 6.26147H15.4068L20.5308 21.7385H17.9478L16.6458 17.3495H11.2698L9.96775 21.7385H7.46875ZM11.8578 15.4175H16.0577L15.4488 13.3385C14.9448 11.6585 14.4828 9.93647 13.9998 8.19347H13.9158C13.4538 9.95747 12.9708 11.6585 12.4668 13.3385L11.8578 15.4175Z" fill="#808080"/>
</svg>

After

Width:  |  Height:  |  Size: 604 B

4
src/assets/images/q.svg

@ -0,0 +1,4 @@
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="28" height="28" rx="7" fill="#B60081"/>
<path d="M13.7478 18.1154C16.3098 18.1154 17.9898 15.8054 17.9898 12.0674C17.9898 8.4554 16.3098 6.2294 13.7478 6.2294C11.1858 6.2294 9.52684 8.4554 9.52684 12.0674C9.52684 15.8054 11.1858 18.1154 13.7478 18.1154ZM18.5988 23.8904C15.6168 23.8904 13.5588 22.2524 12.5928 20.0684C9.25384 19.5224 7.00684 16.5404 7.00684 12.0674C7.00684 7.0694 9.77884 4.1084 13.7478 4.1084C17.7378 4.1084 20.4888 7.0904 20.4888 12.0674C20.4888 16.4144 18.3888 19.3334 15.2178 20.0054C15.9108 21.2864 17.3178 21.8744 18.8718 21.8744C19.5438 21.8744 20.0898 21.7694 20.5308 21.6224L20.9928 23.4704C20.4678 23.7014 19.6068 23.8904 18.5988 23.8904Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 798 B

53
src/base/AutoBackNotification/AutoBackNotification.vue

@ -0,0 +1,53 @@
<template>
<div class="content fixed left-1/2 top-1/2 z-max -ml-[220px] -mt-[220px] size-[440px] rounded-full bg-black/50 font-bold">
<p class="absolute left-1/2 top-[125px] -translate-x-1/2 whitespace-nowrap text-36 text-white">
{{ title }}
</p>
<p class="absolute left-1/2 top-[180px] flex -translate-x-1/2 items-baseline text-[144px] text-white">
{{ delay }}<i class="text-64">s</i>
</p>
<svg xmlns="http://www.w3.org/2000/svg" class="svg absolute -left-[11px] -top-[9px] size-[440px] -rotate-90">
<circle class="borders relative z-10" fill="transparent" stroke-linecap="round" />
</svg>
</div>
</template>
<script setup lang="ts">
type Props = {
title: string
delay: number
}
withDefaults(defineProps<Props>(), {
title: '',
delay: 0
})
</script>
<style lang="scss" scoped>
.content {
border: 10px solid rgb(255 255 255 / 7%);
}
.borders {
stroke: rgb(197 51 154 / 100%);
transform: translate3d(1px, 1px, 0);
stroke-dasharray: 1535px, 1535px;
stroke-dashoffset: 0;
animation: rotate 5s linear;
cx: 220px;
cy: 220px;
r: 215px;
stroke-width: 10px;
}
@keyframes rotate {
0% {
stroke-dashoffset: 1535px;
}
100% {
stroke-dashoffset: 0;
}
}
</style>

8
src/components/List/List.vue

@ -1,18 +1,18 @@
<template> <template>
<div class="relative mx-10 flex items-stretch rounded-[10px] bg-[#E9E9E9]"> <div class="relative mx-10 flex items-stretch rounded-[10px] bg-[#E9E9E9]">
<div class="swiper-container my-[30px] w-[calc(100%-104px)] pl-6">
<div class="swiper-container h-[936px] w-[calc(1840px-104px)]">
<Swiper <Swiper
v-if="chunkList.length" v-if="chunkList.length"
rewind rewind
observer observer
observe-parents observe-parents
observe-slide-children observe-slide-children
:slides-off-set-before="24"
:modules="modules" :modules="modules"
style="height: 879px"
@swiper="swiperInit" @swiper="swiperInit"
> >
<SwiperSlide v-for="(item, index) of chunkList" :key="index"> <SwiperSlide v-for="(item, index) of chunkList" :key="index">
<div class="grid grid-flow-col grid-cols-[repeat(3,556px)] grid-rows-[repeat(2,430px)] gap-4">
<div class="grid h-[936px] grid-flow-col grid-cols-[repeat(3,556px)] grid-rows-[repeat(2,430px)] gap-4 pl-6 pt-7.5">
<ListItem v-for="customer of item" :key="customer.addTime" :customer="customer" /> <ListItem v-for="customer of item" :key="customer.addTime" :customer="customer" />
</div> </div>
</SwiperSlide> </SwiperSlide>
@ -38,7 +38,7 @@ import 'swiper/css'
const modules = [Autoplay] const modules = [Autoplay]
type Props = { type Props = {
customerList: Customer[]
customerList: Customer['proposalList']
} }
const props = defineProps<Props>() const props = defineProps<Props>()

56
src/components/ListItem/ListItem.vue

@ -1,19 +1,26 @@
<template> <template>
<div class="relative flex w-[720px] flex-wrap space-x-8 rounded-xl bg-white pl-6 pr-[6px] pt-6 shadow-[3px_3px_0_0_rgba(0,0,0,0.10)]">
<div class="relative flex flex-wrap space-x-6 rounded-xl bg-white pl-6 pr-[6px] pt-6 shadow-[3px_3px_0_0px_rgba(0,0,0,0.1)]">
<div class="flex-1 justify-between"> <div class="flex-1 justify-between">
<div class="mb-2 text-justify text-12 font-normal leading-4.5 text-[#808080]">{{ customer.addTime }}</div>
<ScrollView class="relative h-[356px]" scrollbar :list="customer.suggestionContent">
<div class="whitespace-normal pr-3.5 text-justify text-16 font-normal leading-normal text-[#C70082]">
Q:{{ customer.suggestionContent }}
</div>
<div class="mb-2 flex items-center gap-x-2.5 text-justify text-16 font-normal leading-4.5 text-black/40">
<img src="../../assets/images/q.svg" class="size-7" alt="" />
{{ formatTime(customer.createTime) }}
</div>
<ScrollView class="relative h-[356px]" :list="customer.content">
<div class="whitespace-normal pr-3.5 text-justify !text-24 font-normal leading-normal text-black/50">{{ customer.content }}</div>
</ScrollView> </ScrollView>
</div> </div>
<div class="flex-1"> <div class="flex-1">
<div class="mb-2 text-justify text-12 font-normal leading-4.5 text-[#808080]">{{ customer.updateTime }}</div>
<ScrollView class="relative h-[356px]" scrollbar observe-image :list="customer.replyContent">
<div class="whitespace-normal pr-3.5 text-justify text-16 font-normal leading-normal text-[#333333]">
A:
<div v-html="customer.replyContent"></div>
<div class="mb-2 flex items-center gap-x-2.5 text-justify text-16 font-normal leading-4.5 text-black/40">
<img src="../../assets/images/a.svg" class="size-7" alt="" />
{{ formatTime(customer.replyTime) }}
</div>
<ScrollView class="relative h-[356px]" observe-image :list="customer.disposeDes">
<div class="whitespace-normal pr-3.5 text-justify !text-24 font-normal leading-normal text-[#333333]">
<div v-html="customer.disposeDes"></div>
<div class="flex items-center pt-4 text-20">
总经理:{{ customer.managerSignature }}
<img class="size-6" :src="customer.sealUrl" alt="" />
</div>
</div> </div>
</ScrollView> </ScrollView>
</div> </div>
@ -24,25 +31,20 @@
import ScrollView from '@/base/ScrollView/ScrollView.vue' import ScrollView from '@/base/ScrollView/ScrollView.vue'
type Props = { type Props = {
customer: Customer
customer: ProposalList
} }
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
customer: () => ({}) as Customer
customer: () => ({}) as ProposalList
}) })
</script>
<style>
.bscroll-vertical-scrollbar {
width: 4px !important;
height: 120px !important;
background-color: rgb(0 0 0 / 3%) !important;
opacity: 1 !important;
function formatTime(time: string) {
const date = new Date(time)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
return `${year}-${month}-${day} ${hour}:${minute}`
} }
.bscroll-indicator {
width: 4px !important;
height: 60px !important;
background-color: rgb(0 0 0 / 6%) !important;
border: none !important;
}
</style>
</script>

68
src/composables/useHandleScreen.ts

@ -1,44 +1,23 @@
import { ref, computed, watch, onUnmounted } from 'vue'
import { ref, watch, onUnmounted } from 'vue'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import * as Sentry from '@sentry/vue'
import { useRootStore } from '@/stores/root' import { useRootStore } from '@/stores/root'
import { useRouter } from 'vue-router'
import { getStatistics } from '@/http/api/statistics'
export const useHandleScreen = (callback: () => void) => { export const useHandleScreen = (callback: () => void) => {
const MIN_TIME = 0 const MIN_TIME = 0
const MAX_TIME = 5 const MAX_TIME = 5
const CHECK_TIME = 1000 const CHECK_TIME = 1000
const DELAY_CHECK_TIME = 400 const DELAY_CHECK_TIME = 400
const title = '即将进入屏幕保护'
const router = useRouter()
const store = useRootStore() const store = useRootStore()
const { nativeMethods, mapStatus, backTime, device } = storeToRefs(store)
const { nativeMethods, backTime } = storeToRefs(store)
const toIndexTime = ref(backTime.value[0]) //回首页的时间
const toWallpaperTime = ref(backTime.value[1]) //回屏保的时间 const toWallpaperTime = ref(backTime.value[1]) //回屏保的时间
const isWallpaper = ref(false) //回首页是否已经跑完 const isWallpaper = ref(false) //回首页是否已经跑完
const showCountDownDialog = ref(false) const showCountDownDialog = ref(false)
const title = computed(() => (isWallpaper.value ? '即将进入屏幕保护' : '即将进入首页'))
const delayTime = computed(() => (isWallpaper.value ? toWallpaperTime.value : toIndexTime.value))
let toIndexInterval: any
let toWallpaperInterval: any let toWallpaperInterval: any
let delayCheckRoutePathTimer: any let delayCheckRoutePathTimer: any
function sleepToIndex() {
isWallpaper.value = false
return new Promise<void>(resolve => {
toIndexInterval = setInterval(() => {
toIndexTime.value--
if (toIndexTime.value <= MIN_TIME) {
clearInterval(toIndexInterval)
toIndexTime.value = backTime.value[0]
resolve()
}
}, CHECK_TIME)
})
}
function sleepToWallpaper() { function sleepToWallpaper() {
isWallpaper.value = true isWallpaper.value = true
return new Promise<void>(resolve => { return new Promise<void>(resolve => {
@ -55,7 +34,6 @@ export const useHandleScreen = (callback: () => void) => {
} }
function clearTimers() { function clearTimers() {
clearInterval(toIndexInterval)
clearInterval(toWallpaperInterval) clearInterval(toWallpaperInterval)
clearTimeout(delayCheckRoutePathTimer) clearTimeout(delayCheckRoutePathTimer)
} }
@ -66,30 +44,14 @@ export const useHandleScreen = (callback: () => void) => {
} }
function checkHandleScreen() { function checkHandleScreen() {
getStatistics({
deviceCode: device.value.machineCode,
projectCode: device.value.projectCode,
tag: 'device'
})
toIndexTime.value = backTime.value[0]
toWallpaperTime.value = backTime.value[1] toWallpaperTime.value = backTime.value[1]
clearTimers() clearTimers()
delayCheckRoutePathTimer = setTimeout(async () => { delayCheckRoutePathTimer = setTimeout(async () => {
try { try {
if (router.currentRoute.value.fullPath !== '/') {
await sleepToIndex()
callback()
}
//没有屏保 //没有屏保
if (backTime.value[1] < 0) { if (backTime.value[1] < 0) {
//在根路由页面上执行全局弹框操作(比如全局搜索, 全局详情等)重新触发返回首页弹框
if (router.currentRoute.value.fullPath === '/') {
await sleepToIndex()
callback()
}
return return
} }
await sleepToWallpaper() await sleepToWallpaper()
@ -97,36 +59,18 @@ export const useHandleScreen = (callback: () => void) => {
nativeMethods.value?.goScreenSave() nativeMethods.value?.goScreenSave()
} catch (error) { } catch (error) {
clearTimers() clearTimers()
Sentry.captureException('checkHandleScreen:' + error)
} }
}, DELAY_CHECK_TIME) }, DELAY_CHECK_TIME)
} }
//监听时间 大于等于0且小于等于5时显示弹框 //监听时间 大于等于0且小于等于5时显示弹框
const stopHandler = watch([toIndexTime, toWallpaperTime], ([indexTime, wallpaperTime]) => {
showCountDownDialog.value = (indexTime >= MIN_TIME && indexTime <= MAX_TIME) || (wallpaperTime >= MIN_TIME && wallpaperTime <= MAX_TIME)
const stopHandler = watch(toWallpaperTime, wallpaperTime => {
showCountDownDialog.value = wallpaperTime >= MIN_TIME && wallpaperTime <= MAX_TIME
}) })
const stopMapStatusHandler = watch(
mapStatus,
newVal => {
if (newVal) {
if (backTime.value[1] < 0) {
return
}
checkHandleScreen()
}
},
{
once: true
}
)
onUnmounted(() => { onUnmounted(() => {
clearTimers() clearTimers()
stopHandler() stopHandler()
stopMapStatusHandler()
toIndexInterval = null
toWallpaperInterval = null toWallpaperInterval = null
delayCheckRoutePathTimer = null delayCheckRoutePathTimer = null
}) })
@ -135,6 +79,6 @@ export const useHandleScreen = (callback: () => void) => {
checkHandleScreen, checkHandleScreen,
showCountDownDialog, showCountDownDialog,
title, title,
delayTime
toWallpaperTime
} }
} }

19
src/composables/useInitBaseData.ts

@ -0,0 +1,19 @@
import { useRootStore } from '@/stores/root'
import { getConfig, getDevice, getWeather, getBackTime } from '@/http/api'
export const useInitBaseData = async () => {
const store = useRootStore()
try {
const _config = await getConfig()
store.SET_CONFIG(_config.data.map(item => item.content)[0])
const [_deviceInfo, _weather, _backTime] = await Promise.all([getDevice(), getWeather(), getBackTime()])
store.SET_DEVICE(_deviceInfo.data)
store.SET_WEATHER(_weather.data)
store.SET_BACK_TIME([_backTime.data[0], _backTime.data[1] ? _backTime.data[1] : -1])
} catch (error) {
console.log('🚀 ~ useInitBaseData ~ error:', error)
alert('数据异常,软件启动失败')
}
}

16
src/http/api.ts

@ -1,16 +1,22 @@
import { request } from '@/http/http'
import { getPrefixUrl, request } from '@/http/http'
import { PREFIX } from '@/enums' import { PREFIX } from '@/enums'
//获取配置项 //获取配置项
export const getConfig = () => request<Config>({ url: `${PREFIX.STATIC_URL}/JSON/getConfig.json` })
export const getConfig = () => request<Base<Config>[]>({ url: `${PREFIX.STATIC_URL}/JSON/getConfig.json` })
//获取设备
//获取当前所处楼层
export const getDevice = () => request<Device>({ url: `${PREFIX.STATIC_URL}/JSON/getDevCoordinateByIP.json` }) export const getDevice = () => request<Device>({ url: `${PREFIX.STATIC_URL}/JSON/getDevCoordinateByIP.json` })
//获取天气 //获取天气
export const getWeather = () => request<Weather>({ url: `${PREFIX.STATIC_URL}/JSON/getWeather.json` }) export const getWeather = () => request<Weather>({ url: `${PREFIX.STATIC_URL}/JSON/getWeather.json` })
// 指定时间返回
export const getBackTime = () => request<[number, number]>({ url: `${PREFIX.STATIC_URL}/JSON/getBackTime.json` })
//获取二维码
export const getCustomerQr = () => request<Base<{ qrUrl: string[] }>[]>({ url: `${PREFIX.STATIC_URL}/JSON/getCustomerQr.json` })
//获取心声列表 //获取心声列表
export const getCustomerList = (url: string, params: { pageIndex: number; pageSize: number; mallCode: string }) => {
return request<{ allPage: number; allCount: number; list: Customer[] }>({ url: `${url}/Api/Suggestion/Page`, params })
export const getCustomerList = (projectCode: string) => {
return request<Customer>({ url: `${getPrefixUrl().interfaceUrl}/data/v1/web/webProposalList/${projectCode}` })
} }

6
src/http/http.ts

@ -2,6 +2,7 @@ import axios from 'axios'
import type { AxiosResponse, AxiosInstance, InternalAxiosRequestConfig } from 'axios' import type { AxiosResponse, AxiosInstance, InternalAxiosRequestConfig } from 'axios'
import { addPrefixByRecursive } from '@/utils/utils' import { addPrefixByRecursive } from '@/utils/utils'
import type { RequestConfig, RequestInterceptors, CreateRequestConfig } from './types' import type { RequestConfig, RequestInterceptors, CreateRequestConfig } from './types'
import { useRootStore } from '@/stores/root'
export default class Request { export default class Request {
// axios 实例 // axios 实例
@ -106,6 +107,11 @@ export type Response<T> = {
code: number code: number
} }
export function getPrefixUrl() {
const store = useRootStore()
return store.config
}
export const request = <T = any>(config: RequestConfig<Response<T>>) => _request.request(config) export const request = <T = any>(config: RequestConfig<Response<T>>) => _request.request(config)
export const cancelRequest = (url: string | string[]) => _request.cancelRequest(url) export const cancelRequest = (url: string | string[]) => _request.cancelRequest(url)
export const cancelAllRequest = () => _request.cancelAllRequest() export const cancelAllRequest = () => _request.cancelAllRequest()

5
src/main.ts

@ -1,9 +1,14 @@
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import { setupPinia } from './stores'
import { useInitBaseData } from './composables/useInitBaseData'
import '@/assets/scss/index.scss' import '@/assets/scss/index.scss'
async function bootstrap() { async function bootstrap() {
const app = createApp(App) const app = createApp(App)
setupPinia(app)
await useInitBaseData()
app.mount('#app') app.mount('#app')
} }

7
src/stores/index.ts

@ -0,0 +1,7 @@
import { createPinia } from 'pinia'
import type { App } from 'vue'
export function setupPinia(app: App) {
const pinia = createPinia()
app.use(pinia)
}

26
src/stores/root/actions.ts

@ -0,0 +1,26 @@
import type { State } from './state'
import type { CreateActions, Root } from '../types'
export interface Actions {
SET_BACK_TIME(times: [number, number]): void
SET_WEATHER(weather: Weather): void
SET_DEVICE(device: Device): void
SET_CONFIG(config: Config): void
}
export type GenActions = CreateActions<Root, State, Actions>
export const actions: GenActions = {
SET_BACK_TIME(times) {
this.backTime = times
},
SET_WEATHER(weather) {
this.weather = weather
},
SET_CONFIG(config) {
this.config = config
},
SET_DEVICE(device) {
this.device = device
}
}

18
src/stores/root/getters.ts

@ -0,0 +1,18 @@
import { DEVICE } from '@/enums'
import type { State } from './state'
import type { CreateGetters } from '../types'
export type Getters = {
nativeMethods(): NativeMethods //容器端暴露的方法
}
export type GenGetters = CreateGetters<State, Getters>
export const getters: GenGetters = {
nativeMethods() {
if (this.device.label === DEVICE.ANDROID) {
return window.android
}
return window?.chrome?.webview?.hostObjects?.csobj
}
}

14
src/stores/root/index.ts

@ -0,0 +1,14 @@
import { defineStore } from 'pinia'
import { state } from './state'
import { getters } from './getters'
import { actions } from './actions'
import type { Actions } from './actions'
import type { Root } from '../types'
import type { State } from './state'
import type { Getters } from './getters'
export const useRootStore = defineStore<Root, State, Getters, Actions>('root', {
state,
getters,
actions
})

13
src/stores/root/state.ts

@ -0,0 +1,13 @@
export interface State {
backTime: [number, number] // 返回弹框的出现时间 第一位为返回首页 第二位返回屏保
config: Config //配置文件
device: Device //当前设备信息
weather: Weather
}
export const state = (): State => ({
weather: {} as Weather,
backTime: [60, -1],
config: {} as Config,
device: {} as Device
})

11
src/stores/types.ts

@ -0,0 +1,11 @@
import type { UnwrapRef } from 'vue'
import type { PiniaCustomProperties, StateTree, _GettersTree, _StoreWithGetters, _StoreWithState } from 'pinia'
export type Root = 'root'
export type CreateActions<Id extends string, S extends StateTree, A> = A &
ThisType<A & UnwrapRef<S> & _StoreWithState<Id, S, _GettersTree<S>, A> & _StoreWithGetters<_GettersTree<S>> & PiniaCustomProperties>
export type CreateGetters<S extends StateTree, G extends _GettersTree<S>> = G &
ThisType<UnwrapRef<S> & _StoreWithGetters<G> & PiniaCustomProperties> &
_GettersTree<S>

6
src/types/base.d.ts

@ -0,0 +1,6 @@
declare interface Base<T> {
id: number
title: string
entryCode: string
content: T
}

6
src/types/config.d.ts

@ -1,5 +1,5 @@
declare interface Config { declare interface Config {
smallUrl: string
bigUrl: string
baseUrl: string
interfaceUrl: string
mobileNav: string
handWriteUrl: string
} }

44
src/types/customer.d.ts

@ -1,6 +1,40 @@
interface Customer {
suggestionContent: string
replyContent: string
addTime: string
updateTime: string
declare interface Customer {
/**
*
*/
managerSignature: string
proposalList: ProposalList[]
/**
*
*/
sealUrl: string
}
declare interface ProposalList {
/**
*
*/
content: string
createTime: string
/**
*
*/
customerName: string
/**
*
*/
customerPhone: string
disposeDes: string
pictureListAfter: PictureListAfter[]
pictureListBefore: PictureListBefore[]
proposalCode: string
replyTime: string
/**
*
*/
signature: string
/**
*
*/
sortName: string
[x: string]: any
} }

30
src/types/device.d.ts

@ -1,12 +1,20 @@
interface Device {
id: number
ip: string
devNum: string
xaxis: string
yaxis: string
angle: string
floorCode: string
floorName: string
order: number
mallCode: string
declare interface Device {
angle: string //角度
building: string //楼栋名称
buildingOrder: number //楼栋order
buildingCode: number //楼栋code
floor: string //楼层名称
floorCode: number //楼层code
floorOrder: number //楼层order
ip: string //设备ip
label: 'windows' | 'android' //设备类型
location: string //设备点位
mac: string //mac 地址
machineCode: string //设备code
machineName: string //设备名称
machineTypeName: string //设备类型
deviceCode: string
projectCode: string //项目code
screenAttribute: string //屏幕属性
deployType: string //部署方式
} }

55
src/types/native.d.ts

@ -0,0 +1,55 @@
export declare global {
type VideoStream = {
age: 23
genderMale: '女' | '男'
faceID: string
faceImage: string //人脸图片base64格式 需自行拼接前缀 data:image/jpg;base64,
}
type HardwareInfo = {
width: string
height: string
ip: string
code: string
mac: string
serverIP: string
}
type VoiceContent = {
code: number | string
msg: string | null
data: {
modelType: string
actionType: string
query: string
ttsMsg?: string
word?: string
reply?: string[]
}
}
interface NativeMethods {
startFace(): boolean //通知APP开启摄像头,开始采集
stopFace(): boolean //通知APP结束识别
pushFaceBase(): VideoStream //APP推送视频流给应用
pushFaceAttribute(): VideoStream //APP获取人脸属性推送给应用
takePhoto(): string //应用通知APP拍照 返回Base64
startVoice(): boolean //应用通知APP开始语音识别
stopVoice(): boolean //应用通知APP停止语音识别
voiceContent(): VoiceContent //语音返回数据
deviceInfo(): HardwareInfo //设备信息
goScreenSave(): void // 针对导视应用与app之间屏保跳转进行通讯
hasProgram(): boolean //是否有节目列表 可结合后台管理系统屏保倒计时时间确认前端导视是否弹起屏保弹框
}
interface Window {
leaveScreenSave(): void
android: NativeMethods
chrome: {
webview: {
hostObjects: {
csobj: NativeMethods
}
}
}
}
}
Loading…
Cancel
Save