Compare commits

..

20 Commits

Author SHA1 Message Date
7ecff344de 优化移动端适配和路由处理逻辑 2025-09-01 10:16:42 +08:00
7a8f096513 refactor: 添加移动端适配和路由中间件 2025-09-01 09:57:10 +08:00
80a48e1e71 refactor: 修改API请求参数传递方式 2025-08-30 17:25:03 +08:00
f6a1dc3513 refactor: 更新样式类名和优化页面跳转逻辑 2025-08-30 17:08:37 +08:00
cb89a861c1 refactor: 优化导航搜索跳转逻辑和代码格式 2025-08-30 17:04:22 +08:00
18d93d3a2a refactor: 简化搜索导航逻辑和优化登录状态检测 2025-08-30 17:02:08 +08:00
31f8d6d23d refactor: 调整登录表单样式和UnoCSS配置 2025-08-30 16:52:40 +08:00
316d82b3ad refactor: 优化SEO标题和描述配置 2025-08-30 16:28:02 +08:00
8e26ae9cde refactor: 移除SEO元标签并添加页面SEO组件 2025-08-30 16:11:01 +08:00
22539f3839 refactor: 重构国外专区组件结构和路由配置 2025-08-30 13:18:06 +08:00
469900d3ac refactor: 更新样式类名和导航路径 2025-08-30 12:57:05 +08:00
9675261481 refactor: 更新API路径和优化面包屑功能 2025-08-30 12:44:51 +08:00
3153a35daf refactor: 优化文件下载功能和移除预览点击 2025-08-30 11:52:36 +08:00
966c5feb5e refactor: 调整评论内容样式和字体大小 2025-08-30 11:40:52 +08:00
f7784e8305 refactor: 移除表单提交中的调试日志 2025-08-30 11:38:53 +08:00
048dd1fe65 refactor: 更新API路径和ElementPlus配置 2025-08-30 11:34:04 +08:00
dbc15f5f52 refactor: 调整登录表单组件间距样式 2025-08-30 11:23:56 +08:00
e30abf7f83 refactor: 移除MQTT连接条件中的用户ID检查 2025-08-30 11:19:46 +08:00
7a91eddbb4 refactor: 添加MQTT连接和token监听功能 2025-08-30 11:18:13 +08:00
e036c88535 refactor: 优化壁纸分类组件参数处理 2025-08-30 11:06:12 +08:00
35 changed files with 468 additions and 160 deletions

View File

@ -86,5 +86,5 @@ export const tab2 = () => {
* 获取具有上下级关系的当前的名称
*/
export const getDictTree = (params: { type: number, id: number}) => {
return useDollarFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/dict/level-by-id', { query: params })
return useDollarFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/app/dict/path-by-id', { query: params })
}

View File

@ -132,14 +132,14 @@ export const getOwnContentPage = (params: { pageNo: number; pageSize: number; ty
* 下架
*/
export const offShelf = (params: { id: number }) => {
return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/business/app/project-draw/down', params)
return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/business/app/project-draw/down?id=' + params.id, params)
}
/**
* 删除资源
*/
export const deleteResource = (params: { id: number }) => {
return useDollarFetchRequest.del<IResponse<boolean>>('/prod-api/app-api/business/app/project-draw/delete', params)
return useDollarFetchRequest.del<IResponse<boolean>>('/prod-api/app-api/business/app/project-draw/delete?id=' + params.id, params)
}
/**

25
app.vue
View File

@ -7,4 +7,27 @@
</div>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import refreshToken from '~/utils/RefreshToken'
import useUserStore from '~/stores/user'
const userStore = useUserStore()
onMounted(() => {
if (!userStore.mqttClient) {
// 判断建立连接没 刷新会走这儿
userStore.connectMqtt()
}
// 浏览器打开新的tab页防止数据不一致
document.addEventListener('visibilitychange', () => {
if (userStore.token !== refreshToken.getToken().token) {
location.reload()
}
})
})
onUnmounted(() => {
// 断开连接
userStore.mqttClient?.disconnect()
})
</script>

View File

@ -9,7 +9,7 @@
<div class="relative top-[4px] text-[14px]!">{{ item.creatorInfo.nickName }}</div>
<div class="text-[12px] text-[#999999] font-normal">发表时间{{ dayjs(item.creatorInfo.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
</div>
<div class="mt-[10px] box-border rd-[4px] bg-[#f8f8f8] px-[18px] py-[10px] text-[14px] text-[#999999] font-normal">{{ item.content }}</div>
<div class="mt-[10px] box-border rd-[4px] bg-[#f8f8f8] px-[12px] py-[10px] text-[13px] text-[#999999] font-normal">{{ item.content }}</div>
</div>
</div>
</div>

View File

@ -20,6 +20,7 @@
<script lang="ts" setup>
import KlTabBar from '~/components/kl-tab-bar/index.vue'
import CardPicture from '~/components/kl-card-picture/index.vue'
import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue'
import { ref } from 'vue'
import type { pageRes } from '~/api/upnew/types'
@ -38,19 +39,19 @@
required: true,
})
const tabIndex = ref(1)
const tabIndex = ref(-1)
const tabBar = ref([
{
label: '图纸推荐',
value: 1,
value: -1,
},
{
label: '原创图纸',
value: 2,
value: 1,
},
{
label: '最新上传',
value: 3,
value: 2,
},
])
</script>

View File

@ -11,7 +11,7 @@
placeholder="电子产品"
:prefix-icon="Search"
class="search-input h-[40px] w-[328px]"
@focus="handleHot(), (showHotList = true)"
@focus="(handleHot(), (showHotList = true))"
@input="handleInput"
></el-input>
<!-- 搜索框 获取到焦点 显示热门列表 -->
@ -67,11 +67,11 @@
import { top } from '~/api/home/index'
import type { ProjectDrawStatisticAppRespVO } from '~/api/home/type'
import { Search } from '@element-plus/icons-vue'
import refreshToken from "~/utils/RefreshToken";
import refreshToken from '~/utils/RefreshToken'
import useUserStore from '~/stores/user'
const userStore = useUserStore()
const app = useNuxtApp()
const props = defineProps({
active: {
type: String,
@ -131,22 +131,25 @@
}
const handleHotItem = (item: ProjectDrawStatisticAppRespVO) => {
const normal = { id: '0', name: '图纸库', isChildren: false }
const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || []
level.unshift(normal)
// const normal = { id: '0', name: '图纸库', isChildren: false }
// const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || []
// level.unshift(normal)
if (item.type === 1) {
navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
navigateTo(`/drawe/${item.projectType}/1/12/-1`)
// navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
} else if (item.type === 2) {
navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
navigateTo(`/text/${item.projectType}/1/12/-1`)
// navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
} else if (item.type === 3) {
navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
navigateTo(`/model/${item.projectType}/1/12/-1`)
// navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
}
}
const handleClick = (item: string) => {
switch (item) {
case '首页':
navigateTo({ path: '/'}) // 修改为在新窗口打开
navigateTo({ path: '/' }) // 修改为在新窗口打开
break
case '图纸':
navigateTo('/drawe') // 修改为在新窗口打开

View File

@ -1,7 +1,7 @@
<template>
<div class="fixed-button-group">
<div class="button-item" @click="handleVip">
<el-badge :is-dot="readCount" class="item">
<el-badge class="item">
<el-icon class="icon-item !color-[#10c55b]"><Trophy /></el-icon>
</el-badge>
<span class="button-text">VIP</span>

View File

@ -1,20 +1,20 @@
<template>
<div>
<header class="h-106px">
<div class="mx-a ml--250px h-full flex items-center justify-center">
<header class="h-[106px]">
<div class="mx-a ml-[-250px] h-full flex items-center justify-center">
<!-- Logo区域 -->
<div class="h-100% flex cursor-pointer items-center" @click="navigateTo('/')">
<img src="~/assets/images/logo5.png" alt="图夕夕" class="h-51px w-182px" />
<div class="h-[100%] flex cursor-pointer items-center" @click="navigateTo('/')">
<img src="~/assets/images/logo5.png" alt="图夕夕" class="h-[51px] w-[182px]" />
</div>
<!-- 搜索区域 -->
<div class="relative ml-49px w-647px px4 p-r-0px!">
<div class="search-input relative w-100%">
<div class="relative ml-[49px] w-[647px] px-4 p-r-[0px]!">
<div class="search-input relative w-[100%]">
<el-input
v-model="searchQuery"
type="text"
placeholder="搜一搜"
:prefix-icon="Search"
class="no-right-border box-border h-40px w-100% rounded-bl-4px rounded-br-0px rounded-tl-4px rounded-tr-0px bg-[#F8F8F8] text-14px outline-#999"
class="no-right-border box-border h-[40px] w-[100%] rounded-bl-[4px] rounded-br-[0px] rounded-tl-[4px] rounded-tr-[0px] bg-[#F8F8F8] text-[14px] outline-[#999]"
@focus="handleHot(), (showHotList = true)"
@input="handleInput"
/>
@ -23,32 +23,32 @@
<div
v-if="showHotList"
v-loading="loading"
class="absolute z-100 w-625px border-width-1px border-color-#1A65FF rounded-bl-4px rounded-br-4px rounded-tl-0px rounded-tr-0px border-solid bg-[#fff] pa-10px"
class="absolute z-100 w-[625px] border-width-[1px] border-color-[#1A65FF] rounded-bl-[4px] rounded-br-[4px] rounded-tl-[0px] rounded-tr-[0px] border-solid bg-[#fff] pa-[10px]"
>
<!-- 这里放置热门列表的内容 -->
<ul class="flex flex-col gap-6px">
<ul class="flex flex-col gap-[6px]">
<li
v-for="(item, index) in hotItems"
:key="index"
class="flex flex-row cursor-pointer items-center justify-between text-13px"
class="flex flex-row cursor-pointer items-center justify-between text-[13px]"
@click="handleHotItem(item)"
>
<span class="color-#333333">{{ item.projectTypeName }}</span>
<span v-if="item.count" class="color-#999999">{{ item.count }}份图纸</span>
<span class="color-[#333333]">{{ item.projectTypeName }}</span>
<span v-if="item.count" class="color-[#999999]">{{ item.count }}份图纸</span>
</li>
</ul>
<div v-if="!hotItems.length" class="text-12px color-#999">无数据</div>
<div v-if="!hotItems.length" class="text-[12px] color-[#999]">无数据</div>
</div>
</div>
<!-- 按钮区域 -->
<div class="flex items-center">
<button
class="h-40px w-111px cursor-pointer border-width-1px border-color-#1A65FF rounded-bl-0px rounded-br-4px rounded-tl-0px rounded-tr-4px border-none border-solid text-center text-14px color-#fff bg-#1A65FF!"
class="h-[40px] w-[111px] cursor-pointer border-width-[1px] border-color-[#1A65FF] rounded-bl-[0px] rounded-br-[4px] rounded-tl-[0px] rounded-tr-[4px] border-none border-solid text-center text-[14px] color-[#fff] !bg-[#1A65FF]"
>
搜索
</button>
<button
class="m-l-16px h-40px w-111px cursor-pointer border-width-1px border-color-#E7B03B rounded-bl-6px rounded-br-6px rounded-tl-4px rounded-tr-6px border-none border-solid text-14px color-#fff bg-#E7B03B!"
class="m-l-[16px] h-[40px] w-[111px] cursor-pointer border-width-[1px] border-color-[#E7B03B] rounded-bl-[6px] rounded-br-[6px] rounded-tl-[4px] rounded-tr-[6px] border-none border-solid text-[14px] color-[#fff] !bg-[#E7B03B]"
@click="handleUpload"
>
上传图纸
@ -76,7 +76,7 @@
// 是否登录
if (!userStore.token) return ElMessage.error('请先登录')
// 新开窗口 用router跳转 新窗口打开
navigateTo('/upnew/drawe')
navigateTo('/upnew')
}
const loading = ref(false)
@ -116,16 +116,17 @@
}
const handleHotItem = (item: ProjectDrawStatisticAppRespVO) => {
const normal = { id: '0', name: '图纸库', isChildren: false }
const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || []
level.unshift(normal)
if (item.type === 1) {
navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
} else if (item.type === 2) {
navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
} else if (item.type === 3) {
navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
}
// const normal = { id: '0', name: '图纸库', isChildren: false }
// const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || []
// level.unshift(normal)
// if (item.type === 1) {
// navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
// } else if (item.type === 2) {
// navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
// } else if (item.type === 3) {
// navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
// }
navigateTo(`/drawe/${item.projectType}/1/12/-1`)
}
onMounted(() => {

View File

@ -1,8 +1,8 @@
<template>
<!-- 面包屑 -->
<div v-if="level.length > 1" class="mb-[-10px] mt-[20px] pl-[20px]">
<div v-if="breadList && breadList.length > 1" class="mb-[-10px] mt-[20px] pl-[20px]">
<el-breadcrumb :separator-icon="ArrowRight">
<el-breadcrumb-item v-for="(item, index) in level" :key="item.name" class="cursor-pointer" @click="handleClickBread(item, index)">{{
<el-breadcrumb-item v-for="(item, index) in breadList" :key="item.name" class="cursor-pointer" @click="handleClickBread(item, index)">{{
item.name
}}</el-breadcrumb-item>
</el-breadcrumb>
@ -75,24 +75,36 @@
const query = defineModel<pageReq>('modelValue', {
required: true,
})
const level = defineModel<{ id: string; name: string; isChildren?: boolean }[]>('level', {
required: true,
})
// const level = defineModel<{ id: string; name: string; isChildren?: boolean }[]>('level', {
// required: true,
// })
const computType = computed(() => {
return props.type === 1 ? '图纸' : props.type === 3 ? '模型' : '文本'
})
const handleParentId = (type?: string) => {
if (level?.value?.length > 1) {
if (type === 'init' && level.value.find((c: any) => c.isChildren)) {
return level.value[level.value.length - 2].id || '' // 获取最后一个元素的 id 或 defaul
}
return level.value[level.value.length - 1].id || '' // 获取最后一个元素的 id 或 defaul
// 获取面包屑
const { data: breadList } = await useAsyncData(
`breadList-${props.type}-${props.id}-${query.value.projectType}-${Date.now()}`,
async () => {
const res = await getDictTree({ type: 1, id: query.value.projectType })
const all = [
{
id: -1,
name: props.type === 1 ? '图纸库' : props.type === 3 ? '模型库' : '文本库',
isChildren: false,
},
]
const arr = [...res.data, ...all]
return arr.reverse()
},
{
immediate: true,
}
return '0'
}
)
console.log('breadList', breadList);
// const projectTypeList = ref<any>([])
/** 获取分类下拉框 */
// const getParent = (type?: string) => {
@ -121,23 +133,28 @@
// }
// getEditionsList()
// 获取面包屑
const {data: breadList} = await useAsyncData(`breadList-${props.type}-${props.id}-${props.groundId}-${Date.now()}`, async () => {
const res = await getDictTree({ type: props.type, id: query.value.projectType })
return res.data
})
console.log('breadList', breadList);
/** 是否是初始化 */
const queryType = ref('init')
/**获取分类下拉框 */
const { data: projectTypeList, refresh } = useAsyncData(`projectType-draw-${props.type}-${Date.now()}`, async () => {
const res = await parent({ type: 1, parentId: handleParentId(queryType.value) })
const all = [{ id: '-1', name: '全部' }]
return [...all, ...res.data]
})
const { data: projectTypeList } = await useAsyncData(
`projectType-draw-${props.type}-${query.value.projectType}`,
async () => {
let parentId: any = '0'
if (breadList?.value && breadList?.value.length > 1) {
if (breadList.value.length > 2) {
const length = breadList.value?.length
parentId = breadList?.value[length - 2].id
} else {
const length = breadList.value?.length
parentId = breadList?.value[length - 1].id
}
}
const res = await parent({ type: 1, parentId: parentId })
const all = [{ id: parentId === '0' ? '-1' : parentId, name: '全部' }]
return [...all, ...res.data]
},
{
immediate: true,
}
)
/** 版本 */
const { data: editionsList } = useAsyncData(`editionsList-${props.type}-${Date.now()}`, async () => {
@ -149,27 +166,25 @@
const handleClick = (row: any) => {
query.value.title = ''
query.value.projectType = row.id
if (row.name === '全部') return
const isChildren = level.value.find((c: any) => c.isChildren)
if (!row.isChildren && isChildren) {
const index = level.value.length - 1
level.value[index] = { id: row.id, name: row.name, isChildren: true }
} else if (!row.isChildren && !isChildren) {
level.value.push({ id: row.id, name: row.name, isChildren: true })
} else {
level.value.push({ id: row.id, name: row.name })
// getParent()
queryType.value = ''
refresh()
}
// if (row.name === '全部') return
// const isChildren = breadList.value.find((c: any) => c.isChildren)
// if (!row.isChildren && isChildren) {
// const index = breadList.value.length - 1
// breadList.value[index] = { id: row.id, name: row.name, isChildren: true }
// } else if (!row.isChildren && !isChildren) {
// breadList.value.push({ id: row.id, name: row.name, isChildren: true })
// } else {
// breadList.value.push({ id: row.id, name: row.name })
// // getParent()
// refresh()
// }
}
const handleClickBread = (row: any, index: number) => {
level.value.splice(index + 1)
// breadList.value.splice(index + 1)
query.value.title = ''
query.value.projectType = row.id
// getParent()
queryType.value = ''
refresh()
// refresh()
}
</script>

View File

@ -16,9 +16,9 @@
})
useHead({
title: `${props.title}`,
title: `${props.title} - 图夕夕`,
meta: [
{ name: 'description', content: props.description },
{ name: 'description', content: `${props.description}`},
{ name: 'keywords', content: props.keywords },
],
})

0
layouts/m.vue Normal file
View File

22
middleware/auth.global.ts Normal file
View File

@ -0,0 +1,22 @@
// 区分是手机端还是移动端
export default defineNuxtRouteMiddleware((to, from) => {
if (import.meta.client) {
// 在客户端处理路由
// 是否是移动端设备
const isMobile = /(Android|webOS|iPhone|iPod|tablet|BlackBerry|Mobile)/i.test(navigator.userAgent)
// 是否是手机端路由开头
const isRouterMobile = to.path.startsWith('/m')
console.log(isMobile, isRouterMobile);
// 移动端并且 不是/m开头路由
if (isMobile && !isRouterMobile) {
return navigateTo(`/m`)
}
// 不是移动端 是/m开头路由
if (!isMobile && isRouterMobile) {
return navigateTo(`/`)
}
}
})

View File

@ -1 +0,0 @@
// 从微信登录重定向到项目 需要获取code值

View File

@ -1,5 +1,6 @@
// import { base_api } from '~/constants/index'
// https://nuxt.com/docs/api/configuration/nuxt-config
import postcsspxtoviewport from 'postcss-px-to-viewport'
export default defineNuxtConfig({
// devServer: {
// port: 6188,
@ -12,6 +13,13 @@ export default defineNuxtConfig({
ssr: true,
modules: ['@unocss/nuxt', '@pinia/nuxt', '@element-plus/nuxt', 'pinia-plugin-persistedstate/nuxt'],
unocss: {
nuxtLayers: true,
},
elementPlus: {
importStyle: 'scss', // 或 'css',确保样式被全局导入
themes: ['dark'], // 按需配置主题
},
css: ['element-plus/dist/index.css', '~/assets/scss/app.scss'],
vite: {
css: {
@ -22,12 +30,22 @@ export default defineNuxtConfig({
},
postcss: {
plugins: [
// postCssPxToRem({
// rootValue: 16, // 结果为:设计稿元素尺寸/16比如元素宽320px,最终页面会换算成 20rem
// mediaQuery: false, //布尔值允许在媒体查询中转换px。
// // exclude: /node_modules/, //node_modules目录下样式全部不转义
// propList: ['*'] //需要做转化处理的属性如`hight`、`width`、`margin`等,`*`表示全部
// })
postcsspxtoviewport({
unitToConvert: 'px', // 要转化的单位
viewportWidth: 750, // UI设计稿的宽度
unitPrecision: 6, // 转换后的精度,即小数点位数
propList: ['*'], // 指定转换的css属性的单位*代表全部css属性的单位都进行转换
viewportUnit: 'vw', // 指定需要转换成的视窗单位默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位默认vw
selectorBlackList: ['el-'], // 指定不转换为视窗单位的类名,例如van-vantUI组件
minPixelValue: 1, // 默认值1小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换默认false
replace: true, // 是否转换后直接更换属性值
exclude: [/node_modules/, /^(?!.*\/pages\/m\/).*$/], // 排除node_modules和非pages/m目录下的文件
landscape: false, // 是否处理横屏情况
// 只转换pages下的m文件
include: [/pages\/m/],
}),
],
},
},
@ -73,20 +91,20 @@ export default defineNuxtConfig({
{ name: 'keywords', content: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸' },
{ name: 'author', content: '图夕夕' },
// SEO meta tags
{
property: 'og:title',
content: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸',
},
{
property: 'og:description',
content: '图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。',
},
{ property: 'og:type', content: 'website' },
{ property: 'og:url', content: 'https://www.xlcig.cn' },
{ property: 'og:site_name', content: 'xlCig' },
{ name: 'theme-color', content: '#00f5ff' },
// robots meta
{ name: 'robots', content: 'index, follow' },
// {
// property: 'og:title',
// content: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸',
// },
// {
// property: 'og:description',
// content: '图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。',
// },
// { property: 'og:type', content: 'website' },
// { property: 'og:url', content: 'https://www.xlcig.cn' },
// { property: 'og:site_name', content: 'xlCig' },
// { name: 'theme-color', content: '#00f5ff' },
// // robots meta
// { name: 'robots', content: 'index, follow' },
],
link: [
{ rel: 'icon', type: 'image/x-icon', href: '/favicon2.ico' },

View File

@ -20,6 +20,7 @@
"nuxt": "^3.18.1",
"pdfjs-dist": "^5.4.54",
"pinia": "^3.0.3",
"postcss-px-to-viewport": "^1.1.1",
"qrcode.vue": "^3.6.0",
"vue": "^3.5.18",
"vue-pdf-embed": "^2.1.3",
@ -32,7 +33,9 @@
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
"@types/prettier": "^3.0.0",
"@unocss/nuxt": "^66.4.2",
"@vant/nuxt": "^1.0.7",
"element-plus": "^2.10.7",
"postcss": "^8.5.6",
"prettier": "3.6.2",
"sass": "^1.90.0",
"unocss": "^66.4.2"

View File

@ -1,4 +1,5 @@
<template>
<SeoHead :title="detail?.title" :description="detail?.description" :keywords="detail?.labels?.toString()" />
<KlNavTab />
<div class="ml-auto mr-auto mt-[20px] w-[1440px]">
<div class="flex items-center">
@ -58,7 +59,8 @@
<div v-for="item in detail?.files" :key="item.id" class="flex items-center justify-between border-b-[1px] border-b-[#eee] border-b-solid py-[10px]">
<!-- <img src="~/assets/images/avater.png" alt="" srcset="" class="h-30px w-30px" /> -->
<div>
<span class="ml-[10px] cursor-pointer" @click="handleDownloadPreview(item)">{{ item.title }}</span>
<!-- <span class="ml-[10px] cursor-pointer" @click="handleDownloadPreview(item)">{{ item.title }}</span> -->
<span class="ml-[10px]">{{ item.title }}</span>
<span v-if="item.size" class="ml-[200px] color-[#999]">{{ item.size || '-' }}</span>
</div>
<el-button
@ -177,6 +179,7 @@
import { downloadFile } from '~/utils/utils'
import { useMessage } from '~/utils/useMessage'
import { Warning } from '@element-plus/icons-vue'
import SeoHead from '~/components/seo-head/index.vue'
import CardPicture from '~/components/kl-card-picture/index.vue'
import { getDetail, getRelationRecommend, report, getUserInfo, getMainWork, createContent, createUserProject, deleteProject } from '~/api/drawe-detail/index'
import KlNavTab from '~/components/kl-nav-tab/index.vue'

View File

@ -1,5 +1,6 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计图纸下载_CAD设计图纸资源库" />
<KlNavTab active="图纸" :type="1" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->

View File

@ -1,5 +1,6 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计图纸下载_CAD设计图纸资源库" />
<KlNavTab active="图纸" :type="1" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->

View File

@ -0,0 +1,128 @@
<template>
<!-- 导航 -->
<KlNavTab active="国外专区" />
<!-- banneer提示 -->
<BannerTips />
<div class="ma-auto w-[1440px]">
<!-- 图片展示鼠标移上去展示提示语 -->
<!-- <ImageTips /> -->
<!-- 推荐栏目 -->
<RecommendedColumnsV2 v-model:query="query" v-model="result"></RecommendedColumnsV2>
<!-- 精选专题 -->
<!-- <FeaturedSpecials></FeaturedSpecials> -->
<!-- 分页 -->
<div class="mt-[10px] flex justify-center">
<el-pagination
v-model:current-page="query.pageNo"
v-model:page-size="query.pageSize"
:page-sizes="[10, 20, 30]"
:total="result?.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleChangeSize"
@current-change="handleChangeCurrent"
/>
</div>
</div>
</template>
<script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue'
import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue'
import RecommendedColumnsV2 from '~/components/foreign-components/RecommendedColumnsV2.vue'
import BannerTips from '~/components/foreign-components/BannerTips.vue'
// import FeaturedSpecials from './components/FeaturedSpecials.vue'
import { useRoute } from 'vue-router'
import { reactive, watch, ref } from 'vue'
import { page } from '~/api/upnew/index'
import { getDictTree } from '~/api/home/index'
import type { pageRes, pageReq } from '~/api/upnew/types'
const route = useRoute()
const projectType = computed(() => (route.params?.projectType ? route.params?.projectType : ''))
const pageNo = computed(() => Number(route.params?.pageNo))
const pageSize = computed(() => Number(route.params?.pageSize))
const editions = computed(() => (route.params?.editions ? route.params?.editions : ''))
const source = computed(() => (route.params?.source ? Number(route.params?.source) : ''))
console.log('route.params----', route.params)
const level = ref(
route.query?.valuelevel
? JSON.parse(route.query.valuelevel as string)
: [
{
id: -1,
name: '图纸库',
isChildren: false,
},
]
)
const keywords = ref((route.query?.valuekeywords as string) || '')
const query = ref<pageReq>({
pageNo: pageNo.value || 1,
pageSize: pageSize.value || 12,
projectType: projectType.value || '-1',
editions: editions.value || '-1',
source: source.value || -1,
type: 1,
title: keywords.value,
})
// const result = reactive<pageRes>({
// list: [],
// total: 0,
// })
// 如果id存在则设置projectType
if (level.value.length) {
// query.value.projectType = level.value[level.value.length - 1].id || ''
}
const handleChangeSize = (val: number) => {
query.value.pageSize = val
// getPage()
navigateTo(`/foreign/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
}
const handleChangeCurrent = (val: number) => {
query.value.pageNo = val
// getPage()
navigateTo(`/foreign/${query.value.projectType}/${val}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
}
const { data: result, refresh: getPage } = useAsyncData(
`foreign-page-list-${query.value.projectType}-${query.value.editions}-${query.value.source}-${query.value.pageNo}-${query.value.pageSize}-${query.value.title}`,
async () => {
const res = await page({
...query.value,
editions: query.value.editions === '-1' ? '' : query.value.editions,
source: query.value.source === -1 ? '' : query.value.source,
projectType: query.value.projectType === '-1' ? '' : query.value.projectType,
})
return res.data
},
{
immediate: true,
}
)
// const getPage = () => {
// page(query).then((res) => {
// const { data, code } = res
// if (code === 0) {
// result.list = data.list
// result.total = data.total
// }
// })
// }
watch([() => query.value.projectType, () => query.value.editions, () => query.value.source], (val) => {
if (val) {
navigateTo(`/foreign/${query.value.projectType}/1/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
}
})
</script>
<style lang="scss" scoped>
:deep(.el-pagination) {
.el-input__inner {
text-align: center !important;
}
}
</style>

View File

@ -26,9 +26,9 @@
</template>
<script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue'
import RecommendedColumnsV2 from './components/RecommendedColumnsV2.vue'
import RecommendedColumnsV2 from '~/components/foreign-components/RecommendedColumnsV2.vue'
// import FeaturedSpecials from './components/FeaturedSpecials.vue'
import BannerTips from './components/BannerTips.vue'
import BannerTips from '~/components/foreign-components/BannerTips.vue'
// import ImageTips from './components/ImageTips.vue'
import { reactive, watch } from 'vue'
@ -38,9 +38,9 @@
const query = reactive<pageReq>({
pageNo: 1,
pageSize: 10,
projectType: '',
editions: '',
source: '',
projectType: '-1',
editions: '-1',
source: -1,
type: 1,
})
// const result = reactive<pageRes>({
@ -48,8 +48,13 @@
// total: 0,
// })
const {data:result, refresh:getPage } = await useAsyncData(`draw-page-list-${Date.now()}`, async () => {
const res = await page(query)
const { data: result } = await useAsyncData(`draw-page-list-${Date.now()}`, async () => {
const res = await page({
...query,
editions: query.editions === '-1' ? '' : query.editions,
source: query.source === -1 ? '' : query.source,
projectType: query.projectType === '-1' ? '' : query.projectType,
})
return res.data
})
// const getPage = () => {
@ -66,18 +71,23 @@
const handleChangeSize = (val: number) => {
query.pageSize = val
query.pageNo = 1
getPage()
// query.pageNo = 1
// getPage()
navigateTo(`/foreign/${query.projectType}/${query.pageNo}/${val}/${query.editions}/${query.source}`)
}
const handleChangeCurrent = (val: number) => {
query.pageNo = val
getPage()
// getPage()
navigateTo(`/foreign/${query.projectType}/${val}/${query.pageSize}/${query.editions}/${query.source}`)
}
watch([() => query.projectType, () => query.editions, () => query.source], (val) => {
if (val) {
getPage()
// getPage()
navigateTo(`/foreign/${query.projectType}/1/${query.pageSize}/${query.editions}/${query.source}`)
}
})
</script>

View File

@ -38,7 +38,7 @@
</div>
</div>
</div>
<div v-if="!isLogin" class="mt-[48px] box-border flex justify-between px-[18px]">
<div v-if="!isLogin" class="mt-[46px] box-border flex justify-between px-[18px]">
<div
class="h-[37px] w-[101px] cursor-pointer border border-[#1A65FF] rounded-[2px] border-solid bg-[#1A65FF] text-center text-[14px] text-[#FFFFFF] font-normal line-height-[37px]"
@click="handleLogin"
@ -50,7 +50,7 @@
>免费注册</div
>
</div>
<div v-else class="mt-[30px] box-border flex justify-between px-[18px]">
<div v-else class="mt-[24px] box-border flex justify-between px-[18px]">
<div
class="h-[37px] w-[101px] cursor-pointer border border-[#1A65FF] rounded-[2px] border-solid bg-[#1A65FF] text-center text-[14px] text-[#FFFFFF] font-normal line-height-[37px]"
@click="handleDrawe"
@ -124,7 +124,7 @@
// 发布图纸
const handleDrawe = () => {
navigateTo('/upnew/drawe') // 修改为在新窗口打开
navigateTo('/upnew') // 修改为在新窗口打开
}
// 推出登录
@ -141,7 +141,7 @@
}
watchEffect(() => {
if (isLogin.value) {
if (isLogin.value && import.meta.client) {
fetchUserStatistics()
}
})

View File

@ -169,7 +169,7 @@
.menu-item {
/* position: relative; */
text-align: center;
padding: 11px 24px;
padding: 10px 24px;
cursor: pointer;
/* transition: all 0.3s ease; */
color: #333;

11
pages/m/index.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<div>
<h1>Mobile Page</h1>
</div>
</template>
<script setup lang="ts">
// Mobile specific logic can be added here
</script>
<style lang="scss" scoped></style>

View File

@ -1,5 +1,6 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计模型下载_CAD设计图纸资源库" />
<KlNavTab active="模型" :type="3" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->

View File

@ -1,5 +1,6 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计模型下载_CAD设计图纸资源库" />
<KlNavTab active="模型" :type="3" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->

View File

@ -1,5 +1,6 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计文本下载_CAD设计图纸资源库" />
<KlNavTab active="文本" :type="2" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->

View File

@ -1,5 +1,6 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计文本下载_CAD设计图纸资源库" />
<KlNavTab active="文本" :type="2" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->

View File

@ -1,12 +1,12 @@
<template>
<KlNavTab />
<div class="mx-auto mt-30px box-border w-1200px border border-[#EEEEEE] rounded-12px border-solid bg-white px-30px py-40px">
<div class="mx-auto mt-[30px] box-border w-[1200px] border border-[#EEEEEE] rounded-[12px] border-solid bg-white px-[30px] py-[40px]">
<el-form ref="formRef" :model="form" label-width="110px" size="large">
<el-form-item label-width="110px" label="标题:" prop="title" :rules="{ required: true, message: '请输入标题', trigger: ['blur', 'change'] }">
<el-input v-model="form.title" placeholder="请输入标题" class="w-361px!" maxlength="128"></el-input>
<el-input v-model="form.title" placeholder="请输入标题" class="w-[361px]!" maxlength="128"></el-input>
</el-form-item>
<el-form-item label-width="110px" label="分类:" prop="projectType" :rules="{ required: true, message: '请选择分类', trigger: ['blur', 'change'] }">
<el-select v-model="form.projectType" placeholder="请选择分类" class="w-361px!" multiple>
<el-select v-model="form.projectType" placeholder="请选择分类" class="w-[361px]!" multiple>
<el-option v-for="(item, index) in projectTypeList" :key="index" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
@ -20,13 +20,13 @@
filterable
remote
placeholder="请输入搜索标签"
class="w-361px!"
class="w-[361px]!"
>
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item label-width="110px" label="金币:" prop="points" :rules="{ required: true, message: '请输入金币', trigger: ['blur', 'change'] }">
<el-input-number v-model="form.points" :controls="false" :precision="0" :min="0" placeholder="请输入金币" class="w-361px! text-left!"></el-input-number>
<el-input-number v-model="form.points" :controls="false" :precision="0" :min="0" placeholder="请输入金币" class="w-[361px]! text-left!"></el-input-number>
</el-form-item>
<el-form-item
@ -43,9 +43,9 @@
tips="上传图片支持jpg/gif/png格式、第一张为封面图片、每张图片大小不得超过1M"
@validate="formRef.validateField('coverImages')"
>
<div class="h-77px w-161px flex items-center justify-center border border-[#cdd0d6] rounded-1px border-dashed bg-[#fafafa]">
<div class="h-[77px] w-[161px] flex items-center justify-center border border-[#cdd0d6] rounded-[1px] border-dashed bg-[#fafafa]">
<el-icon class="text-[#999999]"><Plus /></el-icon>
<div class="ml-4px mt-2px text-14px text-[#999999] font-normal">上传图纸</div>
<div class="ml-[4px] mt-[2px] text-[14px] text-[#999999] font-normal">上传图纸</div>
</div>
</KlUploader>
</el-form-item>
@ -72,7 +72,7 @@
},
]"
>
<el-input v-model="form.description" type="textarea" :rows="6" placeholder="请输入描述" class="w-361px!" minlength="70" show-word-limit></el-input>
<el-input v-model="form.description" type="textarea" :rows="6" placeholder="请输入描述" class="w-[361px]!" minlength="70" show-word-limit></el-input>
</el-form-item>
<!-- 添加预览和保存按钮 -->
<el-form-item label-width="110px" label=" ">
@ -92,6 +92,7 @@
import { parent, keywords, labels } from '~/api/upnew/index'
import { create } from '~/api/toolbox/index.js'
import type { TcreateReq } from '~/api/toolbox/types'
const router = useRouter() // 导入路由实例,用于跳转页面
const form = reactive<TcreateReq>({
title: '',
@ -170,9 +171,10 @@
console.log(res)
if (res.code === 0) {
ElMessage.success('发布成功')
window.setTimeout(() => {
window.close()
}, 1000)
router.back()
// window.setTimeout(() => {
// window.close()
// }, 1000)
}
})
.finally(() => {

View File

@ -33,6 +33,7 @@
import type { TcreateReq } from '~/api/upnew/types'
import { create } from '~/api/upnew/index'
const router = useRouter() // 导入路由实例,用于跳转页面
const form = reactive<TcreateReq>({
activeName: '', // 标签
@ -83,23 +84,20 @@
const loading = ref(false)
const formRef = ref()
const handleSubmit = () => {
console.log(form)
formRef.value.validate((valid: boolean, val: any) => {
console.log('00000----', val)
if (valid) {
loading.value = true
create(form)
.then((res) => {
console.log(res)
const { code } = res
if (code === 0) {
// 弹窗提示
ElMessage.success('操作成功')
router.back()
// 关闭弹窗
setTimeout(() => {
window.close()
}, 300)
// setTimeout(() => {
// window.close()
// }, 300)
}
})
.finally(() => {

2
types/global.d.ts vendored
View File

@ -42,3 +42,5 @@ declare module '@wangeditor/editor-for-vue' {
export const Editor: DefineComponent<any, any, any>
export const Toolbar: DefineComponent<any, any, any>
}
declare module 'postcss-px-to-viewport'

View File

@ -88,15 +88,49 @@ export const formatNumber = (value?: number, decimals = 0) => {
* @param url 文件地址
* @param filename 文件名
*/
export const downloadFile = (url: string, filename: string) => {
const a = document.createElement('a')
a.href = url
a.download = filename || 'download' // 设置下载的文件名
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
}
export const downloadFile = async (url: string, filename: string) => {
if (!process.client) return;
try {
const response = await fetch(url);
const reader = response.body.getReader();
const contentLength = +response.headers.get('Content-Length');
let receivedLength = 0;
const stream = new ReadableStream({
async start(controller) {
while (true) {
const { done, value } = await reader.read();
if (done) {
controller.close();
return;
}
receivedLength += value.length;
const progress = (receivedLength / contentLength * 100).toFixed(2);
console.log(`下载进度: ${progress}%`);
controller.enqueue(value);
}
}
});
const blob = await new Response(stream).blob();
const objectURL = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = objectURL;
a.download = filename || 'download';
a.style.display = 'none';
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(objectURL);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 可添加错误提示逻辑
}
}
/**
* 判断传入的参数是否为数组

View File

@ -1009,7 +1009,7 @@
which "^5.0.0"
ws "^8.18.3"
"@nuxt/kit@3.18.1", "@nuxt/kit@^3.12.2", "@nuxt/kit@^3.13.2", "@nuxt/kit@^3.15.4", "@nuxt/kit@^3.17.6", "@nuxt/kit@^3.9.0":
"@nuxt/kit@3.18.1", "@nuxt/kit@^3.12.2", "@nuxt/kit@^3.13.2", "@nuxt/kit@^3.14.159", "@nuxt/kit@^3.15.4", "@nuxt/kit@^3.17.6", "@nuxt/kit@^3.9.0":
version "3.18.1"
resolved "https://registry.npmmirror.com/@nuxt/kit/-/kit-3.18.1.tgz"
integrity sha512-z6w1Fzv27CIKFlhct05rndkJSfoslplWH5fJ9dtusEvpYScLXp5cATWIbWkte9e9zFSmQTgDQJjNs3geQHE7og==
@ -2187,6 +2187,15 @@
"@uppy/utils" "^4.1.2"
nanoid "^3.1.25"
"@vant/nuxt@^1.0.7":
version "1.0.7"
resolved "https://registry.npmmirror.com/@vant/nuxt/-/nuxt-1.0.7.tgz#02ca3fe67be1d59c3d4f5e35316521d6ce5dfa3d"
integrity sha512-YVRJIDVlCCjWBhi0a/YBY0M04XmGwAqCkDSEIDIcbvzNN2z178iqKS23Py+c4hUv570LoKaIZMCQ75IJphJkTw==
dependencies:
"@nuxt/kit" "^3.14.159"
magic-string "^0.29.0"
unplugin "^1.16.0"
"@vercel/nft@0.29.4", "@vercel/nft@^0.29.4":
version "0.29.4"
resolved "https://registry.npmmirror.com/@vercel/nft/-/nft-0.29.4.tgz"
@ -4870,6 +4879,13 @@ magic-string@^0.27.0:
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13"
magic-string@^0.29.0:
version "0.29.0"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.29.0.tgz#f034f79f8c43dba4ae1730ffb5e8c4e084b16cf3"
integrity sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==
dependencies:
"@jridgewell/sourcemap-codec" "^1.4.13"
magic-string@^0.30.12, magic-string@^0.30.17, magic-string@^0.30.3, magic-string@^0.30.8:
version "0.30.17"
resolved "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz"
@ -5406,6 +5422,11 @@ nypm@^0.6.0, nypm@^0.6.1:
pkg-types "^2.2.0"
tinyexec "^1.0.1"
object-assign@>=4.0.1:
version "4.1.1"
resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.13.3:
version "1.13.4"
resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz"
@ -5922,6 +5943,14 @@ postcss-ordered-values@^7.0.2:
cssnano-utils "^5.0.1"
postcss-value-parser "^4.2.0"
postcss-px-to-viewport@^1.1.1:
version "1.1.1"
resolved "https://registry.npmmirror.com/postcss-px-to-viewport/-/postcss-px-to-viewport-1.1.1.tgz#a25ca410b553c9892cc8b525cc710da47bf1aa55"
integrity sha512-2x9oGnBms+e0cYtBJOZdlwrFg/mLR4P1g2IFu7jYKvnqnH/HLhoKyareW2Q/x4sg0BgklHlP1qeWo2oCyPm8FQ==
dependencies:
object-assign ">=4.0.1"
postcss ">=5.0.2"
postcss-reduce-initial@^7.0.4:
version "7.0.4"
resolved "https://registry.npmmirror.com/postcss-reduce-initial/-/postcss-reduce-initial-7.0.4.tgz"
@ -5974,7 +6003,7 @@ postcss-values-parser@^6.0.2:
is-url-superb "^4.0.0"
quote-unquote "^1.0.0"
postcss@^8.5.1, postcss@^8.5.6:
postcss@>=5.0.2, postcss@^8.5.1, postcss@^8.5.6:
version "8.5.6"
resolved "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
@ -7061,7 +7090,7 @@ unplugin-vue-router@^0.15.0:
unplugin-utils "^0.2.4"
yaml "^2.8.0"
unplugin@^1.10.0, unplugin@^1.15.0:
unplugin@^1.10.0, unplugin@^1.15.0, unplugin@^1.16.0:
version "1.16.1"
resolved "https://registry.npmmirror.com/unplugin/-/unplugin-1.16.1.tgz"
integrity sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==