@ -0,0 +1,97 @@ | |||
package org.jeecg.modules.applet.controller; | |||
import com.baomidou.mybatisplus.core.metadata.IPage; | |||
import io.swagger.v3.oas.annotations.Operation; | |||
import io.swagger.v3.oas.annotations.Parameter; | |||
import io.swagger.v3.oas.annotations.tags.Tag; | |||
import lombok.extern.slf4j.Slf4j; | |||
import org.jeecg.common.api.vo.Result; | |||
import org.jeecg.config.shiro.IgnoreAuth; | |||
import org.jeecg.modules.applet.service.AppletApiBooksService; | |||
import org.jeecg.modules.demo.appletBooks.entity.AppletBooks; | |||
import org.jeecg.modules.demo.appletCategorize.entity.AppletCategorize; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
import org.springframework.web.bind.annotation.*; | |||
import java.util.List; | |||
/** | |||
* @Description: 书籍管理 | |||
* @Author: jeecg-boot | |||
* @Date: 2025-01-XX | |||
* @Version: V1.0 | |||
*/ | |||
@Tag(name = "书籍", description = "书籍") | |||
@RestController | |||
@RequestMapping("/appletApi/books") | |||
@Slf4j | |||
public class AppletApiBooksController { | |||
@Autowired | |||
private AppletApiBooksService appletApiBooksService; | |||
/** | |||
* 查询书籍列表 | |||
* | |||
* @return 查询书籍列表 | |||
*/ | |||
@Operation(summary = "查询书籍列表", description = "查询书籍列表") | |||
@GetMapping(value = "/list") | |||
@IgnoreAuth | |||
public Result<IPage<AppletBooks>> list(@Parameter(description = "搜素关键字") String title, | |||
@Parameter(description = "搜素关键字") String category, | |||
@Parameter(description = "搜素关键字") String label, | |||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, | |||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) { | |||
log.info("查询书籍列表"); | |||
Page<AppletBooks> booksPage = new Page<>(pageNo, pageSize); | |||
IPage<AppletBooks> list = appletApiBooksService | |||
.getBooks(booksPage, title, category, label); | |||
return Result.OK(list); | |||
} | |||
/** | |||
* 查询书桌列表 | |||
* | |||
* @return 查询书桌列表 | |||
*/ | |||
@Operation(summary = "查询书桌列表", description = "查询书桌列表") | |||
@GetMapping(value = "/stand") | |||
public Result<IPage<AppletBooks>> stand(@Parameter(description = "搜素关键字") String title, | |||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, | |||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) { | |||
log.info("查询书桌列表"); | |||
Page<AppletBooks> booksPage = new Page<>(pageNo, pageSize); | |||
IPage<AppletBooks> list = appletApiBooksService | |||
.getBooksStand(booksPage, title); | |||
return Result.OK(list); | |||
} | |||
/** | |||
* 加入书桌 | |||
* | |||
* @return 加入书桌 | |||
*/ | |||
@Operation(summary = "加入书桌", description = "加入书桌") | |||
@PostMapping(value = "/addStand") | |||
public Result<?> addStand(@Parameter(description = "书籍id") String id) { | |||
appletApiBooksService.addStand(id); | |||
return Result.OK(); | |||
} | |||
/** | |||
* 删除书桌 | |||
* | |||
* @return 删除书桌 | |||
*/ | |||
@Operation(summary = "删除书桌", description = "删除书桌,批量删除用,分割") | |||
@PostMapping(value = "/delStand") | |||
public Result<?> delStand(@Parameter(description = "书籍id") String id) { | |||
appletApiBooksService.delStand(id); | |||
return Result.OK(); | |||
} | |||
} |
@ -0,0 +1,16 @@ | |||
package org.jeecg.modules.applet.query; | |||
import lombok.AllArgsConstructor; | |||
import lombok.Builder; | |||
import lombok.Data; | |||
import lombok.NoArgsConstructor; | |||
@Data | |||
@Builder | |||
@AllArgsConstructor | |||
@NoArgsConstructor | |||
public class AppletBookQuery { | |||
private String title; | |||
} |
@ -0,0 +1,36 @@ | |||
package org.jeecg.modules.applet.service; | |||
import com.baomidou.mybatisplus.core.metadata.IPage; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
import org.jeecg.modules.demo.appletBooks.entity.AppletBooks; | |||
import java.util.List; | |||
public interface AppletApiBooksService { | |||
/** | |||
* 分页列表查询 | |||
* | |||
*/ | |||
IPage<AppletBooks> getBooks(Page<AppletBooks> page, String title, String category, String label); | |||
/** | |||
* 查询书桌列表 | |||
* | |||
* @return 查询书桌列表 | |||
*/ | |||
IPage<AppletBooks> getBooksStand(Page<AppletBooks> booksPage, String title); | |||
/** | |||
* 加入书桌 | |||
* | |||
* @return 加入书桌 | |||
*/ | |||
void addStand(String id); | |||
/** | |||
* 删除书桌 | |||
* | |||
* @return 删除书桌 | |||
*/ | |||
void delStand(String id); | |||
} |
@ -0,0 +1,26 @@ | |||
package org.jeecg.modules.applet.service; | |||
import org.jeecg.modules.demo.appletBanner.entity.AppletBanner; | |||
import org.jeecg.modules.demo.appletLink.entity.AppletLink; | |||
import java.util.List; | |||
/** | |||
* 小程序首页 | |||
*/ | |||
public interface AppletApiIndexService { | |||
/** | |||
* 轮播图 | |||
* | |||
* @return | |||
*/ | |||
List<AppletBanner> getBanner(); | |||
/** | |||
* 首页底部内容链接 | |||
* | |||
* @return | |||
*/ | |||
List<AppletLink> getLink(); | |||
} |
@ -0,0 +1,83 @@ | |||
package org.jeecg.modules.applet.service.impl; | |||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | |||
import com.baomidou.mybatisplus.core.metadata.IPage; | |||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||
import jodd.util.StringUtil; | |||
import org.jeecg.common.exception.JeecgBootException; | |||
import org.jeecg.common.system.util.AppletUserUtil; | |||
import org.jeecg.modules.applet.query.AppletBookQuery; | |||
import org.jeecg.modules.applet.service.AppletApiBooksService; | |||
import org.jeecg.modules.demo.appletBookStand.entity.AppletBookStand; | |||
import org.jeecg.modules.demo.appletBookStand.service.IAppletBookStandService; | |||
import org.jeecg.modules.demo.appletBooks.entity.AppletBooks; | |||
import org.jeecg.modules.demo.appletBooks.service.IAppletBooksService; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.util.Arrays; | |||
import java.util.List; | |||
import java.util.stream.Collectors; | |||
@Service | |||
public class AppletApiBooksServiceImpl implements AppletApiBooksService { | |||
@Autowired | |||
private IAppletBooksService appletBooksService; | |||
@Autowired | |||
private IAppletBookStandService appletBookStandService; | |||
@Override | |||
public IPage<AppletBooks> getBooks(Page<AppletBooks> page, String title, String category, String label) { | |||
return appletBooksService | |||
.lambdaQuery() | |||
.like(StringUtil.isNotEmpty(title), AppletBooks::getBooksName, title) | |||
.eq(StringUtil.isNotEmpty(category), AppletBooks::getBooksClassification, category) | |||
.like(StringUtil.isNotEmpty(label), AppletBooks::getBooksLabel, label) | |||
.page(page); | |||
} | |||
@Override | |||
public IPage<AppletBooks> getBooksStand(Page<AppletBooks> booksPage, String title) { | |||
String userId = AppletUserUtil.getCurrentAppletUserId(); | |||
List<String> bookIds = appletBookStandService | |||
.lambdaQuery() | |||
.eq(AppletBookStand::getUserId, userId) | |||
.list().stream() | |||
.map(AppletBookStand::getBookId) | |||
.collect(Collectors.toList()); | |||
return appletBooksService | |||
.lambdaQuery() | |||
.in(AppletBooks::getId) | |||
.like(StringUtil.isNotEmpty(title), AppletBooks::getBooksName, title) | |||
.page(booksPage); | |||
} | |||
@Override | |||
public void addStand(String id) { | |||
String userId = AppletUserUtil.getCurrentAppletUserId(); | |||
AppletBookStand one = appletBookStandService.lambdaQuery() | |||
.eq(AppletBookStand::getUserId, userId) | |||
.eq(AppletBookStand::getBookId, id).one(); | |||
if (one == null){ | |||
AppletBookStand appletBookStand = new AppletBookStand(); | |||
appletBookStand.setUserId(userId); | |||
appletBookStand.setBookId(id); | |||
appletBookStandService.save(appletBookStand); | |||
} | |||
} | |||
@Override | |||
public void delStand(String id) { | |||
if (StringUtil.isBlank(id)){ | |||
throw new JeecgBootException("id不能为空"); | |||
} | |||
String userId = AppletUserUtil.getCurrentAppletUserId(); | |||
appletBookStandService.remove(Wrappers.<AppletBookStand>lambdaQuery() | |||
.eq(AppletBookStand::getUserId, userId) | |||
.in(AppletBookStand::getBookId, Arrays.asList(id.split(",")))); | |||
} | |||
} |
@ -0,0 +1,40 @@ | |||
package org.jeecg.modules.applet.service.impl; | |||
import org.jeecg.modules.applet.service.AppletApiIndexService; | |||
import org.jeecg.modules.demo.appletBanner.entity.AppletBanner; | |||
import org.jeecg.modules.demo.appletBanner.service.IAppletBannerService; | |||
import org.jeecg.modules.demo.appletLink.entity.AppletLink; | |||
import org.jeecg.modules.demo.appletLink.service.IAppletLinkService; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.stereotype.Service; | |||
import java.util.List; | |||
@Service | |||
public class AppletApiIndexServiceImpl implements AppletApiIndexService { | |||
@Autowired | |||
private IAppletBannerService appletBannerService; | |||
@Autowired | |||
private IAppletLinkService appletLinkService; | |||
/*** | |||
* 轮播图 | |||
* @return | |||
*/ | |||
@Override | |||
public List<AppletBanner> getBanner() { | |||
return appletBannerService.list(); | |||
} | |||
/** | |||
* 首页底部内容链接 | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public List<AppletLink> getLink() { | |||
return appletLinkService.list(); | |||
} | |||
} |
@ -1,218 +1,216 @@ | |||
<template> | |||
<div class="app-loading"> | |||
<div class="app-loading-wrap"> | |||
<img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo"> | |||
<div class="app-loading-dots"> | |||
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> | |||
</div> | |||
<div class="app-loading-title">普兆健康管家</div> | |||
</div> | |||
<div class="app-loading"> | |||
<div class="app-loading-wrap"> | |||
<img src="/resource/img/logo.png" class="app-loading-logo" alt="Logo" /> | |||
<div class="app-loading-dots"> | |||
<span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> | |||
</div> | |||
<div class="app-loading-title">四零语境</div> | |||
</div> | |||
</div> | |||
</template> | |||
<script lang="ts"> | |||
/** | |||
* 地址中携带token,跳转至此页面进行登录操作 | |||
*/ | |||
import { useRoute, useRouter } from 'vue-router'; | |||
import { useMessage } from '/@/hooks/web/useMessage'; | |||
import { useUserStore } from '/@/store/modules/user'; | |||
import { useI18n } from '/@/hooks/web/useI18n'; | |||
export default { | |||
name: "TokenLogin", | |||
setup(){ | |||
const route = useRoute(); | |||
let router = useRouter(); | |||
const {createMessage, notification} = useMessage() | |||
const {t} = useI18n(); | |||
const routeQuery:any = route.query; | |||
if(!routeQuery){ | |||
createMessage.warning('参数无效') | |||
/** | |||
* 地址中携带token,跳转至此页面进行登录操作 | |||
*/ | |||
import { useRoute, useRouter } from 'vue-router'; | |||
import { useMessage } from '/@/hooks/web/useMessage'; | |||
import { useUserStore } from '/@/store/modules/user'; | |||
import { useI18n } from '/@/hooks/web/useI18n'; | |||
export default { | |||
name: 'TokenLogin', | |||
setup() { | |||
const route = useRoute(); | |||
let router = useRouter(); | |||
const { createMessage, notification } = useMessage(); | |||
const { t } = useI18n(); | |||
const routeQuery: any = route.query; | |||
if (!routeQuery) { | |||
createMessage.warning('参数无效'); | |||
} | |||
const token = routeQuery['loginToken']; | |||
if (!token) { | |||
createMessage.warning('token无效'); | |||
} | |||
const userStore = useUserStore(); | |||
userStore.ThirdLogin({ token, thirdType: 'email', goHome: false }).then((res) => { | |||
console.log('res====>doThirdLogin', res); | |||
if (res && res.userInfo) { | |||
requestSuccess(res); | |||
} else { | |||
requestFailed(res); | |||
} | |||
const token = routeQuery['loginToken']; | |||
if(!token){ | |||
createMessage.warning('token无效') | |||
} | |||
const userStore = useUserStore(); | |||
userStore.ThirdLogin({ token, thirdType:'email', goHome: false }).then(res => { | |||
console.log("res====>doThirdLogin",res) | |||
if(res && res.userInfo){ | |||
requestSuccess(res) | |||
}else{ | |||
requestFailed(res) | |||
} | |||
}); | |||
function requestFailed(err) { | |||
notification.error({ | |||
message: '登录失败', | |||
description: ((err.response || {}).data || {}).message || err.message || '请求出现错误,请稍后再试', | |||
duration: 4, | |||
}); | |||
} | |||
function requestSuccess(res) { | |||
let info = routeQuery.info; | |||
if (info) { | |||
let query = JSON.parse(info); | |||
//update-begin-author:taoyan date:2023-4-27 for: QQYUN-4882【简流】节点消息通知 邮箱 点击办理跳到了应用首页 | |||
let path = ''; | |||
if (query.isLowApp === 1) { | |||
path = '/myapps/personalOffice/myTodo'; | |||
} else { | |||
let taskId = query.taskId; | |||
path = '/task/handle/' + taskId; | |||
} | |||
//update-end-author:taoyan date:2023-4-27 for: QQYUN-4882【简流】节点消息通知 邮箱 点击办理跳到了应用首页 | |||
function requestFailed (err) { | |||
router.replace({ path, query }); | |||
notification.success({ | |||
message: t('sys.login.loginSuccessTitle'), | |||
description: `${t('sys.login.loginSuccessDesc')}: ${res.userInfo.realname}`, | |||
duration: 3, | |||
}); | |||
} else { | |||
notification.error({ | |||
message: '登录失败', | |||
description: ((err.response || {}).data || {}).message || err.message || "请求出现错误,请稍后再试", | |||
message: '参数失效', | |||
description: '页面跳转参数丢失,请查看日志', | |||
duration: 4, | |||
}); | |||
} | |||
function requestSuccess(res){ | |||
let info = routeQuery.info; | |||
if(info){ | |||
let query = JSON.parse(info); | |||
//update-begin-author:taoyan date:2023-4-27 for: QQYUN-4882【简流】节点消息通知 邮箱 点击办理跳到了应用首页 | |||
let path = ''; | |||
if(query.isLowApp === 1){ | |||
path = '/myapps/personalOffice/myTodo' | |||
}else{ | |||
let taskId = query.taskId; | |||
path = '/task/handle/' + taskId | |||
} | |||
//update-end-author:taoyan date:2023-4-27 for: QQYUN-4882【简流】节点消息通知 邮箱 点击办理跳到了应用首页 | |||
router.replace({ path, query }); | |||
notification.success({ | |||
message: t('sys.login.loginSuccessTitle'), | |||
description: `${t('sys.login.loginSuccessDesc')}: ${res.userInfo.realname}`, | |||
duration: 3, | |||
}); | |||
}else{ | |||
notification.error({ | |||
message: '参数失效', | |||
description: "页面跳转参数丢失,请查看日志", | |||
duration: 4, | |||
}); | |||
} | |||
} | |||
} | |||
} | |||
}, | |||
}; | |||
</script> | |||
<style scoped> | |||
html[data-theme='dark'] .app-loading { | |||
background-color: #2c344a; | |||
} | |||
html[data-theme='dark'] .app-loading .app-loading-title { | |||
color: rgba(255, 255, 255, 0.85); | |||
} | |||
.app-loading { | |||
display: flex; | |||
width: 100%; | |||
height: 100%; | |||
justify-content: center; | |||
align-items: center; | |||
flex-direction: column; | |||
background-color: #f4f7f9; | |||
} | |||
.app-loading .app-loading-wrap { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
display: flex; | |||
-webkit-transform: translate3d(-50%, -50%, 0); | |||
transform: translate3d(-50%, -50%, 0); | |||
justify-content: center; | |||
align-items: center; | |||
flex-direction: column; | |||
} | |||
.app-loading .dots { | |||
display: flex; | |||
padding: 98px; | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.app-loading .app-loading-title { | |||
display: flex; | |||
margin-top: 30px; | |||
font-size: 30px; | |||
color: rgba(0, 0, 0, 0.85); | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.app-loading .app-loading-logo { | |||
display: block; | |||
width: 90px; | |||
margin: 0 auto; | |||
margin-bottom: 20px; | |||
} | |||
.dot { | |||
position: relative; | |||
display: inline-block; | |||
width: 48px; | |||
height: 48px; | |||
margin-top: 30px; | |||
font-size: 32px; | |||
transform: rotate(45deg); | |||
box-sizing: border-box; | |||
animation: antRotate 1.2s infinite linear; | |||
} | |||
.dot i { | |||
position: absolute; | |||
display: block; | |||
width: 20px; | |||
height: 20px; | |||
background-color: #0065cc; | |||
border-radius: 100%; | |||
opacity: 0.3; | |||
transform: scale(0.75); | |||
animation: antSpinMove 1s infinite linear alternate; | |||
transform-origin: 50% 50%; | |||
} | |||
.dot i:nth-child(1) { | |||
top: 0; | |||
left: 0; | |||
} | |||
.dot i:nth-child(2) { | |||
top: 0; | |||
right: 0; | |||
-webkit-animation-delay: 0.4s; | |||
animation-delay: 0.4s; | |||
} | |||
.dot i:nth-child(3) { | |||
right: 0; | |||
bottom: 0; | |||
-webkit-animation-delay: 0.8s; | |||
animation-delay: 0.8s; | |||
} | |||
.dot i:nth-child(4) { | |||
bottom: 0; | |||
left: 0; | |||
-webkit-animation-delay: 1.2s; | |||
animation-delay: 1.2s; | |||
} | |||
@keyframes antRotate { | |||
to { | |||
-webkit-transform: rotate(405deg); | |||
transform: rotate(405deg); | |||
} | |||
} | |||
@-webkit-keyframes antRotate { | |||
to { | |||
-webkit-transform: rotate(405deg); | |||
transform: rotate(405deg); | |||
} | |||
} | |||
@keyframes antSpinMove { | |||
to { | |||
opacity: 1; | |||
} | |||
} | |||
@-webkit-keyframes antSpinMove { | |||
to { | |||
opacity: 1; | |||
} | |||
} | |||
html[data-theme='dark'] .app-loading { | |||
background-color: #2c344a; | |||
} | |||
html[data-theme='dark'] .app-loading .app-loading-title { | |||
color: rgba(255, 255, 255, 0.85); | |||
} | |||
.app-loading { | |||
display: flex; | |||
width: 100%; | |||
height: 100%; | |||
justify-content: center; | |||
align-items: center; | |||
flex-direction: column; | |||
background-color: #f4f7f9; | |||
} | |||
.app-loading .app-loading-wrap { | |||
position: absolute; | |||
top: 50%; | |||
left: 50%; | |||
display: flex; | |||
-webkit-transform: translate3d(-50%, -50%, 0); | |||
transform: translate3d(-50%, -50%, 0); | |||
justify-content: center; | |||
align-items: center; | |||
flex-direction: column; | |||
} | |||
.app-loading .dots { | |||
display: flex; | |||
padding: 98px; | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.app-loading .app-loading-title { | |||
display: flex; | |||
margin-top: 30px; | |||
font-size: 30px; | |||
color: rgba(0, 0, 0, 0.85); | |||
justify-content: center; | |||
align-items: center; | |||
} | |||
.app-loading .app-loading-logo { | |||
display: block; | |||
width: 90px; | |||
margin: 0 auto; | |||
margin-bottom: 20px; | |||
} | |||
.dot { | |||
position: relative; | |||
display: inline-block; | |||
width: 48px; | |||
height: 48px; | |||
margin-top: 30px; | |||
font-size: 32px; | |||
transform: rotate(45deg); | |||
box-sizing: border-box; | |||
animation: antRotate 1.2s infinite linear; | |||
} | |||
.dot i { | |||
position: absolute; | |||
display: block; | |||
width: 20px; | |||
height: 20px; | |||
background-color: #0065cc; | |||
border-radius: 100%; | |||
opacity: 0.3; | |||
transform: scale(0.75); | |||
animation: antSpinMove 1s infinite linear alternate; | |||
transform-origin: 50% 50%; | |||
} | |||
.dot i:nth-child(1) { | |||
top: 0; | |||
left: 0; | |||
} | |||
.dot i:nth-child(2) { | |||
top: 0; | |||
right: 0; | |||
-webkit-animation-delay: 0.4s; | |||
animation-delay: 0.4s; | |||
} | |||
.dot i:nth-child(3) { | |||
right: 0; | |||
bottom: 0; | |||
-webkit-animation-delay: 0.8s; | |||
animation-delay: 0.8s; | |||
} | |||
.dot i:nth-child(4) { | |||
bottom: 0; | |||
left: 0; | |||
-webkit-animation-delay: 1.2s; | |||
animation-delay: 1.2s; | |||
} | |||
@keyframes antRotate { | |||
to { | |||
-webkit-transform: rotate(405deg); | |||
transform: rotate(405deg); | |||
} | |||
} | |||
@-webkit-keyframes antRotate { | |||
to { | |||
-webkit-transform: rotate(405deg); | |||
transform: rotate(405deg); | |||
} | |||
} | |||
@keyframes antSpinMove { | |||
to { | |||
opacity: 1; | |||
} | |||
} | |||
@-webkit-keyframes antSpinMove { | |||
to { | |||
opacity: 1; | |||
} | |||
} | |||
</style> |