feat: add typescript support

This commit is contained in:
zyronon 2024-04-06 02:35:49 +08:00
parent 83d68723d9
commit 906a5728b0
58 changed files with 4261 additions and 3596 deletions

View File

@ -15,5 +15,5 @@ module.exports = {
rules: { rules: {
'vue/multi-word-component-names': 0 'vue/multi-word-component-names': 0
}, },
'ignorePatterns': ['vite.config.js', 'mobile-select.js'] 'ignorePatterns': ['vite.config.ts', 'mobile-select.js']
}; };

31
.gitignore vendored
View File

@ -1,25 +1,30 @@
.DS_Store # Logs
node_modules logs
/dist *.log
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
/cypress/videos/
/cypress/screenshots/
# Editor directories and files # Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea .idea
.vscode
*.suo *.suo
*.ntvs* *.ntvs*
*.njsproj *.njsproj
*.sln *.sln
*.sw? *.sw?
/php_backend
report.html *.tsbuildinfo

18
env.d.ts vendored Normal file
View File

@ -0,0 +1,18 @@
/// <reference types="vite/client" />
declare global {
interface Navigator {
control: any
webkitGetUserMedia: any
mozGetUserMedia: any
getUserMedia: any
}
}
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
export {}

View File

@ -14,15 +14,15 @@
<!-- integrity="sha512-KkkY/3auRhaXDFzFMpwtZ+BrS8EBQ+GfiBxdJ9jGMi6Gg74/sYbq/IZpY593pkNjTmbeRfBwjpZo+7gcpH45Ww=="--> <!-- integrity="sha512-KkkY/3auRhaXDFzFMpwtZ+BrS8EBQ+GfiBxdJ9jGMi6Gg74/sYbq/IZpY593pkNjTmbeRfBwjpZo+7gcpH45Ww=="-->
<!-- src="https://lib.baomitu.com/eruda/3.0.1/eruda.min.js"></script>--> <!-- src="https://lib.baomitu.com/eruda/3.0.1/eruda.min.js"></script>-->
<!-- <script>eruda.init();</script>--> <!-- <script>eruda.init();</script>-->
<!-- <script>--> <!-- <script>-->
<!-- var _hmt = _hmt || []--> <!-- var _hmt = _hmt || []-->
<!-- ;(function () {--> <!-- ;(function () {-->
<!-- var hm = document.createElement('script')--> <!-- var hm = document.createElement('script')-->
<!-- hm.src = 'https://hm.baidu.com/hm.js?6f910830f5a7d8b5f7e75d8d67458a7a'--> <!-- hm.src = 'https://hm.baidu.com/hm.js?6f910830f5a7d8b5f7e75d8d67458a7a'-->
<!-- var s = document.getElementsByTagName('script')[0]--> <!-- var s = document.getElementsByTagName('script')[0]-->
<!-- s.parentNode.insertBefore(hm, s)--> <!-- s.parentNode.insertBefore(hm, s)-->
<!-- })()--> <!-- })()-->
<!-- </script>--> <!-- </script>-->
<style> <style>
::-webkit-scrollbar { ::-webkit-scrollbar {
display: none; /* Chrome Safari */ display: none; /* Chrome Safari */
@ -58,6 +58,6 @@
</head> </head>
<body> <body>
<div id="app"></div> <div id="app"></div>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

View File

@ -1,12 +1,16 @@
{ {
"name": "my-vue-app", "name": "douyin-vue",
"version": "0.0.0", "version": "1.1.0",
"private": true,
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"start": "vite --host", "start": "vite --host",
"serve": "vite --host", "serve": "vite --host",
"build": "vite build --mode prod", "build": "vite build --mode prod",
"build-uni-app": "vite build --mode uni", "build-uni-app": "vite build --mode uni",
"build-only": "vite build",
"buildp-check": "run-p type-check \"build-only {@}\" --",
"type-check": "vue-tsc --build --force",
"report": "vite build", "report": "vite build",
"preview": "vite preview", "preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore", "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
@ -16,7 +20,7 @@
}, },
"dependencies": { "dependencies": {
"@jambonn/vue-lazyload": "1.0.9", "@jambonn/vue-lazyload": "1.0.9",
"axios": "1.6.0", "axios": "^1.6.8",
"axios-mock-adapter": "^1.22.0", "axios-mock-adapter": "^1.22.0",
"core-js": "3.21.1", "core-js": "3.21.1",
"dayjs": "1.11.0", "dayjs": "1.11.0",
@ -34,13 +38,16 @@
"@commitlint/cli": "^19.2.1", "@commitlint/cli": "^19.2.1",
"@commitlint/config-conventional": "^19.1.0", "@commitlint/config-conventional": "^19.1.0",
"@iconify/vue": "^4.1.1", "@iconify/vue": "^4.1.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rushstack/eslint-patch": "^1.3.3", "@rushstack/eslint-patch": "^1.3.3",
"@tsconfig/node20": "^20.1.2",
"@types/jquery": "3.5.29", "@types/jquery": "3.5.29",
"@types/lodash-es": "^4.17.9", "@types/node": "^20.11.28",
"@vitejs/plugin-vue": "4.0.0", "@vitejs/plugin-vue": "4.0.0",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"@vue/eslint-config-prettier": "^8.0.0", "@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0", "@vue/eslint-config-typescript": "^12.0.0",
"@vue/tsconfig": "^0.5.1",
"commitizen": "^4.3.0", "commitizen": "^4.3.0",
"cz-conventional-changelog": "^3.3.0", "cz-conventional-changelog": "^3.3.0",
"eslint": "^8.57.0", "eslint": "^8.57.0",
@ -50,9 +57,12 @@
"lint-staged": "^15.2.2", "lint-staged": "^15.2.2",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"rollup-plugin-visualizer": "^5.9.2", "rollup-plugin-visualizer": "^5.9.2",
"typescript": "~5.4.0",
"unplugin-vue-define-options": "^1.4.1", "unplugin-vue-define-options": "^1.4.1",
"vite": "4.5.3", "vite": "^5.1.6",
"vite-plugin-cdn-import": "0.3.5" "vite-plugin-cdn-import": "0.3.5",
"vite-plugin-commonjs": "^0.10.1",
"vue-tsc": "^2.0.6"
}, },
"lint-staged": { "lint-staged": {
"*.{js,ts,vue,jsx,tsx}": [ "*.{js,ts,vue,jsx,tsx}": [

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
<template> <template>
<router-view v-slot="{ Component }"> <router-view v-slot="{ Component }">
<transition :name="transitionName"> <transition :name="transitionName">
<keep-alive :exclude="excludeRoutes"> <keep-alive :exclude="store.excludeRoutes">
<component :is="Component" /> <component :is="Component" />
</keep-alive> </keep-alive>
</transition> </transition>
@ -17,93 +17,66 @@
</div> </div>
<Call /> <Call />
</template> </template>
<script> <script setup lang="ts">
/* /*
* try {navigator.control.gesture(false);} catch (e) {} //UC * try {navigator.control.gesture(false);} catch (e) {} //UC
try {navigator.control.longpressMenu(false);} catch (e) {} // try {navigator.control.longpressMenu(false);} catch (e) {} //
* */ * */
import { mapActions, mapState } from 'pinia'
import routes from './router/routes' import routes from './router/routes'
import Call from './components/Call' import Call from './components/Call.vue'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia.js'
import { onMounted, ref } from 'vue' import { onMounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import type { RouteRecordRaw } from 'vue-router'
export default { const store = useBaseStore()
name: 'App', const route = useRoute()
setup() { const isMobile = ref(/Mobi|Android|iPhone/i.test(navigator.userAgent))
const isMobile = ref(/Mobi|Android|iPhone/i.test(navigator.userAgent)) const transitionName = ref('go')
onMounted(() => {
console.log('asdf', isMobile.value)
})
return { isMobile }
},
components: {
Call
},
data() {
return {
transitionName: 'go'
}
},
computed: {
...mapState(useBaseStore, ['excludeRoutes'])
},
// watch $route 使
watch: {
$route(to, from) {
this.setMaskDialog({ state: false, mode: this.maskDialogMode })
//footer5 // watch $route 使
let noAnimation = [ watch(
'/', () => route.path,
'/home', (to, from) => {
'/slide', store.setMaskDialog({ state: false, mode: store.maskDialogMode })
'/me', //footer5
'/shop', let noAnimation = [
'/message', '/',
'/publish', '/home',
'/home/live', '/slide',
'slide', '/me',
'/test' '/shop',
] '/message',
if (noAnimation.indexOf(from.path) !== -1 && noAnimation.indexOf(to.path) !== -1) { '/publish',
return (this.transitionName = '') '/home/live',
} 'slide',
'/test'
const toDepth = routes.findIndex((v) => v.path === to.path) ]
const fromDepth = routes.findIndex((v) => v.path === from.path) if (noAnimation.indexOf(from) !== -1 && noAnimation.indexOf(to) !== -1) {
this.transitionName = toDepth > fromDepth ? 'go' : 'back' return (transitionName.value = '')
} }
}, const toDepth = routes.findIndex((v: RouteRecordRaw) => v.path === to)
methods: { const fromDepth = routes.findIndex((v: RouteRecordRaw) => v.path === from)
...mapActions(useBaseStore, ['init', 'setMaskDialog']), transitionName.value = toDepth > fromDepth ? 'go' : 'back'
setVh() {
let vh = window.innerHeight * 0.01
document.documentElement.style.setProperty('--vh', `${vh}px`)
}
},
mounted() {
this.init()
this.setVh()
// resize 1vh
window.addEventListener('resize', () => {
location.reload()
this.setVh()
})
try {
navigator.control.gesture(false)
} catch (e) {
//
}
try {
navigator.control.longpressMenu(false)
} catch (e) {
//
}
document.onselectstart = new Function('return false') //
} }
)
function setVh() {
let vh = window.innerHeight * 0.01
document.documentElement.style.setProperty('--vh', `${vh}px`)
} }
onMounted(() => {
store.init()
setVh()
// resize 1vh
window.addEventListener('resize', () => {
location.reload()
setVh()
})
//
document.onselectstart = new Function('return false') as any
})
</script> </script>
<style lang="less"> <style lang="less">

View File

@ -10,7 +10,7 @@
</div> </div>
</template> </template>
<script> <script lang="ts">
export default { export default {
name: 'AutoInput', name: 'AutoInput',
props: { props: {

View File

@ -1,54 +1,59 @@
<template> <template>
<div id="BaseHeader" :class="[isFixed ? 'fixed' : '']"> <div id="BaseHeader" :class="[props.isFixed ? 'fixed' : '']">
<div class="header"> <div class="header">
<dy-back :mode="backMode" :img="backImg" @click="back()" class="left" direction="left" /> <dy-back
:mode="props.backMode"
:img="props.backImg"
@click="back"
class="left"
direction="left"
/>
<slot name="center"><span></span></slot> <slot name="center"><span></span></slot>
<slot name="right"><span></span></slot> <slot name="right"><span></span></slot>
</div> </div>
<slot name="bottom"></slot> <slot name="bottom"></slot>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { import { useAttrs } from 'vue'
name: 'BaseHeader', import { useRouter } from 'vue-router'
components: {},
props: { defineOptions({
backMode: { name: 'BaseHeader'
type: String, })
default: 'gray'
}, const props = defineProps({
backImg: { backMode: {
type: String, type: String,
default: 'back' default: 'gray'
},
isClose: {
type: Boolean,
default: false
},
isFixed: {
type: Boolean,
default: true
}
}, },
data() { backImg: {
return {} type: String,
default: 'back'
}, },
created() {}, isClose: {
computed: {}, type: Boolean,
methods: { default: false
back() { },
if (this.$attrs.onBack) { isFixed: {
this.$attrs.onBack() type: Boolean,
} else { default: true
this.$back() }
} })
} const router = useRouter()
const attrs: any = useAttrs()
function back() {
if (attrs.onBack) {
attrs.onBack()
} else {
router.back()
} }
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import '../assets/less/index'; @import '@/assets/less/index';
#BaseHeader { #BaseHeader {
width: 100%; width: 100%;

View File

@ -13,7 +13,7 @@
<template v-slot:header> <template v-slot:header>
<div class="title"> <div class="title">
<dy-back mode="dark" img="close" direction="right" style="opacity: 0" /> <dy-back mode="dark" img="close" direction="right" style="opacity: 0" />
<div class="num">{{ formatNumber(comments.length) }}条评论</div> <div class="num">{{ _formatNumber(comments.length) }}条评论</div>
<div class="right"> <div class="right">
<Icon icon="prime:arrow-up-right-and-arrow-down-left-from-center" @click.stop="$no" /> <Icon icon="prime:arrow-up-right-and-arrow-down-left-from-center" @click.stop="$no" />
<Icon icon="ic:round-close" @click.stop="cancel" /> <Icon icon="ic:round-close" @click.stop="cancel" />
@ -36,7 +36,7 @@
<div class="time-wrapper"> <div class="time-wrapper">
<div class="left"> <div class="left">
<div class="time"> <div class="time">
{{ $time(item.create_time) {{ _time(item.create_time)
}}{{ item.ip_location && ` · ${item.ip_location}` }} }}{{ item.ip_location && ` · ${item.ip_location}` }}
</div> </div>
<div class="reply-text">回复</div> <div class="reply-text">回复</div>
@ -84,7 +84,7 @@
<div class="time-wrapper"> <div class="time-wrapper">
<div class="left"> <div class="left">
<div class="time"> <div class="time">
{{ $time(child.create_time) {{ _time(child.create_time)
}}{{ child.ip_location && ` · ${item.ip_location}` }} }}{{ child.ip_location && ` · ${item.ip_location}` }}
</div> </div>
<div class="reply-text">回复</div> <div class="reply-text">回复</div>
@ -104,7 +104,7 @@
v-show="!child.user_digged" v-show="!child.user_digged"
class="love-image" class="love-image"
/> />
<span>{{ formatNumber(child.digg_count) }}</span> <span>{{ _formatNumber(child.digg_count) }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -139,7 +139,7 @@
<img <img
:style="item.select ? 'opacity: .5;' : ''" :style="item.select ? 'opacity: .5;' : ''"
class="avatar" class="avatar"
:src="$imgPreview(item.avatar)" :src="_checkImgUrl(item.avatar)"
alt="" alt=""
/> />
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
@ -170,14 +170,22 @@
</from-bottom-dialog> </from-bottom-dialog>
</template> </template>
<script> <script lang="ts">
import AutoInput from './AutoInput' import AutoInput from './AutoInput.vue'
import ConfirmDialog from './dialog/ConfirmDialog' import ConfirmDialog from './dialog/ConfirmDialog.vue'
import { mapState } from 'pinia' import { mapState } from 'pinia'
import FromBottomDialog from './dialog/FromBottomDialog' import FromBottomDialog from './dialog/FromBottomDialog.vue'
import Loading from './Loading' import Loading from './Loading.vue'
import Search from './Search' import Search from './Search.vue'
import { $no, _checkImgUrl, _formatNumber, sampleSize } from '@/utils' import {
$no,
_checkImgUrl,
_formatNumber,
_showSelectDialog,
_sleep,
_time,
sampleSize
} from '@/utils'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { videoComments } from '@/api/videos' import { videoComments } from '@/api/videos'
@ -243,13 +251,14 @@ export default {
}, },
mounted() {}, mounted() {},
methods: { methods: {
_time,
_formatNumber, _formatNumber,
_checkImgUrl, _checkImgUrl,
$no, $no,
async handShowChildren(item) { async handShowChildren(item) {
this.loadChildrenItemCId = item.comment_id this.loadChildrenItemCId = item.comment_id
this.loadChildren = true this.loadChildren = true
await this.$sleep(500) await _sleep(500)
this.loadChildren = false this.loadChildren = false
if (item.showChildren) { if (item.showChildren) {
item.children = item.children.concat(sampleSize(this.comments, 10)) item.children = item.children.concat(sampleSize(this.comments, 10))
@ -273,7 +282,7 @@ export default {
this.isCall = false this.isCall = false
}, },
async getData() { async getData() {
let res = await videoComments({ id: this.videoId }) let res: any = await videoComments({ id: this.videoId })
if (res.success) { if (res.success) {
res.data.map((v) => { res.data.map((v) => {
v.showChildren = false v.showChildren = false
@ -304,19 +313,12 @@ export default {
row.user_digged = !row.user_digged row.user_digged = !row.user_digged
}, },
showOptions(row) { showOptions(row) {
this.$showSelectDialog(this.options, (e) => { _showSelectDialog(this.options, (e) => {
if (e.id === 1) { if (e.id === 1) {
this.selectRow = row this.selectRow = row
this.showPrivateChat = true this.showPrivateChat = true
} }
}) })
},
// showComment() {
// this.isCommenting = !this.isCommenting;
// console.log(666)
// }
call() {
console.log(this.commit)
} }
} }
} }

View File

@ -2,7 +2,7 @@
<div id="UserPanel" @scroll="scroll" ref="page"> <div id="UserPanel" @scroll="scroll" ref="page">
<div ref="float" class="float" :class="state.floatFixed ? 'fixed' : ''"> <div ref="float" class="float" :class="state.floatFixed ? 'fixed' : ''">
<div class="left"> <div class="left">
<Icon @click="$emit('back')" class="icon" icon="eva:arrow-ios-back-fill" /> <Icon @click="emit('back')" class="icon" icon="eva:arrow-ios-back-fill" />
<transition name="fade"> <transition name="fade">
<div class="float-user" v-if="state.floatFixed"> <div class="float-user" v-if="state.floatFixed">
<img <img
@ -33,7 +33,7 @@
</div> </div>
</transition> </transition>
<Icon class="icon" icon="ion:search" @click.stop="$no()" /> <Icon class="icon" icon="ion:search" @click.stop="$no()" />
<Icon class="icon" icon="ri:more-line" @click.stop="$emit('showFollowSetting')" /> <Icon class="icon" icon="ri:more-line" @click.stop="emit('showFollowSetting')" />
</div> </div>
</div> </div>
<div <div
@ -163,7 +163,7 @@
<span>关注</span> <span>关注</span>
</div> </div>
<div class="followed"> <div class="followed">
<div class="l-button" @click="$emit('showFollowSetting2')"> <div class="l-button" @click="emit('showFollowSetting2')">
<span>已关注</span> <span>已关注</span>
<Icon icon="bxs:down-arrow" class="arrow" /> <Icon icon="bxs:down-arrow" class="arrow" />
</div> </div>
@ -232,11 +232,11 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
import Utils, { $no, _checkImgUrl, _getUserDouyinId } from '@/utils' import Utils, { $no, _checkImgUrl, _getUserDouyinId } from '@/utils'
import { useNav } from '@/utils/hooks/useNav' import { useNav } from '@/utils/hooks/useNav'
import Posters from '@/components/Posters' import Posters from '@/components/Posters.vue'
import { DefaultUser } from '@/utils/const_var' import { DefaultUser } from '@/utils/const_var'
import Loading from '@/components/Loading.vue' import Loading from '@/components/Loading.vue'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
@ -244,7 +244,13 @@ import { userVideoList } from '@/api/user'
const $nav = useNav() const $nav = useNav()
const baseStore = useBaseStore() const baseStore = useBaseStore()
const emit = defineEmits(['update:currentItem', 'back']) const emit = defineEmits<{
'update:currentItem': [val: any]
back: []
showFollowSetting: []
showFollowSetting2: []
}>()
const props = defineProps({ const props = defineProps({
currentItem: { currentItem: {
type: Object, type: Object,
@ -272,9 +278,7 @@ const state = reactive({
previewImg: '', previewImg: '',
floatFixed: false, floatFixed: false,
showFollowSetting: false, showFollowSetting: false,
floatHeight: 52, floatHeight: 52,
loadings: { loadings: {
showRecommend: false showRecommend: false
}, },
@ -296,7 +300,7 @@ watch(
if (newVal && !props.currentItem.aweme_list.length) { if (newVal && !props.currentItem.aweme_list.length) {
// console.log('props.currentItem',props.currentItem) // console.log('props.currentItem',props.currentItem)
let id = _getUserDouyinId(props.currentItem) let id = _getUserDouyinId(props.currentItem)
let r = await userVideoList({ id }) let r: any = await userVideoList({ id })
if (r.success) { if (r.success) {
setTimeout(() => { setTimeout(() => {
r.data = r.data.map((a) => { r.data = r.data.map((a) => {
@ -326,6 +330,10 @@ function stop(e) {
function followButton() {} function followButton() {}
function cancelFollow() {}
defineExpose({ cancelFollow })
function scroll() { function scroll() {
// console.log('scroll', page.value.scrollTop) // console.log('scroll', page.value.scrollTop)
let scrollTop = page.value.scrollTop let scrollTop = page.value.scrollTop

View File

@ -18,7 +18,7 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script lang="ts">
/*TODO 单独使用时没有mark*/ /*TODO 单独使用时没有mark*/
export default { export default {
name: 'ConfirmDialog', name: 'ConfirmDialog',

View File

@ -1,4 +1,4 @@
<script setup> <script setup lang="ts">
import { onMounted, onUnmounted, reactive, ref, watch } from 'vue' import { onMounted, onUnmounted, reactive, ref, watch } from 'vue'
import GM from '../../utils' import GM from '../../utils'
import { import {
@ -73,20 +73,30 @@ onUnmounted(() => {
ob.disconnect() ob.disconnect()
}) })
function touchStart(e) { function touchStart(e: TouchEvent) {
slideTouchStart(e, wrapperEl.value, state) slideTouchStart(e, wrapperEl.value, state)
} }
function touchMove(e) { function touchMove(e: TouchEvent) {
slideTouchMove(e, wrapperEl.value, state, judgeValue, canNext, null, SlideType.HORIZONTAL) slideTouchMove(
e,
wrapperEl.value,
state,
judgeValue,
canNext,
null,
SlideType.HORIZONTAL,
null,
null
)
} }
function touchEnd(e) { function touchEnd(e: TouchEvent) {
slideTouchEnd(e, state, canNext, () => {}) slideTouchEnd(e, state, canNext, () => {})
slideReset(wrapperEl.value, state, SlideType.HORIZONTAL, emit) slideReset(wrapperEl.value, state, SlideType.HORIZONTAL, emit)
} }
function canNext(isNext) { function canNext(isNext: boolean) {
return !( return !(
(state.localIndex === 0 && !isNext) || (state.localIndex === 0 && !isNext) ||
(state.localIndex === state.wrapper.childrenLength - 1 && isNext) (state.localIndex === state.wrapper.childrenLength - 1 && isNext)
@ -95,7 +105,7 @@ function canNext(isNext) {
</script> </script>
<template> <template>
<div class="slide hhhh"> <div class="slide horizontal">
<div <div
class="slide-list" class="slide-list"
ref="wrapperEl" ref="wrapperEl"

View File

@ -4,8 +4,8 @@ export default {
filePreview: 'http://192.168.0.103/static/uploads/' filePreview: 'http://192.168.0.103/static/uploads/'
} }
const BASE_URL_MAP = { const BASE_URL_MAP = {
DEV: './', DEV: '',
PROD: './', PROD: '',
UNI: 'https://dy.ttentau.top' UNI: 'https://dy.ttentau.top'
} }

View File

@ -1,21 +1,17 @@
import * as Vue from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import mitt from 'mitt' import mitt from 'mitt'
import './assets/less/index.less' import './assets/less/index.less'
import { startMock } from './mock' import { startMock } from '@/mock'
import router from './router' import router from './router'
import mixin from './utils/mixin' import mixin from './utils/mixin'
import VueLazyload from '@jambonn/vue-lazyload' import VueLazyload from '@jambonn/vue-lazyload'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
const pinia = createPinia() const pinia = createPinia()
// const vConsole = new VConsole();
const emitter = mitt() const emitter = mitt()
const app = createApp(App)
const app = Vue.createApp(App)
app.config.globalProperties.emitter = emitter app.config.globalProperties.emitter = emitter
app.config.unwrapInjectedRef = true
app.provide('mitt', emitter) app.provide('mitt', emitter)
app.mixin(mixin) app.mixin(mixin)
const loadImage = new URL('./assets/img/icon/img-loading.png', import.meta.url).href const loadImage = new URL('./assets/img/icon/img-loading.png', import.meta.url).href
@ -24,8 +20,8 @@ app.use(VueLazyload, {
loading: loadImage, loading: loadImage,
attempt: 1 attempt: 1
}) })
app.use(router)
app.use(pinia) app.use(pinia)
app.use(router)
app.mount('#app') app.mount('#app')
//放到最后才可以使用pinia //放到最后才可以使用pinia

View File

@ -8,22 +8,22 @@ import MockAdapter from 'axios-mock-adapter'
const mock = new MockAdapter(axiosInstance, { delayResponse: 300 }) const mock = new MockAdapter(axiosInstance, { delayResponse: 300 })
function getPage2(params) { function getPage2(params: any): { limit: number; offset: number; pageNo: number } {
let offset = params.pageNo * params.pageSize const offset = params.pageNo * params.pageSize
let limit = params.pageNo * params.pageSize + params.pageSize const limit = params.pageNo * params.pageSize + params.pageSize
return { limit, offset, pageNo: params.pageNo } return { limit, offset, pageNo: params.pageNo }
} }
let allRecommendPosts = [] let allRecommendPosts = []
let userVideos = [] let userVideos = []
let allRecommendVideos = posts6.map((v) => { let allRecommendVideos = posts6.map((v: any) => {
v.type = 'recommend-video' v.type = 'recommend-video'
return v return v
}) })
// console.log('allRecommendVideos', allRecommendVideos) // console.log('allRecommendVideos', allRecommendVideos)
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line
let t = [ const t = [
{ {
type: 'imgs', type: 'imgs',
src: `https://imgapi.cn/bing.php`, src: `https://imgapi.cn/bing.php`,
@ -74,7 +74,7 @@ async function fetchData() {
} }
v = v.map((w) => { v = v.map((w) => {
w.type = 'recommend-video' w.type = 'recommend-video'
let item = userList.find((a) => String(a.uid) === String(w.author_user_id)) const item: any = userList.find((a) => String(a.uid) === String(w.author_user_id))
if (item) w.author = item if (item) w.author = item
return w return w
}) })
@ -86,7 +86,7 @@ async function fetchData() {
//TODO 有个bug一开始只返回了6条数据但第二次前端传过来的pageNo是2了就是会从第10条数据开始返回导致中间漏了4条 //TODO 有个bug一开始只返回了6条数据但第二次前端传过来的pageNo是2了就是会从第10条数据开始返回导致中间漏了4条
export async function startMock() { export async function startMock() {
mock.onGet(/video\/recommended/).reply(async (config) => { mock.onGet(/video\/recommended/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
console.log('allRecommendVideos', cloneDeep(allRecommendVideos.length), page) console.log('allRecommendVideos', cloneDeep(allRecommendVideos.length), page)
return [ return [
200, 200,
@ -102,7 +102,7 @@ export async function startMock() {
}) })
mock.onGet(/video\/comments/).reply(async (config) => { mock.onGet(/video\/comments/).reply(async (config) => {
let videoIds = [ const videoIds = [
'7260749400622894336', '7260749400622894336',
'7128686458763889956', '7128686458763889956',
'7293100687989148943', '7293100687989148943',
@ -124,8 +124,8 @@ export async function startMock() {
if (!videoIds.includes(String(id))) { if (!videoIds.includes(String(id))) {
id = videoIds[random(0, videoIds.length - 1)] id = videoIds[random(0, videoIds.length - 1)]
} }
let r2 = await fetch(`${FILE_URL}/comments/video_id_${id}.json`) const r2 = await fetch(`${FILE_URL}/comments/video_id_${id}.json`)
let v = await r2.json() const v = await r2.json()
if (v) { if (v) {
return [200, { data: v, code: 200 }] return [200, { data: v, code: 200 }]
} }
@ -133,7 +133,7 @@ export async function startMock() {
}) })
mock.onGet(/video\/private/).reply(async (config) => { mock.onGet(/video\/private/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
return [ return [
200, 200,
{ {
@ -148,7 +148,7 @@ export async function startMock() {
}) })
mock.onGet(/video\/like/).reply(async (config) => { mock.onGet(/video\/like/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
return [ return [
200, 200,
{ {
@ -163,18 +163,18 @@ export async function startMock() {
}) })
mock.onGet(/video\/my/).reply(async (config) => { mock.onGet(/video\/my/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
if (!userVideos.length) { if (!userVideos.length) {
// let r = await fetch(BASE_URL + '/data/user-71158770.json') // let r = await fetch(BASE_URL + '/data/user-71158770.json')
// let r = await fetch(BASE_URL + '/data/user-8357999.json') // let r = await fetch(BASE_URL + '/data/user-8357999.json')
let r = await fetch(BASE_URL + '/data/user_video_list/user-12345xiaolaohu.json') const r = await fetch(BASE_URL + '/data/user_video_list/user-12345xiaolaohu.json')
let list = await r.json() const list = await r.json()
const baseStore = useBaseStore() const baseStore = useBaseStore()
let userList = cloneDeep(baseStore.users) const userList = cloneDeep(baseStore.users)
userVideos = list.map((w) => { userVideos = list.map((w) => {
if (userList.length) { if (userList.length) {
let item = userList.find((a) => String(a.uid) === String(w.author_user_id)) const item = userList.find((a) => String(a.uid) === String(w.author_user_id))
if (item) w.author = item if (item) w.author = item
} }
return w return w
@ -196,7 +196,7 @@ export async function startMock() {
}) })
mock.onGet(/video\/history/).reply(async (config) => { mock.onGet(/video\/history/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
return [ return [
200, 200,
{ {
@ -231,9 +231,9 @@ export async function startMock() {
}) })
mock.onGet(/user\/video_list/).reply(async (config) => { mock.onGet(/user\/video_list/).reply(async (config) => {
let id = config.params.id const id = config.params.id
let r2 = await fetch(`${FILE_URL}/user_video_list/user-${id}.json`) const r2 = await fetch(`${FILE_URL}/user_video_list/user-${id}.json`)
let v = await r2.json() const v = await r2.json()
if (v) { if (v) {
return [200, { data: v, code: 200 }] return [200, { data: v, code: 200 }]
} }
@ -241,11 +241,11 @@ export async function startMock() {
}) })
mock.onGet(/user\/panel/).reply(async () => { mock.onGet(/user\/panel/).reply(async () => {
let r2 = await fetch(BASE_URL + '/data/users.json') const r2 = await fetch(BASE_URL + '/data/users.json')
let v = await r2.json() const v = await r2.json()
// let item = v.find(a => a.uid === '68310389333') // let item = v.find(a => a.uid === '68310389333')
// let item = v.find(a => a.uid === '59054327754') // let item = v.find(a => a.uid === '59054327754')
let item = v.find((a) => a.uid === '2739632844317827') const item = v.find((a) => a.uid === '2739632844317827')
if (item) { if (item) {
return [200, { data: item, code: 200 }] return [200, { data: item, code: 200 }]
} }
@ -253,13 +253,13 @@ export async function startMock() {
}) })
mock.onGet(/user\/friends/).reply(async () => { mock.onGet(/user\/friends/).reply(async () => {
let r2 = await fetch(BASE_URL + '/data/users.json') const r2 = await fetch(BASE_URL + '/data/users.json')
let v = await r2.json() const v = await r2.json()
return [200, { data: v, code: 200 }] return [200, { data: v, code: 200 }]
}) })
mock.onGet(/historyOther/).reply(async (config) => { mock.onGet(/historyOther/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
return [ return [
200, 200,
{ {
@ -275,10 +275,10 @@ export async function startMock() {
}) })
mock.onGet(/post\/recommended/).reply(async (config) => { mock.onGet(/post\/recommended/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
if (!allRecommendPosts.length) { if (!allRecommendPosts.length) {
let r = await fetch(BASE_URL + '/data/posts.json') const r = await fetch(BASE_URL + '/data/posts.json')
allRecommendPosts = await r.json() allRecommendPosts = await r.json()
} }
return [ return [
@ -296,10 +296,10 @@ export async function startMock() {
}) })
mock.onGet(/shop\/recommended/).reply(async (config) => { mock.onGet(/shop\/recommended/).reply(async (config) => {
let page = getPage2(config.params) const page = getPage2(config.params)
let r2 = await fetch(BASE_URL + '/data/goods.json') const r2 = await fetch(BASE_URL + '/data/goods.json')
let v = await r2.json() const v = await r2.json()
return [ return [
200, 200,
{ {

View File

@ -1,18 +1,18 @@
<template> <template>
<div id="Music"> <div id="Music">
<div class="header"> <div class="header">
<dy-back mode="light" @click="$back" /> <dy-back mode="light" @click="router.back()" />
<transition name="fade"> <transition name="fade">
<div class="center" v-if="isFixed"> <div class="center" v-if="data.isFixed">
<span class="f16">{{ music.name }}</span> <span class="f16">{{ data.music.name }}</span>
</div> </div>
</transition> </transition>
<div class="right"> <div class="right">
<!-- TODO 没有淡入淡出的特效--> <!-- TODO 没有淡入淡出的特效-->
<template v-if="isFixed"> <template v-if="data.isFixed">
<img <img
class="star" class="star"
v-if="isCollect" v-if="data.isCollect"
src="../../assets/img/icon/star-yellow.png" src="../../assets/img/icon/star-yellow.png"
@click.stop="toggleCollect()" @click.stop="toggleCollect()"
/> />
@ -23,38 +23,54 @@
@click.stop="toggleCollect()" @click.stop="toggleCollect()"
/> />
</template> </template>
<div class="logo" v-if="!isFixed" @click="$nav('/home/music-rank-list')">抖音音乐榜</div> <div class="logo" v-if="!data.isFixed" @click="nav('/home/music-rank-list')">
<img class="share" src="../../assets/img/icon/share-white.png" @click="isSharing = true" /> 抖音音乐榜
</div>
<img
class="share"
src="../../assets/img/icon/share-white.png"
@click="data.isSharing = true"
/>
</div> </div>
</div> </div>
<div class="content"> <div class="content">
<Scroll <Scroll
class="Scroll" class="Scroll"
:fixedHeight="180" :fixedHeight="180"
@fixed="(e) => (this.isFixed = e)" @fixed="(e) => (data.isFixed = e)"
@pulldown="loadData" @pulldown="loadData"
> >
<div class="desc"> <div class="desc">
<div class="cover-wrapper" @click="togglePlay()"> <div class="cover-wrapper" @click="togglePlay()">
<img class="cover" :src="$imgPreview(music.cover)" alt="" /> <img class="cover" :src="_checkImgUrl(data.music.cover)" alt="" />
<img v-if="!isPlay" src="../../assets/img/icon/play-white.png" alt="" class="play" /> <img
<img v-if="isPlay" src="../../assets/img/icon/pause-white.png" alt="" class="play" /> v-if="!data.isPlay"
src="../../assets/img/icon/play-white.png"
alt=""
class="play"
/>
<img
v-if="data.isPlay"
src="../../assets/img/icon/pause-white.png"
alt=""
class="play"
/>
</div> </div>
<div class="info"> <div class="info">
<div class="name">{{ music.name }}</div> <div class="name">{{ data.music.name }}</div>
<div> <div>
<div class="user">{{ music.author }}</div> <div class="user">{{ data.music.author }}</div>
<div class="peoples">{{ formatNumber(music.use_count) }} 人使用</div> <div class="peoples">{{ _formatNumber(data.music.use_count) }} 人使用</div>
</div> </div>
<div class="collection" @click.stop="toggleCollect()"> <div class="collection" @click.stop="toggleCollect()">
<img v-if="isCollect" src="../../assets/img/icon/star-yellow.png" /> <img v-if="data.isCollect" src="../../assets/img/icon/star-yellow.png" />
<img v-else src="../../assets/img/icon/star-white.png" /> <img v-else src="../../assets/img/icon/star-white.png" />
<span>{{ isCollect ? '已' : '' }}收藏</span> <span>{{ data.isCollect ? '已' : '' }}收藏</span>
</div> </div>
</div> </div>
</div> </div>
<Posters mode="music" :list="videos" /> <Posters mode="music" :list="data.videos" />
<Loading :is-full-screen="false" v-if="loading" /> <Loading :is-full-screen="false" v-if="data.loading" />
<NoMore v-else /> <NoMore v-else />
</Scroll> </Scroll>
</div> </div>
@ -70,165 +86,161 @@
</div> </div>
<Share <Share
v-model="isSharing" v-model="data.isSharing"
mode="music" mode="music"
ref="share" ref="share"
pageId="Music" pageId="Music"
@showDouyinCode="showDouyinCode = true" @showDouyinCode="data.showDouyinCode = true"
@showShare2WeChatZone="shareType = 2" @showShare2WeChatZone="data.shareType = 2"
@share2WeChat="shareType = 3" @share2WeChat="data.shareType = 3"
@share2QQZone="shareType = 4" @share2QQZone="data.shareType = 4"
@share2QQ="shareType = 5" @share2QQ="data.shareType = 5"
@share2Webo="shareType = 8" @share2Webo="data.shareType = 8"
@ShareToFriend="delayShowDialog((e) => (this.shareToFriend = true))" @ShareToFriend="delayShowDialog(() => (data.shareToFriend = true))"
/> />
<DouyinCode v-model="showDouyinCode" /> <DouyinCode v-model="data.showDouyinCode" />
<ConfirmDialog <ConfirmDialog
v-model:visible="showSharePassword" v-model:visible="data.showSharePassword"
title="你的口令已复制" title="你的口令已复制"
subtitle="0F.:/ a【风就应该自由要什么归宿】长按复制此条消息打开抖音搜索聆听音乐##kwu3VCixHl8##[抖音口令]" subtitle="0F.:/ a【风就应该自由要什么归宿】长按复制此条消息打开抖音搜索聆听音乐##kwu3VCixHl8##[抖音口令]"
:okText="okText" :okText="data.okText"
cancelText="不分享了" cancelText="不分享了"
@ok="shareType = -1" @ok="data.shareType = -1"
@cancel="shareType = -1" @cancel="data.shareType = -1"
> >
<template v-slot:header> <template v-slot:header>
<img style="width: 100%" src="../../assets/img/icon/share-password.webp" alt="" /> <img style="width: 100%" src="../../assets/img/icon/share-password.webp" alt="" />
</template> </template>
</ConfirmDialog> </ConfirmDialog>
<ShareToFriend pageId="Music" v-model="shareToFriend" /> <ShareToFriend pageId="Music" v-model="data.shareToFriend" />
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Posters from '../../components/Posters' import Posters from '../../components/Posters.vue'
import Scroll from '../../components/Scroll' import Scroll from '../../components/Scroll.vue'
import Loading from '../../components/Loading' import Loading from '../../components/Loading.vue'
import Share from '../../components/Share' import Share from '../../components/Share.vue'
import DouyinCode from '../../components/DouyinCode' import DouyinCode from '../../components/DouyinCode.vue'
import ConfirmDialog from '../../components/dialog/ConfirmDialog' import ConfirmDialog from '../../components/dialog/ConfirmDialog.vue'
import ShareToFriend from './components/ShareToFriend' import ShareToFriend from './components/ShareToFriend.vue'
import { myVideo } from '@/api/videos' import { myVideo } from '@/api/videos'
import { onDeactivated, onMounted, onUnmounted, reactive, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { useNav } from '@/utils/hooks/useNav'
import { $no, $notice, _checkImgUrl, _formatNumber } from '@/utils'
export default { const route = useRoute()
name: 'Music', const router = useRouter()
components: { const nav = useNav()
Scroll,
Posters,
Loading,
Share,
DouyinCode,
ConfirmDialog,
ShareToFriend
},
data() {
return {
loading: false,
isFixed: false,
isCollect: false,
isPlay: false,
isSharing: false,
okText: '',
showSharePassword: false, const data = reactive({
shareToFriend: false, loading: false,
shareType: -1, isFixed: false,
isCollect: false,
isPlay: false,
isSharing: false,
okText: '',
showDouyinCode: false, showSharePassword: false,
audio: new Audio(), shareToFriend: false,
total: 0, shareType: -1,
pageNo: 0,
pageSize: 15,
videos: [],
music: { showDouyinCode: false,
name: '发如雪', audio: new Audio(),
mp3: 'https://m3.8js.net:99/2014/211204142150965.mp3', total: 0,
cover: new URL('../../assets/img/music-cover/7.jpg', import.meta.url).href, pageNo: 0,
author: '周杰伦', pageSize: 15,
duration: 60, videos: [],
use_count: 37441000,
is_collect: false, music: {
is_play: false name: '发如雪',
} mp3: 'https://m3.8js.net:99/2014/211204142150965.mp3',
} cover: new URL('../../assets/img/music-cover/7.jpg', import.meta.url).href,
}, author: '周杰伦',
watch: { duration: 60,
shareType(newVal) { use_count: 37441000,
if (newVal === -1) return is_collect: false,
this.showSharePassword = true is_play: false
switch (newVal) {
case 2:
case 3:
return (this.okText = '去微信粘贴')
case 4:
case 5:
return (this.okText = '去QQ粘贴')
case 8:
return (this.okText = '去微博粘贴')
}
}
},
created() {
if (this.$route.query.name) {
this.music = this.$route.query
}
this.loadData(true)
},
computed: {},
methods: {
toggleCollect() {
this.isCollect = !this.isCollect
},
async loadData(init = false) {
if (this.loading) return
if (!init) {
if (this.total <= this.videos.length) {
return this.$notice('暂时没有更多了')
}
this.pageNo++
}
this.loading = true
let res = await myVideo({
pageNo: this.pageNo,
pageSize: this.pageSize
})
this.loading = false
if (res.code === this.SUCCESS) {
this.videos = this.videos.concat(res.data.list)
this.total = res.data.total
}
},
togglePlay() {
this.isPlay = !this.isPlay
if (this.isPlay) {
if (!this.audio.src) {
this.audio.src = this.music.mp3
}
this.audio.play()
this.audio.addEventListener('ended', () => (this.isPlay = false))
} else {
this.stopPlay()
}
},
delayShowDialog(cb) {
setTimeout(() => {
cb()
}, 100)
},
stopPlay() {
this.audio.pause()
this.audio.removeEventListener('ended', null)
}
},
unmounted() {
this.stopPlay()
},
deactivated() {
this.stopPlay()
} }
})
watch(
() => data.shareType,
(newVal) => {
if (newVal === -1) return
data.showSharePassword = true
switch (newVal) {
case 2:
case 3:
return (data.okText = '去微信粘贴')
case 4:
case 5:
return (data.okText = '去QQ粘贴')
case 8:
return (data.okText = '去微博粘贴')
}
}
)
onMounted(() => {
if (route.query.name) {
data.music = route.query as any
}
loadData(true)
})
onUnmounted(stopPlay)
onDeactivated(stopPlay)
function toggleCollect() {
data.isCollect = !data.isCollect
}
async function loadData(init = false) {
if (data.loading) return
if (!init) {
if (data.total <= data.videos.length) {
return $notice('暂时没有更多了')
}
data.pageNo++
}
data.loading = true
let res: any = await myVideo({
pageNo: data.pageNo,
pageSize: data.pageSize
})
data.loading = false
if (res.success) {
data.videos = data.videos.concat(res.data.list)
data.total = res.data.total
}
}
function togglePlay() {
data.isPlay = !data.isPlay
if (data.isPlay) {
if (!data.audio.src) {
data.audio.src = data.music.mp3
}
data.audio.play()
data.audio.addEventListener('ended', () => (data.isPlay = false))
} else {
stopPlay()
}
}
function delayShowDialog(cb) {
setTimeout(() => {
cb()
}, 100)
}
function stopPlay() {
data.audio.pause()
data.audio.removeEventListener('ended', null)
} }
</script> </script>

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="MusicRankList" @scroll="scroll"> <div class="MusicRankList" @scroll="scroll">
<dy-back mode="light" img="back" @click="$back()" class="fixed-back" direction="left" /> <dy-back mode="light" img="back" @click="router.back()" class="fixed-back" direction="left" />
<div class="fixed-header" :style="fixedStyle"> <div class="fixed-header" :style="fixedStyle">
<span class="f16">抖音音乐榜</span> <span class="f16">抖音音乐榜</span>
</div> </div>
@ -8,23 +8,23 @@
<div class="content"> <div class="content">
<div class="l-header"> <div class="l-header">
<img src="../../assets/img/icon/music-rank-list.webp" alt="" /> <img src="../../assets/img/icon/music-rank-list.webp" alt="" />
<div class="update-time">更新于{{ $dateFormat(updateTime, 'D') }}</div> <div class="update-time">更新于{{ _dateFormat(data.updateTime, 'D') }}</div>
</div> </div>
<Indicator <Indicator
name="musicRankList" name="musicRankList"
tabStyleWidth="33%" tabStyleWidth="33%"
:tabTexts="['热歌榜', '飙升樘', '原创榜']" :tabTexts="['热歌榜', '飙升樘', '原创榜']"
v-model:active-index="contentIndex" v-model:active-index="data.contentIndex"
> >
</Indicator> </Indicator>
<SlideHorizontal name="musicRankList" v-model:index="contentIndex"> <SlideHorizontal name="musicRankList" v-model:index="data.contentIndex">
<SlideItem> <SlideItem>
<div class="list"> <div class="list">
<div <div
class="item" class="item"
:key="index" :key="index"
v-for="(item, index) in hotList" v-for="(item, index) in data.hotList"
@click="togglePlay(item, hotList)" @click="togglePlay(item, data.hotList)"
> >
<div class="top"> <div class="top">
<div class="rank-wrapper"> <div class="rank-wrapper">
@ -51,7 +51,7 @@
<div class="right"> <div class="right">
<div class="music"> <div class="music">
<div class="cover-wrapper"> <div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" /> <img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img <img
v-if="!item.is_play" v-if="!item.is_play"
src="../../assets/img/icon/play-white.png" src="../../assets/img/icon/play-white.png"
@ -70,9 +70,9 @@
<div class="author">{{ item.author }}</div> <div class="author">{{ item.author }}</div>
<div class="desc-bottom"> <div class="desc-bottom">
<div class="duration"> <div class="duration">
{{ $duration(item.duration) }} {{ _duration(item.duration) }}
</div> </div>
<div class="use_count">{{ formatNumber(item.use_count) }}人使用</div> <div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div> </div>
</div> </div>
</div> </div>
@ -92,7 +92,7 @@
<img <img
src="../../assets/img/icon/menu2-white.png" src="../../assets/img/icon/menu2-white.png"
alt="" alt=""
@click.stop="$nav('/home/music')" @click.stop="nav('/home/music')"
/> />
</div> </div>
</div> </div>
@ -116,8 +116,8 @@
<div <div
class="item" class="item"
:key="index" :key="index"
v-for="(item, index) in hotList" v-for="(item, index) in data.hotList"
@click="togglePlay(item, hotList)" @click="togglePlay(item, data.hotList)"
> >
<div class="top"> <div class="top">
<div class="rank-wrapper"> <div class="rank-wrapper">
@ -144,7 +144,7 @@
<div class="right"> <div class="right">
<div class="music"> <div class="music">
<div class="cover-wrapper"> <div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" /> <img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img <img
v-if="!item.is_play" v-if="!item.is_play"
src="../../assets/img/icon/play-white.png" src="../../assets/img/icon/play-white.png"
@ -163,9 +163,9 @@
<div class="author">{{ item.author }}</div> <div class="author">{{ item.author }}</div>
<div class="desc-bottom"> <div class="desc-bottom">
<div class="duration"> <div class="duration">
{{ $duration(item.duration) }} {{ _duration(item.duration) }}
</div> </div>
<div class="use_count">{{ formatNumber(item.use_count) }}人使用</div> <div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div> </div>
</div> </div>
</div> </div>
@ -185,7 +185,7 @@
<img <img
src="../../assets/img/icon/menu2-white.png" src="../../assets/img/icon/menu2-white.png"
alt="" alt=""
@click.stop="$nav('/home/music')" @click.stop="nav('/home/music')"
/> />
</div> </div>
</div> </div>
@ -209,8 +209,8 @@
<div <div
class="item" class="item"
:key="index" :key="index"
v-for="(item, index) in hotList" v-for="(item, index) in data.hotList"
@click="togglePlay(item, hotList)" @click="togglePlay(item, data.hotList)"
> >
<div class="top"> <div class="top">
<div class="rank-wrapper"> <div class="rank-wrapper">
@ -237,7 +237,7 @@
<div class="right"> <div class="right">
<div class="music"> <div class="music">
<div class="cover-wrapper"> <div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" /> <img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img <img
v-if="!item.is_play" v-if="!item.is_play"
src="../../assets/img/icon/play-white.png" src="../../assets/img/icon/play-white.png"
@ -256,9 +256,9 @@
<div class="author">{{ item.author }}</div> <div class="author">{{ item.author }}</div>
<div class="desc-bottom"> <div class="desc-bottom">
<div class="duration"> <div class="duration">
{{ $duration(item.duration) }} {{ _duration(item.duration) }}
</div> </div>
<div class="use_count">{{ formatNumber(item.use_count) }}人使用</div> <div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div> </div>
</div> </div>
</div> </div>
@ -278,7 +278,7 @@
<img <img
src="../../assets/img/icon/menu2-white.png" src="../../assets/img/icon/menu2-white.png"
alt="" alt=""
@click.stop="$nav('/home/music')" @click.stop="nav('/home/music')"
/> />
</div> </div>
</div> </div>
@ -301,255 +301,258 @@
</div> </div>
</div> </div>
</template> </template>
<script>
export default { <script setup lang="ts">
name: 'MusicRankList', import { useRouter } from 'vue-router'
components: {}, import { computed, onDeactivated, onMounted, onUnmounted, reactive } from 'vue'
data() { import { $notice, _checkImgUrl, _dateFormat, _duration, _formatNumber } from '@/utils/index.jsx'
return { import { useNav } from '@/utils/hooks/useNav'
contentIndex: 0,
hotList: [ defineOptions({
{ name: 'MusicRankList'
name: '龙卷风', })
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/5605.mp3', const router = useRouter()
cover: new URL('../../assets/img/music-cover/1.jpg', import.meta.url).href, const nav = useNav()
author: '周杰伦', const data = reactive({
duration: 99, contentIndex: 0,
use_count: 37441000, hotList: [
is_collect: false, {
is_play: false name: '龙卷风',
}, mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/5605.mp3',
{ cover: new URL('../../assets/img/music-cover/1.jpg', import.meta.url).href,
name: '爱在西元前', author: '周杰伦',
mp3: 'https://m3.8js.net:99/1916/501204165042405.mp3', duration: 99,
cover: new URL('../../assets/img/music-cover/2.jpg', import.meta.url).href, use_count: 37441000,
author: '周杰伦', is_collect: false,
duration: 60, is_play: false
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '蜗牛',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/3684.mp3',
cover: new URL('../../assets/img/music-cover/3.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '半岛铁盒',
mp3: 'https://m3.8js.net:99/2016n/46/94745.mp3',
cover: new URL('../../assets/img/music-cover/4.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '轨迹',
mp3: 'https://m3.8js.net:99/1832/411204324135934.mp3',
cover: new URL('../../assets/img/music-cover/5.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '七里香',
mp3: 'https://m3.8js.net:99/2016n/14/53717.mp3',
cover: new URL('../../assets/img/music-cover/6.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '发如雪',
mp3: 'https://m3.8js.net:99/2014/211204142150965.mp3',
cover: new URL('../../assets/img/music-cover/7.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '霍元甲',
mp3: 'https://m3.8js.net:99/1921/261204212643140.mp3',
cover: new URL('../../assets/img/music-cover/8.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '千里之外(周杰伦/费玉清)',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/121.mp3',
cover: new URL('../../assets/img/music-cover/9.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '菊花台',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/2022.mp3',
cover: new URL('../../assets/img/music-cover/10.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '不能说的秘密',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/165.mp3',
cover: new URL('../../assets/img/music-cover/11.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '牛仔很忙',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/219.mp3',
cover: new URL('../../assets/img/music-cover/12.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '给我一首歌的时间',
mp3: 'https://m3.8js.net:99/1938/041204380445445.mp3',
cover: new URL('../../assets/img/music-cover/13.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '烟花易冷',
mp3: 'https://m3.8js.net:99/1828/051204280535192.mp3',
cover: new URL('../../assets/img/music-cover/14.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '惊叹号',
mp3: 'https://m3.8js.net:99/20111103/150.mp3',
cover: new URL('../../assets/img/music-cover/15.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '明明就',
mp3: 'https://m3.8js.net:99/2016n/27/96537.mp3',
cover: new URL('../../assets/img/music-cover/16.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '算什么男人',
mp3: 'https://m3.8js.net:99/20150107/429.mp3',
cover: new URL('../../assets/img/music-cover/17.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '告白气球',
mp3: 'https://m3.8js.net:99/20161016/481.mp3',
cover: new URL('../../assets/img/music-cover/18.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
}
],
updateTime: Date.now(),
audio: new Audio(),
scrollTop: -1
}
},
computed: {
fixedStyle() {
return {
opacity: this.scrollTop / 120 > 1 ? 1 : this.scrollTop / 120
}
}
},
created() {
this.hotList = this.hotList.concat(this.hotList).concat(this.hotList).concat(this.hotList)
this.hotList = this.hotList.slice(0, 50)
},
methods: {
scroll(e) {
this.scrollTop = e.target.scrollTop
}, },
toggleCollect(item) { {
item.is_collect = !item.is_collect name: '爱在西元前',
if (item.is_collect) { mp3: 'https://m3.8js.net:99/1916/501204165042405.mp3',
this.$notice('收藏成功') cover: new URL('../../assets/img/music-cover/2.jpg', import.meta.url).href,
} else { author: '周杰伦',
this.$notice('取消收藏') duration: 60,
} use_count: 37441000,
is_collect: false,
is_play: false
}, },
togglePlay(item, list) { {
list.map((v) => { name: '蜗牛',
if (v.name !== item.name) { mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/3684.mp3',
v.is_play = false cover: new URL('../../assets/img/music-cover/3.jpg', import.meta.url).href,
} author: '周杰伦',
}) duration: 60,
item.is_play = !item.is_play use_count: 37441000,
if (item.is_play) { is_collect: false,
this.audio.pause() is_play: false
this.audio.src = item.mp3
this.audio.currentTime = 0
this.audio.play()
this.audio.addEventListener('ended', () => (item.is_play = false))
} else {
this.stopPlay()
}
}, },
stopPlay() { {
this.audio.pause() name: '半岛铁盒',
this.audio.currentTime = 0 mp3: 'https://m3.8js.net:99/2016n/46/94745.mp3',
this.audio.removeEventListener('ended', null) cover: new URL('../../assets/img/music-cover/4.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '轨迹',
mp3: 'https://m3.8js.net:99/1832/411204324135934.mp3',
cover: new URL('../../assets/img/music-cover/5.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '七里香',
mp3: 'https://m3.8js.net:99/2016n/14/53717.mp3',
cover: new URL('../../assets/img/music-cover/6.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '发如雪',
mp3: 'https://m3.8js.net:99/2014/211204142150965.mp3',
cover: new URL('../../assets/img/music-cover/7.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '霍元甲',
mp3: 'https://m3.8js.net:99/1921/261204212643140.mp3',
cover: new URL('../../assets/img/music-cover/8.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '千里之外(周杰伦/费玉清)',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/121.mp3',
cover: new URL('../../assets/img/music-cover/9.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '菊花台',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/2022.mp3',
cover: new URL('../../assets/img/music-cover/10.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '不能说的秘密',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/165.mp3',
cover: new URL('../../assets/img/music-cover/11.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '牛仔很忙',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/219.mp3',
cover: new URL('../../assets/img/music-cover/12.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '给我一首歌的时间',
mp3: 'https://m3.8js.net:99/1938/041204380445445.mp3',
cover: new URL('../../assets/img/music-cover/13.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '烟花易冷',
mp3: 'https://m3.8js.net:99/1828/051204280535192.mp3',
cover: new URL('../../assets/img/music-cover/14.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '惊叹号',
mp3: 'https://m3.8js.net:99/20111103/150.mp3',
cover: new URL('../../assets/img/music-cover/15.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '明明就',
mp3: 'https://m3.8js.net:99/2016n/27/96537.mp3',
cover: new URL('../../assets/img/music-cover/16.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '算什么男人',
mp3: 'https://m3.8js.net:99/20150107/429.mp3',
cover: new URL('../../assets/img/music-cover/17.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '告白气球',
mp3: 'https://m3.8js.net:99/20161016/481.mp3',
cover: new URL('../../assets/img/music-cover/18.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
} }
}, ],
unmounted() { updateTime: Date.now(),
this.stopPlay() audio: new Audio(),
}, scrollTop: -1
deactivated() { })
this.stopPlay()
const fixedStyle = computed(() => {
return {
opacity: data.scrollTop / 120 > 1 ? 1 : data.scrollTop / 120
}
})
onMounted(() => {
data.hotList = data.hotList.concat(data.hotList).concat(data.hotList).concat(data.hotList)
data.hotList = data.hotList.slice(0, 50)
})
onUnmounted(stopPlay)
onDeactivated(stopPlay)
function scroll(e) {
data.scrollTop = e.target.scrollTop
}
function toggleCollect(item) {
item.is_collect = !item.is_collect
if (item.is_collect) {
$notice('收藏成功')
} else {
$notice('取消收藏')
} }
} }
</script>
function togglePlay(item, list) {
list.map((v) => {
if (v.name !== item.name) {
v.is_play = false
}
})
item.is_play = !item.is_play
if (item.is_play) {
data.audio.pause()
data.audio.src = item.mp3
data.audio.currentTime = 0
data.audio.play()
data.audio.addEventListener('ended', () => (item.is_play = false))
} else {
data.stopPlay()
}
}
function stopPlay() {
data.audio.pause()
data.audio.currentTime = 0
data.audio.removeEventListener('ended', null)
}
</script>
<style scoped lang="less"> <style scoped lang="less">
@import '../../assets/less/index'; @import '../../assets/less/index';

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="Publish"> <div class="Publish">
<video id="video" autoplay="autoplay" style="width: 100%; height: calc(100% - 60rem)"></video> <video id="video" autoplay style="width: 100%; height: calc(100% - 60rem)"></video>
<div class="footer"> <div class="footer">
<SlideHorizontal style="height: 60rem" v-model:index="activeIndex"> <SlideHorizontal style="height: 60rem" v-model:index="activeIndex">
<SlideItem style="width: 20vw"></SlideItem> <SlideItem style="width: 20vw"></SlideItem>
@ -20,7 +20,7 @@
</SlideHorizontal> </SlideHorizontal>
</div> </div>
<div class="float"> <div class="float">
<Icon class="close" icon="mingcute:close-line" @click="$back" /> <Icon class="close" icon="mingcute:close-line" @click="router.back()" />
<div class="choose-music"> <div class="choose-music">
<Icon icon="vaadin:music" /> <Icon icon="vaadin:music" />
<span>选择音乐</span> <span>选择音乐</span>
@ -48,9 +48,16 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapState } from 'pinia' import { onMounted, ref } from 'vue'
import { useBaseStore } from '@/store/pinia' import { useRouter } from 'vue-router'
defineOptions({
name: 'Publish'
})
const router = useRouter()
const videoEl = ref(null)
const activeIndex = ref(1)
//访 //访
function getUserMedia(constrains, success, error) { function getUserMedia(constrains, success, error) {
@ -63,51 +70,37 @@ function getUserMedia(constrains, success, error) {
} else if (navigator.mozGetUserMedia) { } else if (navigator.mozGetUserMedia) {
//Firefox //Firefox
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
navagator.mozGetUserMedia(constrains).then(success).catch(error) navigator.mozGetUserMedia(constrains).then(success).catch(error)
} else if (navigator.getUserMedia) { } else if (navigator.getUserMedia) {
//API //API
navigator.getUserMedia(constrains).then(success).catch(error) navigator.getUserMedia(constrains).then(success).catch(error)
} }
} }
export default { function getMedia() {
name: 'Publish', // let constraints = {video: {width: this.bodyWidth, height: this.bodyHeight - 60}, audio: false};
data() { // let constraints = {video:{width:480,height:320}, audio: false};
return { let constraints = { video: true, audio: false }
video: null, try {
activeIndex: 1 getUserMedia(
} constraints,
}, (MediaStream) => {
computed: { videoEl.value.srcObject = MediaStream
...mapState(useBaseStore, ['bodyHeight', 'bodyWidth']) videoEl.value.play()
}, },
mounted() { function (PermissionDeniedError) {
//video console.log(PermissionDeniedError)
this.video = document.getElementById('video')
this.getMedia()
},
methods: {
getMedia() {
// let constraints = {video: {width: this.bodyWidth, height: this.bodyHeight - 60}, audio: false};
// let constraints = {video:{width:480,height:320}, audio: false};
let constraints = { video: true, audio: false }
try {
getUserMedia(
constraints,
(MediaStream) => {
this.video.srcObject = MediaStream
this.video.play()
},
function (PermissionDeniedError) {
console.log(PermissionDeniedError)
}
)
} catch (e) {
console.log('e', e)
} }
} )
} catch (e) {
console.log('e', e)
} }
} }
onMounted(() => {
videoEl.value = document.getElementById('video')
getMedia()
})
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -160,7 +160,7 @@
:videoId="state.recommendList[state.itemIndex]?.id" :videoId="state.recommendList[state.itemIndex]?.id"
:canDownload="state.recommendList[state.itemIndex]?.canDownload" :canDownload="state.recommendList[state.itemIndex]?.canDownload"
@play-feedback="state.showPlayFeedback = true" @play-feedback="state.showPlayFeedback = true"
@shareToFriend="delayShowDialog((e) => (state.shareToFriend = true))" @shareToFriend="delayShowDialog(() => (state.shareToFriend = true))"
@showDouyinCode="state.showDouyinCode = true" @showDouyinCode="state.showDouyinCode = true"
@download="state.shareType = 9" @download="state.shareType = 9"
/> />
@ -185,7 +185,7 @@
<FollowSetting2 <FollowSetting2
v-model:currentItem="state.currentItem" v-model:currentItem="state.currentItem"
@cancelFollow="$refs.uploader.cancelFollow()" @cancelFollow="uploader.cancelFollow()"
v-model="state.showFollowSetting2" v-model="state.showFollowSetting2"
/> />
@ -199,13 +199,13 @@
</div> </div>
</template> </template>
<script setup lang="jsx"> <script setup lang="tsx">
import SlideHorizontal from '@/components/slide/SlideHorizontal.vue' import SlideHorizontal from '@/components/slide/SlideHorizontal.vue'
import SlideItem from '@/components/slide/SlideItem.vue' import SlideItem from '@/components/slide/SlideItem.vue'
import Comment from '../../components/Comment.vue' import Comment from '../../components/Comment.vue'
import Share from '../../components/Share.vue' import Share from '../../components/Share.vue'
import IndicatorHome from './components/IndicatorHome.vue' import IndicatorHome from './components/IndicatorHome.vue'
import { onActivated, onDeactivated, onMounted, onUnmounted, reactive } from 'vue' import { onActivated, onDeactivated, onMounted, onUnmounted, reactive, ref } from 'vue'
import bus, { EVENT_KEY } from '../../utils/bus' import bus, { EVENT_KEY } from '../../utils/bus'
import { useNav } from '@/utils/hooks/useNav' import { useNav } from '@/utils/hooks/useNav'
import PlayFeedback from '@/pages/home/components/PlayFeedback.vue' import PlayFeedback from '@/pages/home/components/PlayFeedback.vue'
@ -229,11 +229,13 @@ import { useBaseStore } from '@/store/pinia'
const nav = useNav() const nav = useNav()
const baseStore = useBaseStore() const baseStore = useBaseStore()
const uploader = ref()
const state = reactive({ const state = reactive({
active: true, active: true,
baseIndex: 1, baseIndex: 1,
navIndex: 4, navIndex: 4,
itemIndex: 0,
test: '', test: '',
recommendList: [], recommendList: [],
isSharing: false, isSharing: false,
@ -253,13 +255,14 @@ const state = reactive({
commentVisible: false, commentVisible: false,
fullScreen: false, fullScreen: false,
currentItem: { currentItem: {
aweme_id: '',
author: DefaultUser, author: DefaultUser,
isRequest: false, isRequest: false,
aweme_list: [] aweme_list: []
} }
}) })
function delayShowDialog(cb) { function delayShowDialog(cb: Function) {
setTimeout(cb, 400) setTimeout(cb, 400)
} }

View File

@ -1,14 +1,18 @@
<template> <template>
<div id="MyCard"> <div id="MyCard">
<div class="header"> <div class="header">
<dy-back mode="light" @click="$back" /> <dy-back mode="light" @click="router.back" />
<!-- todo 差一--> <!-- todo 差一-->
<img class="share" src="../../assets/img/icon/share-white.png" @click="isSharing = true" /> <img
class="share"
src="../../assets/img/icon/share-white.png"
@click="data.isSharing = true"
/>
</div> </div>
<div class="content"> <div class="content">
<div class="qrcode"> <div class="qrcode">
<img class="qrcode-bg" src="../../assets/img/icon/me/code-bg.png" alt="" /> <img class="qrcode-bg" src="../../assets/img/icon/me/code-bg.png" alt="" />
<img class="avatar" :src="_checkImgUrl(userinfo.cover_url[0].url_list[0])" alt="" /> <img class="avatar" :src="_checkImgUrl(store.userinfo.cover_url[0].url_list[0])" alt="" />
</div> </div>
<span class="name">ZZZZZZZZZZ</span> <span class="name">ZZZZZZZZZZ</span>
@ -30,65 +34,52 @@
</div> </div>
</div> </div>
<Share v-model="isSharing" mode="qrcode" ref="share" page-id="MyCard" /> <Share v-model:value="data.isSharing" mode="qrcode" ref="share" page-id="MyCard" />
</div> </div>
</template> </template>
<script>
import Share from '../../components/Share' <script setup lang="ts">
import { mapState } from 'pinia' import Share from '../../components/Share.vue'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl } from '@/utils' import { $no, _checkImgUrl } from '@/utils'
import { reactive, watch } from 'vue'
import { useRouter } from 'vue-router'
export default { defineOptions({
name: 'MyCard', name: 'MyCard'
components: { })
Share
},
data() { const router = useRouter()
return { const store = useBaseStore()
isSharing: false, const data = reactive({
okText: '', isSharing: false,
okText: '',
showSharePassword: false,
shareToFriend: false,
shareType: -1,
showDouyinCode: false
})
showSharePassword: false, watch(
shareToFriend: false, () => data.shareType,
shareType: -1, (newVal) => {
if (newVal === -1) return
showDouyinCode: false data.showSharePassword = true
} switch (newVal) {
}, case 2:
watch: { case 3:
shareType(newVal) { return (data.okText = '去微信粘贴')
if (newVal === -1) return case 4:
this.showSharePassword = true case 5:
switch (newVal) { return (data.okText = '去QQ粘贴')
case 2: case 8:
case 3: return (data.okText = '去微博粘贴')
return (this.okText = '去微信粘贴')
case 4:
case 5:
return (this.okText = '去QQ粘贴')
case 8:
return (this.okText = '去微博粘贴')
}
}
},
created() {},
computed: {
...mapState(useBaseStore, ['userinfo'])
},
methods: {
_checkImgUrl,
delayShowDialog(cb) {
setTimeout(() => {
cb()
}, 100)
} }
} }
} )
</script> </script>
<style scoped lang="less"> <style lang="less" scoped>
@import '../../assets/less/index'; @import '../../assets/less/index';
#MyCard { #MyCard {

View File

@ -9,13 +9,13 @@
<div class="list"> <div class="list">
<div <div
class="item" class="item"
v-for="(item, index) in list" v-for="(item, index) in data.list"
:key="index" :key="index"
@click="togglePlay(item, list)" @click="togglePlay(item, data.list)"
> >
<div class="music"> <div class="music">
<div class="cover-wrapper"> <div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" /> <img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img <img
v-if="!item.is_play" v-if="!item.is_play"
src="@/assets/img/icon/play-white.png" src="@/assets/img/icon/play-white.png"
@ -33,7 +33,7 @@
<span class="name">{{ item.name }}</span> <span class="name">{{ item.name }}</span>
<div class="author">{{ item.author }}</div> <div class="author">{{ item.author }}</div>
<div class="desc-bottom"> <div class="desc-bottom">
<div class="duration">{{ $duration(item.duration) }}</div> <div class="duration">{{ _duration(item.duration) }}</div>
</div> </div>
</div> </div>
</div> </div>
@ -41,126 +41,124 @@
<img <img
src="@/assets/img/icon/menu2-white.png" src="@/assets/img/icon/menu2-white.png"
alt="" alt=""
@click.stop="$nav('/home/music', item)" @click.stop="nav('/home/music', item)"
/> />
</div> </div>
</div> </div>
</div> </div>
<Loading v-if="loading" :is-full-screen="false" /> <Loading v-if="data.loading" :is-full-screen="false" />
<no-more v-else class="mb7r" /> <no-more v-else class="mb7r" />
</div> </div>
<div class="float-play-music" v-if="currentItem"> <div class="float-play-music" v-if="data.currentItem">
<div class="process" :style="{ width: process + 'px' }"></div> <div class="process" :style="{ width: data.process + 'px' }"></div>
<div class="music-wrapper"> <div class="music-wrapper">
<div class="music"> <div class="music">
<div class="cover-wrapper" @click="togglePlay(currentItem, list)"> <div class="cover-wrapper" @click="togglePlay(data.currentItem, data.list)">
<img v-lazy="$imgPreview(currentItem.cover)" alt="" class="cover" /> <img v-lazy="_checkImgUrl(data.currentItem.cover)" alt="" class="cover" />
<img <img
v-if="!currentItem.is_play" v-if="!data.currentItem.is_play"
src="@/assets/img/icon/play-white.png" src="@/assets/img/icon/play-white.png"
alt="" alt=""
class="play" class="play"
/> />
<img <img
v-if="currentItem.is_play" v-if="data.currentItem.is_play"
src="@/assets/img/icon/pause-white.png" src="@/assets/img/icon/pause-white.png"
alt="" alt=""
class="play" class="play"
/> />
</div> </div>
<div class="desc"> <div class="desc">
<span class="name">{{ currentItem.name }}</span> <span class="name">{{ data.currentItem.name }}</span>
<div class="desc-bottom"> <div class="desc-bottom">
<div class="duration">{{ $duration(currentItem.duration) }}</div> <div class="duration">{{ _duration(data.currentItem.duration) }}</div>
</div> </div>
</div> </div>
</div> </div>
<div class="option"> <div class="option">
<dy-button type="primary" size="small" @click="$no">使用</dy-button> <dy-button type="primary" size="small" @click="_no">使用</dy-button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapState } from 'pinia'
import { userCollect } from '@/api/user' import { userCollect } from '@/api/user'
import { useBaseStore } from '@/store/pinia'
export default { import { onMounted, onUnmounted, reactive } from 'vue'
name: 'MusicCollect', import { useNav } from '@/utils/hooks/useNav.js'
components: {}, import { useBaseStore } from '@/store/pinia'
props: {}, import { _checkImgUrl, _duration, _no } from '@/utils'
data() {
return { defineOptions({
loading: false, name: 'MusicCollect'
list: [], })
audio: new Audio(),
currentItem: null, const store = useBaseStore()
step: null, const nav = useNav()
process: 0 const data = reactive({
} loading: false,
}, list: [],
computed: { audio: new Audio(),
...mapState(useBaseStore, ['bodyWidth']) currentItem: null,
}, step: null,
created() { process: 0
this.getData() })
},
mounted() { onMounted(() => {
this.audio.addEventListener('loadedmetadata', () => { getData()
this.currentItem.duration = this.audio.duration data.audio.addEventListener('loadedmetadata', () => {
this.step = this.bodyWidth / Math.floor(this.audio.duration) data.currentItem.duration = data.audio.duration
}) data.step = store.bodyWidth / Math.floor(data.audio.duration)
this.audio.addEventListener('timeupdate', (e) => { })
this.process = Math.ceil(e.target.currentTime) * this.step data.audio.addEventListener('timeupdate', (e: any) => {
}) data.process = Math.ceil(e.target.currentTime) * data.step
}, })
methods: { })
async getData() {
this.loading = true onUnmounted(stopPlay)
let res = await userCollect()
this.loading = false async function getData() {
if (res.code === this.SUCCESS) { data.loading = true
this.list = res.data.music.list let res: any = await userCollect()
} data.loading = false
}, if (res.success) {
togglePlay(item, list) { data.list = res.data.music.list
list.map((v) => {
if (v.name !== item.name) {
v.is_play = false
}
})
item.is_play = !item.is_play
if (item.is_play) {
if (this.currentItem) {
if (this.currentItem.name !== item.name) {
this.audio.pause()
this.audio.src = item.mp3
this.audio.currentTime = 0
}
} else {
this.audio.pause()
this.audio.src = item.mp3
this.audio.currentTime = 0
}
this.audio.play()
this.audio.addEventListener('ended', () => (item.is_play = false))
} else {
this.stopPlay()
}
this.currentItem = item
},
stopPlay() {
this.audio.pause()
// this.audio.currentTime = 0
this.audio.removeEventListener('ended', null)
}
},
unmounted() {
this.stopPlay()
} }
} }
function togglePlay(item, list) {
list.map((v) => {
if (v.name !== item.name) {
v.is_play = false
}
})
item.is_play = !item.is_play
if (item.is_play) {
if (data.currentItem) {
if (data.currentItem.name !== item.name) {
data.audio.pause()
data.audio.src = item.mp3
data.audio.currentTime = 0
}
} else {
data.audio.pause()
data.audio.src = item.mp3
data.audio.currentTime = 0
}
data.audio.play()
data.audio.addEventListener('ended', () => (item.is_play = false))
} else {
stopPlay()
}
data.currentItem = item
}
function stopPlay() {
data.audio.pause()
// data.audio.currentTime = 0
data.audio.removeEventListener('ended', null)
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -7,63 +7,59 @@
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<Scroll class="Scroll" @pulldown="loadData"> <Scroll class="Scroll" @pulldown="loadData">
<Posters mode="music" :list="videos" /> <Posters mode="music" :list="data.videos" />
<Loading :is-full-screen="false" v-if="loading" /> <Loading :is-full-screen="false" v-if="data.loading" />
<NoMore v-else /> <NoMore v-else />
</Scroll> </Scroll>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Posters from '../../../components/Posters' import Posters from '@/components/Posters.vue'
import Scroll from '../../../components/Scroll' import Scroll from '@/components/Scroll.vue'
import { myVideo } from '@/api/videos' import { myVideo } from '@/api/videos'
export default { import { onMounted, reactive } from 'vue'
name: 'VideoCollect',
components: { defineOptions({
Posters, name: 'VideoCollect'
Scroll })
},
data() { const data = reactive({
return { loading: false,
loading: false, total: 0,
total: 0, pageNo: 0,
pageNo: 0, pageSize: 15,
pageSize: 15, videos: []
videos: [] })
}
}, onMounted(() => {
computed: {}, loadData(true)
created() { })
this.loadData(true)
}, async function loadData(init = false) {
methods: { if (data.loading) return
async loadData(init = false) { if (!init) {
if (this.loading) return if (data.total <= data.videos.length) {
if (!init) { return
if (this.total <= this.videos.length) {
return
}
this.pageNo++
}
this.loading = true
let res = await myVideo({
pageNo: this.pageNo,
pageSize: this.pageSize
})
this.loading = false
if (res.code === this.SUCCESS) {
this.videos = this.videos.concat(res.data.list)
this.total = res.data.total
}
} }
data.pageNo++
}
data.loading = true
let res: any = await myVideo({
pageNo: data.pageNo,
pageSize: data.pageSize
})
data.loading = false
if (res.success) {
data.videos = data.videos.concat(res.data.list)
data.total = res.data.total
} }
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import '../../../assets/less/index'; @import '@/assets/less/index';
.VideoCollect { .VideoCollect {
position: fixed; position: fixed;

View File

@ -13,16 +13,16 @@
style="width: calc(100vw - 2rem); margin-left: 1rem" style="width: calc(100vw - 2rem); margin-left: 1rem"
tabStyleWidth="50%" tabStyleWidth="50%"
:tabTexts="['视频', '影视综']" :tabTexts="['视频', '影视综']"
v-model:active-index="currentSlideItemIndex" v-model:active-index="data.currentSlideItemIndex"
> >
</Indicator> </Indicator>
<SlideHorizontal v-model:index="currentSlideItemIndex" class="SlideRowList"> <SlideHorizontal v-model:index="data.currentSlideItemIndex" class="SlideRowList">
<SlideItem class="tab1" style="overflow: auto"> <SlideItem class="tab1" style="overflow: auto">
<Scroll class="Scroll" @pulldown="getHistoryVideo"> <Scroll class="Scroll" @pulldown="getHistoryVideo">
<Posters :list="historyVideo.list" v-if="historyVideo.total"></Posters> <Posters :list="data.historyVideo.list" v-if="data.historyVideo.total"></Posters>
<Loading :is-full-screen="false" v-if="loadingVideo" /> <Loading :is-full-screen="false" v-if="data.loadingVideo" />
<template v-else> <template v-else>
<NoMore v-if="historyVideo.list.length" /> <NoMore v-if="data.historyVideo.list.length" />
<div class="empty" v-else> <div class="empty" v-else>
<img src="../../../assets/img/icon/none-bg1.webp" alt="" /> <img src="../../../assets/img/icon/none-bg1.webp" alt="" />
<div class="title">暂无观看历史记录</div> <div class="title">暂无观看历史记录</div>
@ -40,100 +40,97 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Posters from '../../../components/Posters' import Posters from '@/components/Posters.vue'
import Scroll from '../../../components/Scroll' import Scroll from '@/components/Scroll.vue'
import NoMore from '../../../components/NoMore' import NoMore from '@/components/NoMore.vue'
import { historyOther, historyVideo } from '@/api/videos' import { historyOther, historyVideo } from '@/api/videos'
export default { import { computed, onMounted, reactive } from 'vue'
name: 'lookHistory', import { _showConfirmDialog } from '@/utils'
components: {
NoMore, defineOptions({
Posters, name: 'LookHistory'
Scroll })
const data = reactive({
loadingVideo: false,
loadingOther: false,
isClearHistoryVideo: false,
isClearHistoryOther: false,
currentSlideItemIndex: 0,
pageSize: 15,
historyVideo: {
total: 0,
pageNo: 0,
list: []
}, },
data() { historyOther: {
return { total: 0,
loadingVideo: false, pageNo: 0,
loadingOther: false, list: []
isClearHistoryVideo: false,
isClearHistoryOther: false,
currentSlideItemIndex: 0,
pageSize: 15,
historyVideo: {
total: 0,
pageNo: 0,
list: []
},
historyOther: {
total: 0,
pageNo: 0,
list: []
}
}
},
computed: {
isClear() {
if (this.currentSlideItemIndex === 0) {
return this.historyVideo.list.length
}
return this.historyOther.list.length
}
},
created() {
this.getHistoryVideo(true)
this.getHistoryOther(true)
},
methods: {
async getHistoryVideo(init = false) {
if (this.loadingVideo) return
if (this.isClearHistoryVideo) return
if (!init) {
if (this.historyVideo.total <= this.historyVideo.list.length) return
this.historyVideo.pageNo++
}
this.loadingVideo = true
let res = await historyVideo({
pageNo: this.historyVideo.pageNo,
pageSize: this.pageSize
})
console.log(res)
this.loadingVideo = false
if (res.code === this.SUCCESS) {
this.historyVideo.list = this.historyVideo.list.concat(res.data.list)
this.historyVideo.total = res.data.total
}
},
async getHistoryOther(init = false) {
if (this.loadingOther) return
if (this.isClearHistoryOther) return
this.loadingOther = true
if (!init) {
this.historyOther.pageNo++
}
let res = await historyOther({
pageNo: this.historyOther.pageNo,
pageSize: this.pageSize
})
this.loadingOther = false
if (res.code === this.SUCCESS) {
this.historyOther.list = this.historyOther.list.concat(res.data.list)
this.historyOther.total = res.data.total
}
},
clear() {
this.$showConfirmDialog('确定清空?', '清空后,以往观看记录不再展示', 'gray', () => {
if (this.currentSlideItemIndex === 0) {
this.historyVideo.list = []
this.isClearHistoryVideo = true
return
}
this.historyOther.list = []
this.isClearHistoryVideo = true
})
}
} }
})
const isClear = computed(() => {
if (data.currentSlideItemIndex === 0) {
return data.historyVideo.list.length
}
return data.historyOther.list.length
})
onMounted(() => {
getHistoryVideo(true)
getHistoryOther(true)
})
async function getHistoryVideo(init = false) {
if (data.loadingVideo) return
if (data.isClearHistoryVideo) return
if (!init) {
if (data.historyVideo.total <= data.historyVideo.list.length) return
data.historyVideo.pageNo++
}
data.loadingVideo = true
let res: any = await historyVideo({
pageNo: data.historyVideo.pageNo,
pageSize: data.pageSize
})
console.log(res)
data.loadingVideo = false
if (res.success) {
data.historyVideo.list = data.historyVideo.list.concat(res.data.list)
data.historyVideo.total = res.data.total
}
}
async function getHistoryOther(init = false) {
if (data.loadingOther) return
if (data.isClearHistoryOther) return
data.loadingOther = true
if (!init) {
data.historyOther.pageNo++
}
let res: any = await historyOther({
pageNo: data.historyOther.pageNo,
pageSize: data.pageSize
})
data.loadingOther = false
if (res.success) {
data.historyOther.list = data.historyOther.list.concat(res.data.list)
data.historyOther.total = res.data.total
}
}
function clear() {
_showConfirmDialog('确定清空?', '清空后,以往观看记录不再展示', 'gray', () => {
if (data.currentSlideItemIndex === 0) {
data.historyVideo.list = []
data.isClearHistoryVideo = true
return
}
data.historyOther.list = []
data.isClearHistoryVideo = true
})
} }
</script> </script>

View File

@ -1,35 +1,35 @@
<template> <template>
<div class="DetailSetting"> <div class="DetailSetting">
<BaseHeader /> <BaseHeader />
<div class="content type1" v-if="type === 0"> <div class="content type1" v-if="data.type === 0">
<div class="notice"> <div class="notice">
<img src="../../../../assets/img/icon/newicon/left_menu/lock.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/lock.png" alt="" />
<span>时间锁已关闭</span> <span>时间锁已关闭</span>
</div> </div>
<div class="row mt1r no-active"> <div class="row mt1r no-active">
<div class="left"> <div class="left">
<img src="../../../../assets/img/icon/newicon/left_menu/hourglass.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/hourglass.png" alt="" />
<span>可为时间锁设置一个触发时间</span> <span>可为时间锁设置一个触发时间</span>
</div> </div>
</div> </div>
<div class="row mt1r no-active"> <div class="row mt1r no-active">
<div class="left"> <div class="left">
<img src="../../../../assets/img/icon/newicon/left_menu/clock.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/clock.png" alt="" />
<span>开启时间锁后单日使用时长超过触发时间需输入密码才能继续使用</span> <span>开启时间锁后单日使用时长超过触发时间需输入密码才能继续使用</span>
</div> </div>
</div> </div>
<div class="row mt1r mb1r no-active"> <div class="row mt1r mb1r no-active">
<div class="left"> <div class="left">
<img src="../../../../assets/img/icon/newicon/left_menu/lock.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/lock.png" alt="" />
<span>开启时间锁需先设置独立密码忘记密码后可通过申诉重置密码</span> <span>开启时间锁需先设置独立密码忘记密码后可通过申诉重置密码</span>
</div> </div>
</div> </div>
<div class="row mt1r mb1r" @click="$nav('trigger-time', { triggerTime })"> <div class="row mt1r mb1r" @click="nav('trigger-time', { triggerTime: data.triggerTime })">
<div class="left"> <div class="left">
<span>触发时间</span> <span>触发时间</span>
</div> </div>
<div class="right"> <div class="right">
<span>{{ triggerTime }}分钟</span> <span>{{ data.triggerTime }}分钟</span>
<dy-back direction="right"></dy-back> <dy-back direction="right"></dy-back>
</div> </div>
</div> </div>
@ -37,62 +37,62 @@
<div class="button primary">开启时间锁</div> <div class="button primary">开启时间锁</div>
</div> </div>
</div> </div>
<div class="content type2" v-if="type === 1"> <div class="content type2" v-if="data.type === 1">
<img <img class="desc" src="@/assets/img/icon/newicon/left_menu/qingshaonian.png" alt="" />
class="desc"
src="../../../../assets/img/icon/newicon/left_menu/qingshaonian.png"
alt=""
/>
<div class="footer"> <div class="footer">
<div class="notice"> <div class="notice">
<span>更多信息可阅读</span> <span>更多信息可阅读</span>
<span <span
style="color: yellow" style="color: yellow"
@click="$nav('/service-protocol', { type: '儿童/青少年使用须知' })" @click="nav('/service-protocol', { type: '儿童/青少年使用须知' })"
>儿童/青少年使用须知</span >儿童/青少年使用须知</span
> >
</div> </div>
<div class="button primary">开启青少年模式</div> <div class="button primary">开启青少年模式</div>
</div> </div>
</div> </div>
<div class="content type2" v-if="type === 2"> <div class="content type2" v-if="data.type === 2">
<img class="desc" src="../../../../assets/img/icon/newicon/left_menu/img-type3.png" alt="" /> <img class="desc" src="@/assets/img/icon/newicon/left_menu/img-type3.png" alt="" />
<div class="footer"> <div class="footer">
<div class="notice"> <div class="notice">
<!-- TODO 有个勾选没做--> <!-- TODO 有个勾选没做-->
<span>我已阅读并接受</span> <span>我已阅读并接受</span>
<span <span
style="color: yellow" style="color: yellow"
@click="$nav('/service-protocol', { type: '抖音亲子平台服务协议' })" @click="nav('/service-protocol', { type: '抖音亲子平台服务协议' })"
> >
抖音亲子平台服务协议 抖音亲子平台服务协议
</span> </span>
</div> </div>
<div class="button primary">立即绑定</div> <BaseButton type="primary">立即绑定</BaseButton>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import enums from '../../../../utils/enums' import enums from '@/utils/enums'
export default { import { onMounted, reactive } from 'vue'
name: 'DetailSetting', import { useNav } from '@/utils/hooks/useNav.js'
data() { import { useRoute } from 'vue-router'
return { import BaseButton from '@/components/BaseButton.vue'
type: 0,
enums, defineOptions({
triggerTime: enums.TRIGGER_TIME.TIME60 name: 'DetailSetting'
} })
},
computed: {}, const route = useRoute()
created() { const nav = useNav()
this.type = ~~this.$route.query.type const data = reactive({
let triggerTime = localStorage.getItem('changeTriggerTime') type: 0,
if (triggerTime !== null) this.triggerTime = triggerTime triggerTime: enums.TRIGGER_TIME.TIME60
}, })
methods: {}
} onMounted(() => {
data.type = ~~route.query.type
let triggerTime = localStorage.getItem('changeTriggerTime')
if (triggerTime !== null) data.triggerTime = Number(triggerTime)
})
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -6,21 +6,21 @@
</template> </template>
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div class="row" @click="$nav('detail-setting', { type: 0 })"> <div class="row" @click="nav('detail-setting', { type: 0 })">
<div class="left">时间锁</div> <div class="left">时间锁</div>
<div class="right"> <div class="right">
<span>未开启</span> <span>未开启</span>
<dy-back direction="right"></dy-back> <dy-back direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('detail-setting', { type: 1 })"> <div class="row" @click="nav('detail-setting', { type: 1 })">
<div class="left">青少年模式</div> <div class="left">青少年模式</div>
<div class="right"> <div class="right">
<span>未开启</span> <span>未开启</span>
<dy-back direction="right"></dy-back> <dy-back direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('detail-setting', { type: 2 })"> <div class="row" @click="nav('detail-setting', { type: 2 })">
<div class="left">亲子平台</div> <div class="left">亲子平台</div>
<div class="right"> <div class="right">
<span>未开启</span> <span>未开启</span>
@ -30,20 +30,17 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { import { useNav } from '@/utils/hooks/useNav'
name: 'index',
data() { defineOptions({
return {} name: 'MinorProtection'
}, })
computed: {}, const nav = useNav()
created() {},
methods: {}
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import '../../../../assets/less/index'; @import '@/assets/less/index';
.index { .index {
position: fixed; position: fixed;

View File

@ -8,51 +8,53 @@
<div class="content"> <div class="content">
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME40)"> <div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME40)">
<div class="left">40分钟</div> <div class="left">40分钟</div>
<div class="right" v-if="triggerTime === enums.TRIGGER_TIME.TIME40"> <div class="right" v-if="data.triggerTime === enums.TRIGGER_TIME.TIME40">
<img src="../../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME60)"> <div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME60)">
<div class="left">60分钟</div> <div class="left">60分钟</div>
<div class="right" v-if="triggerTime === enums.TRIGGER_TIME.TIME60"> <div class="right" v-if="data.triggerTime === enums.TRIGGER_TIME.TIME60">
<img src="../../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME90)"> <div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME90)">
<div class="left">90分钟</div> <div class="left">90分钟</div>
<div class="right" v-if="triggerTime === enums.TRIGGER_TIME.TIME90"> <div class="right" v-if="data.triggerTime === enums.TRIGGER_TIME.TIME90">
<img src="../../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME120)"> <div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME120)">
<div class="left">120分钟</div> <div class="left">120分钟</div>
<div class="right" v-if="triggerTime === enums.TRIGGER_TIME.TIME120"> <div class="right" v-if="data.triggerTime === enums.TRIGGER_TIME.TIME120">
<img src="../../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import enums from '../../../../utils/enums' import enums from '../../../../utils/enums'
export default { import { onMounted, reactive } from 'vue'
name: 'TriggerTime', import { useRoute } from 'vue-router'
data() {
return { defineOptions({
enums, name: 'ChooseSchool'
triggerTime: enums.TRIGGER_TIME.TIME60 })
}
}, const route = useRoute()
created() { const data = reactive({
this.triggerTime = ~~this.$route.query.triggerTime triggerTime: enums.TRIGGER_TIME.TIME60
}, })
methods: {
setTriggerTime(type) { onMounted(() => {
this.triggerTime = type data.triggerTime = ~~route.query.triggerTime
localStorage.setItem('changeTriggerTime', type) })
}
} function setTriggerTime(type) {
data.triggerTime = type
localStorage.setItem('changeTriggerTime', type)
} }
</script> </script>

View File

@ -9,7 +9,7 @@
<div class="title">帐号</div> <div class="title">帐号</div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/user.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/user.png" alt="" />
<span>帐号与安全</span> <span>帐号与安全</span>
</div> </div>
<div class="right"> <div class="right">
@ -18,7 +18,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/lock.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/lock.png" alt="" />
<span>隐私设置</span> <span>隐私设置</span>
</div> </div>
<div class="right"> <div class="right">
@ -30,7 +30,7 @@
<div class="title">通用</div> <div class="title">通用</div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/remind.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/remind.png" alt="" />
<span>通知设置</span> <span>通知设置</span>
</div> </div>
<div class="right"> <div class="right">
@ -39,7 +39,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/dynamics.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/dynamics.png" alt="" />
<span>动态壁纸</span> <span>动态壁纸</span>
</div> </div>
<div class="right"> <div class="right">
@ -48,7 +48,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/setting-two.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/setting-two.png" alt="" />
<span>通用设置</span> <span>通用设置</span>
</div> </div>
<div class="right"> <div class="right">
@ -60,7 +60,7 @@
<div class="title">帐号互通</div> <div class="title">帐号互通</div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/toutiao.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/toutiao.png" alt="" />
<span>头条主页</span> <span>头条主页</span>
</div> </div>
<div class="right"> <div class="right">
@ -72,7 +72,7 @@
<div class="title">关于</div> <div class="title">关于</div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/adddddddd.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/adddddddd.png" alt="" />
<span>广告反馈与设置</span> <span>广告反馈与设置</span>
</div> </div>
<div class="right"> <div class="right">
@ -81,7 +81,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/book.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/book.png" alt="" />
<span>用户协议</span> <span>用户协议</span>
</div> </div>
<div class="right"> <div class="right">
@ -90,7 +90,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/bookmark.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/bookmark.png" alt="" />
<span>社区自律公约</span> <span>社区自律公约</span>
</div> </div>
<div class="right"> <div class="right">
@ -99,7 +99,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/personal-privacy.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/personal-privacy.png" alt="" />
<span>隐私政策</span> <span>隐私政策</span>
</div> </div>
<div class="right"> <div class="right">
@ -108,7 +108,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/protect.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/protect.png" alt="" />
<span>应用权限</span> <span>应用权限</span>
</div> </div>
<div class="right"> <div class="right">
@ -117,7 +117,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/ring.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/ring.png" alt="" />
<span>第三方SDK列表</span> <span>第三方SDK列表</span>
</div> </div>
<div class="right"> <div class="right">
@ -126,7 +126,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/about.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/about.png" alt="" />
<span>关于抖音</span> <span>关于抖音</span>
</div> </div>
<div class="right"> <div class="right">
@ -135,7 +135,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/feedback.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/feedback.png" alt="" />
<span>反馈与帮助</span> <span>反馈与帮助</span>
</div> </div>
<div class="right"> <div class="right">
@ -144,7 +144,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/delete.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/delete.png" alt="" />
<span>清理占用空间</span> <span>清理占用空间</span>
</div> </div>
<div class="right"> <div class="right">
@ -155,7 +155,7 @@
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/switch.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/switch.png" alt="" />
<span>切换空间</span> <span>切换空间</span>
</div> </div>
<div class="right"> <div class="right">
@ -164,7 +164,7 @@
</div> </div>
<div class="row"> <div class="row">
<div class="left"> <div class="left">
<img src="../../../assets/img/icon/newicon/left_menu/logout.png" alt="" /> <img src="@/assets/img/icon/newicon/left_menu/logout.png" alt="" />
<span>退出登录</span> <span>退出登录</span>
</div> </div>
<div class="right"> <div class="right">
@ -172,32 +172,22 @@
</div> </div>
</div> </div>
<div class="version">抖音 version{{ version }}</div> <div class="version">抖音 version{{ store.version }}</div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
export default { defineOptions({
name: 'Setting', name: 'ChooseSchool'
setup() { })
const baseStore = useBaseStore()
return { baseStore } const store = useBaseStore()
},
data() {
return {
version: this.baseStore.version
}
},
computed: {},
created() {},
methods: {}
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import '../../../assets/less/index'; @import '@/assets/less/index';
.Setting { .Setting {
position: fixed; position: fixed;

View File

@ -11,24 +11,24 @@
</template> </template>
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div class="row" @click="$nav('/me/choose-school')"> <div class="row" @click="nav('/me/choose-school')">
<div class="left">学校</div> <div class="left">学校</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(localSchool.name) }}</span> <span>{{ isEmpty(data.localSchool.name) }}</span>
<dy-back scale="1" direction="right"></dy-back> <dy-back scale="1" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="checkGo('/me/choose-department')"> <div class="row" @click="checkGo('/me/choose-department')">
<div class="left">院系</div> <div class="left">院系</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(localSchool.department) }}</span> <span>{{ isEmpty(data.localSchool.department) }}</span>
<dy-back scale="1" direction="right"></dy-back> <dy-back scale="1" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="showJoinTimeDialog"> <div class="row" @click="showJoinTimeDialog">
<div class="left">入学时间</div> <div class="left">入学时间</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(localSchool.joinTime) }}</span> <span>{{ isEmpty(data.localSchool.joinTime) }}</span>
<dy-back scale="1" direction="right"></dy-back> <dy-back scale="1" direction="right"></dy-back>
<div v-show="false" id="trigger1"></div> <div v-show="false" id="trigger1"></div>
</div> </div>
@ -36,11 +36,14 @@
<div class="row" @click="showEducationDialog"> <div class="row" @click="showEducationDialog">
<div class="left">学历</div> <div class="left">学历</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(localSchool.education) }}</span> <span>{{ isEmpty(data.localSchool.education) }}</span>
<dy-back scale="1" direction="right"></dy-back> <dy-back scale="1" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('/me/display-type', { displayType: localSchool.displayType })"> <div
class="row"
@click="nav('/me/display-type', { displayType: data.localSchool.displayType })"
>
<div class="left">展示范围</div> <div class="left">展示范围</div>
<div class="right"> <div class="right">
<span>{{ displayType }}</span> <span>{{ displayType }}</span>
@ -51,122 +54,128 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapState } from 'pinia'
import enums from '../../../utils/enums' import enums from '../../../utils/enums'
import { inject } from 'vue' import { computed, onMounted, reactive } from 'vue'
import MobileSelect from '../../../components/mobile-select/mobile-select' import MobileSelect from '../../../components/mobile-select/mobile-select'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { useRouter } from 'vue-router'
import { useNav } from '@/utils/hooks/useNav.js'
import {
_hideLoading,
_notice,
_showLoading,
_showSelectDialog,
_showSimpleConfirmDialog,
_sleep,
cloneDeep
} from '@/utils'
//TODO //TODO
export default {
name: 'AddSchool', defineOptions({
setup() { name: 'AddSchool'
const baseStore = useBaseStore() })
return { baseStore }
}, const store = useBaseStore()
data() { const router = useRouter()
return { const nav = useNav()
mitt: inject('mitt'), const data = reactive({
localSchool: this.$clone(this.baseStore.userinfo.school), localSchool: cloneDeep(store.userinfo.school),
educationList: [ educationList: [
{ id: 1, name: '专科' }, { id: 1, name: '专科' },
{ id: 2, name: '本科' }, { id: 2, name: '本科' },
{ id: 3, name: '硕士' }, { id: 3, name: '硕士' },
{ id: 4, name: '博士' } { id: 4, name: '博士' }
] ]
} })
},
created() { onMounted(() => {
let school = localStorage.getItem('changeSchool') let school = localStorage.getItem('changeSchool')
let department = localStorage.getItem('changeDepartment') let department = localStorage.getItem('changeDepartment')
let displayType = localStorage.getItem('changeDisplayType') let displayType = localStorage.getItem('changeDisplayType')
let joinTime = localStorage.getItem('changeJoinTime') let joinTime = localStorage.getItem('changeJoinTime')
let education = localStorage.getItem('changeEducation') let education = localStorage.getItem('changeEducation')
if (school !== null) this.localSchool.name = school if (school !== null) data.localSchool.name = school
if (department !== null) this.localSchool.department = department if (department !== null) data.localSchool.department = department
if (displayType !== null) this.localSchool.displayType = ~~displayType if (displayType !== null) data.localSchool.displayType = ~~displayType
if (joinTime !== null) this.localSchool.joinTime = ~~joinTime if (joinTime !== null) data.localSchool.joinTime = ~~joinTime
if (education !== null) this.localSchool.education = education if (education !== null) data.localSchool.education = education
// localStorage.clear() })
},
computed: { const school = computed(() => {
...mapState(useBaseStore, ['userinfo']), return store.userinfo.school
isChanged() { })
if (this.school.name !== this.localSchool.name) return true const isChanged = computed(() => {
if (this.school.department !== this.localSchool.department) return true if (school.value.name !== data.localSchool.name) return true
if (this.school.joinTime !== this.localSchool.joinTime) return true if (school.value.department !== data.localSchool.department) return true
if (this.school.education !== this.localSchool.education) return true if (school.value.joinTime !== data.localSchool.joinTime) return true
return this.school.displayType !== this.localSchool.displayType if (school.value.education !== data.localSchool.education) return true
}, return school.value.displayType !== data.localSchool.displayType
displayType() { })
if (this.localSchool.displayType === enums.DISPLAY_TYPE.ALL) return '公开可见'
if (this.localSchool.displayType === enums.DISPLAY_TYPE.SCHOOL) return '校友可见' const displayType = computed(() => {
if (this.localSchool.displayType === enums.DISPLAY_TYPE.ME) return '仅自己可见' if (data.localSchool.displayType === enums.DISPLAY_TYPE.ALL) return '公开可见'
return '' if (data.localSchool.displayType === enums.DISPLAY_TYPE.SCHOOL) return '校友可见'
}, if (data.localSchool.displayType === enums.DISPLAY_TYPE.ME) return '仅自己可见'
school() { return ''
return this.userinfo.school })
}
}, function showJoinTimeDialog() {
methods: { new MobileSelect({
showJoinTimeDialog() { trigger: '#trigger1',
new MobileSelect({ title: '学历',
trigger: '#trigger1', wheels: [
title: '学历', {
wheels: [ data: Array.apply(null, { length: 50 }).map((v, i) => new Date().getFullYear() - i)
{
data: Array.apply(null, { length: 50 }).map((v, i) => new Date().getFullYear() - i)
}
],
callback: (indexArr, data) => {
localStorage.setItem('changeJoinTime', data[0])
this.localSchool.joinTime = ~~data[0]
}
}).show()
},
showEducationDialog() {
this.$showSelectDialog(this.educationList, (e) => {
localStorage.setItem('changeEducation', e.name)
this.localSchool.education = e.name
})
},
isEmpty(val) {
if (val) return val
return '点击设置'
},
checkGo(path) {
if (!this.localSchool.name) return this.$notice('请先选择学校 ')
this.$nav(path)
},
back() {
if (this.isChanged) {
this.$showSimpleConfirmDialog(
'学校信息30天内只允许修改一次是否保存修改',
this.save,
() => {
localStorage.clear()
this.$back()
}
)
} else {
localStorage.clear()
this.$back()
} }
}, ],
async save() { callback: (indexArr, data) => {
if (!this.isChanged) return localStorage.setItem('changeJoinTime', data[0])
this.$showLoading() data.localSchool.joinTime = ~~data[0]
let data = { ...this.userinfo, ...{ school: this.localSchool } }
this.baseStore.setUserinfo(data)
await this.$sleep(500)
this.$hideLoading()
localStorage.clear()
this.$back()
this.$notice('修改成功')
} }
}).show()
}
function showEducationDialog() {
_showSelectDialog(data.educationList, (e) => {
localStorage.setItem('changeEducation', e.name)
data.localSchool.education = e.name
})
}
function isEmpty(val) {
if (val) return val
return '点击设置'
}
function checkGo(path) {
if (!data.localSchool.name) return _notice('请先选择学校 ')
nav(path)
}
function back() {
if (isChanged.value) {
_showSimpleConfirmDialog('学校信息30天内只允许修改一次是否保存修改', save, () => {
localStorage.clear()
router.back()
})
} else {
localStorage.clear()
router.back()
} }
} }
async function save() {
if (!isChanged.value) return
_showLoading()
store.userinfo = { ...store.userinfo, ...{ school: data.localSchool } }
await _sleep(500)
_hideLoading()
localStorage.clear()
router.back()
_notice('修改成功')
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -7,7 +7,7 @@
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div class="schools"> <div class="schools">
<div class="row" @click="save(item)" :key="i" v-for="(item, i) in list"> <div class="row" @click="save()" :key="i" v-for="(item, i) in list">
<span>{{ item }}</span> <span>{{ item }}</span>
</div> </div>
</div> </div>
@ -15,56 +15,59 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapState } from 'pinia' import { ref } from 'vue'
import { _hideLoading, _showLoading, _sleep } from '@/utils'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
export default { defineOptions({
name: 'ChooseProvince', name: 'ChooseCity'
setup() { })
const baseStore = useBaseStore()
return { baseStore } const store = useBaseStore()
}, const list = ref([
data() { '河北',
return { '山西',
list: [ '辽宁',
'成都', '吉林',
'自贡', '黑龙江',
'攀枝花', '江苏',
'泸州', '浙江',
'德阳', '安徽',
'绵阳', '福建',
'广元', '江西',
'遂宁', '山东',
'内江', '河南',
'乐山', '湖北',
'南充', '湖南',
'眉山', '广东',
'宜宾', '海南',
'广安', '四川',
'达州', '贵州',
'雅安', '云南',
'巴中', '陕西',
'资阳', '甘肃',
'阿坝', '青海',
'甘孜', '台湾',
'凉山' '内蒙古',
] '广西',
} '西藏',
}, '宁夏',
computed: { '新疆',
...mapState(useBaseStore, ['userinfo']) '北京',
}, '天津',
methods: { '上海',
async save() { '重庆',
this.$showLoading() '香港',
let data = { ...this.userinfo, ...{ location: '中国-四川-成都' } } '澳门'
this.baseStore.setUserinfo(data) ])
await this.$sleep(500)
this.$hideLoading() async function save() {
history.go(-3) _showLoading()
} store.userinfo = { ...store.userinfo, ...{ location: '中国-四川-成都' } }
} await _sleep(500)
_hideLoading()
history.go(-3)
} }
</script> </script>

View File

@ -5,12 +5,17 @@
<span class="f16">选择院系</span> <span class="f16">选择院系</span>
</template> </template>
<template v-slot:right> <template v-slot:right>
<span class="f14" @click="$nav('/me/declare-school', { type: 2 })">没有找到?</span> <span class="f14" @click="nav('/me/declare-school', { type: 2 })">没有找到?</span>
</template> </template>
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div class="nearby"> <div class="nearby">
<div class="item" :key="i" v-for="(item, i) in departments" @click="setDepartment(item)"> <div
class="item"
:key="i"
v-for="(item, i) in data.departments"
@click="setDepartment(item)"
>
{{ item }} {{ item }}
</div> </div>
</div> </div>
@ -18,33 +23,36 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { import { onMounted, reactive } from 'vue'
name: 'ChooseSchool', import { useNav } from '@/utils/hooks/useNav.js'
components: {}, import { useRouter } from 'vue-router'
data() {
return { defineOptions({
departments: [], name: 'ChooseDepartment'
schoolName: '' })
}
}, const router = useRouter()
computed: {}, const nav = useNav()
created() { const data = reactive({
for (let i = 0; i < 5; i++) { departments: [],
this.departments.push('院系' + i) schoolName: ''
} })
},
methods: { onMounted(() => {
setDepartment(val) { for (let i = 0; i < 5; i++) {
localStorage.setItem('changeDepartment', val) data.departments.push('院系' + i)
this.$back()
}
} }
})
function setDepartment(val) {
localStorage.setItem('changeDepartment', val)
router.back()
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import '../../../assets/less/index'; @import '@/assets/less/index';
.choose-school { .choose-school {
position: fixed; position: fixed;

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div class="schools"> <div class="schools">
<div class="row" @click="$nav('/me/choose-city')" :key="i" v-for="(item, i) in list"> <div class="row" @click="nav('/me/choose-city')" :key="i" v-for="(item, i) in list">
<span>{{ item }}</span> <span>{{ item }}</span>
<div class="right"> <div class="right">
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
@ -18,50 +18,51 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
export default { import { ref } from 'vue'
name: 'ChooseProvince', import { useNav } from '@/utils/hooks/useNav.js'
data() {
return { defineOptions({
list: [ name: 'ChooseProvince'
'河北', })
'山西',
'辽宁', const nav = useNav()
'吉林', const list = ref([
'黑龙江', '河北',
'江苏', '山西',
'浙江', '辽宁',
'安徽', '吉林',
'福建', '黑龙江',
'江西', '江苏',
'山东', '浙江',
'河南', '安徽',
'湖北', '福建',
'湖南', '江西',
'广东', '山东',
'海南', '河南',
'四川', '湖北',
'贵州', '湖南',
'云南', '广东',
'陕西', '海南',
'甘肃', '四川',
'青海', '贵州',
'台湾', '云南',
'内蒙古', '陕西',
'广西', '甘肃',
'西藏', '青海',
'宁夏', '台湾',
'新疆', '内蒙古',
'北京', '广西',
'天津', '西藏',
'上海', '宁夏',
'重庆', '新疆',
'香港', '北京',
'澳门' '天津',
] '上海',
} '重庆',
} '香港',
} '澳门'
])
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -5,45 +5,50 @@
<span class="f16">添加学校</span> <span class="f16">添加学校</span>
</template> </template>
<template v-slot:right> <template v-slot:right>
<span class="f14" @click="$nav('/me/declare-school', { type: 1 })">没有找到?</span> <span class="f14" @click="nav('/me/declare-school', { type: 1 })">没有找到?</span>
</template> </template>
<template v-slot:bottom> <template v-slot:bottom>
<Search <Search
class="mt1r mb1r ml2r mr2r" class="mt1r mb1r ml2r mr2r"
placeholder="搜索大学名称" placeholder="搜索大学名称"
v-model="schoolName" v-model="data.schoolName"
@clear="isSearch = false" @clear="data.isSearch = false"
:is-show-right-text="true" :is-show-right-text="true"
@notice="search" @notice="search"
></Search> ></Search>
</template> </template>
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div class="nearby" v-if="!isSearch"> <div class="nearby" v-if="!data.isSearch">
<div class="title"> <div class="title">
<img src="../../../assets/img/icon/location.svg" alt="" /> <img src="../../../assets/img/icon/location.svg" alt="" />
<span>离我最近</span> <span>离我最近</span>
</div> </div>
<template v-if="nearby.length"> <template v-if="data.nearby.length">
<div class="item" :key="i" v-for="(item, i) in nearby" @click="setSchool(item)"> <div class="item" :key="i" v-for="(item, i) in data.nearby" @click="setSchool(item)">
{{ item }} {{ item }}
</div> </div>
</template> </template>
<div v-else class="item">无法获取</div> <div v-else class="item">无法获取</div>
</div> </div>
<div class="line" style="width: calc(100% - 40rem); margin-left: 20rem"></div> <div class="line" style="width: calc(100% - 40rem); margin-left: 20rem"></div>
<div class="schools" v-if="!isSearch"> <div class="schools" v-if="!data.isSearch">
<div class="item" :key="i" v-for="(item, i) in schools" @click="setSchool(item)"> <div class="item" :key="i" v-for="(item, i) in data.schools" @click="setSchool(item)">
{{ item }} {{ item }}
</div> </div>
</div> </div>
<div v-if="isSearch"> <div v-if="data.isSearch">
<template v-if="searchSchools.length"> <template v-if="data.searchSchools.length">
<div class="item" :key="i" v-for="(item, i) in searchSchools" @click="setSchool(item)"> <div
<span v-if="item.indexOf(schoolName) > -1"> class="item"
{{ item.substr(0, item.indexOf(schoolName)) }} :key="i"
<span style="color: #f50">{{ schoolName }}</span> v-for="(item, i) in data.searchSchools"
{{ item.substr(item.indexOf(schoolName) + schoolName.length) }} @click="setSchool(item)"
>
<span v-if="item.indexOf(data.schoolName) > -1">
{{ item.substr(0, item.indexOf(data.schoolName)) }}
<span style="color: #f50">{{ data.schoolName }}</span>
{{ item.substr(item.indexOf(data.schoolName) + data.schoolName.length) }}
</span> </span>
<span v-else>{{ item }}</span> <span v-else>{{ item }}</span>
</div> </div>
@ -52,48 +57,50 @@
<img src="../../../assets/img/icon/head-image.jpeg" alt="" /> <img src="../../../assets/img/icon/head-image.jpeg" alt="" />
<div class="title">搜索结果为空</div> <div class="title">搜索结果为空</div>
<div class="sub-title">没有搜索到相关的内容</div> <div class="sub-title">没有搜索到相关的内容</div>
<div class="btn" @click="$nav('/me/declare-school')">没有学校信息去申报</div> <div class="btn" @click="nav('/me/declare-school')">没有学校信息去申报</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Search from '../../../components/Search' import Search from '../../../components/Search.vue'
import { onMounted, reactive } from 'vue'
import { useNav } from '@/utils/hooks/useNav.js'
import { useRouter } from 'vue-router'
export default { defineOptions({
name: 'ChooseSchool', name: 'ChooseSchool'
components: { })
Search
}, const router = useRouter()
data() { const nav = useNav()
return { const data = reactive({
isSearch: false, isSearch: false,
nearby: [], nearby: [],
schools: [], schools: [],
searchSchools: [], searchSchools: [],
schoolName: '' schoolName: ''
} })
},
created() { onMounted(() => {
for (let i = 0; i < 20; i++) { for (let i = 0; i < 20; i++) {
this.nearby.push('附近大学' + i) data.nearby.push('附近大学' + i)
this.schools.push('所有大学' + i) data.schools.push('所有大学' + i)
}
},
methods: {
setSchool(val) {
localStorage.setItem('changeSchool', val)
this.$back()
},
search() {
if (!this.schoolName.length) return (this.isSearch = false)
this.isSearch = true
let all = this.nearby.concat(this.schools)
this.searchSchools = all.filter((v) => v.includes(this.schoolName))
}
} }
})
function setSchool(val) {
localStorage.setItem('changeSchool', val)
router.back()
}
function search() {
if (!data.schoolName.length) return (data.isSearch = false)
data.isSearch = true
let all = data.nearby.concat(data.schools)
data.searchSchools = all.filter((v) => v.includes(data.schoolName))
} }
</script> </script>

View File

@ -8,13 +8,13 @@
<div class="content"> <div class="content">
<div class="row"> <div class="row">
<div class="label">学校全称</div> <div class="label">学校全称</div>
<input type="text" placeholder="请输入学校全称(必填)" v-model="form.name" /> <input type="text" placeholder="请输入学校全称(必填)" v-model="data.form.name" />
</div> </div>
<div class="row" v-if="type === 1"> <div class="row" v-if="data.type === 1">
<div class="label">所在城市</div> <div class="label">所在城市</div>
<input type="text" placeholder="请输入学校所在城市(必填)" v-model="form.location" /> <input type="text" placeholder="请输入学校所在城市(必填)" v-model="data.form.location" />
</div> </div>
<div class="department-row" v-if="type === 2"> <div class="department-row" v-if="data.type === 2">
<div class="label">信息问题</div> <div class="label">信息问题</div>
<div class="right"> <div class="right">
<span>点击选择(必选)</span> <span>点击选择(必选)</span>
@ -27,32 +27,37 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { onMounted, reactive } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { _notice } from '@/utils'
defineOptions({
name: 'DeclareSchool'
})
const router = useRouter()
const route = useRoute()
const data = reactive({
form: {
name: '',
location: '',
departmentInfoType: ''
},
type: 1
})
//TODO //TODO
export default { onMounted(() => {
name: 'DeclareSchool', data.type = Number(route.query.type)
data() { })
return {
form: { function submit() {
name: '', if (!data.form.name) return _notice('请输入学校全称')
location: '', if (data.type === 1 && !data.form.location) return _notice('请输入学校所在城市')
departmentInfoType: '' if (data.type === 2 && !data.form.departmentInfoType) return _notice('请选择信息问题')
}, _notice('申报成功')
type: 1 setTimeout(router.back, 1000)
}
},
created() {
this.type = Number(this.$route.query.type)
},
methods: {
submit() {
if (!this.form.name) return this.$notice('请输入学校全称')
if (this.type === 1 && !this.form.location) return this.$notice('请输入学校所在城市')
if (this.type === 2 && !this.form.departmentInfoType) return this.$notice('请选择信息问题')
this.$notice('申报成功')
setTimeout(this.$back, 1000)
}
}
} }
</script> </script>

View File

@ -8,46 +8,49 @@
<div class="content"> <div class="content">
<div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.ALL)"> <div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.ALL)">
<div class="left">公开可见</div> <div class="left">公开可见</div>
<div class="right" v-if="displayType === enums.DISPLAY_TYPE.ALL"> <div class="right" v-if="data.displayType === enums.DISPLAY_TYPE.ALL">
<img src="../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
<div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.SCHOOL)"> <div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.SCHOOL)">
<div class="left">校友可见</div> <div class="left">校友可见</div>
<div class="right" v-if="displayType === enums.DISPLAY_TYPE.SCHOOL"> <div class="right" v-if="data.displayType === enums.DISPLAY_TYPE.SCHOOL">
<img src="../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
<div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.ME)"> <div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.ME)">
<div class="left">仅自己可见</div> <div class="left">仅自己可见</div>
<div class="right" v-if="displayType === enums.DISPLAY_TYPE.ME"> <div class="right" v-if="data.displayType === enums.DISPLAY_TYPE.ME">
<img src="../../../assets/img/icon/ok-red.png" alt="" /> <img src="../../../assets/img/icon/ok-red.png" alt="" />
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import enums from '../../../utils/enums' import enums from '../../../utils/enums'
export default { import { onMounted, reactive } from 'vue'
name: 'DisplayType', import { useRoute, useRouter } from 'vue-router'
data() {
return { defineOptions({
enums, name: 'DisplayType'
displayType: enums.DISPLAY_TYPE.ALL })
}
}, const router = useRouter()
created() { const route = useRoute()
this.displayType = ~~this.$route.query.displayType const data = reactive({
}, displayType: enums.DISPLAY_TYPE.ALL
methods: { })
setDisplayType(type) {
this.displayType = type onMounted(() => {
localStorage.setItem('changeDisplayType', type) data.displayType = ~~route.query.displayType
this.$back() })
}
} function setDisplayType(type) {
data.displayType = type
localStorage.setItem('changeDisplayType', type)
router.back()
} }
</script> </script>

View File

@ -11,29 +11,29 @@
<div class="userinfo"> <div class="userinfo">
<div class="change-avatar"> <div class="change-avatar">
<div class="avatar-ctn" @click="showAvatarDialog"> <div class="avatar-ctn" @click="showAvatarDialog">
<img class="avatar" :src="_checkImgUrl(userinfo.cover_url[0].url_list[0])" alt="" /> <img class="avatar" :src="_checkImgUrl(store.userinfo.cover_url[0].url_list[0])" alt="" />
<img class="change" src="../../../assets/img/icon/me/camera-light.png" alt="" /> <img class="change" src="../../../assets/img/icon/me/camera-light.png" alt="" />
</div> </div>
<span>点击更换头像</span> <span>点击更换头像</span>
</div> </div>
<div class="row" @click="$nav('/me/edit-userinfo-item', { type: 1 })"> <div class="row" @click="nav('/me/edit-userinfo-item', { type: 1 })">
<div class="left">名字</div> <div class="left">名字</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(userinfo.nickname) }}</span> <span>{{ isEmpty(store.userinfo.nickname) }}</span>
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('/me/edit-userinfo-item', { type: 2 })"> <div class="row" @click="nav('/me/edit-userinfo-item', { type: 2 })">
<div class="left">抖音号</div> <div class="left">抖音号</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(_getUserDouyinId({ author: userinfo })) }}</span> <span>{{ isEmpty(_getUserDouyinId({ author: store.userinfo })) }}</span>
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('/me/edit-userinfo-item', { type: 3 })"> <div class="row" @click="nav('/me/edit-userinfo-item', { type: 3 })">
<div class="left">简介</div> <div class="left">简介</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(userinfo.signature) }}</span> <span>{{ isEmpty(store.userinfo.signature) }}</span>
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
</div> </div>
</div> </div>
@ -47,143 +47,143 @@
<div class="row" @click="showBirthdayDialog"> <div class="row" @click="showBirthdayDialog">
<div class="left">生日</div> <div class="left">生日</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(userinfo.user_age) }}</span> <span>{{ isEmpty(store.userinfo.user_age) }}</span>
<div v-show="false" id="trigger1"></div> <div v-show="false" id="trigger1"></div>
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('/me/choose-location')"> <div class="row" @click="nav('/me/choose-location')">
<div class="left">所在地</div> <div class="left">所在地</div>
<div class="right"> <div class="right">
<span v-if="userinfo.province || userinfo.city"> <span v-if="store.userinfo.province || store.userinfo.city">
{{ userinfo.province }} {{ store.userinfo.province }}
<template v-if="userinfo.province && userinfo.city"> - </template> <template v-if="store.userinfo.province && store.userinfo.city"> - </template>
{{ userinfo.city }} {{ store.userinfo.city }}
</span> </span>
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
</div> </div>
</div> </div>
<div class="row" @click="$nav('/me/add-school')"> <div class="row" @click="nav('/me/add-school')">
<div class="left">学校</div> <div class="left">学校</div>
<div class="right"> <div class="right">
<span>{{ isEmpty(userinfo.school?.name) }}</span> <span>{{ isEmpty(store.userinfo.school?.name) }}</span>
<dy-back scale=".8" direction="right"></dy-back> <dy-back scale=".8" direction="right"></dy-back>
</div> </div>
</div> </div>
</div> </div>
<transition name="fade"> <transition name="fade">
<div class="preview-img" v-if="previewImg" @click="previewImg = ''"> <div class="preview-img" v-if="data.previewImg" @click="data.previewImg = ''">
<img class="resource" :src="previewImg" alt="" /> <img class="resource" :src="data.previewImg" alt="" />
<img <img
class="download" class="download"
src="../../../assets/img/icon/components/video/download.png" src="../../../assets/img/icon/components/video/download.png"
alt="" alt=""
@click.stop="$no" @click.stop="_no"
/> />
</div> </div>
</transition> </transition>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import MobileSelect from '../../../components/mobile-select/mobile-select' import MobileSelect from '../../../components/mobile-select/mobile-select'
import { mapState } from 'pinia'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl, _getUserDouyinId } from '../../../utils' import {
_checkImgUrl,
_getUserDouyinId,
_hideLoading,
_no,
_showLoading,
_showSelectDialog,
_sleep
} from '@/utils'
import { computed, reactive } from 'vue'
import { useNav } from '@/utils/hooks/useNav'
export default { defineOptions({
name: 'EditUserInfo', name: 'EditUserInfo'
setup() { })
const baseStore = useBaseStore() const store = useBaseStore()
return { baseStore } const nav = useNav()
}, const data = reactive({
components: {}, sexList: [
data() { { id: 1, name: '男' },
return { { id: 2, name: '女' },
sexList: [ { id: 3, name: '不展示' }
{ id: 1, name: '男' }, ],
{ id: 2, name: '女' }, avatarList: [
{ id: 3, name: '不展示' } { id: 1, name: '拍一张' },
], { id: 2, name: '从相册选择' },
avatarList: [ { id: 3, name: '查看大图' },
{ id: 1, name: '拍一张' }, { id: 4, name: '取消' }
{ id: 2, name: '从相册选择' }, ],
{ id: 3, name: '查看大图' }, previewImg: ''
{ id: 4, name: '取消' } })
],
previewImg: '' const sex = computed(() => {
} switch (store.userinfo.gender) {
}, case 1:
computed: { return '男'
...mapState(useBaseStore, ['userinfo']), case 2:
sex() { return '女'
switch (this.userinfo.gender) { default:
case 1: return ''
return '男'
case 2:
return '女'
default:
return ''
}
}
},
methods: {
_checkImgUrl,
_getUserDouyinId,
isEmpty(val) {
if (val && val !== -1) return val
return '点击设置'
},
showSexDialog() {
this.$showSelectDialog(this.sexList, async (e) => {
this.$showLoading()
await this.$sleep(500)
this.baseStore.setUserinfo({ ...this.userinfo, gender: e.id })
this.$hideLoading()
})
},
showAvatarDialog() {
this.$showSelectDialog(this.avatarList, (e) => {
switch (e.id) {
case 1:
case 2:
return this.$no()
case 3:
this.previewImg = _checkImgUrl(this.userinfo.cover_url[0].url_list[0])
break
}
})
},
showBirthdayDialog() {
new MobileSelect({
trigger: '#trigger1',
title: '生日',
connector: '生日',
wheels: [
{
data: Array.apply(null, { length: 100 }).map((v, i) => new Date().getFullYear() - i)
},
{
data: Array.apply(null, { length: 12 }).map((v, i) => 12 - i)
},
{
data: Array.apply(null, { length: 31 }).map((v, i) => 31 - i)
}
],
callback: async (indexArr, data) => {
console.log(data)
this.$showLoading()
await this.$sleep(500)
this.baseStore.setUserinfo({
...this.userinfo,
birthday: data.join('-')
})
this.$hideLoading()
// this.localSchool.joinTime = ~~data[0]
}
}).show()
}
} }
})
function isEmpty(val) {
if (val && val !== -1) return val
return '点击设置'
}
function showSexDialog() {
_showSelectDialog(data.sexList, async (e) => {
_showLoading()
await _sleep(500)
store.setUserinfo({ ...store.userinfo, gender: e.id })
_hideLoading()
})
}
function showAvatarDialog() {
_showSelectDialog(data.avatarList, (e) => {
switch (e.id) {
case 1:
case 2:
return _no()
case 3:
data.previewImg = _checkImgUrl(store.userinfo.cover_url[0].url_list[0])
break
}
})
}
function showBirthdayDialog() {
new MobileSelect({
trigger: '#trigger1',
title: '生日',
connector: '生日',
wheels: [
{
data: Array.apply(null, { length: 100 }).map((v, i) => new Date().getFullYear() - i)
},
{
data: Array.apply(null, { length: 12 }).map((v, i) => 12 - i)
},
{
data: Array.apply(null, { length: 31 }).map((v, i) => 31 - i)
}
],
callback: async (indexArr, data) => {
_showLoading()
await _sleep(500)
store.setUserinfo({
...store.userinfo,
birthday: data.join('-')
})
_hideLoading()
}
}).show()
} }
</script> </script>

View File

@ -2,9 +2,9 @@
<div class="edit-item"> <div class="edit-item">
<BaseHeader @back="back"> <BaseHeader @back="back">
<template v-slot:center> <template v-slot:center>
<span v-if="type === 1" class="f16">修改名字</span> <span v-if="data.type === 1" class="f16">修改名字</span>
<span v-if="type === 2" class="f16">修改抖音号</span> <span v-if="data.type === 2" class="f16">修改抖音号</span>
<span v-if="type === 3" class="f16">修改简介</span> <span v-if="data.type === 3" class="f16">修改简介</span>
</template> </template>
<template v-slot:right> <template v-slot:right>
<div> <div>
@ -14,37 +14,37 @@
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<div v-if="type === 1"> <div v-if="data.type === 1">
<div class="notice">我的名字</div> <div class="notice">我的名字</div>
<div class="input-ctn" style="margin-bottom: 1rem"> <div class="input-ctn" style="margin-bottom: 1rem">
<input type="text" v-model="localUserinfo.nickname" placeholder="记得填写名字哦" /> <input type="text" v-model="data.localUserinfo.nickname" placeholder="记得填写名字哦" />
<img <img
v-if="localUserinfo.nickname" v-if="data.localUserinfo.nickname"
style="transform: scale(2)" style="transform: scale(2)"
class="close" class="close"
src="../../../assets/img/icon/newicon/close-and-bg.png" src="../../../assets/img/icon/newicon/close-and-bg.png"
alt="" alt=""
@click="localUserinfo.nickname = ''" @click="data.localUserinfo.nickname = ''"
/> />
</div> </div>
<div class="num">{{ localUserinfo.nickname.length }}/20</div> <div class="num">{{ data.localUserinfo.nickname.length }}/20</div>
</div> </div>
<div class="l-row" v-if="type === 2"> <div class="l-row" v-if="data.type === 2">
<div class="notice">我的抖音号</div> <div class="notice">我的抖音号</div>
<div class="input-ctn" style="margin-bottom: 10rem"> <div class="input-ctn" style="margin-bottom: 10rem">
<input type="text" v-model="localUserinfo.unique_id" /> <input type="text" v-model="data.localUserinfo.unique_id" />
<img <img
v-if="localUserinfo.unique_id" v-if="data.localUserinfo.unique_id"
style="transform: scale(2)" style="transform: scale(2)"
class="close" class="close"
src="../../../assets/img/icon/newicon/close-and-bg.png" src="../../../assets/img/icon/newicon/close-and-bg.png"
alt="" alt=""
@click="localUserinfo.unique_id = ''" @click="data.localUserinfo.unique_id = ''"
/> />
</div> </div>
<div class="num">最多16个字只允许包含字母数字下划线和点30天内仅能修改一次</div> <div class="num">最多16个字只允许包含字母数字下划线和点30天内仅能修改一次</div>
</div> </div>
<div class="l-row" v-if="type === 3"> <div class="l-row" v-if="data.type === 3">
<div class="notice">个人简介</div> <div class="notice">个人简介</div>
<div class="textarea-ctn"> <div class="textarea-ctn">
<textarea <textarea
@ -52,7 +52,7 @@
id="" id=""
cols="30" cols="30"
rows="10" rows="10"
v-model="localUserinfo.signature" v-model="data.localUserinfo.signature"
placeholder="你可以填写兴趣爱好、心情愿望,有趣的介绍能让被关注的概率变高噢!" placeholder="你可以填写兴趣爱好、心情愿望,有趣的介绍能让被关注的概率变高噢!"
></textarea> ></textarea>
</div> </div>
@ -61,60 +61,68 @@
</div> </div>
</template> </template>
<script> <script setup lang="ts">
//TODO 12 //TODO 12
import { mapState } from 'pinia'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { cloneDeep } from '@/utils' import {
_hideLoading,
_notice,
_showLoading,
_showSimpleConfirmDialog,
_sleep,
cloneDeep
} from '@/utils'
import { computed, onMounted, reactive } from 'vue'
import { useRoute, useRouter } from 'vue-router'
export default { defineOptions({
name: 'EditUserInfo', name: 'EditUserInfo'
setup() { })
const baseStore = useBaseStore() const store = useBaseStore()
return { baseStore } const router = useRouter()
}, const route = useRoute()
data() { const data = reactive({
return { type: 1,
type: 1, localUserinfo: {
localUserinfo: {} nickname: '',
} signature: '',
}, unique_id: '',
computed: { desc: ''
isChanged() {
if (this.type === 1) if (!this.localUserinfo.nickname) return false
if (this.type === 2) if (!this.localUserinfo.desc) return false
if (this.userinfo.nickname !== this.localUserinfo.nickname) return true
if (this.userinfo.desc !== this.localUserinfo.desc) return true
return this.userinfo.unique_id !== this.localUserinfo.unique_id
},
...mapState(useBaseStore, ['userinfo'])
},
created() {
this.localUserinfo = cloneDeep(this.userinfo)
this.type = Number(this.$route.query.type)
},
methods: {
back() {
if (this.isChanged) {
this.$showSimpleConfirmDialog('是否保存修改', this.save, this.$back)
} else {
this.$back()
}
},
async save() {
if (!this.isChanged) return
if (this.type === 1) {
if (!this.localUserinfo.nickname) return this.$notice('名字不能为空')
}
this.$showLoading()
this.baseStore.setUserinfo(this.localUserinfo)
await this.$sleep(500)
this.$hideLoading()
this.$back()
if (this.type === 3) return this.$notice('新签名保存成功')
}
} }
})
const isChanged = computed(() => {
if (data.type === 1) if (!data.localUserinfo.nickname) return false
if (data.type === 2) if (!data.localUserinfo.desc) return false
if (store.userinfo.nickname !== data.localUserinfo.nickname) return true
if (store.userinfo.desc !== data.localUserinfo.desc) return true
return store.userinfo.unique_id !== data.localUserinfo.unique_id
})
onMounted(() => {
data.localUserinfo = cloneDeep(store.userinfo)
data.type = Number(route.query.type)
})
function back() {
if (isChanged.value) {
_showSimpleConfirmDialog('是否保存修改', save, router.back)
} else {
router.back()
}
}
async function save() {
if (!isChanged.value) return
if (data.type === 1) {
if (!data.localUserinfo.nickname) return _notice('名字不能为空')
}
_showLoading()
store.setUserinfo(data.localUserinfo)
await _sleep(500)
_hideLoading()
router.back()
if (data.type === 3) return _notice('新签名保存成功')
} }
</script> </script>

View File

@ -2,61 +2,65 @@
<div id="AllMessage"> <div id="AllMessage">
<BaseHeader> <BaseHeader>
<template v-slot:center> <template v-slot:center>
<div class="center" @click="isShowType = !isShowType"> <div class="center" @click="data.isShowType = !data.isShowType">
<span class="f16">{{ showTypeText }}</span> <span class="f16">{{ showTypeText }}</span>
<img <img
:class="{ show: isShowType }" :class="{ show: data.isShowType }"
src="../../assets/img/icon/arrow-up-white.png" src="@/assets/img/icon/arrow-up-white.png"
alt="" alt=""
/> />
</div> </div>
</template> </template>
</BaseHeader> </BaseHeader>
<transition name="fade"> <transition name="fade">
<div class="type-dialog" v-if="isShowType"> <div class="type-dialog" v-if="data.isShowType">
<div class="dialog-content"> <div class="dialog-content">
<div class="row" @click="toggleShowType(1)"> <div class="row" @click="toggleShowType(1)">
<div class="left"> <div class="left">
<img src="../../assets/img/icon/message/done-gray.png" alt="" /> <img src="@/assets/img/icon/message/done-gray.png" alt="" />
<span>全部消息</span> <span>全部消息</span>
</div> </div>
</div> </div>
<div class="row" @click="toggleShowType(2)"> <div class="row" @click="toggleShowType(2)">
<div class="left"> <div class="left">
<img src="../../assets/img/icon/message/like-gray.png" alt="" /> <img src="@/assets/img/icon/message/like-gray.png" alt="" />
<span></span> <span></span>
</div> </div>
</div> </div>
<div class="row" @click="toggleShowType(3)"> <div class="row" @click="toggleShowType(3)">
<div class="left"> <div class="left">
<img src="../../assets/img/icon/message/call-gray.png" alt="" /> <img src="@/assets/img/icon/message/call-gray.png" alt="" />
<span>@我的</span> <span>@我的</span>
</div> </div>
</div> </div>
<div class="row" @click="toggleShowType(4)"> <div class="row" @click="toggleShowType(4)">
<div class="left"> <div class="left">
<img src="../../assets/img/icon/message/comment-gray.png" alt="" /> <img src="@/assets/img/icon/message/comment-gray.png" alt="" />
<span>评论</span> <span>评论</span>
</div> </div>
</div> </div>
</div> </div>
<div class="mask" @click="isShowType = false"></div> <div class="mask" @click="data.isShowType = false"></div>
</div> </div>
</transition> </transition>
<div class="content"> <div class="content">
<Loading v-if="loading" /> <Loading v-if="data.loading" />
<Scroll <Scroll
v-else v-else
ref="mainScroll" ref="mainScroll"
:use-refresh="true" :use-refresh="true"
:loading="loadingMore" :loading="data.loadingMore"
@refresh="refresh" @refresh="refresh"
@pulldown="loadData" @pulldown="loadData"
> >
<div class="messages"> <div class="messages">
<div class="message" @click="$nav('/message/visitors')"> <div class="message" @click="nav('/message/visitors')">
<div class="left"> <div class="left">
<img v-lazy="_checkImgUrl(userinfo.cover_url[0].url_list[0])" alt="" class="avatar" /> <img
v-lazy="_checkImgUrl(store.userinfo.cover_url[0].url_list[0])"
alt=""
class="avatar"
/>
</div> </div>
<div class="right"> <div class="right">
<div class="desc"> <div class="desc">
@ -68,27 +72,31 @@
<div class="time">01-11</div> <div class="time">01-11</div>
</div> </div>
</div> </div>
<img v-lazy="_checkImgUrl(userinfo.cover_url[0].url_list[0])" alt="" class="poster" /> <img
v-lazy="_checkImgUrl(store.userinfo.cover_url[0].url_list[0])"
alt=""
class="poster"
/>
</div> </div>
</div> </div>
<div class="message" :key="i" v-for="(item, i) in showMessageList" @click="$no"> <div class="message" :key="i" v-for="(item, i) in showMessageList" @click="_no">
<div class="left"> <div class="left">
<img v-lazy="$imgPreview(item.author.avatar)" alt="" class="avatar" /> <img v-lazy="_checkImgUrl(item.author.avatar)" alt="" class="avatar" />
<img <img
v-if="selectShowType === 2" v-if="data.selectShowType === 2"
src="../../assets/img/icon/message/love-message.webp" src="@/assets/img/icon/message/love-message.webp"
alt="" alt=""
class="type" class="type"
/> />
<img <img
v-if="selectShowType === 3" v-if="data.selectShowType === 3"
src="../../assets/img/icon/message/call-message.webp" src="@/assets/img/icon/message/call-message.webp"
alt="" alt=""
class="type" class="type"
/> />
<img <img
v-if="selectShowType === 4" v-if="data.selectShowType === 4"
src="../../assets/img/icon/message/comment-message.webp" src="@/assets/img/icon/message/comment-message.webp"
alt="" alt=""
class="type" class="type"
/> />
@ -100,130 +108,130 @@
<div class="tag">朋友</div> <div class="tag">朋友</div>
</div> </div>
<div class="desc-content"> <div class="desc-content">
<span v-if="selectShowType === 1">好好看啊</span> <span v-if="data.selectShowType === 1">好好看啊</span>
<span v-if="selectShowType === 2">赞了你的作品</span> <span v-if="data.selectShowType === 2">赞了你的作品</span>
<span v-if="selectShowType === 3">@{{ userinfo.nickname }}</span> <span v-if="data.selectShowType === 3">@{{ store.userinfo.nickname }}</span>
<span v-if="selectShowType === 4">好好看啊</span> <span v-if="data.selectShowType === 4">好好看啊</span>
</div> </div>
<div class="bottom"> <div class="bottom">
<div class="type" v-if="selectShowType === 3">在评论中提到了你</div> <div class="type" v-if="data.selectShowType === 3">在评论中提到了你</div>
<div class="type" v-if="selectShowType === 4">回复了你的评论</div> <div class="type" v-if="data.selectShowType === 4">回复了你的评论</div>
<div class="time">01-11</div> <div class="time">01-11</div>
</div> </div>
</div> </div>
<img <img
v-lazy="$imgPreview(item.video + '?vframe/jpg/offset/0/w/300')" v-lazy="_checkImgUrl(item.video + '?vframe/jpg/offset/0/w/300')"
alt="" alt=""
class="poster" class="poster"
/> />
</div> </div>
</div> </div>
<div class="look-all" v-if="!showAll" @click="showAll = true"> <div class="look-all" v-if="!data.showAll" @click="data.showAll = true">
<span>查看全部</span> <span>查看全部</span>
<dy-back /> <dy-back />
</div> </div>
</div> </div>
<div class="title"> <div class="title">
<span>朋友推荐</span> <span>朋友推荐</span>
<img src="../../assets/img/icon/about-gray.png" alt="" /> <img src="@/assets/img/icon/about-gray.png" alt="" />
</div> </div>
<Peoples v-model:list="recommend" :loading="loadingMore" mode="recommend" /> <Peoples v-model:list="data.recommend" :loading="data.loadingMore" mode="recommend" />
</Scroll> </Scroll>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapState } from 'pinia' import Scroll from '@/components/Scroll.vue'
import Scroll from '../../components/Scroll' import Loading from '@/components/Loading.vue'
import Loading from '../../components/Loading' import Peoples from '../people/components/Peoples.vue'
import Peoples from '../people/components/Peoples' import resource from '@/assets/data/resource.js'
import resource from '../../assets/data/resource.js'
import BasePage from '../BasePage'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl } from '@/utils' import { _checkImgUrl, _no, _notice, _sleep, cloneDeep } from '@/utils'
export default { import { computed, onMounted, reactive } from 'vue'
extends: BasePage, import { useNav } from '@/utils/hooks/useNav.js'
name: 'AllMessage',
components: { defineOptions({
Scroll, name: 'AllMessage'
Loading, })
Peoples
}, const store = useBaseStore()
data() { const nav = useNav()
return { const data = reactive({
loading: false, loading: false,
loadingMore: false, loadingMore: false,
isShowType: false, isShowType: false,
showAll: false, showAll: false,
recommend: [], recommend: [],
messages: [], fans: [],
selectShowType: 1 messages: [],
} selectShowType: 1
}, })
computed: {
...mapState(useBaseStore, ['friends', 'userinfo']), onMounted(() => {
showTypeText() { getData()
switch (this.selectShowType) { })
case 1:
return '全部消息' const showTypeText = computed(() => {
case 2: switch (data.selectShowType) {
return '赞' case 1:
case 3: return '全部消息'
return '@我的' case 2:
case 4: return '赞'
return '评论' case 3:
default: return '@我的'
return '' case 4:
} return '评论'
}, default:
showMessageList() { return ''
if (this.showAll) {
return this.messages
}
return this.messages.slice(0, 2)
}
},
created() {
this.getData()
},
methods: {
_checkImgUrl,
async getData() {
this.loading = true
await this.$sleep(800)
this.loading = false
this.recommend = this.$clone(this.friends.all)
this.fans = this.$clone(this.friends.all)
this.recommend.map((v) => {
v.type = -1
})
this.messages = this.$clone(resource.videos)
},
toggleShowType(index) {
this.selectShowType = index
this.isShowType = false
},
remove(index) {
this.$notice('将不会再为你推荐该用户')
this.recommend.splice(index, 1)
},
async refresh() {
await this.$sleep(1000)
this.$refs.mainScroll.refreshEnd()
},
async loadData() {
if (this.loadingMore) return
this.loadingMore = true
await this.$sleep(500)
this.loadingMore = false
let temp = this.$clone(this.friends.all)
temp.map((v) => {
v.type = -1
})
this.recommend = this.recommend.concat(temp)
}
} }
})
const showMessageList = computed(() => {
if (data.showAll) {
return data.messages
}
return data.messages.slice(0, 2)
})
async function getData() {
data.loading = true
await _sleep(800)
data.loading = false
data.recommend = cloneDeep(store.friends.all)
data.fans = cloneDeep(store.friends.all)
data.recommend.map((v) => {
v.type = -1
})
data.messages = cloneDeep(resource.videos)
}
function toggleShowType(index) {
data.selectShowType = index
data.isShowType = false
}
// function remove(index) {
// _notice('')
// data.recommend.splice(index, 1)
// }
async function refresh() {
await _sleep(1000)
//TODO
// data.$refs.mainScroll.refreshEnd()
}
async function loadData() {
if (data.loadingMore) return
data.loadingMore = true
await _sleep(500)
data.loadingMore = false
let temp = cloneDeep(store.friends.all)
temp.map((v) => {
v.type = -1
})
data.recommend = data.recommend.concat(temp)
} }
</script> </script>

View File

@ -6,8 +6,12 @@
</template> </template>
<template v-slot:right> <template v-slot:right>
<div> <div>
<span class="f16" :class="selectFriends.length ? 'save-yes' : 'save-no'" @click="save"> <span
完成{{ selectFriends.length ? `(${selectFriends.length})` : '' }} class="f16"
:class="data.selectFriends.length ? 'save-yes' : 'save-no'"
@click="save"
>
完成{{ data.selectFriends.length ? `(${data.selectFriends.length})` : '' }}
</span> </span>
</div> </div>
</template> </template>
@ -17,11 +21,11 @@
<div <div
class="local-row" class="local-row"
:key="i" :key="i"
v-for="(item, i) of friends.all" v-for="(item, i) of data.friends.all"
@click="toggleSelect(item)" @click="toggleSelect(item)"
> >
<Check mode="red" v-model="item.select" /> <Check mode="red" v-model="item.select" />
<img :src="$imgPreview(item.avatar)" alt="" /> <img :src="_checkImgUrl(item.avatar)" alt="" />
<div class="desc"> <div class="desc">
<span class="name">{{ <span class="name">{{
item.name.length > 20 ? item.name.substr(0, 20) + '...' : item.name item.name.length > 20 ? item.name.substr(0, 20) + '...' : item.name
@ -34,62 +38,63 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Check from '../../components/Check' import Check from '../../components/Check.vue'
import { friends } from '@/api/user' import { friends } from '@/api/user'
export default { import { onMounted, reactive } from 'vue'
name: 'Share2Friend', import { useRouter } from 'vue-router'
components: { Check }, import { _checkImgUrl } from '@/utils'
props: {},
computed: { defineOptions({
// ...mapState(['friends']), name: 'JoinedGroupChat'
})
const router = useRouter()
const data = reactive({
friends: {
all: {},
recent: [],
eachOther: []
}, },
data() { selectFriends: []
return { })
friends: {
all: {}, onMounted(() => {
recent: [], getFriends()
eachOther: [] })
},
selectFriends: [] function save() {
} if (!data.selectFriends.length) return
}, router.back()
created() { }
this.getFriends()
}, function toggleSelect(item) {
methods: { let resIndex = data.selectFriends.findIndex((v) => v.name === item.name)
save() { if (resIndex !== -1) {
if (!this.selectFriends.length) return item.select = false
this.$back() data.selectFriends.splice(resIndex, 1)
}, } else {
toggleSelect(item) { item.select = true
let resIndex = this.selectFriends.findIndex((v) => v.name === item.name) data.selectFriends.push(item)
if (resIndex !== -1) { }
item.select = false }
this.selectFriends.splice(resIndex, 1)
} else { async function getFriends() {
item.select = true let res = await friends()
this.selectFriends.push(item) if (res.success) {
} data.friends = res.data
}, data.friends.all = data.friends.all.sort((a, b) => {
async getFriends() { if (a.pinyin < b.pinyin) return -1
let res = await friends() if (a.pinyin > b.pinyin) return 1
if (res.code === this.SUCCESS) { return 0
this.friends = res.data })
this.friends.all = this.friends.all.sort((a, b) => {
if (a.pinyin < b.pinyin) return -1
if (a.pinyin > b.pinyin) return 1
return 0
})
}
}
} }
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
@import '../../assets/less/index'; @import '@/assets/less/index';
.Share2Friend { .Share2Friend {
position: fixed; position: fixed;

View File

@ -1,22 +1,22 @@
<template> <template>
<div id="Message" ref="app" :class="createChatDialog ? 'disable-scroll' : ''"> <div id="Message" ref="app" :class="data.createChatDialog ? 'disable-scroll' : ''">
<div class="no-search" v-show="!searching"> <div class="no-search" v-show="!data.searching">
<header> <header>
<Icon @click="createChatDialog = true" icon="formkit:add" /> <Icon @click="data.createChatDialog = true" icon="formkit:add" />
<Icon icon="tabler:camera-selfie" /> <Icon icon="tabler:camera-selfie" />
<Icon @click="searching = true" icon="tabler:search" /> <Icon @click="data.searching = true" icon="tabler:search" />
</header> </header>
<Scroll ref="mainScroll"> <Scroll ref="mainScroll">
<div class="friends pl1r"> <div class="friends pl1r">
<div <div
class="friend pr1r pl1r" class="friend pr1r pl1r"
@click="$nav('/message/chat')" @click="nav('/message/chat')"
:key="index" :key="index"
v-for="(item, index) in friends.all" v-for="(item, index) in store.friends.all"
> >
<div class="avatar" :class="index % 2 === 0 ? 'on-line' : ''"> <div class="avatar" :class="index % 2 === 0 ? 'on-line' : ''">
<img :src="$imgPreview(item.avatar)" alt="" /> <img :src="_checkImgUrl(item.avatar)" alt="" />
</div> </div>
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
</div> </div>
@ -28,7 +28,7 @@
<div class="line mt2r"></div> <div class="line mt2r"></div>
<div class="messages"> <div class="messages">
<!-- 粉丝--> <!-- 粉丝-->
<div class="message" @click="$nav('/message/fans')"> <div class="message" @click="nav('/message/fans')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon1.png" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon1.png" alt="" class="head-image" />
</div> </div>
@ -45,7 +45,7 @@
</div> </div>
</div> </div>
<!-- 互动消息--> <!-- 互动消息-->
<div class="message" @click="$nav('/message/all')"> <div class="message" @click="nav('/message/all')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon2.png" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon2.png" alt="" class="head-image" />
</div> </div>
@ -62,14 +62,14 @@
</div> </div>
</div> </div>
<!-- 消息--> <!-- 消息-->
<div class="message" @click="$nav('/message/chat')"> <div class="message" @click="nav('/message/chat')">
<div class="avatar on-line"> <div class="avatar on-line">
<img src="../../assets/img/icon/avatar/2.png" alt="" class="head-image" /> <img src="../../assets/img/icon/avatar/2.png" alt="" class="head-image" />
</div> </div>
<div class="content"> <div class="content">
<div class="left"> <div class="left">
<div class="name"> <div class="name">
<span>{{ userinfo.nickname }}</span> <span>{{ store.userinfo.nickname }}</span>
</div> </div>
<div class="detail"> <div class="detail">
哈哈哈哈哈哈 哈哈哈哈哈哈
@ -86,7 +86,7 @@
</div> </div>
</div> </div>
<!-- 抖音小助手--> <!-- 抖音小助手-->
<div class="message" @click="$nav('/message/douyin-helper')"> <div class="message" @click="nav('/message/douyin-helper')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon5.webp" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon5.webp" alt="" class="head-image" />
</div> </div>
@ -108,7 +108,7 @@
</div> </div>
</div> </div>
<!-- 系统通知--> <!-- 系统通知-->
<div class="message" @click="$nav('/message/system-notice')"> <div class="message" @click="nav('/message/system-notice')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon4.png" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon4.png" alt="" class="head-image" />
</div> </div>
@ -130,7 +130,7 @@
</div> </div>
</div> </div>
<!-- 求更新--> <!-- 求更新-->
<div class="message" @click="$nav('/me/request-update')"> <div class="message" @click="nav('/me/request-update')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon7.webp" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon7.webp" alt="" class="head-image" />
</div> </div>
@ -152,7 +152,7 @@
</div> </div>
</div> </div>
<!-- 任务通知--> <!-- 任务通知-->
<div class="message" @click="$nav('/message/task-notice')"> <div class="message" @click="nav('/message/task-notice')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon6.webp" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon6.webp" alt="" class="head-image" />
</div> </div>
@ -174,7 +174,7 @@
</div> </div>
</div> </div>
<!-- 直播通知--> <!-- 直播通知-->
<div class="message" @click="$nav('/message/live-notice')"> <div class="message" @click="nav('/message/live-notice')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon8.webp" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon8.webp" alt="" class="head-image" />
</div> </div>
@ -196,7 +196,7 @@
</div> </div>
</div> </div>
<!-- 钱包通知--> <!-- 钱包通知-->
<div class="message" @click="$nav('/message/money-notice')"> <div class="message" @click="nav('/message/money-notice')">
<div class="avatar"> <div class="avatar">
<img src="../../assets/img/icon/msg-icon9.webp" alt="" class="head-image" /> <img src="../../assets/img/icon/msg-icon9.webp" alt="" class="head-image" />
</div> </div>
@ -246,23 +246,23 @@
<!-- </div>--> <!-- </div>-->
</div> </div>
</Scroll> </Scroll>
<from-bottom-dialog page-id="Message" v-model="createChatDialog"> <from-bottom-dialog page-id="Message" v-model="data.createChatDialog">
<div class="create-chat-wrapper" v-show="!showJoinedChat"> <div class="create-chat-wrapper" v-show="!data.showJoinedChat">
<Search <Search
:isShowRightText="isShowRightText" :isShowRightText="data.isShowRightText"
@click="isShowRightText = true" @click="data.isShowRightText = true"
@notice="isShowRightText = false" @notice="data.isShowRightText = false"
@clear="isShowRightText = false" @clear="data.isShowRightText = false"
class="ml2r mr2r" class="ml2r mr2r"
placeholder="搜索用户" placeholder="搜索用户"
v-model="createChatSearchKey" v-model="data.createChatSearchKey"
></Search> ></Search>
<template v-if="createChatSearchKey"> <template v-if="data.createChatSearchKey">
<div class="search-result" v-if="searchFriends.length"> <div class="search-result" v-if="data.searchFriends.length">
<div <div
class="search-result-item" class="search-result-item"
:key="i" :key="i"
v-for="(item, i) in searchFriends" v-for="(item, i) in data.searchFriends"
@click="handleClick(item)" @click="handleClick(item)"
> >
<img class="left" src="../../assets/img/icon/head-image.jpeg" alt="" /> <img class="left" src="../../assets/img/icon/head-image.jpeg" alt="" />
@ -286,7 +286,7 @@
</div> </div>
</template> </template>
<template v-else> <template v-else>
<div class="joined-chat" @click="showJoinedChat = true"> <div class="joined-chat" @click="data.showJoinedChat = true">
<img class="left" src="../../assets/img/icon/people-gray.png" alt="" /> <img class="left" src="../../assets/img/icon/people-gray.png" alt="" />
<div class="right"> <div class="right">
<span>已加入的群聊</span> <span>已加入的群聊</span>
@ -298,10 +298,10 @@
<div <div
class="friend-item" class="friend-item"
:key="i" :key="i"
v-for="(item, i) in friends.all" v-for="(item, i) in store.friends.all"
@click="item.select = !item.select" @click="item.select = !item.select"
> >
<img class="left" :src="$imgPreview(item.avatar)" alt="" /> <img class="left" :src="_checkImgUrl(item.avatar)" alt="" />
<div class="right"> <div class="right">
<span>{{ item.name }}</span> <span>{{ item.name }}</span>
<Check mode="red" style="height: 20rem; width: 20rem" v-model="item.select" /> <Check mode="red" style="height: 20rem; width: 20rem" v-model="item.select" />
@ -313,9 +313,9 @@
<div class="btn" :class="selectFriends ? 'primary' : ''">发起聊天</div> <div class="btn" :class="selectFriends ? 'primary' : ''">发起聊天</div>
</div> </div>
</div> </div>
<div class="joined-chat-wrapper" v-show="showJoinedChat"> <div class="joined-chat-wrapper" v-show="data.showJoinedChat">
<div class="nav"> <div class="nav">
<dy-back @click="showJoinedChat = false" mode="light" scale="1.2"></dy-back> <dy-back @click="data.showJoinedChat = false" mode="light" scale="1.2"></dy-back>
<span>已加入的群聊</span> <span>已加入的群聊</span>
<span>&nbsp;</span> <span>&nbsp;</span>
</div> </div>
@ -326,7 +326,7 @@
<div class="right"> <div class="right">
<div class="title"> <div class="title">
<div class="name"> <div class="name">
{{ text.length > 20 ? text.substr(0, 20) + '...' : text }} {{ data.text.length > 20 ? data.text.substr(0, 20) + '...' : data.text }}
</div> </div>
<div class="num">(3)</div> <div class="num">(3)</div>
</div> </div>
@ -339,7 +339,7 @@
</from-bottom-dialog> </from-bottom-dialog>
<transition name="fade"> <transition name="fade">
<div class="recommend-dialog" v-if="isShowRecommend"> <div class="recommend-dialog" v-if="data.isShowRecommend">
<div class="dialog-content"> <div class="dialog-content">
<div class="dialog-header"> <div class="dialog-header">
<img <img
@ -352,15 +352,15 @@
<img src="../../assets/img/icon/about-gray.png" alt="" /> <img src="../../assets/img/icon/about-gray.png" alt="" />
</div> </div>
<img <img
@click="isShowRecommend = false" @click="data.isShowRecommend = false"
src="../../assets/img/icon/components/gray-close-full2.png" src="../../assets/img/icon/components/gray-close-full2.png"
alt="" alt=""
/> />
</div> </div>
<div class="dialog-body"> <div class="dialog-body">
<Scroll ref="scroll" @pulldown="loadRecommendData"> <Scroll ref="scroll" @pulldown="loadRecommendData">
<Peoples v-model:list="recommend" :loading="loading" mode="recommend" /> <Peoples v-model:list="data.recommend" :loading="data.loading" mode="recommend" />
<Loading :is-full-screen="false" v-if="loading" /> <Loading :is-full-screen="false" v-if="data.loading" />
<NoMore v-else /> <NoMore v-else />
</Scroll> </Scroll>
</div> </div>
@ -371,22 +371,22 @@
<BaseFooter v-bind:init-tab="4" /> <BaseFooter v-bind:init-tab="4" />
</div> </div>
<div class="searching" v-show="searching"> <div class="searching" v-show="data.searching">
<Search <Search
v-model="searchKey" v-model="data.searchKey"
right-text="取消" right-text="取消"
right-text-color="white" right-text-color="white"
@notice="searching = false" @notice="data.searching = false"
:isShowRightText="true" :isShowRightText="true"
/> />
<div class="more-chat"> <div class="more-chat">
<template v-if="searchKey"> <template v-if="data.searchKey">
<div class="sub-title" v-if="searchFriendsAll.length"> <div class="sub-title" v-if="searchFriendsAll.length">
<span>联系人</span> <span>联系人</span>
<div <div
class="right" class="right"
v-if="searchFriendsAll.length > 3" v-if="searchFriendsAll.length > 3"
@click="$nav('/message/more-search', { key: searchKey })" @click="nav('/message/more-search', { key: data.searchKey })"
> >
<span>更多联系人</span> <span>更多联系人</span>
<dy-back mode="gray" img="back" scale=".6" direction="right" /> <dy-back mode="gray" img="back" scale=".6" direction="right" />
@ -396,15 +396,15 @@
v-for="item in searchFriendsAll.slice(0, 3)" v-for="item in searchFriendsAll.slice(0, 3)"
:key="item.id" :key="item.id"
mode="search" mode="search"
:searchKey="searchKey" :searchKey="data.searchKey"
:people="item" :people="item"
/> />
<div class="goto-search-page" @click="$nav('/home/search', { key: searchKey })"> <div class="goto-search-page" @click="nav('/home/search', { key: data.searchKey })">
<img class="icon" src="../../assets/img/icon/search-light.png" alt="" /> <img class="icon" src="../../assets/img/icon/search-light.png" alt="" />
<div class="right"> <div class="right">
<div class="left"> <div class="left">
<span <span
>搜索 <span style="color: yellow">{{ searchKey }}</span></span >搜索 <span style="color: yellow">{{ data.searchKey }}</span></span
> >
<span class="second-text-color f12">视频用户音乐话题地点等</span> <span class="second-text-color f12">视频用户音乐话题地点等</span>
</div> </div>
@ -414,105 +414,96 @@
</template> </template>
<template v-else> <template v-else>
<div class="sub-title">更多聊天</div> <div class="sub-title">更多聊天</div>
<People v-for="item in moreChat" :key="item.id" :people="item" /> <People v-for="item in data.moreChat" :key="item.id" :people="item" />
</template> </template>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Search from '../../components/Search' import Search from '../../components/Search.vue'
import FromBottomDialog from '../../components/dialog/FromBottomDialog' import FromBottomDialog from '../../components/dialog/FromBottomDialog.vue'
import Check from '../../components/Check' import Check from '../../components/Check.vue'
import { mapState } from 'pinia' import Peoples from '../people/components/Peoples.vue'
import Peoples from '../people/components/Peoples' import People from '../people/components/Peoples.vue'
import Scroll from '../../components/Scroll' import Scroll from '../../components/Scroll.vue'
import People from '../people/components/People'
import BasePage from '../BasePage'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
export default { import { computed, onMounted, reactive, watch } from 'vue'
extends: BasePage, import { useNav } from '@/utils/hooks/useNav.js'
name: 'Message', import { _checkImgUrl, _sleep, cloneDeep } from '@/utils'
components: {
Scroll, defineOptions({
Search, name: 'Message'
FromBottomDialog, })
Check,
Peoples, const store = useBaseStore()
People const nav = useNav()
}, const data = reactive({
data() { isShowRecommend: false,
return { searching: false,
isShowRecommend: false, searchKey: '',
searching: false, createChatSearchKey: '',
searchKey: '', showJoinedChat: false,
createChatSearchKey: '', loading: false,
showJoinedChat: false, createChatDialog: false,
loading: false, isShowRightText: false,
createChatDialog: false, text: 'AAAAAAAAA、BBBBBBBBBBBBB、CCCCCCCC',
isShowRightText: false, searchFriends: [],
text: 'AAAAAAAAA、BBBBBBBBBBBBB、CCCCCCCC', recommend: [],
searchFriends: [], moreChat: []
recommend: [], })
moreChat: []
} onMounted(() => {
}, console.log('create')
computed: { data.recommend = cloneDeep(store.friends.all)
...mapState(useBaseStore, ['friends', 'userinfo']), data.recommend.map((v) => {
selectFriends() { v.type = -2
return this.friends.all.filter((v) => v.select).length })
}, data.moreChat = cloneDeep(store.friends.all.slice(0, 3))
searchFriendsAll() { })
return this.friends.all.filter((v) => {
return v.name.search(this.searchKey) !== -1 || v.account.search(this.searchKey) !== -1 const selectFriends = computed(() => {
return store.friends.all.filter((v) => v.select).length
})
const searchFriendsAll = computed(() => {
return store.friends.all.filter((v) => {
return v.name.search(data.searchKey) !== -1 || v.account.search(data.searchKey) !== -1
})
})
watch(
() => data.createChatSearchKey,
(newVal) => {
if (newVal) {
//TODO
data.searchFriends = store.friends.all.filter((v) => {
if (v.name.includes(newVal)) return true
return v.account.includes(newVal)
}) })
} } else {
}, data.searchFriends = []
watch: {
createChatSearchKey(newVal) {
if (newVal) {
//TODO
this.searchFriends = this.friends.all.filter((v) => {
if (v.name.includes(newVal)) return true
return v.account.includes(newVal)
})
} else {
this.searchFriends = []
}
}
},
created() {
console.log('create')
this.recommend = this.$clone(this.friends.all)
this.recommend.map((v) => {
v.type = -2
})
this.moreChat = this.$clone(this.friends.all.slice(0, 3))
},
mounted() {
setTimeout(() => {
// this.isShowRecommend = true
}, 1000)
},
methods: {
handleClick(item) {
item.select = !item.select
this.createChatSearchKey = ''
},
async loadRecommendData() {
if (this.loading) return
this.loading = true
await this.$sleep(500)
this.loading = false
let temp = this.$clone(this.friends.all)
temp.map((v) => {
v.type = -2
})
this.recommend = this.recommend.concat(temp)
} }
} }
)
function handleClick(item) {
item.select = !item.select
data.createChatSearchKey = ''
}
async function loadRecommendData() {
if (data.loading) return
data.loading = true
await _sleep(500)
data.loading = false
let temp = cloneDeep(store.friends.all)
temp.map((v) => {
v.type = -2
})
data.recommend = data.recommend.concat(temp)
} }
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -2,53 +2,50 @@
<div id="MoreSearch"> <div id="MoreSearch">
<div class="content"> <div class="content">
<Search <Search
v-model="searchKey" v-model="data.searchKey"
right-text="取消" right-text="取消"
right-text-color="white" right-text-color="white"
@notice="$back" @notice="router.back"
:isShowRightText="true" :isShowRightText="true"
/> />
<People <People
v-for="item in searchFriendsAll" v-for="item in searchFriendsAll"
:key="item.id" :key="item.id"
mode="search" mode="search"
:searchKey="searchKey" :searchKey="data.searchKey"
:people="item" :people="item"
/> />
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import Search from '../../components/Search' import Search from '@/components/Search.vue'
import { mapState } from 'pinia' import People from '../people/components/Peoples.vue'
import People from '../people/components/People'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
export default { import { computed, onMounted, reactive } from 'vue'
name: 'MoreSearch', import { useNav } from '@/utils/hooks/useNav.js'
components: { import { useRoute, useRouter } from 'vue-router'
Search,
People defineOptions({
}, name: 'MoreSearch'
data() { })
return {
searchKey: '' const store = useBaseStore()
} const router = useRouter()
}, const route = useRoute()
computed: { const data = reactive({
...mapState(useBaseStore, ['friends', 'userinfo']), searchKey: ''
searchFriendsAll() { })
return this.friends.all.filter((v) => {
return v.name.search(this.searchKey) !== -1 || v.account.search(this.searchKey) !== -1 const searchFriendsAll = computed(() => {
}) return store.friends.all.filter((v) => {
} return v.name.search(data.searchKey) !== -1 || v.account.search(data.searchKey) !== -1
}, })
watch: {}, })
created() { onMounted(() => {
this.searchKey = this.$route.query.key data.searchKey = String(route.query.key)
}, })
methods: {}
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -5,12 +5,12 @@
<span class="f16">主页访客</span> <span class="f16">主页访客</span>
</template> </template>
<template v-slot:right> <template v-slot:right>
<span class="f14" @click="isShowSetting = !isShowSetting">设置</span> <span class="f14" @click="data.isShowSetting = !data.isShowSetting">设置</span>
</template> </template>
</BaseHeader> </BaseHeader>
<div class="content"> <div class="content">
<template v-if="realDisplay"> <template v-if="data.realDisplay">
<Peoples v-model:list="recommend" :loading="loading" mode="visitor" /> <Peoples v-model:list="data.recommend" :loading="false" mode="visitor" />
<NoMore /> <NoMore />
</template> </template>
<template v-else> <template v-else>
@ -18,7 +18,11 @@
<div class="header"> <div class="header">
<div class="wrapper"> <div class="wrapper">
<img src="../../assets/img/icon/message/display2.webp" alt="" class="icon1" /> <img src="../../assets/img/icon/message/display2.webp" alt="" class="icon1" />
<img :src="_checkImgUrl(userinfo.cover_url[0].url_list[0])" alt="" class="icon2" /> <img
:src="_checkImgUrl(store.userinfo.cover_url[0].url_list[0])"
alt=""
class="icon2"
/>
<img src="../../assets/img/icon/message/display1.webp" alt="" class="icon3" /> <img src="../../assets/img/icon/message/display1.webp" alt="" class="icon3" />
</div> </div>
</div> </div>
@ -31,7 +35,9 @@
<div class="buttons"> <div class="buttons">
<base-button type="dark" @click="keepClose">保持关闭</base-button> <base-button type="dark" @click="keepClose">保持关闭</base-button>
<base-button type="primary" @click="display = realDisplay = true">开启访客</base-button> <base-button type="primary" @click="data.display = data.realDisplay = true"
>开启访客</base-button
>
</div> </div>
</div> </div>
</template> </template>
@ -39,7 +45,7 @@
<from-bottom-dialog <from-bottom-dialog
page-id="Visitors" page-id="Visitors"
v-model="isShowSetting" v-model="data.isShowSetting"
mode="white" mode="white"
mask-mode="dark" mask-mode="dark"
height="270rem" height="270rem"
@ -51,7 +57,7 @@
<img class="icon" src="../../assets/img/icon/message/peoples-black2.png" alt="" /> <img class="icon" src="../../assets/img/icon/message/peoples-black2.png" alt="" />
<transition name="remove"> <transition name="remove">
<img <img
v-if="!display" v-if="!data.display"
class="remove" class="remove"
src="../../assets/img/icon/message/remove.png" src="../../assets/img/icon/message/remove.png"
alt="" alt=""
@ -60,7 +66,7 @@
</div> </div>
<img <img
class="close" class="close"
@click="isShowSetting = false" @click="data.isShowSetting = false"
src="../../assets/img/icon/components/gray-close-full2.png" src="../../assets/img/icon/components/gray-close-full2.png"
alt="" alt=""
/> />
@ -73,60 +79,54 @@
<div class="row"> <div class="row">
<div class="left">展示主页访客</div> <div class="left">展示主页访客</div>
<div class="right"> <div class="right">
<switches v-model="display" theme="bootstrap" color="success"></switches> <switches v-model="data.display" theme="bootstrap" color="success"></switches>
</div> </div>
</div> </div>
</div> </div>
</from-bottom-dialog> </from-bottom-dialog>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { mapState } from 'pinia' import Peoples from '../people/components/Peoples.vue'
import Peoples from '../people/components/Peoples' import NoMore from '@/components/NoMore.vue'
import NoMore from '../../components/NoMore' import FromBottomDialog from '@/components/dialog/FromBottomDialog.vue'
import FromBottomDialog from '../../components/dialog/FromBottomDialog' import Switches from './components/swtich/switches.vue'
import Switches from './components/swtich/switches' import BaseButton from '@/components/BaseButton.vue'
import BaseButton from '../../components/BaseButton'
import { useBaseStore } from '@/store/pinia' import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl } from '@/utils' import { _checkImgUrl, _notice, cloneDeep } from '@/utils'
export default { import { onMounted, reactive, watch } from 'vue'
name: 'visitors', import { useRouter } from 'vue-router'
components: {
BaseButton, defineOptions({
FromBottomDialog, name: 'Visitors'
Peoples, })
NoMore,
Switches const store = useBaseStore()
}, const router = useRouter()
data() { const data = reactive({
return { recommend: [],
recommend: [], isShowSetting: false,
isShowSetting: false, display: false,
display: false, realDisplay: false
realDisplay: false })
}
}, onMounted(() => {
watch: { data.recommend = cloneDeep(store.friends.all)
isShowSetting(newVal) { })
if (!newVal) {
this.realDisplay = this.display watch(
} () => data.isShowSetting,
} (newVal) => {
}, if (!newVal) {
computed: { data.realDisplay = data.display
...mapState(useBaseStore, ['friends', 'userinfo'])
},
created() {
this.recommend = this.$clone(this.friends.all)
},
methods: {
_checkImgUrl,
keepClose() {
this.$notice('你将不会再收到相关通知')
this.$back()
} }
} }
)
function keepClose() {
_notice('你将不会再收到相关通知')
router.back()
} }
</script> </script>
@ -184,6 +184,7 @@ export default {
background: black; background: black;
border-radius: 50%; border-radius: 50%;
width: 80rem; width: 80rem;
height: 80rem;
} }
.icon3 { .icon3 {

View File

@ -5,13 +5,13 @@
<span class="f16">抖音小助手</span> <span class="f16">抖音小助手</span>
</template> </template>
</BaseHeader> </BaseHeader>
<Loading v-if="loading" /> <Loading v-if="data.loading" />
<Scroll v-else ref="mainScroll"> <Scroll v-else ref="mainScroll">
<div class="content"> <div class="content">
<NoMore /> <NoMore />
<div class="list"> <div class="list">
<!--TODO 超过3行显示全文--> <!--TODO 超过3行显示全文-->
<div class="item" :key="i" v-for="(item, i) in list" @click="goDetail(item)"> <div class="item" :key="i" v-for="(item, i) in data.list" @click="goDetail(item)">
<div class="title"> <div class="title">
{{ item.title }} {{ item.title }}
<div class="ml1r not-read" v-if="!item.read"></div> <div class="ml1r not-read" v-if="!item.read"></div>
@ -28,82 +28,78 @@
</Scroll> </Scroll>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { nextTick } from 'vue' import { nextTick, onMounted, reactive } from 'vue'
import Scroll from '../../../components/Scroll' import Scroll from '@/components/Scroll.vue'
import BasePage from '../../BasePage' import { _no, _sleep } from '@/utils'
export default { defineOptions({
extends: BasePage, name: 'DouyinHelper'
name: 'DouyinHelper', })
components: { Scroll },
data() { const data = reactive({
return { loading: false,
loading: false, list: [
list: [ {
{ read: false,
read: false, title: '叮!你有一条《长津湖》观看提醒',
title: '叮!你有一条《长津湖》观看提醒', time: '2021-10-12 12:12',
time: '2021-10-12 12:12', content:
content: '领跑国庆档,吴京、易烊千玺“京玺”兄弟共赴战场!燃爽炸裂的视觉冲击,义勇前行的坚定信念,尽在长津湖“意志之战”。点击查看详情,戳我优惠看>>'
'领跑国庆档,吴京、易烊千玺“京玺”兄弟共赴战场!燃爽炸裂的视觉冲击,义勇前行的坚定信念,尽在长津湖“意志之战”。点击查看详情,戳我优惠看>>'
},
{
read: false,
title: '国庆打卡美好中国',
time: '2021-10-12 12:12',
content:
'山河千年,风景依旧,一幅幅大美的城市山水画在国庆舞台中徐徐展开。点击[查看详情]在拼音打卡美好中国领跑不同城市的韵味最高还能赢10000元旅行红包哦'
},
{
read: false,
title: '#今天谁请客呢',
time: '2021-10-12 12:12',
content:
'你还在为朋友吃饭谁请客发愁吗?快邀请朋友一起来参加花式甩单挑战,今日消费,淘特请客!还不快来拍摄互动视频?现金大奖等你来分!'
},
{
read: false,
title: '#寻找武林第一人',
time: '2021-10-12 12:12',
content:
'《天涯明月刀手游》在线悬赏“武林第一人”。10月10日-10月19日内参与挑战生成你的专属卡面测一测你的武林专属称号吧天刀手游周年庆也在火热进行中全民福利等你拿! [本活动与Apple Inc.无关]'
},
{
read: false,
title: '谁是偷偷爱你的人',
time: '2021-10-12 12:12',
content:
'想知道怎么跟TA们走的更近吗10月11日-10月16日正好有一个合适的机会赶紧点击了解详情>>'
},
{
read: false,
title: '看美好奇妙夜,赢万元红包!',
time: '2021-10-12 12:12',
content:
'今天晚上8点2021抖音美好奇妙夜直播开启30多位艺人、100多位创作者齐聚一堂为你带来全方位的视听盛宴观看直播更有机会赢万元红包大奖更多精彩不容错过快来直接间看看吧 [本活动与Apple Inc.无关]'
}
]
}
},
computed: {},
created() {
this.getData()
},
mounted() {},
methods: {
async getData() {
this.loading = true
await this.$sleep(700)
this.loading = false
await nextTick()
this.$refs.mainScroll.scrollBottom()
}, },
goDetail(item) { {
item.read = true read: false,
this.$no() title: '国庆打卡美好中国',
time: '2021-10-12 12:12',
content:
'山河千年,风景依旧,一幅幅大美的城市山水画在国庆舞台中徐徐展开。点击[查看详情]在拼音打卡美好中国领跑不同城市的韵味最高还能赢10000元旅行红包哦'
},
{
read: false,
title: '#今天谁请客呢',
time: '2021-10-12 12:12',
content:
'你还在为朋友吃饭谁请客发愁吗?快邀请朋友一起来参加花式甩单挑战,今日消费,淘特请客!还不快来拍摄互动视频?现金大奖等你来分!'
},
{
read: false,
title: '#寻找武林第一人',
time: '2021-10-12 12:12',
content:
'《天涯明月刀手游》在线悬赏“武林第一人”。10月10日-10月19日内参与挑战生成你的专属卡面测一测你的武林专属称号吧天刀手游周年庆也在火热进行中全民福利等你拿! [本活动与Apple Inc.无关]'
},
{
read: false,
title: '谁是偷偷爱你的人',
time: '2021-10-12 12:12',
content:
'想知道怎么跟TA们走的更近吗10月11日-10月16日正好有一个合适的机会赶紧点击了解详情>>'
},
{
read: false,
title: '看美好奇妙夜,赢万元红包!',
time: '2021-10-12 12:12',
content:
'今天晚上8点2021抖音美好奇妙夜直播开启30多位艺人、100多位创作者齐聚一堂为你带来全方位的视听盛宴观看直播更有机会赢万元红包大奖更多精彩不容错过快来直接间看看吧 [本活动与Apple Inc.无关]'
} }
} ]
})
onMounted(() => {
getData()
})
async function getData() {
data.loading = true
await _sleep(700)
data.loading = false
await nextTick()
// data.$refs.mainScroll.scrollBottom()
}
function goDetail(item) {
item.read = true
_no()
} }
</script> </script>

View File

@ -5,18 +5,18 @@
<span class="f16">系统通知</span> <span class="f16">系统通知</span>
</template> </template>
<template v-slot:right> <template v-slot:right>
<span class="f14" @click="$nav('/message/notice-setting', { type: 'SYSTEM' })" <span class="f14" @click="nav('/message/notice-setting', { type: 'SYSTEM' })"
>通知设置</span >通知设置</span
> >
</template> </template>
</BaseHeader> </BaseHeader>
<Loading v-if="loading" /> <Loading v-if="data.loading" />
<div class="content" v-else> <div class="content" v-else>
<Scroll ref="mainScroll"> <Scroll ref="mainScroll">
<div class="list"> <div class="list">
<NoMore /> <NoMore />
<!--TODO 超过3行显示全文--> <!--TODO 超过3行显示全文-->
<div class="item" :key="i" v-for="(item, i) in list" @click="goDetail(item)"> <div class="item" :key="i" v-for="(item, i) in data.list" @click="goDetail(item)">
<div class="title"> <div class="title">
{{ item.title }} {{ item.title }}
<div class="ml1r not-read" v-if="!item.read"></div> <div class="ml1r not-read" v-if="!item.read"></div>
@ -32,30 +32,30 @@
</Scroll> </Scroll>
<!--TODO 子页面未做--> <!--TODO 子页面未做-->
<div class="hover-dialog left" v-if="isShowLeftHover"> <div class="hover-dialog left" v-if="data.isShowLeftHover">
<div class="arrow"></div> <div class="arrow"></div>
<div class="l-row no-border" @click="$no">登录设备管理</div> <div class="l-row no-border" @click="_no">登录设备管理</div>
<div class="l-row" @click="$no">账号锁定</div> <div class="l-row" @click="_no">账号锁定</div>
<div class="l-row" @click="$no">账号解锁</div> <div class="l-row" @click="_no">账号解锁</div>
</div> </div>
<div class="hover-dialog right" v-if="isShowRightHover"> <div class="hover-dialog right" v-if="data.isShowRightHover">
<div class="arrow"></div> <div class="arrow"></div>
<div class="l-row no-border" @click="$no">常见问题</div> <div class="l-row no-border" @click="_no">常见问题</div>
<div class="l-row" @click="$no">安全课堂</div> <div class="l-row" @click="_no">安全课堂</div>
</div> </div>
<BaseMask mode="white" v-if="isShowMask" @click="isShowMask = false" /> <BaseMask mode="white" v-if="data.isShowMask" @click="data.isShowMask = false" />
<div class="options"> <div class="options">
<div class="option" @click="isShowLeftHover = !isShowLeftHover"> <div class="option" @click="data.isShowLeftHover = !data.isShowLeftHover">
<img src="../../../assets/img/icon/message/menu-thin.png" alt="" /> <img src="../../../assets/img/icon/message/menu-thin.png" alt="" />
<span>自助工具</span> <span>自助工具</span>
</div> </div>
<div class="option" @click="$no"> <div class="option" @click="_no">
<span>规则中心</span> <span>规则中心</span>
</div> </div>
<div class="option" @click="isShowRightHover = !isShowRightHover"> <div class="option" @click="data.isShowRightHover = !data.isShowRightHover">
<img src="../../../assets/img/icon/message/menu-thin.png" alt="" /> <img src="../../../assets/img/icon/message/menu-thin.png" alt="" />
<span>更多帮助</span> <span>更多帮助</span>
</div> </div>
@ -63,94 +63,102 @@
</div> </div>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { nextTick } from 'vue' import { nextTick, onMounted, reactive, watch } from 'vue'
import Scroll from '../../../components/Scroll' import Scroll from '@/components/Scroll.vue'
import BasePage from '../../BasePage' import { useNav } from '@/utils/hooks/useNav.js'
import { _no, _sleep } from '@/utils'
export default { defineOptions({
extends: BasePage, name: 'SystemNotice'
name: 'SystemNotice', })
components: { Scroll },
data() { const nav = useNav()
return { const data = reactive({
loading: false, loading: false,
isShowMask: false, isShowMask: false,
isShowLeftHover: false, isShowLeftHover: false,
isShowRightHover: false, isShowRightHover: false,
list: [ list: [
{ {
read: false, read: false,
title: '账号登录提醒', title: '账号登录提醒',
detail: 'xxx', detail: 'xxx',
time: '2021-10-12 12:12', time: '2021-10-12 12:12',
content: content:
'您的抖音号4533452342于2021-02-09 07:45:23进行了登录操作。如非本人操作账号可能被盗。建议立即修改密码或在[设置-账号与安全-登录设备管理]中删除异常设备。参考设备iPhone X参考地点上海市' '您的抖音号4533452342于2021-02-09 07:45:23进行了登录操作。如非本人操作账号可能被盗。建议立即修改密码或在[设置-账号与安全-登录设备管理]中删除异常设备。参考设备iPhone X参考地点上海市'
},
{
read: false,
title: '账号登录提醒',
detail: 'xxx',
time: '2021-10-12 12:12',
content:
'您的抖音号4533452342于2021-02-09 07:45:23进行了登录操作。如非本人操作账号可能被盗。建议立即修改密码或在[设置-账号与安全-登录设备管理]中删除异常设备。参考设备iPhone X参考地点上海市'
},
{
read: false,
title: '协议修订通知',
detail: '',
time: '2021-10-12 12:12',
content:
'你好,根据业务开展的实际情况,抖音近期更新了《抖音用户服务协议》《抖音隐私政策》及《儿童/青少年使用须知》中的相关内容。你可以在“我”-“设置”页面中,查看更新后的协议全文。'
},
{
read: false,
title: '协议修订通知',
detail: '',
time: '2021-10-12 12:12',
content:
'你好,根据业务开展的实际情况,抖音近期更新了《抖音用户服务协议》部分条款的表述。你可以在“我”-“设置”页面中,查看更新后的协议全文。'
}
]
}
},
watch: {
isShowLeftHover(newVal) {
if (newVal) {
this.isShowMask = true
}
}, },
isShowRightHover(newVal) { {
if (newVal) { read: false,
this.isShowMask = true title: '账号登录提醒',
} detail: 'xxx',
time: '2021-10-12 12:12',
content:
'您的抖音号4533452342于2021-02-09 07:45:23进行了登录操作。如非本人操作账号可能被盗。建议立即修改密码或在[设置-账号与安全-登录设备管理]中删除异常设备。参考设备iPhone X参考地点上海市'
}, },
isShowMask(newVal) { {
if (!newVal) { read: false,
this.isShowLeftHover = false title: '协议修订通知',
this.isShowRightHover = false detail: '',
} time: '2021-10-12 12:12',
} content:
}, '你好,根据业务开展的实际情况,抖音近期更新了《抖音用户服务协议》《抖音隐私政策》及《儿童/青少年使用须知》中的相关内容。你可以在“我”-“设置”页面中,查看更新后的协议全文。'
computed: {},
created() {
this.getData()
},
mounted() {},
methods: {
async getData() {
this.loading = true
await this.$sleep(700)
this.loading = false
await nextTick()
this.$refs.mainScroll.scrollBottom()
}, },
goDetail(item) { {
item.read = true read: false,
if (item.detail) { title: '协议修订通知',
this.$no() detail: '',
} time: '2021-10-12 12:12',
content:
'你好,根据业务开展的实际情况,抖音近期更新了《抖音用户服务协议》部分条款的表述。你可以在“我”-“设置”页面中,查看更新后的协议全文。'
} }
]
})
onMounted(() => {
getData()
})
watch(
() => data.isShowLeftHover,
(newVal) => {
if (newVal) {
data.isShowMask = true
}
}
)
watch(
() => data.isShowRightHover,
(newVal) => {
if (newVal) {
data.isShowMask = true
}
}
)
watch(
() => data.isShowMask,
(newVal) => {
if (!newVal) {
data.isShowLeftHover = false
data.isShowRightHover = false
}
}
)
async function getData() {
data.loading = true
await _sleep(700)
data.loading = false
await nextTick()
// data.$refs.mainScroll.scrollBottom()
}
function goDetail(item) {
item.read = true
if (item.detail) {
_no()
} }
} }
</script> </script>

View File

@ -2,7 +2,7 @@
<div class="goods-detail base-page" ref="page" @scroll="scroll"> <div class="goods-detail base-page" ref="page" @scroll="scroll">
<header ref="header"> <header ref="header">
<div class="top"> <div class="top">
<Icon @click="$back()" icon="material-symbols-light:arrow-back-ios-new" /> <Icon @click="router.back()" icon="material-symbols-light:arrow-back-ios-new" />
<div class="right"> <div class="right">
<div class="search"> <div class="search">
<Icon icon="jam:search" /> <Icon icon="jam:search" />
@ -18,7 +18,7 @@
</header> </header>
<header class="shadow" ref="headerShadow"> <header class="shadow" ref="headerShadow">
<div class="top"> <div class="top">
<Icon @click="$back()" icon="material-symbols-light:arrow-back-ios-new" /> <Icon @click="router.back()" icon="material-symbols-light:arrow-back-ios-new" />
<div class="right"> <div class="right">
<div class="search"> <div class="search">
<Icon icon="jam:search" /> <Icon icon="jam:search" />
@ -347,7 +347,7 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import SlideHorizontal from '@/components/slide/SlideHorizontal.vue' import SlideHorizontal from '@/components/slide/SlideHorizontal.vue'
import SlideItem from '@/components/slide/SlideItem.vue' import SlideItem from '@/components/slide/SlideItem.vue'
import { onMounted, onUnmounted, reactive, ref } from 'vue' import { onMounted, onUnmounted, reactive, ref } from 'vue'
@ -357,11 +357,13 @@ import { useBaseStore } from '@/store/pinia'
import { recommendedShop } from '@/api/user' import { recommendedShop } from '@/api/user'
import WaterfallList from '@/components/WaterfallList.vue' import WaterfallList from '@/components/WaterfallList.vue'
import ScrollList from '@/components/ScrollList.vue' import ScrollList from '@/components/ScrollList.vue'
import { useRouter } from 'vue-router'
defineOptions({ defineOptions({
name: 'GoodsDetail' name: 'GoodsDetail'
}) })
const router = useRouter()
let activeIndexs = ref([]) let activeIndexs = ref([])
const nav = useNav() const nav = useNav()
const store = useBaseStore() const store = useBaseStore()
@ -382,6 +384,10 @@ function scroll() {
const state = reactive({ const state = reactive({
detail: { detail: {
price: '',
name: '',
sold: '',
real_price: '',
imgs: [] imgs: []
}, },
index: 0, index: 0,

View File

@ -112,7 +112,7 @@
</div> </div>
</template> </template>
<script setup lang="jsx"> <script setup lang="tsx">
import { useNav } from '@/utils/hooks/useNav' import { useNav } from '@/utils/hooks/useNav'
import { $no, _checkImgUrl } from '@/utils' import { $no, _checkImgUrl } from '@/utils'
import ScrollList from '@/components/ScrollList.vue' import ScrollList from '@/components/ScrollList.vue'

View File

@ -5,7 +5,7 @@
<!-- <video ref="videoEl" :src="v1" controls></video>--> <!-- <video ref="videoEl" :src="v1" controls></video>-->
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
defineOptions({ defineOptions({

View File

@ -5,30 +5,12 @@
<VideoShare v-model="t" page-id="Test" /> <VideoShare v-model="t" page-id="Test" />
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import BaseButton from '../../components/BaseButton' import BaseButton from '../../components/BaseButton.vue'
import VideoShare from '../home/components/VideoShare' import VideoShare from '../home/components/VideoShare.vue'
import { ref } from 'vue'
export default { const t = ref(false)
name: 'Test4',
components: {
BaseButton,
VideoShare
},
props: {
text: {
type: String,
default: '@喵嗷污说电影创作的原声'
}
},
data() {
return {
t: false
}
},
methods: {},
mounted() {}
}
</script> </script>
<style scoped lang="less"> <style scoped lang="less">

View File

@ -17,7 +17,7 @@ const router = createRouter({
router.beforeEach((to, from) => { router.beforeEach((to, from) => {
const baseStore = useBaseStore() const baseStore = useBaseStore()
//footer下面的5个按钮对跳不要用动画 //footer下面的5个按钮对跳不要用动画
let noAnimation = ['/', '/home', '/me', '/shop', '/message', '/publish', '/home/live', '/test'] const noAnimation = ['/', '/home', '/me', '/shop', '/message', '/publish', '/home/live', '/test']
if (noAnimation.indexOf(from.path) !== -1 && noAnimation.indexOf(to.path) !== -1) { if (noAnimation.indexOf(from.path) !== -1 && noAnimation.indexOf(to.path) !== -1) {
return true return true
} }
@ -28,7 +28,7 @@ router.beforeEach((to, from) => {
if (toDepth > fromDepth) { if (toDepth > fromDepth) {
if (to.matched && to.matched.length) { if (to.matched && to.matched.length) {
let toComponentName = to.matched[0].components.default.name const toComponentName = to.matched[0].components?.default.name
// store.commit('updateExcludeRoutes', {type: 'remove', value: toComponentName}) // store.commit('updateExcludeRoutes', {type: 'remove', value: toComponentName})
baseStore.updateExcludeRoutes({ type: 'remove', value: toComponentName }) baseStore.updateExcludeRoutes({ type: 'remove', value: toComponentName })
// console.log('to', toComponentName) // console.log('to', toComponentName)
@ -37,7 +37,7 @@ router.beforeEach((to, from) => {
} }
} else { } else {
if (from.matched && from.matched.length) { if (from.matched && from.matched.length) {
let fromComponentName = from.matched[0].components.default.name const fromComponentName = from.matched[0].components?.default.name
// store.commit('updateExcludeRoutes', {type: 'add', value: fromComponentName}) // store.commit('updateExcludeRoutes', {type: 'add', value: fromComponentName})
baseStore.updateExcludeRoutes({ type: 'add', value: fromComponentName }) baseStore.updateExcludeRoutes({ type: 'add', value: fromComponentName })

View File

@ -1,8 +1,9 @@
import Home from '../pages/home' import Home from '../pages/home/index.vue'
import Test from '../pages/test/Test' import Test from '../pages/test/Test.vue'
import Test4 from '../pages/test/Test4' import Test4 from '../pages/test/Test4.vue'
import type { RouteRecordRaw } from 'vue-router'
const routes = [ const routes: RouteRecordRaw[] = [
// {path: '/', redirect: '/attention'}, // {path: '/', redirect: '/attention'},
{ path: '/', redirect: '/home' }, { path: '/', redirect: '/home' },
{ path: '/test', component: Test }, { path: '/test', component: Test },

View File

@ -18,6 +18,14 @@ export const useBaseStore = defineStore('base', {
routeData: null, routeData: null,
users: [], users: [],
userinfo: { userinfo: {
nickname: '',
desc: '',
user_age: '',
signature: '',
unique_id: '',
province: '',
city: '',
gender: '',
school: { school: {
name: '', name: '',
department: null, department: null,

View File

@ -47,7 +47,12 @@ export const SlideItemPlayStatus = {
export const DefaultUser = { export const DefaultUser = {
nickname: '', nickname: '',
unique_id: '', unique_id: '',
certification: '',
short_id: '', short_id: '',
province: '',
city: '',
school: {},
uid: '',
signature: '', //签名 signature: '', //签名
mplatform_followers_count: '', //粉丝 mplatform_followers_count: '', //粉丝
following_count: '', //关注 following_count: '', //关注

View File

@ -427,6 +427,10 @@ const Utils = {
export default Utils export default Utils
export function _dateFormat(val, type) {
return Utils.$dateFormat(val, type)
}
export function $no() { export function $no() {
Utils.$no(arguments) Utils.$no(arguments)
} }
@ -435,8 +439,8 @@ export function $notice(val) {
Utils.$notice(val) Utils.$notice(val)
} }
export function _notice(val) { export function _time(val) {
Utils.$notice(val) return Utils.$time(val)
} }
export function _checkImgUrl(url) { export function _checkImgUrl(url) {
@ -467,6 +471,9 @@ export function _getUserDouyinId(item) {
return item.author.unique_id || item.author.short_id return item.author.unique_id || item.author.short_id
} }
/**
* @param {number} duration
*/
export function _sleep(duration) { export function _sleep(duration) {
return new Promise((resolve) => { return new Promise((resolve) => {
setTimeout(resolve, duration) setTimeout(resolve, duration)
@ -499,3 +506,176 @@ export function sampleSize(arr, num) {
} }
return list return list
} }
export function _showLoading() {
const app = Vue.createApp({
render() {
return <Loading />
}
})
let parent = document.createElement('div')
parent.classList.add(...['dialog-ctn'])
document.body.append(parent)
app.mount(parent)
}
export function _hideLoading() {
let parent = document.querySelector('.dialog-ctn')
parent.remove()
}
export function _showSelectDialog(sexList, cb) {
let remove = () => {
let parent = document.querySelector('.dialog-ctn')
parent.classList.replace('fade-in', 'fade-out')
setTimeout(() => {
parent.remove()
}, 300)
}
let tempCb = (e) => {
remove()
cb(e)
}
const app = Vue.createApp({
render() {
return <SelectDialog onCancel={remove} list={sexList} onOk={tempCb} />
}
})
let parent = document.createElement('div')
parent.classList.add(...['dialog-ctn', 'fade-in'])
document.body.append(parent)
app.mount(parent)
}
export function _showSimpleConfirmDialog(title, okCb, cancelCb, okText, cancelText) {
if (!cancelCb) {
cancelCb = () => {}
}
let remove = () => {
let parent = document.querySelector('.dialog-ctn')
parent.classList.replace('fade-in', 'fade-out')
setTimeout(() => {
parent.remove()
}, 300)
}
let tempOkCb = (e) => {
remove()
okCb(e)
}
let tempCancelCb = (e) => {
remove()
cancelCb(e)
}
const app = Vue.createApp({
render() {
return (
<SimpleConfirmDialog
onCancel={tempCancelCb}
onDismiss={remove}
title={title}
okText={okText}
cancelText={cancelText}
onOk={tempOkCb}
/>
)
}
})
let parent = document.createElement('div')
parent.classList.add(...['dialog-ctn', 'fade-in'])
document.body.append(parent)
app.mount(parent)
}
export function _showConfirmDialog(
title,
subtitle,
subtitleColor,
okCb,
cancelCb,
okText,
cancelText,
cancelTextColor
) {
let remove = () => {
let parent = document.querySelector('.dialog-ctn')
parent.classList.replace('fade-in', 'fade-out')
setTimeout(() => {
parent.remove()
}, 300)
}
let tempOkCb = (e) => {
remove()
okCb && okCb(e)
}
let tempCancelCb = (e) => {
remove()
cancelCb && cancelCb(e)
}
const app = Vue.createApp({
render() {
return (
<ConfirmDialog
onCancel={tempCancelCb}
onDismiss={remove}
title={title}
subtitle={subtitle}
subtitleColor={subtitleColor}
cancelTextColor={cancelTextColor}
okText={okText}
cancelText={cancelText}
onOk={tempOkCb}
/>
)
}
})
let parent = document.createElement('div')
parent.classList.add(...['dialog-ctn', 'fade-in'])
document.body.append(parent)
app.mount(parent)
}
export function _showNoticeDialog(title, subtitle, subtitleColor, cancelCb, cancelText) {
let remove = () => {
let parent = document.querySelector('.dialog-ctn')
parent.classList.replace('fade-in', 'fade-out')
setTimeout(() => {
parent.remove()
}, 300)
}
let tempCancelCb = (e) => {
remove()
cancelCb(e)
}
const app = Vue.createApp({
render() {
return (
<NoticeDialog
onCancel={tempCancelCb}
onDismiss={remove}
title={title}
subtitleColor={subtitleColor}
cancelText={cancelText}
subtitle={subtitle}
/>
)
}
})
let parent = document.createElement('div')
parent.classList.add(...['dialog-ctn', 'fade-in'])
document.body.append(parent)
app.mount(parent)
}
export function _notice(val) {
let div = document.createElement('div')
div.classList.add('global-notice')
div.textContent = val
document.body.append(div)
setTimeout(() => {
document.body.removeChild(div)
}, 1000)
}
export function _no() {
this.$notice('未实现')
}

23
tsconfig.app.json Normal file
View File

@ -0,0 +1,23 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": [
"env.d.ts",
"src/**/*",
"src/**/*.vue"
],
"exclude": [
"src/**/__tests__/*"
],
"compilerOptions": {
"allowJs": true,
"strict": false,
"composite": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"baseUrl": ".",
"paths": {
"@/*": [
"./src/*"
]
}
}
}

11
tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

19
tsconfig.node.json Normal file
View File

@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*"
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

View File

@ -1,20 +1,23 @@
import { defineConfig } from 'vite' import { defineConfig, PluginOption } from 'vite'
import Vue from '@vitejs/plugin-vue' import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx' import VueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path' import { resolve } from 'path'
import { visualizer } from 'rollup-plugin-visualizer' import { visualizer } from 'rollup-plugin-visualizer'
import DefineOptions from 'unplugin-vue-define-options/vite' // 引入插件 import DefineOptions from 'unplugin-vue-define-options/vite' // 引入插件
import { Plugin as importToCDN } from 'vite-plugin-cdn-import' import { Plugin as importToCDN } from 'vite-plugin-cdn-import'
import commonjs from 'vite-plugin-commonjs'
import { fileURLToPath, URL } from 'node:url'
// import viteImagemin from 'vite-plugin-imagemin' // import viteImagemin from 'vite-plugin-imagemin'
// import viteCompression from 'vite-plugin-compression' // import viteCompression from 'vite-plugin-compression'
function pathResolve(dir) {
return resolve(__dirname, '.', dir)
}
const lifecycle = process.env.npm_lifecycle_event const lifecycle = process.env.npm_lifecycle_event
// https://vitejs.dev/config/ // {
// name: 'axios',
// var: 'axios',
// path: 'https://lib.baomitu.com/axios/1.6.8/axios.min.js'
// },
export default defineConfig({ export default defineConfig({
base: './', base: './',
envDir: 'env', envDir: 'env',
@ -29,7 +32,7 @@ export default defineConfig({
// // exclude: [/node_modules/, /jQuery\.js/] // // exclude: [/node_modules/, /jQuery\.js/]
// // } // // }
// }), // }),
lifecycle === 'report' ? visualizer({ open: false }) : null, lifecycle === 'report' ? (visualizer({ open: false }) as any as PluginOption) : null,
DefineOptions(), DefineOptions(),
Vue(), Vue(),
VueJsx(), VueJsx(),
@ -55,11 +58,7 @@ export default defineConfig({
var: 'Mock', var: 'Mock',
path: 'https://lib.baomitu.com/Mock.js/1.0.1-beta3/mock-min.js' path: 'https://lib.baomitu.com/Mock.js/1.0.1-beta3/mock-min.js'
}, },
{
name: 'axios',
var: 'axios',
path: 'https://lib.baomitu.com/axios/1.6.8/axios.min.js'
},
{ {
name: 'jquery', name: 'jquery',
var: '$', var: '$',
@ -109,7 +108,7 @@ export default defineConfig({
], ],
resolve: { resolve: {
alias: { alias: {
'@': pathResolve('src') '@': fileURLToPath(new URL('./src', import.meta.url))
}, },
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
}, },
@ -121,7 +120,7 @@ export default defineConfig({
manualChunks(id, { getModuleInfo }) { manualChunks(id, { getModuleInfo }) {
const reg = /(.*)\/src\/components\/(.*)/ const reg = /(.*)\/src\/components\/(.*)/
if (reg.test(id)) { if (reg.test(id)) {
const importersLen = getModuleInfo(id).importers.length const importersLen = getModuleInfo(id)?.importers.length ?? 0
// 被多处引用 // 被多处引用
if (importersLen > 1) return 'common' if (importersLen > 1) return 'common'
} }