Compare commits

...

202 Commits

Author SHA1 Message Date
a12afc6bc1 优化kl-card-picture组件文本颜色样式 2025-10-16 23:09:12 +08:00
ae57f28357 优化排行榜组件并添加全局错误处理 2025-10-16 22:40:19 +08:00
3265c00fba feat: 启用排行榜组件并优化微信登录参数 2025-10-16 20:13:58 +08:00
ab92461635 优化微信扫码登录openId字段命名 2025-10-15 22:49:48 +08:00
3c6ed5a72c 优化微信扫码登录功能及token获取逻辑 2025-10-15 22:40:23 +08:00
9e225417a6 优化微信扫码登录功能实现 2025-10-15 22:27:12 +08:00
94f1828620 优化微信扫码登录组件UI及交互 2025-10-14 22:15:46 +08:00
58d3f2e890 feat: 添加微信授权成功通知功能 2025-10-14 08:58:36 +08:00
5baae7652f feat: 添加微信扫码登录功能 2025-10-13 23:03:55 +08:00
3f3ea878b9 优化提现手续费动态配置 2025-10-08 22:22:54 +08:00
89f3b9679d 优化交易中心UI展示 2025-10-08 21:16:07 +08:00
b0e5244a79 feat: 统一路由路径规范 2025-10-08 21:12:51 +08:00
f8176abaad 优化收益展示字段及数值 2025-10-08 20:50:30 +08:00
575f6fa2f1 优化VIP价格显示及移除工具函数 2025-10-08 20:39:51 +08:00
5ee69038fa 优化聊天页面UI及API接口调用 2025-09-30 22:20:07 +08:00
2bc5a11946 优化热门绘画组件查询参数处理 2025-09-29 09:37:36 +08:00
b23a85b9ec 优化首页组件代码及调试信息 2025-09-29 09:12:34 +08:00
f2c4d14007 feat: sosososs 2025-09-27 21:59:11 +08:00
82b6f4d63b feat: sosososs 2025-09-27 20:36:51 +08:00
d995ac2b37 feat: sosososs 2025-09-27 19:57:02 +08:00
01392a6329 feat: sosososs 2025-09-27 19:18:52 +08:00
689d12e632 feat: sosososs 2025-09-27 19:09:46 +08:00
8c50ffd479 优化评论功能API接口参数处理 2025-09-26 23:06:31 +08:00
ca7982c974 优化工具箱详情页评论功能及API接口 2025-09-26 22:13:28 +08:00
6c85256b96 优化详情页功能及API接口参数 2025-09-26 21:47:51 +08:00
972bd2c8b8 优化个人中心交易记录显示 2025-09-25 22:12:36 +08:00
ecf120221f 优化工具箱页面分类组件及交互功能 2025-09-25 22:10:26 +08:00
30fd03590c 优化API接口类型修改downloadId字段类型 2025-09-25 20:37:25 +08:00
38fc29ccaa 优化工具箱详情页面及API接口类型 2025-09-24 22:42:52 +08:00
70a75333e8 优化VIP卡片布局及工具箱详情页面 2025-09-24 22:03:23 +08:00
92ca0a4418 Merge branch 'main' of https://git.tuxixi.net/wangqiao/front-pc 2025-09-24 21:05:36 +08:00
6a002ceb16 优化工具箱详情页面移除冗余日志输出 2025-09-24 21:05:28 +08:00
7f7664e68f 优化侧边菜单样式内边距 2025-09-24 08:59:43 +08:00
6f58ba2389 优化工具箱详情页及API接口 2025-09-23 23:14:17 +08:00
33430e0888 优化详情页面包屑显示及ID字段 2025-09-23 22:37:51 +08:00
5b498724c4 优化推荐功能新增参数配置及上传工具 2025-09-23 22:19:33 +08:00
aef8799652 优化推荐功能新增参数配置 2025-09-23 21:34:42 +08:00
bf189f7f5b 优化工具箱详情页面样式及交互功能 2025-09-18 22:44:00 +08:00
06b60a4ff9 优化工具箱详情页面及点击跳转功能 2025-09-16 22:37:41 +08:00
c11116d9a9 优化个人中心头像更新提示文案 2025-09-16 22:07:36 +08:00
48d4ede98e 优化频道内容显示条件判断 2025-09-15 22:17:11 +08:00
fd31274a21 优化帖子删除接口及按钮事件冒泡处理 2025-09-15 22:16:12 +08:00
ec5cddae61 优化群组成员获取接口请求方法 2025-09-15 22:13:09 +08:00
e361ff9c79 feat: sosoos 2025-09-15 21:40:31 +08:00
c93cfeb266 优化缓存标识前缀区分模块 2025-09-15 21:37:32 +08:00
4b36bc2e06 优化个人中心提现管理界面样式 2025-09-15 21:34:05 +08:00
ce3aab1c76 优化个人中心表单必填项验证 2025-09-15 21:26:57 +08:00
0efb671bff 优化工具箱创建接口路径 2025-09-15 11:58:14 +08:00
c0d41b4d8d 优化BannerTips客服联系功能 2025-09-14 21:44:01 +08:00
394df57af1 优化工具箱分页参数及接口路径 2025-09-14 21:13:42 +08:00
ab57b6c9b2 优化邮箱验证码发送接口 2025-09-14 20:45:43 +08:00
87ce756f6b 优化邮箱验证码发送接口 2025-09-14 20:39:59 +08:00
9d2863d6d3 优化工具箱分类筛选功能 2025-09-14 19:34:54 +08:00
8677405266 feat: soso 2025-09-14 19:27:36 +08:00
a8b55412c6 优化个人中心地址选择功能 2025-09-14 17:35:47 +08:00
cc2f0d5eeb 优化工具箱分类筛选功能 2025-09-14 17:23:53 +08:00
9c62a6a198 优化用户注销功能及token处理逻辑 2025-09-14 17:02:57 +08:00
b1174fc1e3 优化工具发布分类及来源类型 2025-09-14 16:47:22 +08:00
e6c99fbbca 优化工具发布分类选择组件 2025-09-14 16:29:00 +08:00
239a1272b2 优化工具发布分类功能及参数 2025-09-14 16:22:30 +08:00
0d4c625b10 优化工具发布分类类型参数 2025-09-14 11:21:07 +08:00
dc824592ce 优化用户注销功能及逻辑处理 2025-09-14 11:17:46 +08:00
d096ebb83c 优化账号绑定功能及消息提示组件 2025-09-14 10:55:19 +08:00
a0a44b4551 优化账号绑定功能及社交解绑 2025-09-14 10:41:15 +08:00
cef6c8c341 优化图纸编辑功能和地址联动逻辑 2025-09-14 09:58:12 +08:00
9d4dda04c9 优化个人中心账号绑定功能 2025-09-13 22:43:38 +08:00
8754d3a333 优化工具发布表单和描述字数限制 2025-09-13 21:36:10 +08:00
8d9bc93955 优化TDK数据获取逻辑和空值处理 2025-09-13 13:49:22 +08:00
1e292102d1 优化个人中心表格字体样式 2025-09-13 13:42:52 +08:00
9417601ab1 优化个人中心表格样式和字体大小 2025-09-13 12:53:55 +08:00
2901b24f43 优化图纸编辑功能和提交逻辑 2025-09-13 12:51:48 +08:00
f3fb25349b 优化图纸编辑功能和提交逻辑 2025-09-13 12:47:10 +08:00
ad61430545 优化图纸预览和编辑功能 2025-09-13 11:50:44 +08:00
164690bcce 优化个人中心表格显示和操作功能 2025-09-13 10:17:32 +08:00
2ac31bc1a3 优化个人中心表单验证和路由跳转 2025-09-13 09:44:30 +08:00
41ff1b8f44 优化导航栏样式和间距调整 2025-09-12 08:50:27 +08:00
9ac4fa6e5d 优化上传表格组件显示和新增工具箱选项 2025-09-11 22:31:03 +08:00
0bc2bdb9bb 优化用户中心VIP图标显示位置 2025-09-11 21:43:17 +08:00
4db377161c 优化用户中心VIP图标位置和积分文案 2025-09-11 21:39:12 +08:00
a60b28aa22 优化账户安全表单样式调整 2025-09-11 21:25:14 +08:00
fec929bf06 refactor: 优化个人中心账户管理功能 2025-09-11 21:21:56 +08:00
b0b50ed960 优化用户头像样式和间距调整 2025-09-10 21:51:54 +08:00
78939951d9 feat: 更新公安备案链接并添加跳转功能 2025-09-10 21:46:27 +08:00
02bc9c00b0 feat: 更新页脚样式并添加公安备案信息 2025-09-10 21:35:29 +08:00
823e3d9f99 修复文件列表显示逻辑,使用otherFiles替代files 2025-09-09 11:05:07 +08:00
a1735a5e94 优化预览组件尺寸与图片展示 2025-09-08 10:31:28 +08:00
7839fa565e 更新用户头像和表单字数限制 2025-09-08 10:15:32 +08:00
9054376279 附件上传表单验证优化 2025-09-08 10:05:24 +08:00
3ae61916ca Merge branch 'main' of https://git.tuxixi.net/wangqiao/front-pc 2025-09-08 10:01:18 +08:00
1a86b4cb7e 添加项目版本号1.0.0 2025-09-08 10:00:52 +08:00
7a33f67aed feat: 添加通知列表功能和样式优化 2025-09-07 22:30:20 +08:00
9f5942546d refactor: 优化请求封装和用户状态管理 2025-09-07 15:26:01 +08:00
7c82166781 refactor: 调整预览组件高度样式 2025-09-06 21:11:55 +08:00
cd58054cfa feat: 添加客服微信功能和页面优化 2025-09-06 21:05:12 +08:00
615ac78156 refactor: 优化表单输入提示文本 2025-09-06 18:47:00 +08:00
18ebc08645 refactor: 移除表单项顶部边距 2025-09-06 18:42:55 +08:00
d006b3e5af refactor: 调整表单选择器宽度样式 2025-09-06 18:42:07 +08:00
b7d6b37e87 refactor: 优化上传表单组件和类型定义 2025-09-06 18:39:16 +08:00
24d2b82221 refactor: 优化工具箱分页和推荐列表逻辑 2025-09-05 22:06:29 +08:00
d24ece1724 refactor: 调整分页组件样式和布局 2025-09-05 21:47:20 +08:00
0e8fb5c838 refactor: 添加国外页面查询字段isDomestic 2025-09-05 21:43:22 +08:00
c8d47fd959 refactor: 优化登录表单组件并统一代码缩进 2025-09-05 09:30:18 +08:00
bdad6d4406 refactor: 更新图片资源路径引用方式 2025-09-05 08:54:25 +08:00
3d8ae8816d feat: sooso 2025-09-04 22:29:48 +08:00
c01f51a488 feat: sooso 2025-09-04 22:24:05 +08:00
5073c33e61 refactor: 更新标签栏组件并引入v3版本 2025-09-04 22:17:09 +08:00
4dcd59f1d4 refactor: 更新标签栏组件并引入v3版本 2025-09-04 22:09:23 +08:00
1a27904f23 refactor: 添加移动端底部导航栏并优化布局样式 2025-09-04 09:19:32 +08:00
85c49890d1 refactor: 清理移动端首页注释代码并优化布局 2025-09-04 09:01:24 +08:00
272c380c6e 移除移动端首页未使用组件导入 2025-09-04 08:59:39 +08:00
5092efdaeb 移除移动端首页组件 2025-09-04 08:46:41 +08:00
df3873c6eb feat: 更新移动端页面,添加新活动展示和分类图标,优化样式 2025-09-03 22:24:26 +08:00
addef04ff0 refactor: 重构搜索栏使用Vant组件并优化样式 2025-09-03 18:03:58 +08:00
b1d2378cec feat: 添加移动端页面组件和图片资源 2025-09-03 17:06:39 +08:00
7f2edb91e4 优化移动端布局样式和配置 2025-09-03 14:48:16 +08:00
694a032d0a 移除页面组件中的SeoHead标签 2025-09-03 11:34:22 +08:00
a96dbb67fe refactor: 重构导航组件使用动态TDK数据和路由 2025-09-03 11:33:00 +08:00
d26970fb3d 修复菜单组件重复添加首页问题 2025-09-03 10:54:15 +08:00
9222d47b37 移除菜单组件调试日志输出 2025-09-03 10:52:18 +08:00
acb7ccaf46 重构菜单组件使用动态TDK数据 2025-09-03 10:50:58 +08:00
e1c1fefa20 添加TDK列表接口并修正字段命名 2025-09-03 09:50:12 +08:00
21780580b8 添加网站TDK设置获取功能 2025-09-03 09:08:16 +08:00
e0507f3bd1 优化壁纸分类组件数据获取逻辑 2025-09-02 18:28:24 +08:00
fa971cff5b 优化版本列表获取逻辑,提升组件性能 2025-09-02 17:49:59 +08:00
a900399dd0 修复分类下拉框服务端渲染兼容性问题 2025-09-02 17:44:49 +08:00
8761a91f13 refactor: 更新移动端路由路径从/m到/mobile 2025-09-01 22:02:21 +08:00
f4312f5f0c refactor: 添加登录中间件和重命名系统中间件 2025-09-01 22:01:08 +08:00
2c2e6f600b 优化依赖配置,调整构建和样式设置 2025-09-01 15:27:52 +08:00
cb56f9b7ba 移动端目录路径从m改为mobile 2025-09-01 11:33:43 +08:00
be82375202 移动端路由路径从/m改为/mobile 2025-09-01 11:33:07 +08:00
a0f1ec758e 添加移动端布局组件和页面配置 2025-09-01 10:58:34 +08:00
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
384 changed files with 9734 additions and 100178 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 useDollarFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/posts/page', {query: params}) return useDollarFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/posts/page', { query: params })
} }
/** /**
@ -49,14 +49,14 @@ export const getChannelPosts = (params: { id: number }) => {
* @return {Promise} * @return {Promise}
*/ */
export const postsDelete = (params: { id: number }) => { export const postsDelete = (params: { id: number }) => {
return useDollarFetchRequest.del<IResponse<boolean>>('/prod-api/app-api/business/posts/delete', { params }) return useDollarFetchRequest.del<IResponse<boolean>>('/prod-api/app-api/business/posts/delete?id=' + params.id, {})
} }
/** /**
* 获取帖子详情 * 获取帖子详情
* @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 useDollarFetchRequest.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 useDollarFetchRequest.get<IResponse<ChannelRespVO>>('/prod-api/app-api/business/channel/get', {query: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'
},
})
} }
/** /**
@ -142,5 +147,5 @@ export const deleteChannelFollow = (params: { channelId: string }) => {
* 根据群组ID获取群组成员 * 根据群组ID获取群组成员
*/ */
export const getGroupMembers = (params: { channelId: string }) => { export const getGroupMembers = (params: { channelId: string }) => {
return useFetchRequest.get<IResponse<MemberUserRespDTO[]>>(`/prod-api/app-api/mqtt/session/users/${params.channelId}`) return useDollarFetchRequest.get<IResponse<MemberUserRespDTO[]>>(`/prod-api/app-api/mqtt/session/users/${params.channelId}`)
} }

View File

@ -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,22 +7,22 @@ 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 })
} }
/** /**
* 获取评论列表 * 获取评论列表
* @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; type: 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 })
} }
/** /**
* 发表评论 * 发表评论
* @return {Promise} * @return {Promise}
*/ */
export const createComment = (params: { relationId?: number | string; content?: string; projectId?: number | string }) => { export const createComment = (params: { relationId?: number | string; content?: string; projectId?: number | string; type: number }) => {
return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/app/project-comment/create', params) return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/app/project-comment/create', params)
} }
@ -31,14 +31,14 @@ 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', { query:params }) return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/top-list', { query: params })
} }
/** /**
* 举报 * 举报
* @return {Promise} * @return {Promise}
*/ */
export const report = (params: { id?: number | string; title?: string; comments?: string; files?: any; projectId: any; drawId: any }) => { export const report = (params: { id?: number | string; title?: string; comments?: string; files?: any; projectId?: any; drawId: any; type: any }) => {
return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/project-report/create', params) return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/project-report/create', params)
} }
@ -50,6 +50,14 @@ export const getUserInfo = (params: { id?: number | string }) => {
return useFetchRequest.get<IResponse<UserExtendSimpleRespDTO>>('/prod-api/app-api/business/app/project-draw/preview-user-info', { params }) return useFetchRequest.get<IResponse<UserExtendSimpleRespDTO>>('/prod-api/app-api/business/app/project-draw/preview-user-info', { params })
} }
/**
* 获取工具发布人信息
*/
export const getToolUserInfo = (params: { id?: number | string }) => {
return useFetchRequest.get<IResponse<UserExtendSimpleRespDTO>>('/prod-api/app-api/business/resource/publish-user-info', { params })
}
/** /**
* 当前用户的主要作品内容 * 当前用户的主要作品内容
*/ */
@ -59,7 +67,7 @@ export const getMainWork = (params: { id?: number | string; limit: number; membe
/** /**
* 创建内容信息 * 创建内容信息
*/ */
export const createContent = (params: { projectId: any; drawId: any }) => { export const createContent = (params: { projectId?: any; drawId: any; type: any }) => {
return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/project-member-favorites/create', params) return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/business/project-member-favorites/create', params)
} }

View File

@ -35,6 +35,7 @@ export interface ProjectRespVO {
projectTypeName: string projectTypeName: string
favoriteId?: string favoriteId?: string
relationDraws: RelationDraws[] relationDraws: RelationDraws[]
no?: string
filesInfo: { filesInfo: {
fileSize: string fileSize: string
count: number count: number
@ -136,6 +137,7 @@ export interface UserExtendSimpleRespDTO {
files: any[] files: any[]
fansCount: number fansCount: number
projectCount: number projectCount: number
postsNum?: number
} }
export interface ProjectDrawMemberRespVO { export interface ProjectDrawMemberRespVO {

View File

@ -7,6 +7,8 @@ import type {
ProjectDrawStatisticAppRespVO, ProjectDrawStatisticAppRespVO,
ProjectTrendingScoreUserInfoVO, ProjectTrendingScoreUserInfoVO,
PageResultIndexSettingRespVO, PageResultIndexSettingRespVO,
TdkSettingsRespVO,
TdkSettingsDO,
} from './type' } from './type'
/** /**
@ -15,7 +17,7 @@ import type {
* @returns * @returns
*/ */
export const hotTop = (params: ThotTopReq) => { export const hotTop = (params: ThotTopReq) => {
return useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/hot-top', { query:params }) return useDollarFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/app/project-draw/hot-top', { query: params })
} }
/** /**
@ -24,21 +26,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', { query: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 useFetchRequest.get<IResponse<ProjectDrawPageRespVO[]>>('/prod-api/app-api/business/project/index/draw-new', { query: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 useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/project/index/index-hot-tab', { query:params }) return useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/project/index/index-hot-tab', { query: params })
} }
/** /**
@ -59,7 +61,7 @@ export const top = (params: { type: number; limit: number }) => {
* 获取用户top数据 * 获取用户top数据
*/ */
export const userTop = (params: { type?: number }) => { export const userTop = (params: { type?: number }) => {
return useFetchRequest.get<IResponse<ProjectTrendingScoreUserInfoVO[]>>('/prod-api/app-api/business/project/index/user-top', { query:params }) return useFetchRequest.get<IResponse<ProjectTrendingScoreUserInfoVO[]>>('/prod-api/app-api/business/project/index/user-top', { query: params })
} }
/** /**
@ -81,3 +83,31 @@ export const getSettingPage = (params: { type: number }) => {
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 })
}
/**
* 获得网站 TDK设置
*/
export const getTDK = () => {
return useFetchRequest.get<IResponse<TdkSettingsRespVO>>('/prod-api/app-api/business/tdk-settings/get-index', {})
}
/**
* 获得网站 TDK栏目列表
*/
export const getTDKList = () => {
return useFetchRequest.get<IResponse<TdkSettingsDO[]>>('/prod-api/app-api/business/tdk-settings/list-menu', {})
}
/**
* 通知列表
*/
export const getNoticeList = () => {
return useDollarFetchRequest.get<IResponse<any>>('/prod-api/app-api/system/index-setting/notice-list', {})
}

View File

@ -105,3 +105,25 @@ export interface PageResultIndexSettingRespVO {
status: number status: number
createTime: string createTime: string
} }
export interface TdkSettingsRespVO {
title: string
describeText: string
keyword: string
}
export interface TdkSettingsDO {
createTime?: string
updateTime?: string
creator?: string
updater?: string
deleted?: boolean
id?: number
title?: string
icon?: string
type?: number
path: string
describeText?: string
keyword?: string
remark: string
}

View File

@ -27,8 +27,18 @@ export const loginByMobile = (params: { mobile: string; code: string; socialCode
/** /**
* 发送邮箱验证码 * 发送邮箱验证码
*/ */
export const sendEmailCode = (params: { email: string }) => { export const sendEmailCode = (data: { email: string }) => {
return useDollarFetchRequest.post<IResponse<any>>('/prod-api/app-api/member/auth/send-email-code', params) return useDollarFetchRequest.post<IResponse<any>>(
'/prod-api/app-api/member/auth/send-email-code?email=' + data.email,
{},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json, text/plain, */*',
'Access-Control-Allow-Origin-Type': '*',
},
}
)
} }
/** /**
@ -44,3 +54,10 @@ export const loginByEmail = (params: { email: string; code: string }) => {
export const resetPassword = (params: { password: string; code: string }) => { export const resetPassword = (params: { password: string; code: string }) => {
return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/member/user/update-password', params) return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/member/user/update-password', params)
} }
/**
* 授权成功通知后台
*/
export const notifyAuthSuccess = (params: { code: string; state: string }) => {
return useDollarFetchRequest.get<IResponse<any>>('/prod-api/app-api/member/auth/wx-login-url', { query: params })
}

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,26 +36,26 @@ 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 })
} }
/** /**
* 通过code获取token * 通过code获取token
*/ */
export const getTokenByCode = (params: { type: number; code: string; state: string }) => { export const getTokenByCode = (params: { type: number; code: string; state: string }) => {
return useDollarFetchRequest.post<IResponse<string>>('/prod-api/app-api/member/auth/token-by-code', params) return useDollarFetchRequest.post<IResponse<string>>('/prod-api/app-api/member/auth/token-by-code', params)
} }
/** /**
* 社交快捷登录,使用 code 授权码 * 社交快捷登录,使用 code 授权码
*/ */
export const socialLoginByCode = (params: { type: number; code: string; state: string }) => { export const socialLoginByCode = (params: { type?: number; code?: string; state?: string; openId?: string; sceneStr?: string }) => {
return useDollarFetchRequest.post< return useDollarFetchRequest.post<
IResponse<{ IResponse<{
accessToken: string accessToken: string
@ -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', {})
} }
/** /**
@ -43,7 +43,7 @@ export const updateUserExtend = (params: UserExtendSaveReqVO) => {
* @returns * @returns
*/ */
export const getUserAuthInfo = () => { export const getUserAuthInfo = () => {
return useFetchRequest.get<IResponse<UserAuthInfoRespVO>>('/prod-api/app-api/member/user-auth-info/get', {}) return useDollarFetchRequest.get<IResponse<UserAuthInfoRespVO>>('/prod-api/app-api/member/user-auth-info/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,56 @@ 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)
} }
/** /**
@ -148,3 +150,49 @@ export const deleteResource = (params: { id: number }) => {
export const getUserExtend = () => { export const getUserExtend = () => {
return useFetchRequest.get<IResponse<UserExtendRespVO>>('/prod-api/app-api/member/user-extend/get', {}) return useFetchRequest.get<IResponse<UserExtendRespVO>>('/prod-api/app-api/member/user-extend/get', {})
} }
/**
* 取消社交绑定
*/
export const cancelSocialBind = (params: { type: number; openid: string }) => {
return useDollarFetchRequest.post<IResponse<boolean>>('/prod-api/app-api/member/social-user/unbind', params)
}
/**
* 用户注销账号
*/
export const userLogout = () => {
return useDollarFetchRequest.del<IResponse<boolean>>('/prod-api/app-api/member/user/unregister-user', {})
}
/**
* 获取钱包配置
*/
export const getWalletConfig = () => {
return useDollarFetchRequest.get<
IResponse<{
rechargeRate: number
commissionRate: number
withdrawRateOfRecharge: number
withdrawRateOfEarn: number
}>
>('/prod-api/app-api/pay/wallet/get-config', {})
}
/**
* 微信登录二维码链接
*/
export const getLoginQrcode = () => {
return useDollarFetchRequest.get<IResponse<{ sceneStr: string; qrCodeUrl: string }>>('/prod-api/app-api/member/auth/wx-login-url', {})
}
/***
* member/auth/checkScanStatus
* 检查扫码登录状态
*/
export const checkScanStatus = (params: { sceneStr: string }) => {
return useDollarFetchRequest.get<IResponse<{ status: string; openId: string | null; message: string }>>('/prod-api/app-api/member/auth/checkScanStatus', {
query: params,
})
}

View File

@ -46,6 +46,8 @@ export interface UserExtendRespVO {
description: string description: string
authStatus: number authStatus: number
createTime: string createTime: string
wxOpenId: string
qqOpenId: string
files: { files: {
id: number id: number
memberId: number memberId: number
@ -153,6 +155,7 @@ export interface UserStatisticsCountRespVO {
currencyCount: number currencyCount: number
previewCount: number previewCount: number
revenueCount: number revenueCount: number
revenueBalance: number
} }
export interface PageResultProjectMemberFavoritesRespVO { export interface PageResultProjectMemberFavoritesRespVO {

View File

@ -1,6 +1,6 @@
import * as useDollarFetchRequest from '~/composables/useDollarFetchRequest' import * as useDollarFetchRequest from '~/composables/useDollarFetchRequest'
import * as useFetchRequest from '~/composables/useFetchRequest' import * as useFetchRequest from '~/composables/useFetchRequest'
import type { TcreateReq, TpageReq, TpageRes } from './types' import type { TcreateReq, TpageReq, TpageRes, ProjectResourceRespVO } from './types'
/** /**
* 新建工具箱 * 新建工具箱
@ -8,12 +8,23 @@ import type { TcreateReq, TpageReq, TpageRes } from './types'
* @returns * @returns
*/ */
export const create = (params: TcreateReq) => { export const create = (params: TcreateReq) => {
return useDollarFetchRequest.post<IResponse<number>>('/prod-api/app-api/business/app/project-resource/create', params) return useDollarFetchRequest.post<IResponse<number>>('/prod-api/app-api/business/resource/create', params)
} }
/*** /***
* 获得内容信息分页 * 获得内容信息分页
*/ */
export const page = (params: TpageReq) => { export const page = (params: any) => {
return useFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/app/project-resource/page', {query:params}) return useFetchRequest.get<IResponse<TpageRes>>('/prod-api/app-api/business/resource/page', {
query: params,
})
}
/**
* 获得工具箱
*/
export const get = (params: { id: string }) => {
return useFetchRequest.get<IResponse<ProjectResourceRespVO>>('/prod-api/app-api/business/resource/get', {
query: params,
})
} }

View File

@ -6,6 +6,9 @@ export interface TcreateReq {
createAddress?: string createAddress?: string
createIp?: string createIp?: string
projectType: number[] projectType: number[]
categoryId?: number
sourceType?: number
categoryName?: string
files: { files: {
id: number id: number
title: string title: string
@ -59,3 +62,57 @@ export interface TpageItem {
commentsPoint: number commentsPoint: number
ownedUserId: string ownedUserId: string
} }
export interface ProjectResourceRespVO {
id: number
title: string
labels: string[]
createAddress: string
createIp: string
projectType: number[]
categoryId: number
categoryName: string
favoriteId?: number
downloadId?: string
sourceType: number
ownedUserName?: string
ownedUserAvatar?: string
ownedUserIdInfo: {
id: number
nickName: string
avatar: string
}
files: {
id: number
title: string
fileId: number
drawId: number
type: number
url: string
sort: number
size: number
}[]
coverImages: {
id: number
title: string
fileId: number
drawId: number
type: number
url: string
sort: number
size: number
}[]
points: number
createTime: string
updateTime: string
status: number
recommend: boolean
iconUrl: string
hotPoint: number
description: string
previewPoint: number
previewUrl: string
previewImageUrl: string
commentsPoint: number
ownedUserId: string
}

View File

@ -16,7 +16,7 @@ export const create = (params: TcreateReq) => {
* @returns * @returns
*/ */
export const parent = (params: { type: string | number; parentId: number | string }) => { export const parent = (params: { type: string | number; parentId: number | string }) => {
return useFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/app/dict/parent', { query:params }) return useDollarFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/app/dict/parent', { query: params })
} }
/** /**
* 获取具有上下级的字典信息 * 获取具有上下级的字典信息
@ -24,7 +24,7 @@ export const parent = (params: { type: string | number; parentId: number | strin
* @returns * @returns
*/ */
export const parentV2 = (params: { type: string | number; parentId: number | string }) => { 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 }) return useDollarFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/app/dict/parent', { query: params })
} }
/** /**
* 获取具有上下级的字典信息 * 获取具有上下级的字典信息
@ -32,7 +32,7 @@ export const parentV2 = (params: { type: string | number; parentId: number | str
* @returns * @returns
*/ */
export const indexTabs = () => { export const indexTabs = () => {
return useFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/project/index/index-tab3') return useDollarFetchRequest.get<IResponse<parentRes[]>>('/prod-api/app-api/business/project/index/index-tab3')
} }
/** /**
* 模糊查询获取标签内容 * 模糊查询获取标签内容
@ -40,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 })
} }
/** /**
* 获取格式类型字典信息 * 获取格式类型字典信息
@ -56,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', {query:params}) return useFetchRequest.get<IResponse<pageRes>>('/prod-api/app-api/business/app/project-draw/page', { query: params })
} }
/** /**
* 获得项目表内容信息分页 * 获得项目表内容信息分页
@ -73,3 +73,24 @@ export const recommendTop = (params: recommendTopReq) => {
export const homeLabel = () => { export const homeLabel = () => {
return useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/app/dict/index-tab') return useFetchRequest.get<IResponse<ProjectDictNodeVO[]>>('/prod-api/app-api/business/app/dict/index-tab')
} }
/**
* 获取客服微信
*/
export const getWechat = () => {
return useDollarFetchRequest.get<IResponse<string>>('/prod-api/app-api/system/index-setting/kefu-wechat')
}
/**
* 查看图纸
*/
export const view = (params: { id: string | number; projectId: string | number }) => {
return useDollarFetchRequest.get<IResponse<TcreateReq>>('/prod-api/app-api/business/app/project-draw/get', { query: params })
}
/**
* 编辑图纸
*/
export const edit = (data: TcreateReq) => {
return useDollarFetchRequest.put<IResponse<boolean>>('/prod-api/app-api/business/app/project/update', data)
}

View File

@ -10,9 +10,9 @@ export interface FileItem {
// 定义整个 JSON 对象的类型 // 定义整个 JSON 对象的类型
export interface TcreateReq { export interface TcreateReq {
activeName: string activeName: string | number
id: number | string id: number | string
type: any[] type: number
isDomestic: number | string isDomestic: number | string
province: string // 省份编码 province: string // 省份编码
city: string // 城市编码 city: string // 城市编码
@ -61,6 +61,7 @@ export interface pageReq {
createAddress?: string createAddress?: string
createIp?: string createIp?: string
projectType?: any projectType?: any
recommend: boolean // 是否推荐
} }
export interface pageRes { export interface pageRes {
list: { list: {
@ -79,6 +80,7 @@ export interface pageRes {
previewPoint?: number previewPoint?: number
commentsPoint?: number commentsPoint?: number
hotPoint?: number hotPoint?: number
source?: number
}[] }[]
total: number total: number
} }

35
app.vue
View File

@ -1,12 +1,33 @@
<template> <template>
<div> <div class="flex flex-col flex-1">
<NuxtLoadingIndicator /> <NuxtLoadingIndicator />
<NuxtLayout> <NuxtLayout>
<NuxtPage /> <NuxtPage />
</NuxtLayout> </NuxtLayout>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import zhCn from 'element-plus/es/locale/lang/zh-cn' 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> </script>

BIN
assets/images/activity1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
assets/images/activity2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 434 KiB

BIN
assets/images/bg-yy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
assets/images/crown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
assets/images/faxian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
assets/images/info copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
assets/images/info.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
assets/images/pingdao.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
assets/images/shouye.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
assets/images/tuzhi (1).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
assets/images/tuzhi (2).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

BIN
assets/images/tuzhi (3).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
assets/images/tuzhi (4).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
assets/images/tuzhi (5).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

BIN
assets/images/tuzhi (6).png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

BIN
assets/images/wode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
assets/images/x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
assets/images/yuan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/images/zhuan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

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,17 @@
<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]">
<div v-for="item in result.list" :key="item.id" class="mt-20px border-b-1px border-b-[#eee] border-b-solid pb-14px"> 共有{{ 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 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 +21,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>
@ -44,7 +46,7 @@
}, },
projectId: { projectId: {
type: Number, type: Number,
required: true, required: false,
}, },
}) })
@ -66,7 +68,12 @@
// 获取评论列表 // 获取评论列表
const handleGetCommentList = async () => { const handleGetCommentList = async () => {
const res = await getCommentList({ relationId: props.relationId, pageNum: query.value.pageNo, pageSize: query.value.pageSize }) const res = await getCommentList({
relationId: props.relationId,
pageNum: query.value.pageNo,
pageSize: query.value.pageSize,
type: props.projectId ? 1 : 2,
})
if (res.code === 0) { if (res.code === 0) {
result.value.list = res.data.list result.value.list = res.data.list
result.value.total = res.data.total result.value.total = res.data.total
@ -75,7 +82,7 @@
// 发表评论 // 发表评论
const handleCreateComment = async () => { const handleCreateComment = async () => {
const res = await createComment({ relationId: props.relationId, content: commentContent.value, projectId: props.projectId }) const res = await createComment({ relationId: props.relationId, content: commentContent.value, projectId: props.projectId || props.relationId, type: props.projectId ? 1 : 2 })
if (res.code === 0) { if (res.code === 0) {
commentContent.value = '' commentContent.value = ''
query.value.pageNo = 1 query.value.pageNo = 1
@ -87,6 +94,9 @@
() => props.relationId, () => props.relationId,
() => { () => {
handleGetCommentList() handleGetCommentList()
},
{
immediate: true,
} }
) )
</script> </script>

View File

@ -1,8 +1,9 @@
<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-[0px] text-[16px] text-[#999999] font-normal">
><span class="color-[#1A65FF]">{{ result?.total }}</span <el-button type="warning" class="mr-4px" @click="handleUpload">上传工具</el-button>
<span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div >个筛选结果</div
> >
<div class="content mt-[10px]"> <div class="content mt-[10px]">
@ -17,11 +18,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import KlTabBar from '~/components/kl-tab-bar/index.vue' import KlTabBar from '~/components/kl-tab-bar/v3/index.vue'
import CardPicture from '~/components/kl-card-picture/index.vue' import CardPicture from '~/components/kl-card-picture/index.vue'
import { ref } from 'vue' import { ref } from 'vue'
import type { pageRes, pageReq } from '~/api/upnew/types' import type { pageRes, pageReq } from '~/api/upnew/types'
import emptyImg from '~/assets/images/empty.png' import emptyImg from '~/assets/images/empty.png'
import useUserStore from '~/stores/user'
const query = defineModel<pageReq>('modelValue', { const query = defineModel<pageReq>('modelValue', {
required: true, required: true,
@ -34,17 +36,25 @@
const tabBar = ref([ const tabBar = ref([
{ {
label: '图纸推荐', label: '图纸推荐',
value: '', value: -1,
}, },
{ {
label: '原创图纸', label: '原创图纸',
value: 1, value: 1,
}, },
{ {
label: '最新上传', label: '转载分享',
value: 2, value: 2,
}, },
]) ])
const handleUpload = () => {
//
const store = useUserStore()
if (!store.token) {
return ElMessage.error('请先登录')
}
navigateTo('/upnew?drawType=1')
}
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

View File

@ -4,17 +4,33 @@
<div class="banner-text"> <div class="banner-text">
<h1 class="title">开启 CAD 学习之旅</h1> <h1 class="title">开启 CAD 学习之旅</h1>
<p class="subtitle">为你的创意引擎注入强劲动力驱动设计梦想在市场中乘风破浪</p> <p class="subtitle">为你的创意引擎注入强劲动力驱动设计梦想在市场中乘风破浪</p>
<button class="join-button">快来加入</button> <button class="join-button" @click="handleService">客服联系</button>
</div> </div>
<div class="banner-image"> <div class="banner-image">
<img src="~/assets/images/foreign_banner.png" alt="CAD工作环境" /> <img src="~/assets/images/foreign_banner.png" alt="CAD工作环境" />
</div> </div>
</div> </div>
</div> </div>
<!-- 打开客服弹窗 弄成组件 -->
<KlService v-if="dialogVisible" v-model:dialog-visible="dialogVisible"></KlService>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useUserStore from '~/stores/user'
import KlService from '~/components/kl-quick-menu/components/kl-service.vue'
// //
const dialogVisible = ref(false)
const handleService = () => {
//
const userStore = useUserStore()
if (!userStore.token) {
ElMessage.error('请先登录')
return
}
dialogVisible.value = true
//
// readCount.value = false
}
</script> </script>
<style scoped> <style scoped>

View File

@ -1,8 +1,8 @@
<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="query.source" :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-[0px] text-[16px] text-[#999999] font-normal"
><span class="color-[#1A65FF]">{{ result?.total }}</span ><span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div >个筛选结果</div
> >
@ -18,8 +18,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import KlTabBar from '~/components/kl-tab-bar/index.vue' import KlTabBar from '~/components/kl-tab-bar/v3/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'
@ -38,19 +39,19 @@
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,38 @@
<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 flex items-center" :title="props.itemInfo.title">
<div class="mt-8px text-15px text-[#999999] font-normal">by {{ props.itemInfo?.ownedUserIdInfo?.nickName }}</div> <img v-if="props.itemInfo.source === 2" src="~/assets/images/zhuan.png" alt="" srcset="" class="mr-[2px] w-[22px] relative bottom-[-4px]" />
</div> <img v-if="props.itemInfo.source === 1" src="~/assets/images/yuan.png" alt="" srcset="" class="mr-[2px] w-[20px] relative bottom-[-4px]" />
<div><img :src="props.itemInfo?.ownedUserIdInfo?.avatar" alt="" srcset="" class="h-40px w-40px rd-50%" /></div> {{ props.itemInfo.title }}
</div>
<div class="mt-24px flex items-center justify-between">
<div class="flex items-center justify-between text-14px text-[#666666] font-normal">
<div class="mr-9px flex items-center">
<img src="~/assets/images/look.png" alt="" srcset="" class="mr-2px h-17px" />
<span class="color-#666">{{ props.itemInfo.previewPoint }}</span>
</div> </div>
<div class="mr-9px flex items-center"> <div class="mt-[8px] text-[15px] text-[#666] font-normal">by {{ props.itemInfo?.ownedUserIdInfo?.nickName }}</div>
<img src="~/assets/images/add.png" alt="" srcset="" class="mr-2px h-22px" /> <div class="mt-[4px] text-[13px] text-[#999999] font-normal">{{ dayjs(props.itemInfo.createTime).format('YYYY-MM-DD HH:mm:ss') }}</div>
<span class="color-#666">{{ props.itemInfo.hotPoint }}</span> </div>
<div><img :src="props.itemInfo?.ownedUserIdInfo?.avatar" alt="" srcset="" class="h-[40px] w-[40px] rd-[50%]" /></div>
</div>
<div class="mt-[14px] flex items-center justify-between">
<div class="flex items-center justify-between text-[14px] text-[#666666] font-normal">
<div class="mr-[9px] flex items-center">
<img src="~/assets/images/look.png" alt="" srcset="" class="mr-[2px] h-[17px]" />
<span class="color-[#666]">{{ props.itemInfo.previewPoint }}</span>
</div>
<div class="mr-[9px] flex items-center">
<img src="~/assets/images/add.png" alt="" srcset="" class="mr-[2px] h-[22px]" />
<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>
@ -35,6 +40,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import dayjs from 'dayjs'
import type { PropType } from 'vue' import type { PropType } from 'vue'
import type { pageRes } from '~/api/upnew/types' import type { pageRes } from '~/api/upnew/types'
const props = defineProps({ const props = defineProps({
@ -46,11 +52,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

@ -1,16 +1,16 @@
<template> <template>
<div class="mt-30px bg-[#14213d] px-[40px] pb-[20px] pt-[50px] text-white lg:px-[80px] sm:px-[60px]"> <div class="mt-[30px] bg-[#14213d] px-[40px] pb-[20px] pt-[50px] text-white lg:px-[80px] sm:px-[60px]">
<!-- 主内容区 --> <!-- 主内容区 -->
<div class="mb-[40px] flex flex-col items-start justify-between gap-[30px] lg:flex-row"> <div class="mb-[40px] flex flex-col items-start justify-between gap-[30px] lg:flex-row">
<!-- 左侧 Logo --> <!-- 左侧 Logo -->
<div class="mx-auto w-[200px] shrink-0 lg:mx0"> <div class="mx-auto w-[200px] shrink-0 lg:mx-0">
<img src="~/assets/images/logo5.png" class="h-auto w-full" /> <img src="~/assets/images/logo5.png" class="h-auto w-full" />
</div> </div>
<!-- 中间部分 --> <!-- 中间部分 -->
<div class="grid grid-cols-2 mx-[80px] flex-1 gap-[10px] lg:grid-cols-3"> <div class="grid grid-cols-2 mx-[80px] flex-1 gap-[10px] lg:grid-cols-3">
<div v-for="(col, index) in bannerList?.slice(0, 3)" :key="index"> <div v-for="(col, index) in bannerList?.slice(0, 3)" :key="index">
<h3 v-if="handle(col)" class="ma-0px mb-[20px] pa-0px text-[16px]">{{ handle(col) }}</h3> <h3 v-if="handle(col)" class="ma-[0px] mb-[20px] pa-[0px] text-[16px]">{{ handle(col) }}</h3>
<ul> <ul>
<li <li
v-for="(item, i) in handle2(col)" v-for="(item, i) in handle2(col)"
@ -44,6 +44,8 @@
<div class="border-t border-white/20 pt-[20px] text-center text-[14px] text-white/70"> <div class="border-t border-white/20 pt-[20px] text-center text-[14px] text-white/70">
Copyright 2007-2025 图夕夕网络科技(成都)有限公司 Copyright 2007-2025 图夕夕网络科技(成都)有限公司
<a href="http://beian.miit.gov.cn/" target="_blank" class="text-white/90 hover:text-blue-400!"> 蜀ICP备2025141494号-1</a> <a href="http://beian.miit.gov.cn/" target="_blank" class="text-white/90 hover:text-blue-400!"> 蜀ICP备2025141494号-1</a>
<img src="~/assets/images/x.png" class="w-[20px] h-[20px] relative top-[2px]" />
<a href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=51010702043645" rel="noreferrer" target="_blank">川公网安备51010702043645号</a >
</div> </div>
<el-image-viewer v-if="showViewer" :url-list="previewImgList" :url-index="0" @close="showViewer = false"></el-image-viewer> <el-image-viewer v-if="showViewer" :url-list="previewImgList" :url-index="0" @close="showViewer = false"></el-image-viewer>

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" />
@ -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 app = 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: {
@ -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
} }

View File

@ -8,18 +8,18 @@
</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 tdkList"
:key="index" :key="index"
:to="item.path" :to="item.path"
class="parent-links relative rounded-lg px-3 py-2 text-[#1A65FF]" class="parent-links relative rounded-lg px-3 py-2 text-[#1A65FF]"
> >
{{ item.name }} {{ item.remark }}
<img v-if="item.path === '/communication/channel'" src="~/assets/images/hot.png" alt="火" class="absolute right-[-15px] top-[-2px]" /> <img v-if="item.path === '/channel'" src="~/assets/images/hot.png" alt="火" class="absolute right-[-12px] top-[0px]" />
</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">
@ -30,20 +30,30 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import useUserStore from '~/store/user' import useUserStore from '~/stores/user'
import { getTDKList } from '~/api/home/index'
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: '/' }, // { 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: '/channel' }, // { name: '交流频道', path: '/channel' },
// { name: '牛人社区', path: '/community' }, // // { name: '牛人社区', path: '/community' },
]) // ])
const { data: tdkList } = await useAsyncData('get-tdk-list-home', async () => {
const res = await getTDKList()
// 添加首页
if (!res.data.find((c) => c.remark === '首页')) {
res.data.unshift({ remark: '首页', path: '/' })
}
return res.data
})
// 是否登录 // 是否登录
const isLogin = computed(() => { const isLogin = computed(() => {
@ -52,12 +62,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

@ -3,7 +3,9 @@
<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="navigateTo('/')" /> <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 tdkList" :key="item.id" class="nav" :class="props.active === item.remark ? 'active' : ''" @click="handleClick(item.path)">{{
item.remark
}}</span>
</div> </div>
<div class="relative ml-[30px]"> <div class="relative ml-[30px]">
<el-input <el-input
@ -11,7 +13,7 @@
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>
<!-- 搜索框 获取到焦点 显示热门列表 --> <!-- 搜索框 获取到焦点 显示热门列表 -->
@ -37,12 +39,12 @@
</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] relative top-[7px] left-[10px]" /> <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-full w-full 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-[10px] 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="个人中心"
@ -64,10 +66,11 @@
import { ref, getCurrentInstance, computed, onMounted } from 'vue' import { ref, getCurrentInstance, computed, onMounted } from 'vue'
import { Setting, SwitchButton } from '@element-plus/icons-vue' import { Setting, SwitchButton } from '@element-plus/icons-vue'
import { page } from '~/api/upnew/index' import { page } from '~/api/upnew/index'
import { top } from '~/api/home/index' import { top, getTDKList } 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 app = useNuxtApp() const app = useNuxtApp()
@ -91,7 +94,15 @@
return !!userStore.token return !!userStore.token
}) })
const navList = ref(['首页', '图纸', '文本', '模型', '国外专区', '工具箱', '交流频道']) const { data: tdkList } = await useAsyncData('get-tdk-list-nav', async () => {
const res = await getTDKList()
// 添加首页
if (!res.data.find((c) => c.remark === '首页')) {
res.data.unshift({ remark: '首页', path: '/' })
}
return res.data
})
// const navList = ref(['首页', '图纸', '文本', '模型', '国外专区', '工具箱', '交流频道'])
const loading = ref(false) const loading = ref(false)
const handleHot = async () => { const handleHot = async () => {
@ -130,57 +141,34 @@
} }
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/${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 || ''}`) 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 || ''}`) 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) { navigateTo(item)
case '首页':
navigateTo({ path: '/'}) // 修改为在新窗口打开
break
case '图纸':
navigateTo('/drawe') // 修改为在新窗口打开
break
case '文本':
navigateTo('/text') // 修改为在新窗口打开
break
case '模型':
navigateTo('/model') // 修改为在新窗口打开
break
case '国外专区':
navigateTo('/foreign') // 修改为在新窗口打开
break
case '牛人社区':
navigateTo('/community') // 修改为在新窗口打开
break
case '交流频道':
navigateTo('/channel') // 修改为在新窗口打开
break
case '工具箱':
navigateTo('/toolbox') // 修改为在新窗口打开
break
default:
break
}
} }
const handleLogin = () => { const handleLogin = () => {
app?.$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,17 +1,17 @@
<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> -->
</div> </div>
<div class="vip-card-price"> <div class="vip-card-price">
<span class="price">{{ accDiv(item.payPrice || 0, 100) }}</span> <span class="price">{{ item.payPrice }}</span>
<span class="per">/1</span> <span class="per">/1</span>
</div> </div>
<ul class="vip-card-features"> <ul class="vip-card-features">
@ -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>
@ -36,12 +36,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, watch, onMounted } from 'vue' import { ref, watch, onMounted } from 'vue'
import { listVip, submitPayOrder, getPayStatus } from '~/api/pay/index' import { listVip, submitPayOrder, getPayStatus } from '~/api/pay/index'
import { accDiv } from '~/utils/utils' // import { accDiv } from '~/utils/utils'
import { Close } from '@element-plus/icons-vue' import { Close } from '@element-plus/icons-vue'
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>
@ -145,15 +153,17 @@
.vip-cards { .vip-cards {
display: flex; display: flex;
gap: 32px; gap: 32px;
justify-content: center; /* justify-content: center; */
margin: 24px 0; margin: 24px 0;
overflow-x: auto;
padding: 10px;
} }
.vip-card { .vip-card {
background: #fff; background: #fff;
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 +241,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

@ -7,7 +7,7 @@
> >
<el-tab-pane v-for="(item, index) in data" :key="index" :label="item.label" :name="item.value"> <el-tab-pane v-for="(item, index) in data" :key="index" :label="item.label" :name="item.value">
<template v-if="showNum" #label> <template v-if="showNum" #label>
<img v-if="item.value === tabActive && showIcon" src="~/assets/images/2.png" alt="" srcset="" class="mr-7px" /> <img v-if="item.value === tabActive && showIcon" src="~/assets/images/2.png" alt="" srcset="" class="mr-[7px]" />
<span>{{ item.label }}</span> <span>{{ item.label }}</span>
<el-badge :value="item.num" class="item" :max="9999999999999" :hidden="!item.num" /> <el-badge :value="item.num" class="item" :max="9999999999999" :hidden="!item.num" />
</template> </template>

View File

@ -0,0 +1,47 @@
<template>
<div class="flex items-center gap-[50px] mb-[20px]">
<el-link
v-for="(item, index) in props.data"
:key="index"
class="text-[15px]!"
:underline="modelValue === item.value ? 'always' : 'never'"
:type="modelValue === item.value ? 'primary' : 'info'"
@click="handleChange(item)"
>
{{ item.label }}
</el-link>
</div>
</template>
<script lang="ts" setup>
import type { PropType } from 'vue'
const props = defineProps({
data: {
type: Array as PropType<{ num?: number; label: string; value: string | number; [key: string]: any }[]>,
default: () => [],
},
})
const emits = defineEmits(['change'])
const modelValue = defineModel<any>('modelValue', {
required: true,
}) // 双向绑定的value
const handleChange = (value: { value: string | number; [key: string]: any }) => {
modelValue.value = value.value
emits('change', value)
}
</script>
<style lang="scss" scoped>
:deep(.el-link.is-underline) {
&::hover {
color: #1a65ff !important; // 鼠标悬停时的颜色
}
&::after {
bottom: -3px;
border-bottom: 2px solid #1a65ff; // 去掉下划线
}
}
</style>

View File

@ -323,7 +323,7 @@
// 判断是否是图片 // 判断是否是图片
const handelFileType = (fileName: string) => { const handelFileType = (fileName: string) => {
const ext = fileName.split('.').pop()?.toLowerCase() || '' const ext = fileName?.split('.').pop()?.toLowerCase() || ''
return ['png', 'jpg', 'jpeg'].includes(ext) return ['png', 'jpg', 'jpeg'].includes(ext)
} }
</script> </script>

View File

@ -1,30 +1,38 @@
<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" <el-breadcrumb-item v-for="(item, index) in breadList" :key="item.name" class="cursor-pointer" @click="handleClickBread(item, index)">{{
@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 v-for="(item, index) in projectTypeList" :key="index" <div
v-for="(item, index) in projectTypeList"
: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]' : ''" @click="handleClick(item)">{{ :class="item.id === query.projectType ? '!bg-[#ebeefe] !text-[#1A65FF]' : ''"
item.name }}</div> @click="handleClick(item)"
>{{ 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 v-for="(item, index) in editionsList" :key="index" <div
v-for="(item, index) in editionsList"
: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]' : ''" @click="query.editions = item.id"> :class="item.id === query.editions ? '!bg-[#ebeefe] !text-[#1A65FF]' : ''"
{{ item.name }}</div> @click="query.editions = item.id"
>
{{ item.name }}</div
>
</div> </div>
</div> </div>
<!-- <div class="mb-14px flex items-start"> <!-- <div class="mb-14px flex items-start">
@ -43,123 +51,156 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
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 { ArrowRight } from '@element-plus/icons-vue' import { getDictTree } from '~/api/home/index'
import { ArrowRight } from '@element-plus/icons-vue'
const props = defineProps({ const props = defineProps({
type: { type: {
type: Number, type: Number,
default: 1, default: 1,
}, },
id: { id: {
type: String, type: String,
default: '', default: '',
}, },
groundId: { groundId: {
type: String, type: String,
default: '', default: '',
}, },
}) })
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 { data: editionsList } = useAsyncData(`editionsList-${props.type}}`, async () => {
const res = await parent({ type: 2, parentId: 0 })
const all = [{ id: '-1', name: '全部' }]
return [...all, ...res.data]
})
// 获取面包屑
const { data: breadList } = await useAsyncData(
`breadList-${props.type}-${props.id}-${query.value.projectType}}`,
async () => {
const res = await getDictTree({ type: 1, id: query.value.projectType })
const handleParentId = (type?: string) => { const all = [
if (level?.value?.length > 1) { {
if (type === 'init' && level.value.find((c: any) => c.isChildren)) { id: -1,
return level.value[level.value.length - 2].id || '' // 获取最后一个元素的 id 或 defaul name: props.type === 1 ? '图纸库' : props.type === 3 ? '模型库' : '文本库',
isChildren: false,
},
]
const arr = [...res.data, ...all]
return arr.reverse()
}
)
// const projectTypeList = ref<any>([])
/** 获取分类下拉框 */
// const getParent = (type?: string) => {
// parent({
// type: 1,
// parentId: handleParentId(type),
// }).then((res) => {
// if (Array.isArray(res.data)) {
// // projectTypeList.value = [...[{ id: handleParentId(type), name: '全部' }], ...res.data]
// }
// })
// }
// getParent('init')
/** 版本 */
// const editionsList = ref<any>([])
// const getEditionsList = () => {
// parent({
// type: 2,
// parentId: 0,
// }).then((res) => {
// if (Array.isArray(res.data)) {
// // editionsList.value = [...[{ id: '', name: '全部' }], ...res.data]
// }
// })
// }
// getEditionsList()
console.log('breadList.value.length----', breadList.value);
// 服务端渲染兼容方案:顺序执行异步操作
// 1. 先获取面包屑数据已在上面通过useAsyncData获取
// 2. 顺序获取分类下拉框数据
// 创建一个函数来获取分类数据,确保服务端渲染时能获取到数据
const getProjectTypeList = async () => {
let parentId: any = '0'
// 计算parentId的逻辑
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
}
}
console.log('parentId', parentId);
try {
// 获取分类数据
const res = await parent({ type: 1, parentId: parentId })
const all = [{ id: parentId === '0' ? '-1' : parentId, name: '全部' }]
return [...all, ...res.data]
} catch (error) {
console.error('获取分类数据失败:', error)
return []
} }
return level.value[level.value.length - 1].id || '' // 获取最后一个元素的 id 或 defaul
} }
return '0'
} // 使用useAsyncData获取分类列表确保服务端渲染兼容性
const { data: projectTypeList } = await useAsyncData(
// const projectTypeList = ref<any>([]) `projectType-draw-${props.type}-${query.value.projectType}}`,
/** 获取分类下拉框 */ getProjectTypeList,
// const getParent = (type?: string) => { { server: true } // 确保在服务端执行
// parent({ )
// type: 1,
// parentId: handleParentId(type),
// }).then((res) => {
// if (Array.isArray(res.data)) {
// // projectTypeList.value = [...[{ id: handleParentId(type), name: '全部' }], ...res.data]
// }
// })
// }
// getParent('init')
/** 版本 */
// const editionsList = ref<any>([])
// const getEditionsList = () => {
// parent({
// type: 2,
// parentId: 0,
// }).then((res) => {
// if (Array.isArray(res.data)) {
// // editionsList.value = [...[{ id: '', name: '全部' }], ...res.data]
// }
// })
// }
// getEditionsList()
/** 是否是初始化 */ const handleClick = (row: any) => {
const queryType = ref('init') query.value.title = ''
/**获取分类下拉框 */ query.value.projectType = row.id
const { data: projectTypeList, refresh } = useAsyncData(`projectType-draw-${props.type}-${Date.now()}`, async () => { // if (row.name === '全部') return
const res = await parent({ type: 1, parentId: handleParentId(queryType.value) }) // const isChildren = breadList.value.find((c: any) => c.isChildren)
const all= [{ id: handleParentId(queryType.value), name: '全部' }] // if (!row.isChildren && isChildren) {
return [...all, ...res.data] // const index = breadList.value.length - 1
}) // breadList.value[index] = { id: row.id, name: row.name, isChildren: true }
// } else if (!row.isChildren && !isChildren) {
// breadList.value.push({ id: row.id, name: row.name, isChildren: true })
// } else {
// breadList.value.push({ id: row.id, name: row.name })
// // getParent()
// refresh()
// }
}
/** 版本 */ const handleClickBread = (row: any, index: number) => {
const { data: editionsList } = useAsyncData(`editionsList-${props.type}-${Date.now()}`, async () => { // breadList.value.splice(index + 1)
const res = await parent({ type: 2, parentId: 0 }) query.value.title = ''
const all = [{ id: '', name: '全部' }] query.value.projectType = row.id
return [...all, ...res.data]
})
const handleClick = (row: any) => {
query.value.title = ''
query.value.projectType = row.id
if (row.name === '全部') return
const isChildren = level.value.find((c: any) => c.isChildren)
if (!row.isChildren && isChildren) {
const index = level.value.length - 1
level.value[index] = { id: row.id, name: row.name, isChildren: true }
} else if (!row.isChildren && !isChildren) {
level.value.push({ id: row.id, name: row.name, isChildren: true })
} else {
level.value.push({ id: row.id, name: row.name })
// getParent() // getParent()
queryType.value = '' // refresh()
refresh()
} }
}
const handleClickBread = (row: any, index: number) => {
level.value.splice(index + 1)
query.value.title = ''
query.value.projectType = row.id
// getParent()
queryType.value = ''
refresh()
}
</script> </script>

View File

@ -0,0 +1,181 @@
<template>
<view class="design-list">
<view class="design-item" v-for="(item, index) in items" :key="index">
<view class="design-preview">
<image :src="item.image" mode="aspectFit"></image>
</view>
<view class="design-info">
<view class="design-title">{{ item.title }}</view>
<view class="design-author">by:{{ item.author }}</view>
<view class="design-stats">
<view class="stat-item">
<text class="iconfont">👁</text>
<text>{{ item.views }}</text>
</view>
<view class="stat-item">
<text class="iconfont">👍</text>
<text>{{ item.likes }}</text>
</view>
<view class="stat-item">
<text class="iconfont">💬</text>
<text>{{ item.comments }}</text>
</view>
</view>
</view>
<view class="design-action">
<button class="view-btn">查看</button>
</view>
</view>
</view>
</template>
<script>
export default {
name: "RecommendItem",
props: {
currentTab: {
type: Number,
default: 0,
},
},
data() {
return {
items: [
{
id: 1,
title: "高压细水雾灭火推车描述",
author: "赵其",
image: "/static/images/activity1.png",
views: 128,
likes: 16,
comments: 35,
url: "/pages/drawings/detail?id=1",
},
{
id: 2,
title: "高压细水雾灭火推车描述",
author: "赵其",
image: "/static/images/activity2.png",
views: 128,
likes: 16,
comments: 35,
url: "/pages/drawings/detail?id=2",
},
{
id: 1,
title: "室内CAD模型设计室内CAD模型设计",
author: "张泽",
image: "/static/images/activity1.png",
views: 95,
likes: 12,
comments: 28,
url: "/pages/models/detail?id=1",
},
{
id: 1,
title: "CAD设计规范文档",
author: "李华",
image: "/static/images/activity2.png",
views: 156,
likes: 23,
comments: 42,
url: "/pages/documents/detail?id=1",
},
],
};
},
methods: {
viewDetail(item) {
console.log("View detail for:", item.title);
uni.navigateTo({
url: item.url,
});
},
},
};
</script>
<style lang="scss" scoped>
// 变量定义
$primary-color: #007aff;
$text-color: #333;
$secondary-text: #666;
$light-text: #999;
$bg-color: #f0f4f9;
$white: #fff;
$border-color: #eee;
$border-radius: 15rpx;
.design-list {
// padding: 10rpx 20rpx;
.design-item {
display: flex;
margin-bottom: 20rpx;
background-color: $white;
border-radius: $border-radius;
border: 1px solid $border-color;
padding: 17rpx 10rpx;
box-sizing: border-box;
position: relative;
box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
.design-preview {
width: 220rpx;
height: 168rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
.design-info {
flex: 1;
padding: 0 20rpx;
.design-title {
font-size: 27rpx;
margin-bottom: 10rpx;
}
.design-author {
font-size: 25rpx;
color: $light-text;
margin-bottom: 30rpx;
}
}
.design-stats {
display: flex;
.stat-item {
display: flex;
align-items: center;
margin-right: 20rpx;
font-size: 24rpx;
color: $light-text;
.iconfont {
margin-right: 5rpx;
}
}
}
.design-action {
position: absolute;
right: 20rpx;
bottom: 20rpx;
.view-btn {
background-color: $primary-color;
color: $white;
font-size: 25rpx;
padding: 0rpx 25rpx !important;
border-radius: 5rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,16 @@
<template>
<div class="works">
<RecommendList />
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'Works' })
import RecommendList from "~/components/m/RecommendItem/index.vue";
</script>
<style lang="scss" scoped>
.works {
width: 100%;
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<div
class="cert-item"
v-for="(cert, index) in talentInfo.certificates"
:key="index"
>
<div
v-if="index !== talentInfo.certificates.length - 1"
class="border-left"
></div>
<div class="cert-dot"></div>
<div class="cert-info">
<span class="cert-year">{{ cert.year }}</span>
<span class="cert-desc">{{ cert.description }}</span>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'Certificates' })
interface Certificate { year: string; description: string }
interface TalentInfo { certificates: Certificate[] }
defineProps<{ talentInfo: TalentInfo }>()
</script>
<style lang="scss" scoped>
.border-left {
width: 1px;
height: calc(100%);
border-left: 2px dotted #e6f0ff;
position: absolute;
left: 7rpx;
top: 0;
}
// 证书列表
.cert-item {
display: flex;
align-items: flex-start;
position: relative;
}
.cert-dot {
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background-color: #1a65ff;
// margin-top: 12rpx;
margin-right: 20rpx;
flex-shrink: 0;
z-index: 1;
}
.cert-info {
flex: 1;
margin-bottom: 11rpx;
margin-top: -10rpx;
padding-bottom: 32rpx;
}
.cert-year {
font-size: 25rpx;
font-weight: bold;
color: #333;
display: block;
margin-bottom: 8rpx;
}
.cert-desc {
font-size: 21rpx;
color: #666;
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<div class="flex">
<div
class="cert-technicalCertificates"
v-for="(cert, index) in 10"
:key="index"
>
<img
class="cert-image"
:src="`https://picsum.photos/90/90?random=${index}`"
alt="certificate"
/>
</div>
</div>
</template>
<script setup lang="ts">
defineOptions({ name: 'TechnicalCertificates' })
</script>
<style lang="scss" scoped>
.flex {
display: flex;
flex-wrap: wrap;
gap: 50rpx;
}
.cert-technicalCertificates {
width: 135rpx;
height: 191rpx;
.cert-image {
width: 100%;
height: 100%;
}
}
</style>

View File

@ -0,0 +1,201 @@
<template>
<!-- 顶部导航栏 -->
<view class="nav-bar">
<view class="nav-item active">全部</view>
<view class="nav-item">机械设备</view>
<view class="nav-item">零部件模型</view>
<view class="nav-item">交通运输</view>
<view class="nav-item">电子产品</view>
<view class="expand-btn" @tap="toggleCategoryMenu">
<text class="expand-icon"></text>
</view>
</view>
<!-- 全部分类浮窗 -->
<view
class="category-popup"
v-if="showCategoryMenu"
@tap.stop="closeCategoryMenu"
>
<view class="popup-content" @tap.stop>
<view class="category-list">
<view
:key="index"
v-for="(section, index) in categoryList"
class="category-item"
@tap="selectCategory(section)"
>
{{ section.name }}
</view>
</view>
</view>
</view>
<!-- 筛选选项 -->
<view class="filter-bar">
<view class="filter-item"> 软件分类 <text class="icon-down"></text> </view>
<view class="filter-item"> 图纸类型 <text class="icon-down"></text> </view>
</view>
</template>
<script>
export default {
data() {
return {
showCategoryMenu: false,
categoryList: [
{ name: "机械设备", active: false },
{ name: "零部件模型", active: false },
{ name: "交通运输", active: false },
{ name: "电子产品", active: false },
{ name: "机械设备", active: false },
{ name: "零部件模型", active: false },
{ name: "交通运输", active: false },
{ name: "电子产品", active: false },
{ name: "机械设备", active: false },
{ name: "零部件模型", active: false },
{ name: "交通运输", active: false },
{ name: "电子产品", active: false },
],
};
},
methods: {
toggleCategoryMenu() {
this.showCategoryMenu = !this.showCategoryMenu;
},
closeCategoryMenu() {
this.showCategoryMenu = false;
},
selectCategory(section) {
// 演示用显示toast提示
},
},
onLoad(options) {
console.log(options);
// 监听点击事件,点击页面空白处关闭菜单
uni.$on("page-click", () => {
if (this.showCategoryMenu) {
this.closeCategoryMenu();
}
});
},
onUnload() {
// 移除事件监听
uni.$off("page-click");
},
};
</script>
<style scoped lang="scss">
// 变量定义
$primary-color: #007aff;
$text-color: #333;
$secondary-text: #666;
$light-text: #999;
$bg-color: #fff;
$white: #fff;
$border-color: #eee;
$border-radius: 10rpx;
$mask-bg: rgba(0, 0, 0, 0.5);
.nav-bar {
display: flex;
background-color: $white;
padding: 10rpx 0;
position: relative;
padding-right: 60rpx;
.nav-item {
flex: 1;
text-align: center;
font-size: 25rpx;
padding: 15rpx 0;
&.active {
color: $primary-color;
font-weight: bold;
}
}
.expand-btn {
position: absolute;
right: 0;
top: 0;
height: 100%;
width: 80rpx;
display: flex;
align-items: center;
justify-content: center;
.expand-icon {
font-size: 40rpx;
color: $text-color;
}
}
}
// 分类浮窗样式
.category-popup {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: $mask-bg;
z-index: 999;
display: flex;
flex-direction: column;
align-items: center;
.popup-content {
width: 100%;
max-height: 80vh;
background-color: $white;
animation: slideDown 0.3s ease;
overflow-y: auto;
.category-list {
padding: 20rpx;
display: flex;
flex-wrap: wrap;
.category-item {
padding: 8rpx 12.94rpx;
box-sizing: border-box;
margin-right: 3%;
margin-bottom: 20rpx;
text-align: center;
font-size: 25rpx;
background-color: $bg-color;
border-radius: 4rpx;
border: 1px solid $border-color;
color: #666;
}
}
}
}
// 滑入动画
@keyframes slideDown {
from {
transform: translateY(-100%);
opacity: 0.5;
}
to {
transform: translateY(0);
opacity: 1;
}
}
.filter-bar {
display: flex;
padding: 20rpx 30rpx;
.filter-item {
margin-right: 30rpx;
font-size: 25rpx;
color: $secondary-text;
}
.icon-down {
font-size: 24rpx;
}
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<div class="relative mt-[34px] w-[100%]">
<KlTabBar v-model="query.source" :data="tabBar" />
<div class="absolute right-[0px] top-[0px] text-[16px] text-[#999999] font-normal">
<el-button type="warning" class="mr-4px" @click="handleUpload">上传工具</el-button>
<span class="color-[#1A65FF]">{{ result?.total }}</span
>个筛选结果</div
>
<div class="content mt-[10px]">
<el-row :gutter="20">
<el-col v-for="(item, index) in result?.list" :key="index" :span="6">
<CardPicture :item-info="item" />
</el-col>
</el-row>
<el-empty v-if="!result?.list.length" :image="emptyImg"></el-empty>
</div>
</div>
</template>
<script lang="ts" setup>
import KlTabBar from '~/components/kl-tab-bar/v3/index.vue'
import CardPicture from '~/components/kl-card-picture/index.vue'
import { ref } from 'vue'
import type { pageRes, pageReq } from '~/api/upnew/types'
import emptyImg from '~/assets/images/empty.png'
import useUserStore from '~/stores/user'
const query = defineModel<pageReq>('modelValue', {
required: true,
})
const result = defineModel<pageRes | null>('result', {
required: true,
})
const tabBar = ref([
{
label: '模型推荐',
value: -1,
},
{
label: '原创模型',
value: 1,
},
{
label: '转载分享',
value: 2,
},
])
const handleUpload = () => {
// 先判断登录
const store = useUserStore()
if (!store.token) {
return ElMessage.error('请先登录')
}
navigateTo('/upnew?drawType=3')
}
</script>
<style lang="scss" scoped></style>

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

@ -0,0 +1,61 @@
<template>
<div class="relative mt-[34px] w-[100%]">
<KlTabBar v-model="query.source" :data="tabBar" />
<div class="absolute right-[0px] top-[0px] text-[16px] text-[#999999] font-normal">
<el-button type="warning" class="mr-4px" @click="handleUpload">上传工具</el-button>
<span class="color-[#1A65FF]">{{ result?.total }}</span>
个筛选结果
</div>
<div class="content mt-[10px]">
<el-row :gutter="20">
<el-col v-for="(item, index) in result?.list" :key="index" :span="6">
<CardPicture :item-info="item" />
</el-col>
</el-row>
<el-empty v-if="!result?.list?.length" :image="emptyImg"></el-empty>
</div>
</div>
</template>
<script lang="ts" setup>
import KlTabBar from '~/components/kl-tab-bar/v3/index.vue'
import CardPicture from '~/components/kl-card-picture/index.vue'
import { ref } from 'vue'
import type { pageRes, pageReq } from '~/api/upnew/types'
import emptyImg from '~/assets/images/empty.png'
import useUserStore from '~/stores/user'
const query = defineModel<pageReq>('modelValue', {
required: true,
})
const result = defineModel<pageRes | null>('result', {
required: true,
})
const tabBar = ref([
{
label: '文本推荐',
value: -1,
},
{
label: '原创文本',
value: 1,
},
{
label: '转载分享',
value: 2,
},
])
const handleUpload = () => {
// 先判断登录
const store = useUserStore()
if (!store.token) {
return ElMessage.error('请先登录')
}
navigateTo('/upnew?drawType=2')
}
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,77 @@
<template>
<div class="box-border w-[100%] border border-[#EEEEEE] rounded-[12px] border-solid bg-[#FFFFFF] px-[20px] py-[26px]">
<div class="mb-[10px] flex items-start">
<div class="flex-shrink-0 text-[15px] text-[#333333] font-normal">软件分类</div>
<div class="ml-[30px] mt-[-6px] flex flex-wrap">
<div
v-for="(item, index) in projectTypeList"
:key="index"
class="mb-[8px] mr-[26px] cursor-pointer rounded-[15px] px-[15px] py-[6px] text-[14px] text-[#666666] font-normal"
:class="item.id === query ? '!bg-[#ebeefe] !text-[#1A65FF]' : ''"
@click="handleClick(item)"
>{{ item.name }}</div
>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue'
import { parent } from '~/api/upnew/index'
import type { pageReq } from '~/api/upnew/types'
import { getDictTree } from '~/api/home/index'
import { ArrowRight } from '@element-plus/icons-vue'
const props = defineProps({
type: {
type: Number,
default: 1,
},
id: {
type: String,
default: '',
},
groundId: {
type: String,
default: '',
},
})
const query = defineModel<string | undefined>('modelValue', {
required: true,
})
// 服务端渲染兼容方案:顺序执行异步操作
// 1. 先获取面包屑数据已在上面通过useAsyncData获取
// 2. 顺序获取分类下拉框数据
// 创建一个函数来获取分类数据,确保服务端渲染时能获取到数据
const getProjectTypeList = async () => {
try {
// 获取分类数据
const res = await parent({
type: 3,
parentId: 0,
})
const all = [{ id: '-1', name: '全部' }]
return [...all, ...res.data]
} catch (error) {
return []
}
}
// 使用useAsyncData获取分类列表确保服务端渲染兼容性
const { data: projectTypeList } = await useAsyncData(
`projectType-draw-toolbox-${props.type}-${query.value}}`,
getProjectTypeList,
{ server: true } // 确保在服务端执行
)
const handleClick = (row: any) => {
query.value = row.id
}
const handleClickBread = (row: any, index: number) => {
query.value = row.id
}
</script>

View File

@ -0,0 +1,62 @@
<template>
<el-dialog v-model="dialogVisible" title="提现申请" width="600" :before-close="handleClose">
<el-form :model="form" label-width="80px">
<el-form-item label="收款账户" prop="amount">
<el-input v-model="form.amount" placeholder="微信手机账号" />
<div class="text-12px color-#A8ABB2">*仅支持微信收款用户需在微信[收付款>向手机号转账>手机号收款设置]开启收款开关*</div>
</el-form-item>
<el-form-item label="提现类型" prop="amount">
<el-radio-group v-model="form.amount">
<el-radio :label="1">收益提现</el-radio>
<el-radio :label="2">全部提现</el-radio>
</el-radio-group>
<div class="text-12px color-#A8ABB2">*收益提现将收益金币提现全部提现可将充值金币提现*</div>
</el-form-item>
<el-form-item label="提现金额" prop="amount">
<div class="flex items-center">
<el-input-number v-model="form.amount" :controls="false" :min="0" placeholder="请输入提现金额" class="w-150px!" />
<div class="ml-10px">[可提现金币数<span class="text-red">1200</span>=<span class="text-red">120</span> ]</div>
</div>
<div class="text-12px color-#A8ABB2">*提现金币比例10金币=1元最低提现额度100元*</div>
</el-form-item>
</el-form>
<div class="text-12px color-red">
提示:收益金币提现正常收取平台手续费{{ walletConfig?.withdrawRateOfEarn }}%如需将充值金币提现选择全部提现选项因涉及充值赠送金额将收取{{ walletConfig?.withdrawRateOfRecharge }}%高额手续费如果恶意套利提现将提现审核不通过!
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="dialogVisible = false"> 立即申请 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { getWalletConfig } from '~/api/personal-center/index'
const dialogVisible = defineModel('modelValue', {
default: false,
})
const form = reactive({
amount: 0,
})
const handleClose = (done: () => void) => {
done()
}
const walletConfig = ref<{
rechargeRate: number
commissionRate: number
withdrawRateOfRecharge: number
withdrawRateOfEarn: number
}>()
onMounted(() => {
getWalletConfig().then((res) => {
walletConfig.value = res.data
})
})
</script>
<style lang="scss" scoped></style>

99
components/wx.vue Normal file
View File

@ -0,0 +1,99 @@
<!-- 二维码弹窗 -->
<template>
<div>
<el-dialog title="微信登录" v-model="visible" width="30%" :close-on-click-modal="false" :close-on-press-escape="false" @before-close="close">
<qrcode-vue :value="qrcode" :size="300" :margin="2" colorDark="#2c3e50" colorLight="#f8f9fa" errorCorrectionLevel="H" />
<!-- 扫码状态 -->
<div class="text-center">
<span>{{ checkLoginStatus }}</span>
</div>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import useUserStore from '~/stores/user'
import { getLoginQrcode, checkScanStatus } from '~/api/personal-center/index'
import QrcodeVue from 'qrcode.vue'
const store = useUserStore()
const visible = defineModel('visible', {
default: false,
})
const checkLoginStatus = computed(() => {
const type = {
EXPIRED: '已过期',
CONFIRMED: '已确认',
SCANNED: '已扫码',
WAITING: '等待扫码',
SUCCESS: '登录成功',
}
if (checkLoginInfo.value) {
return type[checkLoginInfo.value.status as keyof typeof type]
}
return '微信扫码登录'
})
const qrcode = ref('')
const sceneStr = ref('')
const timer = ref<any>()
const getQrcode = async () => {
// 获取二维码
const res = await getLoginQrcode()
if (res.code === 0) {
const jsonurl = res.data.qrCodeUrl
const qrCodeUrl = jsonurl ? JSON.parse(jsonurl) : ''
if (qrCodeUrl?.qrCodeUrl) {
qrcode.value = qrCodeUrl.qrCodeUrl
sceneStr.value = res.data.sceneStr
// 轮询检查扫码状态
getCheckScanStatus()
}
}
}
const checkLoginInfo = ref<{ status: string; openId: string | null; message: string }>()
const getCheckScanStatus = async () => {
// 轮询检查扫码状态
timer.value = setInterval(async () => {
const response = await checkScanStatus({ sceneStr: sceneStr.value })
if (response.code === 0) {
checkLoginInfo.value = response.data
if (checkLoginInfo.value.status === 'SCANNED') {
// 登录
await store.getTokenV2({ openId: checkLoginInfo.value.openId as string, sceneStr: sceneStr.value })
// 扫码成功
visible.value = false
clearInterval(timer.value)
} else if (checkLoginInfo.value.status === 'EXPIRED') {
// 二维码过期
clearInterval(timer.value)
// 重新获取二维码
getQrcode()
}
}
}, 5000)
}
const close = () => {
visible.value = false
qrcode.value = ''
}
onMounted(() => {
// 获取二维码
getQrcode()
})
// 关闭定时器
onUnmounted(() => {
clearInterval(timer.value)
})
</script>
<style lang="scss" scoped>
:deep(.el-dialog__body) {
text-align: center !important;
}
</style>

View File

@ -2,7 +2,27 @@
export const useToken = () => export const useToken = () =>
useState<string>('token', () => { useState<string>('token', () => {
const token = useCookie<string | undefined>('token'); const token = useCookie<string | undefined>('token');
return token.value ? 'Bearer ' + token.value : ''; 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,
}
}); });
/** 热门数据 */ /** 热门数据 */

View File

@ -1,75 +1,56 @@
import { isArray } from "~/utils/utils"; import { isArray } from '~/utils/utils'
import refreshToken from "~/utils/RefreshToken"; import useUserStore from '~/stores/user'
// 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]
const useClientRequest = async <T = unknown>( const useClientRequest = async <T = unknown>(url: string, opts?: FetchOptions) => {
url: string, const token = useCookie<string | undefined>('token')
opts?: FetchOptions const runtimeConfig = useRuntimeConfig()
) => { const userStore = useUserStore()
// const token = useCookie<string | undefined>("token");
const runtimeConfig = useRuntimeConfig();
const defaultOptions: FetchOptions = { const defaultOptions: FetchOptions = {
baseURL: runtimeConfig.public.apiBase, baseURL: runtimeConfig.public.apiBase,
onRequest({ options }) { onRequest({ options }) {
options.headers = options.headers || 'application/json'; options.headers = options.headers || 'application/json'
if (refreshToken.getToken().token) { if (token.value || userStore.token) {
options.headers.set("Authorization", `Bearer ${refreshToken.getToken().token}`); options.headers.set('Authorization', `Bearer ${token.value || userStore.token}`)
} }
}, },
onResponse({ response }) { onResponse({ response }) {
if (+response.status === 200 && +response._data.code !== 0) { if (+response.status === 200 && +response._data.code !== 0) {
ElMessage.error(response._data.msg); ElMessage.error(response._data.msg)
} }
}, },
onResponseError({ response }) { onResponseError({ response }) {
ElMessage.error( ElMessage.error(isArray(response._data.data.msg) ? response._data.data.msg[0] : response._data.data.msg)
isArray(response._data.data.msg)
? response._data.data.msg[0]
: response._data.data.msg
);
}, },
};
// 明确转换返回类型
const response = await $fetch(url, { ...defaultOptions, ...opts });
return response as unknown as T;
};
// GET请求
export const get = <T = unknown>(
endpoint: string,
config?: Omit<FetchOptions, 'method'>
): Promise<T> => {
return useClientRequest<T>(endpoint, { ...config, method: 'GET' })
} }
// POST请求 // 明确转换返回类型
export const post = <T = unknown>( const response = await $fetch(url, { ...defaultOptions, ...opts })
endpoint: string, return response as unknown as T
body?: any, }
config?: Omit<FetchOptions, 'method' | 'body'>
): Promise<T> => {
return useClientRequest<T>(endpoint, { ...config, method: 'POST', body })
}
// GET请求
// DELETE请求 export const get = <T = unknown>(endpoint: string, config?: Omit<FetchOptions, 'method'>): Promise<T> => {
export const del = <T = unknown>( return useClientRequest<T>(endpoint, { ...config, method: 'GET' })
endpoint: string, }
config?: Omit<FetchOptions, 'method'>
): Promise<T> => {
return useClientRequest<T>(endpoint, { ...config, method: 'DELETE' })
}
// PUT请求 // POST请求
export const put = <T = unknown>( export const post = <T = unknown>(endpoint: string, body?: any, config?: Omit<FetchOptions, 'method' | 'body'>): Promise<T> => {
endpoint: string, return useClientRequest<T>(endpoint, { ...config, method: 'POST', body })
body?: any, }
config?: Omit<FetchOptions, 'method' | 'body'>
): Promise<T> => { // DELETE请求
return useClientRequest<T>(endpoint, { ...config, method: 'PUT', body }) export const del = <T = unknown>(endpoint: string, config?: Omit<FetchOptions, 'method'>): Promise<T> => {
} return useClientRequest<T>(endpoint, { ...config, method: 'DELETE' })
}
// PUT请求
export const put = <T = unknown>(endpoint: string, body?: any, config?: Omit<FetchOptions, 'method' | 'body'>): Promise<T> => {
console.log({ ...config, method: 'PUT', body })
return useClientRequest<T>(endpoint, { ...config, method: 'PUT', body })
}

View File

@ -1,77 +1,54 @@
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'
import useUserStore from '~/stores/user'
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 userStore = useUserStore()
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) {
// @ts-ignore if (token.value || userStore.token) {
options.headers["authorization"] = "Bearer " + token.value; options.headers.set('Authorization', `Bearer ${token.value || userStore.token}`)
} }
}, },
onResponse({ response }) { onResponse({ response }) {
if (+response.status === 200 && +response._data.code !== 0) { 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 } as any); const response = await useFetch<T>(url, { ...defaultOptions, ...opts } as any)
console.log(url +'-----' + 'response----', response.data.value); 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> => {
console.log(endpoint + '----' + 'config----', config);
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请求
// DELETE请求 export const del = <T = unknown>(endpoint: string, config?: Omit<FetchOptions, 'method'>): Promise<T> => {
export const del = <T = unknown>( return useServerRequest<T>(endpoint, { ...config, method: 'DELETE' })
endpoint: string, }
config?: Omit<FetchOptions, 'method'>
): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'DELETE' })
}
// PUT请求 // PUT请求
export const put = <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 })
body?: any, }
config?: Omit<FetchOptions, 'method' | 'body'>
): Promise<T> => {
return useServerRequest<T>(endpoint, { ...config, method: 'PUT', body })
}

81
composables/useMessage.ts Normal file
View File

@ -0,0 +1,81 @@
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
export const useMessage = () => {
return {
// 消息提示
info(content: string) {
ElMessage.info(content)
},
// 错误消息
error(content: string) {
ElMessage.error(content)
},
// 成功消息
success(content: string) {
ElMessage.success(content)
},
// 警告消息
warning(content: string) {
ElMessage.warning(content)
},
// 弹出提示
alert(content: string, title: string) {
ElMessageBox.alert(content, title)
},
// 错误提示
alertError(content: string, title: string) {
ElMessageBox.alert(content, title, { type: 'error' })
},
// 成功提示
alertSuccess(content: string, title: string) {
ElMessageBox.alert(content, title, { type: 'success' })
},
// 警告提示
alertWarning(content: string, title: string) {
ElMessageBox.alert(content, title, { type: 'warning' })
},
// 通知提示
notify(content: string) {
ElNotification.info(content)
},
// 错误通知
notifyError(content: string) {
ElNotification.error(content)
},
// 成功通知
notifySuccess(content: string) {
ElNotification.success(content)
},
// 警告通知
notifyWarning(content: string) {
ElNotification.warning(content)
},
// 确认窗体
confirm(content: string, title?: string, options?: any) {
return ElMessageBox.confirm(content, title, {
type: 'warning',
...options
})
},
// 删除窗体
delConfirm(content?: string, title?: string, options?: any) {
return ElMessageBox.confirm(content, title, {
type: 'warning',
...options
})
},
// 导出窗体
exportConfirm(content?: string, title?: string, options?: any) {
return ElMessageBox.confirm(content, title, {
type: 'warning',
...options
})
},
// 提交内容
prompt(content: string, title: string, options?: any) {
return ElMessageBox.prompt(content, title, {
type: 'warning',
...options
})
}
}
}

View File

@ -12,7 +12,7 @@
<script lang="ts" setup> <script lang="ts" setup>
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' import KlFooter from '~/components/kl-footer/index.vue'
</script> </script>

70
layouts/m.vue Normal file
View File

@ -0,0 +1,70 @@
<script setup lang="ts">
const active = ref('home')
</script>
<template>
<div class="m">
<div class="scroll-container">
<slot />
</div>
<!-- 底部信息 -->
<div class="footer">
<van-tabbar v-model="active">
<van-tabbar-item name="home" icon="home-o">标签</van-tabbar-item>
<van-tabbar-item name="search" icon="search">标签</van-tabbar-item>
<van-tabbar-item name="friends" icon="friends-o">标签</van-tabbar-item>
<van-tabbar-item name="setting" icon="setting-o">标签</van-tabbar-item>
</van-tabbar>
</div>
</div>
</template>
<style lang="scss" scoped>
/* 移动端样式重置 - 只在移动端布局中生效 */
.m {
display: flex;
flex-direction: column;
background-color: #f8f8f8;
.scroll-container {
// flex: 1;
overflow-y: auto;
height: calc(100vh - 40px);
}
.footer {
height: auto;
}
/* 重置 body 的最小宽度限制 */
:global(body) {
min-width: unset !important;
width: 100% !important;
max-width: 100% !important;
background-color: #f8f8f8;
}
/* 重置 #__nuxt 容器的样式 */
:global(#__nuxt) {
min-width: unset !important;
width: 100% !important;
max-width: 100% !important;
// .page-content {
// padding: 0 !important;
// overflow-x: hidden !important;
// .page-table-wrap {
// padding: 8px !important;
// }
// .page-card {
// padding: 8px !important;
// }
// }
}
/* 移动端优化滚动条 */
:global(::-webkit-scrollbar) {
width: 4px !important;
height: 4px !important;
}
}
</style>

20
layouts/success.vue Normal file
View File

@ -0,0 +1,20 @@
<template>
<div class="layout-wrap">
<div class="flex flex-1 flex-col">
<slot></slot>
</div>
</div>
</template>
<script lang="ts" setup></script>
<style lang="scss" scoped>
.layout-wrap {
flex: 1;
display: flex;
flex-direction: column;
background-color: #fbfcff;
width: 100%;
margin: auto;
}
</style>

View File

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

View File

@ -0,0 +1,12 @@
import useUserStore from '~/stores/user'
export default defineNuxtRouteMiddleware((to, from) => {
const { code, state, type } = to.query
if (code && state && type && import.meta.client) {
const userStore = useUserStore()
userStore.getToken({
code: code as string,
state: state as string,
type: type as string,
})
}
})

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('/mobile')
console.log(isMobile, isRouterMobile);
// 移动端并且 不是/m开头路由
if (isMobile && !isRouterMobile) {
return navigateTo(`/mobile`)
}
// 不是移动端 是/m开头路由
if (!isMobile && isRouterMobile) {
return navigateTo(`/`)
}
}
})

19
middleware/tdk.global.ts Normal file
View File

@ -0,0 +1,19 @@
import { getTDKList } from '~/api/home/index'
// middleware/tdk.global.ts
export default defineNuxtRouteMiddleware(async (to) => {
const { data: tdkData } = await getTDKList()
// 获取当前路由
const currentPath = to.path;
// 根据当前路由获取对应的TDK数据
const currentTdk = tdkData?.find((item) => item.path === currentPath)
if (currentTdk) {
useHead({
title: currentTdk.title,
meta: [
{ name: 'description', content: currentTdk.describeText },
{ name: 'keywords', content: currentTdk.keyword }
]
})
}
})

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','@vant/nuxt'],
css: ["@unocss/reset/tailwind.css", "element-plus/dist/index.css","~/assets/scss/app.scss"], 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,96 @@ export default defineNuxtConfig({
}, },
postcss: { postcss: {
plugins: [ plugins: [
// postCssPxToRem({ postcsspxtoviewport({
// rootValue: 16, // 结果为:设计稿元素尺寸/16比如元素宽320px,最终页面会换算成 20rem unitToConvert: 'px',
// mediaQuery: false, //布尔值允许在媒体查询中转换px。 viewportWidth: 750,
// // exclude: /node_modules/, //node_modules目录下样式全部不转义 unitPrecision: 6,
// propList: ['*'] //需要做转化处理的属性如`hight`、`width`、`margin`等,`*`表示全部 propList: ['*'],
// }) viewportUnit: 'vw',
fontViewportUnit: 'vw',
selectorBlackList: ['el-'],
minPixelValue: 1,
mediaQuery: true,
replace: true,
// 严格只允许 pages/mobile 下的文件被转换
// 使用强排除:排除 node_modules 和 所有不在 pages/mobile 下的文件
exclude: [
/node_modules/,
/^(?!.*\/(pages|src)\/mobile\/).*$/
],
landscape: false,
// 可选:进一步放宽 include以便某些移动端专用布局文件也参与
include: [/\/pages\/mobile\//],
}),
], ],
}, },
}, },
optimizeDeps: { optimizeDeps: {
include: ["naive-ui"], include: ['element-plus','unocss'], // 预构建依赖项
}, },
// 生产环境构建优化 // 生产环境构建优化
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: "default", // name: "default",
mode: "out-in", // mode: "out-in",
duration: 400, // duration: 400,
}, // },
head: { head: {
title: "图夕夕-世界图纸 夕夕共享", 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: "图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。", content: '图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。',
}, },
{ name: "keywords", content: "图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸" }, { name: 'keywords', content: '图纸,图纸下载,设计素材,图纸大全,设计图纸,,工程图纸,cad图纸' },
{ name: "author", content: "图夕夕" }, { 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: "图夕夕是一家图纸素材分享交易平台提供AutoCAD/ProE/Creo/CATIA/UG/inventor/CAXA/等建筑图纸的素材下载及免费教程。", // },
}, // {
{ 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,22 +130,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","@tinymce/tinymce-vue","tinymce"], transpile: process.env.NODE_ENV === 'production' ? ['lodash'] : [],
}, },
plugins: [ plugins: [
// 在这里引入插件 // 在这里引入插件
// { src: "~plugins/tinymce" ,ssr: false}, {
] src: '~/plugins/wang-editor',
}); mode: 'client',
},
],
piniaPluginPersistedstate: {
storage: 'localStorage',
},
})

View File

@ -1,5 +1,6 @@
{ {
"name": "nuxt-app", "name": "nuxt-app",
"version": "1.0.0",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@ -12,15 +13,18 @@
"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": "^5.0.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",
"lodash": "^4.17.21",
"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": "^6.2.0", "postcss-px-to-viewport": "^1.1.1",
"qrcode.vue": "^3.6.0",
"vant": "^4.9.21",
"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 +33,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,139 @@
<template>
<div class="auth-success-container">
<!-- 顶部成功图标 -->
<div class="success-icon">
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="40" cy="40" r="40" fill="#4CD964" />
<path d="M25 40L35 50L55 30" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" />
</svg>
</div>
<!-- 标题和描述 -->
<div class="text-content">
<h1 class="title">授权成功</h1>
<p class="desc">
您已完成微信授权<br />
请返回电脑端继续操作
</p>
</div>
<!-- 倒计时提示 -->
<!-- <div class="countdown" v-if="countdown > 0">
<p>页面将在 {{ countdown }} 秒后自动关闭</p>
</div> -->
<!-- 底部按钮 -->
<!-- <div class="btn-group">
<button class="close-btn" @click="closePage">立即关闭</button>
</div> -->
</div>
</template>
<script setup lang="ts">
import { notifyAuthSuccess } from '~/api/login/index'
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
const route = useRoute()
definePageMeta({
layout: 'success',
})
onMounted(() => {
notifyAuthSuccess({ code: route.query.code as string, state: route.query.state as string }).then(() => {
console.log('Notified backend of auth success')
})
})
</script>
<style scoped>
.auth-success-container {
min-height: 100vh;
box-sizing: border-box;
padding: 20px;
background-color: #f5f7fa;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.success-icon {
margin-bottom: 30px;
animation: pop 0.5s ease-out;
}
@keyframes pop {
0% {
transform: scale(0.8);
opacity: 0;
}
70% {
transform: scale(1.1);
}
100% {
transform: scale(1);
opacity: 1;
}
}
.text-content {
text-align: center;
margin-bottom: 40px;
}
.title {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0 0 15px 0;
}
.desc {
font-size: 16px;
color: #666;
line-height: 1.6;
margin: 0;
}
.countdown {
margin-bottom: 50px;
color: #999;
font-size: 14px;
}
.btn-group {
width: 100%;
max-width: 300px;
}
.close-btn {
width: 100%;
height: 48px;
background-color: #07c160;
color: white;
border: none;
border-radius: 24px;
font-size: 16px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.3s;
}
.close-btn:hover {
background-color: #06b355;
}
/* 适配小屏手机 */
@media (max-width: 320px) {
.title {
font-size: 22px;
}
.desc {
font-size: 15px;
}
.close-btn {
height: 44px;
font-size: 15px;
}
}
</style>

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,12 +4,12 @@
<!-- 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>
@ -21,7 +21,7 @@
<!-- 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 = () => {
navigateTo('/channel/create?channelId=' + lunTanRes.value.channelId) 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

@ -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>

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