Browse Source

feat: add typescript support

dev
zyronon 1 year ago
parent
commit
906a5728b0
  1. 2
      .eslintrc.cjs
  2. 31
      .gitignore
  3. 18
      env.d.ts
  4. 20
      index.html
  5. 22
      package.json
  6. 849
      pnpm-lock.yaml
  7. 133
      src/App.vue
  8. 2
      src/components/AutoInput.vue
  9. 77
      src/components/BaseHeader.vue
  10. 46
      src/components/Comment.vue
  11. 26
      src/components/UserPanel.vue
  12. 2
      src/components/dialog/ConfirmDialog.vue
  13. 24
      src/components/slide/SlideHorizontal.vue
  14. 4
      src/config/index.js
  15. 12
      src/main.ts
  16. 66
      src/mock/index.ts
  17. 318
      src/pages/home/Music.vue
  18. 531
      src/pages/home/MusicRankList.vue
  19. 75
      src/pages/home/Publish.vue
  20. 13
      src/pages/home/index.vue
  21. 101
      src/pages/me/MyCard.vue
  22. 170
      src/pages/me/collect/MusicCollect.vue
  23. 84
      src/pages/me/collect/VideoCollect.vue
  24. 187
      src/pages/me/rightMenu/LookHistory.vue
  25. 74
      src/pages/me/rightMenu/MinorProtection/DetailSetting.vue
  26. 25
      src/pages/me/rightMenu/MinorProtection/Index.vue
  27. 46
      src/pages/me/rightMenu/MinorProtection/TriggerTime.vue
  28. 60
      src/pages/me/rightMenu/Setting.vue
  29. 235
      src/pages/me/userinfo/AddSchool.vue
  30. 101
      src/pages/me/userinfo/ChooseCity.vue
  31. 56
      src/pages/me/userinfo/ChooseDepartment.vue
  32. 2317
      src/pages/me/userinfo/ChooseLocation.vue
  33. 91
      src/pages/me/userinfo/ChooseProvince.vue
  34. 107
      src/pages/me/userinfo/ChooseSchool.vue
  35. 63
      src/pages/me/userinfo/DeclareSchool.vue
  36. 47
      src/pages/me/userinfo/DisplayType.vue
  37. 220
      src/pages/me/userinfo/EditUserInfo.vue
  38. 136
      src/pages/me/userinfo/EditUserInfoItem.vue
  39. 252
      src/pages/message/AllMessage.vue
  40. 109
      src/pages/message/JoinedGroupChat.vue
  41. 257
      src/pages/message/Message.vue
  42. 61
      src/pages/message/MoreSearch.vue
  43. 107
      src/pages/message/Visitors.vue
  44. 146
      src/pages/message/notice/DouyinHelper.vue
  45. 202
      src/pages/message/notice/SystemNotice.vue
  46. 12
      src/pages/shop/GoodsDetail.vue
  47. 2
      src/pages/shop/Shop.vue
  48. 2
      src/pages/test/Test.vue
  49. 28
      src/pages/test/Test4.vue
  50. 6
      src/router/index.ts
  51. 9
      src/router/routes.ts
  52. 8
      src/store/pinia.js
  53. 5
      src/utils/const_var.js
  54. 184
      src/utils/index.jsx
  55. 23
      tsconfig.app.json
  56. 11
      tsconfig.json
  57. 19
      tsconfig.node.json
  58. 27
      vite.config.ts

2
.eslintrc.cjs

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

31
.gitignore vendored

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

18
env.d.ts vendored

@ -0,0 +1,18 @@ @@ -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 {}

20
index.html

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

22
package.json

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

849
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

133
src/App.vue

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

2
src/components/AutoInput.vue

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

77
src/components/BaseHeader.vue

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

46
src/components/Comment.vue

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

26
src/components/UserPanel.vue

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

2
src/components/dialog/ConfirmDialog.vue

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

24
src/components/slide/SlideHorizontal.vue

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

4
src/config/index.js

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

12
src/main.js → src/main.ts

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

66
src/mock/index.js → src/mock/index.ts

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

318
src/pages/home/Music.vue

@ -1,18 +1,18 @@ @@ -1,18 +1,18 @@
<template>
<div id="Music">
<div class="header">
<dy-back mode="light" @click="$back" />
<dy-back mode="light" @click="router.back()" />
<transition name="fade">
<div class="center" v-if="isFixed">
<span class="f16">{{ music.name }}</span>
<div class="center" v-if="data.isFixed">
<span class="f16">{{ data.music.name }}</span>
</div>
</transition>
<div class="right">
<!-- TODO 没有淡入淡出的特效-->
<template v-if="isFixed">
<template v-if="data.isFixed">
<img
class="star"
v-if="isCollect"
v-if="data.isCollect"
src="../../assets/img/icon/star-yellow.png"
@click.stop="toggleCollect()"
/>
@ -23,38 +23,54 @@ @@ -23,38 +23,54 @@
@click.stop="toggleCollect()"
/>
</template>
<div class="logo" v-if="!isFixed" @click="$nav('/home/music-rank-list')">抖音音乐榜</div>
<img class="share" src="../../assets/img/icon/share-white.png" @click="isSharing = true" />
<div class="logo" v-if="!data.isFixed" @click="nav('/home/music-rank-list')">
抖音音乐榜
</div>
<img
class="share"
src="../../assets/img/icon/share-white.png"
@click="data.isSharing = true"
/>
</div>
</div>
<div class="content">
<Scroll
class="Scroll"
:fixedHeight="180"
@fixed="(e) => (this.isFixed = e)"
@fixed="(e) => (data.isFixed = e)"
@pulldown="loadData"
>
<div class="desc">
<div class="cover-wrapper" @click="togglePlay()">
<img class="cover" :src="$imgPreview(music.cover)" alt="" />
<img v-if="!isPlay" src="../../assets/img/icon/play-white.png" alt="" class="play" />
<img v-if="isPlay" src="../../assets/img/icon/pause-white.png" alt="" class="play" />
<img class="cover" :src="_checkImgUrl(data.music.cover)" alt="" />
<img
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 class="info">
<div class="name">{{ music.name }}</div>
<div class="name">{{ data.music.name }}</div>
<div>
<div class="user">{{ music.author }}</div>
<div class="peoples">{{ formatNumber(music.use_count) }} 人使用</div>
<div class="user">{{ data.music.author }}</div>
<div class="peoples">{{ _formatNumber(data.music.use_count) }} 人使用</div>
</div>
<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" />
<span>{{ isCollect ? '已' : '' }}收藏</span>
<span>{{ data.isCollect ? '已' : '' }}收藏</span>
</div>
</div>
</div>
<Posters mode="music" :list="videos" />
<Loading :is-full-screen="false" v-if="loading" />
<Posters mode="music" :list="data.videos" />
<Loading :is-full-screen="false" v-if="data.loading" />
<NoMore v-else />
</Scroll>
</div>
@ -70,166 +86,162 @@ @@ -70,166 +86,162 @@
</div>
<Share
v-model="isSharing"
v-model="data.isSharing"
mode="music"
ref="share"
pageId="Music"
@showDouyinCode="showDouyinCode = true"
@showShare2WeChatZone="shareType = 2"
@share2WeChat="shareType = 3"
@share2QQZone="shareType = 4"
@share2QQ="shareType = 5"
@share2Webo="shareType = 8"
@ShareToFriend="delayShowDialog((e) => (this.shareToFriend = true))"
@showDouyinCode="data.showDouyinCode = true"
@showShare2WeChatZone="data.shareType = 2"
@share2WeChat="data.shareType = 3"
@share2QQZone="data.shareType = 4"
@share2QQ="data.shareType = 5"
@share2Webo="data.shareType = 8"
@ShareToFriend="delayShowDialog(() => (data.shareToFriend = true))"
/>
<DouyinCode v-model="showDouyinCode" />
<DouyinCode v-model="data.showDouyinCode" />
<ConfirmDialog
v-model:visible="showSharePassword"
v-model:visible="data.showSharePassword"
title="你的口令已复制"
subtitle="0F.:/ a【风就应该自由要什么归宿】长按复制此条消息,打开抖音搜索,聆听音乐##kwu3VCixHl8##[抖音口令]"
:okText="okText"
:okText="data.okText"
cancelText="不分享了"
@ok="shareType = -1"
@cancel="shareType = -1"
@ok="data.shareType = -1"
@cancel="data.shareType = -1"
>
<template v-slot:header>
<img style="width: 100%" src="../../assets/img/icon/share-password.webp" alt="" />
</template>
</ConfirmDialog>
<ShareToFriend pageId="Music" v-model="shareToFriend" />
<ShareToFriend pageId="Music" v-model="data.shareToFriend" />
</div>
</template>
<script>
import Posters from '../../components/Posters'
import Scroll from '../../components/Scroll'
import Loading from '../../components/Loading'
import Share from '../../components/Share'
import DouyinCode from '../../components/DouyinCode'
import ConfirmDialog from '../../components/dialog/ConfirmDialog'
import ShareToFriend from './components/ShareToFriend'
<script setup lang="ts">
import Posters from '../../components/Posters.vue'
import Scroll from '../../components/Scroll.vue'
import Loading from '../../components/Loading.vue'
import Share from '../../components/Share.vue'
import DouyinCode from '../../components/DouyinCode.vue'
import ConfirmDialog from '../../components/dialog/ConfirmDialog.vue'
import ShareToFriend from './components/ShareToFriend.vue'
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 {
name: 'Music',
components: {
Scroll,
Posters,
Loading,
Share,
DouyinCode,
ConfirmDialog,
ShareToFriend
},
data() {
return {
loading: false,
isFixed: false,
isCollect: false,
isPlay: false,
isSharing: false,
okText: '',
showSharePassword: false,
shareToFriend: false,
shareType: -1,
showDouyinCode: false,
audio: new Audio(),
total: 0,
pageNo: 0,
pageSize: 15,
videos: [],
music: {
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
}
}
},
watch: {
shareType(newVal) {
if (newVal === -1) return
this.showSharePassword = true
switch (newVal) {
case 2:
case 3:
return (this.okText = '去微信粘贴')
case 4:
case 5:
return (this.okText = '去QQ粘贴')
case 8:
return (this.okText = '去微博粘贴')
}
const route = useRoute()
const router = useRouter()
const nav = useNav()
const data = reactive({
loading: false,
isFixed: false,
isCollect: false,
isPlay: false,
isSharing: false,
okText: '',
showSharePassword: false,
shareToFriend: false,
shareType: -1,
showDouyinCode: false,
audio: new Audio(),
total: 0,
pageNo: 0,
pageSize: 15,
videos: [],
music: {
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
}
})
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 = '去微博粘贴')
}
},
created() {
if (this.$route.query.name) {
this.music = this.$route.query
}
)
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('暂时没有更多了')
}
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)
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
}
},
unmounted() {
this.stopPlay()
},
deactivated() {
this.stopPlay()
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>
<style scoped lang="less">

531
src/pages/home/MusicRankList.vue

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
<template>
<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">
<span class="f16">抖音音乐榜</span>
</div>
@ -8,23 +8,23 @@ @@ -8,23 +8,23 @@
<div class="content">
<div class="l-header">
<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>
<Indicator
name="musicRankList"
tabStyleWidth="33%"
:tabTexts="['热歌榜', '飙升樘', '原创榜']"
v-model:active-index="contentIndex"
v-model:active-index="data.contentIndex"
>
</Indicator>
<SlideHorizontal name="musicRankList" v-model:index="contentIndex">
<SlideHorizontal name="musicRankList" v-model:index="data.contentIndex">
<SlideItem>
<div class="list">
<div
class="item"
:key="index"
v-for="(item, index) in hotList"
@click="togglePlay(item, hotList)"
v-for="(item, index) in data.hotList"
@click="togglePlay(item, data.hotList)"
>
<div class="top">
<div class="rank-wrapper">
@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
<div class="right">
<div class="music">
<div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" />
<img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img
v-if="!item.is_play"
src="../../assets/img/icon/play-white.png"
@ -70,9 +70,9 @@ @@ -70,9 +70,9 @@
<div class="author">{{ item.author }}</div>
<div class="desc-bottom">
<div class="duration">
{{ $duration(item.duration) }}
{{ _duration(item.duration) }}
</div>
<div class="use_count">{{ formatNumber(item.use_count) }}人使用</div>
<div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div>
</div>
</div>
@ -92,7 +92,7 @@ @@ -92,7 +92,7 @@
<img
src="../../assets/img/icon/menu2-white.png"
alt=""
@click.stop="$nav('/home/music')"
@click.stop="nav('/home/music')"
/>
</div>
</div>
@ -116,8 +116,8 @@ @@ -116,8 +116,8 @@
<div
class="item"
:key="index"
v-for="(item, index) in hotList"
@click="togglePlay(item, hotList)"
v-for="(item, index) in data.hotList"
@click="togglePlay(item, data.hotList)"
>
<div class="top">
<div class="rank-wrapper">
@ -144,7 +144,7 @@ @@ -144,7 +144,7 @@
<div class="right">
<div class="music">
<div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" />
<img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img
v-if="!item.is_play"
src="../../assets/img/icon/play-white.png"
@ -163,9 +163,9 @@ @@ -163,9 +163,9 @@
<div class="author">{{ item.author }}</div>
<div class="desc-bottom">
<div class="duration">
{{ $duration(item.duration) }}
{{ _duration(item.duration) }}
</div>
<div class="use_count">{{ formatNumber(item.use_count) }}人使用</div>
<div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div>
</div>
</div>
@ -185,7 +185,7 @@ @@ -185,7 +185,7 @@
<img
src="../../assets/img/icon/menu2-white.png"
alt=""
@click.stop="$nav('/home/music')"
@click.stop="nav('/home/music')"
/>
</div>
</div>
@ -209,8 +209,8 @@ @@ -209,8 +209,8 @@
<div
class="item"
:key="index"
v-for="(item, index) in hotList"
@click="togglePlay(item, hotList)"
v-for="(item, index) in data.hotList"
@click="togglePlay(item, data.hotList)"
>
<div class="top">
<div class="rank-wrapper">
@ -237,7 +237,7 @@ @@ -237,7 +237,7 @@
<div class="right">
<div class="music">
<div class="cover-wrapper">
<img v-lazy="$imgPreview(item.cover)" alt="" class="cover" />
<img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img
v-if="!item.is_play"
src="../../assets/img/icon/play-white.png"
@ -256,9 +256,9 @@ @@ -256,9 +256,9 @@
<div class="author">{{ item.author }}</div>
<div class="desc-bottom">
<div class="duration">
{{ $duration(item.duration) }}
{{ _duration(item.duration) }}
</div>
<div class="use_count">{{ formatNumber(item.use_count) }}人使用</div>
<div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div>
</div>
</div>
@ -278,7 +278,7 @@ @@ -278,7 +278,7 @@
<img
src="../../assets/img/icon/menu2-white.png"
alt=""
@click.stop="$nav('/home/music')"
@click.stop="nav('/home/music')"
/>
</div>
</div>
@ -301,255 +301,258 @@ @@ -301,255 +301,258 @@
</div>
</div>
</template>
<script>
export default {
name: 'MusicRankList',
components: {},
data() {
return {
contentIndex: 0,
hotList: [
{
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,
author: '周杰伦',
duration: 99,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '爱在西元前',
mp3: 'https://m3.8js.net:99/1916/501204165042405.mp3',
cover: new URL('../../assets/img/music-cover/2.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/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
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { computed, onDeactivated, onMounted, onUnmounted, reactive } from 'vue'
import { $notice, _checkImgUrl, _dateFormat, _duration, _formatNumber } from '@/utils/index.jsx'
import { useNav } from '@/utils/hooks/useNav'
defineOptions({
name: 'MusicRankList'
})
const router = useRouter()
const nav = useNav()
const data = reactive({
contentIndex: 0,
hotList: [
{
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,
author: '周杰伦',
duration: 99,
use_count: 37441000,
is_collect: false,
is_play: false
},
toggleCollect(item) {
item.is_collect = !item.is_collect
if (item.is_collect) {
this.$notice('收藏成功')
} else {
this.$notice('取消收藏')
}
{
name: '爱在西元前',
mp3: 'https://m3.8js.net:99/1916/501204165042405.mp3',
cover: new URL('../../assets/img/music-cover/2.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
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) {
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()
}
{
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
},
stopPlay() {
this.audio.pause()
this.audio.currentTime = 0
this.audio.removeEventListener('ended', null)
{
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() {
this.stopPlay()
},
deactivated() {
this.stopPlay()
],
updateTime: Date.now(),
audio: new Audio(),
scrollTop: -1
})
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
}
</script>
function toggleCollect(item) {
item.is_collect = !item.is_collect
if (item.is_collect) {
$notice('收藏成功')
} else {
$notice('取消收藏')
}
}
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">
@import '../../assets/less/index';

75
src/pages/home/Publish.vue

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

13
src/pages/home/index.vue

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

101
src/pages/me/MyCard.vue

@ -1,14 +1,18 @@ @@ -1,14 +1,18 @@
<template>
<div id="MyCard">
<div class="header">
<dy-back mode="light" @click="$back" />
<dy-back mode="light" @click="router.back" />
<!-- 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 class="content">
<div class="qrcode">
<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>
<span class="name">ZZZZZZZZZZ</span>
@ -30,65 +34,52 @@ @@ -30,65 +34,52 @@
</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>
</template>
<script>
import Share from '../../components/Share'
import { mapState } from 'pinia'
import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl } from '@/utils'
export default {
name: 'MyCard',
components: {
Share
},
data() {
return {
isSharing: false,
okText: '',
showSharePassword: false,
shareToFriend: false,
shareType: -1,
showDouyinCode: false
}
},
watch: {
shareType(newVal) {
if (newVal === -1) return
this.showSharePassword = true
switch (newVal) {
case 2:
case 3:
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 setup lang="ts">
import Share from '../../components/Share.vue'
import { useBaseStore } from '@/store/pinia'
import { $no, _checkImgUrl } from '@/utils'
import { reactive, watch } from 'vue'
import { useRouter } from 'vue-router'
defineOptions({
name: 'MyCard'
})
const router = useRouter()
const store = useBaseStore()
const data = reactive({
isSharing: false,
okText: '',
showSharePassword: false,
shareToFriend: false,
shareType: -1,
showDouyinCode: false
})
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 = '去微博粘贴')
}
}
}
)
</script>
<style scoped lang="less">
<style lang="less" scoped>
@import '../../assets/less/index';
#MyCard {

170
src/pages/me/collect/MusicCollect.vue

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

84
src/pages/me/collect/VideoCollect.vue

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

187
src/pages/me/rightMenu/LookHistory.vue

@ -13,16 +13,16 @@ @@ -13,16 +13,16 @@
style="width: calc(100vw - 2rem); margin-left: 1rem"
tabStyleWidth="50%"
:tabTexts="['视频', '影视综']"
v-model:active-index="currentSlideItemIndex"
v-model:active-index="data.currentSlideItemIndex"
>
</Indicator>
<SlideHorizontal v-model:index="currentSlideItemIndex" class="SlideRowList">
<SlideHorizontal v-model:index="data.currentSlideItemIndex" class="SlideRowList">
<SlideItem class="tab1" style="overflow: auto">
<Scroll class="Scroll" @pulldown="getHistoryVideo">
<Posters :list="historyVideo.list" v-if="historyVideo.total"></Posters>
<Loading :is-full-screen="false" v-if="loadingVideo" />
<Posters :list="data.historyVideo.list" v-if="data.historyVideo.total"></Posters>
<Loading :is-full-screen="false" v-if="data.loadingVideo" />
<template v-else>
<NoMore v-if="historyVideo.list.length" />
<NoMore v-if="data.historyVideo.list.length" />
<div class="empty" v-else>
<img src="../../../assets/img/icon/none-bg1.webp" alt="" />
<div class="title">暂无观看历史记录</div>
@ -40,100 +40,97 @@ @@ -40,100 +40,97 @@
</div>
</div>
</template>
<script>
import Posters from '../../../components/Posters'
import Scroll from '../../../components/Scroll'
import NoMore from '../../../components/NoMore'
<script setup lang="ts">
import Posters from '@/components/Posters.vue'
import Scroll from '@/components/Scroll.vue'
import NoMore from '@/components/NoMore.vue'
import { historyOther, historyVideo } from '@/api/videos'
export default {
name: 'lookHistory',
components: {
NoMore,
Posters,
Scroll
},
data() {
return {
loadingVideo: false,
loadingOther: false,
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)
import { computed, onMounted, reactive } from 'vue'
import { _showConfirmDialog } from '@/utils'
defineOptions({
name: 'LookHistory'
})
const data = reactive({
loadingVideo: false,
loadingOther: false,
isClearHistoryVideo: false,
isClearHistoryOther: false,
currentSlideItemIndex: 0,
pageSize: 15,
historyVideo: {
total: 0,
pageNo: 0,
list: []
},
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
})
}
historyOther: {
total: 0,
pageNo: 0,
list: []
}
})
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>

74
src/pages/me/rightMenu/MinorProtection/DetailSetting.vue

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

25
src/pages/me/rightMenu/MinorProtection/Index.vue

@ -6,21 +6,21 @@ @@ -6,21 +6,21 @@
</template>
</BaseHeader>
<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="right">
<span>未开启</span>
<dy-back direction="right"></dy-back>
</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="right">
<span>未开启</span>
<dy-back direction="right"></dy-back>
</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="right">
<span>未开启</span>
@ -30,20 +30,17 @@ @@ -30,20 +30,17 @@
</div>
</div>
</template>
<script>
export default {
name: 'index',
data() {
return {}
},
computed: {},
created() {},
methods: {}
}
<script setup lang="ts">
import { useNav } from '@/utils/hooks/useNav'
defineOptions({
name: 'MinorProtection'
})
const nav = useNav()
</script>
<style scoped lang="less">
@import '../../../../assets/less/index';
@import '@/assets/less/index';
.index {
position: fixed;

46
src/pages/me/rightMenu/MinorProtection/TriggerTime.vue

@ -8,51 +8,53 @@ @@ -8,51 +8,53 @@
<div class="content">
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME40)">
<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="" />
</div>
</div>
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME60)">
<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="" />
</div>
</div>
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME90)">
<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="" />
</div>
</div>
<div class="row" @click="setTriggerTime(enums.TRIGGER_TIME.TIME120)">
<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="" />
</div>
</div>
</div>
</div>
</template>
<script>
<script setup lang="ts">
import enums from '../../../../utils/enums'
export default {
name: 'TriggerTime',
data() {
return {
enums,
triggerTime: enums.TRIGGER_TIME.TIME60
}
},
created() {
this.triggerTime = ~~this.$route.query.triggerTime
},
methods: {
setTriggerTime(type) {
this.triggerTime = type
localStorage.setItem('changeTriggerTime', type)
}
}
import { onMounted, reactive } from 'vue'
import { useRoute } from 'vue-router'
defineOptions({
name: 'ChooseSchool'
})
const route = useRoute()
const data = reactive({
triggerTime: enums.TRIGGER_TIME.TIME60
})
onMounted(() => {
data.triggerTime = ~~route.query.triggerTime
})
function setTriggerTime(type) {
data.triggerTime = type
localStorage.setItem('changeTriggerTime', type)
}
</script>

60
src/pages/me/rightMenu/Setting.vue

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
<div class="title">帐号</div>
<div class="row">
<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>
</div>
<div class="right">
@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -30,7 +30,7 @@ @@ -30,7 +30,7 @@
<div class="title">通用</div>
<div class="row">
<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>
</div>
<div class="right">
@ -39,7 +39,7 @@ @@ -39,7 +39,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -48,7 +48,7 @@ @@ -48,7 +48,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -60,7 +60,7 @@ @@ -60,7 +60,7 @@
<div class="title">帐号互通</div>
<div class="row">
<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>
</div>
<div class="right">
@ -72,7 +72,7 @@ @@ -72,7 +72,7 @@
<div class="title">关于</div>
<div class="row">
<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>
</div>
<div class="right">
@ -81,7 +81,7 @@ @@ -81,7 +81,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -90,7 +90,7 @@ @@ -90,7 +90,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -99,7 +99,7 @@ @@ -99,7 +99,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -108,7 +108,7 @@ @@ -108,7 +108,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -117,7 +117,7 @@ @@ -117,7 +117,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -126,7 +126,7 @@ @@ -126,7 +126,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -135,7 +135,7 @@ @@ -135,7 +135,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -144,7 +144,7 @@ @@ -144,7 +144,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -155,7 +155,7 @@ @@ -155,7 +155,7 @@
<div class="row">
<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>
</div>
<div class="right">
@ -164,7 +164,7 @@ @@ -164,7 +164,7 @@
</div>
<div class="row">
<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>
</div>
<div class="right">
@ -172,32 +172,22 @@ @@ -172,32 +172,22 @@
</div>
</div>
<div class="version">抖音 version{{ version }}</div>
<div class="version">抖音 version{{ store.version }}</div>
</div>
</div>
</template>
<script>
<script setup lang="ts">
import { useBaseStore } from '@/store/pinia'
export default {
name: 'Setting',
setup() {
const baseStore = useBaseStore()
return { baseStore }
},
data() {
return {
version: this.baseStore.version
}
},
computed: {},
created() {},
methods: {}
}
defineOptions({
name: 'ChooseSchool'
})
const store = useBaseStore()
</script>
<style scoped lang="less">
@import '../../../assets/less/index';
@import '@/assets/less/index';
.Setting {
position: fixed;

235
src/pages/me/userinfo/AddSchool.vue

@ -11,24 +11,24 @@ @@ -11,24 +11,24 @@
</template>
</BaseHeader>
<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="right">
<span>{{ isEmpty(localSchool.name) }}</span>
<span>{{ isEmpty(data.localSchool.name) }}</span>
<dy-back scale="1" direction="right"></dy-back>
</div>
</div>
<div class="row" @click="checkGo('/me/choose-department')">
<div class="left">院系</div>
<div class="right">
<span>{{ isEmpty(localSchool.department) }}</span>
<span>{{ isEmpty(data.localSchool.department) }}</span>
<dy-back scale="1" direction="right"></dy-back>
</div>
</div>
<div class="row" @click="showJoinTimeDialog">
<div class="left">入学时间</div>
<div class="right">
<span>{{ isEmpty(localSchool.joinTime) }}</span>
<span>{{ isEmpty(data.localSchool.joinTime) }}</span>
<dy-back scale="1" direction="right"></dy-back>
<div v-show="false" id="trigger1"></div>
</div>
@ -36,11 +36,14 @@ @@ -36,11 +36,14 @@
<div class="row" @click="showEducationDialog">
<div class="left">学历</div>
<div class="right">
<span>{{ isEmpty(localSchool.education) }}</span>
<span>{{ isEmpty(data.localSchool.education) }}</span>
<dy-back scale="1" direction="right"></dy-back>
</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="right">
<span>{{ displayType }}</span>
@ -51,122 +54,128 @@ @@ -51,122 +54,128 @@
</div>
</template>
<script>
import { mapState } from 'pinia'
<script setup lang="ts">
import enums from '../../../utils/enums'
import { inject } from 'vue'
import { computed, onMounted, reactive } from 'vue'
import MobileSelect from '../../../components/mobile-select/mobile-select'
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
export default {
name: 'AddSchool',
setup() {
const baseStore = useBaseStore()
return { baseStore }
},
data() {
return {
mitt: inject('mitt'),
localSchool: this.$clone(this.baseStore.userinfo.school),
educationList: [
{ id: 1, name: '专科' },
{ id: 2, name: '本科' },
{ id: 3, name: '硕士' },
{ id: 4, name: '博士' }
]
}
},
created() {
let school = localStorage.getItem('changeSchool')
let department = localStorage.getItem('changeDepartment')
let displayType = localStorage.getItem('changeDisplayType')
let joinTime = localStorage.getItem('changeJoinTime')
let education = localStorage.getItem('changeEducation')
if (school !== null) this.localSchool.name = school
if (department !== null) this.localSchool.department = department
if (displayType !== null) this.localSchool.displayType = ~~displayType
if (joinTime !== null) this.localSchool.joinTime = ~~joinTime
if (education !== null) this.localSchool.education = education
// localStorage.clear()
},
computed: {
...mapState(useBaseStore, ['userinfo']),
isChanged() {
if (this.school.name !== this.localSchool.name) return true
if (this.school.department !== this.localSchool.department) return true
if (this.school.joinTime !== this.localSchool.joinTime) return true
if (this.school.education !== this.localSchool.education) return true
return this.school.displayType !== this.localSchool.displayType
},
displayType() {
if (this.localSchool.displayType === enums.DISPLAY_TYPE.ALL) return '公开可见'
if (this.localSchool.displayType === enums.DISPLAY_TYPE.SCHOOL) return '校友可见'
if (this.localSchool.displayType === enums.DISPLAY_TYPE.ME) return '仅自己可见'
return ''
},
school() {
return this.userinfo.school
}
},
methods: {
showJoinTimeDialog() {
new MobileSelect({
trigger: '#trigger1',
title: '学历',
wheels: [
{
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()
defineOptions({
name: 'AddSchool'
})
const store = useBaseStore()
const router = useRouter()
const nav = useNav()
const data = reactive({
localSchool: cloneDeep(store.userinfo.school),
educationList: [
{ id: 1, name: '专科' },
{ id: 2, name: '本科' },
{ id: 3, name: '硕士' },
{ id: 4, name: '博士' }
]
})
onMounted(() => {
let school = localStorage.getItem('changeSchool')
let department = localStorage.getItem('changeDepartment')
let displayType = localStorage.getItem('changeDisplayType')
let joinTime = localStorage.getItem('changeJoinTime')
let education = localStorage.getItem('changeEducation')
if (school !== null) data.localSchool.name = school
if (department !== null) data.localSchool.department = department
if (displayType !== null) data.localSchool.displayType = ~~displayType
if (joinTime !== null) data.localSchool.joinTime = ~~joinTime
if (education !== null) data.localSchool.education = education
})
const school = computed(() => {
return store.userinfo.school
})
const isChanged = computed(() => {
if (school.value.name !== data.localSchool.name) return true
if (school.value.department !== data.localSchool.department) return true
if (school.value.joinTime !== data.localSchool.joinTime) return true
if (school.value.education !== data.localSchool.education) return true
return school.value.displayType !== data.localSchool.displayType
})
const displayType = computed(() => {
if (data.localSchool.displayType === enums.DISPLAY_TYPE.ALL) return '公开可见'
if (data.localSchool.displayType === enums.DISPLAY_TYPE.SCHOOL) return '校友可见'
if (data.localSchool.displayType === enums.DISPLAY_TYPE.ME) return '仅自己可见'
return ''
})
function showJoinTimeDialog() {
new MobileSelect({
trigger: '#trigger1',
title: '学历',
wheels: [
{
data: Array.apply(null, { length: 50 }).map((v, i) => new Date().getFullYear() - i)
}
},
async save() {
if (!this.isChanged) return
this.$showLoading()
let data = { ...this.userinfo, ...{ school: this.localSchool } }
this.baseStore.setUserinfo(data)
await this.$sleep(500)
this.$hideLoading()
localStorage.clear()
this.$back()
this.$notice('修改成功')
],
callback: (indexArr, data) => {
localStorage.setItem('changeJoinTime', data[0])
data.localSchool.joinTime = ~~data[0]
}
}).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>
<style scoped lang="less">

101
src/pages/me/userinfo/ChooseCity.vue

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
</BaseHeader>
<div class="content">
<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>
</div>
</div>
@ -15,56 +15,59 @@ @@ -15,56 +15,59 @@
</div>
</template>
<script>
import { mapState } from 'pinia'
<script setup lang="ts">
import { ref } from 'vue'
import { _hideLoading, _showLoading, _sleep } from '@/utils'
import { useBaseStore } from '@/store/pinia'
export default {
name: 'ChooseProvince',
setup() {
const baseStore = useBaseStore()
return { baseStore }
},
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()
history.go(-3)
}
}
defineOptions({
name: 'ChooseCity'
})
const store = useBaseStore()
const list = ref([
'河北',
'山西',
'辽宁',
'吉林',
'黑龙江',
'江苏',
'浙江',
'安徽',
'福建',
'江西',
'山东',
'河南',
'湖北',
'湖南',
'广东',
'海南',
'四川',
'贵州',
'云南',
'陕西',
'甘肃',
'青海',
'台湾',
'内蒙古',
'广西',
'西藏',
'宁夏',
'新疆',
'北京',
'天津',
'上海',
'重庆',
'香港',
'澳门'
])
async function save() {
_showLoading()
store.userinfo = { ...store.userinfo, ...{ location: '中国-四川-成都' } }
await _sleep(500)
_hideLoading()
history.go(-3)
}
</script>

56
src/pages/me/userinfo/ChooseDepartment.vue

@ -5,12 +5,17 @@ @@ -5,12 +5,17 @@
<span class="f16">选择院系</span>
</template>
<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>
</BaseHeader>
<div class="content">
<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 }}
</div>
</div>
@ -18,33 +23,36 @@ @@ -18,33 +23,36 @@
</div>
</template>
<script>
export default {
name: 'ChooseSchool',
components: {},
data() {
return {
departments: [],
schoolName: ''
}
},
computed: {},
created() {
for (let i = 0; i < 5; i++) {
this.departments.push('院系' + i)
}
},
methods: {
setDepartment(val) {
localStorage.setItem('changeDepartment', val)
this.$back()
}
<script setup lang="ts">
import { onMounted, reactive } from 'vue'
import { useNav } from '@/utils/hooks/useNav.js'
import { useRouter } from 'vue-router'
defineOptions({
name: 'ChooseDepartment'
})
const router = useRouter()
const nav = useNav()
const data = reactive({
departments: [],
schoolName: ''
})
onMounted(() => {
for (let i = 0; i < 5; i++) {
data.departments.push('院系' + i)
}
})
function setDepartment(val) {
localStorage.setItem('changeDepartment', val)
router.back()
}
</script>
<style scoped lang="less">
@import '../../../assets/less/index';
@import '@/assets/less/index';
.choose-school {
position: fixed;

2317
src/pages/me/userinfo/ChooseLocation.vue

File diff suppressed because it is too large Load Diff

91
src/pages/me/userinfo/ChooseProvince.vue

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

107
src/pages/me/userinfo/ChooseSchool.vue

@ -5,45 +5,50 @@ @@ -5,45 +5,50 @@
<span class="f16">添加学校</span>
</template>
<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 v-slot:bottom>
<Search
class="mt1r mb1r ml2r mr2r"
placeholder="搜索大学名称"
v-model="schoolName"
@clear="isSearch = false"
v-model="data.schoolName"
@clear="data.isSearch = false"
:is-show-right-text="true"
@notice="search"
></Search>
</template>
</BaseHeader>
<div class="content">
<div class="nearby" v-if="!isSearch">
<div class="nearby" v-if="!data.isSearch">
<div class="title">
<img src="../../../assets/img/icon/location.svg" alt="" />
<span>离我最近</span>
</div>
<template v-if="nearby.length">
<div class="item" :key="i" v-for="(item, i) in nearby" @click="setSchool(item)">
<template v-if="data.nearby.length">
<div class="item" :key="i" v-for="(item, i) in data.nearby" @click="setSchool(item)">
{{ item }}
</div>
</template>
<div v-else class="item">无法获取</div>
</div>
<div class="line" style="width: calc(100% - 40rem); margin-left: 20rem"></div>
<div class="schools" v-if="!isSearch">
<div class="item" :key="i" v-for="(item, i) in schools" @click="setSchool(item)">
<div class="schools" v-if="!data.isSearch">
<div class="item" :key="i" v-for="(item, i) in data.schools" @click="setSchool(item)">
{{ item }}
</div>
</div>
<div v-if="isSearch">
<template v-if="searchSchools.length">
<div class="item" :key="i" v-for="(item, i) in searchSchools" @click="setSchool(item)">
<span v-if="item.indexOf(schoolName) > -1">
{{ item.substr(0, item.indexOf(schoolName)) }}
<span style="color: #f50">{{ schoolName }}</span>
{{ item.substr(item.indexOf(schoolName) + schoolName.length) }}
<div v-if="data.isSearch">
<template v-if="data.searchSchools.length">
<div
class="item"
:key="i"
v-for="(item, i) in data.searchSchools"
@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 v-else>{{ item }}</span>
</div>
@ -52,48 +57,50 @@ @@ -52,48 +57,50 @@
<img src="../../../assets/img/icon/head-image.jpeg" alt="" />
<div class="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>
</template>
<script>
import Search from '../../../components/Search'
export default {
name: 'ChooseSchool',
components: {
Search
},
data() {
return {
isSearch: false,
nearby: [],
schools: [],
searchSchools: [],
schoolName: ''
}
},
created() {
for (let i = 0; i < 20; i++) {
this.nearby.push('附近大学' + i)
this.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))
}
<script setup lang="ts">
import Search from '../../../components/Search.vue'
import { onMounted, reactive } from 'vue'
import { useNav } from '@/utils/hooks/useNav.js'
import { useRouter } from 'vue-router'
defineOptions({
name: 'ChooseSchool'
})
const router = useRouter()
const nav = useNav()
const data = reactive({
isSearch: false,
nearby: [],
schools: [],
searchSchools: [],
schoolName: ''
})
onMounted(() => {
for (let i = 0; i < 20; i++) {
data.nearby.push('附近大学' + i)
data.schools.push('所有大学' + i)
}
})
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>

63
src/pages/me/userinfo/DeclareSchool.vue

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

47
src/pages/me/userinfo/DisplayType.vue

@ -8,46 +8,49 @@ @@ -8,46 +8,49 @@
<div class="content">
<div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.ALL)">
<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="" />
</div>
</div>
<div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.SCHOOL)">
<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="" />
</div>
</div>
<div class="row" @click="setDisplayType(enums.DISPLAY_TYPE.ME)">
<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="" />
</div>
</div>
</div>
</div>
</template>
<script>
<script setup lang="ts">
import enums from '../../../utils/enums'
export default {
name: 'DisplayType',
data() {
return {
enums,
displayType: enums.DISPLAY_TYPE.ALL
}
},
created() {
this.displayType = ~~this.$route.query.displayType
},
methods: {
setDisplayType(type) {
this.displayType = type
localStorage.setItem('changeDisplayType', type)
this.$back()
}
}
import { onMounted, reactive } from 'vue'
import { useRoute, useRouter } from 'vue-router'
defineOptions({
name: 'DisplayType'
})
const router = useRouter()
const route = useRoute()
const data = reactive({
displayType: enums.DISPLAY_TYPE.ALL
})
onMounted(() => {
data.displayType = ~~route.query.displayType
})
function setDisplayType(type) {
data.displayType = type
localStorage.setItem('changeDisplayType', type)
router.back()
}
</script>

220
src/pages/me/userinfo/EditUserInfo.vue

@ -11,29 +11,29 @@ @@ -11,29 +11,29 @@
<div class="userinfo">
<div class="change-avatar">
<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="" />
</div>
<span>点击更换头像</span>
</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="right">
<span>{{ isEmpty(userinfo.nickname) }}</span>
<span>{{ isEmpty(store.userinfo.nickname) }}</span>
<dy-back scale=".8" direction="right"></dy-back>
</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="right">
<span>{{ isEmpty(_getUserDouyinId({ author: userinfo })) }}</span>
<span>{{ isEmpty(_getUserDouyinId({ author: store.userinfo })) }}</span>
<dy-back scale=".8" direction="right"></dy-back>
</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="right">
<span>{{ isEmpty(userinfo.signature) }}</span>
<span>{{ isEmpty(store.userinfo.signature) }}</span>
<dy-back scale=".8" direction="right"></dy-back>
</div>
</div>
@ -47,143 +47,143 @@ @@ -47,143 +47,143 @@
<div class="row" @click="showBirthdayDialog">
<div class="left">生日</div>
<div class="right">
<span>{{ isEmpty(userinfo.user_age) }}</span>
<span>{{ isEmpty(store.userinfo.user_age) }}</span>
<div v-show="false" id="trigger1"></div>
<dy-back scale=".8" direction="right"></dy-back>
</div>
</div>
<div class="row" @click="$nav('/me/choose-location')">
<div class="row" @click="nav('/me/choose-location')">
<div class="left">所在地</div>
<div class="right">
<span v-if="userinfo.province || userinfo.city">
{{ userinfo.province }}
<template v-if="userinfo.province && userinfo.city"> - </template>
{{ userinfo.city }}
<span v-if="store.userinfo.province || store.userinfo.city">
{{ store.userinfo.province }}
<template v-if="store.userinfo.province && store.userinfo.city"> - </template>
{{ store.userinfo.city }}
</span>
<dy-back scale=".8" direction="right"></dy-back>
</div>
</div>
<div class="row" @click="$nav('/me/add-school')">
<div class="row" @click="nav('/me/add-school')">
<div class="left">学校</div>
<div class="right">
<span>{{ isEmpty(userinfo.school?.name) }}</span>
<span>{{ isEmpty(store.userinfo.school?.name) }}</span>
<dy-back scale=".8" direction="right"></dy-back>
</div>
</div>
</div>
<transition name="fade">
<div class="preview-img" v-if="previewImg" @click="previewImg = ''">
<img class="resource" :src="previewImg" alt="" />
<div class="preview-img" v-if="data.previewImg" @click="data.previewImg = ''">
<img class="resource" :src="data.previewImg" alt="" />
<img
class="download"
src="../../../assets/img/icon/components/video/download.png"
alt=""
@click.stop="$no"
@click.stop="_no"
/>
</div>
</transition>
</div>
</template>
<script>
<script setup lang="ts">
import MobileSelect from '../../../components/mobile-select/mobile-select'
import { mapState } from '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 {
name: 'EditUserInfo',
setup() {
const baseStore = useBaseStore()
return { baseStore }
},
components: {},
data() {
return {
sexList: [
{ id: 1, name: '男' },
{ id: 2, name: '女' },
{ id: 3, name: '不展示' }
],
avatarList: [
{ id: 1, name: '拍一张' },
{ id: 2, name: '从相册选择' },
{ id: 3, name: '查看大图' },
{ id: 4, name: '取消' }
],
previewImg: ''
defineOptions({
name: 'EditUserInfo'
})
const store = useBaseStore()
const nav = useNav()
const data = reactive({
sexList: [
{ id: 1, name: '男' },
{ id: 2, name: '女' },
{ id: 3, name: '不展示' }
],
avatarList: [
{ id: 1, name: '拍一张' },
{ id: 2, name: '从相册选择' },
{ id: 3, name: '查看大图' },
{ id: 4, name: '取消' }
],
previewImg: ''
})
const sex = computed(() => {
switch (store.userinfo.gender) {
case 1:
return '男'
case 2:
return '女'
default:
return ''
}
})
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
}
},
computed: {
...mapState(useBaseStore, ['userinfo']),
sex() {
switch (this.userinfo.gender) {
case 1:
return '男'
case 2:
return '女'
default:
return ''
})
}
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)
}
}
},
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
}
],
callback: async (indexArr, data) => {
_showLoading()
await _sleep(500)
store.setUserinfo({
...store.userinfo,
birthday: data.join('-')
})
},
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()
_hideLoading()
}
}
}).show()
}
</script>

136
src/pages/me/userinfo/EditUserInfoItem.vue

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

252
src/pages/message/AllMessage.vue

@ -2,61 +2,65 @@ @@ -2,61 +2,65 @@
<div id="AllMessage">
<BaseHeader>
<template v-slot:center>
<div class="center" @click="isShowType = !isShowType">
<div class="center" @click="data.isShowType = !data.isShowType">
<span class="f16">{{ showTypeText }}</span>
<img
:class="{ show: isShowType }"
src="../../assets/img/icon/arrow-up-white.png"
:class="{ show: data.isShowType }"
src="@/assets/img/icon/arrow-up-white.png"
alt=""
/>
</div>
</template>
</BaseHeader>
<transition name="fade">
<div class="type-dialog" v-if="isShowType">
<div class="type-dialog" v-if="data.isShowType">
<div class="dialog-content">
<div class="row" @click="toggleShowType(1)">
<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>
</div>
</div>
<div class="row" @click="toggleShowType(2)">
<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>
</div>
</div>
<div class="row" @click="toggleShowType(3)">
<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>
</div>
</div>
<div class="row" @click="toggleShowType(4)">
<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>
</div>
</div>
</div>
<div class="mask" @click="isShowType = false"></div>
<div class="mask" @click="data.isShowType = false"></div>
</div>
</transition>
<div class="content">
<Loading v-if="loading" />
<Loading v-if="data.loading" />
<Scroll
v-else
ref="mainScroll"
:use-refresh="true"
:loading="loadingMore"
:loading="data.loadingMore"
@refresh="refresh"
@pulldown="loadData"
>
<div class="messages">
<div class="message" @click="$nav('/message/visitors')">
<div class="message" @click="nav('/message/visitors')">
<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 class="right">
<div class="desc">
@ -68,27 +72,31 @@ @@ -68,27 +72,31 @@
<div class="time">01-11</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 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">
<img v-lazy="$imgPreview(item.author.avatar)" alt="" class="avatar" />
<img v-lazy="_checkImgUrl(item.author.avatar)" alt="" class="avatar" />
<img
v-if="selectShowType === 2"
src="../../assets/img/icon/message/love-message.webp"
v-if="data.selectShowType === 2"
src="@/assets/img/icon/message/love-message.webp"
alt=""
class="type"
/>
<img
v-if="selectShowType === 3"
src="../../assets/img/icon/message/call-message.webp"
v-if="data.selectShowType === 3"
src="@/assets/img/icon/message/call-message.webp"
alt=""
class="type"
/>
<img
v-if="selectShowType === 4"
src="../../assets/img/icon/message/comment-message.webp"
v-if="data.selectShowType === 4"
src="@/assets/img/icon/message/comment-message.webp"
alt=""
class="type"
/>
@ -100,130 +108,130 @@ @@ -100,130 +108,130 @@
<div class="tag">朋友</div>
</div>
<div class="desc-content">
<span v-if="selectShowType === 1">好好看啊</span>
<span v-if="selectShowType === 2">赞了你的作品</span>
<span v-if="selectShowType === 3">@{{ userinfo.nickname }}</span>
<span v-if="selectShowType === 4">好好看啊</span>
<span v-if="data.selectShowType === 1">好好看啊</span>
<span v-if="data.selectShowType === 2">赞了你的作品</span>
<span v-if="data.selectShowType === 3">@{{ store.userinfo.nickname }}</span>
<span v-if="data.selectShowType === 4">好好看啊</span>
</div>
<div class="bottom">
<div class="type" v-if="selectShowType === 3">在评论中提到了你</div>
<div class="type" v-if="selectShowType === 4">回复了你的评论</div>
<div class="type" v-if="data.selectShowType === 3">在评论中提到了你</div>
<div class="type" v-if="data.selectShowType === 4">回复了你的评论</div>
<div class="time">01-11</div>
</div>
</div>
<img
v-lazy="$imgPreview(item.video + '?vframe/jpg/offset/0/w/300')"
v-lazy="_checkImgUrl(item.video + '?vframe/jpg/offset/0/w/300')"
alt=""
class="poster"
/>
</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>
<dy-back />
</div>
</div>
<div class="title">
<span>朋友推荐</span>
<img src="../../assets/img/icon/about-gray.png" alt="" />
<img src="@/assets/img/icon/about-gray.png" alt="" />
</div>
<Peoples v-model:list="recommend" :loading="loadingMore" mode="recommend" />
<Peoples v-model:list="data.recommend" :loading="data.loadingMore" mode="recommend" />
</Scroll>
</div>
</div>
</template>
<script>
import { mapState } from 'pinia'
import Scroll from '../../components/Scroll'
import Loading from '../../components/Loading'
import Peoples from '../people/components/Peoples'
import resource from '../../assets/data/resource.js'
import BasePage from '../BasePage'
<script setup lang="ts">
import Scroll from '@/components/Scroll.vue'
import Loading from '@/components/Loading.vue'
import Peoples from '../people/components/Peoples.vue'
import resource from '@/assets/data/resource.js'
import { useBaseStore } from '@/store/pinia'
import { _checkImgUrl } from '@/utils'
export default {
extends: BasePage,
name: 'AllMessage',
components: {
Scroll,
Loading,
Peoples
},
data() {
return {
loading: false,
loadingMore: false,
isShowType: false,
showAll: false,
recommend: [],
messages: [],
selectShowType: 1
}
},
computed: {
...mapState(useBaseStore, ['friends', 'userinfo']),
showTypeText() {
switch (this.selectShowType) {
case 1:
return '全部消息'
case 2:
return '赞'
case 3:
return '@我的'
case 4:
return '评论'
default:
return ''
}
},
showMessageList() {
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)
}
import { _checkImgUrl, _no, _notice, _sleep, cloneDeep } from '@/utils'
import { computed, onMounted, reactive } from 'vue'
import { useNav } from '@/utils/hooks/useNav.js'
defineOptions({
name: 'AllMessage'
})
const store = useBaseStore()
const nav = useNav()
const data = reactive({
loading: false,
loadingMore: false,
isShowType: false,
showAll: false,
recommend: [],
fans: [],
messages: [],
selectShowType: 1
})
onMounted(() => {
getData()
})
const showTypeText = computed(() => {
switch (data.selectShowType) {
case 1:
return '全部消息'
case 2:
return '赞'
case 3:
return '@我的'
case 4:
return '评论'
default:
return ''
}
})
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>

109
src/pages/message/JoinedGroupChat.vue

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

257
src/pages/message/Message.vue

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

61
src/pages/message/MoreSearch.vue

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

107
src/pages/message/Visitors.vue

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

146
src/pages/message/notice/DouyinHelper.vue

@ -5,13 +5,13 @@ @@ -5,13 +5,13 @@
<span class="f16">抖音小助手</span>
</template>
</BaseHeader>
<Loading v-if="loading" />
<Loading v-if="data.loading" />
<Scroll v-else ref="mainScroll">
<div class="content">
<NoMore />
<div class="list">
<!--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">
{{ item.title }}
<div class="ml1r not-read" v-if="!item.read"></div>
@ -28,82 +28,78 @@ @@ -28,82 +28,78 @@
</Scroll>
</div>
</template>
<script>
import { nextTick } from 'vue'
import Scroll from '../../../components/Scroll'
import BasePage from '../../BasePage'
<script setup lang="ts">
import { nextTick, onMounted, reactive } from 'vue'
import Scroll from '@/components/Scroll.vue'
import { _no, _sleep } from '@/utils'
export default {
extends: BasePage,
name: 'DouyinHelper',
components: { Scroll },
data() {
return {
loading: false,
list: [
{
read: false,
title: '叮!你有一条《长津湖》观看提醒',
time: '2021-10-12 12:12',
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()
defineOptions({
name: 'DouyinHelper'
})
const data = reactive({
loading: false,
list: [
{
read: false,
title: '叮!你有一条《长津湖》观看提醒',
time: '2021-10-12 12:12',
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.无关]'
},
goDetail(item) {
item.read = true
this.$no()
{
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>

202
src/pages/message/notice/SystemNotice.vue

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

12
src/pages/shop/GoodsDetail.vue

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

2
src/pages/shop/Shop.vue

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

2
src/pages/test/Test.vue

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

28
src/pages/test/Test4.vue

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

6
src/router/index.js → src/router/index.ts

@ -17,7 +17,7 @@ const router = createRouter({ @@ -17,7 +17,7 @@ const router = createRouter({
router.beforeEach((to, from) => {
const baseStore = useBaseStore()
//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) {
return true
}
@ -28,7 +28,7 @@ router.beforeEach((to, from) => { @@ -28,7 +28,7 @@ router.beforeEach((to, from) => {
if (toDepth > fromDepth) {
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})
baseStore.updateExcludeRoutes({ type: 'remove', value: toComponentName })
// console.log('to', toComponentName)
@ -37,7 +37,7 @@ router.beforeEach((to, from) => { @@ -37,7 +37,7 @@ router.beforeEach((to, from) => {
}
} else {
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})
baseStore.updateExcludeRoutes({ type: 'add', value: fromComponentName })

9
src/router/routes.js → src/router/routes.ts

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

8
src/store/pinia.js

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

5
src/utils/const_var.js

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

184
src/utils/index.jsx

@ -427,6 +427,10 @@ const Utils = { @@ -427,6 +427,10 @@ const Utils = {
export default Utils
export function _dateFormat(val, type) {
return Utils.$dateFormat(val, type)
}
export function $no() {
Utils.$no(arguments)
}
@ -435,8 +439,8 @@ export function $notice(val) { @@ -435,8 +439,8 @@ export function $notice(val) {
Utils.$notice(val)
}
export function _notice(val) {
Utils.$notice(val)
export function _time(val) {
return Utils.$time(val)
}
export function _checkImgUrl(url) {
@ -467,6 +471,9 @@ export function _getUserDouyinId(item) { @@ -467,6 +471,9 @@ export function _getUserDouyinId(item) {
return item.author.unique_id || item.author.short_id
}
/**
* @param {number} duration
*/
export function _sleep(duration) {
return new Promise((resolve) => {
setTimeout(resolve, duration)
@ -499,3 +506,176 @@ export function sampleSize(arr, num) { @@ -499,3 +506,176 @@ export function sampleSize(arr, num) {
}
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

@ -0,0 +1,23 @@ @@ -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

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

19
tsconfig.node.json

@ -0,0 +1,19 @@ @@ -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"]
}
}

27
vite.config.js → vite.config.ts

@ -1,20 +1,23 @@ @@ -1,20 +1,23 @@
import { defineConfig } from 'vite'
import { defineConfig, PluginOption } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx'
import { resolve } from 'path'
import { visualizer } from 'rollup-plugin-visualizer'
import DefineOptions from 'unplugin-vue-define-options/vite' // 引入插件
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 viteCompression from 'vite-plugin-compression'
function pathResolve(dir) {
return resolve(__dirname, '.', dir)
}
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({
base: './',
envDir: 'env',
@ -29,7 +32,7 @@ export default defineConfig({ @@ -29,7 +32,7 @@ export default defineConfig({
// // exclude: [/node_modules/, /jQuery\.js/]
// // }
// }),
lifecycle === 'report' ? visualizer({ open: false }) : null,
lifecycle === 'report' ? (visualizer({ open: false }) as any as PluginOption) : null,
DefineOptions(),
Vue(),
VueJsx(),
@ -55,11 +58,7 @@ export default defineConfig({ @@ -55,11 +58,7 @@ export default defineConfig({
var: 'Mock',
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',
var: '$',
@ -109,7 +108,7 @@ export default defineConfig({ @@ -109,7 +108,7 @@ export default defineConfig({
],
resolve: {
alias: {
'@': pathResolve('src')
'@': fileURLToPath(new URL('./src', import.meta.url))
},
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
},
@ -121,7 +120,7 @@ export default defineConfig({ @@ -121,7 +120,7 @@ export default defineConfig({
manualChunks(id, { getModuleInfo }) {
const reg = /(.*)\/src\/components\/(.*)/
if (reg.test(id)) {
const importersLen = getModuleInfo(id).importers.length
const importersLen = getModuleInfo(id)?.importers.length ?? 0
// 被多处引用
if (importersLen > 1) return 'common'
}
Loading…
Cancel
Save