Compare commits

..

81 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
4fb57c13c7 refactor: 更新字典树API参数和面包屑功能 2025-08-30 10:47:13 +08:00
29c34caa94 refactor: 重构模型页面组件和路由结构 2025-08-29 22:51:42 +08:00
8593a8b52e refactor: 优化空状态检查和页面缓存键 2025-08-29 22:24:09 +08:00
ede6eec78b refactor: 重构文本页面组件和数据获取方式 2025-08-29 22:07:46 +08:00
f0e65ee2ab refactor: 更新评论API请求方式和监听配置 2025-08-29 21:37:58 +08:00
b6fbdb2b26 fix: 修改清空未读信息API参数传递方式 2025-08-29 21:28:07 +08:00
8c265e3a21 refactor: 移除未使用的依赖并优化代码
- 移除 nuxt-swiper、tinymce 等相关未使用的依赖
- 优化频道关注接口的请求头配置
- 在频道操作中添加登录验证
- 修复样式类名中的语法错误
2025-08-29 21:18:39 +08:00
2c8eda876d refactor(tinymce): 移除冗余的TinyMCE静态资源文件
清理不再使用的TinyMCE插件、皮肤和语言文件,包括:
1. 删除多个插件的index.js入口文件
2. 移除shadowdom相关的CSS/JS皮肤文件
3. 清理content皮肤的多余CSS/JS文件
4. 删除中文帮助导航文档
5. 移除多个插件的plugin.js实现文件

这些文件在项目中已不再使用,清理以减少代码体积和维护成本。
2025-08-29 20:38:26 +08:00
229916000f Update sign content layout styles 2025-08-29 17:52:54 +08:00
7fe259299e Refactor drawing query pages and routing structure 2025-08-29 17:40:54 +08:00
a2c2a87e38 Add dict tree API and drawing query pages 2025-08-29 15:42:46 +08:00
0de028d6eb Refactor user store and persist configuration 2025-08-29 10:27:19 +08:00
41922df68b Refactor user store and async data handling 2025-08-29 09:25:55 +08:00
639486cd29 Update request headers and authorization handling 2025-08-29 09:23:07 +08:00
f4e80bc25a Update app layout and styles 2025-08-29 09:04:00 +08:00
e02c17066d Add dialog close handler and fix personal center routing 2025-08-29 08:53:43 +08:00
53121ca5f7 Add SEO Head component with meta tags 2025-08-28 22:38:17 +08:00
17a70cf2ac Update icon color styles and timeout values 2025-08-28 22:19:04 +08:00
6b8e6918aa Update styles and routing in components 2025-08-28 22:11:31 +08:00
6074bf9c8d Update WangEditor config and upload settings 2025-08-28 21:52:49 +08:00
9b46dacbb1 Update API request formatting and editor styles 2025-08-28 21:19:06 +08:00
cb81e9f0df Update navigation routing in UserInfo component 2025-08-28 18:01:08 +08:00
fadab4eacb Replace TinyMCE with WangEditor and update dependencies 2025-08-28 17:41:36 +08:00
3f1431f972 Update API formatting and routing structure 2025-08-28 11:29:20 +08:00
9edc63ff4f Fix typo in resetPassword function name 2025-08-28 08:51:59 +08:00
067dbae5ce Update sign bonus routing and cursor style 2025-08-27 22:07:29 +08:00
bc006c404b Update menu styles and navigation routing 2025-08-27 22:02:57 +08:00
170113e11c Update API request formatting and routing structure 2025-08-27 21:54:08 +08:00
0f96406b5a Fix code formatting and error handling 2025-08-27 16:31:23 +08:00
dc628e3494 Update API request formatting and error handling 2025-08-27 16:24:22 +08:00
600c3e9f18 Add TypeScript ignore comments to swiper initialization 2025-08-27 15:49:35 +08:00
720ff7484c Update swiper button styles 2025-08-27 15:49:11 +08:00
7413b1a74d Update swiper implementation and configuration 2025-08-27 15:45:40 +08:00
9acc229704 Refactor request formatting and configuration 2025-08-27 14:32:39 +08:00
64d0696cb9 Remove unused TinyMCE plugin file 2025-08-27 09:23:54 +08:00
2b2586293d Update swiper component formatting and structure 2025-08-27 09:22:25 +08:00
0c59337af3 Update Prettier config and code style 2025-08-27 09:18:26 +08:00
dd46a68b6c Update async navigation and optimize detail page 2025-08-26 22:42:39 +08:00
84a7263f36 Update detail page routing and swiper implementation 2025-08-26 21:28:11 +08:00
bfa0f5e6bd Update API request and styling 2025-08-26 20:32:31 +08:00
4263a0a235 Update store paths and add persistence plugin 2025-08-26 16:25:58 +08:00
e021ac1e05 Update token handling and request logging 2025-08-26 14:22:02 +08:00
5a2be1bc3b Refactor login response handling and token management 2025-08-26 11:30:29 +08:00
bc26b478a9 Add Prettier config and update code style 2025-08-26 11:20:53 +08:00
7291768b03 Update token handling and user state management 2025-08-25 22:51:06 +08:00
63fa551041 Update API request and navigation in sign page 2025-08-25 22:09:48 +08:00
cdcb6e76c2 Update API requests and styling 2025-08-25 22:04:25 +08:00
7413a1f8ea Update login components styling 2025-08-25 18:01:33 +08:00
6e2d5989eb Restructure components and update page configuration 2025-08-25 08:47:00 +08:00
0fe0f14193 Update login components and styling 2025-08-24 22:05:43 +08:00
43724c4d4d Refactor token handling and enable MQTT connection 2025-08-24 21:48:14 +08:00
587dbbeca6 Refactor token handling and update login flow 2025-08-24 21:06:11 +08:00
9bc983793f Refactor API requests and update global methods 2025-08-24 16:46:10 +08:00
366b48188d feat: downgrade tinymce and fix editor config 2025-08-24 15:54:02 +08:00
a2fe21a497 feat: tinymce 2025-08-24 09:04:21 +08:00
b603d9d854 Refactor API requests and update component styles 2025-08-23 22:55:59 +08:00
ee8ce32af6 Update navigation routing and add Element Plus plugin 2025-08-23 21:35:25 +08:00
38f6d8c062 Refactor API requests and update component styles 2025-08-23 17:14:42 +08:00
bf86652ff2 Restructure layout components and update page metadata 2025-08-21 22:40:17 +08:00
b7113c4e12 Update API types and request handling 2025-08-21 22:15:17 +08:00
0de28e554a Refactor API requests and update component keys 2025-08-21 21:43:46 +08:00
112 changed files with 3765 additions and 2323 deletions

17
.prettierrc.cjs Normal file
View File

@ -0,0 +1,17 @@
module.exports = {
singleQuote: true, // 使用单引号
printWidth: 160,
tabWidth: 2,
useTabs: false,
semi: false,
vueIndentScriptAndStyle: true,
quoteProps: 'as-needed',
bracketSpacing: true,
arrowParens: 'always',
insertPragma: false,
requirePragma: false,
trailingComma: 'es5',
proseWrap: 'never',
htmlWhitespaceSensitivity: 'strict',
endOfLine: 'auto',
}

10
.vscode/settings.json vendored
View File

@ -1,5 +1,5 @@
{ // {
"editor.formatOnSave": true, // "editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode", // "editor.defaultFormatter": "esbenp.prettier-vscode",
"prettier.configPath": "./.prettierrc" // "prettier.configPath": "../.prettierrc",
} // }

View File

@ -20,7 +20,7 @@ import type {
* @return {Promise} * @return {Promise}
*/ */
export const page = (params: TpageReq) => { export const page = (params: TpageReq) => {
return useFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/posts/page', params) return useDollarFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/posts/page', { query: params })
} }
/** /**
@ -35,7 +35,7 @@ export const create = (params: TcreateReq) => {
* @return {Promise} * @return {Promise}
*/ */
export const list = () => { export const list = () => {
return useFetchRequest.get<IResponse<TlistRes[]>>('/prod-api/app-api/business/channel/list') return useDollarFetchRequest.get<IResponse<TlistRes[]>>('/prod-api/app-api/business/channel/list')
} }
/** /**
* 获得论坛频道列表 * 获得论坛频道列表
@ -56,7 +56,7 @@ export const postsDelete = (params: { id: number }) => {
* @return {Promise} * @return {Promise}
*/ */
export const getChannelDetail = (params: { id: string }) => { export const getChannelDetail = (params: { id: string }) => {
return useFetchRequest.get<IResponse<TGetChannelPostsRes>>('/prod-api/app-api/business/posts/get', params) return useFetchRequest.get<IResponse<TGetChannelPostsRes>>('/prod-api/app-api/business/posts/get', { query: params })
} }
/** /**
@ -64,7 +64,7 @@ export const getChannelDetail = (params: { id: string }) => {
* @return {Promise} * @return {Promise}
*/ */
export const postscommentpage = (params: { postsId: string; pageNo: number; pageSize: number }) => { export const postscommentpage = (params: { postsId: string; pageNo: number; pageSize: number }) => {
return useFetchRequest.get<IResponse<PageResultPostsCommentRespVO>>('/prod-api/app-api/business/posts-comment/page', params) return useFetchRequest.get<IResponse<PageResultPostsCommentRespVO>>('/prod-api/app-api/business/posts-comment/page', { query: params })
} }
/** /**
* 创建帖子评论 * 创建帖子评论
@ -93,42 +93,47 @@ export const sendKefuMessage = (params: SingleMessageVo) => {
* 获得消息记录分页 * 获得消息记录分页
*/ */
export const getMessagePage = (params: { pageNo: number; pageSize: number; fromId?: number; msgType?: number; topic: string }) => { export const getMessagePage = (params: { pageNo: number; pageSize: number; fromId?: number; msgType?: number; topic: string }) => {
return useFetchRequest.get<IResponse<PageResultMessageRespVO>>('/prod-api/app-api/mqtt/message/page', params) return useDollarFetchRequest.get<IResponse<PageResultMessageRespVO>>('/prod-api/app-api/mqtt/message/page', { query: params })
} }
/** /**
* 会话列表 * 会话列表
*/ */
export const conversationList = () => { export const conversationList = () => {
return useFetchRequest.get<IResponse<PageResultSessionRespVO[]>>('/prod-api/app-api/mqtt/session/list') return useDollarFetchRequest.get<IResponse<PageResultSessionRespVO[]>>('/prod-api/app-api/mqtt/session/list')
} }
/** /**
* 获取聊天记录 * 获取聊天记录
*/ */
export const getChatDetail = (params: { sessionId: number; pageNo: number; pageSize: number }) => { export const getChatDetail = (params: { sessionId: number; pageNo: number; pageSize: number }) => {
return useFetchRequest.get<IResponse<PageResultMessageRespVO>>('/prod-api/app-api/mqtt/message/pageBySession', params) return useDollarFetchRequest.get<IResponse<PageResultMessageRespVO>>('/prod-api/app-api/mqtt/message/pageBySession', { query: params })
} }
/** /**
* 清空未读信息 * 清空未读信息
*/ */
export const clearUnreadMessage = (params: { id: number }) => { export const clearUnreadMessage = (params: { id: number }) => {
return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/mqtt/session/clear', { params }) return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/mqtt/session/clear?id=' + params.id, params)
} }
/** /**
* 获得论坛频道 * 获得论坛频道
*/ */
export const getChannelLunTanDetail = (params: { id: string }) => { export const getChannelLunTanDetail = (params: { id: string }) => {
return useFetchRequest.get<IResponse<ChannelRespVO>>('/prod-api/app-api/business/channel/get', params) return useDollarFetchRequest.get<IResponse<ChannelRespVO>>('/prod-api/app-api/business/channel/get', { query: params })
} }
/** /**
* 创建论坛关注 * 创建论坛关注
*/ */
export const createChannelFollow = (params: { channelId: string }) => { export const createChannelFollow = (params: { channelId: string }) => {
return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/channel-follow/create', params) return useDollarFetchRequest.post<IResponse<boolean>>(`/prod-api/app-api/business/channel-follow/create?channelId=${params.channelId}`, {}, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
},
})
} }
/** /**

View File

@ -36,7 +36,7 @@ export const creatFile = (params: fileCreateReqVO) => {
* @return {Promise} * @return {Promise}
*/ */
export const getUserInfo = () => { export const getUserInfo = () => {
return useFetchRequest.get<IResponse<AppMemberUserInfoRespVO>>('/prod-api/app-api/member/user/get') return useDollarFetchRequest.get<IResponse<AppMemberUserInfoRespVO>>('/prod-api/app-api/member/user/get')
} }
/** /**
@ -59,5 +59,5 @@ export const sendSms = (params: { mobile: string; scene: number }) => {
* 获得站内信 * 获得站内信
*/ */
export const getMessage = (params: { id: number }) => { export const getMessage = (params: { id: number }) => {
return useFetchRequest.get<IResponse<NotifyMessageRespVO>>('/prod-api/app-api/system/notify-message/get', { params }) return useDollarFetchRequest.get<IResponse<NotifyMessageRespVO>>('/prod-api/app-api/system/notify-message/get', { query:params })
} }

View File

@ -7,7 +7,7 @@ import type { ProjectRespVO, PageResultProjectCommentResVO, ProjectDrawPageRespV
* @return {Promise} * @return {Promise}
*/ */
export const getDetail = (params: { id?: number | string }) => { export const getDetail = (params: { id?: number | string }) => {
return useFetchRequest.get<IResponse<ProjectRespVO>>('/prod-api/app-api/business/app/project-draw/preview', { params }) return useFetchRequest.get<IResponse<ProjectRespVO>>('/prod-api/app-api/business/app/project-draw/preview', { query:params })
} }
/** /**
@ -15,7 +15,7 @@ export const getDetail = (params: { id?: number | string }) => {
* @return {Promise} * @return {Promise}
*/ */
export const getCommentList = (params: { relationId?: number | string; pageNum?: number; pageSize?: number }) => { export const getCommentList = (params: { relationId?: number | string; pageNum?: number; pageSize?: number }) => {
return useFetchRequest.get<IResponse<PageResultProjectCommentResVO>>('/prod-api/app-api/business/app/project-comment/page', { params }) return useDollarFetchRequest.get<IResponse<PageResultProjectCommentResVO>>('/prod-api/app-api/business/app/project-comment/page', { query:params })
} }
/** /**
@ -31,7 +31,7 @@ export const createComment = (params: { relationId?: number | string; content?:
* @return {Promise} * @return {Promise}
*/ */
export const getRelationRecommend = (params: { type?: number | string; projectType?: number | string }) => { export const getRelationRecommend = (params: { type?: number | string; projectType?: number | string }) => {
return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/top-list', { params }) return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/top-list', { query:params })
} }
/** /**

View File

@ -15,7 +15,7 @@ import type {
* @returns * @returns
*/ */
export const hotTop = (params: ThotTopReq) => { export const hotTop = (params: ThotTopReq) => {
return useDollarFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/hot-top', { params }) return useDollarFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/hot-top', { query: params })
} }
/** /**
@ -24,21 +24,21 @@ export const hotTop = (params: ThotTopReq) => {
* @returns * @returns
*/ */
export const recommendTop = (params: ThotTopReq) => { export const recommendTop = (params: ThotTopReq) => {
return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/recommend-top', { params }) return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/recommend-top', { query: params })
} }
/** /**
* 获取最新图纸信息 * 获取最新图纸信息
*/ */
export const newDraw = (params: { type: number; limit: number }) => { export const newDraw = (params: { type: number; limit: number }) => {
return useDollarFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/project/index/draw-new', { params }) return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/project/index/draw-new', { query: params })
} }
/** /**
* 首页-热点标签 * 首页-热点标签
*/ */
export const hotTag = (params: { type: number; limit: number; size: number }) => { export const hotTag = (params: { type: number; limit: number; size: number }) => {
return useDollarFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/project/index/index-hot-tab', { params }) return useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/project/index/index-hot-tab', { query: params })
} }
/** /**
@ -59,7 +59,7 @@ export const top = (params: { type: number; limit: number }) => {
* 获取用户top数据 * 获取用户top数据
*/ */
export const userTop = (params: { type?: number }) => { export const userTop = (params: { type?: number }) => {
return useDollarFetchRequest.get<IResponse<ProjectTrendingScoreUserInfoVO[]>>('/prod-api/app-api/business/project/index/user-top', { params }) return useFetchRequest.get<IResponse<ProjectTrendingScoreUserInfoVO[]>>('/prod-api/app-api/business/project/index/user-top', { query: params })
} }
/** /**
@ -72,13 +72,19 @@ export const settinngPage = (params: { pageNo?: number; pageSize: number; type:
* 获得首页设置信息分页 * 获得首页设置信息分页
*/ */
export const getSettingPage = (params: { type: number }) => { export const getSettingPage = (params: { type: number }) => {
return useFetchRequest.get<IResponse<PageResultIndexSettingRespVO[]>>('/prod-api/app-api/system/index-setting/list', { params }) return useFetchRequest.get<IResponse<PageResultIndexSettingRespVO[]>>('/prod-api/app-api/system/index-setting/list', { query: params })
} }
/** /**
* 首页-标签2 * 首页-标签2
*/ */
export const tab2 = () => { export const tab2 = () => {
return useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/project/index/index-tab2', { return useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/project/index/index-tab2', {})
}) }
/**
* 获取具有上下级关系的当前的名称
*/
export const getDictTree = (params: { type: number, id: number}) => {
return useDollarFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/app/dict/path-by-id', { query: params })
} }

View File

@ -2,7 +2,7 @@ export interface ThotTopReq {
/** 类型: 1 图纸 2 文本 3 模型 */ /** 类型: 1 图纸 2 文本 3 模型 */
type: number type: number
/** 项目分类 */ /** 项目分类 */
projectType: string projectType: number
/** 是否国内 0 国外 1 国内 */ /** 是否国内 0 国外 1 国内 */
isDomestic: number isDomestic: number
projectTypeTop?: string projectTypeTop?: string

View File

@ -8,7 +8,7 @@ import type { AppPayWalletPackageRespVO, PayOrderSubmitReqVO, PayOrderRespVO, Pa
* @returns * @returns
*/ */
export const listVip = () => { export const listVip = () => {
return useFetchRequest.get<IResponse<AppPayWalletPackageRespVO[]>>('/prod-api/app-api/pay/wallet-recharge-package/list-vip', {}) return useDollarFetchRequest.get<IResponse<AppPayWalletPackageRespVO[]>>('/prod-api/app-api/pay/wallet-recharge-package/list-vip', {})
} }
/** /**
@ -36,14 +36,14 @@ export const createOrder = (params: { spuId: number }) => {
* 获得钱包充值套餐列表 * 获得钱包充值套餐列表
*/ */
export const listWalletRechargePackage = () => { export const listWalletRechargePackage = () => {
return useFetchRequest.get<IResponse<AppPayWalletPackageRespVO[]>>('/prod-api/app-api/pay/wallet-recharge-package/list', {}) return useDollarFetchRequest.get<IResponse<AppPayWalletPackageRespVO[]>>('/prod-api/app-api/pay/wallet-recharge-package/list', {})
} }
/** /**
* 获取支付状态 * 获取支付状态
*/ */
export const getPayStatus = (params: { id: number }) => { export const getPayStatus = (params: { id: number }) => {
return useDollarFetchRequest.get<IResponse<PayOrderRespVO>>('/prod-api/app-api/pay/order/get', params) return useDollarFetchRequest.get<IResponse<PayOrderRespVO>>('/prod-api/app-api/pay/order/get', {query:params})
} }
/** /**
@ -71,5 +71,5 @@ export const socialLoginByCode = (params: { type: number; code: string; state: s
* 获得钱包充值记录分页 * 获得钱包充值记录分页
*/ */
export const getWalletRechargeRecordPage = (params: { pageNo: number; pageSize: number }) => { export const getWalletRechargeRecordPage = (params: { pageNo: number; pageSize: number }) => {
return useFetchRequest.get<IResponse<PageResultAppPayWalletRechargeRespVO>>('/prod-api/app-api/pay/wallet-transaction/page', params) return useDollarFetchRequest.get<IResponse<PageResultAppPayWalletRechargeRespVO>>('/prod-api/app-api/pay/wallet-transaction/page', {query:params})
} }

View File

@ -17,7 +17,7 @@ import type {
* @returns * @returns
*/ */
export const getUserInfo = () => { export const getUserInfo = () => {
return useFetchRequest.get<IResponse<UserExtendRespVO>>('/prod-api/app-api/member/user-extend/get', {}) return useDollarFetchRequest.get<IResponse<UserExtendRespVO>>('/prod-api/app-api/member/user-extend/get', {})
} }
/** /**
@ -66,13 +66,13 @@ export const updateUserAuthInfo = (params: UserAuthInfoRespVO) => {
* 获得内容信息分页 * 获得内容信息分页
*/ */
export const getContentPage = (params: { type: number }) => { export const getContentPage = (params: { type: number }) => {
return useFetchRequest.get<IResponse<PageResultProjectHistoryResVO>>('/prod-api/app-api/business/project-history/page', params) return useDollarFetchRequest.get<IResponse<PageResultProjectHistoryResVO>>('/prod-api/app-api/business/project-history/page', {query:params})
} }
/** /**
* 获得用户项目工具箱下载分页 * 获得用户项目工具箱下载分页
*/ */
export const getUserToolBoxPage = (params: { pageNum: number; pageSize: number; type?: number }) => { export const getUserToolBoxPage = (params: { pageNum: number; pageSize: number; type?: number }) => {
return useFetchRequest.get<IResponse<PageResultProjectHistoryResVO>>('/prod-api/app-api/business/project-member-file/page', params) return useDollarFetchRequest.get<IResponse<PageResultProjectHistoryResVO>>('/prod-api/app-api/business/project-member-file/page', {query:params})
} }
/** /**
@ -92,54 +92,54 @@ export const signIn = () => {
* 获得用户积分记录分页 * 获得用户积分记录分页
*/ */
export const getUserPointPage = (params: { pageNo: number; pageSize: number }) => { export const getUserPointPage = (params: { pageNo: number; pageSize: number }) => {
return useFetchRequest.get<IResponse<PageResultMemberPointRecordRespVO>>('/prod-api/app-api/member/point/record/page', params) return useDollarFetchRequest.get<IResponse<PageResultMemberPointRecordRespVO>>('/prod-api/app-api/member/point/record/page', {query:params})
} }
/** /**
* 近期收益和近期活跃 * 近期收益和近期活跃
*/ */
export const getRecentIncomeAndActive = (params: { type: number; limit: number }) => { export const getRecentIncomeAndActive = (params: { type: number; limit: number }) => {
return useFetchRequest.get<IResponse<UserStatisticsLineRespVO>>('/prod-api/app-api/member/statistics/line', params) return useDollarFetchRequest.get<IResponse<UserStatisticsLineRespVO>>('/prod-api/app-api/member/statistics/line', {query:params})
} }
/** /**
* *
资源下载分布 资源下载分布
*/ */
export const getResourceDistribution = (params: { type: number; limit: number }) => { export const getResourceDistribution = (params: { type: number; limit: number }) => {
return useFetchRequest.get<IResponse<UserStatisticsBarRespVO>>('/prod-api/app-api/member/statistics/bar', params) return useDollarFetchRequest.get<IResponse<UserStatisticsBarRespVO>>('/prod-api/app-api/member/statistics/bar', {query:params})
} }
/** /**
* 我的数据统计 包括我的金币 我的关注 我的发布等等 * 我的数据统计 包括我的金币 我的关注 我的发布等等
*/ */
export const getUserStatistics = () => { export const getUserStatistics = () => {
return useFetchRequest.get<IResponse<UserStatisticsCountRespVO>>('/prod-api/app-api/member/statistics/count', {}) return useDollarFetchRequest.get<IResponse<UserStatisticsCountRespVO>>('/prod-api/app-api/member/statistics/count', {})
} }
/** /**
* 获得项目订单用户收藏信息分页 * 获得项目订单用户收藏信息分页
*/ */
export const getUserFavoritePage = (params: { pageNo: number; pageSize: number; userId: any; type: number }) => { export const getUserFavoritePage = (params: { pageNo: number; pageSize: number; userId: any; type: number }) => {
return useFetchRequest.get<IResponse<PageResultProjectMemberFavoritesRespVO>>('/prod-api/app-api/business/project-member-favorites/page', params) return useDollarFetchRequest.get<IResponse<PageResultProjectMemberFavoritesRespVO>>('/prod-api/app-api/business/project-member-favorites/page', {query:params})
} }
/*** /***
* 自己发布的-内容信息分页 * 自己发布的-内容信息分页
*/ */
export const getOwnContentPage = (params: { pageNo: number; pageSize: number; type: number }) => { export const getOwnContentPage = (params: { pageNo: number; pageSize: number; type: number }) => {
return useFetchRequest.get<IResponse<PageResultProjectHistoryResVO>>('/prod-api/app-api/business/app/project-draw/my-page', params) return useDollarFetchRequest.get<IResponse<PageResultProjectHistoryResVO>>('/prod-api/app-api/business/app/project-draw/my-page', {query:params})
} }
/** /**
* 下架 * 下架
*/ */
export const offShelf = (params: { id: number }) => { 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 }) => { 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)
} }
/** /**

View File

@ -15,5 +15,5 @@ export const create = (params: TcreateReq) => {
* 获得内容信息分页 * 获得内容信息分页
*/ */
export const page = (params: TpageReq) => { export const page = (params: TpageReq) => {
return useFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/app/project-resource/page', params) return useFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/app/project-resource/page', {query:params})
} }

View File

@ -15,8 +15,16 @@ export const create = (params: TcreateReq) => {
* @param params * @param params
* @returns * @returns
*/ */
export const parent = (params: { type: string | number; parentId: number }) => { export const parent = (params: { type: string | number; parentId: number | string }) => {
return useFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/app/dict/parent', params) return useFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/app/dict/parent', { query: params })
}
/**
* 获取具有上下级的字典信息
* @param params
* @returns
*/
export const parentV2 = (params: { type: string | number; parentId: number | string }) => {
return useDollarFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/app/dict/parent', { query: params })
} }
/** /**
* 获取具有上下级的字典信息 * 获取具有上下级的字典信息
@ -32,7 +40,7 @@ export const indexTabs = () => {
* @returns * @returns
*/ */
export const keywords = (params: { type: string | number; keywords: string }) => { export const keywords = (params: { type: string | number; keywords: string }) => {
return useFetchRequest.get<IResponse<boolean>>('/prod-api/app-api/business/app/dict/label-keywords', params) return useFetchRequest.get<IResponse<boolean>>('/prod-api/app-api/business/app/dict/label-keywords', {query:params})
} }
/** /**
* 获取格式类型字典信息 * 获取格式类型字典信息
@ -48,7 +56,7 @@ export const labels = (params: { type: string | number }) => {
* @returns * @returns
*/ */
export const page = (params: pageReq) => { export const page = (params: pageReq) => {
return useFetchRequest.get<IResponse<pageRes>>('/prod-api/app-api/business/app/project-draw/page', params) return useFetchRequest.get<IResponse<pageRes>>('/prod-api/app-api/business/app/project-draw/page', { query: params })
} }
/** /**
* 获得项目表内容信息分页 * 获得项目表内容信息分页

View File

@ -52,7 +52,7 @@ export interface pageReq {
projectId?: number | string projectId?: number | string
title?: string title?: string
ownedUserId?: string ownedUserId?: string
editions?: string editions?: any
labels?: any[] labels?: any[]
type?: number // 类型: 1 图纸 2 文本 3 模型 type?: number // 类型: 1 图纸 2 文本 3 模型
source?: number | string source?: number | string
@ -60,7 +60,7 @@ export interface pageReq {
status?: any status?: any
createAddress?: string createAddress?: string
createIp?: string createIp?: string
projectType?: string projectType?: any
} }
export interface pageRes { export interface pageRes {
list: { list: {

31
app.vue
View File

@ -1,6 +1,33 @@
<template> <template>
<div> <div class="flex flex-col flex-1">
<NuxtLoadingIndicator /> <NuxtLoadingIndicator />
<NuxtPage /> <NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div> </div>
</template> </template>
<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

@ -5,12 +5,20 @@ ul,
li { li {
margin: 0; margin: 0;
padding: 0; padding: 0;
font-family: Microsoft YaHei, PingFang SC, Helvetica Neue, Helvetica, Hiragino Sans GB, Arial, sans-serif; font-family:
Microsoft YaHei,
PingFang SC,
Helvetica Neue,
Helvetica,
Hiragino Sans GB,
Arial,
sans-serif;
font-weight: normal; font-weight: normal;
word-break: break-all; word-break: break-all;
} }
body { body {
min-width: 1500px; min-width: 1500px;
font-size: 14px;
} }
img { img {
user-select: none; user-select: none;
@ -26,7 +34,7 @@ a {
--el-color-primary-light-1: rgba(8, 88, 247, 0.9); --el-color-primary-light-1: rgba(8, 88, 247, 0.9);
} }
#app { #__nuxt {
min-height: 100vh; min-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@ -1,15 +1,15 @@
<template> <template>
<div class="mt-30px"> <div class="mt-[30px] w-[100%]">
<div class="h-48px w-938px rounded-1px bg-[#F8F8F8] pl-10px text-16px text-[#333333] font-normal line-height-50px"> 共有{{ result.total || 0 }}条评论 </div> <div class="h-[48px] w-[100%] rounded-[1px] bg-[#F8F8F8] pl-[10px] text-[16px] text-[#333333] font-normal line-height-[50px]"> 共有{{ result.total || 0 }}条评论 </div>
<div v-for="item in result.list" :key="item.id" class="mt-20px border-b-1px border-b-[#eee] border-b-solid pb-14px"> <div v-for="item in result.list" :key="item.id" class="mt-[20px] border-b-[1px] border-b-[#eee] border-b-solid pb-[14px]">
<div class="flex items-start"> <div class="flex items-start">
<el-avatar :src="item.creatorInfo.avatar" alt="" srcset="" class="h-50px w-49px rounded-full" /> <el-avatar :src="item.creatorInfo.avatar" alt="" srcset="" class="h-[50px] w-[49px] rounded-full" />
<div class="flex-1 pl-8px"> <div class="flex-1 pl-[8px]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="relative top-4px text-14px!">{{ item.creatorInfo.nickName }}</div> <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 class="text-[12px] text-[#999999] font-normal">发表时间{{ dayjs(item.creatorInfo.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
</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> </div>
</div> </div>
@ -19,12 +19,12 @@
:page-size="query.pageSize" :page-size="query.pageSize"
layout="prev, pager, next" layout="prev, pager, next"
:total="result.total" :total="result.total"
class="mt-10px" class="mt-[10px]"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
<el-input v-model="commentContent" type="textarea" :rows="6" placeholder="请输入内容" class="mt-20px w-100%"></el-input> <el-input v-model="commentContent" type="textarea" :rows="6" placeholder="请输入内容" class="mt-[20px] w-[100%]"></el-input>
<div> <div>
<el-button type="primary" class="mt-10px h-40px w-101px rounded-4px text-16px text-[#FFFFFF] font-bold" @click="handleCreateComment"> <el-button type="primary" class="mt-[10px] h-[40px] w-[101px] rounded-[4px] text-[16px] text-[#FFFFFF] font-bold" @click="handleCreateComment">
发表评论 发表评论
</el-button> </el-button>
</div> </div>
@ -87,6 +87,8 @@
() => props.relationId, () => props.relationId,
() => { () => {
handleGetCommentList() handleGetCommentList()
},{
immediate: true
} }
) )
</script> </script>

View File

@ -1,17 +1,17 @@
<template> <template>
<div class="relative mt-34px w-100%"> <div class="relative mt-[34px] w-[100%]">
<KlTabBar v-model="query.source" :data="tabBar" /> <KlTabBar v-model="query.source" :data="tabBar" />
<div class="absolute right-0px top-10px text-16px text-[#999999] font-normal" <div class="absolute right-[0px] top-[10px] text-[16px] text-[#999999] font-normal"
><span class="color-#1A65FF">{{ result.total }}</span ><span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div >个筛选结果</div
> >
<div class="content mt-10px"> <div class="content mt-[10px]">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col v-for="(item, index) in result.list" :key="index" :span="6"> <el-col v-for="(item, index) in result?.list" :key="index" :span="6">
<CardPicture :item-info="item" /> <CardPicture :item-info="item" />
</el-col> </el-col>
</el-row> </el-row>
<el-empty v-if="!result.list.length" :image="emptyImg"></el-empty> <el-empty v-if="!result?.list.length" :image="emptyImg"></el-empty>
</div> </div>
</div> </div>
</template> </template>
@ -27,14 +27,14 @@
required: true, required: true,
}) })
const result = defineModel<pageRes>('result', { const result = defineModel<pageRes | null>('result', {
required: true, required: true,
}) })
const tabBar = ref([ const tabBar = ref([
{ {
label: '图纸推荐', label: '图纸推荐',
value: '', value: -1,
}, },
{ {
label: '原创图纸', label: '原创图纸',

View File

@ -1,18 +1,18 @@
<template> <template>
<div class="relative mt-34px w-100%"> <div class="relative mt-[34px] w-[100%]">
<KlTabBar v-model="tabIndex" :data="tabBar" /> <KlTabBar v-model="tabIndex" :data="tabBar" />
<KlWallpaperCategory v-model="query" v-model:level="level" :type="1" /> <KlWallpaperCategory v-model="query" v-model:level="level" :type="1" />
<div class="absolute right-0px top-10px text-16px text-[#999999] font-normal" <div class="absolute right-[0px] top-[10px] text-[16px] text-[#999999] font-normal"
><span class="color-#1A65FF">{{ result.total }}</span ><span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div >个筛选结果</div
> >
<div class="content mt-10px"> <div class="content mt-[10px]">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col v-for="(item, index) in result.list" :key="index" :span="6"> <el-col v-for="(item, index) in result?.list" :key="index" :span="6">
<CardPicture :item-info="item" /> <CardPicture :item-info="item" />
</el-col> </el-col>
</el-row> </el-row>
<el-empty v-if="!result.list.length" description="暂无数据"></el-empty> <el-empty v-if="!result?.list.length" description="暂无数据"></el-empty>
</div> </div>
</div> </div>
</template> </template>
@ -20,6 +20,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import KlTabBar from '~/components/kl-tab-bar/index.vue' import KlTabBar from '~/components/kl-tab-bar/index.vue'
import CardPicture from '~/components/kl-card-picture/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 { ref } from 'vue'
import type { pageRes } from '~/api/upnew/types' import type { pageRes } from '~/api/upnew/types'
@ -31,26 +32,26 @@
}, },
]) ])
const result = defineModel<pageRes>('modelValue', { const result = defineModel<pageRes| null>('modelValue', {
required: true, required: true,
}) })
const query = defineModel<any>('query', { const query = defineModel<any>('query', {
required: true, required: true,
}) })
const tabIndex = ref(1) const tabIndex = ref(-1)
const tabBar = ref([ const tabBar = ref([
{ {
label: '图纸推荐', label: '图纸推荐',
value: 1, value: -1,
}, },
{ {
label: '原创图纸', label: '原创图纸',
value: 2, value: 1,
}, },
{ {
label: '最新上传', label: '最新上传',
value: 3, value: 2,
}, },
]) ])
</script> </script>

View File

@ -1,33 +1,33 @@
<template> <template>
<div class="mb-20px w-100% cursor-pointer overflow-hidden border border-[#EEEEEE] rounded-10px border-solid bg-[#FFFFFF]" @click="handleClick"> <div class="mb-[20px] w-[100%] cursor-pointer overflow-hidden border border-[#EEEEEE] rounded-[10px] border-solid bg-[#FFFFFF]" @click="handleClick">
<div> <div>
<el-image :src="props.itemInfo.iconUrl" class="h-216px w-100%" fit="cover"></el-image> <el-image :src="props.itemInfo.iconUrl" class="h-[216px] w-[100%]" fit="cover"></el-image>
</div> </div>
<div class="box-border p-16px"> <div class="box-border p-[16px]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div> <div>
<div class="title mr-38px text-16px text-[#333333] font-bold">{{ props.itemInfo.title }}</div> <div class="title mr-[38px] text-[16px] text-[#333333] font-bold">{{ props.itemInfo.title }}</div>
<div class="mt-8px text-15px text-[#999999] font-normal">by {{ props.itemInfo?.ownedUserIdInfo?.nickName }}</div> <div class="mt-[8px] text-[15px] text-[#999999] font-normal">by {{ props.itemInfo?.ownedUserIdInfo?.nickName }}</div>
</div> </div>
<div><img :src="props.itemInfo?.ownedUserIdInfo?.avatar" alt="" srcset="" class="h-40px w-40px rd-50%" /></div> <div><img :src="props.itemInfo?.ownedUserIdInfo?.avatar" alt="" srcset="" class="h-[40px] w-[40px] rd-[50%]" /></div>
</div> </div>
<div class="mt-24px flex items-center justify-between"> <div class="mt-[24px] flex items-center justify-between">
<div class="flex items-center justify-between text-14px text-[#666666] font-normal"> <div class="flex items-center justify-between text-[14px] text-[#666666] font-normal">
<div class="mr-9px flex items-center"> <div class="mr-[9px] flex items-center">
<img src="~/assets/images/look.png" alt="" srcset="" class="mr-2px h-17px" /> <img src="~/assets/images/look.png" alt="" srcset="" class="mr-[2px] h-[17px]" />
<span class="color-#666">{{ props.itemInfo.previewPoint }}</span> <span class="color-[#666]">{{ props.itemInfo.previewPoint }}</span>
</div> </div>
<div class="mr-9px flex items-center"> <div class="mr-[9px] flex items-center">
<img src="~/assets/images/add.png" alt="" srcset="" class="mr-2px h-22px" /> <img src="~/assets/images/add.png" alt="" srcset="" class="mr-[2px] h-[22px]" />
<span class="color-#666">{{ props.itemInfo.hotPoint }}</span> <span class="color-[#666]">{{ props.itemInfo.hotPoint }}</span>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/chat.png" alt="" srcset="" class="mr-4px h-17px" /> <img src="~/assets/images/chat.png" alt="" srcset="" class="mr-[4px] h-[17px]" />
<span class="color-#666">{{ props.itemInfo.commentsPoint }}</span> <span class="color-[#666]">{{ props.itemInfo.commentsPoint }}</span>
</div> </div>
</div> </div>
<div class="h-30px w-90px cursor-pointer border border-[#1A65FF] rounded-15px border-solid text-center line-height-30px" <div class="h-[30px] w-[90px] cursor-pointer border border-[#1A65FF] rounded-[15px] border-solid text-center line-height-[30px]"
><span class="text-14px text-[#1A65FF] font-normal">查看详情</span></div ><span class="text-[14px] text-[#1A65FF] font-normal">查看详情</span></div
> >
</div> </div>
</div> </div>
@ -46,11 +46,11 @@
}, },
}) })
const handleClick = () => { const handleClick = async () => {
console.log(props.itemInfo) console.log(props.itemInfo)
// 跳转到下载详情页 并且是单独开标签 // 跳转到下载详情页 并且是单独开标签
window.open(`/down-drawe-detail?id=${props.itemInfo.id}`, '_blank') // 修改为在新窗口打开 await navigateTo(`/down-drawe-detail/${props.itemInfo.id}`) // 修改为在新窗口打开
} }
</script> </script>

View File

@ -3,7 +3,7 @@
<div v-if="visible" class="popup-overlay"> <div v-if="visible" class="popup-overlay">
<div class="popup-content"> <div class="popup-content">
<div class="login-container relative"> <div class="login-container relative">
<el-icon class="absolute right-0 top-0 cursor-pointer" @click="onClose()"><Close /></el-icon> <el-icon class="absolute! right-0 top-0 cursor-pointer" @click="onClose()"><Close /></el-icon>
<!-- 左侧插图 --> <!-- 左侧插图 -->
<div class="login-left"> <div class="login-left">
<img src="~/assets/images/login-illustration.png" alt="login" class="login-img" /> <img src="~/assets/images/login-illustration.png" alt="login" class="login-img" />
@ -87,7 +87,7 @@
import { login, sendEmailCode, loginByEmail } from '~/api/login/index' import { login, sendEmailCode, loginByEmail } from '~/api/login/index'
import refreshToken from '~/utils/RefreshToken' import refreshToken from '~/utils/RefreshToken'
import { handleLoginQQ, handleLoginWechat, generateRandomString } from '~/utils/login' import { handleLoginQQ, handleLoginWechat, generateRandomString } from '~/utils/login'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const { $openRegister, $openLogin } = useNuxtApp() const { $openRegister, $openLogin } = useNuxtApp()

View File

@ -3,7 +3,7 @@
<div v-if="visible" class="popup-overlay"> <div v-if="visible" class="popup-overlay">
<div class="popup-content"> <div class="popup-content">
<div class="login-container relative"> <div class="login-container relative">
<el-icon class="absolute right-0 top-0 cursor-pointer" @click="onClose()"><Close /></el-icon> <el-icon class="absolute! right-[0px] top-[0px] cursor-pointer" @click="onClose()"><Close /></el-icon>
<!-- 左侧插图 --> <!-- 左侧插图 -->
<div class="login-left"> <div class="login-left">
<img src="~/assets/images/login-illustration.png" alt="login" class="login-img" /> <img src="~/assets/images/login-illustration.png" alt="login" class="login-img" />
@ -23,7 +23,7 @@
<el-form ref="formRef" :model="loginForm" :rules="rules" class="login-form"> <el-form ref="formRef" :model="loginForm" :rules="rules" class="login-form">
<template v-if="activeTab === 'account'"> <template v-if="activeTab === 'account'">
<el-form-item prop="mobile"> <el-form-item prop="mobile">
<el-input v-model="loginForm.mobile" placeholder="请输入用户名" :prefix-icon="User" class="w-322px!" /> <el-input v-model="loginForm.mobile" placeholder="请输入用户名" :prefix-icon="User" class="w-[322px]!" />
</el-form-item> </el-form-item>
<el-form-item prop="password"> <el-form-item prop="password">
<el-input v-model="loginForm.password" placeholder="请输入密码" :prefix-icon="Lock" type="password" /> <el-input v-model="loginForm.password" placeholder="请输入密码" :prefix-icon="Lock" type="password" />
@ -33,7 +33,7 @@
<!-- 验证码登录 --> <!-- 验证码登录 -->
<template v-else> <template v-else>
<el-form-item prop="phone"> <el-form-item prop="phone">
<el-input v-model="loginForm.phone" placeholder="请输入手机号" class="w-322px!" /> <el-input v-model="loginForm.phone" placeholder="请输入手机号" class="w-[322px]!" />
</el-form-item> </el-form-item>
<el-form-item prop="code"> <el-form-item prop="code">
<div class="verify-code-input"> <div class="verify-code-input">
@ -88,9 +88,12 @@
import { sendSms } from '~/api/common/index' import { sendSms } from '~/api/common/index'
import REFRESHTOKEN from '~/utils/RefreshToken' import REFRESHTOKEN from '~/utils/RefreshToken'
import { handleLoginQQ, handleLoginWechat } from '~/utils/login' import { handleLoginQQ, handleLoginWechat } from '~/utils/login'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const { $openRegister, $openLogin, $openLoginEmail } = useNuxtApp() const app = useNuxtApp()
const token = useToken();
const userStore = useUserStore() const userStore = useUserStore()
const tokenCookie = useCookie<string | undefined>('token');
const props = defineProps({ const props = defineProps({
visible: { visible: {
@ -157,7 +160,7 @@
const handleRegister = () => { const handleRegister = () => {
props.onClose() props.onClose()
nextTick(() => { nextTick(() => {
// $openRegister() app.$openRegister()
}) })
} }
// 发送验证码 // 发送验证码
@ -208,20 +211,25 @@
loading.value = true loading.value = true
try { try {
const res = await login(loginForm) const res = await login(loginForm)
const { code, data, msg } = res const { code, data} = res
if (code !== 0) return ElMessage.error(msg) if (code === 0) {
REFRESHTOKEN.setToken(data.accessToken, data.refreshToken) // 设置cookie
REFRESHTOKEN.setUserId(data.userId.toString()) tokenCookie.value = data.accessToken;
REFRESHTOKEN.setUserName(loginForm.mobile) // 更新state
userStore.setToken(data.accessToken) token.value = data.accessToken;
userStore.setUserId(data.userId.toString()) REFRESHTOKEN.setToken(data.accessToken, data.refreshToken)
userStore.setUserName(loginForm.mobile) REFRESHTOKEN.setUserId(data.userId.toString())
userStore.setRefreshToken(data.refreshToken) REFRESHTOKEN.setUserName(loginForm.mobile)
ElMessage.success('登录成功') userStore.setToken(data.accessToken)
props.onClose() userStore.setUserId(data.userId.toString())
// 获取用户信息 userStore.setUserName(loginForm.mobile)
userStore.getUserInfo() userStore.setRefreshToken(data.refreshToken)
// 登录成功 ElMessage.success('登录成功')
props.onClose()
// 获取用户信息
userStore.getUserInfo()
// 登录成功
}
} finally { } finally {
loading.value = false loading.value = false
} }
@ -262,7 +270,7 @@
const handleLoginEmail = () => { const handleLoginEmail = () => {
props.onClose() props.onClose()
nextTick(() => { nextTick(() => {
// $openLoginEmail() app.$openLoginEmail()
}) })
} }
</script> </script>

View File

@ -2,27 +2,27 @@
<div class="box-border"> <div class="box-border">
<div class="flex items-center"> <div class="flex items-center">
<div class="flex items-center"> <div class="flex items-center">
<div class="box-border h-100% h-55px w-221px flex items-center rd-2px bg-[#1A65FF] pl-24px text-white"> <div class="box-border h-[100%] h-[55px] w-[221px] flex items-center rd-[2px] bg-[#1A65FF] pl-[24px] text-white">
<img src="~/assets/images/1.png" alt="" srcset="" /> <img src="~/assets/images/1.png" alt="" srcset="" />
<span class="ml-12px text-16px">全部资源分类</span> <span class="ml-[12px] text-[16px]">全部资源分类</span>
</div> </div>
<div class="item-center ml-45px w-660px flex justify-between"> <div class="item-center ml-[45px] w-[660px] flex justify-between">
<nuxt-link <nuxt-link
v-for="(item, index) in menuItems" v-for="(item, index) in menuItems"
:key="index" :key="index"
:to="item.path" :to="item.path"
class="parent-links relative rounded-lg px3 py2 text-[#1A65FF]" class="parent-links relative rounded-lg px-3 py-2 text-[#1A65FF]"
> >
{{ item.name }} {{ item.name }}
<img v-if="item.path === '/communication/channel'" src="~/assets/images/hot.png" alt="火" class="absolute right--15px top--2px" /> <img v-if="item.path === '/communication/channel'" src="~/assets/images/hot.png" alt="火" class="absolute right-[-15px] top-[-2px]" />
</nuxt-link> </nuxt-link>
</div> </div>
</div> </div>
<div v-if="isLogin" class="flex flex-1 items-center justify-end"> <div v-if="isLogin" class="flex flex-1 items-center justify-end">
<div class="h-36px w-36px cursor-pointer border-rd-[50%] bg-[#F5F5F5] text-center line-height-44px" @click="handleUserCenter"> <div class="h-[36px] w-[36px] cursor-pointer border-rd-[50%] bg-[#F5F5F5] text-center flex items-center justify-center" @click="handleUserCenter">
<img src="~/assets/images/user.png" alt="" srcset="" class="h-19px w-17px" /> <img src="~/assets/images/user.png" alt="" srcset="" class="h-[19px] w-[17px]" />
</div> </div>
<div class="ml-8px h-36px w-36px cursor-pointer border-rd-[50%] text-center line-height-44px" @click="handleMessageCenter"> <div class="ml-[8px] h-[36px] w-[36px] cursor-pointer border-rd-[50%] text-center line-height-[44px]" @click="handleMessageCenter">
<el-icon size="20px" color="#999999"><BellFilled /></el-icon> <el-icon size="20px" color="#999999"><BellFilled /></el-icon>
</div> </div>
</div> </div>
@ -30,18 +30,18 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
import { BellFilled } from '@element-plus/icons-vue' import { BellFilled } from '@element-plus/icons-vue'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
const menuItems = ref([ const menuItems = ref([
{ name: '首页', path: '/index' }, { name: '首页', path: '/' },
{ name: '图纸', path: '/drawe' }, { name: '图纸', path: '/drawe' },
{ name: '文本', path: '/text' }, { name: '文本', path: '/text' },
{ name: '模型', path: '/model' }, { name: '模型', path: '/model' },
{ name: '国外专区', path: '/foreign' }, { name: '国外专区', path: '/foreign' },
{ name: '工具箱', path: '/toolbox' }, { name: '工具箱', path: '/toolbox' },
{ name: '交流频道', path: '/communication/channel' }, { name: '交流频道', path: '/channel' },
// { name: '', path: '/community' }, // { name: '', path: '/community' },
]) ])
@ -52,12 +52,12 @@
// //
const handleUserCenter = () => { const handleUserCenter = () => {
navigateTo('/personal/center/info') navigateTo('/personal-Center/info')
} }
// //
const handleMessageCenter = () => { const handleMessageCenter = () => {
navigateTo('/personal/center/message') navigateTo('/personal-Center/message-center')
} }
</script> </script>
<style scoped> <style scoped>

View File

@ -1,48 +1,48 @@
<template> <template>
<div class="w-100% border-b-1px border-b-[#eee] border-b-solid"> <div class="w-[100%] border-b-[1px] border-b-[#eee] border-b-solid">
<div class="relative ma-auto flex items-center py-20px w-1500px!"> <div class="relative ma-auto flex items-center py-[20px] w-[1500px]!">
<img src="~/assets/images/logo5.png" alt="图夕夕" srcset="" class="h-51px w-182px cursor-pointer" @click="router.push('/index')" /> <img src="~/assets/images/logo5.png" alt="图夕夕" srcset="" class="h-[51px] w-[182px] cursor-pointer" @click="navigateTo('/')" />
<div class="ml-60px flex items-center"> <div class="ml-[60px] flex items-center">
<span v-for="item in navList" :key="item" class="nav" :class="props.active === item ? 'active' : ''" @click="handleClick(item)">{{ item }}</span> <span v-for="item in navList" :key="item" class="nav" :class="props.active === item ? 'active' : ''" @click="handleClick(item)">{{ item }}</span>
</div> </div>
<div class="relative ml-30px"> <div class="relative ml-[30px]">
<el-input <el-input
v-model="searchQuery" v-model="searchQuery"
placeholder="电子产品" placeholder="电子产品"
:prefix-icon="Search" :prefix-icon="Search"
class="search-input h-40px w-328px" class="search-input h-[40px] w-[328px]"
@focus="handleHot(), (showHotList = true)" @focus="(handleHot(), (showHotList = true))"
@input="handleInput" @input="handleInput"
></el-input> ></el-input>
<!-- 搜索框 获取到焦点 显示热门列表 --> <!-- 搜索框 获取到焦点 显示热门列表 -->
<div <div
v-if="showHotList" v-if="showHotList"
v-loading="loading" v-loading="loading"
class="absolute left-16px top-42px z-100 w-276px 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 left-[16px] top-[42px] z-100 w-[276px] 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 <li
v-for="(item, index) in hotItems" v-for="(item, index) in hotItems"
:key="index" :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)" @click="handleHotItem(item)"
> >
<span class="color-#333333">{{ item.projectTypeName }}</span> <span class="color-[#333333]">{{ item.projectTypeName }}</span>
<span v-if="item.count" class="color-#999999">{{ item.count }}份图纸</span> <span v-if="item.count" class="color-[#999999]">{{ item.count }}份图纸</span>
</li> </li>
</ul> </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> </div>
<div class="absolute right-10px flex items-center"> <div class="absolute right-[10px] flex items-center">
<div class="h-36px w-36px border-rd-[50%] bg-[#F5F5F5] text-center line-height-44px"> <div class="h-[36px] w-[36px] border-rd-[50%] bg-[#F5F5F5] text-center line-height-[44px]">
<img v-if="!isLogin" src="~/assets/images/user.png" alt="" srcset="" class="h-19px w-17px" /> <img v-if="!isLogin" src="~/assets/images/user.png" alt="" srcset="" class="h-[19px] w-[17px] relative top-[0px] left-[0px]" />
<img v-else :src="userStore.userInfoRes.avatar" alt="" srcset="" class="h-19px w-17px rd-50%" /> <img v-else :src="userStore.userInfoRes.avatar" alt="" srcset="" class="h-[19px] w-[17px] rd-[50%]" />
</div> </div>
<span v-if="!isLogin" class="ml-14px cursor-pointer text-14px text-[#1A65FF] font-normal" @click="handleLogin">立即登录</span> <span v-if="!isLogin" class="ml-[14px] cursor-pointer text-[14px] text-[#1A65FF] font-normal" @click="handleLogin">立即登录</span>
<el-dropdown v-else placement="top-start" @command="handleCommand"> <el-dropdown v-else placement="top-start" @command="handleCommand">
<span class="ml-14px cursor-pointer text-14px text-[#1A65FF] font-normal">{{ userStore.userInfoRes.nickname || '立即登录' }}</span> <span class="ml-[14px] cursor-pointer text-[14px] text-[#1A65FF] font-normal">{{ userStore.userInfoRes.nickname || '立即登录' }}</span>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item command="个人中心" <el-dropdown-item command="个人中心"
@ -67,10 +67,10 @@
import { top } from '~/api/home/index' import { top } from '~/api/home/index'
import type { ProjectDrawStatisticAppRespVO } from '~/api/home/type' import type { ProjectDrawStatisticAppRespVO } from '~/api/home/type'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import useUserStore from '~/store/user' import refreshToken from '~/utils/RefreshToken'
import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const { $openLogin } = useNuxtApp() const app = useNuxtApp()
const props = defineProps({ const props = defineProps({
active: { active: {
@ -131,22 +131,25 @@
} }
const handleHotItem = (item: ProjectDrawStatisticAppRespVO) => { const handleHotItem = (item: ProjectDrawStatisticAppRespVO) => {
const normal = { id: '0', name: '图纸库', isChildren: false } // const normal = { id: '0', name: '图纸库', isChildren: false }
const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || [] // const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || []
level.unshift(normal) // level.unshift(normal)
if (item.type === 1) { if (item.type === 1) {
navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`, '_blank') navigateTo(`/drawe/${item.projectType}/1/12/-1`)
// navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
} else if (item.type === 2) { } else if (item.type === 2) {
navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`, '_blank') navigateTo(`/text/${item.projectType}/1/12/-1`)
// navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
} else if (item.type === 3) { } else if (item.type === 3) {
navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`, '_blank') navigateTo(`/model/${item.projectType}/1/12/-1`)
// navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`)
} }
} }
const handleClick = (item: string) => { const handleClick = (item: string) => {
switch (item) { switch (item) {
case '首页': case '首页':
navigateTo('/index') // 修改为在新窗口打开 navigateTo({ path: '/' }) // 修改为在新窗口打开
break break
case '图纸': case '图纸':
navigateTo('/drawe') // 修改为在新窗口打开 navigateTo('/drawe') // 修改为在新窗口打开
@ -164,7 +167,7 @@
navigateTo('/community') // 修改为在新窗口打开 navigateTo('/community') // 修改为在新窗口打开
break break
case '交流频道': case '交流频道':
navigateTo('/communication/channel') // 修改为在新窗口打开 navigateTo('/channel') // 修改为在新窗口打开
break break
case '工具箱': case '工具箱':
navigateTo('/toolbox') // 修改为在新窗口打开 navigateTo('/toolbox') // 修改为在新窗口打开
@ -174,14 +177,15 @@
} }
} }
const handleLogin = () => { const handleLogin = () => {
$openLogin() app?.$openLogin() // 调用全局方法
} }
const handleCommand = (command: string) => { const handleCommand = (command: string) => {
if (command === '退出') { if (command === '退出') {
clearNuxtState(['token', 'userInfo'])
userStore.logout() userStore.logout()
userStore.$reset() userStore.$reset()
} else if (command === '个人中心') { } else if (command === '个人中心') {
navigateTo('/personal/center/info') navigateTo('/personal-Center/info')
} }
} }

View File

@ -28,10 +28,10 @@
</div> </div>
<div class="message-content"> <div class="message-content">
<div v-if="msg.msgType === 0" class="message-bubble whitespace-pre-wrap">{{ msg.content }}</div> <div v-if="msg.msgType === 0" class="message-bubble whitespace-pre-wrap">{{ msg.content }}</div>
<div v-else-if="msg.msgType === 1" class="message-bubble max-w-50%"> <div v-else-if="msg.msgType === 1" class="message-bubble max-w-[50%]">
<img :src="msg.content" alt="图片" class="w-100%" /> <img :src="msg.content" alt="图片" class="w-[100%]" />
</div> </div>
<div v-else class="message-bubble max-w-50%"> <div v-else class="message-bubble max-w-[50%]">
{{ msg.content.split('/').pop() }} {{ msg.content.split('/').pop() }}
</div> </div>
<div class="message-time">{{ dayjs(msg.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div> <div class="message-time">{{ dayjs(msg.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
@ -91,7 +91,7 @@
import { upload } from '~/api/common' import { upload } from '~/api/common'
import { sendKefuMessage, getMessagePage } from '~/api/channel/index' import { sendKefuMessage, getMessagePage } from '~/api/channel/index'
import type { msgType, PageResultMessageRespVO } from '~/api/channel/types' import type { msgType, PageResultMessageRespVO } from '~/api/channel/types'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -299,6 +299,9 @@
inputMessage.value = imageUrl inputMessage.value = imageUrl
handleSend(msgType) handleSend(msgType)
} }
img.onerror = () => {
ElMessage.error('图片加载失败')
}
} else { } else {
inputMessage.value = imageUrl inputMessage.value = imageUrl
handleSend(msgType) handleSend(msgType)

View File

@ -1,11 +1,11 @@
<template> <template>
<el-dialog v-model="visible" width="800px" class="vip-dialog" align-center> <el-dialog v-model="visible" width="800px" class="vip-dialog" align-center @close="handleClose">
<template #header> <template #header>
<div class="vip-modal-title">VIP套餐</div> <div class="vip-modal-title">VIP套餐</div>
</template> </template>
<div v-loading="loading" class="vip-cards"> <div v-loading="loading" class="vip-cards">
<div v-for="item in viplist" :key="item.id" class="vip-card"> <div v-for="item in viplist" :key="item.id" class="vip-card">
<div class="relative w-100% flex flex-col items-center"> <div class="relative! w-[100%] flex flex-col items-center">
<div class="vip-card-header basic"> <div class="vip-card-header basic">
<div class="vip-card-title">{{ item.name }}</div> <div class="vip-card-title">{{ item.name }}</div>
<!-- <div class="vip-card-subtitle">中小微企业</div> --> <!-- <div class="vip-card-subtitle">中小微企业</div> -->
@ -22,7 +22,7 @@
> >
</ul> </ul>
<div v-if="item.qrCodeUrl" class="vip-card-qrcode"> <div v-if="item.qrCodeUrl" class="vip-card-qrcode">
<el-icon class="absolute right-0px top-0px cursor-pointer" @click="item.qrCodeUrl = ''"><Close /></el-icon> <el-icon class="absolute! right-[0px] top-[0px] cursor-pointer" @click="item.qrCodeUrl = ''"><Close /></el-icon>
<qrcode-vue :value="item.qrCodeUrl" :size="150" level="H" /> <qrcode-vue :value="item.qrCodeUrl" :size="150" level="H" />
<div>请使用微信扫二维码</div> <div>请使用微信扫二维码</div>
</div> </div>
@ -41,7 +41,7 @@
import type { AppPayWalletPackageRespVO } from '~/api/pay/types' import type { AppPayWalletPackageRespVO } from '~/api/pay/types'
// @ts-ignore // @ts-ignore
import QrcodeVue from 'qrcode.vue' import QrcodeVue from 'qrcode.vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@ -133,6 +133,14 @@
clearInterval(interval.value) clearInterval(interval.value)
interval.value = undefined interval.value = undefined
} }
/** 关闭弹窗 */
const handleClose = () => {
visible.value = false
// 清空任务
clearInterval(interval.value)
interval.value = undefined
}
</script> </script>
<style scoped> <style scoped>
@ -153,7 +161,7 @@
border-radius: 12px; border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06); box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
padding: 24px 32px; padding: 24px 32px;
width: 260px; width: 290px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -231,5 +239,15 @@
z-index: 1; z-index: 1;
text-align: center; text-align: center;
background-color: #fff; background-color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
:deep(.el-dialog__header) {
.el-dialog__close {
top: -10px !important;
}
} }
</style> </style>

View File

@ -1,26 +1,26 @@
<template> <template>
<div class="fixed-button-group"> <div class="fixed-button-group">
<div class="button-item" @click="handleVip"> <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-icon class="icon-item !color-[#10c55b]"><Trophy /></el-icon>
</el-badge> </el-badge>
<span class="button-text">VIP</span> <span class="button-text">VIP</span>
</div> </div>
<div class="button-item" @click="handleService"> <div class="button-item" @click="handleService">
<el-badge :is-dot="readCount" class="item"> <el-badge :is-dot="readCount" class="item">
<el-icon class="icon-item color-#10c55b!"><Service /></el-icon> <el-icon class="icon-item !color-[#10c55b]"><Service /></el-icon>
</el-badge> </el-badge>
<span class="button-text">客服</span> <span class="button-text">客服</span>
</div> </div>
<div class="button-item" @click="handleSign"> <div class="button-item" @click="handleSign">
<el-icon class="icon-item color-#10c55b!"><Checked /></el-icon> <el-icon class="icon-item !color-[#10c55b]"><Checked /></el-icon>
<span class="button-text">签到</span> <span class="button-text">签到</span>
</div> </div>
<div class="button-item" @click="handlePublish"> <div class="button-item" @click="handlePublish">
<el-icon class="icon-item color-#C561F9!"><Promotion /></el-icon> <el-icon class="icon-item !color-[#C561F9]"><Promotion /></el-icon>
<span class="button-text">发布</span> <span class="button-text">发布</span>
</div> </div>
<div class="button-item mt-10px" @click="scrollToTop"> <div class="button-item mt-[10px]" @click="scrollToTop">
<el-icon class="icon-item"><Top /></el-icon> <el-icon class="icon-item"><Top /></el-icon>
<span class="button-text">顶部</span> <span class="button-text">顶部</span>
</div> </div>
@ -33,9 +33,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue' import { ref, onMounted } from 'vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
import { Service, Top, Promotion, Checked, Trophy } from '@element-plus/icons-vue' import { Service, Top, Promotion, Checked, Trophy } from '@element-plus/icons-vue'
import KlService from './components/kl-service.vue' import KlService from './components/kl-service.vue'
import KlVip from './components/kl-vip.vue'
const showVip = ref(false) const showVip = ref(false)
const handleVip = () => { const handleVip = () => {
@ -63,7 +64,7 @@
return return
} }
// 新开窗口 用router跳转 新窗口打开 // 新开窗口 用router跳转 新窗口打开
navigateTo('/upnew/drawe') navigateTo('/upnew')
} }
const dialogVisible = ref(false) const dialogVisible = ref(false)
@ -87,7 +88,7 @@
ElMessage.error('请先登录') ElMessage.error('请先登录')
return return
} }
navigateTo('/sign-page') navigateTo('/sign-content')
} }
const readCount = ref(false) const readCount = ref(false)

View File

@ -2,7 +2,7 @@
<div v-if="visible" class="popup-overlay"> <div v-if="visible" class="popup-overlay">
<div class="popup-content"> <div class="popup-content">
<div class="register-container relative"> <div class="register-container relative">
<el-icon class="absolute right-0 top-0 cursor-pointer" @click="onClose()"><Close /></el-icon> <el-icon class="absolute! right-0 top-0 cursor-pointer" @click="onClose()"><Close /></el-icon>
<!-- 左侧插图 --> <!-- 左侧插图 -->
<div class="register-left"> <div class="register-left">
<img src="~/assets/images/login-illustration.png" alt="register" class="register-img" /> <img src="~/assets/images/login-illustration.png" alt="register" class="register-img" />
@ -58,7 +58,7 @@
const { $openLogin } = useNuxtApp() const { $openLogin } = useNuxtApp()
import REFRESHTOKEN from '~/utils/RefreshToken' import REFRESHTOKEN from '~/utils/RefreshToken'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const props = defineProps({ const props = defineProps({

View File

@ -1,20 +1,20 @@
<template> <template>
<div> <div>
<header class="h-106px"> <header class="h-[106px]">
<div class="mx-a ml--250px h-full flex items-center justify-center"> <div class="mx-a ml-[-250px] h-full flex items-center justify-center">
<!-- Logo区域 --> <!-- Logo区域 -->
<div class="h-100% flex cursor-pointer items-center" @click="navigateTo('/')"> <div class="h-[100%] flex cursor-pointer items-center" @click="navigateTo('/')">
<img src="~/assets/images/logo5.png" alt="图夕夕" class="h-51px w-182px" /> <img src="~/assets/images/logo5.png" alt="图夕夕" class="h-[51px] w-[182px]" />
</div> </div>
<!-- 搜索区域 --> <!-- 搜索区域 -->
<div class="relative ml-49px w-647px px4 p-r-0px!"> <div class="relative ml-[49px] w-[647px] px-4 p-r-[0px]!">
<div class="search-input relative w-100%"> <div class="search-input relative w-[100%]">
<el-input <el-input
v-model="searchQuery" v-model="searchQuery"
type="text" type="text"
placeholder="搜一搜" placeholder="搜一搜"
:prefix-icon="Search" :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)" @focus="handleHot(), (showHotList = true)"
@input="handleInput" @input="handleInput"
/> />
@ -23,32 +23,32 @@
<div <div
v-if="showHotList" v-if="showHotList"
v-loading="loading" 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 <li
v-for="(item, index) in hotItems" v-for="(item, index) in hotItems"
:key="index" :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)" @click="handleHotItem(item)"
> >
<span class="color-#333333">{{ item.projectTypeName }}</span> <span class="color-[#333333]">{{ item.projectTypeName }}</span>
<span v-if="item.count" class="color-#999999">{{ item.count }}份图纸</span> <span v-if="item.count" class="color-[#999999]">{{ item.count }}份图纸</span>
</li> </li>
</ul> </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> </div>
<!-- 按钮区域 --> <!-- 按钮区域 -->
<div class="flex items-center"> <div class="flex items-center">
<button <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>
<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" @click="handleUpload"
> >
上传图纸 上传图纸
@ -61,7 +61,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, ref } from 'vue' import { onMounted, ref } from 'vue'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
import { top } from '~/api/home/index' import { top } from '~/api/home/index'
@ -76,7 +76,7 @@
// //
if (!userStore.token) return ElMessage.error('请先登录') if (!userStore.token) return ElMessage.error('请先登录')
// router // router
navigateTo('/upnew/drawe') navigateTo('/upnew')
} }
const loading = ref(false) const loading = ref(false)
@ -116,16 +116,17 @@
} }
const handleHotItem = (item: ProjectDrawStatisticAppRespVO) => { const handleHotItem = (item: ProjectDrawStatisticAppRespVO) => {
const normal = { id: '0', name: '图纸库', isChildren: false } // const normal = { id: '0', name: '', isChildren: false }
const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || [] // const level = item.pairs?.filter(Boolean).map((item) => ({ id: item?.id, name: item?.name, isChildren: false })) || []
level.unshift(normal) // level.unshift(normal)
if (item.type === 1) { // if (item.type === 1) {
navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,) // navigateTo(`/drawe?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
} else if (item.type === 2) { // } else if (item.type === 2) {
navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,) // navigateTo(`/text?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
} else if (item.type === 3) { // } else if (item.type === 3) {
navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,) // navigateTo(`/model?level=${JSON.stringify(level)}&keywords=${item.title || ''}`,)
} // }
navigateTo(`/drawe/${item.projectType}/1/12/-1`)
} }
onMounted(() => { onMounted(() => {

View File

@ -1,36 +1,37 @@
<template> <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 :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 item.name
}}</el-breadcrumb-item> }}</el-breadcrumb-item>
</el-breadcrumb> </el-breadcrumb>
</div> </div>
<div class="mt-30px box-border w-100% border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] px-20px py-26px"> <div class="mt-[30px] box-border w-[100%] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[20px] py-[26px]">
<div class="mb-14px flex items-start"> <div class="mb-[14px] flex items-start">
<div class="flex-shrink-0 text-15px text-[#333333] font-normal">{{ computType }}分类</div> <div class="flex-shrink-0 text-[15px] text-[#333333] font-normal">{{ computType }}分类</div>
<div class="ml-30px mt--6px flex flex-wrap"> <div class="ml-[30px] mt-[-6px] flex flex-wrap">
<div <div
v-for="(item, index) in projectTypeList" v-for="(item, index) in projectTypeList"
:key="index" :key="index"
class="mb-8px mr-26px cursor-pointer rounded-15px px-15px py-6px text-14px text-[#666666] font-normal" class="mb-[8px] mr-[26px] cursor-pointer rounded-[15px] px-[15px] py-[6px] text-[14px] text-[#666666] font-normal"
:class="item.id === query.projectType ? 'bg-#EBEEFE! !text-[#1A65FF]' : ''" :class="item.id === query.projectType ? '!bg-[#ebeefe] !text-[#1A65FF]' : ''"
@click="handleClick(item)" @click="handleClick(item)"
>{{ item.name }}</div >{{ item.name }}</div
> >
</div> </div>
</div> </div>
<div class="flex items-start"> <div class="flex items-start">
<div class="flex-shrink-0 text-15px text-[#333333] font-normal">软件分类</div> <div class="flex-shrink-0 text-[15px] text-[#333333] font-normal">软件分类</div>
<div class="ml-30px mt--6px flex flex-wrap"> <div class="ml-[30px] mt-[-6px] flex flex-wrap">
<div <div
v-for="(item, index) in editionsList" v-for="(item, index) in editionsList"
:key="index" :key="index"
class="mb-8px mr-26px cursor-pointer rounded-15px px-15px py-6px text-14px text-[#666666] font-normal" class="mb-[8px] mr-[26px] cursor-pointer rounded-[15px] px-[15px] py-[6px] text-[14px] text-[#666666] font-normal"
:class="item.id === query.editions ? '!bg-[#EBEEFE] !text-[#1A65FF]' : ''" :class="item.id === query.editions ? '!bg-[#ebeefe] !text-[#1A65FF]' : ''"
@click="query.editions = item.id" @click="query.editions = item.id"
>{{ item.name }}</div >
{{ item.name }}</div
> >
</div> </div>
</div> </div>
@ -53,6 +54,7 @@
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { parent } from '~/api/upnew/index' import { parent } from '~/api/upnew/index'
import type { pageReq } from '~/api/upnew/types' import type { pageReq } from '~/api/upnew/types'
import { getDictTree } from '~/api/home/index'
import { ArrowRight } from '@element-plus/icons-vue' import { ArrowRight } from '@element-plus/icons-vue'
const props = defineProps({ const props = defineProps({
@ -73,73 +75,116 @@
const query = defineModel<pageReq>('modelValue', { const query = defineModel<pageReq>('modelValue', {
required: true, required: true,
}) })
const level = defineModel<{ id: string; name: string; isChildren?: boolean }[]>('level', { // const level = defineModel<{ id: string; name: string; isChildren?: boolean }[]>('level', {
required: true, // required: true,
}) // })
const computType = computed(() => { const computType = computed(() => {
return props.type === 1 ? '图纸' : props.type === 3 ? '模型' : '文本' return props.type === 1 ? '图纸' : props.type === 3 ? '模型' : '文本'
}) })
const handleParentId = (type?: string) => { // 获取面包屑
if (level.value.length > 1) { const { data: breadList } = await useAsyncData(
if (type === 'init' && level.value.find((c: any) => c.isChildren)) { `breadList-${props.type}-${props.id}-${query.value.projectType}-${Date.now()}`,
return level.value[level.value.length - 2].id || '' // 获取最后一个元素的 id 或 defaul async () => {
} const res = await getDictTree({ type: 1, id: query.value.projectType })
return level.value[level.value.length - 1].id || '' // 获取最后一个元素的 id 或 defaul 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' )
}
const projectTypeList = ref<any>([]) console.log('breadList', breadList);
// const projectTypeList = ref<any>([])
/** 获取分类下拉框 */ /** 获取分类下拉框 */
const getParent = (type?: string) => { // const getParent = (type?: string) => {
parent({ // parent({
type: 1, // type: 1,
// @ts-ignore // parentId: handleParentId(type),
parentId: handleParentId(type), // }).then((res) => {
}).then((res) => { // if (Array.isArray(res.data)) {
if (Array.isArray(res.data)) { // // projectTypeList.value = [...[{ id: handleParentId(type), name: '全部' }], ...res.data]
projectTypeList.value = [...[{ id: handleParentId(type), name: '全部' }], ...res.data] // }
} // })
}) // }
} // getParent('init')
getParent('init')
/** 版本 */ /** 版本 */
const editionsList = ref<any>([]) // const editionsList = ref<any>([])
const getEditionsList = () => { // const getEditionsList = () => {
parent({ // parent({
type: 2, // type: 2,
parentId: 0, // parentId: 0,
}).then((res) => { // }).then((res) => {
if (Array.isArray(res.data)) { // if (Array.isArray(res.data)) {
editionsList.value = [...[{ id: '', name: '全部' }], ...res.data] // // editionsList.value = [...[{ id: '', name: '全部' }], ...res.data]
// }
// })
// }
// getEditionsList()
/**获取分类下拉框 */
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: '全部' }]
getEditionsList() return [...all, ...res.data]
},
{
immediate: true,
}
)
/** 版本 */
const { data: editionsList } = useAsyncData(`editionsList-${props.type}-${Date.now()}`, async () => {
const res = await parent({ type: 2, parentId: 0 })
const all = [{ id: '-1', name: '全部' }]
return [...all, ...res.data]
})
const handleClick = (row: any) => { const handleClick = (row: any) => {
query.value.title = '' query.value.title = ''
query.value.projectType = row.id query.value.projectType = row.id
if (row.name === '全部') return // if (row.name === '全部') return
const isChildren = level.value.find((c: any) => c.isChildren) // const isChildren = breadList.value.find((c: any) => c.isChildren)
if (!row.isChildren && isChildren) { // if (!row.isChildren && isChildren) {
const index = level.value.length - 1 // const index = breadList.value.length - 1
level.value[index] = { id: row.id, name: row.name, isChildren: true } // breadList.value[index] = { id: row.id, name: row.name, isChildren: true }
} else if (!row.isChildren && !isChildren) { // } else if (!row.isChildren && !isChildren) {
level.value.push({ id: row.id, name: row.name, isChildren: true }) // breadList.value.push({ id: row.id, name: row.name, isChildren: true })
} else { // } else {
level.value.push({ id: row.id, name: row.name }) // breadList.value.push({ id: row.id, name: row.name })
getParent() // // getParent()
} // refresh()
// }
} }
const handleClickBread = (row: any, index: number) => { const handleClickBread = (row: any, index: number) => {
level.value.splice(index + 1) // breadList.value.splice(index + 1)
query.value.title = '' query.value.title = ''
query.value.projectType = row.id query.value.projectType = row.id
getParent() // getParent()
// refresh()
} }
</script> </script>

View File

@ -1,17 +1,17 @@
<template> <template>
<div class="relative mt-34px w-100%"> <div class="relative mt-[34px] w-[100%]">
<KlTabBar v-model="query.source" :data="tabBar" /> <KlTabBar v-model="query.source" :data="tabBar" />
<div class="absolute right-0px top-10px text-16px text-[#999999] font-normal" <div class="absolute right-[0px] top-[10px] text-[16px] text-[#999999] font-normal"
><span class="color-#1A65FF">{{ result.total }}</span ><span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div >个筛选结果</div
> >
<div class="content mt-10px"> <div class="content mt-[10px]">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col v-for="(item, index) in result.list" :key="index" :span="6"> <el-col v-for="(item, index) in result?.list" :key="index" :span="6">
<CardPicture :item-info="item" /> <CardPicture :item-info="item" />
</el-col> </el-col>
</el-row> </el-row>
<el-empty v-if="!result.list.length" :image="emptyImg"></el-empty> <el-empty v-if="!result?.list.length" :image="emptyImg"></el-empty>
</div> </div>
</div> </div>
</template> </template>
@ -26,14 +26,14 @@
const query = defineModel<pageReq>('modelValue', { const query = defineModel<pageReq>('modelValue', {
required: true, required: true,
}) })
const result = defineModel<pageRes>('result', { const result = defineModel<pageRes| null>('result', {
required: true, required: true,
}) })
const tabBar = ref([ const tabBar = ref([
{ {
label: '模型推荐', label: '模型推荐',
value: '', value: -1,
}, },
{ {
label: '原创模型', label: '原创模型',

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
const props = defineProps({
title: {
type: String,
required: true,
default: '图夕夕-世界图纸 夕夕共享',
},
description: {
type: String,
default: '图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。',
},
keywords: {
type: String,
default: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸',
},
})
useHead({
title: `${props.title} - 图夕夕`,
meta: [
{ name: 'description', content: `${props.description}`},
{ name: 'keywords', content: props.keywords },
],
})
</script>

View File

@ -1,17 +1,17 @@
<template> <template>
<div class="relative mt-34px w-100%"> <div class="relative mt-[34px] w-[100%]">
<KlTabBar v-model="query.source" :data="tabBar" /> <KlTabBar v-model="query.source" :data="tabBar" />
<div class="absolute right-0px top-10px text-16px text-[#999999] font-normal" <div class="absolute right-[0px] top-[10px] text-[16px] text-[#999999] font-normal"
><span class="color-#1A65FF">{{ result.total }}</span ><span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div >个筛选结果</div
> >
<div class="content mt-10px"> <div class="content mt-[10px]">
<el-row :gutter="20"> <el-row :gutter="20">
<el-col v-for="(item, index) in result.list" :key="index" :span="6"> <el-col v-for="(item, index) in result?.list" :key="index" :span="6">
<CardPicture :item-info="item" /> <CardPicture :item-info="item" />
</el-col> </el-col>
</el-row> </el-row>
<el-empty v-if="!result.list.length" :image="emptyImg"></el-empty> <el-empty v-if="!result?.list?.length" :image="emptyImg"></el-empty>
</div> </div>
</div> </div>
</template> </template>
@ -27,14 +27,14 @@
required: true, required: true,
}) })
const result = defineModel<pageRes>('result', { const result = defineModel<pageRes | null>('result', {
required: true, required: true,
}) })
const tabBar = ref([ const tabBar = ref([
{ {
label: '文本推荐', label: '文本推荐',
value: '', value: -1
}, },
{ {
label: '原创文本', label: '原创文本',

34
composables/states.ts Normal file
View File

@ -0,0 +1,34 @@
/** 用户信息 **/
export const useToken = () =>
useState<string>('token', () => {
const token = useCookie<string | undefined>('token');
return token.value ? token.value : '';
});
type UserInfo = {
nickname: string,
avatar: string,
mobile: string,
id: number | undefined,
vipLevel: number | undefined,
sex: number | undefined,
}
/** 用户信息 */
export const useUserInfo = () => useState<UserInfo>('userInfo', () => {
return {
nickname: '',
avatar: '',
mobile: '',
id: undefined,
vipLevel: undefined,
sex: undefined,
}
});
/** 热门数据 */
export const useHotMeg = () => useState<any>('hotMsg', () => {
return {
projectType:'',
projectTypeTop: ''
}
});

View File

@ -1,4 +1,5 @@
import { isArray } from "~/utils/utils"; import { isArray } from "~/utils/utils";
// import refreshToken from "~/utils/RefreshToken";
type FetchType = typeof $fetch; type FetchType = typeof $fetch;
export type FetchOptions = Parameters<FetchType>[1]; export type FetchOptions = Parameters<FetchType>[1];
@ -15,12 +16,11 @@ const useClientRequest = async <T = unknown>(
onRequest({ options }) { onRequest({ options }) {
options.headers = options.headers || 'application/json'; options.headers = options.headers || 'application/json';
if (token.value) { if (token.value) {
// @ts-ignore options.headers.set("Authorization", `Bearer ${token.value}`);
options.headers["authorization"] = "Bearer " + token.value;
} }
}, },
onResponse({ response }) { onResponse({ response }) {
if (+response.status === 200 && +response._data.code !== 200) { if (+response.status === 200 && +response._data.code !== 0) {
ElMessage.error(response._data.msg); ElMessage.error(response._data.msg);
} }
}, },
@ -71,5 +71,7 @@ const useClientRequest = async <T = unknown>(
body?: any, body?: any,
config?: Omit<FetchOptions, 'method' | 'body'> config?: Omit<FetchOptions, 'method' | 'body'>
): Promise<T> => { ): Promise<T> => {
console.log({ ...config, method: 'PUT', body });
return useClientRequest<T>(endpoint, { ...config, method: 'PUT', body }) return useClientRequest<T>(endpoint, { ...config, method: 'PUT', body })
} }

View File

@ -1,74 +1,51 @@
import { useFetch } from "#app"; import { useFetch } from '#app'
import type { UseFetchOptions } from "#app"; import type { UseFetchOptions } from '#app'
import { isArray } from "~/utils/utils"; import { isArray } from '~/utils/utils'
const useServerRequest = async <T>( const useServerRequest = async <T>(url: string, opts?: UseFetchOptions<T, unknown>) => {
url: string, const token = useToken()
opts?: UseFetchOptions<T, unknown> const runtimeConfig = useRuntimeConfig()
) => {
const token = useCookie<string | undefined>("token");
const runtimeConfig = useRuntimeConfig();
const defaultOptions: UseFetchOptions<unknown> = { const defaultOptions: UseFetchOptions<unknown> = {
baseURL: runtimeConfig.public.apiBase, baseURL: runtimeConfig.public.apiBase,
onRequest({ options }) { onRequest({ options }) {
options.headers = options.headers || "application/json"; options.headers = options.headers || {}
if (token.value) { if (token.value) {
// @ts-ignore options.headers.set('Authorization', `Bearer ${token.value}`)
options.headers["authorization"] = "Bearer " + token.value;
} }
}, },
onResponse({ response }) { onResponse({ response }) {
if (+response.status === 200 && +response._data.code !== 200) { if (+response.status === 200 && +response._data.code !== 0) {
process.client && ElMessage.error(response._data.msg); process.client && ElMessage.error(response._data.msg)
} }
}, },
onResponseError({ response }) { onResponseError({ response }) {
process.client && process.client && ElMessage.error(isArray(response._data.data.msg) ? response._data.data.msg[0] : response._data.data.msg)
ElMessage.error(
isArray(response._data.data.msg)
? response._data.data.msg[0]
: response._data.data.msg
);
}, },
}; }
// return useFetch<T>(url, { ...defaultOptions, ...opts } as any); // return useFetch<T>(url, { ...defaultOptions, ...opts } as any);
// 明确转换返回类型 // 明确转换返回类型
const response = await useFetch<T>(url, { ...defaultOptions, ...opts, key: 'unique-key-' + Date.now(), } as any); const response = await useFetch<T>(url, { ...defaultOptions, ...opts } as any)
return response.data.value as unknown as T; return response.data.value as unknown as T
}; }
// GET请求 // GET请求
export const get = <T = unknown>( export const get = <T = unknown>(endpoint: string, config?: Omit<FetchOptions, 'method'>): Promise<T> => {
endpoint: string, return useServerRequest<T>(endpoint, { ...config, method: 'GET' })
config?: Omit<FetchOptions, 'method'> }
): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'GET' })
}
// POST请求 // POST请求
export const post = <T = unknown>( export const post = <T = unknown>(endpoint: string, body?: any, config?: Omit<FetchOptions, 'method' | 'body'>): Promise<T> => {
endpoint: string, return useServerRequest<T>(endpoint, { ...config, method: 'POST', body })
body?: any, }
config?: Omit<FetchOptions, 'method' | 'body'>
): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'POST', body })
}
// DELETE请求
export const del = <T = unknown>(endpoint: string, config?: Omit<FetchOptions, 'method'>): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'DELETE' })
}
// DELETE请求 // PUT请求
export const del = <T = unknown>( export const put = <T = unknown>(endpoint: string, body?: any, config?: Omit<FetchOptions, 'method' | 'body'>): Promise<T> => {
endpoint: string, return useServerRequest<T>(endpoint, { ...config, method: 'PUT', body })
config?: Omit<FetchOptions, 'method'> }
): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'DELETE' })
}
// PUT请求
export const put = <T = unknown>(
endpoint: string,
body?: any,
config?: Omit<FetchOptions, 'method' | 'body'>
): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'PUT', body })
}

View File

@ -10,8 +10,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import KlFooter from '~/layout/kl-footer/index.vue'
import KlQuickMenu from '~/components/kl-quick-menu/index.vue' import KlQuickMenu from '~/components/kl-quick-menu/index.vue'
import KlFooter from '~/components/kl-footer/index.vue'
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

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,18 +1,26 @@
// import { base_api } from '~/constants/index' // import { base_api } from '~/constants/index'
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
import postcsspxtoviewport from 'postcss-px-to-viewport'
export default defineNuxtConfig({ export default defineNuxtConfig({
// devServer: { // devServer: {
// port: 6188, // port: 6188,
// }, // },
devtools: { devtools: {
enabled: process.env.NODE_ENV === "development", enabled: process.env.NODE_ENV === 'development',
}, },
debug: process.env.NODE_ENV === "development", // 开启详细调试日志 debug: process.env.NODE_ENV === 'development', // 开启详细调试日志
ssr: true, ssr: true,
modules: ["@unocss/nuxt", "@pinia/nuxt", "@element-plus/nuxt"], modules: ['@unocss/nuxt', '@pinia/nuxt', '@element-plus/nuxt', 'pinia-plugin-persistedstate/nuxt'],
css: ["@unocss/reset/tailwind.css", "element-plus/dist/index.css"], unocss: {
nuxtLayers: true,
},
elementPlus: {
importStyle: 'scss', // 或 'css',确保样式被全局导入
themes: ['dark'], // 按需配置主题
},
css: ['element-plus/dist/index.css', '~/assets/scss/app.scss'],
vite: { vite: {
css: { css: {
preprocessorOptions: { preprocessorOptions: {
@ -22,77 +30,91 @@ export default defineNuxtConfig({
}, },
postcss: { postcss: {
plugins: [ plugins: [
// postCssPxToRem({ postcsspxtoviewport({
// rootValue: 16, // 结果为:设计稿元素尺寸/16比如元素宽320px,最终页面会换算成 20rem unitToConvert: 'px', // 要转化的单位
// mediaQuery: false, //布尔值允许在媒体查询中转换px。 viewportWidth: 750, // UI设计稿的宽度
// // exclude: /node_modules/, //node_modules目录下样式全部不转义 unitPrecision: 6, // 转换后的精度,即小数点位数
// propList: ['*'] //需要做转化处理的属性如`hight`、`width`、`margin`等,`*`表示全部 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/],
}),
], ],
}, },
}, },
optimizeDeps: { optimizeDeps: {
include: ["naive-ui"], include: ['naive-ui'],
}, },
// 生产环境构建优化 // 生产环境构建优化
build: { build: {
// 生产环境移除 console 和 debugger // 生产环境移除 console 和 debugger
minify: "esbuild", minify: 'esbuild',
target: "es2020", target: 'es2020',
}, },
esbuild: { esbuild: {
// 生产环境下移除所有 console 语句和 debugger // 生产环境下移除所有 console 语句和 debugger
drop: drop: process.env.NODE_ENV === 'production' ? ['console', 'debugger'] : [],
process.env.NODE_ENV === "production" ? ["console", "debugger"] : [],
}, },
}, },
// 页面过渡配置 // 页面过渡配置
app: { app: {
pageTransition: { // pageTransition: {
name: "page", // name: "page",
mode: "out-in", // mode: "out-in",
duration: 400, // duration: 400,
}, // },
layoutTransition: { // layoutTransition: {
name: "layout", // name: "default",
mode: "out-in", // mode: "out-in",
duration: 400, // duration: 400,
}, // },
head: { head: {
title: "xlCig - 专业PC硬件产品和装机服务", title: '图夕夕-世界图纸 夕夕共享',
htmlAttrs: { htmlAttrs: {
lang: "en", lang: 'en',
}, },
meta: [ meta: [
{ charset: "utf-8" }, { charset: 'utf-8' },
{ name: "viewport", content: "width=device-width, initial-scale=1" }, { name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ {
name: "description", name: 'description',
content: "专业的PC硬件产品和装机建议助您打造梦想中的高性能电脑", content: '图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。',
}, },
{ name: "keywords", content: "xlCig,PC硬件,电脑配置,显卡,CPU,装机" }, { name: 'keywords', content: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸' },
{ name: "author", content: "xlCig" }, { name: 'author', content: '图夕夕' },
// 百度站点验证
{ name: "baidu-site-verification", content: "codeva-2z90c1PlRw" },
// SEO meta tags // SEO meta tags
{ property: "og:title", content: "xlCig - 专业PC硬件产品和装机服务" }, // {
{ // property: 'og:title',
property: "og:description", // content: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸',
content: "专业的PC硬件产品和装机建议助您打造梦想中的高性能电脑", // },
}, // {
{ property: "og:type", content: "website" }, // property: 'og:description',
{ property: "og:url", content: "https://www.xlcig.cn" }, // content: '图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。',
{ property: "og:site_name", content: "xlCig" }, // },
{ name: "theme-color", content: "#00f5ff" }, // { property: 'og:type', content: 'website' },
// robots meta // { property: 'og:url', content: 'https://www.xlcig.cn' },
{ name: "robots", content: "index, follow" }, // { property: 'og:site_name', content: 'xlCig' },
// { name: 'theme-color', content: '#00f5ff' },
// // robots meta
// { name: 'robots', content: 'index, follow' },
], ],
link: [ link: [
{ rel: "icon", type: "image/png", href: "/logo.png" }, { rel: 'icon', type: 'image/x-icon', href: '/favicon2.ico' },
{ rel: "apple-touch-icon", sizes: "180x180", href: "/logo.png" }, { rel: 'stylesheet', type: 'text/css', href: 'https://unpkg.com/swiper@8/swiper-bundle.css' },
{ rel: "icon", type: "image/png", sizes: "32x32", href: "/logo.png" }, ],
{ rel: "icon", type: "image/png", sizes: "16x16", href: "/logo.png" }, script: [
{
type: 'text/javascript',
src: 'https://unpkg.com/swiper@8/swiper-bundle.js',
},
], ],
}, },
}, },
@ -103,18 +125,28 @@ export default defineNuxtConfig({
apiBase: 'https://tuxixi.net', apiBase: 'https://tuxixi.net',
// 应用信息 // 应用信息
appName: "xlCig", appName: 'xlCig',
appVersion: "1.0.0", appVersion: '1.0.0',
// 调试模式 // 调试模式
debug: process.env.NODE_ENV === "development", debug: process.env.NODE_ENV === 'development',
// 环境标识 // 环境标识
environment: process.env.NODE_ENV || "development", environment: process.env.NODE_ENV || 'development',
}, },
}, },
build: { build: {
transpile: ["vueuc", "@css-render/vue3-ssr","@unocss"], transpile: ['vueuc', '@css-render/vue3-ssr', '@tinymce/tinymce-vue', 'tinymce'],
}, },
}); plugins: [
// 在这里引入插件
{
src: '~/plugins/wang-editor',
mode: 'client',
},
],
piniaPluginPersistedstate: {
storage: 'localStorage',
},
})

View File

@ -12,15 +12,16 @@
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.13.6", "@nuxtjs/axios": "^5.13.6",
"@pinia/nuxt": "^0.11.2", "@pinia/nuxt": "^0.11.2",
"@tinymce/tinymce-vue": "^6.3.0", "@wangeditor/editor": "^5.1.23",
"@types/tinymce": "^5.5.0", "@wangeditor/editor-for-vue": "^5.1.12",
"decimal.js": "^10.6.0", "decimal.js": "^10.6.0",
"echarts": "^6.0.0", "echarts": "^6.0.0",
"mqtt": "^5.14.0", "mqtt": "^5.14.0",
"nuxt": "^3.18.1", "nuxt": "^3.18.1",
"pdfjs-dist": "^5.4.54", "pdfjs-dist": "^5.4.54",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"tinymce": "^8.0.2", "postcss-px-to-viewport": "^1.1.1",
"qrcode.vue": "^3.6.0",
"vue": "^3.5.18", "vue": "^3.5.18",
"vue-pdf-embed": "^2.1.3", "vue-pdf-embed": "^2.1.3",
"vue-router": "^4.5.1", "vue-router": "^4.5.1",
@ -29,8 +30,13 @@
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610", "packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610",
"devDependencies": { "devDependencies": {
"@element-plus/nuxt": "^1.1.4", "@element-plus/nuxt": "^1.1.4",
"@pinia-plugin-persistedstate/nuxt": "^1.2.1",
"@types/prettier": "^3.0.0",
"@unocss/nuxt": "^66.4.2", "@unocss/nuxt": "^66.4.2",
"@vant/nuxt": "^1.0.7",
"element-plus": "^2.10.7", "element-plus": "^2.10.7",
"postcss": "^8.5.6",
"prettier": "3.6.2",
"sass": "^1.90.0", "sass": "^1.90.0",
"unocss": "^66.4.2" "unocss": "^66.4.2"
} }

View File

@ -0,0 +1,99 @@
<template>
<!-- 导航 -->
<KlNavTab active="交流频道" />
<div class="ma-auto mt-[30px] w-[1440px] flex">
<LeftContent v-model="pageReq.channelId" v-model:channelIdList="channelIdList"></LeftContent>
<RightContent v-model="pageRes" v-model:lun-tan-res="lunTanRes" v-model:page-no="pageReq.pageNo" @update-page-no="handleUpdatePageNo"></RightContent>
</div>
</template>
<script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue'
import LeftContent from '../components/LeftContent.vue'
import RightContent from '../components/RightContent.vue'
import { page, getChannelLunTanDetail, list } from '~/api/channel/index'
import { reactive, watch, ref } from 'vue'
import type { TpageRes, ChannelRespVO } from '~/api/channel/types'
const route = useRoute()
const channelId = computed(() => (route.params.channelId as string) || '')
const pageNo = computed(() => Number(route.params.pageNo) || 1)
const pageReq = reactive({
pageNo: pageNo.value,
pageSize: 10,
channelId: channelId.value, // 频道ID
})
// const pageRes = reactive<TpageRes>({
// list: [],
// total: 0,
// })
// 获取频道列表
const { data: channelIdList } = await useAsyncData(`prod-api/app-api/business/channel/list-${Date.now()}`, async () => {
const res = await list()
return res.data as any[]
})
console.log(channelIdList)
// 获取第一个论坛详情
// if (!channelIdList.value?.length) return
const { data: lunTanRes, execute: refreshLunTanDetail } = await useAsyncData(
`prod-api/app-api/business/channel/detail-${Date.now()}`,
async () => {
if (!channelId.value) return null // 无参数时不请求
const res = await getChannelLunTanDetail({
id: channelId.value,
})
return res.data as ChannelRespVO
},
{
immediate: true,
}
)
const { data: pageRes, refresh: getPage } = await useAsyncData(`prod-api/app-api/business/posts/page-${Date.now()}`, async () => {
const res = await page(pageReq)
return res.data as TpageRes
})
// 获得频道帖子分页
// const getPage = () => {
// page(pageReq).then((res) => {
// pageRes.list = res.data.list
// pageRes.total = res.data.total
// })
// }
// getPage()
const handleUpdatePageNo = (pageNo: number) => {
pageReq.pageNo = pageNo
navigateTo(`/channel/${channelId.value}/${pageNo}`)
// getPage()
}
// 获取论坛详情
// const lunTanRes = ref({} as ChannelRespVO)
// const getLunTanDetaiil = (val: string) => {
// getChannelLunTanDetail({
// id: val,
// }).then((res) => {
// lunTanRes.value = res.data
// })
// }
// watch(
// () => channelId.value,
// (val) => {
// if (val) {
// // 更新分页请求的channelId
// pageReq.channelId = val
// // 重新请求分页数据和论坛详情
// getPage()
// refreshLunTanDetail() // 刷新论坛详情
// }
// }
// )
</script>
<style lang="scss" scoped></style>

View File

@ -4,24 +4,24 @@
<!-- Logo and Title Section --> <!-- Logo and Title Section -->
<div class="logo-title-section"> <div class="logo-title-section">
<div class="logo"> <div class="logo">
<img :src="lunTanRes.channelIcon" alt="JRS Logo" /> <img :src="lunTanRes?.channelIcon" alt="JRS Logo" />
</div> </div>
<div class="title-section"> <div class="title-section">
<h1 class="main-title">#{{ lunTanRes.channelTitle }}</h1> <h1 class="main-title">#{{ lunTanRes?.channelTitle }}</h1>
<div class="action-buttons"> <div class="action-buttons">
<el-button v-if="!lunTanRes.isFollow" type="danger" class="subscribe-btn" @click="handleFollow" <el-button v-if="!lunTanRes?.isFollow" type="danger" class="subscribe-btn" @click="handleFollow"
><el-icon class="mr-4px color-#fff!"><Plus /></el-icon> 关注 ><el-icon class="mr-[4px] color-[#fff!]"><Plus /></el-icon> 关注
</el-button> </el-button>
<el-button v-else type="danger" class="subscribe-btn" @click="handleUnfollow"> 取消关注 </el-button> <el-button v-else type="danger" class="subscribe-btn" @click="handleUnfollow"> 取消关注 </el-button>
<el-button type="danger" class="post-btn" @click="handleClick"> <el-button type="danger" class="post-btn" @click="handleClick">
<el-icon class="mr-4px color-#fff!"><EditPen /></el-icon> 发帖 <el-icon class="mr-[4px] color-[#fff!]"><EditPen /></el-icon> 发帖
</el-button> </el-button>
</div> </div>
<!-- Channel Info --> <!-- Channel Info -->
<div class="channel-info"> <div class="channel-info">
<span class="info-item">话题介绍</span> <span class="info-item">话题介绍</span>
<span class="info-item">{{ lunTanRes.channelProfile }}</span> <span class="info-item">{{ lunTanRes?.channelProfile }}</span>
</div> </div>
<!-- Stats --> <!-- Stats -->
@ -32,20 +32,20 @@
</div> </div>
<div class="stats-item"> <div class="stats-item">
<span class="stats-label">关注人数</span> <span class="stats-label">关注人数</span>
<span class="stats-value"><i class="el-icon-arrow-up"></i> {{ lunTanRes.followCount }}</span> <span class="stats-value"><i class="el-icon-arrow-up"></i> {{ lunTanRes?.followCount }}</span>
</div> </div>
<div class="stats-item"> <div class="stats-item">
<span class="stats-label">当前有</span> <span class="stats-label">当前有</span>
<span class="stats-value"><i class="el-icon-arrow-up"></i> {{ lunTanRes.chatUserCount }}人聊天</span> <span class="stats-value"><i class="el-icon-arrow-up"></i> {{ lunTanRes?.chatUserCount }}人聊天</span>
<span class="stats-value ml-2px cursor-pointer color-#1a65ff!" @click="handleChat">立即加入</span> <span class="stats-value ml-[2px] cursor-pointer !color-[#1a65ff]" @click="handleChat">立即加入</span>
</div> </div>
</div> </div>
<!-- Tags --> <!-- Tags -->
<div class="channel-tags"> <div class="channel-tags">
<span class="tag-label">标签:</span> <span class="tag-label">标签:</span>
<span v-for="(item, index) in lunTanRes.hotTags" :key="index" class="tag-item" <span v-for="(item, index) in lunTanRes?.hotTags" :key="index" class="tag-item"
>{{ item }}{{ index === lunTanRes.hotTags.length - 1 ? '' : '、' }}</span >{{ item }}{{ index === lunTanRes!.hotTags.length - 1 ? '' : '、' }}</span
> >
</div> </div>
</div> </div>
@ -55,9 +55,9 @@
<ChatPage <ChatPage
v-if="isChat" v-if="isChat"
v-model:is-chat="isChat" v-model:is-chat="isChat"
:chat-title="lunTanRes.channelTitle" :chat-title="lunTanRes!.channelTitle"
:chat-description="lunTanRes.channelProfile" :chat-description="lunTanRes!.channelProfile"
:channel-id="lunTanRes.channelId" :channel-id="lunTanRes!.channelId"
/> />
</div> </div>
</template> </template>
@ -68,28 +68,40 @@
import type { ChannelRespVO } from '~/api/channel/types' import type { ChannelRespVO } from '~/api/channel/types'
import { createChannelFollow, deleteChannelFollow } from '~/api/channel/index' import { createChannelFollow, deleteChannelFollow } from '~/api/channel/index'
import ChatPage from '~/pages/chat-page/index.vue' import ChatPage from '~/pages/chat-page/index.vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const lunTanRes = defineModel<ChannelRespVO>('modelValue', { const lunTanRes = defineModel<ChannelRespVO | null>('modelValue', {
required: true, required: true,
}) })
const handleClick = () => { const handleClick = () => {
window.open('/channel/create?channelId=' + lunTanRes.value.channelId, '_blank') if (!userStore.token) {
ElMessage.warning('请先登录')
return
}
navigateTo('/channel/create?channelId=' + lunTanRes.value?.channelId)
} }
const handleFollow = () => { const handleFollow = () => {
createChannelFollow({ channelId: lunTanRes.value.channelId }).then((res) => { if (!userStore.token) {
ElMessage.warning('请先登录')
return
}
createChannelFollow({ channelId: lunTanRes.value!.channelId }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
lunTanRes.value.isFollow = true lunTanRes.value!.isFollow = true
ElMessage.success('关注成功') ElMessage.success('关注成功')
} }
}) })
} }
const handleUnfollow = () => { const handleUnfollow = () => {
deleteChannelFollow({ channelId: lunTanRes.value.channelId }).then((res) => { if (!userStore.token) {
ElMessage.warning('请先登录')
return
}
deleteChannelFollow({ channelId: lunTanRes.value!.channelId }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
lunTanRes.value.isFollow = false lunTanRes.value!.isFollow = false
ElMessage.success('取消关注成功') ElMessage.success('取消关注成功')
} }
}) })
@ -103,7 +115,7 @@
ElMessage.warning('请先登录') ElMessage.warning('请先登录')
return return
} }
await userStore.mqttClient?.subscribe(`zbjk_message_group/${lunTanRes.value.channelId}`) await userStore.mqttClient?.subscribe(`zbjk_message_group/${lunTanRes.value!.channelId}`)
isChat.value = true isChat.value = true
} }
</script> </script>

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="box-border w-320px border border-[#EEEEEE] rounded-8px border-solid bg-[#FFFFFF] px-23px py-25px"> <div class="box-border w-[320px] border border-[#EEEEEE] rounded-[8px] border-solid bg-[#FFFFFF] px-[23px] py-[25px]">
<div class="text-16px text-[#333333] font-normal">频道列表</div> <div class="text-[16px] text-[#333333] font-normal">频道列表</div>
<div class="mt-30px"> <div class="mt-[30px]">
<el-row> <el-row>
<el-col v-for="(item, index) in channelIdList" :key="index" :span="8" class="mb-10px"> <el-col v-for="(item, index) in channelIdList" :key="index" :span="8" class="mb-[10px]">
<span <span
class="cursor-pointer text-14px text-[#999999] font-normal" class="cursor-pointer text-[14px] text-[#999999] font-normal"
:class="{ active: channelId === item.channelId }" :class="{ active: channelId === item.channelId }"
@click="handleClick(item.channelId)" @click="handleClick(item.channelId)"
>{{ item.channelTitle }}</span >{{ item.channelTitle }}</span
@ -16,27 +16,32 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' // import { ref } from 'vue'
import { list } from '~/api/channel/index' // import { list } from '~/api/channel/index'
const channelId = defineModel('modelValue', { const channelId = defineModel('modelValue', {
required: true, required: true,
}) })
const channelIdList = defineModel<any[]>('channelIdList', {
required: true,
})
/** 获取频道列表 */ /** 获取频道列表 */
const channelIdList = ref<any>([]) // const channelIdList = ref<any>([])
const getChannelIdList = () => { // const getChannelIdList = () => {
list().then((res) => { // list().then((res) => {
channelIdList.value = res.data // channelIdList.value = res.data
if (channelIdList.value.length > 0) { // if (channelIdList.value.length > 0) {
channelId.value = channelIdList.value[0].channelId // channelId.value = channelIdList.value[0].channelId
} // }
}) // })
} // }
getChannelIdList() // getChannelIdList()
const handleClick = (id: number) => { const handleClick = (id: number) => {
console.log(id) console.log(id)
channelId.value = id channelId.value = id
navigateTo(`/channel/${id}/1`)
} }
</script> </script>

View File

@ -2,7 +2,7 @@
<!-- 用户信息 --> <!-- 用户信息 -->
<div class="flex flex-col"> <div class="flex flex-col">
<UserInfo></UserInfo> <UserInfo></UserInfo>
<HotLlabel v-model="channelId" class="mt-18px"></HotLlabel> <HotLlabel v-model="channelId" class="mt-18px" v-model:channelIdList="channelIdList"></HotLlabel>
</div> </div>
</template> </template>
@ -13,6 +13,10 @@
const channelId = defineModel('modelValue', { const channelId = defineModel('modelValue', {
required: true, required: true,
}) })
const channelIdList = defineModel<any>('channelIdList', {
required: true,
})
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -1,37 +1,39 @@
<template> <template>
<div class="ml-19px w-100%"> <div class="ml-[19px] w-[100%]">
<ChannelHeader v-if="Object.keys(lunTanRes).length" v-model="lunTanRes"></ChannelHeader> <ChannelHeader v-if="Object.keys(lunTanRes || {}).length" v-model="lunTanRes"></ChannelHeader>
<div class="mb-13px box-border flex flex-1 flex-col cursor-pointer gap-12px border border-[#EEEEEE] rounded-8px border-solid bg-[#FFFFFF] px-20px py-16px"> <div
class="mb-[13px] box-border flex flex-1 flex-col cursor-pointer gap-[12px] border border-[#EEEEEE] rounded-[8px] border-solid bg-[#FFFFFF] px-[20px] py-[16px]"
>
<div <div
v-for="(item, index) in pageRes.list" v-for="(item, index) in pageRes?.list"
:key="index" :key="index"
class="flex justify-between border-b-1px border-b-[#eee] border-b-solid pb-8px" class="flex justify-between border-b-[1px] border-b-[#eee] border-b-solid pb-[8px]"
:class="{ 'border-b-0! pb-0px!': index === pageRes.list.length - 1 }" :class="{ 'border-b-0! pb-[0px]!': index === pageRes!.list!.length - 1 }"
@click="handleClick(item.postsId)" @click="handleClick(item.postsId)"
> >
<div class="flex flex-1 items-center"> <div class="flex flex-1 items-center">
<div class="ellipsis max-w-70% text-15px text-[#2d3137] font-normal">{{ item.postsTitle }}</div> <div class="ellipsis max-w-[70%] text-[15px] text-[#2d3137] font-normal">{{ item.postsTitle }}</div>
<span class="ml-10px flex-shrink-0 text-13px color-#96999f">{{ item.likeNum || 0 }}人赞过</span> <span class="ml-[10px] flex-shrink-0 text-[13px] color-[#96999f]">{{ item.likeNum || 0 }}人赞过</span>
<span class="ml-10px flex-shrink-0 text-13px color-#96999f">{{ item.commentNum || 0 }}评论</span> <span class="ml-[10px] flex-shrink-0 text-[13px] color-[#96999f]">{{ item.commentNum || 0 }}评论</span>
<span class="ml-10px flex-shrink-0 text-13px color-#96999f">{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}发布</span> <span class="ml-[10px] flex-shrink-0 text-[13px] color-[#96999f]">{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}发布</span>
</div> </div>
<div class="w-100px flex flex-shrink-0 items-center justify-end"> <div class="w-[100px] flex flex-shrink-0 items-center justify-end">
<span class="ellipsis text-13px color-#96999f">{{ item.creatorName }}</span> <span class="ellipsis text-[13px] color-[#96999f]">{{ item.creatorName }}</span>
<!-- 删除 --> <!-- 删除 -->
<el-button v-if="false" type="danger" size="small" @click="handleDelete(item.postsId)">删除</el-button> <el-button v-if="false" type="danger" size="small" @click="handleDelete(item.postsId)">删除</el-button>
</div> </div>
</div> </div>
</div> </div>
<el-pagination <el-pagination
v-if="pageRes.list.length > 0" v-if="pageRes?.list.length"
v-model:current-page="pageNo" v-model:current-page="pageNo"
:page-size="10" :page-size="10"
layout="prev, pager, next" layout="prev, pager, next"
:total="pageRes.total" :total="pageRes?.total"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
<!-- 暂无数据 --> <!-- 暂无数据 -->
<el-empty v-if="!pageRes.list.length" description="暂无数据"></el-empty> <el-empty v-if="!pageRes?.list.length" description="暂无数据"></el-empty>
</div> </div>
</template> </template>
@ -43,10 +45,10 @@
const emit = defineEmits(['updatePageNo']) const emit = defineEmits(['updatePageNo'])
const pageRes = defineModel<TpageRes>('modelValue', { const pageRes = defineModel<TpageRes | null>('modelValue', {
required: true, required: true,
}) })
const lunTanRes = defineModel<ChannelRespVO>('lunTanRes', { const lunTanRes = defineModel<ChannelRespVO | null>('lunTanRes', {
required: true, required: true,
}) })
const pageNo = defineModel<number>('pageNo', { const pageNo = defineModel<number>('pageNo', {
@ -73,7 +75,7 @@
const handleClick = (channelId: number) => { const handleClick = (channelId: number) => {
// 新开窗口 // 新开窗口
window.open(`/chat-detail?channelId=${channelId}`, '_blank') navigateTo(`/chat-detail/${channelId}-1`)
} }
</script> </script>

View File

@ -36,7 +36,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const handleClick = () => { const handleClick = () => {
// 判断是否登录 // 判断是否登录
@ -44,6 +44,6 @@
ElMessage.warning('请先登录') ElMessage.warning('请先登录')
return return
} }
window.open('/channel/create', '_blank') navigateTo('/channel/create')
} }
</script> </script>

View File

@ -1,367 +0,0 @@
<template>
<KlNavTab></KlNavTab>
<div class="mx-auto w-1440px">
<!-- 使用 el-form 重构表单区域 -->
<el-form ref="formRef" inline :model="formData" label-width="110px" class="custom-form mb-20px mt-20px border rounded p-20px!">
<el-form-item label="标题:" prop="postsTitle" :rules="{ required: true, message: '请输入标题', trigger: 'blur' }">
<el-input v-model="formData.postsTitle" placeholder="请输入标题" class="w-300px!" minlength="4" maxlength="40"></el-input>
</el-form-item>
<el-form-item label="分类:" class="mb-10px" prop="projectDicId" :rules="{ required: true, message: '请选择分类', trigger: ['blur', 'change'] }">
<el-select v-model="formData.projectDicId" placeholder="请选择分类" class="w-300px!">
<el-option v-for="item in projectTypeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="标签:" class="mb-10px" prop="postsTags" :rules="{ required: true, message: '请输入标签', trigger: ['blur', 'change'] }">
<el-select
v-model="formData.postsTags"
:remote-method="remoteMethod"
:loading="loading"
filterable
remote
multiple
placeholder="请输入搜索标签"
class="w-300px!"
>
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="频道列表:" class="mb-10px" prop="channelId" :rules="{ required: true, message: '请选择频道', trigger: ['blur', 'change'] }">
<el-select v-model="formData.channelId" placeholder="请选择频道" class="w-300px!">
<el-option v-for="item in channelIdList" :key="item.channelId" :label="item.channelTitle" :value="item.channelId" />
</el-select>
</el-form-item>
<!-- <el-form-item label="上传封面:" prop="postsCover" :rules="{ required: true, message: '请上传封面', trigger: ['blur', 'change'] }">
<KlUploader v-model:file-list="formData.postsCover" :limit="1" :size="1" tips="上传图片支持jpg/gif/png格式"> </KlUploader>
</el-form-item> -->
</el-form>
<Editor :id="tinymceId" v-model="myValue" :init="init" :disabled="disabled" :placeholder="placeholder" />
<!-- 按钮区域 -->
<div class="mt-20px flex justify-end">
<el-button :loading="post_loading" class="mr-10px" @click="previewContent">预览</el-button>
<el-button :loading="post_loading" type="primary" @click="saveContent">发表</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { keywords } from '~/api/upnew/index'
import { reactive, ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
import { create, list } from '~/api/channel/index'
import { parent } from '~/api/upnew/index'
import { upload } from '~/api/common/index' // 自定义上传方法
import Editor from '@tinymce/tinymce-vue'
import tinymce from 'tinymce/tinymce'
import 'tinymce/themes/silver'
import 'tinymce/themes/silver/theme'
import 'tinymce/models/dom'
import 'tinymce/icons/default'
import 'tinymce/icons/default/icons'
// 引入编辑器插件
import 'tinymce/plugins/code' //编辑源码
import 'tinymce/plugins/image' //插入编辑图片
import 'tinymce/plugins/media' //插入视频
import 'tinymce/plugins/link' //超链接
import 'tinymce/plugins/preview' //预览
// import 'tinymce/plugins/template' //模板
import 'tinymce/plugins/table' //表格
import 'tinymce/plugins/pagebreak' //分页
import 'tinymce/plugins/lists' //列
import 'tinymce/plugins/advlist' //列
import 'tinymce/plugins/quickbars' //快速工具条
import 'tinymce/plugins/wordcount' // 字数统计插件
// import '~/assets/tinymce/langs/zh-Hans.js' //下载后的语言包
// import 'tinymce/skins/content/default/content.css'
// 获取从其他地方传过来的参数
const channelId = route.query.channelId as string
const props = defineProps({
value: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '请输入帖子内容',
},
height: {
type: Number,
default: 500,
},
disabled: {
type: Boolean,
default: false,
},
plugins: {
type: [String, Array],
default: 'code image link preview table quickbars pagebreak lists advlist',
},
toolbar: {
type: [String, Array],
default:
'undo redo codesample bold italic underline strikethrough link alignleft aligncenter alignright alignjustify \
bullist numlist outdent indent removeformat forecolor backcolor |formatselect fontselect fontsizeselect | \
blocks fontfamily fontsize pagebreak lists image customvideoupload table preview | code selectall',
},
templates: {
type: Array,
default: () => [],
},
options: {
type: Object,
default: () => ({}),
},
})
//用于接收外部传递进来的富文本
const myValue = ref(props.value)
const tinymceId = ref('vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + ''))
const init = reactive({
selector: '#' + tinymceId.value, //富文本编辑器的id,
language_url: '/tinymce/langs/zh_CN.js', // 语言包的路径具体路径看自己的项目文档后面附上中文js文件
language: 'zh-Hans', //语言
skin_url: '/tinymce/skins/ui/oxide', // skin路径具体路径看自己的项目
content_css: '/tinymce/skins/content/default/content.css',
menubar: true, //顶部菜单栏显示
statusbar: true, // 底部的状态栏
plugins: props.plugins,
toolbar: props.toolbar,
toolbar_mode: 'sliding',
font_formats: 'Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;', //字体
paste_convert_word_fake_lists: false, // 插入word文档需要该属性
font_size_formats: '12px 14px 16px 18px 22px 24px 36px 72px', //文字大小
height: props.height, //编辑器高度
placeholder: props.placeholder,
branding: false, //是否禁用"Powered by TinyMCE"
promotion: false, //禁用升级按钮
image_dimensions: false, //去除宽高属性
paste_webkit_styles: 'all',
paste_merge_formats: true,
nonbreaking_force_tab: false,
paste_auto_cleanup_on_paste: false,
file_picker_types: 'file',
resize: true,
elementpath: true,
content_style: `img {max-width:100%;} body{background-color: #fff;}`, // 直接自定义可编辑区域的css样式
templates: props.templates,
quickbars_selection_toolbar: 'forecolor backcolor bold italic underline strikethrough link',
quickbars_image_toolbar: 'alignleft aligncenter alignright',
quickbars_insert_toolbar: false,
image_caption: true,
image_advtab: true,
convert_urls: false,
images_upload_url: import.meta.env.VITE_BASE_API,
images_upload_handler: function (blobInfo: any, progress: any) {
console.log(blobInfo, progress)
return new Promise((resolve, reject) => {
const data = new FormData()
data.append('file', blobInfo.blob())
data.append('fieldName', blobInfo.filename())
upload('/prod-api/app-api/infra/file/upload', data)
.then((res) => {
resolve(res.data)
})
.catch(() => {
reject('Image upload failed')
})
})
},
// 添加自定义按钮
setup: function (editor: any) {
// 注册一个新的视频上传按钮
editor.ui.registry.addButton('customvideoupload', {
icon: 'embed', // 使用嵌入媒体图标
tooltip: '上传视频',
onAction: function () {
// 创建文件输入元素
const input = document.createElement('input')
input.setAttribute('type', 'file')
input.setAttribute('accept', 'video/*')
// 处理文件选择事件
input.onchange = function () {
if (input.files && input.files[0]) {
const file = input.files[0]
const data = new FormData()
data.append('file', file)
data.append('fieldName', file.name)
// 可以在这里添加上传进度显示
upload('/prod-api/app-api/infra/file/upload', data)
.then((res) => {
// 插入视频到编辑器
editor.insertContent(`
<video controls width="400">
<source src="${res.data}" type="video/${file.name.split('.').pop()}">
您的浏览器不支持视频标签
</video>
`)
})
.catch((error) => {
console.error('视频上传失败:', error)
// 可以添加错误提示
})
}
}
// 触发文件选择
input.click()
},
})
},
preview_styles: true,
...props.options,
})
// 表单数据
const formData = ref({
projectDicId: undefined,
postsTags: [],
postsCover: [] as any,
postsTitle: '',
channelId: channelId || undefined,
})
const formRef = ref()
const post_loading = ref(false)
const saveContent = () => {
formRef.value.validate().then(() => {
if (!myValue.value) {
ElMessage.error('请输入帖子内容')
return
}
post_loading.value = true
create({
postsTitle: formData.value.postsTitle,
// postsCover: formData.value.postsCover[0].url,
postsTags: formData.value.postsTags.join(','),
postsContent: myValue.value,
projectDicId: formData.value.projectDicId,
channelId: formData.value.channelId,
})
.then((res) => {
console.log(res)
if (res.code === 0) {
ElMessage.success('发表成功')
router.push('/communication/channel')
}
})
.catch((err) => {
console.log(err)
})
.finally(() => {
post_loading.value = false
})
})
}
const previewContent = () => {
// 获取编辑器实例
const editor = tinymce.get(tinymceId.value)
// 调用编辑器的预览命令
if (editor) {
editor.execCommand('mcePreview')
}
}
//在onMounted中初始化编辑器
onMounted(() => {
tinymce.init({})
})
/** 获取频道列表 */
const channelIdList = ref<any>([])
const getChannelIdList = () => {
list().then((res) => {
channelIdList.value = res.data
})
}
getChannelIdList()
const projectTypeList = ref<any>([])
/** 获取分类下拉框 */
const getParent = () => {
parent({
type: 1,
parentId: 0,
}).then((res) => {
projectTypeList.value = res.data
})
}
getParent()
const loading = ref(false)
/** 获取标签 */
const labelsList = ref<any>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
keywords({
type: 1,
keywords: query,
})
.then((res) => {
labelsList.value = res.data
})
.finally(() => {
loading.value = false
})
} else {
labelsList.value = []
}
}
</script>
<style scoped>
.custom-form {
border: 2px solid #eeeeee;
border-radius: 4px;
}
.upload-container {
display: flex;
flex-direction: column;
}
.cover-uploader {
:deep(.el-upload) {
width: fit-content;
}
}
.image-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #909399;
height: 100%;
}
.image-actions {
display: flex;
justify-content: center;
}
:deep(.el-form-item__label) {
font-weight: normal;
}
:deep(.el-button--primary.is-link) {
padding: 0;
height: auto;
font-size: 14px;
}
.text-gray-500 {
color: #999;
}
.text-12px {
font-size: 12px;
}
</style>

View File

@ -0,0 +1,352 @@
<template>
<KlNavTab></KlNavTab>
<div class="mx-auto w-[1440px]">
<!-- 使用 el-form 重构表单区域 -->
<el-form ref="formRef" inline :model="formData" label-width="110px" class="custom-form mb-[20px] mt-[20px] border rounded p-[20px]!">
<el-form-item label="标题:" prop="postsTitle" :rules="{ required: true, message: '请输入标题', trigger: 'blur' }">
<el-input v-model="formData.postsTitle" placeholder="请输入标题" class="w-[300px]!" minlength="4" maxlength="40"></el-input>
</el-form-item>
<el-form-item label="分类:" class="mb-[10px]" prop="projectDicId" :rules="{ required: true, message: '请选择分类', trigger: ['blur', 'change'] }">
<el-select v-model="formData.projectDicId" placeholder="请选择分类" class="w-[300px]!">
<el-option v-for="item in projectTypeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="标签:" class="mb-[10px]" prop="postsTags" :rules="{ required: true, message: '请输入标签', trigger: ['blur', 'change'] }">
<el-select
v-model="formData.postsTags"
:remote-method="remoteMethod"
:loading="loading"
filterable
remote
multiple
placeholder="请输入搜索标签"
class="w-[300px]!"
>
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item label="频道列表:" class="mb-[10px]" prop="channelId" :rules="{ required: true, message: '请选择频道', trigger: ['blur', 'change'] }">
<el-select v-model="formData.channelId" placeholder="请选择频道" class="w-[300px]!">
<el-option v-for="item in channelIdList" :key="item.channelId" :label="item.channelTitle" :value="item.channelId" />
</el-select>
</el-form-item>
<!-- <el-form-item label="上传封面:" prop="postsCover" :rules="{ required: true, message: '请上传封面', trigger: ['blur', 'change'] }">
<KlUploader v-model:file-list="formData.postsCover" :limit="1" :size="1" tips="上传图片支持jpg/gif/png格式"> </KlUploader>
</el-form-item> -->
</el-form>
<div style="border: 1px solid #eeeeee; margin-top: 10px" class="rounded">
<WeToolbar style="border-bottom: 1px solid #eeeeee" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
<WeEditor
style="min-height: 300px; overflow-y: hidden"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
@onChange="handleChange"
@onDestroyed="handleDestroyed"
@onFocus="handleFocus"
@onBlur="handleBlur"
@customAlert="customAlert"
@customPaste="customPaste"
/>
</div>
<!-- 按钮区域 -->
<div class="mt-[20px] flex justify-end">
<!-- <el-button :loading="post_loading" class="mr-10px" @click="previewContent">预览</el-button> -->
<el-button :loading="post_loading" type="primary" @click="saveContent">发表</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { keywords } from '@/api/upnew/index'
import { reactive, ref, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
const router = useRouter()
const route = useRoute()
import { create, list } from '@/api/channel/index'
import { parent } from '@/api/upnew/index'
import { upload } from '@/api/common/index' // 自定义上传方法
import '@wangeditor/editor/dist/css/style.css' // 引入 css
// import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { onBeforeUnmount, shallowRef } from 'vue'
import useUserStore from '~/stores/user'
const userStore = useUserStore()
// 获取从其他地方传过来的参数
const channelId = route.query.channelId as string
const props = defineProps({
value: {
type: String,
default: '',
},
placeholder: {
type: String,
default: '请输入帖子内容',
},
height: {
type: Number,
default: 500,
},
disabled: {
type: Boolean,
default: false,
},
plugins: {
type: [String, Array],
default: 'code image link preview table quickbars pagebreak lists advlist',
},
toolbar: {
type: [String, Array],
default:
'undo redo codesample bold italic underline strikethrough link alignleft aligncenter alignright alignjustify \
bullist numlist outdent indent removeformat forecolor backcolor |formatselect fontselect fontsizeselect | \
blocks fontfamily fontsize pagebreak lists image customvideoupload table preview | code selectall',
},
templates: {
type: Array,
default: () => [],
},
options: {
type: Object,
default: () => ({}),
},
})
const mode = 'default'
const isClient = ref(false)
if (import.meta.client) {
isClient.value = true
}
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
// 内容 HTML
const valueHtml = ref('<p></p>')
// 模拟 ajax 异步获取内容
onMounted(() => {
setTimeout(() => {
valueHtml.value = '<p></p>'
}, 1500)
})
const toolbarConfig = {}
const editorConfig = {
placeholder: '请输入内容...',
// 上传图片的配置
MENU_CONF: {
uploadImage: {
server: 'https://tuxixi.net/prod-api/app-api/infra/file/upload',
fieldName: 'file', //这个是参数名字
headers: {
//配置token 接口需要就配 不需要就不用
Authorization: `Bearer ${userStore.token}`,
},
customInsert(res: any, insertFn: any) {
// 这个是获取接口返回的数据
insertFn(res.data) // 从 res 中找到 url也就是接口返回的图片地址然后插入图片
},
},
uploadVideo: {
server: 'https://tuxixi.net/prod-api/app-api/infra/file/upload',
fieldName: 'file', //这个是参数名字
maxFileSize: 1200 * 1024 * 1024, // 1200M
headers: {
//配置token 接口需要就配 不需要就不用
Authorization: `Bearer ${userStore.token}`,
},
customInsert(res: any, insertFn: any) {
// 这个是获取接口返回的数据
insertFn(res.data) // 从 res 中找到 url也就是接口返回的图片地址然后插入图片
},
},
},
}
// 表单数据
const formData = ref({
projectDicId: undefined,
postsTags: [],
postsCover: [] as any,
postsTitle: '',
channelId: channelId || undefined,
})
const formRef = ref()
const post_loading = ref(false)
const saveContent = () => {
formRef.value.validate().then(() => {
if (!valueHtml.value) {
ElMessage.error('请输入帖子内容')
return
}
post_loading.value = true
create({
postsTitle: formData.value.postsTitle,
// postsCover: formData.value.postsCover[0].url,
postsTags: formData.value.postsTags.join(','),
postsContent: valueHtml.value,
projectDicId: formData.value.projectDicId,
channelId: formData.value.channelId,
})
.then((res) => {
console.log(res)
if (res.code === 0) {
ElMessage.success('发表成功')
router.back()
// router.push('/communication/channel')
}
})
.catch((err) => {
console.log(err)
})
.finally(() => {
post_loading.value = false
})
})
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
const handleChange = (editor: any) => {
console.log('change:', editor.getHtml())
}
const handleDestroyed = (editor: any) => {
console.log('destroyed', editor)
}
const handleFocus = (editor: any) => {
console.log('focus', editor)
}
const handleBlur = (editor: any) => {
console.log('blur', editor)
}
const customAlert = (info: any, type: any) => {
alert(`【自定义提示】${type} - ${info}`)
}
const customPaste = (editor: any, event: any, callback: any) => {
console.log('ClipboardEvent 粘贴事件对象', event)
// const html = event.clipboardData.getData('text/html') // 获取粘贴的 html
const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本
// const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴)
// 自定义插入内容
editor.insertText(text)
// 返回 false ,阻止默认粘贴行为
event.preventDefault()
callback(false) // 返回值注意vue 事件的返回值,不能用 return
// 返回 true ,继续默认的粘贴行为
// callback(true)
}
/** 获取频道列表 */
const channelIdList = ref<any>([])
const getChannelIdList = () => {
list().then((res) => {
channelIdList.value = res.data
})
}
getChannelIdList()
const projectTypeList = ref<any>([])
/** 获取分类下拉框 */
const getParent = () => {
parent({
type: 1,
parentId: 0,
}).then((res) => {
projectTypeList.value = res.data
})
}
getParent()
const loading = ref(false)
/** 获取标签 */
const labelsList = ref<any>([])
const remoteMethod = (query: string) => {
if (query) {
loading.value = true
keywords({
type: 1,
keywords: query,
})
.then((res) => {
labelsList.value = res.data
})
.finally(() => {
loading.value = false
})
} else {
labelsList.value = []
}
}
</script>
<style scoped>
.custom-form {
border: 2px solid #eeeeee;
border-radius: 4px;
}
.upload-container {
display: flex;
flex-direction: column;
}
.cover-uploader {
:deep(.el-upload) {
width: fit-content;
}
}
.image-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #909399;
height: 100%;
}
.image-actions {
display: flex;
justify-content: center;
}
:deep(.el-form-item__label) {
font-weight: normal;
}
:deep(.el-button--primary.is-link) {
padding: 0;
height: auto;
font-size: 14px;
}
.text-gray-500 {
color: #999;
}
.text-12px {
font-size: 12px;
}
</style>

View File

@ -1,9 +1,9 @@
<template> <template>
<!-- 导航 --> <!-- 导航 -->
<KlNavTab active="交流频道" /> <KlNavTab active="交流频道" />
<div class="ma-auto mt-30px w-1440px flex"> <div class="ma-auto mt-[30px] w-[1440px] flex">
<LeftContent v-model="pageReq.channelId"></LeftContent> <LeftContent v-model="pageReq.channelId" v-model:channelIdList="channelIdList"></LeftContent>
<RightContent v-model="pageRes" v-model:lun-tan-res="lunTanRes" v-model:page-no="pageReq.pageNo" @update-page-no="handleUpdatePageNo"></RightContent> <!-- <RightContent v-model="pageRes" v-model:lun-tan-res="lunTanRes" v-model:page-no="pageReq.pageNo" @update-page-no="handleUpdatePageNo"></RightContent> -->
</div> </div>
</template> </template>
@ -11,7 +11,7 @@
import KlNavTab from '~/components/kl-nav-tab/index.vue' import KlNavTab from '~/components/kl-nav-tab/index.vue'
import LeftContent from './components/LeftContent.vue' import LeftContent from './components/LeftContent.vue'
import RightContent from './components/RightContent.vue' import RightContent from './components/RightContent.vue'
import { page, getChannelLunTanDetail } from '~/api/channel/index' import { page, getChannelLunTanDetail, list } from '~/api/channel/index'
import { reactive, watch, ref } from 'vue' import { reactive, watch, ref } from 'vue'
import type { TpageRes, ChannelRespVO } from '~/api/channel/types' import type { TpageRes, ChannelRespVO } from '~/api/channel/types'
@ -24,38 +24,60 @@
list: [], list: [],
total: 0, total: 0,
}) })
// 获得频道帖子分页
const getPage = () => {
page(pageReq).then((res) => {
pageRes.list = res.data.list
pageRes.total = res.data.total
})
}
getPage()
const handleUpdatePageNo = (pageNo: number) => { // 获取频道列表
pageReq.pageNo = pageNo const { data: channelIdList } = await useAsyncData(`prod-api/app-api/business/channel/list-${Date.now()}`, async () => {
getPage() const res = await list()
} return res.data as any[]
})
// console.log(channelIdList)
// 获取第一个论坛详情
// if (!channelIdList.value?.length) return
// const { data: lunTanRes, execute } = await useAsyncData(`prod-api/app-api/business/channel/detail-${Date.now()}`, async () => {
// const res = await getChannelLunTanDetail({
// id: channelIdList.value?.[0].channelId,
// })
// return res.data as ChannelRespVO
// })
// const {data:pageRes, refresh:getPage} = await useAsyncData(`prod-api/app-api/business/posts/page-${Date.now()}`, async () => {
// const res = await page(pageReq)
// return res.data as TpageRes
// })
// 获得频道帖子分页
// const getPage = () => {
// page(pageReq).then((res) => {
// pageRes.list = res.data.list
// pageRes.total = res.data.total
// })
// }
// getPage()
// const handleUpdatePageNo = (pageNo: number) => {
// pageReq.pageNo = pageNo
// getPage()
// }
// 获取论坛详情 // 获取论坛详情
const lunTanRes = ref({} as ChannelRespVO) // const lunTanRes = ref({} as ChannelRespVO)
const getLunTanDetaiil = (val: string) => { // const getLunTanDetaiil = (val: string) => {
getChannelLunTanDetail({ // getChannelLunTanDetail({
id: val, // id: val,
}).then((res) => { // }).then((res) => {
lunTanRes.value = res.data // lunTanRes.value = res.data
}) // })
} // }
watch( watch(
() => pageReq.channelId, () => channelIdList.value,
(val) => { (val) => {
if (val) { if (val) {
getPage() navigateTo(`/channel/${val[0].channelId}/1`)
getLunTanDetaiil(val)
} }
} },
{ immediate: true }
) )
</script> </script>

View File

@ -1,70 +1,70 @@
<template> <template>
<KlNavTab /> <KlNavTab />
<div class="ml-auto mr-auto mt-20px w1440 flex"> <div class="ml-auto mr-auto mt-[20px] w-[1440px] flex">
<div class="left box-border w-1019px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] px-42px py-30px"> <div class="left box-border w-[100%] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[42px] py-[30px]">
<div class="title text-24px text-[#333333] font-bold">{{ channelDetail?.postsTitle }}</div> <div class="title text-[20px] text-[#333333] font-bold">{{ channelDetail?.postsTitle }}</div>
<div class="mt-20px flex items-center justify-between border-b-1px border-b-[#eee] border-b-solid pb-14px"> <div class="mt-[20px] flex items-center justify-between border-b-[1px] border-b-[#eee] border-b-solid pb-[14px]">
<div class="flex items-center"> <div class="flex items-center">
<img :src="channelDetail?.creatorAvatar" alt="" srcset="" class="h-50px w-49px rounded-full" /> <img :src="channelDetail?.creatorAvatar" alt="" srcset="" class="h-[50px] w-[49px] rounded-full" />
<div class="ml-10px"> <div class="ml-[10px]">
<div class="text-16px text-[#333333] font-normal">{{ channelDetail?.creatorName }}</div> <div class="text-[16px] text-[#333333] font-normal">{{ channelDetail?.creatorName }}</div>
<div class="text-12px text-[#999999] font-normal">发表时间{{ dayjs(channelDetail?.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div> <div class="text-[12px] text-[#999999] font-normal">发表时间{{ dayjs(channelDetail?.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
</div> </div>
</div> </div>
<div class="mt-19px flex items-center justify-between"> <div class="mt-[19px] flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/look.png" alt="" srcset="" class="mr-4px h-17px" /> <img src="~/assets/images/look.png" alt="" srcset="" class="mr-[4px] h-[17px]" />
<span class="text-[#666666]">{{ channelDetail?.likeNum || 0 }}人赞过</span> <span class="text-[#666666]">{{ channelDetail?.likeNum || 0 }}人赞过</span>
<div class="ml-16px flex items-center"> <div class="ml-[16px] flex items-center">
<img src="~/assets/images/add.png" alt="" class="mr-4px h-23px" /> <img src="~/assets/images/add.png" alt="" class="mr-[4px] h-[23px]" />
<span class="text-[#666666]">{{ channelDetail?.commentNum || 0 }}评论</span> <span class="text-[#666666]">{{ channelDetail?.commentNum || 0 }}评论</span>
</div> </div>
</div> </div>
<div class="ml-16px flex items-center"> <div class="ml-[16px] flex items-center">
<img src="~/assets/images/chat.png" alt="" srcset="" class="mr-4px h-17px" /> <img src="~/assets/images/chat.png" alt="" srcset="" class="mr-[4px] h-[17px]" />
<span class="text-[#666666]">{{ channelDetail?.browseNum || 0 }}人看过</span> <span class="text-[#666666]">{{ channelDetail?.browseNum || 0 }}人看过</span>
</div> </div>
</div> </div>
</div> </div>
<div> <div>
<img :src="channelDetail?.postsCover" alt="" srcset="" class="h-396px w-auto" /> <img :src="channelDetail?.postsCover" alt="" srcset="" class="h-[396px] w-auto" />
<div class="mt-20px text-14px text-[#333333] font-normal" v-html="channelDetail?.postsContent"></div> <div class="mt-[20px] text-[14px] text-[#333333] font-normal" v-html="channelDetail?.postsContent"></div>
</div> </div>
<div class="mt-30px"> <div class="mt-[30px]">
<div class="h-48px w-938px rounded-1px bg-[#F8F8F8] pl-10px text-16px text-[#333333] font-normal line-height-50px" <div class="h-[48px] w-[100%] rounded-[1px] bg-[#F8F8F8] pl-[10px] text-[16px] text-[#333333] font-normal line-height-[50px]"
>共有{{ commentList.total || 0 }}条评论</div >共有{{ commentList?.total || 0 }}条评论</div
> >
<div v-for="item in commentList.list" :key="item.commentId" class="mt-10px border-b-1px border-b-[#eee] border-b-solid pb-14px"> <div v-for="item in commentList?.list" :key="item.commentId" class="mt-[10px] border-b-[1px] border-b-[#eee] border-b-solid pb-[14px]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<img :src="item.creatorAvatar" alt="" srcset="" class="relative top-12px h-50px w-49px rounded-full" /> <img :src="item.creatorAvatar" alt="" srcset="" class="relative top-[12px] h-[50px] w-[49px] rounded-full" />
<div class="ml-10px"> <div class="ml-[10px]">
<span>{{ item.creatorName }}</span> <span>{{ item.creatorName }}</span>
</div> </div>
</div> </div>
<div class="text-12px text-[#999999] font-normal">发表时间{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div> <div class="text-[12px] text-[#999999] font-normal">发表时间{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
</div> </div>
<div class="ml-60px mt--10px box-border rd-4px bg-[#F8F8F8] pa-6px px-8px text-14px text-[#999999] font-normal">{{ item.content }}</div> <div class="ml-[60px] mt-[-10px] box-border rd-[4px] bg-[#F8F8F8] pa-[6px] px-[8px] text-[14px] text-[#999999] font-normal">{{ item.content }}</div>
</div> </div>
<!-- 添加element-plus分页 --> <!-- 添加element-plus分页 -->
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
:page-size="query.pageSize" :page-size="query.pageSize"
layout="prev, pager, next" layout="prev, pager, next"
:total="commentList.total" :total="commentList?.total"
class="mt-10px" class="mt-[10px]"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
<el-input v-model="commentContent" type="textarea" :rows="6" placeholder="请输入内容" class="mt-20px w-100%"></el-input> <el-input v-model="commentContent" type="textarea" :rows="6" placeholder="请输入内容" class="mt-[20px] w-[100%]"></el-input>
</div> </div>
<div> <div>
<el-button type="primary" class="mt-10px h-40px w-101px rounded-4px text-16px text-[#FFFFFF] font-bold" @click="handleCreateComment" <el-button type="primary" class="mt-[10px] h-[40px] w-[101px] rounded-[4px] text-[16px] text-[#FFFFFF] font-bold" @click="handleCreateComment"
>发表评论</el-button >发表评论</el-button
> >
</div> </div>
</div> </div>
<div class="right ml-23px w-100%"> <!-- <div class="right ml-23px w-100%">
<div class="mt-20px w-398px border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid bg-[#FFFFFF]"> <div class="mt-20px w-398px border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid bg-[#FFFFFF]">
<img src="~/assets/images/sign.png" alt="" srcset="" class="h-206px w-100%" /> <img src="~/assets/images/sign.png" alt="" srcset="" class="h-206px w-100%" />
<div class="box-border border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid border-t-none bg-[#FFFFFF] pa-18px"> <div class="box-border border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid border-t-none bg-[#FFFFFF] pa-18px">
@ -89,7 +89,7 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div> -->
</div> </div>
</template> </template>
@ -101,50 +101,69 @@
import { getChannelDetail, postscommentpage, createPostsComment } from '~/api/channel' import { getChannelDetail, postscommentpage, createPostsComment } from '~/api/channel'
import type { TGetChannelPostsRes, PageResultPostsCommentRespVO } from '~/api/channel/types' import type { TGetChannelPostsRes, PageResultPostsCommentRespVO } from '~/api/channel/types'
const route = useRoute() const route = useRoute()
const channelId = route.query.channelId as string const channelId = computed(() => route.params.channelId as string)
const pageNo = computed(() => Number(route.params.pageNo))
const channelDetail = ref<TGetChannelPostsRes>() console.log(channelId.value, pageNo.value)
const commentList = reactive<PageResultPostsCommentRespVO>({
list: [], // const channelDetail = ref<TGetChannelPostsRes>()
total: 0, // const commentList = reactive<PageResultPostsCommentRespVO>({
}) // list: [],
const getChannel = () => { // total: 0,
getChannelDetail({ // })
id: channelId,
}).then((res) => { const { data: channelDetail } = await useAsyncData(`prod-api/app-api/business/posts/detail-${Date.now()}`, async () => {
if (res.code === 0) { const res = await getChannelDetail({
channelDetail.value = res.data id: channelId.value,
}
}) })
} return res.data as TGetChannelPostsRes
})
// const getChannel = () => {
// getChannelDetail({
// id: channelId,
// }).then((res) => {
// if (res.code === 0) {
// channelDetail.value = res.data
// }
// })
// }
const query = reactive({ const query = reactive({
pageNo: 1, pageNo: pageNo.value,
pageSize: 4, pageSize: 4,
}) })
const getComment = () => {
postscommentpage({ const { data: commentList, refresh } = await useAsyncData(`prod-api/app-api/business/posts/comment/page-${Date.now()}`, async () => {
postsId: channelId, const res = await postscommentpage({
pageNo: query.pageNo, postsId: channelId.value,
pageSize: query.pageSize, pageNo: pageNo.value,
}).then((res) => { pageSize: 4,
if (res.code === 0) {
commentList.list = res.data.list
commentList.total = res.data.total
}
}) })
} return res.data as PageResultPostsCommentRespVO
watch( })
() => channelId, // const getComment = () => {
(val) => { // postscommentpage({
if (val) { // postsId: channelId,
getChannel() // pageNo: query.pageNo,
getComment() // pageSize: query.pageSize,
} // }).then((res) => {
}, // if (res.code === 0) {
{ // commentList.list = res.data.list
immediate: true, // commentList.total = res.data.total
} // }
) // })
// }
// watch(
// () => channelId,
// (val) => {
// if (val) {
// getChannel()
// getComment()
// }
// },
// {
// immediate: true,
// }
// )
const commentContent = ref('') const commentContent = ref('')
const handleCreateComment = () => { const handleCreateComment = () => {
@ -153,17 +172,18 @@
return return
} }
createPostsComment({ createPostsComment({
postsId: channelId, postsId: channelId.value,
content: commentContent.value, content: commentContent.value,
}).then((res) => { }).then((res) => {
if (res.code === 0) { if (res.code === 0) {
getComment() refresh()
commentContent.value = '' commentContent.value = ''
} }
}) })
} }
const handleCurrentChange = (pageNo: number) => { const handleCurrentChange = (pageNo: number) => {
query.pageNo = pageNo // query.pageNo = pageNo
getComment() // getComment()
navigateTo(`/chat-detail/${channelId.value}-${pageNo}`)
} }
</script> </script>

View File

@ -106,7 +106,7 @@
import type { msgType, PageResultMessageRespVO, MemberUserRespDTO } from '~/api/channel/types' import type { msgType, PageResultMessageRespVO, MemberUserRespDTO } from '~/api/channel/types'
import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue' import { ref, onMounted, nextTick, watch, onUnmounted } from 'vue'
// import dayjs from 'dayjs' // import dayjs from 'dayjs'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const props = defineProps({ const props = defineProps({

View File

@ -0,0 +1,372 @@
<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">
<div
class="box-border h-[60px] w-[1019px] flex items-center justify-between border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[27px] py-[24px]"
>
<div class="text-[20px] text-[#333333] font-normal"> {{ detail?.title }}</div>
<div class="flex items-center">
<img :src="detail?.ownedUserIdInfo?.avatar" alt="" srcset="" class="h-[30px] w-[30px] rd-[50%]" />
<span class="ml-[12px] color-[#999999]">by {{ detail?.ownedUserIdInfo?.nickName }}</span>
</div>
</div>
<div class="ml-[23px] flex flex-1 text-[18px] text-[#FFFFFF] font-normal">
<div class="h-[60px] w-[160px] flex cursor-pointer items-center justify-center rounded-[8px] bg-[#1A65FF]" @click="handleDownload">
<img src="~/assets/images/download.png" alt="" srcset="" class="mr-[4px] h-[22px] w-[27px]" />
{{ detail?.points === 0 ? '免费下载' : '立即下载' }}
</div>
<div
v-if="!detail?.favoriteId"
class="ml-[11px] h-[60px] flex flex-1 cursor-pointer items-center justify-center rounded-[8px] bg-[#E7B03B]"
@click="handleCollect"
><img src="~/assets/images/collect.png" alt="" srcset="" class="mr-[4px] h-[24px] w-[24px]" /> 收藏</div
>
<div v-else class="ml-[11px] h-[60px] flex flex-1 cursor-pointer items-center justify-center rounded-[8px] bg-[#E7B03B]" @click="handleCollect"
><img src="~/assets/images/wjx2.png" alt="" srcset="" class="mr-[4px] h-[18px] w-[18px]" /> 已收藏</div
>
<div class="ml-[11px] h-[60px] flex flex-1 cursor-pointer items-center justify-center rounded-[8px] bg-[#F56C6C]" @click="handleReport"
><el-icon class="mr-[4px] mt-[4px]"><Warning /></el-icon> 举报</div
>
</div>
</div>
</div>
<!-- -->
<div class="ma-auto mt-[21px] flex">
<div class="w-[1019px]">
<div>
<ThumBnail :data="detail?.coverImages" :type="detail?.type"></ThumBnail>
</div>
<div class="mb-[20px] mt-[34px] flex items-center text-[16px] text-[#333333] font-normal">
<div class="h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div><span class="ml-[10px]">{{ detail?.title }}描述</span></div
>
<div
class="box-border min-h-[90px] w-[1019px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] pa-[24px] text-[14px] text-[#333333] font-normal"
>
{{ detail?.description }}
</div>
<div id="section1" class="mb-[20px] mt-[34px] flex items-center text-[16px] text-[#333333] font-normal">
<div class="h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div><span class="ml-[10px]">{{ detail?.title }}附件</span></div
>
<div class="box-border w-[1019px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] pa-[24px]">
<div class="border-b-[1px] border-b-[#eee] border-b-solid p-b-[10px]">
{{ detail?.type === 1 ? '图纸' : detail?.type === 2 ? '文本' : '模型' }}中包含的文件
</div>
<div>
<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]">{{ item.title }}</span>
<span v-if="item.size" class="ml-[200px] color-[#999]">{{ item.size || '-' }}</span>
</div>
<el-button
v-if="detail?.downloadId || detail?.points === 0"
type="text"
tag="a"
target="download"
:href="item.url"
@click="handleDownloadFile(item.url, item.title)"
>
下载
</el-button>
</div>
</div>
</div>
<!-- 关联项目 -->
<div class="mb-[20px] mt-[34px] flex items-center text-[16px] text-[#333333] font-normal">
<div class="h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div
><span class="ml-[10px]">关联{{ detail?.type === 1 ? '图纸' : detail?.type === 2 ? '文本' : '模型' }}</span></div
>
<el-row :gutter="20">
<el-col v-for="(item, index) in detail?.relationDraws" :key="index" :span="12">
<CardPicture :item-info="item" />
</el-col>
</el-row>
<el-empty v-if="!detail?.relationDraws?.length" description="暂无数据"></el-empty>
<!-- 关联模型 -->
<div class="mb-[20px] mt-[34px] flex items-center text-[16px] text-[#333333] font-normal">
<div class="h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div
><span class="ml-[10px]">相关{{ detail?.type === 1 ? '图纸' : detail?.type === 2 ? '文本' : '模型' }}推荐</span></div
>
<el-row :gutter="20">
<el-col v-for="(item, index) in relationRecommend" :key="index" :span="12">
<CardPicture :item-info="item" />
</el-col>
</el-row>
<!-- 评论 -->
<CommentSection :relation-id="detail!.id" :project-id="detail!.projectId" />
</div>
<div class="ml-[22px]">
<div class="box-border min-h-[269px] w-[397px] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] pa-[22px]">
<div class="mb-[10px]">图纸ID: {{ detail?.id }}</div>
<div class="mb-[10px]">文件大小{{ detail?.filesInfo?.fileSize || 0 }} </div>
<!-- <div class="mb-10px">图纸版本{{ detail.editionsName }} </div> -->
<div class="mb-[10px]">图纸格式{{ detail?.formatType?.toString() }}</div>
<div class="mb-[10px]">所需金币{{ detail?.points }}金币</div>
<div class="mb-[10px]">发布时间{{ dayjs(detail?.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="mb-[10px]">图纸参数{{ detail?.editTypeName }}</div>
<div class="mb-[10px]">图纸分类{{ detail?.projectTypeName }}</div>
<div class="mb-[10px]">软件分类{{ detail?.editionsName }}</div>
</div>
<div class="mt-[20px] w-[398px] border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid bg-[#FFFFFF]">
<img src="~/assets/images/banner.png" alt="" srcset="" class="w-[100%]" />
<div class="box-border border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid border-t-none bg-[#FFFFFF] pa-[18px]">
<div class="flex flex-wrap items-start">
<div v-if="userInfo.nickname" class="text-[18px] text-[#333333] font-bold">{{ userInfo.nickname }}</div>
<div
v-for="item in userInfo.labels"
:key="item"
class="mb-[10px] ml-[10px] box-border border border-[#1A65FF] rounded-[13px] border-solid px-[8px] py-[3px] color-[#1a65ff]"
>{{ item }}</div
>
</div>
<div v-if="userInfo.description" class="mb-[20px] text-[14px] text-[#333333] font-normal">{{ userInfo.description }}</div>
<!-- 显示作品 粉丝 荣誉证书 -->
<div class="flex items-center gap-[40px]">
<div class="flex items-center">
<div class="h-[20px]">
<img src="~/assets/images/folder.png" alt="works" class="w-[80%]" />
</div>
<div class="ml-[8px] mt-[-4px] text-[14px] text-[#666] font-normal">作品: {{ userInfo.projectCount || 0 }}</div>
</div>
<div class="flex items-center">
<div class="h-[20px]">
<img src="~/assets/images/user4.png" alt="fans" class="w-[80%] rounded-full vertical-top" />
</div>
<div class="relative top-[-3px] ml-[8px] text-[14px] text-[#666] font-normal">粉丝: {{ userInfo.fansCount || 0 }}</div>
</div>
</div>
<!-- 3个图片一排 超过换下一行 -->
<div v-if="userInfo.files?.length" class="mt-[20px] flex flex-wrap gap-[16px]">
<div v-for="i in userInfo.files" :key="i" class="flex-1">
<div
class="box-border h-[200px] w-full overflow-hidden border border-[#E5E7EB] rounded-[8px] border-solid from-[#FFFFFF] to-[#F5F7FA] bg-gradient-to-b p-[16px]"
>
<el-image :src="i.url" fit="cover" alt="" srcset="" class="h-full object-cover" />
</div>
</div>
</div>
<div class="mt-[20px] h-[1px] w-[336px] rounded-[1px] bg-[#EEEEEE]"></div>
<div class="mt-[20px] flex items-center">
<div class="h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div>
<span class="ml-[10px] text-[16px]">最新发布</span>
</div>
<div class="mt-[10px]">
<div
v-for="item in mainWork"
:key="item.id"
class="flex cursor-pointer items-center justify-between px-[10px] py-[10px] hover:bg-[#f5f5f5]"
@click="handleClick(item.id)"
>
<div class="ellipsis text-[15px] text-[#333333] font-normal">{{ item.title }}</div>
<span class="ml-[10px] flex-shrink-0 color-[#999999]">{{ dayjs(item.createTime).format('MM-DD') }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
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'
import ThumBnail from './components/swiper.vue'
import CommentSection from '~/components/comment-section/index.vue'
import { useRoute } from 'vue-router'
import { onMounted } from 'vue'
import type { ProjectRespVO, ProjectDrawPageRespVO, UserExtendSimpleRespDTO, ProjectDrawMemberRespVO } from '~/api/drawe-detail/types'
import useUserStore from '~/stores/user'
const message = useMessage()
const userStore = useUserStore()
// 获取路由参数
const route = useRoute()
const id = route.params.id as string
// 获取详情
// const detail = ref<ProjectRespVO>({} as ProjectRespVO)
const { data: detail, refresh: refreshDetail } = await useAsyncData(`getDetail${id}`, async () => {
const res = await getDetail({ id })
return res.data
})
console.log('==', detail.value)
if (!detail.value) {
throw createError({
statusCode: 404,
statusMessage: 'Page Not Found',
fatal: true,
})
}
// const init = () => {
// getDetail({ id }).then((res) => {
// if (res.code === 0) {
// detail.value = res.data
// // 获取推荐信息
// getRelationRecommendList()
// // 获取用户信息
// handleGetUserInfo()
// // 最新发布
// handleGetMainWork()
// }
// })
// }
// init()
const [{ data: mainWork }, { data: userInfo }, { data: relationRecommend }] = await Promise.all([
getMainWork({ id: detail.value?.id, limit: 10, memberId: detail.value?.ownedUserId }),
getUserInfo({ id: detail.value?.id }),
getRelationRecommend({ type: detail.value?.type, projectType: detail.value?.projectType[0] }),
])
// 获取最新发布
// const mainWork = ref<ProjectDrawMemberRespVO[]>([])
// const handleGetMainWork = () => {
// getMainWork({ id: detail.value?.id, limit: 10, memberId: detail.value?.ownedUserId }).then((res) => {
// if (res.code === 0) {
// mainWork.value = res.data
// }
// })
// }
// 获取用户信息
// const userInfo = ref<UserExtendSimpleRespDTO>({} as UserExtendSimpleRespDTO)
// const handleGetUserInfo = () => {
// getUserInfo({ id: detail.value?.id }).then((res) => {
// if (res.code === 0) {
// userInfo.value = res.data
// }
// })
// }
// 获取关联推荐
// const relationRecommend = ref<ProjectDrawPageRespVO[]>([])
// const getRelationRecommendList = () => {
// getRelationRecommend({ type: detail.value?.type, projectType: detail.value?.projectType[0] }).then((res) => {
// if (res.code === 0) {
// relationRecommend.value = res.data
// }
// })
// }
const handleDownloadPreview = (item: any) => {
// 预览pdf
navigateTo(`/pdf-preview?url=${item.url}`)
}
/** 获取下载类型 */
const getType = (type: number) => {
if (type === 1 || type === 2 || type === 3) {
// 图纸 文本 模型都传1
return 1
}
// 工具箱传
return 2
}
const handleDownload = async () => {
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
if (detail.value?.points === 0) {
scrollTo()
return
}
if (detail.value?.downloadId) {
ElMessage.success('您已获取下载权限')
scrollTo()
return
}
const res = await message.confirm(`是否花费${detail.value?.points}金币下载此资源,是否继续?`, '提示')
if (res) {
createUserProject({ relationId: detail.value?.id, type: getType(detail.value?.type as number) }).then((res) => {
if (res.code === 0) {
ElMessage.success('获取下载权限成功')
detail.value!.downloadId = res.data
scrollTo()
}
})
}
}
const scrollTo = () => {
const element = document.getElementById('section1')
if (element) {
element.scrollIntoView({
behavior: 'smooth',
})
}
}
const handleReport = () => {
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
console.log('举报')
ElMessageBox.prompt('说明内容', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPlaceholder: '请输入举报内容',
inputErrorMessage: '请输入举报内容',
}).then(({ value }) => {
report({
id: detail.value?.id,
title: detail.value?.title,
files: detail.value?.files,
comments: value,
projectId: detail.value?.projectId,
drawId: detail.value?.id,
}).then((res) => {
if (res.code === 0) {
ElMessage.success('举报成功')
}
})
})
}
const handleCollect = async () => {
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
const res = detail.value?.favoriteId
? await deleteProject({ id: detail.value.favoriteId })
: await createContent({
projectId: detail.value?.projectId,
drawId: detail.value?.id,
})
if (res.code === 0) {
ElMessage.success(`${detail.value?.favoriteId ? '取消' : '收藏'}成功`)
refreshDetail()
}
}
const handleClick = (id: string | number) => {
navigateTo(`/down-drawe-detail/${id}`) // 修改为在新窗口打开
}
const handleDownloadFile = (url: string, name: string) => {
downloadFile(url, name)
}
</script>

View File

@ -1,64 +1,32 @@
<template> <template>
<div class="box-border h-631px w-1019px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] pa-30px"> <div class="box-border h-[631px] w-[1019px] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] pa-[30px]">
<swiper <div style="--swiper-navigation-color: #fff; --swiper-pagination-color: #fff" class="swiper mySwiper2">
:style="{ <div class="swiper-wrapper">
'--swiper-navigation-color': '#666', <div class="swiper-slide" v-for="(item, index) in props.data" :key="index">
'--swiper-pagination-color': '#666', <img :src="item.url" />
}"
:space-between="10"
disabled-class=""
:thumbs="{ swiper: thumbsSwiper }"
:modules="modules"
class="mySwiper2"
@swiper="onSwiper"
@slide-change="changeSwiper"
>
<swiper-slide v-for="(item, index) in props.data" :key="index"><el-image fit="cover" :src="item.url" /></swiper-slide>
<div class="swiper-button-prev color-#fff" @click="handlePrev"></div
><!--左箭头如果放置在swiper外面需要自定义样式-->
<div class="swiper-button-next color-#fff" @click="handleNext"></div
><!--右箭头如果放置在swiper外面需要自定义样式-->
</swiper>
</div>
<div class="mb-20px mt-34px flex items-center text-16px text-[#333333] font-normal">
<div class="h-24px w-4px rounded-1px bg-[#1A65FF]"></div
><span class="ml-10px">{{ props.type === 1 ? '图纸' : props.type === 2 ? '文本' : '模型' }}</span></div
>
<div class="box-border h-126px w-1019px border border-[#EEEEEE] rounded-6px border-solid bg-[#FFFFFF] pa-23px">
<swiper
:space-between="10"
:slides-per-view="4"
:free-mode="true"
:watch-slides-progress="true"
:modules="modules"
class="mySwiper"
@swiper="setThumbsSwiper"
>
<swiper-slide v-for="(item, index) in props.data" :key="index">
<div class="Thumbs">
<el-image fit="cover" :src="item.url" />
</div> </div>
</swiper-slide> </div>
</swiper> <div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
</div>
</div>
<div class="mb-[20px] mt-[34px] flex items-center text-[16px] text-[#333333] font-normal">
<div class="h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div>
<span class="ml-[10px]">{{ props.type === 1 ? '图纸' : props.type === 2 ? '文本' : '模型' }}</span>
</div>
<div class="box-border h-[126px] w-[1019px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] pa-[23px]">
<div thumbsSlider="" class="swiper mySwiper">
<div class="swiper-wrapper">
<div class="swiper-slide" v-for="(item, index) in props.data" :key="index">
<img :src="item.url" />
</div>
</div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { PropType} from 'vue' import type { PropType } from 'vue'
import { ref, watch, nextTick } from 'vue' import { ref, watch, nextTick, onMounted } from 'vue'
// @ts-ignore
import { Swiper, SwiperSlide } from 'swiper/vue'
import 'swiper/css'
import 'swiper/css/free-mode'
import 'swiper/css/navigation'
import 'swiper/css/thumbs'
// import required modules
// @ts-ignore
import { FreeMode, Navigation, Thumbs } from 'swiper/modules'
const modules = [FreeMode, Navigation, Thumbs]
const props = defineProps({ const props = defineProps({
data: { data: {
@ -71,50 +39,27 @@
}, },
}) })
const emit = defineEmits(['changeSwiper', 'nextSwiper']) onMounted(() => {
// Initialize Swiper
const thumbsSwiper = ref<any>(null) // @ts-ignore
const useSwiper = ref<any>(null) var swiper = new Swiper('.mySwiper', {
const activeIndex = ref<number>(0) spaceBetween: 10,
slidesPerView: 4,
watch( freeMode: true,
() => props.data, watchSlidesProgress: true,
(value) => { })
if (value.length) { // @ts-ignore
nextTick(() => { var swiper2 = new Swiper('.mySwiper2', {
const thumb_active = document.querySelector(`.mySwiper .swiper-slide`) spaceBetween: 10,
thumb_active?.classList.add('swiper-slide-thumb-active') navigation: {
}) nextEl: '.swiper-button-next',
} prevEl: '.swiper-button-prev',
} },
) thumbs: {
swiper: swiper,
const setThumbsSwiper = (swiper: any) => { },
thumbsSwiper.value = swiper })
} })
const handlePrev = () => {
if (activeIndex.value === 0 || props.data.length === 0) {
emit('nextSwiper', false)
}
useSwiper.value.slidePrev()
}
const handleNext = () => {
if (activeIndex.value === props.data.length - 1 || props.data.length === 0) {
emit('nextSwiper', true)
}
useSwiper.value.slideNext()
}
const onSwiper = (swiper: any) => {
useSwiper.value = swiper
}
const changeSwiper = (e: any) => {
activeIndex.value = e.activeIndex
emit('changeSwiper', e.activeIndex)
}
</script> </script>
<style scoped> <style scoped>
@ -210,4 +155,9 @@
height: 100%; height: 100%;
pointer-events: none; pointer-events: none;
} }
.swiper-button-next,
.swiper-button-prev {
color: #666 !important;
}
</style> </style>

View File

@ -1,350 +0,0 @@
<template>
<KlNavTab />
<div class="ml-auto mr-auto mt-20px w1440">
<div class="flex items-center">
<div class="box-border h-60px w-1019px flex items-center justify-between border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] px-27px py-24px">
<div class="text-20px text-[#333333] font-normal"> {{ detail.title }}</div>
<div class="flex items-center">
<img :src="detail.ownedUserIdInfo?.avatar" alt="" srcset="" class="h-30px w-30px rd-50%" />
<span class="ml-12px color-#999999">by {{ detail.ownedUserIdInfo?.nickName }}</span>
</div>
</div>
<div class="ml-23px flex flex-1 text-18px text-[#FFFFFF] font-normal">
<div class="h-60px w-160px flex cursor-pointer items-center justify-center rounded-8px bg-[#1A65FF]" @click="handleDownload">
<img src="~/assets/images/download.png" alt="" srcset="" class="mr-4px h-22px w-27px" />
{{ detail.points === 0 ? '免费下载' : '立即下载' }}
</div>
<div
v-if="!detail.favoriteId"
class="ml-11px h-60px flex flex-1 cursor-pointer items-center justify-center rounded-8px bg-[#E7B03B]"
@click="handleCollect"
><img src="~/assets/images/collect.png" alt="" srcset="" class="mr-4px h-24px w-24px" /> 收藏</div
>
<div v-else class="ml-11px h-60px flex flex-1 cursor-pointer items-center justify-center rounded-8px bg-[#E7B03B]" @click="handleCollect"
><img src="~/assets/images/wjx2.png" alt="" srcset="" class="mr-4px h-18px w-18px" /> 已收藏</div
>
<div class="ml-11px h-60px flex flex-1 cursor-pointer items-center justify-center rounded-8px bg-[#F56C6C]" @click="handleReport"
><el-icon class="mr-4px mt-4px"><Warning /></el-icon> 举报</div
>
</div>
</div>
</div>
<!-- -->
<div class="ma-auto mt-21px flex">
<div class="w-1019px">
<div>
<ThumBnail :data="detail.coverImages" :type="detail.type" @change-swiper="changeSwiper" @next-swiper="nextSwiper"></ThumBnail>
</div>
<div class="mb-20px mt-34px flex items-center text-16px text-[#333333] font-normal">
<div class="h-24px w-4px rounded-1px bg-[#1A65FF]"></div><span class="ml-10px">{{ detail.title }}描述</span></div
>
<div class="box-border min-h-90px w-1019px border border-[#EEEEEE] rounded-6px border-solid bg-[#FFFFFF] pa-24px text-14px text-[#333333] font-normal">
{{ detail.description }}
</div>
<div id="section1" class="mb-20px mt-34px flex items-center text-16px text-[#333333] font-normal">
<div class="h-24px w-4px rounded-1px bg-[#1A65FF]"></div><span class="ml-10px">{{ detail.title }}附件</span></div
>
<div class="box-border w-1019px border border-[#EEEEEE] rounded-6px border-solid bg-[#FFFFFF] pa-24px">
<div class="border-b-1px border-b-[#eee] border-b-solid p-b-10px">
{{ detail.type === 1 ? '图纸' : detail.type === 2 ? '文本' : '模型' }}中包含的文件
</div>
<div>
<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 v-if="item.size" class="ml-200px color-#999">{{ item.size || '-' }}</span>
</div>
<el-button
v-if="detail.downloadId || detail.points === 0"
type="text"
tag="a"
target="download"
:href="item.url"
@click="handleDownloadFile(item.url, item.title)"
>
下载
</el-button>
</div>
</div>
</div>
<!-- 关联项目 -->
<div class="mb-20px mt-34px flex items-center text-16px text-[#333333] font-normal">
<div class="h-24px w-4px rounded-1px bg-[#1A65FF]"></div
><span class="ml-10px">关联{{ detail.type === 1 ? '图纸' : detail.type === 2 ? '文本' : '模型' }}</span></div
>
<el-row :gutter="20">
<el-col v-for="(item, index) in detail.relationDraws" :key="index" :span="12">
<CardPicture :item-info="item" />
</el-col>
</el-row>
<el-empty v-if="!detail.relationDraws?.length" description="暂无数据"></el-empty>
<!-- 关联模型 -->
<div class="mb-20px mt-34px flex items-center text-16px text-[#333333] font-normal">
<div class="h-24px w-4px rounded-1px bg-[#1A65FF]"></div
><span class="ml-10px">相关{{ detail.type === 1 ? '图纸' : detail.type === 2 ? '文本' : '模型' }}推荐</span></div
>
<el-row :gutter="20">
<el-col v-for="(item, index) in relationRecommend" :key="index" :span="12">
<CardPicture :item-info="item" />
</el-col>
</el-row>
<!-- 评论 -->
<CommentSection :relation-id="detail.id" :project-id="detail.projectId" />
</div>
<div class="ml-22px">
<div class="box-border h-269px w-397px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] pa-22px">
<div class="mb-10px">图纸ID: {{ detail.id }}</div>
<div class="mb-10px">文件大小{{ detail.filesInfo?.fileSize || 0 }} </div>
<!-- <div class="mb-10px">图纸版本{{ detail.editionsName }} </div> -->
<div class="mb-10px">图纸格式{{ detail.formatType?.toString() }}</div>
<div class="mb-10px">所需金币{{ detail.points }}金币</div>
<div class="mb-10px">发布时间{{ dayjs(detail.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="mb-10px">图纸参数{{ detail.editTypeName }}</div>
<div class="mb-10px">图纸分类{{ detail.projectTypeName }}</div>
<div class="mb-10px">软件分类{{ detail.editionsName }}</div>
</div>
<div class="mt-20px w-398px border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid bg-[#FFFFFF]">
<img src="~/assets/images/banner.png" alt="" srcset="" class="w-100%" />
<div class="box-border border border-[#EEEEEE] border-rd-[10px_10px_0px_0px] border-solid border-t-none bg-[#FFFFFF] pa-18px">
<div class="flex flex-wrap items-start">
<div v-if="userInfo.nickname" class="mt-10px text-18px text-[#333333] font-bold">{{ userInfo.nickname }}</div>
<div
v-for="item in userInfo.labels"
:key="item"
class="mb-10px ml-10px box-border border border-[#1A65FF] rounded-13px border-solid px-8px py-3px color-#1a65ff"
>{{ item }}</div
>
</div>
<div v-if="userInfo.description" class="mb-20px text-14px text-[#333333] font-normal">{{ userInfo.description }}</div>
<!-- 显示作品 粉丝 荣誉证书 -->
<div class="flex items-center gap-40px">
<div class="flex items-center">
<div class="h-20px">
<img src="~/assets/images/folder.png" alt="works" class="w-80%" />
</div>
<div class="ml-8px mt--4px text-14px text-[#666] font-normal">作品: {{ userInfo.projectCount || 0 }}</div>
</div>
<div class="flex items-center">
<div class="h-20px">
<img src="~/assets/images/user4.png" alt="fans" class="w-80% rounded-full vertical-top" />
</div>
<div class="relative top--3px ml-8px text-14px text-[#666] font-normal">粉丝: {{ userInfo.fansCount || 0 }}</div>
</div>
</div>
<!-- 3个图片一排 超过换下一行 -->
<div v-if="userInfo.files?.length" class="mt-20px flex flex-wrap gap-16px">
<div v-for="i in userInfo.files" :key="i" class="flex-1">
<div
class="box-border h-200px w-full overflow-hidden border border-[#E5E7EB] rounded-8px border-solid from-[#FFFFFF] to-[#F5F7FA] bg-gradient-to-b p-16px"
>
<el-image :src="i.url" fit="cover" alt="" srcset="" class="h-full object-cover" />
</div>
</div>
</div>
<div class="mt-20px h-1px w-336px rounded-1px bg-[#EEEEEE]"></div>
<div class="mt-20px flex items-center">
<div class="h-24px w-4px rounded-1px bg-[#1A65FF]"></div>
<span class="ml-10px text-16px">最新发布</span>
</div>
<div class="mt-10px">
<div
v-for="item in mainWork"
:key="item.id"
class="flex cursor-pointer items-center justify-between px-10px py-10px hover:bg-#f5f5f5"
@click="handleClick(item.id)"
>
<div class="ellipsis text-15px text-[#333333] font-normal">{{ item.title }}</div>
<span class="ml-10px flex-shrink-0 color-#999999">{{ dayjs(item.createTime).format('MM-DD') }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import dayjs from 'dayjs'
import { downloadFile } from '~/utils/utils'
import { useMessage } from '~/utils/useMessage'
import { Warning } from '@element-plus/icons-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'
import ThumBnail from './components/swiper.vue'
import CommentSection from '~/components/comment-section/index.vue'
import { useRoute } from 'vue-router'
import { ref } from 'vue'
import type { ProjectRespVO, ProjectDrawPageRespVO, UserExtendSimpleRespDTO, ProjectDrawMemberRespVO } from '~/api/drawe-detail/types'
import useUserStore from '~/store/user'
const message = useMessage()
const userStore = useUserStore()
// 获取路由参数
const route = useRoute()
const id = route.query.id as string
// 获取详情
const detail = ref<ProjectRespVO>({} as ProjectRespVO)
const init = () => {
getDetail({ id }).then((res) => {
if (res.code === 0) {
detail.value = res.data
// 获取推荐信息
getRelationRecommendList()
// 获取用户信息
handleGetUserInfo()
// 最新发布
handleGetMainWork()
}
})
}
init()
// 获取最新发布
const mainWork = ref<ProjectDrawMemberRespVO[]>([])
const handleGetMainWork = () => {
getMainWork({ id: detail.value.id, limit: 10, memberId: detail.value.ownedUserId }).then((res) => {
if (res.code === 0) {
mainWork.value = res.data
}
})
}
// 获取用户信息
const userInfo = ref<UserExtendSimpleRespDTO>({} as UserExtendSimpleRespDTO)
const handleGetUserInfo = () => {
getUserInfo({ id: detail.value.id }).then((res) => {
if (res.code === 0) {
userInfo.value = res.data
}
})
}
// 获取关联推荐
const relationRecommend = ref<ProjectDrawPageRespVO[]>([])
const getRelationRecommendList = () => {
getRelationRecommend({ type: detail.value.type, projectType: detail.value.projectType[0] }).then((res) => {
if (res.code === 0) {
relationRecommend.value = res.data
}
})
}
const changeSwiper = (index: number) => {
console.log(index)
}
const nextSwiper = (val: any) => {
console.log(val)
}
const handleDownloadPreview = (item: any) => {
// 预览pdf
navigateTo(`/pdf-preview?url=${item.url}`)
}
/** 获取下载类型 */
const getType = (type: number) => {
if (type === 1 || type === 2 || type === 3) {
// 图纸 文本 模型都传1
return 1
}
// 工具箱传
return 2
}
const handleDownload = async () => {
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
if (detail.value.points === 0) {
scrollTo()
return
}
if (detail.value.downloadId) {
ElMessage.success('您已获取下载权限')
scrollTo()
return
}
const res = await message.confirm(`是否花费${detail.value.points}金币下载此资源,是否继续?`, '提示')
if (res) {
createUserProject({ relationId: detail.value.id, type: getType(detail.value.type) }).then((res) => {
if (res.code === 0) {
ElMessage.success('获取下载权限成功')
detail.value.downloadId = res.data
scrollTo()
}
})
}
}
const scrollTo = () => {
const element = document.getElementById('section1')
if (element) {
element.scrollIntoView({
behavior: 'smooth',
})
}
}
const handleReport = () => {
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
console.log('举报')
ElMessageBox.prompt('说明内容', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPlaceholder: '请输入举报内容',
inputErrorMessage: '请输入举报内容',
}).then(({ value }) => {
report({
id: detail.value.id,
title: detail.value.title,
files: detail.value.files,
comments: value,
projectId: detail.value.projectId,
drawId: detail.value.id,
}).then((res) => {
if (res.code === 0) {
ElMessage.success('举报成功')
}
})
})
}
const handleCollect = async () => {
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
const res = detail.value.favoriteId
? await deleteProject({ id: detail.value.favoriteId })
: await createContent({
projectId: detail.value.projectId,
drawId: detail.value.id,
})
if (res.code === 0) {
ElMessage.success(`${detail.value.favoriteId ? '取消' : '收藏'}成功`)
init()
}
}
const handleClick = (id: string | number) => {
navigateTo(`/down-drawe-detail?id=${id}`) // 修改为在新窗口打开
}
const handleDownloadFile = (url: string, name: string) => {
downloadFile(url, name)
}
</script>

View File

@ -0,0 +1,126 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计图纸下载_CAD设计图纸资源库" />
<KlNavTab active="图纸" :type="1" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->
<KlWallpaperCategory v-model="query" v-model:level="level" :type="1" />
<!-- 推荐栏目 -->
<RecommendedColumnsV2 v-model="query" v-model:result="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="[12, 24, 48]"
:total="result?.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleClickSize"
@current-change="handeClickCurrent"
/>
</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/drawe-components/RecommendedColumnsV2.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 handleClickSize = (val: number) => {
query.value.pageSize = val
// getPage()
navigateTo(`/drawe/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
}
const handeClickCurrent = (val: number) => {
query.value.pageNo = val
// getPage()
navigateTo(`/drawe/${query.value.projectType}/${val}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
}
const { data: result, refresh: getPage } = useAsyncData(
`draw-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(`/drawe/${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

@ -1,7 +1,8 @@
<template> <template>
<!-- 导航 --> <!-- 导航 -->
<SeoHead title="工程设计图纸下载_CAD设计图纸资源库" />
<KlNavTab active="图纸" :type="1" /> <KlNavTab active="图纸" :type="1" />
<div class="ma-auto w-1440px"> <div class="ma-auto w-[1440px]">
<!-- 图纸分类 --> <!-- 图纸分类 -->
<KlWallpaperCategory v-model="query" v-model:level="level" :type="1" /> <KlWallpaperCategory v-model="query" v-model:level="level" :type="1" />
<!-- 推荐栏目 --> <!-- 推荐栏目 -->
@ -9,12 +10,12 @@
<!-- 精选专题 --> <!-- 精选专题 -->
<!-- <FeaturedSpecials></FeaturedSpecials> --> <!-- <FeaturedSpecials></FeaturedSpecials> -->
<!-- 分页 --> <!-- 分页 -->
<div class="mt-10px flex justify-center"> <div class="mt-[10px] flex justify-center">
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
v-model:page-size="query.pageSize" v-model:page-size="query.pageSize"
:page-sizes="[12, 24, 48]" :page-sizes="[12, 24, 48]"
:total="result.total" :total="result?.total"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleClickSize" @size-change="handleClickSize"
@current-change="handeClickCurrent" @current-change="handeClickCurrent"
@ -25,70 +26,87 @@
<script setup lang="ts"> <script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue' import KlNavTab from '~/components/kl-nav-tab/index.vue'
import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue' import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue'
import RecommendedColumnsV2 from './components/RecommendedColumnsV2.vue' import RecommendedColumnsV2 from '~/components/drawe-components/RecommendedColumnsV2.vue'
// import FeaturedSpecials from './components/FeaturedSpecials.vue' // import FeaturedSpecials from './components/FeaturedSpecials.vue'
import { useRoute } from 'vue-router' import { useRoute } from 'vue-router'
import { reactive, watch, ref } from 'vue' import { reactive, watch, ref } from 'vue'
import { page } from '~/api/upnew/index' import { page } from '~/api/upnew/index'
import { getDictTree } from '~/api/home/index'
import type { pageRes, pageReq } from '~/api/upnew/types' import type { pageRes, pageReq } from '~/api/upnew/types'
const route = useRoute() const route = useRoute()
const level = ref( const level = ref(
route.query.level route.query?.valuelevel
? JSON.parse(route.query.level as string) ? JSON.parse(route.query.valuelevel as string)
: [ : [
{ {
id: '0', id: -1,
name: '图纸库', name: '图纸库',
isChildren: false, isChildren: false,
}, },
] ]
) )
const keywords = ref(route.query.keywords as string) const keywords = ref((route.query?.valuekeywords as string) || '')
const query = reactive<pageReq>({ const query = ref<pageReq>({
pageNo: 1, pageNo: 1,
pageSize: 12, pageSize: 12,
projectType: '', projectType: '-1',
editions: '', editions: '-1',
source: '', source: -1,
type: 1, type: 1,
title: keywords.value, title: keywords.value,
}) })
const result = reactive<pageRes>({ // const result = reactive<pageRes>({
list: [], // list: [],
total: 0, // total: 0,
}) // })
// 如果id存在则设置projectType // 如果id存在则设置projectType
if (level.value.length) { if (level.value.length) {
query.projectType = level.value[level.value.length - 1].id || '' // query.value.projectType = level.value[level.value.length - 1].id || ''
} }
const handleClickSize = (val: number) => { const handleClickSize = (val: number) => {
query.pageSize = val query.value.pageSize = val
getPage() // getPage()
navigateTo(`/drawe/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
} }
const handeClickCurrent = (val: number) => { const handeClickCurrent = (val: number) => {
query.pageNo = val query.value.pageNo = val
getPage() // getPage()
navigateTo(`/drawe/${query.value.projectType}/${val}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
} }
const getPage = () => { const { data: result, refresh: getPage } = useAsyncData(
page(query).then((res) => { `draw-page-list-${query.value.projectType}-${query.value.editions}-${query.value.source}-${query.value.pageNo}-${query.value.pageSize}-${query.value.title}`,
const { data, code } = res async () => {
if (code === 0) { const res = await page({
result.list = data.list ...query.value,
result.total = data.total 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
// }
// })
// }
getPage() watch([() => query.value.projectType, () => query.value.editions, () => query.value.source], (val) => {
watch([() => query.projectType, () => query.editions, () => query.source], (val) => {
if (val) { if (val) {
getPage() navigateTo(`/drawe/${query.value.projectType}/1/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
} }
}) })
</script> </script>

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

@ -3,7 +3,7 @@
<KlNavTab active="国外专区" /> <KlNavTab active="国外专区" />
<!-- banneer提示 --> <!-- banneer提示 -->
<BannerTips /> <BannerTips />
<div class="ma-auto w-1440px"> <div class="ma-auto w-[1440px]">
<!-- 图片展示鼠标移上去展示提示语 --> <!-- 图片展示鼠标移上去展示提示语 -->
<!-- <ImageTips /> --> <!-- <ImageTips /> -->
<!-- 推荐栏目 --> <!-- 推荐栏目 -->
@ -11,12 +11,12 @@
<!-- 精选专题 --> <!-- 精选专题 -->
<!-- <FeaturedSpecials></FeaturedSpecials> --> <!-- <FeaturedSpecials></FeaturedSpecials> -->
<!-- 分页 --> <!-- 分页 -->
<div class="mt-10px flex justify-center"> <div class="mt-[10px] flex justify-center">
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
v-model:page-size="query.pageSize" v-model:page-size="query.pageSize"
:page-sizes="[10, 20, 30]" :page-sizes="[10, 20, 30]"
:total="result.total" :total="result?.total"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleChangeSize" @size-change="handleChangeSize"
@current-change="handleChangeCurrent" @current-change="handleChangeCurrent"
@ -26,9 +26,9 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue' 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 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 ImageTips from './components/ImageTips.vue'
import { reactive, watch } from 'vue' import { reactive, watch } from 'vue'
@ -38,42 +38,56 @@
const query = reactive<pageReq>({ const query = reactive<pageReq>({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
projectType: '', projectType: '-1',
editions: '', editions: '-1',
source: '', source: -1,
type: 1, type: 1,
}) })
const result = reactive<pageRes>({ // const result = reactive<pageRes>({
list: [], // list: [],
total: 0, // total: 0,
}) // })
const getPage = () => { const { data: result } = await useAsyncData(`draw-page-list-${Date.now()}`, async () => {
page(query).then((res) => { const res = await page({
const { data, code } = res ...query,
if (code === 0) { editions: query.editions === '-1' ? '' : query.editions,
result.list = data.list source: query.source === -1 ? '' : query.source,
result.total = data.total projectType: query.projectType === '-1' ? '' : query.projectType,
}
}) })
} return res.data
})
// const getPage = () => {
// page(query).then((res) => {
// const { data, code } = res
// if (code === 0) {
// result.list = data.list
// result.total = data.total
// }
// })
// }
getPage() // getPage()
const handleChangeSize = (val: number) => { const handleChangeSize = (val: number) => {
query.pageSize = val query.pageSize = val
query.pageNo = 1 // query.pageNo = 1
getPage() // getPage()
navigateTo(`/foreign/${query.projectType}/${query.pageNo}/${val}/${query.editions}/${query.source}`)
} }
const handleChangeCurrent = (val: number) => { const handleChangeCurrent = (val: number) => {
query.pageNo = val query.pageNo = val
getPage() // getPage()
navigateTo(`/foreign/${query.projectType}/${val}/${query.pageSize}/${query.editions}/${query.source}`)
} }
watch([() => query.projectType, () => query.editions, () => query.source], (val) => { watch([() => query.projectType, () => query.editions, () => query.source], (val) => {
if (val) { if (val) {
getPage() // getPage()
navigateTo(`/foreign/${query.projectType}/1/${query.pageSize}/${query.editions}/${query.source}`)
} }
}) })
</script> </script>

View File

@ -1,63 +1,63 @@
<template> <template>
<div class="flex"> <div class="flex">
<div> <div>
<div class="my-32px mb-20px text-18px text-[#333333] font-normal flex items-center"><img src="~/assets/images/2.png" alt="" srcset="" class="mr-6px" /> 多多排行榜</div> <div class="my-[32px] mb-[20px] text-[18px] text-[#333333] font-normal flex items-center"><img src="~/assets/images/2.png" alt="" srcset="" class="mr-[6px]" /> 多多排行榜</div>
<div class="flex"> <div class="flex">
<div class="ma-auto box-border h-470px w-460px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] px-28px"> <div class="ma-auto box-border h-[470px] w-[460px] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[28px]">
<div class="title-bg ma-auto mb-40px mt-20px">一周图纸作者排行</div> <div class="title-bg ma-auto mb-[40px] mt-[20px]">一周图纸作者排行</div>
<div v-for="(item, index) in topList" :key="item.ownUserId" class="mb-23px flex items-center"> <div v-for="(item, index) in topList" :key="item.ownUserId" class="mb-[23px] flex items-center">
<div class="w-30px text-center" <div class="w-[30px] text-center"
><img ><img
v-if="index === 0 || index === 1 || index === 2" v-if="index === 0 || index === 1 || index === 2"
:src="imagesUrl(index)" :src="imagesUrl(index)"
alt="" alt=""
srcset="" srcset=""
:class="index === 0 ? 'w-20px h-22px' : 'w-28px h-29px'" :class="index === 0 ? 'w-[20px] h-[22px]' : 'w-[28px] h-[29px]'"
/> />
<span v-else class="LiHei (Noncommercial) font-MF text-16px text-[#999999] font-normal">{{ index }}</span> <span v-else class="LiHei (Noncommercial) font-MF text-[16px] text-[#999999] font-normal">{{ index }}</span>
</div> </div>
<div class="ml-20px w-120px flex items-center text-16px text-[#333333] font-normal"> <div class="ml-[20px] w-[120px] flex items-center text-[16px] text-[#333333] font-normal">
<img :src="item?.avatar" alt="" srcset="" class="h-36px w-36px rd-50%" /> <img :src="item?.avatar" alt="" srcset="" class="h-[36px] w-[36px] rd-[50%]" />
<span class="ellipsis1 ml-10px">{{ item.nickname }}</span> <span class="ellipsis1 ml-[10px]">{{ item.nickname }}</span>
</div> </div>
<div class="ml-20px flex text-14px text-[#666666] font-normal"> <div class="ml-[20px] flex text-[14px] text-[#666666] font-normal">
<!-- <el-icon class="text-17px color-#a8abb2!"><Folder /></el-icon> --> <!-- <el-icon class="text-17px color-#a8abb2!"><Folder /></el-icon> -->
<img src="~/assets/images/file.png" alt="" srcset="" class="h-18px" /> <img src="~/assets/images/file.png" alt="" srcset="" class="h-[18px]" />
<div class="ellipsis1 ml-10px">作品{{ item.projectCount || 0 }}</div> <div class="ellipsis1 ml-[10px]">作品{{ item.projectCount || 0 }}</div>
</div> </div>
<div class="ml-20px flex text-14px text-[#666666] font-normal"> <div class="ml-[20px] flex text-[14px] text-[#666666] font-normal">
<!-- <el-icon class="text-17px color-[#e4e7ed!]"><User /></el-icon> --> <!-- <el-icon class="text-17px color-[#e4e7ed!]"><User /></el-icon> -->
<img src="~/assets/images/user4.png" alt="" srcset="" class="h-17px" /> <img src="~/assets/images/user4.png" alt="" srcset="" class="h-[17px]" />
<div class="ellipsis1 ml-10px">粉丝{{ item.fansCount || 0 }}</div> <div class="ellipsis1 ml-[10px]">粉丝{{ item.fansCount || 0 }}</div>
</div> </div>
</div> </div>
<!-- 暂无数据 --> <!-- 暂无数据 -->
<el-empty v-if="!topList.length" description="暂无数据"></el-empty> <el-empty v-if="!topList.length" description="暂无数据"></el-empty>
</div> </div>
<div class="ma-auto ml-18px box-border h-470px w-460px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] px-28px"> <div class="ma-auto ml-[18px] box-border h-[470px] w-[460px] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[28px]">
<div class="title-bg ma-auto mb-40px mt-20px">优质上传作者图纸</div> <div class="title-bg ma-auto mb-[40px] mt-[20px]">优质上传作者图纸</div>
<div v-for="(item, index) in userTopList" :key="index" class="mb-23px flex items-center"> <div v-for="(item, index) in userTopList" :key="index" class="mb-[23px] flex items-center">
<div class="w-30px text-center" <div class="w-[30px] text-center"
><img ><img
v-if="index === 0 || index === 1 || index === 2" v-if="index === 0 || index === 1 || index === 2"
:src="imagesUrl(index)" :src="imagesUrl(index)"
alt="" alt=""
srcset="" srcset=""
:class="index === 0 ? 'w-20px h-22px' : 'w-28px h-29px'" :class="index === 0 ? 'w-[20px] h-[22px]' : 'w-[28px] h-[29px]'"
/> />
<span v-else class="font-MF LiHei (Noncommercial) text-16px text-[#999999] font-normal">{{ index }}</span> <span v-else class="font-MF LiHei (Noncommercial) text-[16px] text-[#999999] font-normal">{{ index }}</span>
</div> </div>
<div class="ml-20px w-120px flex items-center text-16px text-[#333333] font-normal"> <div class="ml-[20px] w-[120px] flex items-center text-[16px] text-[#333333] font-normal">
<img :src="item?.avatar" alt="" srcset="" class="h-36px w-36px rd-50%" /> <img :src="item?.avatar" alt="" srcset="" class="h-[36px] w-[36px] rd-[50%]" />
<span class="ellipsis1 ml-10px">{{ item.nickname }}</span> <span class="ellipsis1 ml-[10px]">{{ item.nickname }}</span>
</div> </div>
<div class="ml-20px flex text-14px text-[#666666] font-normal"> <div class="ml-[20px] flex text-[14px] text-[#666666] font-normal">
<img src="~/assets/images/file.png" alt="" srcset="" class="h-18px" /> <img src="~/assets/images/file.png" alt="" srcset="" class="h-[18px]" />
<div class="ellipsis1 ml-10px">作品{{ item.projectCount || 0 }}</div> <div class="ellipsis1 ml-[10px]">作品{{ item.projectCount || 0 }}</div>
</div> </div>
<div class="ml-20px flex text-14px text-[#666666] font-normal"> <div class="ml-[20px] flex text-[14px] text-[#666666] font-normal">
<img src="~/assets/images/user4.png" alt="" srcset="" class="h-17px" /> <img src="~/assets/images/user4.png" alt="" srcset="" class="h-[17px]" />
<div class="ellipsis1 ml-10px">粉丝{{ item.fansCount || 0 }}</div> <div class="ellipsis1 ml-[10px]">粉丝{{ item.fansCount || 0 }}</div>
</div> </div>
</div> </div>
<!-- 暂无数据 --> <!-- 暂无数据 -->
@ -65,18 +65,18 @@
</div> </div>
</div> </div>
</div> </div>
<div class="ml-63px"> <div class="ml-[63px]">
<div class="my-32px mb-20px text-18px text-[#333333] font-normal flex items-center"><img src="~/assets/images/2.png" alt="" srcset="" class="mr-6px" /> 发布动态</div> <div class="my-[32px] mb-[20px] text-[18px] text-[#333333] font-normal flex items-center"><img src="~/assets/images/2.png" alt="" srcset="" class="mr-[6px]" /> 发布动态</div>
<div class="box-border h-470px w-437px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] p-15px"> <div class="box-border h-[470px] w-[437px] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] p-[15px]">
<div <div
v-for="item in newDrawList" v-for="item in newDrawList"
:key="item.id" :key="item.id"
class="flex cursor-pointer items-center justify-between px-10px py-10px text-15px text-[#333333] font-normal hover:bg-[#f5f5f5]" class="flex cursor-pointer items-center justify-between px-[10px] py-[10px] text-[15px] text-[#333333] font-normal hover:bg-[#f5f5f5]"
@click="handleClick(item)" @click="handleClick(item)"
> >
<div class="ellipsis1 w-70%">{{ item.title }}</div> <div class="ellipsis1 w-[70%]">{{ item.title }}</div>
<div class="text-15px color-#999">{{ dayjs(item.createTime).format('MM-DD') }}</div> <div class="text-[15px] color-[#999]">{{ dayjs(item.createTime).format('MM-DD') }}</div>
</div> </div>
<!-- 暂无数据 --> <!-- 暂无数据 -->
<el-empty v-if="!newDrawList.length" description="暂无数据"></el-empty> <el-empty v-if="!newDrawList.length" description="暂无数据"></el-empty>
@ -105,31 +105,36 @@
}) })
// 最新动态 // 最新动态
const newDrawList = ref<ProjectDrawPageRespVO[]>([]) // const newDrawList = ref<ProjectDrawPageRespVO[]>([])
const handlenewDraw = async () => { // const handlenewDraw = async () => {
const res = await newDraw({ // const res = await newDraw({
// type: 1,
// limit: 11,
// })
// // newDrawList.value = res.data
// }
// handlenewDraw()
const [{data: topList},{ data: userTopList}, {data: newDrawList}] = await Promise.all([ userTop({ type: 1 }), userTop({}),newDraw({
type: 1, type: 1,
limit: 11, limit: 11,
}) })])
// newDrawList.value = res.data
}
handlenewDraw()
const topList = ref<ProjectTrendingScoreUserInfoVO[]>([]) // 最新动态 // const topList = ref<ProjectTrendingScoreUserInfoVO[]>([]) // 最新动态
const userTopList = ref<ProjectTrendingScoreUserInfoVO[]>([]) // 最新动态 // const userTopList = ref<ProjectTrendingScoreUserInfoVO[]>([]) // 最新动态
const handleuserTop = () => { // const handleuserTop = () => {
Promise.all([userTop({ type: 1 }), userTop({})]).then((res) => { // Promise.all([userTop({ type: 1 }), userTop({})]).then((res) => {
// topList.value = res[0].data // topList.value = res[0].data
// userTopList.value = res[1].data // userTopList.value = res[1].data
// console.log('res----',JSON.stringify(res)); // console.log('res----',JSON.stringify(res));
}) // })
} // }
handleuserTop() // handleuserTop()
const handleClick = (item: ProjectDrawPageRespVO) => { const handleClick = (item: ProjectDrawPageRespVO) => {
window.open(`/down-drawe-detail?id=${item.id}`, '_blank') // 修改为在新窗口打开 navigateTo(`/down-drawe-detail/${item.id}`) // 修改为在新窗口打开
} }
</script> </script>

View File

@ -31,7 +31,7 @@
status: 0, status: 0,
}) })
const { data: bannerList } = useAsyncData('get-setting-Page', async () => { const { data: bannerList } = useAsyncData('get-setting-Page-learning-recommendations', async () => {
const res = await getSettingPage(pageReq) const res = await getSettingPage(pageReq)
return res.data return res.data
}) })

View File

@ -1,75 +1,75 @@
<template> <template>
<div class="login-container flex flex-col justify-between"> <div class="login-container flex flex-col justify-between">
<div class="ma-auto mt-25px w-100% flex flex-col items-center"> <div class="ma-auto mt-[25px] w-[100%] flex flex-col items-center">
<el-image <el-image
:src="userStore.userInfoRes.avatar || 'https://tuxixi.oss-cn-chengdu.aliyuncs.com/avater.png'" :src="userStore.userInfoRes.avatar || 'https://tuxixi.oss-cn-chengdu.aliyuncs.com/avater.png'"
alt="" alt=""
srcset="" srcset=""
class="h-69px w-69px rd-50%" class="h-[69px] w-[69px] rd-[50%]"
:class="{ 'cursor-pointer': isLogin }" :class="{ 'cursor-pointer': isLogin }"
fit="cover" fit="cover"
@click="handleUserInfo" @click="handleUserInfo"
/> />
<div class="mt-10px text-16px text-[#333333] font-normal"> <div class="mt-[10px] text-[16px] text-[#333333] font-normal flex items-center">
<img v-if="userStore.userInfoRes.vipLevel === 1" src="~/assets/svg/vip.svg" alt="" class="relative top-12px" />
<img v-if="userStore.userInfoRes.vipLevel === 2" src="~/assets/svg/svip.svg" alt="" class="relative top-12px" />
Hi{{ userStore.userInfoRes.nickname || '欢迎访问~' }} Hi{{ userStore.userInfoRes.nickname || '欢迎访问~' }}
<img v-if="userStore.userInfoRes.vipLevel === 1" src="~/assets/svg/vip.svg" alt="" class="relative top-[1px]" />
<img v-if="userStore.userInfoRes.vipLevel === 2" src="~/assets/svg/svip.svg" alt="" class="relative top-[1px]" />
</div> </div>
</div> </div>
<div v-if="isLogin" class="mt-20px flex flex-col gap-20px px-20px text-14px text-[#333333] font-normal"> <div v-if="isLogin" class="mt-[20px] flex flex-col gap-[20px] px-[20px] text-[14px] text-[#333333] font-normal">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/cad_0 (1).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (1).png" alt="" srcset="" />
<span class="title ml-4px" :title="`${userStaticInfo?.pointCount}`">我的积分: {{ userStaticInfo?.pointCount || 0 }}</span> <span class="title ml-[4px]" :title="`${userStaticInfo?.pointCount}`">我的积分: {{ userStaticInfo?.pointCount || 0 }}</span>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/cad_0 (2).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (2).png" alt="" srcset="" />
<span class="title ml-4px" :title="`${userStaticInfo?.followCount}`">我的收藏: {{ userStaticInfo?.followCount || 0 }}</span> <span class="title ml-[4px]" :title="`${userStaticInfo?.followCount}`">我的收藏: {{ userStaticInfo?.followCount || 0 }}</span>
</div> </div>
</div> </div>
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/cad_0 (3).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (3).png" alt="" srcset="" />
<span class="title ml-4px" :title="`${userStaticInfo?.projectCount}`">我的发布: {{ userStaticInfo?.projectCount || 0 }}</span> <span class="title ml-[4px]" :title="`${userStaticInfo?.projectCount}`">我的发布: {{ userStaticInfo?.projectCount || 0 }}</span>
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/cad_0 (4).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (4).png" alt="" srcset="" />
<span class="title ml-4px" :title="`${userStaticInfo?.downloadCount}`">我的下载: {{ userStaticInfo?.downloadCount || 0 }}</span> <span class="title ml-[4px]" :title="`${userStaticInfo?.downloadCount}`">我的下载: {{ userStaticInfo?.downloadCount || 0 }}</span>
</div> </div>
</div> </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 <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" 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" @click="handleLogin"
>立即登录</div >立即登录</div
> >
<div <div
class="h-37px w-101px cursor-pointer border border-[#DDDDDD] rounded-2px border-solid text-center text-14px text-[#333333] font-normal line-height-37px" class="h-[37px] w-[101px] cursor-pointer border border-[#DDDDDD] rounded-[2px] border-solid text-center text-[14px] text-[#333333] font-normal line-height-[37px]"
@click="handleRegister" @click="handleRegister"
>免费注册</div >免费注册</div
> >
</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 <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" 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" @click="handleDrawe"
>发布图纸</div >发布图纸</div
> >
<div <div
class="h-37px w-101px cursor-pointer border border-[#DDDDDD] rounded-2px border-solid text-center text-14px text-[#333333] font-normal line-height-37px" class="h-[37px] w-[101px] cursor-pointer border border-[#DDDDDD] rounded-[2px] border-solid text-center text-[14px] text-[#333333] font-normal line-height-[37px]"
@click="handleLoginOut" @click="handleLoginOut"
>退出登录</div >退出登录</div
> >
</div> </div>
<div v-if="!isLogin" class="mt-30px flex justify-between px-20px"> <div v-if="!isLogin" class="mt-[30px] flex justify-between px-[20px]">
<img src="~/assets/images/qq-v2.png" alt="QQ登录" class="social-icon" @click="handleLoginQQ" /> <img src="~/assets/images/qq-v2.png" alt="QQ登录" class="social-icon" @click="handleLoginQQ" />
<img src="~/assets/images/weixin-v2.png" alt="微信登录" class="social-icon" @click="handleLoginWechat" /> <img src="~/assets/images/weixin-v2.png" alt="微信登录" class="social-icon" @click="handleLoginWechat" />
<img src="~/assets/images/email-v2.png" alt="邮箱登录" class="social-icon" @click="handleLoginEmail" /> <img src="~/assets/images/email-v2.png" alt="邮箱登录" class="social-icon" @click="handleLoginEmail" />
<img src="~/assets/images/phone-v2.png" alt="手机登录" class="social-icon" @click="handleLoginPhone" /> <img src="~/assets/images/phone-v2.png" alt="手机登录" class="social-icon" @click="handleLoginPhone" />
</div> </div>
<div class="sign-bonus mt-18px" @click="handleSign"> <div class="sign-bonus mt-[18px] cursor-pointer" @click="handleSign">
<img src="~/assets/images/sign.png" alt="签到奖励" class="bonus-image" /> <img src="~/assets/images/sign.png" alt="签到奖励" class="bonus-image" />
</div> </div>
</div> </div>
@ -81,9 +81,9 @@
import { handleLoginQQ, handleLoginWechat } from '~/utils/login' import { handleLoginQQ, handleLoginWechat } from '~/utils/login'
import type { UserStatisticsCountRespVO } from '~/api/personal-center/types' import type { UserStatisticsCountRespVO } from '~/api/personal-center/types'
import { getUserStatistics } from '~/api/personal-center/index' import { getUserStatistics } from '~/api/personal-center/index'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const instance = getCurrentInstance() const app = useNuxtApp()
// 是否登录 // 是否登录
const isLogin = computed(() => { const isLogin = computed(() => {
@ -102,27 +102,19 @@
} }
const handleLogin = () => { const handleLogin = () => {
if (instance) { app.$openLogin()
// instance.appContext.config.globalProperties.$openLogin()
}
} }
const handleRegister = () => { const handleRegister = () => {
if (instance) { app.$openRegister()
// instance.appContext.config.globalProperties.$openRegister()
}
} }
const handleLoginPhone = () => { const handleLoginPhone = () => {
if (instance) { app.$openLogin('verify')
// instance.appContext.config.globalProperties.$openLogin('verify')
}
} }
const handleLoginEmail = () => { const handleLoginEmail = () => {
if (instance) { app.$openLoginEmail()
// instance.appContext.config.globalProperties.$openLoginEmail()
}
} }
const handleUserInfo = () => { const handleUserInfo = () => {
@ -132,7 +124,7 @@
// 发布图纸 // 发布图纸
const handleDrawe = () => { const handleDrawe = () => {
navigateTo('/upnew/drawe') // 修改为在新窗口打开 navigateTo('/upnew') // 修改为在新窗口打开
} }
// 推出登录 // 推出登录
@ -145,11 +137,11 @@
} }
const handleSign = () => { const handleSign = () => {
navigateTo('/sign-page') // 修改为在新窗口打开 navigateTo('/sign-content') // 修改为在新窗口打开
} }
watchEffect(() => { watchEffect(() => {
if (isLogin.value) { if (isLogin.value && import.meta.client) {
fetchUserStatistics() fetchUserStatistics()
} }
}) })

View File

@ -20,55 +20,40 @@
<div <div
class="box-border h-56px w-1219px flex items-center border border-[#EEEEEE] border-solid border-t-none bg-[#FFFFFF] pl-10px line-height-46px" class="box-border h-56px w-1219px flex items-center border border-[#EEEEEE] border-solid border-t-none bg-[#FFFFFF] pl-10px line-height-46px"
> >
<img <img src="~/assets/images/voice.png" alt="" srcset="" class="mr-10px h-15px w-16px" />
src="~/assets/images/voice.png" <Vue3Marquee :duration="10" direction="normal" pause-on-hover>
alt="" · 经典来袭SolidWorks装配经典案例之气动发动机
srcset=""
class="mr-10px h-15px w-16px"
/>
<Vue3Marquee :duration="10" direction="normal" pause-on-hover
>· 经典来袭SolidWorks装配经典案例之气动发动机
</Vue3Marquee> </Vue3Marquee>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref } from "vue"; import { reactive, ref } from 'vue'
import LoginForm from "./LoginForm.vue"; import LoginForm from './LoginForm.vue'
import { Vue3Marquee } from "vue3-marquee"; import { Vue3Marquee } from 'vue3-marquee'
import { getSettingPage } from "~/api/home/index"; import { getSettingPage } from '~/api/home/index'
import type { PageResultIndexSettingRespVO } from "~/api/home/type";
const pageReq = reactive({ const pageReq = reactive({
type: 1, type: 1,
status: 0, status: 0,
}); })
const { data: bannerList } = useAsyncData("get-setting-Page", async () => { const { data: bannerList } = useAsyncData('get-setting-Page-main-content', async () => {
const res = await getSettingPage(pageReq); const res = await getSettingPage(pageReq)
return res.data; return res.data
}); })
// const bannerList = ref<PageResultIndexSettingRespVO[]>([]) const handleClick = (url: string) => {
// const getBanner = async () => { if (url) {
// const res = await getSettingPage(pageReq) navigateTo(url)
// if (res.code === 0) { }
// bannerList.value = res.data
// }
// }
// getBanner()
const handleClick = (url: string) => {
if (url) {
navigateTo(url);
} }
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.main-content { .main-content {
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -1,13 +1,13 @@
<template> <template>
<div class="mt-34px w-100%"> <div class="mt-[34px] w-[100%]">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<KlTabBar v-model="query.type" :data="tabBar" :show-icon="true" /> <KlTabBar v-model="query.type" :data="tabBar" :show-icon="true" />
<div class="flex gap-15px"> <div class="flex gap-[15px]">
<span <span
v-for="(item, index) in projectTypeList" v-for="(item, index) in projectTypeList"
:key="index" :key="index"
:class="{ 'color-#1A65FF! border-b-2px border-b-solid border-b-[#1A65FF] rounded-1px pb-3px': query.projectTypeDay === item.id }" :class="{ 'color-[#1A65FF!] border-b-[2px] border-b-solid border-b-[#1A65FF] rounded-[1px] pb-[3px]': query.projectTypeDay === item.id }"
class="cursor-pointer text-14px color-#333333" class="cursor-pointer text-[14px] color-[#333333]"
@mouseenter="handleHover(item)" @mouseenter="handleHover(item)"
@click="handleClickType(item)" @click="handleClickType(item)"
> >
@ -15,7 +15,7 @@
</span> </span>
</div> </div>
</div> </div>
<div class="content mt-10px flex"> <div class="content mt-[10px] flex">
<!-- <div class="sider"> <!-- <div class="sider">
<div class="box-border h-100% h-55px w-221px flex items-center rounded-lg bg-[#1A65FF] pl-24px text-white"> <div class="box-border h-100% h-55px w-221px flex items-center rounded-lg bg-[#1A65FF] pl-24px text-white">
<img src="~/assets/images/1.png" alt="" srcset="" /> <img src="~/assets/images/1.png" alt="" srcset="" />
@ -40,23 +40,23 @@
<el-empty v-if="projectTypeList.length === 0" description="暂无数据"></el-empty> <el-empty v-if="projectTypeList.length === 0" description="暂无数据"></el-empty>
</div> </div>
</div> --> </div> -->
<div class="box-border w-100%"> <div class="box-border w-[100%]">
<div class="grid grid-cols-5 gap-17px"> <div class="grid grid-cols-5 gap-[17px]">
<div <div
v-for="item in hotTopList" v-for="item in hotTopList"
:key="item.id" :key="item.id"
class="cursor-pointer border border-[#EEEEEE] rounded-1px border-solid bg-[#FFFFFF]" class="cursor-pointer border border-[#EEEEEE] rounded-[1px] border-solid bg-[#FFFFFF]"
@click="handleCardClick(item)" @click="handleCardClick(item)"
> >
<div> <div>
<div class="h-212px w-100%"> <div class="h-[212px] w-[100%]">
<el-image :src="item.iconUrl" alt="" srcset="" fit="cover" class="block h-100% w-100%" /> <el-image :src="item.iconUrl" alt="" srcset="" fit="cover" class="block h-[100%] w-[100%]" />
</div> </div>
<div class="ellipsis mx-20px my-11px text-14px color-#333333 font-normal">{{ item.title }}</div> <div class="ellipsis mx-[20px] my-[11px] text-[14px] color-[#333333] font-normal">{{ item.title }}</div>
</div> </div>
</div> </div>
</div> </div>
<el-empty v-if="hotTopList.length === 0" description="暂无数据"></el-empty> <el-empty v-if="hotTopList?.length === 0" description="暂无数据"></el-empty>
</div> </div>
</div> </div>
<!-- <div class="morefont-400 text-16px text-[#1A65FF] text-right cursor-pointer"> 查看更多 >> </div> --> <!-- <div class="morefont-400 text-16px text-[#1A65FF] text-right cursor-pointer"> 查看更多 >> </div> -->
@ -70,6 +70,11 @@
import { hotTop, hotTag } from '~/api/home/index' import { hotTop, hotTag } from '~/api/home/index'
import type { ProjectDrawPageRespVO, ProjectDictNodeVO } from '~/api/home/type' import type { ProjectDrawPageRespVO, ProjectDictNodeVO } from '~/api/home/type'
/** tianjia key 接口刷新了 页面没变化 */
definePageMeta({
key: 'PopularDrawings', // 页面的唯一标识,用于区分不同的页面
})
/** 请求参数 */ /** 请求参数 */
const query = reactive({ const query = reactive({
type: 1, type: 1,
@ -105,7 +110,7 @@
/** 点击卡片 */ /** 点击卡片 */
const handleCardClick = (item: ProjectDrawPageRespVO) => { const handleCardClick = (item: ProjectDrawPageRespVO) => {
// 跳转到下载详情页 并且是单独开标签 // 跳转到下载详情页 并且是单独开标签
navigateTo(`/down-drawe-detail?id=${item.id}`) // 修改为在新窗口打开 navigateTo(`/down-drawe-detail/${item.id}`) // 修改为在新窗口打开
} }
const handleClickType = (primary?: ProjectDictNodeVO, secondary?: ProjectDictNodeVO) => { const handleClickType = (primary?: ProjectDictNodeVO, secondary?: ProjectDictNodeVO) => {
@ -127,48 +132,83 @@
} }
/** 热门数据 */ /** 热门数据 */
const hotTopList = ref<ProjectDrawPageRespVO[]>([]) // const hotTopList = ref<ProjectDrawPageRespVO[]>([])
const getHotTop = () => { // const getHotTop = () => {
hotTop({ // hotTop({
type: query.type, // type: query.type,
projectType: query.projectTypeDay, // projectType: query.projectTypeDay,
isDomestic: query.isDomestic, // isDomestic: query.isDomestic,
projectTypeTop: query.projectTypeDay, // projectTypeTop: query.projectTypeDay,
}).then((res) => { // }).then((res) => {
if (res.code === 0) { // if (res.code === 0) {
hotTopList.value = res.data || [] // hotTopList.value = res.data || []
} // }
}) // })
} // }
/** 获取分类下拉框 */ const { data: projectTypeList, refresh: getParent } = await useAsyncData('projectTypeListChildren-PopularDrawings-popularDrawings', async () => {
const projectTypeList = ref<ProjectDictNodeVO[]>([]) const res = await hotTag({
const projectTypeListChildren = ref<ProjectDictNodeVO[]>([])
const getParent = () => {
hotTag({
type: query.type, type: query.type,
limit: 6, limit: 6,
size: 1000, size: 1000,
}).then((res) => {
if (res.code === 0) {
if (Array.isArray(res.data) && res.data.length > 0) {
projectTypeList.value = [...res.data, { id: '0', name: '全部分类', children: [] }]
projectTypeListChildren.value = res.data[0].children || []
query.projectTypeDay = res.data[0].id || ''
query.projectType = res.data[0]!.children?.[0]!.id || ''
// 热门数据
getHotTop()
} else {
hotTopList.value = []
}
}
}) })
} const all = [{ id: '0', name: '全部分类', children: [] }]
if (Array.isArray(res.data) && res.data?.length > 0) {
const total = [...res.data, ...all]
// query.projectTypeDay = total[0]?.id || ''
// query.projectType = total[0]!.children?.[0]?.id || ''
return total
}
return []
})
// if (!projectTypeList.value?.length) {
// throw createError({
// statusCode: 404,
// statusMessage: 'Page Not Found',
// fatal: true,
// })
// }
const { data: hotTopList, refresh: getHotTop } = useAsyncData('hotTop-PopularDrawings-popularDrawings', async () => {
const res = await hotTop({
type: query.type,
// @ts-ignore
projectType: query.projectTypeDay || projectTypeList.value[0].id,
isDomestic: query.isDomestic || 1,
projectTypeTop: query.projectTypeDay || projectTypeList.value?.[0].id,
})
return res.data || []
})
/** 获取分类下拉框 */
// const projectTypeList = ref<ProjectDictNodeVO[]>([])
// // const projectTypeListChildren = ref<ProjectDictNodeVO[]>([])
// const getParent = () => {
// hotTag({
// type: query.type,
// limit: 6,
// size: 1000,
// }).then((res) => {
// if (res.code === 0) {
// if (Array.isArray(res.data) && res.data.length > 0) {
// projectTypeList.value = [...res.data, { id: '0', name: '全部分类', children: [] }]
// projectTypeListChildren.value = res.data[0].children || []
// query.projectTypeDay = res.data[0].id || ''
// query.projectType = res.data[0]!.children?.[0]!.id || ''
// // 热门数据
// getHotTop()
// } else {
// hotTopList.value = []
// }
// }
// })
// }
const handleHover = (item: ProjectDictNodeVO) => { const handleHover = (item: ProjectDictNodeVO) => {
query.projectTypeDay = item.id || '' query.projectTypeDay = item.id || ''
if (item.name === '全部分类') return if (item.name === '全部分类') return
projectTypeListChildren.value = item.children || [] // projectTypeListChildren.value = item.children || []
query.projectType = item.children?.[0].id || '' query.projectType = item.children?.[0].id || ''
// 热门数据 // 热门数据
getHotTop() getHotTop()
@ -182,11 +222,12 @@
watch( watch(
() => query.type, () => query.type,
() => { async () => {
getParent() await getParent()
await getHotTop()
}, },
{ {
immediate: true, // immediate: true,
} }
) )
</script> </script>

View File

@ -54,8 +54,8 @@
// }) // })
// } // }
const { data: recommendTopList, refresh: getRecommendTop} = useAsyncData('recommendTop', async () => { const { data: recommendTopList, refresh: getRecommendTop} = useAsyncData('recommendTop-RecommendedColumns', async () => {
// @ts-ignore
const res = await recommendTop(query) const res = await recommendTop(query)
return res.data || [] return res.data || []
}) })

View File

@ -11,30 +11,16 @@
<!-- @click="handleSubmenuClick(item)" --> <!-- @click="handleSubmenuClick(item)" -->
{{ item.name }} {{ item.name }}
<!-- 悬浮浮窗 --> <!-- 悬浮浮窗 -->
<div <div v-if="activeIndex === index" class="submenu-panel" :style="{ top: submenuTop + 'px' }" @mouseenter="keepSubmenuVisible" @mouseleave="hideSubMenu">
v-if="activeIndex === index"
class="submenu-panel"
:style="{ top: submenuTop + 'px' }"
@mouseenter="keepSubmenuVisible"
@mouseleave="hideSubMenu"
>
<div class="submenu-content"> <div class="submenu-content">
<!-- <div class="text-right">更多</div> --> <!-- <div class="text-right">更多</div> -->
<div class="submenu-group"> <div class="submenu-group">
<template v-for="group in item.children" :key="group.id"> <template v-for="group in item.children" :key="group.id">
<div <div class="submenu-group-title" @click.stop="handleSubmenuClick(group)">
class="submenu-group-title"
@click.stop="handleSubmenuClick(group)"
>
{{ group.name }} {{ group.name }}
</div> </div>
<div class="submenu-group-items"> <div class="submenu-group-items">
<div <div v-for="sub in group.children" :key="sub.id" class="submenu-item" @click.stop="handleSubmenuClick(sub)">
v-for="sub in group.children"
:key="sub.id"
class="submenu-item"
@click.stop="handleSubmenuClick(group, sub)"
>
{{ sub.name }} {{ sub.name }}
</div> </div>
</div> </div>
@ -47,134 +33,126 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from "vue"; import { ref, onMounted } from 'vue'
import type { ComponentPublicInstance } from "vue"; import type { ComponentPublicInstance } from 'vue'
import { tab2 } from "~/api/home/index"; import { tab2 } from '~/api/home/index'
import type { ProjectDictNodeVO } from "~/api/home/type"; import type { ProjectDictNodeVO } from '~/api/home/type'
const activeIndex = ref(-1); const activeIndex = ref(-1)
const submenuTop = ref(0); const submenuTop = ref(0)
// const submenuLeft = ref(0) // const submenuLeft = ref(0)
const sideMenu = ref(); const sideMenu = ref()
const menuItemRefs = ref<HTMLElement[]>([]); const menuItemRefs = ref<HTMLElement[]>([])
// const menuItems = ref<ProjectDictNodeVO[]>([]); // const menuItems = ref<ProjectDictNodeVO[]>([]);
// 等待数据加载完成再进行渲染 :courseData 对data进行别名赋值 // 等待数据加载完成再进行渲染 :courseData 对data进行别名赋值
const { data: menuItems, pending, error } = useAsyncData( const {
'tab2-list', data: menuItems,
async () => { pending,
error,
} = useAsyncData('tab2-list', async () => {
const res = await tab2() const res = await tab2()
const arr = []; const arr = []
for (let i = 0; i < res.data?.length; i += 2) { for (let i = 0; i < res.data?.length; i += 2) {
arr.push({ arr.push({
children: res.data.slice(i, i + 2), children: res.data.slice(i, i + 2),
name: getName(res.data.slice(i, i + 2)), name: getName(res.data.slice(i, i + 2)),
}); })
} }
return arr return arr
} })
)
const showSubMenu = (index: number) => {
// if (menuItems.value.length === index + 1) {
// return
const showSubMenu = (index: number) => { // }
// if (menuItems.value.length === index + 1) { activeIndex.value = index
// return const dom = menuItemRefs.value[index].getBoundingClientRect()
// } console.log(dom)
activeIndex.value = index; // submenuTop.value = window.scrollY
const dom = menuItemRefs.value[index].getBoundingClientRect();
console.log(dom);
// submenuTop.value = window.scrollY
};
const hideSubMenu = () => {
activeIndex.value = -1;
};
const handleSubmenuClick = (
primary?: ProjectDictNodeVO,
secondary?: ProjectDictNodeVO,
tertiary?: ProjectDictNodeVO
) => {
const normal = { id: "0", name: "图纸库", isChildren: false };
const level = [primary, secondary, tertiary]
.filter(Boolean)
.map((item) => ({
id: item?.id,
name: item?.name,
isChildren: item?.children?.length ? false : true,
}));
if (primary?.id === "0") {
level[0].name = "图纸库";
} else {
level.unshift(normal);
} }
navigateTo(`/drawe?level=${JSON.stringify(level)}`); const hideSubMenu = () => {
}; activeIndex.value = -1
const keepSubmenuVisible = () => {
// activeIndex.value = activeIndex.value // 保持当前索引
};
const setMenuItemRef = (
el: Element | ComponentPublicInstance | null,
index: number
) => {
if (el && "offsetTop" in el) {
menuItemRefs.value[index] = el as HTMLElement;
} }
};
const getName = (arr: any[]) => { const handleSubmenuClick = (primary: ProjectDictNodeVO) => {
if (arr.length === 1) { // const normal = { id: "0", name: "图纸库", isChildren: false };
return arr[0].name; // const level = [primary, secondary, tertiary]
} else { // .filter(Boolean)
return arr[0].name + " / " + arr[1].name; // .map((item) => ({
// id: item?.id,
// name: item?.name,
// isChildren: item?.children?.length ? false : true,
// }));
// if (primary?.id === "0") {
// level[0].name = "图纸库";
// } else {
// level.unshift(normal);
// }
// navigateTo(`/drawe?level=${JSON.stringify(level)}`)
navigateTo(`/drawe/${primary.id}/1/12/-1`)
} }
};
const init = () => { const keepSubmenuVisible = () => {
// 获取标签 // activeIndex.value = activeIndex.value // 保持当前索引
// getLabel() }
};
onMounted(() => { const setMenuItemRef = (el: Element | ComponentPublicInstance | null, index: number) => {
// init(); if (el && 'offsetTop' in el) {
}); menuItemRefs.value[index] = el as HTMLElement
}
}
const getName = (arr: any[]) => {
if (arr.length === 1) {
return arr[0].name
} else {
return arr[0].name + ' / ' + arr[1].name
}
}
const init = () => {
// 获取标签
// getLabel()
}
onMounted(() => {
// init();
})
</script> </script>
<style scoped> <style scoped>
.side-menu { .side-menu {
width: 221px; width: 221px;
background-color: #fff; background-color: #fff;
/* padding: 10px 0; */ /* padding: 10px 0; */
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */ /* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
/* overflow: visible; */ /* overflow: visible; */
box-sizing: border-box; box-sizing: border-box;
position: relative; /* 添加定位上下文 */ position: relative; /* 添加定位上下文 */
/* overflow-y: auto; */ /* overflow-y: auto; */
/* 隐藏滚动条 */ /* 隐藏滚动条 */
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */ -ms-overflow-style: none; /* IE and Edge */
z-index: 9; z-index: 9;
/* padding-top: 10px; */ /* padding-top: 10px; */
} }
/* 隐藏 Webkit 浏览器的滚动条 */ /* 隐藏 Webkit 浏览器的滚动条 */
.side-menu::-webkit-scrollbar { .side-menu::-webkit-scrollbar {
display: none; display: none;
} }
/* 鼠标悬停时显示滚动条 */ /* 鼠标悬停时显示滚动条 */
.side-menu:hover { .side-menu:hover {
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* IE and Edge */ -ms-overflow-style: none; /* IE and Edge */
} }
/* .side-menu:hover::-webkit-scrollbar { /* .side-menu:hover::-webkit-scrollbar {
display: block; display: block;
width: 8px !important; width: 8px !important;
} }
@ -188,44 +166,44 @@ onMounted(() => {
background-color: #fff; background-color: #fff;
} */ } */
.menu-item { .menu-item {
/* position: relative; */ /* position: relative; */
text-align: center; text-align: center;
padding: 4px 24px; padding: 10px 24px;
cursor: pointer; cursor: pointer;
/* transition: all 0.3s ease; */ /* transition: all 0.3s ease; */
color: #333; color: #333;
font-size: 14px; font-size: 14px;
border: 1px solid transparent; border: 1px solid transparent;
z-index: 9; z-index: 9;
} }
.menu-item:hover { .menu-item:hover {
background-color: #fff; background-color: #fff;
color: #1890ff; color: #1890ff;
border: 1px solid #1890ff; border: 1px solid #1890ff;
border-right: transparent !important; border-right: transparent !important;
} }
.submenu-panel { .submenu-panel {
position: absolute; position: absolute;
left: 220px; left: 220px;
top: 0; top: 0;
width: 957px; width: 957px;
background: #fff; background: #fff;
/* box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); */ /* box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); */
border: 1px solid #1890ff; border: 1px solid #1890ff;
/* border-radius: 4px; */ /* border-radius: 4px; */
display: flex; display: flex;
padding: 20px; padding: 20px;
z-index: -1; z-index: -1;
box-sizing: border-box; box-sizing: border-box;
min-height: 480px; min-height: 480px;
overflow-y: auto; overflow-y: auto;
} }
.submenu-panel::before { .submenu-panel::before {
/* content: ''; /* content: '';
position: absolute; position: absolute;
left: -6px; left: -6px;
top: 50%; top: 50%;
@ -236,61 +214,61 @@ onMounted(() => {
border-bottom: 8px solid transparent; border-bottom: 8px solid transparent;
border-right: 8px solid #fff; border-right: 8px solid #fff;
filter: drop-shadow(-2px 0 2px rgba(0, 0, 0, 0.1)); */ filter: drop-shadow(-2px 0 2px rgba(0, 0, 0, 0.1)); */
} }
.submenu-content { .submenu-content {
/* flex: 1; */ /* flex: 1; */
/* display: grid; */ /* display: grid; */
/* grid-template-columns: repeat(3, 1fr); */ /* grid-template-columns: repeat(3, 1fr); */
/* gap: 15px; */ /* gap: 15px; */
width: 100%; width: 100%;
display: flex;
flex-direction: column;
.submenu-group {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; .submenu-group {
flex-wrap: wrap; display: flex;
-webkit-column-break-inside: avoid; /* 兼容webkit内核 */ flex-direction: column;
/* border: 1px solid #e6e8eb; */
.submenu-group-title {
font-size: 15px;
/* font-weight: 700; */
padding: 10px 16px;
color: #000;
font-weight: 600;
transition: all 0.2s;
/* border-bottom: 1px solid #e6e8eb; */
}
.submenu-group-title:hover {
color: #1890ff;
/* border-bottom: 1px solid #e6e8eb; */
}
.submenu-group-items {
display: flex !important;
flex-direction: row;
align-items: flex-start; align-items: flex-start;
flex-wrap: wrap; flex-wrap: wrap;
color: #666; -webkit-column-break-inside: avoid; /* 兼容webkit内核 */
padding: 0px 16px; /* border: 1px solid #e6e8eb; */
gap: 10px; .submenu-group-title {
.submenu-item { font-size: 15px;
cursor: pointer; /* font-weight: 700; */
padding: 10px 16px;
color: #000;
font-weight: 600;
transition: all 0.2s;
/* border-bottom: 1px solid #e6e8eb; */
}
.submenu-group-title:hover {
color: #1890ff;
/* border-bottom: 1px solid #e6e8eb; */
}
.submenu-group-items {
display: flex !important;
flex-direction: row;
align-items: flex-start;
flex-wrap: wrap;
color: #666;
padding: 0px 16px;
gap: 10px;
.submenu-item {
cursor: pointer;
}
} }
} }
} }
}
.submenu-item { .submenu-item {
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
display: inline-block; /* 改为行内块元素 */ display: inline-block; /* 改为行内块元素 */
/* padding: 8px 12px; */ /* padding: 8px 12px; */
padding-right: 12px; padding-right: 12px;
/* padding-bottom: 8px; */ /* padding-bottom: 8px; */
} }
.submenu-item:hover { .submenu-item:hover {
color: #1890ff; color: #1890ff;
} }
</style> </style>

View File

@ -21,8 +21,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import KlSearch from '~/layout/kl-search/index.vue' import KlSearch from '~/components/kl-search/index.vue'
import KlMenuV2 from '~/layout/kl-menus-v2/index.vue' import KlMenuV2 from '~/components/kl-menus-v2/index.vue'
import SideMenu from '~/pages/home/components/SideMenu.vue' import SideMenu from '~/pages/home/components/SideMenu.vue'
import MainContent from '~/pages/home/components/MainContent.vue' import MainContent from '~/pages/home/components/MainContent.vue'
import RecommendedColumns from '~/pages/home/components/RecommendedColumns.vue' import RecommendedColumns from '~/pages/home/components/RecommendedColumns.vue'

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

@ -0,0 +1,126 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计模型下载_CAD设计图纸资源库" />
<KlNavTab active="模型" :type="3" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->
<KlWallpaperCategory v-model="query" v-model:level="level" :type="3" />
<!-- 推荐栏目 -->
<RecommendedColumnsV2 v-model="query" v-model:result="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="[12, 24, 48]"
:total="result?.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleClickSize"
@current-change="handeClickCurrent"
/>
</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/model-components/RecommendedColumnsV2.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: 3,
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 handleClickSize = (val: number) => {
query.value.pageSize = val
// getPage()
navigateTo(`/model/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
}
const handeClickCurrent = (val: number) => {
query.value.pageNo = val
// getPage()
navigateTo(`/model/${query.value.projectType}/${val}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
}
const { data: result, refresh: getPage } = useAsyncData(
`draw-page-list-${query.value.projectType}-${query.value.editions}-${query.value.source}-${query.value.pageNo}-${query.value.pageSize}`,
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(`/model/${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

@ -1,7 +1,8 @@
<template> <template>
<!-- 导航 --> <!-- 导航 -->
<SeoHead title="工程设计模型下载_CAD设计图纸资源库" />
<KlNavTab active="模型" :type="3" /> <KlNavTab active="模型" :type="3" />
<div class="ma-auto w-1440px"> <div class="ma-auto w-[1440px]">
<!-- 图纸分类 --> <!-- 图纸分类 -->
<KlWallpaperCategory v-model="query" v-model:level="level" :type="3" /> <KlWallpaperCategory v-model="query" v-model:level="level" :type="3" />
<!-- 推荐栏目 --> <!-- 推荐栏目 -->
@ -9,12 +10,12 @@
<!-- 精选专题 --> <!-- 精选专题 -->
<!-- <FeaturedSpecials></FeaturedSpecials> --> <!-- <FeaturedSpecials></FeaturedSpecials> -->
<!-- 分页 --> <!-- 分页 -->
<div class="mt-10px flex justify-center"> <div class="mt-[10px] flex justify-center">
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
v-model:page-size="query.pageSize" v-model:page-size="query.pageSize"
:page-sizes="[12, 24, 48]" :page-sizes="[12, 24, 48]"
:total="result.total" :total="result?.total"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
@size-change="handleChangeSize" @size-change="handleChangeSize"
@current-change="handleChangeCurrent" @current-change="handleChangeCurrent"
@ -25,7 +26,7 @@
<script setup lang="ts"> <script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue' import KlNavTab from '~/components/kl-nav-tab/index.vue'
import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue' import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue'
import RecommendedColumnsV2 from './components/RecommendedColumnsV2.vue' import RecommendedColumnsV2 from '~/components/model-components/RecommendedColumnsV2.vue'
// import FeaturedSpecials from './components/FeaturedSpecials.vue' // import FeaturedSpecials from './components/FeaturedSpecials.vue'
import { reactive, watch, ref } from 'vue' import { reactive, watch, ref } from 'vue'
@ -38,57 +39,76 @@
? JSON.parse(route.query.level as string) ? JSON.parse(route.query.level as string)
: [ : [
{ {
id: '0', id: -1,
name: '模型库', name: '模型库',
isChildren: false, isChildren: false,
}, },
] ]
) )
const query = reactive<pageReq>({ const query = ref<pageReq>({
pageNo: 1, pageNo: 1,
pageSize: 12, pageSize: 12,
projectType: '', projectType: '-1',
editions: '', editions: '-1',
source: '', source: -1,
type: 3, type: 3,
}) })
const result = reactive<pageRes>({ // const result = reactive<pageRes>({
list: [], // list: [],
total: 0, // total: 0,
}) // })
// 如果id存在则设置projectType // 如果id存在则设置projectType
if (level.value.length) { if (level.value.length) {
query.projectType = level.value[level.value.length - 1].id || '' // query.projectType = level.value[level.value.length - 1].id || ''
} }
const getPage = () => { const { data: result } = useAsyncData(
page(query).then((res) => { `draw-page-list-${query.value.projectType}-${query.value.editions}-${query.value.source}-${query.value.pageNo}-${query.value.pageSize}`,
const { data, code } = res async () => {
if (code === 0) { const res = await page({
result.list = data.list ...query.value,
result.total = data.total 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,
}
)
getPage() // const getPage = () => {
// page(query).then((res) => {
// const { data, code } = res
// if (code === 0) {
// result.list = data.list
// result.total = data.total
// }
// })
// }
const handleChangeSize = (size: number) => { // getPage()
query.pageSize = size
query.pageNo = 1 const handleChangeSize = (val: number) => {
getPage() query.value.pageSize = val
// query.pageNo = 1
// getPage()
navigateTo(`/model/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
} }
const handleChangeCurrent = (current: number) => { const handleChangeCurrent = (current: number) => {
query.pageNo = current query.value.pageNo = current
getPage() // getPage()
navigateTo(`/model/${query.value.projectType}/${current}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
} }
watch([() => query.projectType, () => query.editions, () => query.source], (val) => { watch([() => query.value.projectType, () => query.value.editions, () => query.value.source], (val) => {
if (val) { if (val) {
getPage() // getPage()
navigateTo(`/model/${query.value.projectType}/1/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
} }
}) })
</script> </script>

View File

@ -1,70 +1,72 @@
<template> <template>
<KlNavTab /> <KlNavTab />
<div class="ma-auto w-1198px flex justify-between"> <div class="ma-auto w-[1198px] flex justify-between">
<div class="left mt-25px box-border h-370px w-260px border border-[#EEEEEE] rounded-4px border-solid bg-[#FFFFFF] text-15px text-[#333333] font-medium"> <div
<nuxt-link to="/personal/center/info" class="flex items-center justify-between py-14px"> class="left mt-[25px] box-border h-[370px] w-[260px] border border-[#EEEEEE] rounded-[4px] border-solid bg-[#FFFFFF] text-[15px] text-[#333333] font-medium"
<div class="flex items-center pl-20px"> >
<img v-if="route.path.startsWith('/personal/center/info')" src="~/assets/images/user3.png" alt="" srcset="" class="h-20px" /> <nuxt-link to="/personal-Center/info" class="flex items-center justify-between py-[14px]">
<img v-else src="~/assets/images/个人.png" alt="" srcset="" class="h-20px" /> <div class="flex items-center pl-[20px]">
<span class="ml-10px">个人中心</span> <img v-if="route.path.startsWith('/personal-Center/info')" src="~/assets/images/user3.png" alt="" srcset="" class="h-[20px]" />
<img v-else src="~/assets/images/个人.png" alt="" srcset="" class="h-[20px]" />
<span class="ml-[10px]">个人中心</span>
</div> </div>
<div class="pr-20px"> <div class="pr-[20px]">
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</div> </div>
</nuxt-link> </nuxt-link>
<nuxt-link to="/personal/profile" class="flex items-center justify-between py-14px"> <nuxt-link to="/personal-Center/personal-profile" class="flex items-center justify-between py-[14px]">
<div class="flex items-center pl-20px"> <div class="flex items-center pl-[20px]">
<img v-if="!route.path.startsWith('/personal/profile')" src="~/assets/images/user_zl.png" alt="" srcset="" class="h-16px" /> <img v-if="!route.path.startsWith('/personal-Center/personal-profile')" src="~/assets/images/user_zl.png" alt="" srcset="" class="h-[16px]" />
<img v-else src="~/assets/images/个人资料 (1).png" alt="" srcset="" class="h-16px" /> <img v-else src="~/assets/images/个人资料 (1).png" alt="" srcset="" class="h-[16px]" />
<span class="ml-10px">个人资料</span> <span class="ml-[10px]">个人资料</span>
</div> </div>
<div class="pr-20px"> <div class="pr-[20px]">
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</div> </div>
</nuxt-link> </nuxt-link>
<nuxt-link to="/personal/account/security" class="flex items-center justify-between py-14px"> <nuxt-link to="/personal-Center/account-security" class="flex items-center justify-between py-[14px]">
<div class="flex items-center pl-20px"> <div class="flex items-center pl-[20px]">
<img v-if="!route.path.startsWith('/personal/account/security')" src="~/assets/images/account.png" alt="" srcset="" class="h-20px" /> <img v-if="!route.path.startsWith('/personal-Center/account-security')" src="~/assets/images/account.png" alt="" srcset="" class="h-[20px]" />
<img v-else src="~/assets/images/账户安全.png" alt="" srcset="" class="h-20px" /> <img v-else src="~/assets/images/账户安全.png" alt="" srcset="" class="h-[20px]" />
<span class="ml-14px">账户与安全</span> <span class="ml-[14px]">账户与安全</span>
</div> </div>
<div class="pr-20px"> <div class="pr-[20px]">
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</div> </div>
</nuxt-link> </nuxt-link>
<nuxt-link to="/personal/resource/center" class="flex items-center justify-between py-14px"> <nuxt-link to="/personal-Center/resource-center" class="flex items-center justify-between py-[14px]">
<div class="flex items-center pl-20px"> <div class="flex items-center pl-[20px]">
<img v-if="!route.path.startsWith('/personal/resource/center')" src="~/assets/images/ziyuan.png" alt="" srcset="" class="h-18px" /> <img v-if="!route.path.startsWith('/personal-Center/resource-center')" src="~/assets/images/ziyuan.png" alt="" srcset="" class="h-[18px]" />
<img v-else src="~/assets/images/资源.png" alt="" srcset="" class="h-18px" /> <img v-else src="~/assets/images/资源.png" alt="" srcset="" class="h-[18px]" />
<span class="ml-12px">资源中心</span> <span class="ml-[12px]">资源中心</span>
</div> </div>
<div class="pr-20px"> <div class="pr-[20px]">
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</div> </div>
</nuxt-link> </nuxt-link>
<nuxt-link to="/personal/trading/center" class="flex items-center justify-between py-14px"> <nuxt-link to="/personal-Center/trading-center" class="flex items-center justify-between py-[14px]">
<div class="flex items-center pl-20px"> <div class="flex items-center pl-[20px]">
<img v-if="!route.path.startsWith('/personal/trading/center')" src="~/assets/images/pay.png" alt="" srcset="" class="h-20px" /> <img v-if="!route.path.startsWith('/personal-Center/trading-center')" src="~/assets/images/pay.png" alt="" srcset="" class="h-[20px]" />
<img v-else src="~/assets/images/交易管理.png" alt="" srcset="" class="h-20px" /> <img v-else src="~/assets/images/交易管理.png" alt="" srcset="" class="h-[20px]" />
<span class="ml-12px">交易中心</span> <span class="ml-[12px]">交易中心</span>
</div> </div>
<div class="pr-20px"> <div class="pr-[20px]">
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</div> </div>
</nuxt-link> </nuxt-link>
<nuxt-link to="/personal/center/message" class="flex items-center justify-between py-14px"> <nuxt-link to="/personal-Center/message-center" class="flex items-center justify-between py-[14px]">
<div class="flex items-center pl-20px"> <div class="flex items-center pl-[20px]">
<img v-if="!route.path.startsWith('/personal/center/message')" src="~/assets/images/message.png" alt="" srcset="" class="h-18px" /> <img v-if="!route.path.startsWith('/personal-Center/message-center')" src="~/assets/images/message.png" alt="" srcset="" class="h-[18px]" />
<img v-else src="~/assets/images/消息.png" alt="" srcset="" class="h-18px" /> <img v-else src="~/assets/images/消息.png" alt="" srcset="" class="h-[18px]" />
<span class="ml-14px">消息通知</span> <span class="ml-[14px]">消息通知</span>
</div> </div>
<div class="pr-20px"> <div class="pr-[20px]">
<el-icon><ArrowRight /></el-icon> <el-icon><ArrowRight /></el-icon>
</div> </div>
</nuxt-link> </nuxt-link>
</div> </div>
<div class="right mt-25px"> <div class="right mt-[25px]">
<NuxtPage /> <NuxtPage />
</div> </div>
</div> </div>

View File

@ -54,9 +54,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
import { resetPassoword } from '~/api/login/index' import { resetPassword } from '~/api/login/index'
import { sendSms } from '~/api/common/index' import { sendSms } from '~/api/common/index'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const activeName = ref('修改密码') const activeName = ref('修改密码')
@ -75,7 +75,7 @@
await formRef.value.validate() await formRef.value.validate()
if (form.password !== form.passwordV2) return ElMessage.error('两次密码不一致') if (form.password !== form.passwordV2) return ElMessage.error('两次密码不一致')
try { try {
const res = await resetPassoword(form) const res = await resetPassword(form)
const { code } = res const { code } = res
if (code === 0) { if (code === 0) {
ElMessage.success(`修改密码成功,请重新登录`) ElMessage.success(`修改密码成功,请重新登录`)

View File

@ -131,7 +131,7 @@
import { upload } from '~/api/common' import { upload } from '~/api/common'
import { sendSingleChat, conversationList, getChatDetail, clearUnreadMessage } from '~/api/channel/index' import { sendSingleChat, conversationList, getChatDetail, clearUnreadMessage } from '~/api/channel/index'
import type { chatMessagesReq, msgType, PageResultSessionRespVO, PageResultMessageRespVO } from '~/api/channel/types' import type { chatMessagesReq, msgType, PageResultSessionRespVO, PageResultMessageRespVO } from '~/api/channel/types'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
import dayjs from 'dayjs' import dayjs from 'dayjs'

View File

@ -1,11 +1,11 @@
<template> <template>
<el-dialog v-model="visible" width="800px" class="vip-dialog" align-center> <el-dialog v-model="visible" width="800px" class="vip-dialog" align-center @close="handleClose">
<template #header> <template #header>
<div class="vip-modal-title">钱包充值</div> <div class="vip-modal-title">钱包充值</div>
</template> </template>
<div v-loading="loading" class="vip-cards"> <div v-loading="loading" class="vip-cards">
<div v-for="item in viplist" :key="item.id" class="vip-card"> <div v-for="item in viplist" :key="item.id" class="vip-card">
<div class="relative w-100% flex flex-col items-center"> <div class="relative w-[100%] flex flex-col items-center">
<div class="vip-card-header basic"> <div class="vip-card-header basic">
<div class="vip-card-title">{{ item.name }}</div> <div class="vip-card-title">{{ item.name }}</div>
<!-- <div class="vip-card-subtitle">中小微企业</div> --> <!-- <div class="vip-card-subtitle">中小微企业</div> -->
@ -25,8 +25,8 @@
> --> > -->
</ul> </ul>
<div v-if="item.qrCodeUrl" class="vip-card-qrcode"> <div v-if="item.qrCodeUrl" class="vip-card-qrcode">
<el-icon class="absolute right--10px top-0px cursor-pointer" @click="item.qrCodeUrl = ''"><Close /></el-icon> <el-icon class="absolute! right-[-10px] top-[0px] cursor-pointer" @click="item.qrCodeUrl = ''"><Close /></el-icon>
<qrcode-vue :value="item.qrCodeUrl" :size="140" level="H" /> <qrcode-vue :value="item.qrCodeUrl" :size="140" level="H" class="relative! left-[12px]" />
<div>请使用微信扫二维码</div> <div>请使用微信扫二维码</div>
</div> </div>
</div> </div>
@ -44,7 +44,7 @@
import type { AppPayWalletPackageRespVO } from '~/api/pay/types' import type { AppPayWalletPackageRespVO } from '~/api/pay/types'
// @ts-ignore // @ts-ignore
import QrcodeVue from 'qrcode.vue' import QrcodeVue from 'qrcode.vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const props = defineProps({ const props = defineProps({
@ -135,6 +135,14 @@
clearInterval(interval.value) clearInterval(interval.value)
interval.value = undefined interval.value = undefined
} }
/** 关闭弹窗 */
const handleClose = () => {
visible.value = false
//
clearInterval(interval.value)
interval.value = undefined
}
</script> </script>
<style scoped> <style scoped>

View File

@ -1,45 +1,45 @@
<template> <template>
<div class="box-border w-913px border border-[#EEEEEE] rounded-6px border-solid bg-[#FFFFFF] px-30px py-18px"> <div class="box-border w-[913px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] px-[30px] py-[18px]">
<div class="flex items-center"> <div class="flex items-center">
<img :src="userStore.userInfoRes.avatar" alt="" srcset="" class="h-105px w-105px rounded-full" /> <img :src="userStore.userInfoRes.avatar" alt="" srcset="" class="h-[105px] w-[105px] rounded-full" />
<div class="ml-29px"> <div class="ml-[29px]">
<div class="flex items-center"> <div class="flex items-center">
<span class="text-20px text-[#333333] font-normal">Hi{{ userStore.userInfoRes.nickname }}</span> <span class="text-[20px] text-[#333333] font-normal">Hi{{ userStore.userInfoRes.nickname }}</span>
<img v-if="userStore.userInfoRes.vipLevel === 1" src="~/assets/svg/vip.svg" alt="" class="relative top-2px ml-5px" /> <img v-if="userStore.userInfoRes.vipLevel === 1" src="~/assets/svg/vip.svg" alt="" class="relative top-[2px] ml-[5px]" />
<img v-if="userStore.userInfoRes.vipLevel === 2" src="~/assets/svg/svip.svg" alt="" class="relative top-2px ml-5px" /> <img v-if="userStore.userInfoRes.vipLevel === 2" src="~/assets/svg/svip.svg" alt="" class="relative top-[2px] ml-[5px]" />
<div <div
class="ml-18px h-30px w-80px cursor-pointer border border-[#1A65FF] rounded-15px border-solid text-center text-14px text-[#1A65FF] font-normal line-height-30px" class="ml-[18px] h-[30px] w-[80px] cursor-pointer border border-[#1A65FF] rounded-[15px] border-solid text-center text-[14px] text-[#1A65FF] font-normal line-height-[30px]"
@click="handleClick" @click="handleClick"
>编辑资料</div >编辑资料</div
> >
</div> </div>
<div class="mt-20px flex items-center text-14px text-[#333333] font-normal"> <div class="mt-[20px] flex items-center text-[14px] text-[#333333] font-normal">
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/cad_0 (1).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (1).png" alt="" srcset="" />
<span class="ml-4px">我的积分: {{ userStaticInfo?.pointCount || 0 }}</span> <span class="ml-[4px]">我的积分: {{ userStaticInfo?.pointCount || 0 }}</span>
</div> </div>
<div class="ml-37px flex items-center"> <div class="ml-[37px] flex items-center">
<img src="~/assets/images/cad_0 (2).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (2).png" alt="" srcset="" />
<span class="ml-4px">我的收藏: {{ userStaticInfo?.followCount || 0 }}</span> <span class="ml-[4px]">我的收藏: {{ userStaticInfo?.followCount || 0 }}</span>
</div> </div>
<div class="ml-37px flex items-center"> <div class="ml-[37px] flex items-center">
<img src="~/assets/images/cad_0 (3).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (3).png" alt="" srcset="" />
<span class="ml-4px">我的发布: {{ userStaticInfo?.projectCount || 0 }}</span> <span class="ml-[4px]">我的发布: {{ userStaticInfo?.projectCount || 0 }}</span>
</div> </div>
<div class="ml-37px flex items-center"> <div class="ml-[37px] flex items-center">
<img src="~/assets/images/cad_0 (4).png" alt="" srcset="" /> <img src="~/assets/images/cad_0 (4).png" alt="" srcset="" />
<span class="ml-4px">我的下载: {{ userStaticInfo?.downloadCount || 0 }}</span> <span class="ml-[4px]">我的下载: {{ userStaticInfo?.downloadCount || 0 }}</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="mt-30px flex items-center justify-around"> <div class="mt-[30px] flex items-center justify-around">
<div class="flex items-center"> <div class="flex items-center">
<div class="relative"> <div class="relative">
<img src="~/assets/images/info_1 (3).png" alt="" srcset="" /> <img src="~/assets/images/info_1 (3).png" alt="" srcset="" />
<img src="~/assets/images/info_1 (4).png" alt="" srcset="" class="absolute left-18px top-18px" /> <img src="~/assets/images/info_1 (4).png" alt="" srcset="" class="absolute left-[18px] top-[18px]" />
</div> </div>
<div class="ml-18px"> <div class="ml-[18px]">
<div class="flex items-center"> <div class="flex items-center">
<span class="info_num text-[#17A86D]">{{ userStaticInfo?.currencyCount || 0 }}</span> <span class="info_num text-[#17A86D]">{{ userStaticInfo?.currencyCount || 0 }}</span>
<div class="info_pay cursor-pointer" @click="handlePay">充值</div> <div class="info_pay cursor-pointer" @click="handlePay">充值</div>
@ -50,9 +50,9 @@
<div class="flex items-center"> <div class="flex items-center">
<div class="relative"> <div class="relative">
<img src="~/assets/images/info_1 (5).png" alt="" srcset="" /> <img src="~/assets/images/info_1 (5).png" alt="" srcset="" />
<img src="~/assets/images/info_1 (6).png" alt="" srcset="" class="absolute left-18px top-22px" /> <img src="~/assets/images/info_1 (6).png" alt="" srcset="" class="absolute left-[18px] top-[22px]" />
</div> </div>
<div class="ml-18px"> <div class="ml-[18px]">
<div> <div>
<span class="info_num text-[#328CD7]">{{ userStaticInfo?.previewCount || 0 }}</span> <span class="info_num text-[#328CD7]">{{ userStaticInfo?.previewCount || 0 }}</span>
</div> </div>
@ -62,9 +62,9 @@
<div class="flex items-center"> <div class="flex items-center">
<div class="relative"> <div class="relative">
<img src="~/assets/images/info_1 (1).png" alt="" srcset="" /> <img src="~/assets/images/info_1 (1).png" alt="" srcset="" />
<img src="~/assets/images/info_1 (2).png" alt="" srcset="" class="absolute left-20px top-18px" /> <img src="~/assets/images/info_1 (2).png" alt="" srcset="" class="absolute left-[20px] top-[18px]" />
</div> </div>
<div class="ml-18px"> <div class="ml-[18px]">
<div> <div>
<span class="info_num text-[#FFC415]">{{ userStaticInfo?.revenueCount || 0 }}</span> <span class="info_num text-[#FFC415]">{{ userStaticInfo?.revenueCount || 0 }}</span>
</div> </div>
@ -74,20 +74,20 @@
</div> </div>
</div> </div>
<!-- --> <!-- -->
<div class="mt-23px box-border h-183px w-913px border border-[#EEEEEE] rounded-6px border-solid bg-[#FFFFFF] px-30px py-21px"> <div class="mt-[23px] box-border h-[183px] w-[913px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] px-[30px] py-[21px]">
<div class="title">快捷入口</div> <div class="title">快捷入口</div>
<div class="mt-20px flex items-center"> <div class="mt-[20px] flex items-center">
<div class="info_item cursor-pointer" @click="handleClickPush('/upnew/drawe')"> <div class="info_item cursor-pointer" @click="handleClickPush('/upnew')">
<img src="~/assets/images/fabu_2 (3).png" alt="" srcset="" /> <img src="~/assets/images/fabu_2 (3).png" alt="" srcset="" />
<div class="mt-10px">发布资源</div> <div class="mt-[10px]">发布资源</div>
</div> </div>
<div class="info_item ml-31px cursor-pointer" @click="handleClickPush('/communication/channel')"> <div class="info_item ml-[31px] cursor-pointer" @click="handleClickPush('/channel')">
<img src="~/assets/images/fabu_2 (1).png" alt="" srcset="" /> <img src="~/assets/images/fabu_2 (1).png" alt="" srcset="" />
<div class="mt-10px">交流频道</div> <div class="mt-[10px]">交流频道</div>
</div> </div>
<div class="info_item ml-31px cursor-pointer" @click="handleService"> <div class="info_item ml-[31px] cursor-pointer" @click="handleService">
<img src="~/assets/images/fabu_2 (2).png" alt="" srcset="" /> <img src="~/assets/images/fabu_2 (2).png" alt="" srcset="" />
<div class="mt-10px">消息</div> <div class="mt-[10px]">消息</div>
</div> </div>
</div> </div>
</div> </div>
@ -107,7 +107,7 @@
import Message from './components/message.vue' import Message from './components/message.vue'
import InfoEcharts from './info-echarts.vue' import InfoEcharts from './info-echarts.vue'
import Pay from './components/pay.vue' import Pay from './components/pay.vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
// //
@ -123,7 +123,7 @@
fetchUserStatistics() fetchUserStatistics()
const handleClick = () => { const handleClick = () => {
router.push({ path: '/personal/profile' }) router.push({ path: '/personal-Center/personal-profile' })
} }
const payVisible = ref(false) const payVisible = ref(false)

View File

@ -1,10 +1,10 @@
<template> <template>
<div class="box-border h-1082px w-913px border border-[#EEEEEE] rounded-6px border-solid bg-[#FFFFFF] px-30px py-21px"> <div class="box-border h-[782px] w-[913px] border border-[#EEEEEE] rounded-[6px] border-solid bg-[#FFFFFF] px-[30px] py-[21px]">
<div class="flex items-center justify-between border-b-1px border-b-[#eeeeee] border-b-solid pb-18px"> <div class="flex items-center justify-between border-b-[1px] border-b-[#eeeeee] border-b-solid pb-[18px]">
<div class="text-16px text-[#333333] font-normal">个人资料</div> <div class="text-[16px] text-[#333333] font-normal">个人资料</div>
<div class="flex items-center"> <div class="flex items-center">
<!-- <img src="~/assets/images/fans.png" alt="" srcset="" /> --> <!-- <img src="~/assets/images/fans.png" alt="" srcset="" /> -->
<span class="ml-8px text-14px text-[#333333] font-normal"></span> <span class="ml-[8px] text-[14px] text-[#333333] font-normal"></span>
</div> </div>
</div> </div>
<div class="user-profile-container"> <div class="user-profile-container">
@ -12,7 +12,7 @@
<el-upload class="avatar-uploader" action="#" :show-file-list="false" :auto-upload="false" :on-change="handleAvatarChange"> <el-upload class="avatar-uploader" action="#" :show-file-list="false" :auto-upload="false" :on-change="handleAvatarChange">
<div class="flex flex-col items-center"> <div class="flex flex-col items-center">
<el-avatar :size="100" :src="userForm.avatar" /> <el-avatar :size="100" :src="userForm.avatar" />
<div class="mt-15px"> <div class="mt-[15px]">
<el-button type="primary" plain>更改头像</el-button> <el-button type="primary" plain>更改头像</el-button>
</div> </div>
</div> </div>
@ -24,7 +24,7 @@
<!-- User information section --> <!-- User information section -->
<el-form-item label="用户名:" prop="nickname"> <el-form-item label="用户名:" prop="nickname">
<div class="flex items-center"> <div class="flex items-center">
<el-input v-model="userForm.nickname" class="w-247px" /> <el-input v-model="userForm.nickname" class="w-[247px]" />
<el-button type="primary" class="verify-btn" @click="handleVerify">实名认证</el-button> <el-button type="primary" class="verify-btn" @click="handleVerify">实名认证</el-button>
</div> </div>
</el-form-item> </el-form-item>
@ -38,37 +38,37 @@
<el-form-item label="手机号:" prop="phone"> <el-form-item label="手机号:" prop="phone">
<div class="flex items-center"> <div class="flex items-center">
<el-input v-model="userForm.phone" disabled class="w-247px" /> <el-input v-model="userForm.phone" disabled class="w-[247px]" />
<!-- <el-link type="primary" class="modify-link">修改</el-link> --> <!-- <el-link type="primary" class="modify-link">修改</el-link> -->
</div> </div>
</el-form-item> </el-form-item>
<el-form-item label="电子邮箱:" prop="email"> <el-form-item label="电子邮箱:" prop="email">
<div class="flex items-center"> <div class="flex items-center">
<el-input v-model="userForm.email" placeholder="请输入电子邮箱" class="w-247px" /> <el-input v-model="userForm.email" placeholder="请输入电子邮箱" class="w-[247px]" />
<!-- <el-link type="primary" class="modify-link">绑定</el-link> --> <!-- <el-link type="primary" class="modify-link">绑定</el-link> -->
</div> </div>
</el-form-item> </el-form-item>
<div class="flex items-center"> <div class="flex items-center">
<el-form-item label="所在地区:" prop="isDomestic"> <el-form-item label="所在地区:" prop="isDomestic">
<el-select v-model="userForm.isDomestic" placeholder="请选择" class="w-120px!" @change="handleCountryChange"> <el-select v-model="userForm.isDomestic" placeholder="请选择" class="w-[120px]!" @change="handleCountryChange">
<el-option label="国内" :value="1"></el-option> <el-option label="国内" :value="1"></el-option>
<el-option label="国外" :value="0"></el-option> <el-option label="国外" :value="0"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label-width="6px" prop="province"> <el-form-item label-width="6px" prop="province">
<el-select v-model="userForm.province" placeholder="请选择省份" class="w-120px" @change="handleProvinceChange"> <el-select v-model="userForm.province" placeholder="请选择省份" class="w-[120px]!" @change="handleProvinceChange">
<el-option v-for="item in provinceList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in provinceList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label-width="6px" prop="city"> <el-form-item label-width="6px" prop="city">
<el-select v-model="userForm.city" placeholder="请选择城市" class="w-120px" @change="handleCityChange"> <el-select v-model="userForm.city" placeholder="请选择城市" class="w-[120px]!" @change="handleCityChange">
<el-option v-for="item in cityList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in cityList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label-width="6px" prop="county"> <el-form-item label-width="6px" prop="county">
<el-select v-model="userForm.county" placeholder="请选择区县" class="w-120px"> <el-select v-model="userForm.county" placeholder="请选择区县" class="w-[120px]!">
<el-option v-for="item in countyList" :key="item.id" :label="item.name" :value="item.id" /> <el-option v-for="item in countyList" :key="item.id" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -83,7 +83,7 @@
filterable filterable
remote remote
placeholder="请输入搜索标签" placeholder="请输入搜索标签"
class="w-498px!" class="w-[498px]!"
> >
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" /> <el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
</el-select> </el-select>
@ -97,9 +97,9 @@
:size="1" :size="1"
tips="上传图片支持jpg/gif/png格式、第一张为封面图片、每张图片大小不得超过1M" tips="上传图片支持jpg/gif/png格式、第一张为封面图片、每张图片大小不得超过1M"
> >
<div class="h-77px w-161px flex items-center justify-center bg-[#fafafa]"> <div class="h-[77px] w-[161px] flex items-center justify-center bg-[#fafafa]">
<el-icon class="text-[#999999]"><Plus /></el-icon> <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> </div>
</KlUploader> </KlUploader>
</el-form-item> </el-form-item>
@ -109,27 +109,27 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" class="w-120px !h-37px" @click="submitForm">提交</el-button> <el-button type="primary" class="w-[120px] !h-[37px]" :loading="submitLoading" @click="submitForm">提交</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<div class="flex items-center justify-between border-b-1px border-b-[#eeeeee] border-b-solid pb-18px"> <!-- <div class="flex items-center justify-between border-b-[1px] border-b-[#eeeeee] border-b-solid pb-[18px]">
<div class="text-16px text-[#333333] font-normal">社交帐号绑定</div> <div class="text-[16px] text-[#333333] font-normal">社交帐号绑定</div>
</div> </div>
<div class="flex flex-col justify-center text-14px text-[#333333] font-normal"> <div class="flex flex-col justify-center text-[14px] text-[#333333] font-normal">
<div class="mt-30px flex items-center"> <div class="mt-[30px] flex items-center">
<img src="~/assets/images/qq-v2.png" alt="" srcset="" class="h-35px w-34px" /> <img src="~/assets/images/qq-v2.png" alt="" srcset="" class="h-[35px] w-[34px]" />
<div class="ml-19px">QQ</div> <div class="ml-[19px]">QQ</div>
<div class="ml-100px flex items-center"><div class="w-90px">QQ昵称</div><div class="w-180px">xxx</div></div> <div class="ml-[100px] flex items-center"><div class="w-[90px]">QQ昵称</div><div class="w-[180px]">xxx</div></div>
<div class="btn">绑定</div> <div class="btn">绑定</div>
</div> </div>
<div class="mt-30px flex items-center"> <div class="mt-[30px] flex items-center">
<img src="~/assets/images/weixin-v2.png" alt="" srcset="" class="h-35px w-34px" /> <img src="~/assets/images/weixin-v2.png" alt="" srcset="" class="h-[35px] w-[34px]" />
<div class="ml-19px">微信</div> <div class="ml-[19px]">微信</div>
<div class="ml-95px flex items-center"><div class="w-90px">微信昵称</div><div class="w-180px">xxx</div></div> <div class="ml-[95px] flex items-center"><div class="w-[90px]">微信昵称</div><div class="w-[180px]">xxx</div></div>
<div class="btn">绑定</div> <div class="btn">绑定</div>
</div> </div>
</div> </div> -->
</div> </div>
<verifyDialog ref="verifyDialogRef" /> <verifyDialog ref="verifyDialogRef" />
</template> </template>
@ -281,11 +281,13 @@
} }
// //
const submitLoading = ref(false)
const submitForm = async () => { const submitForm = async () => {
if (!userFormRef.value) return if (!userFormRef.value) return
try { try {
await userFormRef.value.validate() await userFormRef.value.validate()
submitLoading.value = true
const res = userForm.id ? await updateUserExtend(userForm) : await userExtend(userForm) const res = userForm.id ? await updateUserExtend(userForm) : await userExtend(userForm)
if (res.code === 0) { if (res.code === 0) {
ElMessage.success('个人信息提交成功') ElMessage.success('个人信息提交成功')
@ -295,6 +297,8 @@
} catch (error) { } catch (error) {
console.error('Validation failed:', error) console.error('Validation failed:', error)
ElMessage.error('表单验证失败,请检查输入') ElMessage.error('表单验证失败,请检查输入')
} finally {
submitLoading.value = false
} }
} }

View File

@ -40,7 +40,7 @@
import downloadTable from './components/download-table.vue' import downloadTable from './components/download-table.vue'
import favoriteTable from './components/favorite-table.vue' import favoriteTable from './components/favorite-table.vue'
import browseTable from './components/browse-table.vue' import browseTable from './components/browse-table.vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const activeName = ref('我的上传') const activeName = ref('我的上传')

View File

@ -5,20 +5,20 @@
<div class="profile-header"> <div class="profile-header">
<div class="profile-container"> <div class="profile-container">
<div class="avatar-container"> <div class="avatar-container">
<el-image :src="userForm.avatar" alt="用户头像" class="avatar mt-4px" fit="cover" /> <el-image :src="userForm.avatar" alt="用户头像" class="avatar mt-[4px]" fit="cover" />
</div> </div>
<div class="user-info"> <div class="user-info">
<h2 class="username">{{ userForm.nickname }}</h2> <h2 class="username">{{ userForm.nickname }}</h2>
<!-- <div class="education">手机号码{{ userForm.phone }}</div> --> <!-- <div class="education">手机号码{{ userForm.phone }}</div> -->
<div class="stats"> <div class="stats">
技能标签<el-tag v-for="label in userForm.labels" :key="label" type="primary" class="mr-10px" size="small">{{ label }}</el-tag> 技能标签<el-tag v-for="label in userForm.labels" :key="label" type="primary" class="mr-[10px]" size="small">{{ label }}</el-tag>
</div> </div>
<div class="description">{{ userForm.description }}</div> <div class="description">{{ userForm.description }}</div>
</div> </div>
</div> </div>
</div> </div>
<div class="items-flex-start mx-auto mt-20px max-w-[1200px] flex justify-center"> <div class="items-flex-start mx-auto mt-[20px] max-w-[1200px] flex justify-center">
<div class="flex-1"> <div class="flex-1">
<!-- 导航标签 --> <!-- 导航标签 -->
<div class="nav-tabs"> <div class="nav-tabs">
@ -30,27 +30,27 @@
</div> </div>
<!-- 作品展示区 --> <!-- 作品展示区 -->
<div class="content w-800px"> <div class="content w-[800px]">
<el-table v-loading="result.loading" :data="result.tableList" style="width: 100%" class="mt-14px"> <el-table v-loading="result.loading" :data="result.tableList" style="width: 100%" class="mt-[14px]">
<el-table-column prop="date" label="文件信息"> <el-table-column prop="date" label="文件信息">
<template #default="scope"> <template #default="scope">
<div class="flex items-center"> <div class="flex items-center">
<el-image :src="scope.row.iconUrl" fit="cover" alt="" srcset="" class="h-91px w-181px rd-4px" /> <el-image :src="scope.row.iconUrl" fit="cover" alt="" srcset="" class="h-[91px] w-[181px] rd-[4px]" />
<div class="ml-17px"> <div class="ml-[17px]">
<div class="text-16px text-[#333333] font-normal">{{ scope.row.title }}</div> <div class="text-[16px] text-[#333333] font-normal">{{ scope.row.title }}</div>
<div class="text-14px text-[#666] font-normal my-10px!">{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div> <div class="text-[14px] text-[#666] font-normal my-[10px]!">{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
<div class="flex items-center"> <div class="flex items-center">
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/look.png" alt="" srcset="" class="h-17px" /> <img src="~/assets/images/look.png" alt="" srcset="" class="h-[17px]" />
<span class="ml-4px">{{ scope.row.previewPoint }}</span> <span class="ml-[4px]">{{ scope.row.previewPoint }}</span>
</div> </div>
<div class="ml-13px flex items-center"> <div class="ml-[13px] flex items-center">
<img src="~/assets/images/add.png" alt="" srcset="" class="h-23px" /> <img src="~/assets/images/add.png" alt="" srcset="" class="h-[23px]" />
<span class="ml-4px">{{ scope.row.hotPoint }}</span> <span class="ml-[4px]">{{ scope.row.hotPoint }}</span>
</div> </div>
<div class="ml-13px flex items-center"> <div class="ml-[13px] flex items-center">
<img src="~/assets/images/chat.png" alt="" srcset="" class="h-17px" /> <img src="~/assets/images/chat.png" alt="" srcset="" class="h-[17px]" />
<span class="ml-4px">{{ scope.row.commentsPoint }}</span> <span class="ml-[4px]">{{ scope.row.commentsPoint }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -70,7 +70,7 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页 --> <!-- 分页 -->
<div class="mt-10px flex justify-center"> <div class="mt-[10px] flex justify-center">
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
v-model:page-size="query.pageSize" v-model:page-size="query.pageSize"
@ -91,10 +91,10 @@
<div <div
v-for="item in mainWork" v-for="item in mainWork"
:key="item.id" :key="item.id"
class="flex cursor-pointer items-center justify-between px-10px py-4px hover:bg-#f5f5f5" class="flex cursor-pointer items-center justify-between px-[10px] py-[4px] hover:bg-[#f5f5f5]"
@click="handleClickV2(item.id)" @click="handleClickV2(item.id)"
> >
<div class="ellipsis text-15px text-[#333333] font-normal">{{ item.title }}发货速度发货速度开发还是看东方航空</div> <div class="ellipsis text-[15px] text-[#333333] font-normal">{{ item.title }}发货速度发货速度开发还是看东方航空</div>
</div> </div>
</div> </div>
</div> </div>
@ -249,7 +249,7 @@
} }
const handleClickV2 = (id: string | number) => { const handleClickV2 = (id: string | number) => {
navigateTo(`/down-drawe-detail?id=${id}`) // 修改为在新窗口打开 navigateTo(`/down-drawe-detail/${id}`) // 修改为在新窗口打开
} }
// 获取最新发布 // 获取最新发布
const mainWork = ref<ProjectDrawMemberRespVO[]>([]) const mainWork = ref<ProjectDrawMemberRespVO[]>([])

View File

@ -1,6 +1,6 @@
<template> <template>
<KlNavTab /> <KlNavTab />
<div class="sign-content"> <div class="sign-content w-1440px ma-auto">
<!-- 顶部统计 --> <!-- 顶部统计 -->
<div class="sign-header"> <div class="sign-header">
<div class="sign-icon"> <div class="sign-icon">
@ -18,7 +18,7 @@
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button> <el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</div> </div>
<div class="sign-center ml-10px"> <div class="sign-center ml-[10px]">
<el-button type="success" class="sign-btn" @click="handleClickSign"> 立即签到 </el-button> <el-button type="success" class="sign-btn" @click="handleClickSign"> 立即签到 </el-button>
</div> </div>
</div> </div>
@ -40,7 +40,7 @@
<div>积分</div> <div>积分</div>
<div>发生时间</div> <div>发生时间</div>
</div> </div>
<div v-for="item in result.list" :key="item.id" class="table-row"> <div v-for="item in result?.list" :key="item.id" class="table-row">
<!-- <div class="avatar"> --> <!-- <div class="avatar"> -->
<!-- <img :src="item.avatar" alt="avatar" /> --> <!-- <img :src="item.avatar" alt="avatar" /> -->
<!-- <span>{{ item.nickname }}</span> --> <!-- <span>{{ item.nickname }}</span> -->
@ -52,12 +52,12 @@
</div> </div>
</div> </div>
<!-- 分页组件 --> <!-- 分页组件 -->
<div class="mt-10px flex justify-center"> <div class="mt-[10px] flex justify-center">
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
:page-size="query.pageSize" :page-size="query.pageSize"
layout="prev, pager, next" layout="prev, pager, next"
:total="result.total" :total="result?.total"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
/> />
</div> </div>
@ -69,21 +69,21 @@
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import { signIn, getUserPointPage } from '~/api/personal-center/index' import { signIn, getUserPointPage } from '~/api/personal-center/index'
import type { PageResultMemberPointRecordRespVO } from '~/api/personal-center/types' import type { PageResultMemberPointRecordRespVO } from '~/api/personal-center/types'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
import dayjs from 'dayjs' import dayjs from 'dayjs'
const userStore = useUserStore() const user = useUserInfo()
const query = reactive({ const query = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
userId: userStore.userId, userId: user.value.id,
title: '', title: '',
}) })
const result = reactive<PageResultMemberPointRecordRespVO>({ // const result = reactive<PageResultMemberPointRecordRespVO>({
total: 0, // total: 0,
list: [], // list: [],
}) // })
// const signList = ref([ // const signList = ref([
// { // {
// avatar: 'https://dummyimage.com/40x40/ccc/fff.png&text=美', // avatar: 'https://dummyimage.com/40x40/ccc/fff.png&text=美',
@ -115,14 +115,25 @@
getUserPointPageList() getUserPointPageList()
} }
const getUserPointPageList = async () => { const { data: result, refresh: getUserPointPageList } = await useAsyncData(
const res = await getUserPointPage(query) 'getUserPointPage',
if (res.code === 0) { async () => {
result.list = res.data.list const res = await getUserPointPage(query)
result.total = res.data.total return res.data
},
{
immediate: true, // 立即请求
} }
} )
getUserPointPageList()
// const getUserPointPageList = async () => {
// const res = await getUserPointPage(query)
// if (res.code === 0) {
// result.list = res.data.list
// result.total = res.data.total
// }
// }
// getUserPointPageList()
const handleClickSign = async () => { const handleClickSign = async () => {
const res = await signIn() const res = await signIn()

View File

@ -0,0 +1,126 @@
<template>
<!-- 导航 -->
<SeoHead title="工程设计文本下载_CAD设计图纸资源库" />
<KlNavTab active="文本" :type="2" />
<div class="ma-auto w-[1440px]">
<!-- 图纸分类 -->
<KlWallpaperCategory v-model="query" v-model:level="level" :type="2" />
<!-- 推荐栏目 -->
<RecommendedColumnsV2 v-model="query" v-model:result="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="[12, 24, 48]"
:total="result?.total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleClickSize"
@current-change="handeClickCurrent"
/>
</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/text-components/RecommendedColumnsV2.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: 2,
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 handleClickSize = (val: number) => {
query.value.pageSize = val
// getPage()
navigateTo(`/text/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
}
const handeClickCurrent = (val: number) => {
query.value.pageNo = val
// getPage()
navigateTo(`/text/${query.value.projectType}/${val}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
}
const { data: result, refresh: getPage } = useAsyncData(
`draw-page-list-${query.value.projectType}-${query.value.editions}-${query.value.source}-${query.value.pageNo}-${query.value.pageSize}`,
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(`/text/${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

@ -1,7 +1,8 @@
<template> <template>
<!-- 导航 --> <!-- 导航 -->
<SeoHead title="工程设计文本下载_CAD设计图纸资源库" />
<KlNavTab active="文本" :type="2" /> <KlNavTab active="文本" :type="2" />
<div class="ma-auto w-1440px"> <div class="ma-auto w-[1440px]">
<!-- 图纸分类 --> <!-- 图纸分类 -->
<KlWallpaperCategory v-model="query" v-model:level="level" :type="2" /> <KlWallpaperCategory v-model="query" v-model:level="level" :type="2" />
<!-- 推荐栏目 --> <!-- 推荐栏目 -->
@ -9,12 +10,12 @@
<!-- 精选专题 --> <!-- 精选专题 -->
<!-- <FeaturedSpecials></FeaturedSpecials> --> <!-- <FeaturedSpecials></FeaturedSpecials> -->
<!-- 分页 --> <!-- 分页 -->
<div class="mt-10px flex justify-center"> <div class="mt-[10px] flex justify-center">
<el-pagination <el-pagination
v-model:current-page="query.pageNo" v-model:current-page="query.pageNo"
v-model:page-size="query.pageSize" v-model:page-size="query.pageSize"
:page-sizes="[12, 24, 48]" :page-sizes="[12, 24, 48]"
:total="result.total" :total="result?.total"
layout="total, sizes, prev, pager, next, jumper" layout="total, sizes, prev, pager, next, jumper"
@size-change="handeChangeSize" @size-change="handeChangeSize"
@current-change="handeChangeCurrent" @current-change="handeChangeCurrent"
@ -25,7 +26,7 @@
<script setup lang="ts"> <script setup lang="ts">
import KlNavTab from '~/components/kl-nav-tab/index.vue' import KlNavTab from '~/components/kl-nav-tab/index.vue'
import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue' import KlWallpaperCategory from '~/components/kl-wallpaper-category/index.vue'
import RecommendedColumnsV2 from './components/RecommendedColumnsV2.vue' import RecommendedColumnsV2 from '~/components/text-components/RecommendedColumnsV2.vue'
// import FeaturedSpecials from './components/FeaturedSpecials.vue' // import FeaturedSpecials from './components/FeaturedSpecials.vue'
import { reactive, watch, ref } from 'vue' import { reactive, watch, ref } from 'vue'
@ -39,57 +40,76 @@
? JSON.parse(route.query.level as string) ? JSON.parse(route.query.level as string)
: [ : [
{ {
id: '0', id: -1,
name: '文本库', name: '文本库',
isChildren: false, isChildren: false,
}, },
] ]
) )
const query = reactive<pageReq>({ const query = ref<pageReq>({
pageNo: 1, pageNo: 1,
pageSize: 12, pageSize: 12,
projectType: '', projectType: '-1',
editions: '', editions: '-1',
source: '', source: -1,
type: 2, type: 2,
}) })
const result = reactive<pageRes>({ // const result = reactive<pageRes>({
list: [], // list: [],
total: 0, // total: 0,
}) // })
// 如果id存在则设置projectType // 如果id存在则设置projectType
if (level.value.length) { if (level.value.length) {
query.projectType = level.value[level.value.length - 1].id || '' // query.projectType = level.value[level.value.length - 1].id || ''
} }
const getPage = () => { const { data: result } = useAsyncData(
page(query).then((res) => { `draw-page-list-${query.value.projectType}-${query.value.editions}-${query.value.source}-${query.value.pageNo}-${query.value.pageSize}`,
const { data, code } = res async () => {
if (code === 0) { const res = await page({
result.list = data.list ...query.value,
result.total = data.total 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,
}
)
getPage() // const getPage = () => {
// page(query).then((res) => {
// const { data, code } = res
// if (code === 0) {
// result.list = data.list
// result.total = data.total
// }
// })
// }
// getPage()
const handeChangeSize = (val: number) => { const handeChangeSize = (val: number) => {
query.pageSize = val query.value.pageSize = val
query.pageNo = 1 // query.pageNo = 1
getPage() // getPage()
navigateTo(`/text/${query.value.projectType}/${query.value.pageNo}/${val}/${query.value.editions}/${query.value.source}`)
} }
const handeChangeCurrent = (val: number) => { const handeChangeCurrent = (val: number) => {
query.pageNo = val query.value.pageNo = val
getPage() // getPage()
navigateTo(`/text/${query.value.projectType}/${val}/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
} }
watch([() => query.projectType, () => query.editions, () => query.source], (val) => { watch([() => query.value.projectType, () => query.value.editions, () => query.value.source], (val) => {
if (val) { if (val) {
getPage() // getPage()
navigateTo(`/text/${query.value.projectType}/1/${query.value.pageSize}/${query.value.editions}/${query.value.source}`)
} }
}) })
</script> </script>

View File

@ -1,12 +1,12 @@
<template> <template>
<KlNavTab /> <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 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-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>
<el-form-item label-width="110px" label="分类:" prop="projectType" :rules="{ required: true, message: '请选择分类', trigger: ['blur', 'change'] }"> <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-option v-for="(item, index) in projectTypeList" :key="index" :label="item.name" :value="item.id" />
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -20,13 +20,13 @@
filterable filterable
remote remote
placeholder="请输入搜索标签" placeholder="请输入搜索标签"
class="w-361px!" class="w-[361px]!"
> >
<el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" /> <el-option v-for="(item, index) in labelsList" :key="index" :label="item" :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label-width="110px" label="金币:" prop="points" :rules="{ required: true, message: '请输入金币', trigger: ['blur', 'change'] }"> <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>
<el-form-item <el-form-item
@ -43,9 +43,9 @@
tips="上传图片支持jpg/gif/png格式、第一张为封面图片、每张图片大小不得超过1M" tips="上传图片支持jpg/gif/png格式、第一张为封面图片、每张图片大小不得超过1M"
@validate="formRef.validateField('coverImages')" @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> <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> </div>
</KlUploader> </KlUploader>
</el-form-item> </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>
<!-- 添加预览和保存按钮 --> <!-- 添加预览和保存按钮 -->
<el-form-item label-width="110px" label=" "> <el-form-item label-width="110px" label=" ">
@ -92,6 +92,7 @@
import { parent, keywords, labels } from '~/api/upnew/index' import { parent, keywords, labels } from '~/api/upnew/index'
import { create } from '~/api/toolbox/index.js' import { create } from '~/api/toolbox/index.js'
import type { TcreateReq } from '~/api/toolbox/types' import type { TcreateReq } from '~/api/toolbox/types'
const router = useRouter() // 导入路由实例,用于跳转页面
const form = reactive<TcreateReq>({ const form = reactive<TcreateReq>({
title: '', title: '',
@ -170,9 +171,10 @@
console.log(res) console.log(res)
if (res.code === 0) { if (res.code === 0) {
ElMessage.success('发布成功') ElMessage.success('发布成功')
window.setTimeout(() => { router.back()
window.close() // window.setTimeout(() => {
}, 1000) // window.close()
// }, 1000)
} }
}) })
.finally(() => { .finally(() => {

View File

@ -1,16 +1,16 @@
<template> <template>
<div> <div>
<header class="h-90px"> <header class="h-[90px]">
<div class="mx-a h-full flex items-center justify-center"> <div class="mx-a h-full flex items-center justify-center">
<!-- 搜索区域 --> <!-- 搜索区域 -->
<div class="relative w-647px px4 p-r-0px!"> <div class="relative w-[647px] px-4 p-r-[0px]!">
<div class="search-input relative w-100%"> <div class="search-input relative w-[100%]">
<el-input <el-input
v-model="searchQuery" v-model="searchQuery"
type="text" type="text"
placeholder="搜一搜" placeholder="搜一搜"
:prefix-icon="Search" :prefix-icon="Search"
class="no-right-border box-border h40 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]"
@keyup.enter="search" @keyup.enter="search"
/> />
</div> </div>
@ -18,13 +18,13 @@
<!-- 按钮区域 --> <!-- 按钮区域 -->
<div class="flex items-center"> <div class="flex items-center">
<button <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]"
@click="search" @click="search"
> >
搜索 搜索
</button> </button>
<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" @click="handleUpload"
> >
上传工具 上传工具
@ -37,7 +37,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from 'vue'
import { Search } from '@element-plus/icons-vue' import { Search } from '@element-plus/icons-vue'
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
const userStore = useUserStore() const userStore = useUserStore()
const emits = defineEmits(['search']) const emits = defineEmits(['search'])

View File

@ -2,47 +2,47 @@
<KlNavTab active="工具箱" /> <KlNavTab active="工具箱" />
<!-- 搜索 --> <!-- 搜索 -->
<KlSearch @search="search"></KlSearch> <KlSearch @search="search"></KlSearch>
<div v-loading="loading" class="ml-auto mr-auto mt-20px w1440 flex justify-center gap-60px"> <div v-loading="loading" class="ma-auto mt-[20px] w-[1440px] flex justify-center gap-[60px]">
<div class="left w-821px"> <div class="left w-[821px]">
<img src="~/assets/images/banner2.png" alt="" srcset="" class="h-284px w-100%" /> <img src="~/assets/images/banner2.png" alt="" srcset="" class="h-[284px] w-[100%]" />
<div <div
class="box-border border border-t-0px border-t-0px border-[#EEEEEE] rounded-12px border-solid border-t-none bg-[#FFFFFF] px-28px py-17px" class="box-border border border-t-[0px] border-t-[0px] border-[#EEEEEE] rounded-[12px] border-solid border-t-none bg-[#FFFFFF] px-[28px] py-[17px]"
style="border-top-left-radius: 0px; border-top-right-radius: 0px" style="border-top-left-radius: 0px; border-top-right-radius: 0px"
> >
<div v-for="item in pageRes.list" :key="item.id" class="mt-20px flex border-b-1px border-b-[#eee] border-b-solid pb-20px"> <div v-for="item in pageRes?.list" :key="item.id" class="mt-[20px] flex border-b-[1px] border-b-[#eee] border-b-solid pb-[20px]">
<div class="h-142px w-200px text-center"> <div class="h-[142px] w-[200px] text-center">
<el-image :src="item.iconUrl" alt="" srcset="" class="max-w-100% rd-4px" fit="cover" /> <el-image :src="item.iconUrl" alt="" srcset="" class="max-w-[100%] rd-[4px]" fit="cover" />
</div> </div>
<div class="ml-25px flex-1"> <div class="ml-[25px] flex-1">
<div class="text-16px text-[#333333] font-normal">{{ item.title }}</div> <div class="text-[16px] text-[#333333] font-normal">{{ item.title }}</div>
<div class="mt-8px text-14px text-[#999999] font-normal">{{ item.description }}</div> <div class="mt-[8px] text-[14px] text-[#999999] font-normal">{{ item.description }}</div>
<div class="mt-10px flex items-center justify-between"> <div class="mt-[10px] flex items-center justify-between">
<div class="flex items-center"> <div class="flex items-center">
<div class="flex items-center text-14px text-[#666666] font-normal"> <div class="flex items-center text-[14px] text-[#666666] font-normal">
<img src="~/assets/images/look.png" alt="" srcset="" class="mr-4px h-17px w-23px" />{{ item.previewPoint }} <img src="~/assets/images/look.png" alt="" srcset="" class="mr-[4px] h-[17px] w-[23px]" />{{ item.previewPoint }}
</div> </div>
<div class="ml-26px flex items-center text-14px text-[#666666] font-normal"> <div class="ml-[26px] flex items-center text-[14px] text-[#666666] font-normal">
<img src="~/assets/images/chat.png" alt="" srcset="" class="mr-4px h-17px w-19px" /> {{ item.commentsPoint }} <img src="~/assets/images/chat.png" alt="" srcset="" class="mr-[4px] h-[17px] w-[19px]" /> {{ item.commentsPoint }}
</div> </div>
<div class="ml-20px"> <div class="ml-[20px]">
<div v-for="(v, index) in item.labels" :key="index" class="mr-10px inline-block text-14px text-[#1A65FF] font-normal">#{{ v }}</div> <div v-for="(v, index) in item.labels" :key="index" class="mr-[10px] inline-block text-[14px] text-[#1A65FF] font-normal">#{{ v }}</div>
</div> </div>
</div> </div>
<div class="text-14px text-[#999999] font-normal">{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div> <div class="text-[14px] text-[#999999] font-normal">{{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
</div> </div>
</div> </div>
</div> </div>
<!-- 暂无数据 --> <!-- 暂无数据 -->
<div v-if="pageRes.list.length === 0" class="mt-10px flex items-center justify-center"> <div v-if="pageRes?.list.length === 0" class="mt-[10px] flex items-center justify-center">
<!-- <div class="text-16px text-[#999999] font-normal">暂无数据</div> --> <!-- <div class="text-16px text-[#999999] font-normal">暂无数据</div> -->
<el-empty v-if="!pageRes.list.length" :image="emptyImg"></el-empty> <el-empty v-if="!pageRes.list.length" :image="emptyImg"></el-empty>
</div> </div>
<div class="mt-20px"> <div class="mt-[20px]">
<el-pagination <el-pagination
v-model:current-page="pageReq.pageNum" v-model:current-page="pageReq.pageNum"
:page-size="pageReq.pageSize" :page-size="pageReq.pageSize"
layout="total, sizes, prev, pager, next" layout="total, sizes, prev, pager, next"
:total="pageRes.total" :total="pageRes?.total"
:page-sizes="[10, 20, 30]" :page-sizes="[10, 20, 30]"
class="justify-center!" class="justify-center!"
@current-change="handleCurrentChange" @current-change="handleCurrentChange"
@ -51,13 +51,13 @@
</div> </div>
</div> </div>
</div> </div>
<div class="right w-398px"> <div class="right w-[398px]">
<div class="box-border border border-[#EEEEEE] rounded-10px border-solid bg-[#FFFFFF] pa-20px"> <div class="box-border border border-[#EEEEEE] rounded-[10px] border-solid bg-[#FFFFFF] pa-[20px]">
<div class="flex items-center text-16px text-[#333333] font-normal"> <div class="flex items-center text-[16px] text-[#333333] font-normal">
<div class="mr-14px h-24px w-4px rounded-1px bg-[#1A65FF]"></div> <div class="mr-[14px] h-[24px] w-[4px] rounded-[1px] bg-[#1A65FF]"></div>
热门排行 热门排行
</div> </div>
<div v-for="item in recommendList" :key="item.id" class="mt-20px text-14px text-[#666] font-normal">{{ item.title }}</div> <div v-for="item in recommendList" :key="item.id" class="mt-[20px] text-[14px] text-[#666] font-normal">{{ item.title }}</div>
</div> </div>
<!-- --> <!-- -->
<!-- <div class="mt-20px box-border w-398px border border-[#EEEEEE] rounded-10px border-solid bg-[#FFFFFF] pa-20px"> <!-- <div class="mt-20px box-border w-398px border border-[#EEEEEE] rounded-10px border-solid bg-[#FFFFFF] pa-20px">
@ -110,26 +110,31 @@
title: '', title: '',
}) })
const pageRes = ref<TpageRes>({ // const pageRes = ref<TpageRes>({
list: [], // list: [],
total: 0, // total: 0,
// })
const {data: pageRes, refresh: getPage } = await useAsyncData(`draw-page-list-${Date.now()}`, async () => {
const res = await page(pageReq)
return res.data
}) })
const loading = ref(false) const loading = ref(false)
const getPage = () => { // const getPage = () => {
loading.value = true // loading.value = true
page(pageReq) // page(pageReq)
.then((res) => { // .then((res) => {
if (res.code === 0) { // if (res.code === 0) {
pageRes.value = res.data // pageRes.value = res.data
} // }
}) // })
.finally(() => { // .finally(() => {
loading.value = false // loading.value = false
}) // })
} // }
getPage() // getPage()
const handleCurrentChange = (page: number) => { const handleCurrentChange = (page: number) => {
pageReq.pageNum = page pageReq.pageNum = page
@ -148,17 +153,24 @@
getPage() getPage()
} }
// 猜你喜欢 const {data: recommendList} = await useAsyncData(`draw-recommend-list-${Date.now()}`, async () => {
const recommendList = ref<ProjectDrawPageRespVO[]>([]) // 猜你喜欢数据 const res = await getRelationRecommend({
const getRelationRecommendList = () => {
getRelationRecommend({
type: 4, type: 4,
}).then((res) => {
if (res.code === 0) {
console.log(res.data)
recommendList.value = res.data
}
}) })
} return res.data
getRelationRecommendList() })
// 猜你喜欢
// const recommendList = ref<ProjectDrawPageRespVO[]>([]) // 猜你喜欢数据
// const getRelationRecommendList = () => {
// getRelationRecommend({
// type: 4,
// }).then((res) => {
// if (res.code === 0) {
// console.log(res.data)
// recommendList.value = res.data
// }
// })
// }
// getRelationRecommendList()
</script> </script>

View File

@ -1,24 +1,25 @@
<template> <template>
<div class="ml-23px box-border min-h-930px w-516px border border-[#EEEEEE] rounded-12px border-solid bg-[#FFFFFF] px-33px py-22px"> <div class="ml-[23px] box-border min-h-[930px] w-[516px] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[33px] py-[22px]">
<div class="flex items-center"> <div class="flex items-center">
<img src="~/assets/images/preview.png" alt="" srcset="" width="16px" height="19px" /><span class="ml-7px text-18px text-[#333333] font-normal" <img src="~/assets/images/preview.png" alt="" srcset="" width="16px" height="19px" /><span class="ml-[7px] text-[18px] text-[#333333] font-normal"
>预览</span >预览</span
></div ></div
> >
<div class="mt-20px"> <div class="mt-[20px]">
<el-image :src="previewUrl" class="mb-16px max-h-320px max-w-460px min-h-200px" fit="contain"></el-image> <el-image :src="previewUrl" class="mb-[16px] max-h-[320px] max-w-[460px] min-h-[200px]" fit="contain"></el-image>
<span class="text-16px text-[#333333] font-normal">{{ previewName || '图纸标题' }}</span></div <span class="text-[16px] text-[#333333] font-normal">{{ previewName || '图纸标题' }}</span></div
> >
<div class="my-30px h-1px w-460px rounded-1px bg-[#EEEEEE]"></div> <div class="my-[30px] h-[1px] w-[460px] rounded-[1px] bg-[#EEEEEE]"></div>
<div class="flex items-center" <div class="flex items-center">
><img src="~/assets/images/tip.png" width="20px" height="20px" /><span class="ml-7px text-18px text-[#333333] font-normal" <img src="~/assets/images/tip.png" width="20px" height="20px" />
>上传遇到问题可以咨询</span <span class="ml-[7px] text-[18px] text-[#333333] font-normal">
></div 上传遇到问题可以咨询
> </span>
<div class="mt-20px text-center"><el-image src="https://picsum.photos/290/290?_t" alt="" srcset="" class="h-290px w290" /></div> </div>
<div class="mt-30px text-center text-16px text-[#333333] font-normal"> <div class="mt-[20px] text-center"><el-image src="https://picsum.photos/290/290?_t" alt="" srcset="" class="h-[290px] w-[290px]" /></div>
<div class="mt-[30px] text-center text-[16px] text-[#333333] font-normal">
<div>TEL13315189735 </div> <div>TEL13315189735 </div>
<div class="mt-4px">在线时间8:30-18:00</div> <div class="mt-[4px]">在线时间8:30-18:00</div>
</div> </div>
</div> </div>
</template> </template>

Some files were not shown because too many files have changed in this diff Show More