🧹 清理重复配置文件
- 删除根目录中重复的 NestJS 配置文件 - 删除 tsconfig.json, tsconfig.build.json, eslint.config.mjs, .prettierrc - 保留 wwjcloud-nest/ 目录中的完整配置 - 避免配置冲突,确保项目结构清晰
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<div @click="openDialog">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<el-dialog v-model="showDialog" :title="t('upload.select' + type)" width="60%" class="attachment-dialog" :destroy-on-close="true">
|
||||
<div class="flex border-t border-b main-wrap border-color w-full h-[40vh]">
|
||||
<!-- 素材 -->
|
||||
<div class="attachment-list-wrap flex flex-col p-[15px] flex-1 overflow-hidden">
|
||||
<el-row :gutter="15" class="h-[32px]">
|
||||
<el-col :span="10">
|
||||
<div class="flex" v-if="prop.type != 'news'">
|
||||
<upload-media :type="prop.type" @success="getAttachmentList()">
|
||||
<el-button type="primary">{{ t('upload.upload' + type) }}</el-button>
|
||||
</upload-media>
|
||||
</div>
|
||||
<div class="flex" v-else>
|
||||
<el-button type="primary" :loading="syncLoading" @click="syncWechatNews">
|
||||
{{ syncLoading ? '同步中' : '同步微信图文' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="flex-1 my-[15px] h-0" v-loading="attachment.loading">
|
||||
<el-scrollbar>
|
||||
<!-- 素材管理 -->
|
||||
<div v-if="attachment.data.length">
|
||||
<div class="flex flex-wrap" v-if="prop.type != 'news'">
|
||||
<div class="attachment-item mr-[10px] mb-[10px] w-[120px]"
|
||||
v-for="(item, index) in attachment.data" :key="index" @click="selectedFile = item">
|
||||
<div
|
||||
class="attachment-wrap w-full rounded cursor-pointer overflow-hidden relative flex items-center justify-center h-[120px]">
|
||||
<el-image :src="img(item.value)" fit="contain" v-if="type == 'image'" :preview-src-list="item.image_list" />
|
||||
<video :src="img(item.value)" v-else-if="type == 'video'"></video>
|
||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
||||
<icon name="element Select" color="#fff" size="40px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative" ref="waterfallContainerRef" v-else>
|
||||
<div ref="waterfallItemRef"
|
||||
class="absolute attachment-item mr-[10px] mb-[10px] w-[280px] rounded-lg overflow-hidden border border-color"
|
||||
v-for="(item, index) in attachment.data"
|
||||
:style="{ left: listPosition[index] ? listPosition[index].left : '', top: listPosition[index] ? listPosition[index].top : '' }"
|
||||
:key="index" @click="selectedFile = item">
|
||||
<div class="relative">
|
||||
<div class="w-full h-[130px] relative">
|
||||
<el-image :src="item.value.news_item[0].thumb_url" class="w-full h-full" />
|
||||
<div class="absolute left-0 bottom-0 p-[10px] w-full truncate text-white leading-none" v-if="item.value.news_item.length > 1">
|
||||
{{ item.value.news_item[0].title }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.value.news_item.length > 1">
|
||||
<template v-for="(newsItem, newsIndex) in item.value.news_item">
|
||||
<div class="px-[15px] py-[10px] flex" :class="{'border-b border-color' : newsIndex < item.value.news_item.length - 1 }" v-if="newsIndex > 0">
|
||||
<div class="flex-1 w-0 truncate">{{ newsItem.title }}</div>
|
||||
<div class="w-[50px] h-[50px] ml-[10px]">
|
||||
<el-image :src="newsItem.thumb_url" class="w-full h-full" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="px-[15px] py-[10px]" v-else>{{ item.value.news_item[0].title }}</div>
|
||||
<div class="absolute z-[1] flex items-center justify-center w-full h-full inset-0 bg-black bg-opacity-60" v-show="selectedFile.id == item.id">
|
||||
<icon name="element Select" color="#fff" size="40px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-center" v-else>
|
||||
<el-empty v-if="!attachment.loading" :description="t('upload.mediaEmpty')" :image-size="100" />
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
<el-row :gutter="20">
|
||||
<el-col span="24">
|
||||
<div class="flex h-full justify-end items-center">
|
||||
<el-pagination v-model:current-page="attachment.page" :small="true"
|
||||
v-model:page-size="attachment.limit" :page-sizes="[10, 20, 30, 40, 60]"
|
||||
layout="total, sizes, prev, pager, next, jumper" :total="attachment.total"
|
||||
@size-change="getAttachmentList()" @current-change="getAttachmentList" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||||
<el-button type="primary" @click="confirm">{{ t('confirm') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, nextTick } from 'vue'
|
||||
import { t } from '@/lang'
|
||||
import UploadMedia from './upload-media.vue'
|
||||
import { img, debounce } from '@/utils/common'
|
||||
import { getMediaList, syncNews } from '@/app/api/wechat'
|
||||
|
||||
const prop = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
default: 'image'
|
||||
}
|
||||
})
|
||||
|
||||
const showDialog = ref(false)
|
||||
|
||||
const openDialog = () => {
|
||||
prop.type == 'news' && waterfall()
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const attachment: Record<string, any> = reactive({
|
||||
loading: true,
|
||||
page: 1,
|
||||
total: 0,
|
||||
limit: 10,
|
||||
data: []
|
||||
})
|
||||
|
||||
/**
|
||||
* 查询素材
|
||||
*/
|
||||
const getAttachmentList = (page: number = 1) => {
|
||||
attachment.loading = true
|
||||
attachment.page = page
|
||||
getMediaList({
|
||||
page: attachment.page,
|
||||
limit: attachment.limit,
|
||||
type: prop.type
|
||||
}).then(res => {
|
||||
attachment.data = res.data.data
|
||||
attachment.total = res.data.total
|
||||
attachment.loading = false
|
||||
prop.type == 'news' && waterfall()
|
||||
}).catch(() => {
|
||||
attachment.loading = false
|
||||
})
|
||||
}
|
||||
getAttachmentList()
|
||||
|
||||
const emits = defineEmits(['success'])
|
||||
const selectedFile: Record<string, any> = ref({})
|
||||
|
||||
const confirm = () => {
|
||||
emits('success', selectedFile.value)
|
||||
}
|
||||
|
||||
const syncLoading = ref(false)
|
||||
const syncWechatNews = () => {
|
||||
if (syncLoading.value) return
|
||||
syncLoading.value = true
|
||||
|
||||
syncNews().then(() => {
|
||||
syncLoading.value = false
|
||||
getAttachmentList()
|
||||
}).catch(() => {
|
||||
syncLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const meta = document.createElement('meta')
|
||||
meta.content = 'same-origin'
|
||||
meta.name = 'referrer'
|
||||
document.getElementsByTagName('head')[0].appendChild(meta)
|
||||
|
||||
// 瀑布流计算
|
||||
const waterfallContainerRef = ref(null)
|
||||
const waterfallItemRef = ref([])
|
||||
const listPosition = ref([])
|
||||
const waterfall = debounce(() => {
|
||||
nextTick(() => {
|
||||
const containerWidth = waterfallContainerRef.value.clientWidth
|
||||
const column = parseInt(containerWidth / 292)
|
||||
const heights = []
|
||||
const positions = []
|
||||
|
||||
waterfallItemRef.value.forEach((item, i) => {
|
||||
if (i < column) {
|
||||
const position = {}
|
||||
position.top = '0px'
|
||||
if (i % column == 0) {
|
||||
position.left = item.clientWidth * i + 'px'
|
||||
} else {
|
||||
position.left = item.clientWidth * i + (i % column * 10) + 'px'
|
||||
}
|
||||
positions[i] = position
|
||||
heights[i] = item.clientHeight + 10
|
||||
} else {
|
||||
const minHeight = Math.min(...heights) // 找到第一列的最小高度
|
||||
const minIndex = heights.findIndex(item => item === minHeight) // 找到最小高度的索引
|
||||
const position = {}
|
||||
position.top = minHeight + 10 + 'px'
|
||||
position.left = positions[minIndex].left
|
||||
positions[i] = position
|
||||
heights[minIndex] += item.clientHeight + 10
|
||||
}
|
||||
})
|
||||
listPosition.value = positions
|
||||
})
|
||||
}, 800)
|
||||
|
||||
// 重新布局,以适应窗口变化
|
||||
window.addEventListener('resize', () => waterfall())
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
</style>
|
||||
Reference in New Issue
Block a user