Последняя версия с сервера прошлого разработчика

This commit is contained in:
2025-07-10 04:35:51 +00:00
commit c731570032
1174 changed files with 134314 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
<template>
<div class="shadow-classic rounded-md bg-indigo-200 p-3 md:px-5 md:py-7 relative">
<div class="absolute inset-0 flex items-center justify-center z-10 text-white text-lg w-full object-cover font-medium">
18 + Контент
</div>
<div class="blur-2xl flex flex-grow bg-indigo-300 w-full h-36 object-cover">
</div>
</div>
</template>
<script>
export default {
props: {
entity: Object,
user: Object,
feed_id: Number,
},
}
</script>

View File

@@ -0,0 +1,47 @@
<template>
<div class="mt-3 md:mt-6 feed-footer">
<div class="flex justify-between">
<div v-if="ads"></div>
<div v-else class="misc-info flex space-x-4">
<like-count :likes="likes" :liked="liked"
@likeFeed="likeFeed"
/>
<comment-count :comments="comments" />
<share-count />
</div>
<view-count :count="count" />
</div>
</div>
</template>
<script>
import LikeCount from '@/Shared/Misc/LikeCount.vue'
import ViewCount from '@/Shared/Misc/ViewCount.vue'
import CommentCount from '@/Shared/Misc/CommentCount.vue'
import ShareCount from '@/Shared/Misc/ShareCount.vue'
export default {
components: {
LikeCount,
CommentCount,
ShareCount,
ViewCount,
},
props: {
comments: Number,
likes: Number,
liked: Boolean,
ads: Boolean,
count: {
type: Number,
default: 0
},
},
emits:['likeFeed'],
methods:{
likeFeed(){
this.$emit('likeFeed')
}
}
}
</script>

View File

@@ -0,0 +1,80 @@
<template>
<div class="feed-header flex items-center justify-between" @click.stop="">
<div v-if="entity.is_ads" class="flex items-center gap-3">
<user-avatar :user="user" size="small"
class="w-10 h-10 md:w-14 md:h-14 text-lg"
/>
<div class="flex flex-col">
<span class="text-sm md:text-base block font-medium text-white">{{ user.name }}</span>
</div>
</div>
<div v-else class="flex items-center">
<inertia-link :href="route('profile.user', user.username)" class="flex-shrink-0 block mr-2 md:mr-4">
<user-avatar :user="user" size="small"
class="w-10 h-10 md:w-14 md:h-14 text-lg"
/>
</inertia-link>
<inertia-link :href="route('profile.user', user.username)" class="flex flex-col">
<span class="hover:underline text-sm md:text-base block font-medium text-white">{{ user.name }}</span>
<span class="hover:underline text-xs text-gray-light">{{ created_at }}</span>
</inertia-link>
</div>
<div v-if="entity.is_ads == false" class="flex-shrink-0 text-white">
<dropdown-menu-point>
<MenuItems class="origin-top-right absolute right-0 mt-2 w-64 bg-indigo-300 shadow-lg max-h-60 rounded-md text-base ring-1 ring-indigo-200 overflow-auto focus:outline-none">
<MenuItem v-if="user.id === $page.props.auth.user.id">
<inertia-link :href="route(`${entity.type}.edit`, entity.slug)" class="w-full group flex items-center px-4 py-2 text-base hover:bg-indigo-100 text-gray-light">
<PencilAltIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
Редактировать
</inertia-link>
</MenuItem>
<MenuItem v-if="user.id === $page.props.auth.user.id">
<button class="w-full group flex items-center px-4 py-2 text-base hover:bg-indigo-100 text-gray-light" @click="onRemoveFeed()">
<MinusCircleIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
Удалить
</button>
</MenuItem>
<MenuItem v-if="user.id !== $page.props.auth.user.id">
<inertia-link :href="route('complaint.reason', feed_id)" class="group flex items-center px-4 py-2 text-base hover:bg-indigo-100 text-gray-light">
<ExclamationIcon class="mr-3 h-5 w-5 text-gray-400 group-hover:text-orange" aria-hidden="true" />
Пожаловаться
</inertia-link>
</MenuItem>
</MenuItems>
</dropdown-menu-point>
</div>
</div>
</template>
<script>
import { MenuItem, MenuItems } from '@headlessui/vue'
import { MinusCircleIcon, ExclamationIcon, PencilAltIcon } from '@heroicons/vue/solid'
import UserAvatar from '@/Shared/Misc/UserAvatar.vue'
import DropdownMenuPoint from '@/Shared/Form/DropdownMenuPoint.vue'
export default {
components: {
UserAvatar,
DropdownMenuPoint,
MinusCircleIcon,
ExclamationIcon,
PencilAltIcon,
MenuItem,
MenuItems,
},
props: {
user: Object,
created_at: String,
feed_id: Number,
entity: Object,
},
emits: ['onRemoveFeed'],
methods: {
onRemoveFeed() {
this.$emit('onRemoveFeed')
},
},
}
</script>

View File

@@ -0,0 +1,115 @@
<template>
<modal-feed
:modal-feed="modalFeed"
:open="show"
@close-modal="closeModal"
@destroyFeed="destroyFeed"
/>
<modal-warning
:feed_id="modalFeedId"
:open="warningShow"
@action="deleteActionModal"
/>
<InfinityScroll :node-element="lastNodeLement" :next-cursor="nextCursor"
@fromPagination="putFromPagination"
>
<div v-for="feed in feedLists" :key="feed.id"
:ref="el => { if (el && feed.id === lastElementID) lastNodeLement = el }"
>
<feed-list-node :feed="feed" @onRemoveFeed="onRemoveFeed"
@open-modal="openModal"
/>
</div>
</InfinityScroll>
</template>
<script>
import { ref } from 'vue'
import { Inertia } from '@inertiajs/inertia'
import filter from 'lodash/filter'
import FeedListNode from '@/Shared/FeedList/FeedListNode.vue'
import ModalFeed from '@/Shared/Overlay/ModalFeed.vue'
import ModalWarning from '@/Shared/Overlay/ModalWarning.vue'
import InfinityScroll from '@/Shared/Misc/InfinityScroll.vue'
export default {
components: {
FeedListNode,
ModalFeed,
ModalWarning,
InfinityScroll,
},
props: {
feeds: Array,
nextCursor: { type: String, default: '' },
},
setup() {
const containerRef = ref(null)
return {
lastNodeLement: containerRef,
}
},
data() {
return {
show: false,
modalFeedId: 0,
warningShow: false,
feedLists: [],
modalFeed: {},
}
},
computed: {
lastElementID() {
return this.feedLists[this.feedLists.length - 1]?.id
},
},
mounted() {
this.feedLists = this.feeds
},
methods: {
putFromPagination(lists) {
for (let list of lists) {
this.feedLists.push(list)
}
},
deleteActionModal(removeFeed) {
if (removeFeed) {
this.destroyFeed(this.modalFeedId)
}
this.warningShow = false
},
onRemoveFeed(id) {
this.warningShow = true
this.modalFeedId = id
},
destroyFeed(id) {
Inertia.delete(route('feed.destroy', id), {
preserveScroll: true,
preserveState: true,
})
this.feedLists = filter(this.feedLists, function (x) {
return x.id !== id
})
},
openModal(feed) {
this.show = true
this.modalFeed = feed
},
closeModal() {
this.show = false
},
},
}
</script>

View File

@@ -0,0 +1,86 @@
<template>
<component
:is="currentTypeNode"
:feed_id="feed.id"
:user="feed.user"
:entity="feed.entity"
@click.prevent="openModal(feed)"
@onRemoveFeed="onRemoveFeed"
@like-feed="likeFeed"
></component>
</template>
<script>
import { Inertia } from '@inertiajs/inertia'
import { usePage } from '@inertiajs/inertia-vue3'
import axios from 'axios'
import FeedImages from '@/Shared/FeedList/Images.vue'
import FeedVideos from '@/Shared/FeedList/Videos.vue'
import FeedMusics from '@/Shared/FeedList/Musics.vue'
import FeedAdults from '@/Shared/FeedList/Adults.vue'
import FeedProhibited from '@/Shared/FeedList/Prohibited.vue'
export default {
components: {
FeedImages,
FeedVideos,
FeedMusics,
FeedAdults,
FeedProhibited,
},
props: {
feed: Object,
},
emits: ['openModal', 'onRemoveFeed'],
computed: {
authUser() {
return usePage().props.value.auth.user
},
currentTypeNode() {
return 'feed-' + this.feed.type.toLowerCase()
},
},
methods: {
onRemoveFeed(id) {
this.$emit('onRemoveFeed', id)
},
openModal(feed) {
if(feed.entity){
this.$emit('openModal', feed)
}
},
likeFeed() {
axios
.post(route('feed.like', this.feed.id))
.then(() => {
if (this.feed.entity.liked) {
this.feed.entity.liked = false
this.feed.entity.likes--
} else {
this.feed.entity.liked = true
this.feed.entity.likes++
}
})
// Inertia.post(
// route('feed.like', this.feed.id),
// {},
// {
// preserveScroll: true,
// preserveState: true,
// }
// )
// if (this.feed.entity.liked) {
// this.feed.entity.liked = false
// this.feed.entity.likes--
// } else {
// this.feed.entity.liked = true
// this.feed.entity.likes++
// }
},
},
}
</script>

View File

@@ -0,0 +1,109 @@
<template>
<div class="">
<div class="flex items-center border-b border-indigo-100">
<div class="flex-shrink-0 mr-5 w-28 md:w-56">
<feed-preview class="h-28 w-28 md:w-56 md:h-56 object-cover" type="music"
:source="preview"
/>
</div>
<div class="flex flex-col">
<span class="text-base md:text-xl font-semibold text-gray">{{ title }}</span>
</div>
</div>
<div data-simplebar class="max-h-72 overflow-auto">
<div class="divide-y divide-indigo-100" @click.stop="">
<div
v-for="media in medias"
:key="media.id" :class="[currentSong?.id === media.id ?
'bg-indigo-300 bg-opacity-25' :
'hover:bg-indigo-300 hover:bg-opacity-25',
'p-4 flex items-center space-x-4']"
@click.prevent="startPlay(media)"
>
<div class="flex">
<toggle-play-button :media_id="media.id" />
</div>
<div class="flex-1 text-sm text-gray-light">
{{ media.name }}
</div>
<div class="text-sm text-gray-light">
<div v-show="currentSong?.id === media.id">
{{ seek }}
</div>
<div v-show="currentSong?.id !== media.id">
{{ media.time }}
</div>
</div>
<music-add-count v-if="$page.props.auth.user" :media_id="media.id"
:liked="media.liked" @addAudio="likeAudio"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapState, mapGetters } from 'vuex'
import find from 'lodash/find'
import { Inertia } from '@inertiajs/inertia'
import FeedPreview from '@/Shared/Feed/FeedPreview.vue'
import TogglePlayButton from '@/Shared/Misc/TogglePlayButton.vue'
import MusicAddCount from '@/Shared/Misc/MusicAddCount.vue'
export default {
components: {
FeedPreview,
TogglePlayButton,
MusicAddCount,
},
props: {
medias: Array,
feed_id: Number,
title: String,
preview: String,
},
emits: ['trackClick'],
computed: {
...mapGetters(['playing']),
...mapState({
playlist_id: (state) => state.player.playlist_id,
seek: (state) => state.player.seek,
currentSong: (state) => state.player.currentSong,
}),
},
methods: {
likeAudio(media_id) {
Inertia.post(
route('feed.audio.like', media_id),
{},
{
preserveScroll: true,
preserveState: true,
}
)
let media = find(this.medias, (item) => item.id === media_id)
if(media){
media.liked = !media.liked
}
},
...mapActions(['toggleAudio', 'newCurrentPlaylist', 'skipToIndexByMusic']),
startPlay(music) {
this.$emit('trackClick')
if (this.currentSong?.id === music.id) {
this.toggleAudio()
return
}
// if (this.playlist_id === this.feed_id) {
// this.skipToIndexByMusic(music);
// return;
// }
this.newCurrentPlaylist([music, this.medias, this.feed_id])
},
},
}
</script>

View File

@@ -0,0 +1,104 @@
<template>
<div v-if="disabled === 0 && is_paid === 1 && $page.props.auth.user.id !== user_id" class="mt-3 rounded-md bg-indigo-300 p-4"
@click.stop=""
>
<div v-if="bought == 0" class="flex items-center justify-center">
<div class="mr-5 mb-2 md:mb-0 font-bold text-center md:text-left w-full md:w-auto">
Цена: {{ price }}
</div>
<div>
<button type="button" class="my-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-1 md:py-3 justify-center text-sm md:text-base rounded-md text-white bg-pink focus:outline-none"
@click="purchaseFeed"
>
Купить
</button>
</div>
</div>
<div v-else class="flex flex-wrap items-center justify-center">
<div class="paid-feed-msg mr-5 mb-2 md:mb-0 font-bold text-center md:text-left w-full md:w-auto">
Данный товар вами куплен
</div>
<button v-if="disable_btn == 0 && paid_open == 0" type="button"
class="my-1 mx-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-1 md:py-3 justify-center text-sm md:text-base rounded-md text-white bg-orange focus:outline-none" @click="replaceFeed"
>
Показать
</button>
<inertia-link :href="route('setting.show.purchases', feed_id)" class="my-1 mx-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-1 md:py-3 justify-center text-sm md:text-base rounded-md text-white bg-pink focus:outline-none">
Скачать
</inertia-link>
</div>
</div>
<div v-else-if="is_paid === 1 && $page.props.auth.user.id === user_id" class="p-4 text-center"
@click.stop=""
>
<button v-if="paid_open === 0" type="button"
class="my-1 mx-1 transition shadow-none hover:shadow-classic2 inline-flex items-center px-8 py-1 md:py-2 justify-center text-sm rounded-md text-white bg-orange focus:outline-none" @click="replaceFeed"
>
Показать эксклюзивный контент
</button>
</div>
</template>
<script>
import axios from 'axios'
export default {
components: {},
props: {
user_id: Number,
is_paid: Number,
feed_id: Number,
price: String,
paid_open: {
type: Number,
default: 0,
},
},
emits: ['onReplaceFeed'],
data() {
return {
bought: 0,
disable_btn: 0,
disabled: 1,
}
},
mounted() {
if (this.is_paid === 1) {
axios
.post(route('feeds.purchase_check', this.feed_id))
.then(({ data }) => {
if (data === 1) {
this.bought = 1
}
this.disabled = 0
})
}
},
methods: {
purchaseFeed() {
axios.post(route('feed.purchase', this.feed_id)).then(({ data }) => {
if (data.error == 1) {
alert(data.msg)
} else {
this.$emit('onReplaceFeed', data)
this.disable_btn = 1
this.bought = 1
}
})
},
replaceFeed() {
axios.post(route('feeds.replace', this.feed_id)).then(({ data }) => {
if (data.collection) {
this.$emit('onReplaceFeed', data)
this.disable_btn = 1
}
})
},
},
}
</script>

View File

@@ -0,0 +1,101 @@
<template>
<div class="shadow-classic rounded-md bg-indigo-200 p-3 md:px-5 md:py-7">
<feed-header
:entity="entity"
:feed_id="feed_id"
:created_at="entity.created_at_humans"
:user="user"
@onRemoveFeed="onRemoveFeed"
/>
<div class="mt-3 md:mt-6 feed-body">
<div class="mb-3 md:mb-6 text-gray text-sm md:text-base">
<div @click.stop="">
<div v-if="entity.is_ads" class="prose-content"
v-html="entity.body"
>
</div>
<template v-else>
<feed-tags
v-if="entity.tags.length"
class="mb-1"
:tags="entity.tags"
/>
{{ entity.body }}
</template>
</div>
<feed-paid-block
:is_paid="entity.is_paid"
:user_id="user.id"
:price="entity.price"
:feed_id="feed_id"
:paid_open="entity.paid_open"
@onReplaceFeed="onReplaceFeed"
/>
</div>
<div
:class="[{
'grid-cols-2': countMediaContent > 1
}]"
class="grid gap-1 md:gap-3 md:grid-cols-[repeat(auto-fit,minmax(280px,1fr))]"
>
<div v-for="media in entity.collection_medias" :key="media.id">
<img class="w-full h-full object-cover" :src="media.url"
alt=""
/>
</div>
</div>
</div>
<feed-footer
:likes="entity.likes"
:liked="entity.liked"
:comments="entity.comments"
:count="entity.views_count"
:ads="entity.is_ads"
@likeFeed="likeFeed"
/>
</div>
</template>
<script>
import FeedHeader from '@/Shared/FeedList/FeedHeader.vue'
import FeedFooter from '@/Shared/FeedList/FeedFooter.vue'
import FeedPaidBlock from '@/Shared/FeedList/FeedPaidBlock.vue'
import FeedTags from '@/Shared/Misc/FeedTags.vue'
export default {
components: {
FeedHeader,
FeedFooter,
FeedPaidBlock,
FeedTags,
},
props: {
entity: Object,
user: Object,
feed_id: Number,
},
emits: ['likeFeed', 'onRemoveFeed'],
computed: {
countMediaContent() {
return this.entity.collection_medias.length
}
},
methods: {
onReplaceFeed(data) {
this.entity.collection_medias = data.collection
this.entity.preview = data.preview
this.entity.paid_open = 1
},
onRemoveFeed() {
this.$emit('onRemoveFeed', this.feed_id)
},
likeFeed() {
this.$emit('likeFeed')
},
},
}
</script>

View File

@@ -0,0 +1,90 @@
<template>
<div class="shadow-classic rounded-md bg-indigo-200 p-3 md:px-5 md:py-7">
<feed-header :entity="entity" :feed_id="feed_id"
:created_at="entity.created_at_humans" :user="user"
@onRemoveFeed="onRemoveFeed"
/>
<div class="mt-3 md:mt-6 feed-body">
<div class="mb-3 md:mb-6 text-gray text-sm md:text-base" @click.stop="">
<div>
<feed-tags v-if="entity.tags.length" class="mb-1"
:tags="entity.tags"
/>
{{ entity.body }}
</div>
<feed-paid-block
:is_paid="entity.is_paid"
:user_id="user.id"
:price="entity.price"
:feed_id="feed_id"
:paid_open="entity.paid_open"
@onReplaceFeed="onReplaceFeed"
/>
</div>
<feed-music-body
:feed_id="feed_id"
:title="entity.title"
:preview="entity.preview"
:medias="entity.collection_medias"
@trackClick="addViewShow"
/>
</div>
<feed-footer
:likes="entity.likes"
:liked="entity.liked"
:comments="entity.comments"
:count="entity.views_count"
:ads="entity.is_ads"
@likeFeed="likeFeed"
/>
</div>
</template>
<script>
import FeedHeader from '@/Shared/FeedList/FeedHeader.vue'
import FeedFooter from '@/Shared/FeedList/FeedFooter.vue'
import FeedMusicBody from '@/Shared/FeedList/FeedMusicBody.vue'
import FeedPaidBlock from '@/Shared/FeedList/FeedPaidBlock.vue'
import FeedTags from '@/Shared/Misc/FeedTags.vue'
import axios from 'axios'
export default {
components: {
FeedHeader,
FeedFooter,
FeedMusicBody,
FeedPaidBlock,
FeedTags,
},
props: {
entity: Object,
user: Object,
feed_id: Number,
},
emits: ['likeFeed', 'onRemoveFeed'],
methods: {
addViewShow() {
axios
.post(route('add.view.feed', this.feed_id))
.then(({ data }) => {
data && this.entity.views_count++
})
},
onReplaceFeed(data) {
this.entity.collection_medias = data.collection
this.entity.preview = data.preview
this.entity.paid_open = 1
},
onRemoveFeed() {
this.$emit('onRemoveFeed', this.feed_id)
},
likeFeed() {
this.$emit('likeFeed')
},
},
}
</script>

View File

@@ -0,0 +1,29 @@
<template>
<div class="shadow-classic rounded-md bg-indigo-200 p-3 md:px-5 md:py-7 relative">
<div class="absolute inset-0 flex items-center justify-center z-10 text-white text-sm sm:text-lg w-full object-cover font-medium">
<div class="text-center p-2">
<p>Контент доступен по подписке!</p>
<p>
Купить: <inertia-link :href="route('profile.user', user.username)" class="font-semibold underline inline-block mr-2">
{{ user.username }}
</inertia-link>
</p>
</div>
</div>
<div class="blur-2xl flex flex-grow bg-indigo-300 w-full h-36 object-cover"></div>
</div>
</template>
<script>
export default {
props: {
entity: Object,
user: Object,
feed_id: Number,
},
}
</script>

View File

@@ -0,0 +1,118 @@
<template>
<div class="shadow-classic rounded-md bg-indigo-200 p-3 md:px-5 md:py-7">
<feed-header :entity="entity" :feed_id="feed_id"
:created_at="entity.created_at_humans" :user="user"
@onRemoveFeed="onRemoveFeed"
/>
<div class="mt-3 md:mt-6 feed-body" @click.stop="">
<div class="mb-3 md:mb-6 text-gray text-sm md:text-base">
<div @click.stop="">
<div class="flex items-start border-b border-indigo-100">
<div class="flex-shrink-0 mr-5 w-28 md:w-56">
<feed-preview class="h-28 w-28 md:w-56 md:h-56 object-cover" type="music"
:source="entity.preview"
/>
</div>
<div class="flex flex-col">
<div class="text-base md:text-xl font-semibold text-gray pb-1">
{{ entity.title }}
</div>
<div v-if="entity.is_ads" class="prose-content"
v-html="entity.body"
>
</div>
<template v-else>
<feed-tags v-if="entity.tags.length" class="mb-1"
:tags="entity.tags"
/>
<div class="pb-2">
{{ entity.body }}
</div>
</template>
</div>
</div>
</div>
<feed-paid-block
:is_paid="entity.is_paid"
:user_id="user.id"
:price="entity.price"
:feed_id="feed_id"
:paid_open="entity.paid_open"
@onReplaceFeed="onReplaceFeed"
/>
</div>
<div :class="[{
'grid-cols-2': countMediaContent > 1
}]" class="grid gap-1 md:gap-3 md:grid-cols-[repeat(auto-fit,minmax(280px,1fr))]"
@click="addViewShow"
>
<div v-for="media in entity.collection_medias" :key="media.id"
class="aspect-w-16 aspect-h-9"
>
<video :src="media.url" :poster="entity.preview"
controls
></video>
</div>
</div>
</div>
<feed-footer
:likes="entity.likes"
:liked="entity.liked"
:comments="entity.comments"
:count="entity.views_count"
:ads="entity.is_ads"
@likeFeed="likeFeed"
/>
</div>
</template>
<script>
import FeedHeader from '@/Shared/FeedList/FeedHeader.vue'
import FeedFooter from '@/Shared/FeedList/FeedFooter.vue'
import FeedPaidBlock from '@/Shared/FeedList/FeedPaidBlock.vue'
import FeedPreview from '@/Shared/Feed/FeedPreview.vue'
import FeedTags from '@/Shared/Misc/FeedTags.vue'
import axios from 'axios'
export default {
components: {
FeedHeader,
FeedFooter,
FeedPaidBlock,
FeedTags,
FeedPreview,
},
props: {
entity: Object,
user: Object,
feed_id: Number,
},
emits: ['likeFeed', 'onRemoveFeed'],
computed: {
countMediaContent() {
return this.entity.collection_medias.length
}
},
methods: {
addViewShow(){
axios
.post(route('add.view.feed', this.feed_id))
.then(({ data }) => {
data && this.entity.views_count++
})
},
onReplaceFeed(data) {
this.entity.collection_medias = data.collection
this.entity.preview = data.preview
this.entity.paid_open = 1
},
onRemoveFeed() {
this.$emit('onRemoveFeed', this.feed_id)
},
likeFeed() {
this.$emit('likeFeed')
},
},
}
</script>