@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "人生无限公司 - 你的图文部落",
|
||||
"name": "人生无限公司",
|
||||
"short_name": "人生无限公司",
|
||||
"icons": [
|
||||
{
|
||||
|
@@ -15,7 +15,7 @@ function transformPostData(backendPost) {
|
||||
|
||||
const transformedData = {
|
||||
id: backendPost.id,
|
||||
image: (backendPost.images && backendPost.images[0]) || new URL('@/assets/imgs/未加载.png', import.meta.url).href,
|
||||
image: (backendPost.images && backendPost.images[0]) || new URL('@/assets/imgs/avatar (11).jpg', import.meta.url).href,
|
||||
title: backendPost.title,
|
||||
content: backendPost.content,
|
||||
images: backendPost.images || [],
|
||||
@@ -23,7 +23,7 @@ function transformPostData(backendPost) {
|
||||
video_url: backendPost.video_url,
|
||||
cover_url: backendPost.cover_url,
|
||||
videos: backendPost.videos || [],
|
||||
avatar: backendPost.user_avatar || new URL('@/assets/imgs/avatar.png', import.meta.url).href,
|
||||
avatar: backendPost.user_avatar || new URL('@/assets/imgs/avatar.jpg', import.meta.url).href,
|
||||
author: backendPost.nickname || '匿名用户',
|
||||
location: backendPost.location || '',
|
||||
// 统计数据 - 统一使用后端字段名
|
||||
|
BIN
vue3-project/src/assets/imgs/avatar (10).jpg
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
vue3-project/src/assets/imgs/avatar (11).jpg
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
vue3-project/src/assets/imgs/avatar (12).jpg
Normal file
After Width: | Height: | Size: 227 KiB |
BIN
vue3-project/src/assets/imgs/avatar (13).jpg
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
vue3-project/src/assets/imgs/avatar (14).jpg
Normal file
After Width: | Height: | Size: 153 KiB |
BIN
vue3-project/src/assets/imgs/avatar (16).jpg
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
vue3-project/src/assets/imgs/avatar (17).jpg
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
vue3-project/src/assets/imgs/avatar (18).jpg
Normal file
After Width: | Height: | Size: 177 KiB |
BIN
vue3-project/src/assets/imgs/avatar (19).jpg
Normal file
After Width: | Height: | Size: 215 KiB |
BIN
vue3-project/src/assets/imgs/avatar (20).jpg
Normal file
After Width: | Height: | Size: 230 KiB |
BIN
vue3-project/src/assets/imgs/avatar (21).jpg
Normal file
After Width: | Height: | Size: 240 KiB |
BIN
vue3-project/src/assets/imgs/avatar (22).jpg
Normal file
After Width: | Height: | Size: 240 KiB |
BIN
vue3-project/src/assets/imgs/avatar (23).jpg
Normal file
After Width: | Height: | Size: 245 KiB |
BIN
vue3-project/src/assets/imgs/avatar (24).jpg
Normal file
After Width: | Height: | Size: 111 KiB |
BIN
vue3-project/src/assets/imgs/avatar (25).jpg
Normal file
After Width: | Height: | Size: 236 KiB |
BIN
vue3-project/src/assets/imgs/avatar (26).jpg
Normal file
After Width: | Height: | Size: 224 KiB |
BIN
vue3-project/src/assets/imgs/avatar (9).jpg
Normal file
After Width: | Height: | Size: 242 KiB |
BIN
vue3-project/src/assets/imgs/avatar.jpg
Normal file
After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 1.6 MiB |
@@ -125,7 +125,7 @@ const getGridClass = () => {
|
||||
|
||||
// 图片加载失败处理
|
||||
const handleImageError = (event) => {
|
||||
import('@/assets/imgs/未加载.png').then(module => {
|
||||
import('@/assets/imgs/avatar (11).jpg').then(module => {
|
||||
event.target.src = module.default
|
||||
})
|
||||
}
|
||||
|
@@ -674,7 +674,7 @@ const authorData = computed(() => {
|
||||
return {
|
||||
id: userId,
|
||||
name: props.item.nickname || props.item.author || '匿名用户',
|
||||
avatar: props.item.user_avatar || props.item.avatar || new URL('@/assets/imgs/未加载.png', import.meta.url).href,
|
||||
avatar: props.item.user_avatar || props.item.avatar || new URL('@/assets/imgs/avatar (11).jpg', import.meta.url).href,
|
||||
verified: props.item.verified || props.item.author_verified || 0,
|
||||
isFollowing: followState.followed,
|
||||
buttonType: followState.buttonType
|
||||
@@ -722,7 +722,7 @@ const imageList = computed(() => {
|
||||
if (props.item.image) {
|
||||
return [props.item.image]
|
||||
}
|
||||
return [new URL('@/assets/imgs/未加载.png', import.meta.url).href]
|
||||
return [new URL('@/assets/imgs/avatar (11).jpg', import.meta.url).href]
|
||||
})
|
||||
|
||||
const hasMultipleImages = computed(() => imageList.value.length > 1)
|
||||
|
@@ -13,7 +13,7 @@ import { useCollectStore } from '@/stores/collect.js'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { getPostList } from '@/api/posts.js'
|
||||
import defaultAvatar from '@/assets/imgs/avatar.png'
|
||||
import defaultPlaceholder from '@/assets/imgs/未加载.png'
|
||||
import defaultPlaceholder from '@/assets/imgs/avatar (11).jpg'
|
||||
|
||||
const props = defineProps({
|
||||
refreshKey: {
|
||||
@@ -868,7 +868,7 @@ function handleAvatarError(event) {
|
||||
// 处理封面图加载失败
|
||||
function handleImageError(event) {
|
||||
if (event.target) {
|
||||
import('@/assets/imgs/未加载.png').then(module => {
|
||||
import('@/assets/imgs/avatar (11).jpg').then(module => {
|
||||
if (event.target) {
|
||||
event.target.src = module.default
|
||||
}
|
||||
|
@@ -21,53 +21,9 @@
|
||||
<div class="intro-section">
|
||||
<h3>项目简介</h3>
|
||||
<p>
|
||||
人生无限公司是一个面向开发者与学习者的开源示例项目,旨在提供从前端到后端的完整实践范本,帮助大家学习现代 Web 应用的架构设计、工程化与业务实现。
|
||||
人生无限公司是一个面向wmls的交流共享社区,无任何盈利和商业行为,不会提供定向聊天功能,审核不通过的图文会被删除。
|
||||
</p>
|
||||
</div>
|
||||
<div class="author-section">
|
||||
<h3>开发者</h3>
|
||||
<a href="https://github.com/ZTMYO" target="_blank" class="author-link">
|
||||
<div class="author-info">
|
||||
<img class="author-avatar" :src="ztmyoUrl" alt="ZTMYO">
|
||||
<div class="author-details">
|
||||
<p class="author-name">@ZTMYO</p>
|
||||
<p class="author-desc">全栈开发者</p>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="features-section">
|
||||
<h3>项目亮点</h3>
|
||||
<ul class="features-list">
|
||||
<li><strong>前端:</strong>Vue 3+Vite+Pinia+Vue Router</li>
|
||||
<li><strong>后端:</strong>Node.js/Express+MySQL</li>
|
||||
<li><strong>工程化:</strong>环境配置、代码规范、构建与产物优化的完整流程</li>
|
||||
<li><strong>业务能力:</strong>鉴权流程、路由守卫、状态管理与接口封装</li>
|
||||
<li><strong>体验优化:</strong>骨架屏、懒加载、预加载、无障碍与响应式适配</li>
|
||||
<li><strong>组件与分层:</strong>可复用组件拆分、按领域分组与别名引入</li>
|
||||
<li><strong>后台管理:</strong>基础CRUD、数据管理与配置面板,支持后续扩展权限与统计</li>
|
||||
<li><strong>第三方库:</strong>VueUse、Cropper.js、vue3-emoji-picker、svg-captcha等的集成与实践</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="api-section">
|
||||
<h3>接口服务</h3>
|
||||
<div class="api-content">
|
||||
<p>
|
||||
<strong>图片存储:</strong>灌装的示例图片来自 <a href="https://t.alcy.cc/" target="_blank" class="api-link"><img
|
||||
:src="liciUrl" alt="栗次元" class="api-icon">栗次元图床</a>,提供稳定的图片存储服务。
|
||||
</p>
|
||||
<p>
|
||||
<strong>图片上传:</strong>用户上传图片使用了 <a href="https://api.aa1.cn/doc/xinyew_jdtc.html" target="_blank"
|
||||
class="api-link"><img :src="xiaRouUrl" alt="夏柔" class="api-icon">夏柔API</a>,确保图片上传的稳定性和速度。
|
||||
</p>
|
||||
<p>
|
||||
<strong>属地查询:</strong>IP属地查询服务使用 <a href="https://api.pearktrue.cn/dashboard/detail/290" target="_blank"
|
||||
class="api-link"><img :src="baoLuoUrl" alt="保罗" class="api-icon">保罗API</a>,实现精准的IP属地定位功能。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="privacy-section">
|
||||
@@ -92,7 +48,7 @@
|
||||
<h3>版权声明</h3>
|
||||
<div class="copyright-content">
|
||||
<p>
|
||||
<strong>设计灵感:</strong>本校园图文社区的UI设计和交互体验参考了小红书平台,旨在为下载该开源项目的人员提供一个熟悉的项目体验。
|
||||
<strong>设计灵感:</strong>UI设计和交互体验参考了小红书平台,旨在为下载该开源项目的人员提供一个熟悉的项目体验。
|
||||
</p>
|
||||
<p>
|
||||
<strong>开源项目:</strong>本项目基于 GPLv3 协议开源,仅供学习交流使用,不用于商业用途。所有代码遵循 GPLv3 开源协议,欢迎技术交流与讨论。
|
||||
@@ -104,7 +60,7 @@
|
||||
</div>
|
||||
|
||||
<div class="about-footer">
|
||||
<p>© 2025 人生无限公司. Made with ❤️ by @ZTMYO</p>
|
||||
<p>© 2025 人生无限公司. Made with ❤️ by @Eirf</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -123,10 +79,6 @@ const { lock, unlock } = useScrollLock()
|
||||
|
||||
// 静态资源URL
|
||||
const logoUrl = new URL('@/assets/imgs/logo.png', import.meta.url).href
|
||||
const ztmyoUrl = new URL('@/assets/imgs/ztmyo.png', import.meta.url).href
|
||||
const liciUrl = new URL('@/assets/imgs/栗次元.ico', import.meta.url).href
|
||||
const xiaRouUrl = new URL('@/assets/imgs/夏柔.ico', import.meta.url).href
|
||||
const baoLuoUrl = new URL('@/assets/imgs/保罗.ico', import.meta.url).href
|
||||
|
||||
const isAnimating = ref(false)
|
||||
|
||||
|
@@ -120,7 +120,7 @@ class StuckItemManager {
|
||||
img.onerror = () => {
|
||||
// 根据图片类型选择不同的占位图
|
||||
const isAvatar = el.classList.contains('lazy-avatar')
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/未加载.png'
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/avatar (11).jpg'
|
||||
el.src = new URL(placeholderImg, import.meta.url).href
|
||||
el.alt = '图片加载失败'
|
||||
el.dispatchEvent(new Event('load'))
|
||||
@@ -132,7 +132,7 @@ class StuckItemManager {
|
||||
if (!el.src || el.src === 'data:' || el.src.includes('blob:')) {
|
||||
// 根据图片类型选择不同的占位图
|
||||
const isAvatar = el.classList.contains('lazy-avatar')
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/未加载.png'
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/avatar (11).jpg'
|
||||
el.src = new URL(placeholderImg, import.meta.url).href
|
||||
el.alt = '图片加载超时'
|
||||
el.dispatchEvent(new Event('load'))
|
||||
@@ -155,7 +155,7 @@ const loadImageImmediately = (el, src) => {
|
||||
img.onerror = null
|
||||
// 根据图片类型选择不同的占位图
|
||||
const isAvatar = el.classList.contains('lazy-avatar')
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/未加载.png'
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/avatar (11).jpg'
|
||||
el.src = new URL(placeholderImg, import.meta.url).href
|
||||
el.alt = '图片加载超时'
|
||||
el.style.opacity = '1'
|
||||
@@ -178,7 +178,7 @@ const loadImageImmediately = (el, src) => {
|
||||
clearTimeout(timeout)
|
||||
// 根据图片类型选择不同的占位图
|
||||
const isAvatar = el.classList.contains('lazy-avatar')
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/未加载.png'
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/avatar (11).jpg'
|
||||
el.src = new URL(placeholderImg, import.meta.url).href
|
||||
el.alt = '图片加载失败'
|
||||
el.style.opacity = '1'
|
||||
@@ -249,7 +249,7 @@ export const lazyPlugin = {
|
||||
clearTimeout(loadTimeout)
|
||||
// 根据图片类型选择不同的占位图
|
||||
const isAvatar = el.classList.contains('lazy-avatar')
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/未加载.png'
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/avatar (11).jpg'
|
||||
el.src = new URL(placeholderImg, import.meta.url).href
|
||||
el.alt = '图片加载失败'
|
||||
el.style.opacity = '1'
|
||||
@@ -264,7 +264,7 @@ export const lazyPlugin = {
|
||||
}).catch(() => {
|
||||
// 队列加载失败,显示默认图片
|
||||
const isAvatar = el.classList.contains('lazy-avatar')
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/未加载.png'
|
||||
const placeholderImg = isAvatar ? '@/assets/imgs/avatar.png' : '@/assets/imgs/avatar (11).jpg'
|
||||
el.src = new URL(placeholderImg, import.meta.url).href
|
||||
el.alt = '图片加载失败'
|
||||
el.style.opacity = '1'
|
||||
|
@@ -104,7 +104,7 @@ export const useCommentStore = defineStore('comment', () => {
|
||||
user_id: reply.user_display_id || reply.user_id, // 职员号(用于导航)
|
||||
user_auto_id: reply.user_auto_id || reply.user_id, // 用户自增ID(用于权限判断)
|
||||
username: reply.nickname || '匿名用户',
|
||||
avatar: reply.user_avatar || new URL('@/assets/imgs/未加载.png', import.meta.url).href,
|
||||
avatar: reply.user_avatar || new URL('@/assets/imgs/avatar (11).jpg', import.meta.url).href,
|
||||
verified: reply.verified || 0, // 认证状态
|
||||
content: reply.content,
|
||||
time: formatTime(reply.created_at),
|
||||
|
@@ -129,5 +129,5 @@ export function getImageDimensions(imageData, maxWidth = null, maxHeight = null)
|
||||
* @returns {string} 备用图片 URL
|
||||
*/
|
||||
export function generateFallbackImageUrl(width = 400, height = 300, text = '图片', bgColor = 'CCCCCC') {
|
||||
return new URL('@/assets/imgs/未加载.png', import.meta.url).href
|
||||
return new URL('@/assets/imgs/avatar (11).jpg', import.meta.url).href
|
||||
}
|
@@ -320,7 +320,7 @@ const handleDelete = async () => {
|
||||
const handleImageError = (event) => {
|
||||
const img = event.target
|
||||
// 直接替换为未加载图片
|
||||
img.src = '/src/assets/imgs/未加载.png'
|
||||
img.src = '/src/assets/imgs/avatar (11).jpg'
|
||||
img.style.display = 'block'
|
||||
}
|
||||
|
||||
|
@@ -26,7 +26,7 @@ import { useCommentStore } from '@/stores/comment'
|
||||
import { formatTime } from '@/utils/timeFormat'
|
||||
import { sanitizeContent } from '@/utils/contentSecurity'
|
||||
import avatarPlaceholder from '@/assets/imgs/avatar.png'
|
||||
import imagePlaceholder from '@/assets/imgs/未加载.png'
|
||||
import imagePlaceholder from '@/assets/imgs/avatar (11).jpg'
|
||||
|
||||
|
||||
// Store实例
|
||||
|
@@ -330,7 +330,7 @@ const handlePostUpdate = () => {
|
||||
const handleImageError = (event) => {
|
||||
const img = event.target
|
||||
// 直接替换为未加载图片
|
||||
img.src = '/src/assets/imgs/未加载.png'
|
||||
img.src = '/src/assets/imgs/avatar (11).jpg'
|
||||
img.style.display = 'block'
|
||||
}
|
||||
|
||||
|