Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2713ecac0b | ||
|
|
6a2c86d9f2 | ||
|
|
994b3954ed | ||
|
|
cb9f1f22e6 | ||
|
|
1723f2514f | ||
|
|
27dffd9919 | ||
|
|
83d316a2d1 | ||
|
|
4b36de6de5 | ||
|
|
d170a9d189 | ||
|
|
3b31558adb | ||
|
|
728ddb9a7e | ||
|
|
2c72327c8e |
24
README.md
@@ -2,16 +2,17 @@
|
|||||||
|
|
||||||
**SmartAdmin** 由 **河南·洛阳** [1024创新实验室](https://www.1024lab.net/) 坚持以 <font color="#DC143C">**「高质量代码」为核心,「简洁、高效、安全」**</font>的中后台解决方案!
|
**SmartAdmin** 由 **河南·洛阳** [1024创新实验室](https://www.1024lab.net/) 坚持以 <font color="#DC143C">**「高质量代码」为核心,「简洁、高效、安全」**</font>的中后台解决方案!
|
||||||
|
|
||||||
<font color="#DC143C">**国内首个满足《网络安全》、《数据安全》、三级等保**</font>, 支持登录限制、支持接口国产加解密、支持数据加解密等一系列安全措施的开源项目。
|
<font color="#DC143C">**国内满足《网络安全》、《数据安全》、三级等保**</font>, 支持登录限制、支持接口国产加解密、支持数据加解密等一系列安全措施的开源项目。
|
||||||
|
|
||||||
<font color="#DC143C">**我们开源一套漂亮的代码和一套整洁的代码规范**</font>,让大家在这浮躁的代码世界里感受到一股把代码写好的清流!同时又让开发者节省大量的时间,减少加班,快乐工作,保持谦逊,保持学习,**热爱代码,更热爱生活**
|
<font color="#DC143C">**我们开源一套漂亮的代码和一套整洁的代码规范**</font>,让大家在这浮躁的代码世界里感受到一股把代码写好的清流!同时又让开发者节省大量的时间,减少加班,快乐工作,保持谦逊,保持学习,**热爱代码,更热爱生活**
|
||||||
### **技术体系**
|
### **技术体系**
|
||||||
|
|
||||||
- 前端:Vue3 + Vite5 + Vue-Router + Pinia + Ant Design Vue 4.X
|
- 前端:Vue3 + Vite5 + Vue-Router + Pinia + Ant Design Vue 4.X
|
||||||
- 移动端:uniapp + uview2.x
|
- 移动端:uniapp (vue3版本) + uni-ui + (同时支持APP、小程序、H5)
|
||||||
- 后端:SpringBoot + Sa Token + Mybatis-plus + 多种数据库
|
- 后端:SpringBoot + Sa Token + Mybatis-plus + 多种数据库
|
||||||
- 在线预览:[https://preview.smartadmin.vip](https://preview.smartadmin.vip)
|
- 电脑在线预览:[https://preview.smartadmin.vip](https://preview.smartadmin.vip)
|
||||||
- 官方文档:[https://smartadmin.vip](https://smartadmin.vip)
|
- 官方文档:[https://smartadmin.vip](https://smartadmin.vip)
|
||||||
|
- 移动端在线预览:[https://app.smartadmin.vip](https://app.smartadmin.vip/#/pages/login/login)
|
||||||
### **理念与思想**
|
### **理念与思想**
|
||||||
|
|
||||||
- 我们分享的不是徒劳无功的各种功能,而是必须有的功能,如:网络安全、数据变动记录、系统说明文档、版本更新记录、意见反馈、日志、心跳、单号生成器等等。
|
- 我们分享的不是徒劳无功的各种功能,而是必须有的功能,如:网络安全、数据变动记录、系统说明文档、版本更新记录、意见反馈、日志、心跳、单号生成器等等。
|
||||||
@@ -74,22 +75,5 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### **联系我们**
|
|
||||||
|
|
||||||
[1024 创新实验室-主任:卓大](https://zhuoda.vip),混迹于各个技术圈,研究过计算机,熟悉点 java,略懂点前端。
|
|
||||||
[1024 创新实验室(河南·洛阳)](https://1024lab.net) 致力于成为中原领先、国内一流的技术团队,以技术创新为驱动,合作各类项目(软件外包、技术顾问、培训等等)。
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><img src="https://img.smartadmin.1024lab.net/wechat/zhuoda-wechat.jpg" width="250" /></td>
|
|
||||||
<td><img src="https://img.smartadmin.1024lab.net/wechat/liubianxing-gzh.jpg" width="250" /></td>
|
|
||||||
<td><img src="https://img.smartadmin.1024lab.net/wechat/zhuoda-wechat-money-v1.jpg" width="250" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td style="text-align:center">加 主任 “卓大” 微信 <br> 拉你入群,一起学习</td>
|
|
||||||
<td style="text-align:center">关注 “六边形工程师” <br> 分享:赚钱、代码、生活</td>
|
|
||||||
<td style="text-align:center">请 “1024创新实验室” 喝咖啡 <br> 支持我们的开源与分享 </td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,11 @@ public class AdminInterceptor implements HandlerInterceptor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是超级管理员的话,不需要校验权限
|
||||||
|
if(requestEmployee.getAdministratorFlag()){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
SaStrategy.instance.checkMethodAnnotation.accept(method);
|
SaStrategy.instance.checkMethodAnnotation.accept(method);
|
||||||
|
|
||||||
} catch (SaTokenException e) {
|
} catch (SaTokenException e) {
|
||||||
|
|||||||
@@ -44,11 +44,9 @@ public class NoticeVO {
|
|||||||
private LocalDateTime publishTime;
|
private LocalDateTime publishTime;
|
||||||
|
|
||||||
@Schema(description = "作者")
|
@Schema(description = "作者")
|
||||||
@NotBlank(message = "作者不能为空")
|
|
||||||
private String author;
|
private String author;
|
||||||
|
|
||||||
@Schema(description = "来源")
|
@Schema(description = "来源")
|
||||||
@NotBlank(message = "标题不能为空")
|
|
||||||
private String source;
|
private String source;
|
||||||
|
|
||||||
@Schema(description = "文号")
|
@Schema(description = "文号")
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package net.lab1024.sa.admin.module.system.login.controller;
|
package net.lab1024.sa.admin.module.system.login.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import cn.hutool.extra.servlet.ServletUtil;
|
import cn.hutool.extra.servlet.ServletUtil;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
@@ -47,7 +48,10 @@ public class LoginController {
|
|||||||
@GetMapping("/login/getLoginInfo")
|
@GetMapping("/login/getLoginInfo")
|
||||||
@Operation(summary = "获取登录结果信息 @author 卓大")
|
@Operation(summary = "获取登录结果信息 @author 卓大")
|
||||||
public ResponseDTO<LoginResultVO> getLoginInfo() {
|
public ResponseDTO<LoginResultVO> getLoginInfo() {
|
||||||
return ResponseDTO.ok(loginService.getLoginResult(AdminRequestUtil.getRequestUser()));
|
LoginResultVO loginResult = loginService.getLoginResult(AdminRequestUtil.getRequestUser());
|
||||||
|
String tokenValue = StpUtil.getTokenValue();
|
||||||
|
loginResult.setToken(tokenValue);
|
||||||
|
return ResponseDTO.ok(loginResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "退出登陆 @author 卓大")
|
@Operation(summary = "退出登陆 @author 卓大")
|
||||||
|
|||||||
@@ -124,7 +124,7 @@
|
|||||||
<if test="query.keywords != null and query.keywords !=''">
|
<if test="query.keywords != null and query.keywords !=''">
|
||||||
AND ( INSTR(t_notice.title,#{query.keywords})
|
AND ( INSTR(t_notice.title,#{query.keywords})
|
||||||
OR INSTR(t_notice.author,#{query.keywords})
|
OR INSTR(t_notice.author,#{query.keywords})
|
||||||
OR INSTR(t_notice.documentNumber,#{query.keywords})
|
OR INSTR(t_notice.document_number,#{query.keywords})
|
||||||
OR INSTR(t_notice.source,#{query.keywords})
|
OR INSTR(t_notice.source,#{query.keywords})
|
||||||
)
|
)
|
||||||
</if>
|
</if>
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ public class PageParam {
|
|||||||
|
|
||||||
@Schema(description = "页码(不能为空)", example = "1")
|
@Schema(description = "页码(不能为空)", example = "1")
|
||||||
@NotNull(message = "分页参数不能为空")
|
@NotNull(message = "分页参数不能为空")
|
||||||
private Integer pageNum;
|
private Long pageNum;
|
||||||
|
|
||||||
@Schema(description = "每页数量(不能为空)", example = "10")
|
@Schema(description = "每页数量(不能为空)", example = "10")
|
||||||
@NotNull(message = "每页数量不能为空")
|
@NotNull(message = "每页数量不能为空")
|
||||||
@Max(value = 200, message = "每页最大为200")
|
@Max(value = 500, message = "每页最大为500")
|
||||||
private Integer pageSize;
|
private Long pageSize;
|
||||||
|
|
||||||
@Schema(description = "是否查询总条数")
|
@Schema(description = "是否查询总条数")
|
||||||
protected Boolean searchCount;
|
protected Boolean searchCount;
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class ResponseDTO<T> {
|
|||||||
|
|
||||||
public static final int OK_CODE = 0;
|
public static final int OK_CODE = 0;
|
||||||
|
|
||||||
public static final String OK_MSG = "success";
|
public static final String OK_MSG = "操作成功";
|
||||||
|
|
||||||
@Schema(description = "返回码")
|
@Schema(description = "返回码")
|
||||||
private Integer code;
|
private Integer code;
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import net.lab1024.sa.base.constant.SwaggerTagConst;
|
|||||||
import net.lab1024.sa.base.module.support.changelog.domain.form.ChangeLogQueryForm;
|
import net.lab1024.sa.base.module.support.changelog.domain.form.ChangeLogQueryForm;
|
||||||
import net.lab1024.sa.base.module.support.changelog.domain.vo.ChangeLogVO;
|
import net.lab1024.sa.base.module.support.changelog.domain.vo.ChangeLogVO;
|
||||||
import net.lab1024.sa.base.module.support.changelog.service.ChangeLogService;
|
import net.lab1024.sa.base.module.support.changelog.service.ChangeLogService;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
@@ -36,4 +34,11 @@ public class ChangeLogController extends SupportBaseController {
|
|||||||
public ResponseDTO<PageResult<ChangeLogVO>> queryPage(@RequestBody @Valid ChangeLogQueryForm queryForm) {
|
public ResponseDTO<PageResult<ChangeLogVO>> queryPage(@RequestBody @Valid ChangeLogQueryForm queryForm) {
|
||||||
return ResponseDTO.ok(changeLogService.queryPage(queryForm));
|
return ResponseDTO.ok(changeLogService.queryPage(queryForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Operation(summary = "变更内容详情 @author 卓大")
|
||||||
|
@GetMapping("/changeLog/getDetail/{changeLogId}")
|
||||||
|
public ResponseDTO<ChangeLogVO> getDetail(@PathVariable Long changeLogId) {
|
||||||
|
return ResponseDTO.ok(changeLogService.getById(changeLogId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,6 @@ public class ChangeLogService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询
|
* 分页查询
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public PageResult<ChangeLogVO> queryPage(ChangeLogQueryForm queryForm) {
|
public PageResult<ChangeLogVO> queryPage(ChangeLogQueryForm queryForm) {
|
||||||
Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
|
Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
|
||||||
@@ -57,7 +56,6 @@ public class ChangeLogService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新
|
* 更新
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public synchronized ResponseDTO<String> update(ChangeLogUpdateForm updateForm) {
|
public synchronized ResponseDTO<String> update(ChangeLogUpdateForm updateForm) {
|
||||||
ChangeLogEntity existVersion = changeLogDao.selectByVersion(updateForm.getVersion());
|
ChangeLogEntity existVersion = changeLogDao.selectByVersion(updateForm.getVersion());
|
||||||
@@ -71,7 +69,6 @@ public class ChangeLogService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量删除
|
* 批量删除
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public synchronized ResponseDTO<String> batchDelete(List<Long> idList) {
|
public synchronized ResponseDTO<String> batchDelete(List<Long> idList) {
|
||||||
if (CollectionUtils.isEmpty(idList)) {
|
if (CollectionUtils.isEmpty(idList)) {
|
||||||
@@ -93,4 +90,8 @@ public class ChangeLogService {
|
|||||||
changeLogDao.deleteById(changeLogId);
|
changeLogDao.deleteById(changeLogId);
|
||||||
return ResponseDTO.ok();
|
return ResponseDTO.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ChangeLogVO getById(Long changeLogId) {
|
||||||
|
return SmartBeanUtil.copy(changeLogDao.selectById(changeLogId), ChangeLogVO.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public class CodeGeneratorController extends SupportBaseController {
|
|||||||
ResponseDTO<byte[]> download = codeGeneratorService.download(tableName);
|
ResponseDTO<byte[]> download = codeGeneratorService.download(tableName);
|
||||||
|
|
||||||
if (download.getOk()) {
|
if (download.getOk()) {
|
||||||
SmartResponseUtil.setDownloadFileHeader(response, tableName + "-code.zip", (long) download.getData().length);
|
SmartResponseUtil.setDownloadFileHeader(response, tableName + "_code.zip", (long) download.getData().length);
|
||||||
response.getOutputStream().write(download.getData());
|
response.getOutputStream().write(download.getData());
|
||||||
} else {
|
} else {
|
||||||
SmartResponseUtil.write(response, download);
|
SmartResponseUtil.write(response, download);
|
||||||
|
|||||||
@@ -55,22 +55,15 @@ public class RepeatSubmitAspect {
|
|||||||
if (StringUtils.isEmpty(ticket)) {
|
if (StringUtils.isEmpty(ticket)) {
|
||||||
return point.proceed();
|
return point.proceed();
|
||||||
}
|
}
|
||||||
Long timeStamp = this.repeatSubmitTicket.getTicketTimestamp(ticket);
|
Long lastRequestTime = this.repeatSubmitTicket.getTicketTimestamp(ticket);
|
||||||
if (timeStamp != null) {
|
if (lastRequestTime != null) {
|
||||||
Method method = ((MethodSignature) point.getSignature()).getMethod();
|
Method method = ((MethodSignature) point.getSignature()).getMethod();
|
||||||
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
|
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
|
||||||
|
|
||||||
// 说明注解去掉了
|
|
||||||
if (annotation != null) {
|
|
||||||
return point.proceed();
|
|
||||||
}
|
|
||||||
|
|
||||||
int interval = Math.min(annotation.value(), RepeatSubmit.MAX_INTERVAL);
|
int interval = Math.min(annotation.value(), RepeatSubmit.MAX_INTERVAL);
|
||||||
if (System.currentTimeMillis() < timeStamp + interval) {
|
if (System.currentTimeMillis() < lastRequestTime + interval) {
|
||||||
// 提交频繁
|
// 提交频繁
|
||||||
return ResponseDTO.error(UserErrorCode.REPEAT_SUBMIT);
|
return ResponseDTO.error(UserErrorCode.REPEAT_SUBMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Object obj = null;
|
Object obj = null;
|
||||||
try {
|
try {
|
||||||
@@ -80,8 +73,6 @@ public class RepeatSubmitAspect {
|
|||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
log.error("", throwable);
|
log.error("", throwable);
|
||||||
throw throwable;
|
throw throwable;
|
||||||
} finally {
|
|
||||||
this.repeatSubmitTicket.removeTicket(ticket);
|
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,9 +60,9 @@ public class ${name.upperCamel}Controller {
|
|||||||
|
|
||||||
#if($deleteInfo.deleteEnum == "Single" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
#if($deleteInfo.deleteEnum == "Single" || $deleteInfo.deleteEnum == "SingleAndBatch")
|
||||||
@Operation(summary = "单个删除 @author ${basic.backendAuthor}")
|
@Operation(summary = "单个删除 @author ${basic.backendAuthor}")
|
||||||
@GetMapping("/${name.lowerCamel}/delete/{${name.lowerCamel}Id}")
|
@GetMapping("/${name.lowerCamel}/delete/{${primaryKeyFieldName}}")
|
||||||
public ResponseDTO<String> batchDelete(@PathVariable ${primaryKeyJavaType} ${primaryKeyFieldName}) {
|
public ResponseDTO<String> batchDelete(@PathVariable ${primaryKeyJavaType} ${primaryKeyFieldName}) {
|
||||||
return ${name.lowerCamel}Service.delete(${name.lowerCamel}Id);
|
return ${name.lowerCamel}Service.delete(${primaryKeyFieldName});
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import net.lab1024.sa.base.common.domain.ResponseDTO;
|
|||||||
import net.lab1024.sa.base.common.domain.PageResult;
|
import net.lab1024.sa.base.common.domain.PageResult;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
|
|||||||
@@ -62,25 +62,9 @@ server:
|
|||||||
basedir: ${project.log-directory}/tomcat-logs
|
basedir: ${project.log-directory}/tomcat-logs
|
||||||
accesslog:
|
accesslog:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
max-days: 7
|
||||||
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
||||||
|
|
||||||
|
|
||||||
# 文件上传 配置
|
|
||||||
#file:
|
|
||||||
# storage:
|
|
||||||
# mode: local
|
|
||||||
# local:
|
|
||||||
# upload-path: /home/smart_admin_v3/upload/ #文件上传目录
|
|
||||||
# url-prefix:
|
|
||||||
# cloud:
|
|
||||||
# region: oss-cn-qingdao
|
|
||||||
# endpoint: oss-cn-qingdao.aliyuncs.com
|
|
||||||
# bucket-name: common
|
|
||||||
# access-key:
|
|
||||||
# secret-key:
|
|
||||||
# url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
|
||||||
# private-url-expire-seconds: 3600
|
|
||||||
|
|
||||||
# 文件上传 配置
|
# 文件上传 配置
|
||||||
file:
|
file:
|
||||||
storage:
|
storage:
|
||||||
@@ -108,7 +92,7 @@ springdoc:
|
|||||||
knife4j:
|
knife4j:
|
||||||
enable: true
|
enable: true
|
||||||
basic:
|
basic:
|
||||||
enable: true
|
enable: false
|
||||||
username: api # Basic认证用户名
|
username: api # Basic认证用户名
|
||||||
password: 1024 # Basic认证密码
|
password: 1024 # Basic认证密码
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ server:
|
|||||||
basedir: ${project.log-directory}/tomcat-logs
|
basedir: ${project.log-directory}/tomcat-logs
|
||||||
accesslog:
|
accesslog:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
max-days: 7
|
||||||
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ server:
|
|||||||
basedir: ${project.log-directory}/tomcat-logs
|
basedir: ${project.log-directory}/tomcat-logs
|
||||||
accesslog:
|
accesslog:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
max-days: 30
|
||||||
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ server:
|
|||||||
basedir: ${project.log-directory}/tomcat-logs
|
basedir: ${project.log-directory}/tomcat-logs
|
||||||
accesslog:
|
accesslog:
|
||||||
enabled: true
|
enabled: true
|
||||||
|
max-days: 7
|
||||||
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1445
smart-admin-web/javascript-ant-design-vue3/package-lock.json
generated
@@ -19,33 +19,32 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@wangeditor/editor": "5.1.14",
|
"@wangeditor/editor": "5.1.14",
|
||||||
"@wangeditor/editor-for-vue": "5.1.12",
|
"@wangeditor/editor-for-vue": "5.1.12",
|
||||||
"ant-design-vue": "4.0.7",
|
"ant-design-vue": "4.1.2",
|
||||||
"axios": "1.4.0",
|
"axios": "1.6.8",
|
||||||
"clipboard": "2.0.11",
|
"clipboard": "2.0.11",
|
||||||
"crypto-js": "4.1.1",
|
"crypto-js": "4.1.1",
|
||||||
"decimal.js": "10.3.1",
|
"decimal.js": "10.3.1",
|
||||||
"diff": "5.1.0",
|
"diff": "5.2.0",
|
||||||
"diff2html": "3.4.18",
|
"diff2html": "3.4.47",
|
||||||
"echarts": "5.4.3",
|
"echarts": "5.4.3",
|
||||||
"highlight.js": "11.8.0",
|
"highlight.js": "11.8.0",
|
||||||
"js-cookie": "3.0.5",
|
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"lunar-javascript": "1.6.3",
|
"lunar-javascript": "1.6.12",
|
||||||
"mitt": "3.0.0",
|
"mitt": "3.0.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"pinia": "2.1.6",
|
"pinia": "2.1.7",
|
||||||
"sm-crypto": "^0.3.13",
|
"sm-crypto": "0.3.13",
|
||||||
"sortablejs": "1.15.0",
|
"sortablejs": "1.15.0",
|
||||||
"ua-parser-js": "1.0.35",
|
"ua-parser-js": "1.0.35",
|
||||||
"v-viewer": "~1.6.4",
|
"v-viewer": "~1.6.4",
|
||||||
"vue": "3.3.4",
|
"vue": "3.3.13",
|
||||||
"vue-i18n": "9.2.2",
|
"vue-i18n": "9.10.2",
|
||||||
"vue-router": "4.2.4",
|
"vue-router": "4.3.0",
|
||||||
"vue3-json-viewer": "2.2.2"
|
"vue3-json-viewer": "2.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vitejs/plugin-vue": "4.5.0",
|
"@vitejs/plugin-vue": "5.0.4",
|
||||||
"@vue/compiler-sfc": "3.3.4",
|
"@vue/compiler-sfc": "3.4.21",
|
||||||
"eslint": "^8.16.0",
|
"eslint": "^8.16.0",
|
||||||
"eslint-config-prettier": "~9.0.0",
|
"eslint-config-prettier": "~9.0.0",
|
||||||
"eslint-plugin-prettier": "~5.0.0",
|
"eslint-plugin-prettier": "~5.0.0",
|
||||||
@@ -58,9 +57,9 @@
|
|||||||
"stylelint-config-prettier": "~9.0.3",
|
"stylelint-config-prettier": "~9.0.3",
|
||||||
"stylelint-config-standard": "~25.0.0",
|
"stylelint-config-standard": "~25.0.0",
|
||||||
"stylelint-order": "~5.0.0",
|
"stylelint-order": "~5.0.0",
|
||||||
"terser": "~5.19.2",
|
"terser": "~5.29.2",
|
||||||
"vite": "5.0.0",
|
"vite": "5.2.6",
|
||||||
"vue-eslint-parser": "~9.3.1"
|
"vue-eslint-parser": "~9.4.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|||||||
@@ -9,7 +9,29 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<a-config-provider :locale="antdLocale">
|
<a-config-provider
|
||||||
|
:locale="antdLocale"
|
||||||
|
:theme="{
|
||||||
|
algorithm: compactFlag ? theme.compactAlgorithm : theme.defaultAlgorithm,
|
||||||
|
token: {
|
||||||
|
colorPrimary: themeColors[colorIndex].primaryColor,
|
||||||
|
colorLink: themeColors[colorIndex].primaryColor,
|
||||||
|
colorLinkActive: themeColors[colorIndex].activeColor,
|
||||||
|
colorLinkHover: themeColors[colorIndex].hoverColor,
|
||||||
|
colorIcon: themeColors[colorIndex].primaryColor,
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
Button: {
|
||||||
|
colorLink: themeColors[colorIndex].primaryColor,
|
||||||
|
colorLinkActive: themeColors[colorIndex].activeColor,
|
||||||
|
colorLinkHover: themeColors[colorIndex].hoverColor,
|
||||||
|
},
|
||||||
|
Icon: {
|
||||||
|
colorIcon: themeColors[colorIndex].primaryColor,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
<!---全局loading--->
|
<!---全局loading--->
|
||||||
<a-spin :spinning="spinning" tip="稍等片刻,我在拼命加载中..." size="large">
|
<a-spin :spinning="spinning" tip="稍等片刻,我在拼命加载中..." size="large">
|
||||||
<!--- 路由 -->
|
<!--- 路由 -->
|
||||||
@@ -23,12 +45,21 @@
|
|||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { messages } from '/@/i18n';
|
import { messages } from '/@/i18n';
|
||||||
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
||||||
import { useSpinStore } from './store/modules/system/spin';
|
import { useSpinStore } from '/@/store/modules/system/spin';
|
||||||
|
import { theme } from 'ant-design-vue';
|
||||||
|
import { themeColors } from '/@/theme/color.js';
|
||||||
|
|
||||||
const antdLocale = computed(() => messages[useAppConfigStore().language].antdLocale);
|
const antdLocale = computed(() => messages[useAppConfigStore().language].antdLocale);
|
||||||
const dayjsLocale = computed(() => messages[useAppConfigStore().language].dayjsLocale);
|
const dayjsLocale = computed(() => messages[useAppConfigStore().language].dayjsLocale);
|
||||||
dayjs.locale(dayjsLocale);
|
dayjs.locale(dayjsLocale);
|
||||||
|
|
||||||
|
// 全局loading
|
||||||
let spinStore = useSpinStore();
|
let spinStore = useSpinStore();
|
||||||
const spinning = computed(() => spinStore.loading);
|
const spinning = computed(() => spinStore.loading);
|
||||||
|
// 是否紧凑
|
||||||
|
const compactFlag = computed(() => useAppConfigStore().compactFlag);
|
||||||
|
// 主题颜色
|
||||||
|
const colorIndex = computed(() => {
|
||||||
|
return useAppConfigStore().colorIndex;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 354 KiB |
|
After Width: | Height: | Size: 234 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 272 KiB |
|
After Width: | Height: | Size: 206 KiB |
|
Before Width: | Height: | Size: 394 KiB After Width: | Height: | Size: 394 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 3.3 KiB |
@@ -16,6 +16,8 @@ export const appDefaultConfig = {
|
|||||||
sideMenuWidth: 200,
|
sideMenuWidth: 200,
|
||||||
// 菜单主题
|
// 菜单主题
|
||||||
sideMenuTheme: 'dark',
|
sideMenuTheme: 'dark',
|
||||||
|
// 主题颜色索引
|
||||||
|
colorIndex: 0,
|
||||||
// 顶部菜单页面宽度
|
// 顶部菜单页面宽度
|
||||||
pageWidth: '99%',
|
pageWidth: '99%',
|
||||||
// 标签页
|
// 标签页
|
||||||
@@ -32,4 +34,6 @@ export const appDefaultConfig = {
|
|||||||
websiteName: 'SmartAdmin 3.X',
|
websiteName: 'SmartAdmin 3.X',
|
||||||
// 主题颜色
|
// 主题颜色
|
||||||
primaryColor: 'red',
|
primaryColor: 'red',
|
||||||
|
// 紧凑
|
||||||
|
compactFlag: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ const KEY_PREFIX = 'smart_admin_';
|
|||||||
* localStorageKey集合
|
* localStorageKey集合
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
|
// 用户token
|
||||||
|
USER_TOKEN: `${KEY_PREFIX}user_token`,
|
||||||
// 用户信息
|
// 用户信息
|
||||||
USER_INFO: `${KEY_PREFIX}user_info`,
|
USER_INFO: `${KEY_PREFIX}user_info`,
|
||||||
// 用户权限点
|
// 用户权限点
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ export default {
|
|||||||
antdLocale: antd,
|
antdLocale: antd,
|
||||||
dayjsLocale: dayjs,
|
dayjsLocale: dayjs,
|
||||||
'setting.title': 'Setting',
|
'setting.title': 'Setting',
|
||||||
|
'setting.color': 'Theme Color',
|
||||||
'setting.menu.layout': 'Menu Layout',
|
'setting.menu.layout': 'Menu Layout',
|
||||||
'setting.menu.width': 'Menu Width',
|
'setting.menu.width': 'Menu Width',
|
||||||
'setting.menu.theme': 'Menu Theme',
|
'setting.menu.theme': 'Menu Theme',
|
||||||
'setting.page.width': 'Page Width',
|
'setting.page.width': 'Page Width',
|
||||||
|
'setting.compact': 'Page Compact',
|
||||||
'setting.bread': 'Show Bread',
|
'setting.bread': 'Show Bread',
|
||||||
'setting.pagetag': 'Show PageTag',
|
'setting.pagetag': 'Show PageTag',
|
||||||
'setting.footer': 'Show Footer',
|
'setting.footer': 'Show Footer',
|
||||||
|
|||||||
@@ -14,9 +14,11 @@ export default {
|
|||||||
antdLocale: antd,
|
antdLocale: antd,
|
||||||
dayjsLocale: dayjs,
|
dayjsLocale: dayjs,
|
||||||
'setting.title': '网站设置',
|
'setting.title': '网站设置',
|
||||||
|
'setting.color': '主题颜色',
|
||||||
'setting.menu.layout': '菜单布局',
|
'setting.menu.layout': '菜单布局',
|
||||||
'setting.menu.width': '菜单宽度',
|
'setting.menu.width': '菜单宽度',
|
||||||
'setting.menu.theme': '菜单主题',
|
'setting.menu.theme': '菜单主题',
|
||||||
|
'setting.compact': '页面紧凑',
|
||||||
'setting.page.width': '页面宽度',
|
'setting.page.width': '页面宽度',
|
||||||
'setting.bread': '面包屑',
|
'setting.bread': '面包屑',
|
||||||
'setting.pagetag': '标签页',
|
'setting.pagetag': '标签页',
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
import { computed, ref, onMounted } from 'vue';
|
import { computed, ref, onMounted } from 'vue';
|
||||||
import { loginApi } from '/src/api/system/login-api';
|
import { loginApi } from '/src/api/system/login-api';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
import { clearAllCoolies } from '/@/utils/cookie-util';
|
|
||||||
import { localClear } from '/@/utils/local-util';
|
import { localClear } from '/@/utils/local-util';
|
||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
import HeaderResetPassword from './header-reset-password-modal/index.vue';
|
import HeaderResetPassword from './header-reset-password-modal/index.vue';
|
||||||
@@ -48,9 +47,7 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
smartSentry.captureError(e);
|
smartSentry.captureError(e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
localClear();
|
localClear();
|
||||||
clearAllCoolies();
|
|
||||||
useUserStore().logout();
|
useUserStore().logout();
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,10 +11,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-drawer :title="$t('setting.title')" placement="right" :open="visible" @close="close">
|
<a-drawer :title="$t('setting.title')" placement="right" :open="visible" @close="close">
|
||||||
<a-form layout="horizontal" :label-col="{ span: 8 }">
|
<a-form layout="horizontal" :label-col="{ span: 8 }">
|
||||||
<a-form-item label="语言/Language">
|
<a-form-item :label="$t('setting.color')">
|
||||||
<a-select v-model:value="formState.language" @change="changeLanguage" style="width: 120px">
|
<div style="display: flex; align-items: center">
|
||||||
<a-select-option v-for="item in i18nList" :key="item.value" :value="item.value">{{ item.text }}</a-select-option>
|
<template v-for="(item, index) in themeColors">
|
||||||
</a-select>
|
<div v-if="index === formState.colorIndex" class="color">
|
||||||
|
<CheckSquareFilled :style="{ color: item.primaryColor, fontSize: '22px' }" />
|
||||||
|
</div>
|
||||||
|
<div v-else @click="changeColor(index)" class="color">
|
||||||
|
<svg
|
||||||
|
class="icon"
|
||||||
|
viewBox="0 0 1024 1024"
|
||||||
|
version="1.1"
|
||||||
|
:fill="item.primaryColor"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="26"
|
||||||
|
height="26"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M128 160.01219c0-17.67619 14.336-32.01219 32.01219-32.01219h704c17.65181 0 31.98781 14.336 31.98781 32.01219v704c0 17.65181-14.336 31.98781-32.01219 31.98781H160.036571a31.98781 31.98781 0 0 1-32.01219-32.01219V160.036571z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item :label="$t('setting.compact')">
|
||||||
|
<a-radio-group v-model:value="formState.compactFlag" button-style="solid" @change="changeCompactFlag">
|
||||||
|
<a-radio-button :value="false">默认</a-radio-button>
|
||||||
|
<a-radio-button :value="true">紧凑</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="$t('setting.menu.layout')">
|
<a-form-item :label="$t('setting.menu.layout')">
|
||||||
<a-radio-group @change="changeLayout" button-style="solid" v-model:value="formState.layout">
|
<a-radio-group @change="changeLayout" button-style="solid" v-model:value="formState.layout">
|
||||||
@@ -23,6 +48,12 @@
|
|||||||
</a-radio-button>
|
</a-radio-button>
|
||||||
</a-radio-group>
|
</a-radio-group>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
<a-form-item :label="$t('setting.menu.theme')">
|
||||||
|
<a-radio-group v-model:value="formState.sideMenuTheme" button-style="solid" @change="changeMenuTheme">
|
||||||
|
<a-radio-button value="dark">Dark</a-radio-button>
|
||||||
|
<a-radio-button value="light">Light</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
<a-form-item :label="$t('setting.menu.width')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
|
<a-form-item :label="$t('setting.menu.width')" v-if="formState.layout === LAYOUT_ENUM.SIDE.value">
|
||||||
<a-input-number @change="changeSideMenuWidth" v-model:value="formState.sideMenuWidth" :min="1" />
|
<a-input-number @change="changeSideMenuWidth" v-model:value="formState.sideMenuWidth" :min="1" />
|
||||||
像素(px)
|
像素(px)
|
||||||
@@ -31,11 +62,10 @@
|
|||||||
<a-input @change="changePageWidth" v-model:value="formState.pageWidth" />
|
<a-input @change="changePageWidth" v-model:value="formState.pageWidth" />
|
||||||
像素(px)或者 百分比
|
像素(px)或者 百分比
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="$t('setting.menu.theme')">
|
<a-form-item label="语言/Language">
|
||||||
<a-radio-group v-model:value="formState.sideMenuTheme" button-style="solid" @change="changeMenuTheme">
|
<a-select v-model:value="formState.language" @change="changeLanguage" style="width: 120px">
|
||||||
<a-radio-button value="dark">Dark</a-radio-button>
|
<a-select-option v-for="item in i18nList" :key="item.value" :value="item.value">{{ item.text }}</a-select-option>
|
||||||
<a-radio-button value="light">Light</a-radio-button>
|
</a-select>
|
||||||
</a-radio-group>
|
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :label="$t('setting.bread')">
|
<a-form-item :label="$t('setting.bread')">
|
||||||
<a-switch @change="changeBreadCrumbFlag" v-model:checked="formState.breadCrumbFlag" checked-children="显示" un-checked-children="隐藏" />
|
<a-switch @change="changeBreadCrumbFlag" v-model:checked="formState.breadCrumbFlag" checked-children="显示" un-checked-children="隐藏" />
|
||||||
@@ -69,6 +99,7 @@
|
|||||||
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
||||||
import { Modal } from 'ant-design-vue';
|
import { Modal } from 'ant-design-vue';
|
||||||
import { appDefaultConfig } from '/@/config/app-config';
|
import { appDefaultConfig } from '/@/config/app-config';
|
||||||
|
import { themeColors } from '/@/theme/color.js';
|
||||||
|
|
||||||
// ----------------- modal 显示与隐藏 -----------------
|
// ----------------- modal 显示与隐藏 -----------------
|
||||||
|
|
||||||
@@ -130,10 +161,14 @@
|
|||||||
layout: appConfigStore.layout,
|
layout: appConfigStore.layout,
|
||||||
// 页面宽度
|
// 页面宽度
|
||||||
pageWidth: appConfigStore.pageWidth,
|
pageWidth: appConfigStore.pageWidth,
|
||||||
|
// 颜色
|
||||||
|
colorIndex: appConfigStore.colorIndex,
|
||||||
// 侧边菜单宽度
|
// 侧边菜单宽度
|
||||||
sideMenuWidth: appConfigStore.sideMenuWidth,
|
sideMenuWidth: appConfigStore.sideMenuWidth,
|
||||||
// 菜单主题
|
// 菜单主题
|
||||||
sideMenuTheme: appConfigStore.sideMenuTheme,
|
sideMenuTheme: appConfigStore.sideMenuTheme,
|
||||||
|
// 页面紧凑
|
||||||
|
compactFlag: appConfigStore.compactFlag,
|
||||||
// 标签页
|
// 标签页
|
||||||
pageTagFlag: appConfigStore.pageTagFlag,
|
pageTagFlag: appConfigStore.pageTagFlag,
|
||||||
// 面包屑
|
// 面包屑
|
||||||
@@ -162,6 +197,13 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeColor(index) {
|
||||||
|
formState.colorIndex = index;
|
||||||
|
appConfigStore.$patch({
|
||||||
|
colorIndex: index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function changeSideMenuWidth(value) {
|
function changeSideMenuWidth(value) {
|
||||||
appConfigStore.$patch({
|
appConfigStore.$patch({
|
||||||
sideMenuWidth: value,
|
sideMenuWidth: value,
|
||||||
@@ -180,6 +222,12 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeCompactFlag(e) {
|
||||||
|
appConfigStore.$patch({
|
||||||
|
compactFlag: e.target.value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function changeBreadCrumbFlag(e) {
|
function changeBreadCrumbFlag(e) {
|
||||||
appConfigStore.$patch({
|
appConfigStore.$patch({
|
||||||
breadCrumbFlag: e,
|
breadCrumbFlag: e,
|
||||||
@@ -222,4 +270,14 @@
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.color {
|
||||||
|
margin-left: 8px;
|
||||||
|
display: inline;
|
||||||
|
height: 26px;
|
||||||
|
width: 26px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -34,9 +34,9 @@
|
|||||||
<HeaderAvatar />
|
<HeaderAvatar />
|
||||||
</div>
|
</div>
|
||||||
<!---帮助文档--->
|
<!---帮助文档--->
|
||||||
<div class="user-space-item" @click="showHelpDoc">
|
<div class="user-space-item" @click="showHelpDoc" v-if="!showHelpDocFlag">
|
||||||
<question-circle-two-tone style="font-size: 18px; margin-right: 5px; margin-top: 5px" />
|
|
||||||
<span>帮助文档</span>
|
<span>帮助文档</span>
|
||||||
|
<DoubleLeftOutlined v-if="!showHelpDocFlag" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<HeaderSetting ref="headerSetting" />
|
<HeaderSetting ref="headerSetting" />
|
||||||
@@ -48,7 +48,8 @@
|
|||||||
import HeaderSetting from './header-setting.vue';
|
import HeaderSetting from './header-setting.vue';
|
||||||
import HeaderMessage from './header-message.vue';
|
import HeaderMessage from './header-message.vue';
|
||||||
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import { theme } from 'ant-design-vue';
|
||||||
|
|
||||||
// 设置
|
// 设置
|
||||||
const headerSetting = ref();
|
const headerSetting = ref();
|
||||||
@@ -67,10 +68,17 @@
|
|||||||
useAppConfigStore().showHelpDoc();
|
useAppConfigStore().showHelpDoc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const showHelpDocFlag = computed(() => {
|
||||||
|
return useAppConfigStore().helpDocFlag;
|
||||||
|
});
|
||||||
|
|
||||||
//搜索
|
//搜索
|
||||||
function search(){
|
function search() {
|
||||||
window.open("https://1024lab.net");
|
window.open('https://1024lab.net');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { useToken } = theme;
|
||||||
|
const { token } = useToken();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
@@ -91,7 +99,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.user-space-item:hover {
|
.user-space-item:hover {
|
||||||
color: @primary-color;
|
color: v-bind('token.colorPrimary');
|
||||||
background: @hover-bg-color;
|
background: @hover-bg-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<a-row style="border-bottom: 1px solid #eeeeee; position: relative" v-show="pageTagFlag">
|
<a-row style="border-bottom: 1px solid #eeeeee; position: relative" v-show="pageTagFlag">
|
||||||
<a-dropdown :trigger="['contextmenu']">
|
<a-dropdown :trigger="['contextmenu']">
|
||||||
<div class="smart-page-tag">
|
<div class="smart-page-tag">
|
||||||
<a-tabs style="width: 100%" :tab-position="mode" v-model:activeKey="selectedKey" size="small" @tabClick="selectTab" >
|
<a-tabs style="width: 100%" :tab-position="mode" v-model:activeKey="selectedKey" size="small" @tabClick="selectTab">
|
||||||
<a-tab-pane v-for="item in tagNav" :key="item.menuName">
|
<a-tab-pane v-for="item in tagNav" :key="item.menuName">
|
||||||
<template #tab>
|
<template #tab>
|
||||||
<span>
|
<span>
|
||||||
@@ -55,12 +55,7 @@
|
|||||||
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
||||||
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
import { useAppConfigStore } from '/@/store/modules/system/app-config';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
|
import { theme } from 'ant-design-vue';
|
||||||
// //样式
|
|
||||||
// const tagOperateWidth = ref(40);
|
|
||||||
// const tabBarStyle = {
|
|
||||||
// width: 'calc(100% - 80px)'
|
|
||||||
// }
|
|
||||||
|
|
||||||
//标签页 是否显示
|
//标签页 是否显示
|
||||||
const pageTagFlag = computed(() => useAppConfigStore().$state.pageTagFlag);
|
const pageTagFlag = computed(() => useAppConfigStore().$state.pageTagFlag);
|
||||||
@@ -85,7 +80,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 寻找tag
|
// 寻找tag
|
||||||
let tag = tagNav.value.find((e) => e.menuName == name);
|
let tag = tagNav.value.find((e) => e.menuName === name);
|
||||||
if (!tag) {
|
if (!tag) {
|
||||||
router.push({ name: HOME_PAGE_NAME });
|
router.push({ name: HOME_PAGE_NAME });
|
||||||
return;
|
return;
|
||||||
@@ -96,7 +91,7 @@
|
|||||||
|
|
||||||
//通过菜单关闭
|
//通过菜单关闭
|
||||||
function closeByMenu(closeAll) {
|
function closeByMenu(closeAll) {
|
||||||
let find = tagNav.value.find((e) => e.menuName == selectedKey.value);
|
let find = tagNav.value.find((e) => e.menuName === selectedKey.value);
|
||||||
if (!find || closeAll) {
|
if (!find || closeAll) {
|
||||||
closeTag(null, true);
|
closeTag(null, true);
|
||||||
} else {
|
} else {
|
||||||
@@ -110,12 +105,12 @@
|
|||||||
if (item && !closeAll) {
|
if (item && !closeAll) {
|
||||||
let goName = HOME_PAGE_NAME;
|
let goName = HOME_PAGE_NAME;
|
||||||
let goQuery = undefined;
|
let goQuery = undefined;
|
||||||
if (item.fromMenuName && tagNav.value.some((e) => e.menuName == item.fromMenuName)) {
|
if (item.fromMenuName && tagNav.value.some((e) => e.menuName === item.fromMenuName)) {
|
||||||
goName = item.fromMenuName;
|
goName = item.fromMenuName;
|
||||||
goQuery = item.fromMenuQuery;
|
goQuery = item.fromMenuQuery;
|
||||||
} else {
|
} else {
|
||||||
// 查询左侧tag
|
// 查询左侧tag
|
||||||
let index = tagNav.value.findIndex((e) => e.menuName == item.menuName);
|
let index = tagNav.value.findIndex((e) => e.menuName === item.menuName);
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
// 查询左侧tag
|
// 查询左侧tag
|
||||||
let leftTagNav = tagNav.value[index - 1];
|
let leftTagNav = tagNav.value[index - 1];
|
||||||
@@ -132,10 +127,14 @@
|
|||||||
// 关闭其他tag不做处理 直接调用closeTagNav
|
// 关闭其他tag不做处理 直接调用closeTagNav
|
||||||
useUserStore().closeTagNav(item ? item.menuName : null, closeAll);
|
useUserStore().closeTagNav(item ? item.menuName : null, closeAll);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { useToken } = theme;
|
||||||
|
const { token } = useToken();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="less">
|
<style scoped lang="less">
|
||||||
@smart-page-tag-operate-width: 40px;
|
@smart-page-tag-operate-width: 40px;
|
||||||
|
@color-primary: v-bind('token.colorPrimary');
|
||||||
|
|
||||||
.smart-page-tag-operate {
|
.smart-page-tag-operate {
|
||||||
width: @smart-page-tag-operate-width;
|
width: @smart-page-tag-operate-width;
|
||||||
@@ -164,7 +163,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.smart-page-tag-operate:hover {
|
.smart-page-tag-operate:hover {
|
||||||
color: @primary-color;
|
color: @color-primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.smart-page-tag {
|
.smart-page-tag {
|
||||||
@@ -184,7 +183,7 @@
|
|||||||
.smart-page-tag-close {
|
.smart-page-tag-close {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
color: #8c8c8c;
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 覆盖 ant design vue的 tabs 样式,变小一点 **/
|
/** 覆盖 ant design vue的 tabs 样式,变小一点 **/
|
||||||
@@ -203,15 +202,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-tabs-tab-active) {
|
:deep(.ant-tabs-tab-active) {
|
||||||
background-color: #e8f4ff;
|
background-color: #eeeeee;
|
||||||
.smart-page-tag-close {
|
.smart-page-tag-close {
|
||||||
color: @primary-color;
|
color: @color-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
:deep(.ant-tabs-nav .ant-tabs-tab:hover) {
|
:deep(.ant-tabs-nav .ant-tabs-tab:hover) {
|
||||||
background-color: #e8f4ff;
|
background-color: #eeeeee;
|
||||||
.smart-page-tag-close {
|
.smart-page-tag-close {
|
||||||
color: @primary-color;
|
color: @color-primary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,17 +90,20 @@
|
|||||||
z-index: 100;
|
z-index: 100;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.logo-img {
|
.logo-img {
|
||||||
width: 40px;
|
width: 30px;
|
||||||
height: 40px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: nowrap;
|
||||||
color: v-bind('theme === "light" ? "#001529": "#ffffff"');
|
color: v-bind('theme === "light" ? "#001529": "#ffffff"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,9 +79,10 @@
|
|||||||
z-index: 21;
|
z-index: 21;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
.logo-img {
|
.logo-img {
|
||||||
width: 32px;
|
width: 30px;
|
||||||
height: 32px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +94,11 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
.logo-img {
|
.logo-img {
|
||||||
width: 40px;
|
width: 30px;
|
||||||
height: 40px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
|||||||
@@ -86,10 +86,10 @@
|
|||||||
|
|
||||||
const color = computed(() => {
|
const color = computed(() => {
|
||||||
let isLight = useAppConfigStore().$state.sideMenuTheme === 'light';
|
let isLight = useAppConfigStore().$state.sideMenuTheme === 'light';
|
||||||
return {
|
return {
|
||||||
color: isLight ? '#001529' : '#FFFFFF',
|
color: isLight ? '#001529' : '#FFFFFF',
|
||||||
background:isLight ? '#FFFFFF' : '#001529',
|
background: isLight ? '#FFFFFF' : '#001529',
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -118,7 +118,7 @@
|
|||||||
|
|
||||||
.logo-img {
|
.logo-img {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
height: 45px;
|
height: 30px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
.title {
|
.title {
|
||||||
@@ -161,13 +161,13 @@
|
|||||||
color: v-bind('color.color');
|
color: v-bind('color.color');
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-space-item{
|
.user-space-item {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.ant-menu-horizontal){
|
:deep(.ant-menu-horizontal) {
|
||||||
border-bottom:0;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
*/
|
*/
|
||||||
import { message, Modal } from 'ant-design-vue';
|
import { message, Modal } from 'ant-design-vue';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { clearAllCoolies, getTokenFromCookie } from '/@/utils/cookie-util';
|
import { localClear, localRead } from '/@/utils/local-util';
|
||||||
import { localClear } from '/@/utils/local-util';
|
|
||||||
import { decryptData, encryptData } from './encrypt';
|
import { decryptData, encryptData } from './encrypt';
|
||||||
import { DATA_TYPE_ENUM } from '../constants/common-const';
|
import { DATA_TYPE_ENUM } from '../constants/common-const';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
// token的消息头
|
// token的消息头
|
||||||
const TOKEN_HEADER = 'x-access-token';
|
const TOKEN_HEADER = 'x-access-token';
|
||||||
@@ -28,7 +28,7 @@ const smartAxios = axios.create({
|
|||||||
smartAxios.interceptors.request.use(
|
smartAxios.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
// 在发送请求之前消息头加入token token
|
// 在发送请求之前消息头加入token token
|
||||||
const token = getTokenFromCookie();
|
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
||||||
if (token) {
|
if (token) {
|
||||||
config.headers[TOKEN_HEADER] = token;
|
config.headers[TOKEN_HEADER] = token;
|
||||||
} else {
|
} else {
|
||||||
@@ -73,7 +73,6 @@ smartAxios.interceptors.response.use(
|
|||||||
if (res.code === 30007 || res.code === 30008) {
|
if (res.code === 30007 || res.code === 30008) {
|
||||||
message.destroy();
|
message.destroy();
|
||||||
message.error('您没有登录,请重新登录');
|
message.error('您没有登录,请重新登录');
|
||||||
clearAllCoolies();
|
|
||||||
localClear();
|
localClear();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.href = '/';
|
location.href = '/';
|
||||||
@@ -97,7 +96,6 @@ smartAxios.interceptors.response.use(
|
|||||||
content: res.msg,
|
content: res.msg,
|
||||||
onOk() {
|
onOk() {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
clearAllCoolies();
|
|
||||||
localClear();
|
localClear();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
location.href = '/';
|
location.href = '/';
|
||||||
|
|||||||
@@ -25,8 +25,10 @@ import smartEnumPlugin from '/@/plugins/smart-enums-plugin';
|
|||||||
import { buildRoutes, router } from '/@/router';
|
import { buildRoutes, router } from '/@/router';
|
||||||
import { store } from '/@/store';
|
import { store } from '/@/store';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
|
import 'ant-design-vue/dist/reset.css';
|
||||||
import '/@/theme/index.less';
|
import '/@/theme/index.less';
|
||||||
import { getTokenFromCookie } from '/@/utils/cookie-util';
|
import { localRead } from '/@/utils/local-util.js';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -------------------- ※ 着重 解释说明下main.js的初始化逻辑 begin ※ --------------------
|
* -------------------- ※ 着重 解释说明下main.js的初始化逻辑 begin ※ --------------------
|
||||||
@@ -82,7 +84,7 @@ function initVue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//不需要获取用户信息、用户菜单、用户菜单动态路由,直接初始化vue即可
|
//不需要获取用户信息、用户菜单、用户菜单动态路由,直接初始化vue即可
|
||||||
let token = getTokenFromCookie();
|
let token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
initVue();
|
initVue();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import { PAGE_PATH_404, PAGE_PATH_LOGIN } from '/@/constants/common-const';
|
|||||||
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
||||||
import SmartLayout from '../layout/index.vue';
|
import SmartLayout from '../layout/index.vue';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
import { clearAllCoolies, getTokenFromCookie } from '/@/utils/cookie-util';
|
import { localClear, localRead } from '/@/utils/local-util';
|
||||||
import { localClear } from '/@/utils/local-util';
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
@@ -39,9 +39,8 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证登录
|
// 验证登录
|
||||||
const token = getTokenFromCookie();
|
const token = localRead(LocalStorageKeyConst.USER_TOKEN);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
clearAllCoolies();
|
|
||||||
localClear();
|
localClear();
|
||||||
next({ path: PAGE_PATH_LOGIN });
|
next({ path: PAGE_PATH_LOGIN });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -17,5 +17,5 @@ export const loginRouters = [
|
|||||||
title: '登录',
|
title: '登录',
|
||||||
hideInMenu: true,
|
hideInMenu: true,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import { defineStore } from 'pinia';
|
|||||||
import localKey from '/@/constants/local-storage-key-const';
|
import localKey from '/@/constants/local-storage-key-const';
|
||||||
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
import { HOME_PAGE_NAME } from '/@/constants/system/home-const';
|
||||||
import { MENU_TYPE_ENUM } from '/@/constants/system/menu-const';
|
import { MENU_TYPE_ENUM } from '/@/constants/system/menu-const';
|
||||||
import { getTokenFromCookie } from '/@/utils/cookie-util';
|
|
||||||
import { localClear, localRead, localSave } from '/@/utils/local-util';
|
import { localClear, localRead, localSave } from '/@/utils/local-util';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const';
|
||||||
|
|
||||||
export const useUserStore = defineStore({
|
export const useUserStore = defineStore({
|
||||||
id: 'userStore',
|
id: 'userStore',
|
||||||
@@ -61,7 +61,7 @@ export const useUserStore = defineStore({
|
|||||||
if (state.token) {
|
if (state.token) {
|
||||||
return state.token;
|
return state.token;
|
||||||
}
|
}
|
||||||
return getTokenFromCookie();
|
return localRead(LocalStorageKeyConst.USER_TOKEN);
|
||||||
},
|
},
|
||||||
//是否初始化了 路由
|
//是否初始化了 路由
|
||||||
getMenuRouterInitFlag(state) {
|
getMenuRouterInitFlag(state) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
export const themeColors = [
|
||||||
|
// 蓝色
|
||||||
|
{
|
||||||
|
primaryColor: '#1677ff',
|
||||||
|
activeColor: '#0958d9',
|
||||||
|
hoverColor: '#bae0ff',
|
||||||
|
},
|
||||||
|
// 紫色
|
||||||
|
{
|
||||||
|
primaryColor: '#722ED1',
|
||||||
|
activeColor: '#531dab',
|
||||||
|
hoverColor: '#9254de',
|
||||||
|
},
|
||||||
|
// 红色
|
||||||
|
{
|
||||||
|
primaryColor: '#F5222D',
|
||||||
|
activeColor: '#cf1322',
|
||||||
|
hoverColor: '#ff4d4f',
|
||||||
|
},
|
||||||
|
// 青色
|
||||||
|
{
|
||||||
|
primaryColor: '#13c2c2',
|
||||||
|
activeColor: '#08979c',
|
||||||
|
hoverColor: '#36cfc9',
|
||||||
|
},
|
||||||
|
// 粉色
|
||||||
|
{
|
||||||
|
primaryColor: '#EB2F96',
|
||||||
|
activeColor: '#c41d7f',
|
||||||
|
hoverColor: '#f759ab',
|
||||||
|
},
|
||||||
|
// 绿色
|
||||||
|
{
|
||||||
|
primaryColor: '#52C41A',
|
||||||
|
activeColor: '#389e0d',
|
||||||
|
hoverColor: '#73d13d',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* cookie相关操作
|
|
||||||
*
|
|
||||||
* @Author: 1024创新实验室-主任:卓大
|
|
||||||
* @Date: 2022-09-06 20:58:49
|
|
||||||
* @Wechat: zhuda1024
|
|
||||||
* @Email: lab1024@163.com
|
|
||||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
|
||||||
*/
|
|
||||||
import Cookies from 'js-cookie';
|
|
||||||
|
|
||||||
export const COOKIE_TOKEN_KEY = 'user_token';
|
|
||||||
|
|
||||||
export const clearAllCoolies = () => {
|
|
||||||
Cookies.remove(COOKIE_TOKEN_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getTokenFromCookie = () => {
|
|
||||||
return Cookies.get(COOKIE_TOKEN_KEY);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 一年后cookie过期
|
|
||||||
*
|
|
||||||
* @param token
|
|
||||||
*/
|
|
||||||
export const saveTokenToCookie = (token) => {
|
|
||||||
Cookies.set(COOKIE_TOKEN_KEY, token, { expires: 365 });
|
|
||||||
};
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
* @Email: lab1024@163.com
|
* @Email: lab1024@163.com
|
||||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const localSave = (key, value) => {
|
export const localSave = (key, value) => {
|
||||||
localStorage.setItem(key, value);
|
localStorage.setItem(key, value);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,12 @@
|
|||||||
* @Copyright 1024创新实验室
|
* @Copyright 1024创新实验室
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<a-modal title="更新日志" width="700px" :open="visibleFlag" @close="onClose" >
|
<a-modal title="更新日志" width="700px" :open="visibleFlag" @cancel="onClose">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<pre>{{ content }}</pre>
|
<pre>{{ content }}</pre>
|
||||||
<div v-if="link">链接:<a :href="link" target="_blank">{{ link }}</a></div>
|
<div v-if="link">
|
||||||
|
链接:<a :href="link" target="_blank">{{ link }}</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -18,7 +19,6 @@
|
|||||||
<a-button type="primary" @click="onClose">关闭</a-button>
|
<a-button type="primary" @click="onClose">关闭</a-button>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</a-modal>
|
</a-modal>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
@close="onClose"
|
@close="onClose"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
|
<a-alert message="超管需要直接在数据库表 t_employee修改哦" type="error" closable />
|
||||||
|
<br />
|
||||||
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
|
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
|
||||||
<a-form-item label="姓名" name="actualName">
|
<a-form-item label="姓名" name="actualName">
|
||||||
<a-input v-model:value.trim="form.actualName" placeholder="请输入姓名" />
|
<a-input v-model:value.trim="form.actualName" placeholder="请输入姓名" />
|
||||||
@@ -63,7 +65,7 @@
|
|||||||
import { GENDER_ENUM } from '/@/constants/common-const';
|
import { GENDER_ENUM } from '/@/constants/common-const';
|
||||||
import { regular } from '/@/constants/regular-const';
|
import { regular } from '/@/constants/regular-const';
|
||||||
import { SmartLoading } from '/@/components/framework/smart-loading';
|
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
// ----------------------- 以下是字段定义 emits props ---------------------
|
// ----------------------- 以下是字段定义 emits props ---------------------
|
||||||
const departmentTreeSelect = ref();
|
const departmentTreeSelect = ref();
|
||||||
// emit
|
// emit
|
||||||
|
|||||||
@@ -57,6 +57,9 @@
|
|||||||
bordered
|
bordered
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ text, record, index, column }">
|
<template #bodyCell="{ text, record, index, column }">
|
||||||
|
<template v-if="column.dataIndex === 'administratorFlag'">
|
||||||
|
<a-tag color="error" v-if="text">超管</a-tag>
|
||||||
|
</template>
|
||||||
<template v-if="column.dataIndex === 'disabledFlag'">
|
<template v-if="column.dataIndex === 'disabledFlag'">
|
||||||
<a-tag :color="text ? 'error' : 'processing'">{{ text ? '禁用' : '启用' }}</a-tag>
|
<a-tag :color="text ? 'error' : 'processing'">{{ text ? '禁用' : '启用' }}</a-tag>
|
||||||
</template>
|
</template>
|
||||||
@@ -152,6 +155,11 @@
|
|||||||
dataIndex: 'loginName',
|
dataIndex: 'loginName',
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '超管',
|
||||||
|
dataIndex: 'administratorFlag',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '状态',
|
title: '状态',
|
||||||
dataIndex: 'disabledFlag',
|
dataIndex: 'disabledFlag',
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<a-button class="button-style" type="primary" @click="updateDataScope" v-privilege="'system:role:dataScope:update'"> 保存 </a-button>
|
<a-button class="button-style" type="primary" @click="updateDataScope" v-privilege="'system:role:dataScope:update'"> 保存 </a-button>
|
||||||
<a-button class="button-style" @click="getDataScope" > 刷新 </a-button>
|
<a-button class="button-style" @click="getDataScope"> 刷新 </a-button>
|
||||||
</div>
|
</div>
|
||||||
<a-row class="header">
|
<a-row class="header">
|
||||||
<a-col class="tab-margin" :span="4">业务单据</a-col>
|
<a-col class="tab-margin" :span="4">业务单据</a-col>
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
roleId: selectRoleId.value,
|
roleId: selectRoleId.value,
|
||||||
dataScopeItemList: selectedDataScopeList.value.filter((e) => !_.isUndefined(e.viewType)),
|
dataScopeItemList: selectedDataScopeList.value.filter((e) => !_.isUndefined(e.viewType)),
|
||||||
};
|
};
|
||||||
await roleApi.updateRoleDataScopeList(data);
|
await roleApi.updateDataScope(data);
|
||||||
message.success('保存成功');
|
message.success('保存成功');
|
||||||
getDataScope();
|
getDataScope();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<default-home-card extra="更多" icon="FireTwoTone" title="更新日志" @extraClick="onMore">
|
<default-home-card extra="更多" icon="FlagOutlined" title="更新日志" @extraClick="onMore">
|
||||||
<a-empty v-if="$lodash.isEmpty(data)" />
|
<a-empty v-if="$lodash.isEmpty(data)" />
|
||||||
<ul v-else>
|
<ul v-else>
|
||||||
<template v-for="(item, index) in data" :key="index">
|
<template v-for="(item, index) in data" :key="index">
|
||||||
|
|||||||
@@ -13,9 +13,9 @@
|
|||||||
<a-card size="small">
|
<a-card size="small">
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<component :is="$antIcons[props.icon]" v-if="props.icon" :style="{ fontSize: '18px' }" />
|
<component :is="$antIcons[props.icon]" v-if="props.icon" :style="{ fontSize: '18px', color: token.colorPrimary }" />
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
<span v-if="!$slots.title" class="smart-margin-left10">{{ props.title }}</span>
|
<span v-if="!$slots.title" class="smart-margin-left10">{{ props.title }} </span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="props.extra" #extra>
|
<template v-if="props.extra" #extra>
|
||||||
@@ -27,34 +27,43 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
let props = defineProps({
|
import { theme } from 'ant-design-vue';
|
||||||
icon: String,
|
import { computed } from 'vue';
|
||||||
title: String,
|
|
||||||
extra: String,
|
|
||||||
});
|
|
||||||
let emits = defineEmits(['extraClick']);
|
|
||||||
|
|
||||||
function extraClick() {
|
let props = defineProps({
|
||||||
emits('extraClick');
|
icon: String,
|
||||||
}
|
title: String,
|
||||||
|
extra: String,
|
||||||
|
});
|
||||||
|
let emits = defineEmits(['extraClick']);
|
||||||
|
|
||||||
|
function extraClick() {
|
||||||
|
emits('extraClick');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { useToken } = theme;
|
||||||
|
const { token } = useToken();
|
||||||
|
const color = computed(() => {
|
||||||
|
return token.colorPrimary;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.card-container {
|
.card-container {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 3px;
|
width: 3px;
|
||||||
height: 30px;
|
height: 30px;
|
||||||
background-color: @primary-color;
|
background-color: v-bind('token.colorPrimary');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<default-home-card icon="ProfileTwoTone" title="销量统计">
|
<default-home-card icon="Profile" title="销量统计">
|
||||||
<div class="echarts-box">
|
<div class="echarts-box">
|
||||||
<div class="category-main" id="category-main"></div>
|
<div class="category-main" id="category-main"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,113 +7,112 @@
|
|||||||
* @FilePath: /smart-admin/src/views/system/home/components/gauge.vue
|
* @FilePath: /smart-admin/src/views/system/home/components/gauge.vue
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<default-home-card icon="RocketTwoTone" title="业绩完成度">
|
<default-home-card icon="Rocket" title="业绩完成度">
|
||||||
<div class="echarts-box">
|
<div class="echarts-box">
|
||||||
<div id="gauge-main" class="gauge-main"></div>
|
<div id="gauge-main" class="gauge-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</default-home-card>
|
</default-home-card>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import DefaultHomeCard from "/@/views/system/home/components/default-home-card.vue";
|
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
||||||
import * as echarts from "echarts";
|
import * as echarts from 'echarts';
|
||||||
import {onMounted, watch} from "vue";
|
import { onMounted, watch } from 'vue';
|
||||||
import {reactive} from "vue";
|
import { reactive } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
percent: {
|
percent: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0
|
default: 0,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let option = reactive({});
|
let option = reactive({});
|
||||||
watch(
|
watch(
|
||||||
() => props.percent,
|
() => props.percent,
|
||||||
() => {
|
() => {
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
option = {
|
option = {
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
type: "gauge",
|
type: 'gauge',
|
||||||
startAngle: 90,
|
startAngle: 90,
|
||||||
endAngle: -270,
|
endAngle: -270,
|
||||||
pointer: {
|
pointer: {
|
||||||
show: false,
|
show: false,
|
||||||
},
|
|
||||||
progress: {
|
|
||||||
show: true,
|
|
||||||
overlap: false,
|
|
||||||
roundCap: true,
|
|
||||||
clip: false,
|
|
||||||
itemStyle: {
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "#464646",
|
|
||||||
},
|
},
|
||||||
},
|
progress: {
|
||||||
axisLine: {
|
show: true,
|
||||||
lineStyle: {
|
overlap: false,
|
||||||
width: 20,
|
roundCap: true,
|
||||||
},
|
clip: false,
|
||||||
},
|
itemStyle: {
|
||||||
splitLine: {
|
borderWidth: 1,
|
||||||
show: false,
|
borderColor: '#464646',
|
||||||
distance: 0,
|
|
||||||
length: 10,
|
|
||||||
},
|
|
||||||
axisTick: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
axisLabel: {
|
|
||||||
show: false,
|
|
||||||
distance: 50,
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
value: props.percent,
|
|
||||||
name: "完成度",
|
|
||||||
title: {
|
|
||||||
offsetCenter: ["0%", "-10%"],
|
|
||||||
},
|
|
||||||
detail: {
|
|
||||||
offsetCenter: ["0%", "20%"],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
axisLine: {
|
||||||
title: {
|
lineStyle: {
|
||||||
fontSize: 18,
|
width: 20,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false,
|
||||||
|
distance: 0,
|
||||||
|
length: 10,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false,
|
||||||
|
distance: 50,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
value: props.percent,
|
||||||
|
name: '完成度',
|
||||||
|
title: {
|
||||||
|
offsetCenter: ['0%', '-10%'],
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
offsetCenter: ['0%', '20%'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
fontSize: 16,
|
||||||
|
color: 'auto',
|
||||||
|
formatter: '{value}%',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
detail: {
|
],
|
||||||
fontSize: 16,
|
};
|
||||||
color: "auto",
|
let chartDom = document.getElementById('gauge-main');
|
||||||
formatter: "{value}%",
|
if (chartDom) {
|
||||||
},
|
let myChart = echarts.init(chartDom);
|
||||||
},
|
option && myChart.setOption(option);
|
||||||
],
|
}
|
||||||
};
|
|
||||||
let chartDom = document.getElementById("gauge-main");
|
|
||||||
if (chartDom) {
|
|
||||||
let myChart = echarts.init(chartDom);
|
|
||||||
option && myChart.setOption(option);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.echarts-box {
|
.echarts-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
.gauge-main {
|
.gauge-main {
|
||||||
width: 260px;
|
width: 260px;
|
||||||
height: 260px;
|
height: 260px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,210 +1,210 @@
|
|||||||
<template>
|
<template>
|
||||||
<default-home-card icon="FundTwoTone" title="代码提交量">
|
<default-home-card icon="BarChartOutlined" title="代码提交量">
|
||||||
<div class="echarts-box">
|
<div class="echarts-box">
|
||||||
<div class="gradient-main" id="gradient-main"></div>
|
<div class="gradient-main" id="gradient-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</default-home-card>
|
</default-home-card>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import DefaultHomeCard from "/@/views/system/home/components/default-home-card.vue";
|
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import {onMounted} from "vue";
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
function init(){
|
function init() {
|
||||||
let option = {
|
let option = {
|
||||||
color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],
|
color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
axisPointer: {
|
axisPointer: {
|
||||||
type: 'cross',
|
type: 'cross',
|
||||||
label: {
|
label: {
|
||||||
backgroundColor: '#6a7985'
|
backgroundColor: '#6a7985',
|
||||||
}
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
data: ['罗伊', '佩弦', '开云', '清野', '飞叶']
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
left: '3%',
|
|
||||||
right: '4%',
|
|
||||||
bottom: '3%',
|
|
||||||
containLabel: true
|
|
||||||
},
|
|
||||||
xAxis: [
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
boundaryGap: false,
|
|
||||||
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
yAxis: [
|
|
||||||
{
|
|
||||||
type: 'value'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: '罗伊',
|
|
||||||
type: 'line',
|
|
||||||
stack: 'Total',
|
|
||||||
smooth: true,
|
|
||||||
lineStyle: {
|
|
||||||
width: 0
|
|
||||||
},
|
},
|
||||||
showSymbol: false,
|
|
||||||
areaStyle: {
|
|
||||||
opacity: 0.8,
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{
|
|
||||||
offset: 0,
|
|
||||||
color: 'rgb(128, 255, 165)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: 'rgb(1, 191, 236)'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series'
|
|
||||||
},
|
|
||||||
data: [140, 232, 101, 264, 90, 340, 250]
|
|
||||||
},
|
},
|
||||||
{
|
legend: {
|
||||||
name: '佩弦',
|
data: ['罗伊', '佩弦', '开云', '清野', '飞叶'],
|
||||||
type: 'line',
|
|
||||||
stack: 'Total',
|
|
||||||
smooth: true,
|
|
||||||
lineStyle: {
|
|
||||||
width: 0
|
|
||||||
},
|
|
||||||
showSymbol: false,
|
|
||||||
areaStyle: {
|
|
||||||
opacity: 0.8,
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{
|
|
||||||
offset: 0,
|
|
||||||
color: 'rgb(0, 221, 255)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: 'rgb(77, 119, 255)'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series'
|
|
||||||
},
|
|
||||||
data: [120, 282, 111, 234, 220, 340, 310]
|
|
||||||
},
|
},
|
||||||
{
|
grid: {
|
||||||
name: '开云',
|
left: '3%',
|
||||||
type: 'line',
|
right: '4%',
|
||||||
stack: 'Total',
|
bottom: '3%',
|
||||||
smooth: true,
|
containLabel: true,
|
||||||
lineStyle: {
|
|
||||||
width: 0
|
|
||||||
},
|
|
||||||
showSymbol: false,
|
|
||||||
areaStyle: {
|
|
||||||
opacity: 0.8,
|
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
||||||
{
|
|
||||||
offset: 0,
|
|
||||||
color: 'rgb(55, 162, 255)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: 'rgb(116, 21, 219)'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series'
|
|
||||||
},
|
|
||||||
data: [320, 132, 201, 334, 190, 130, 220]
|
|
||||||
},
|
},
|
||||||
{
|
xAxis: [
|
||||||
name: '清野',
|
{
|
||||||
type: 'line',
|
type: 'category',
|
||||||
stack: 'Total',
|
boundaryGap: false,
|
||||||
smooth: true,
|
data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||||
lineStyle: {
|
|
||||||
width: 0
|
|
||||||
},
|
},
|
||||||
showSymbol: false,
|
],
|
||||||
areaStyle: {
|
yAxis: [
|
||||||
opacity: 0.8,
|
{
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
type: 'value',
|
||||||
{
|
|
||||||
offset: 0,
|
|
||||||
color: 'rgb(255, 0, 135)'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: 1,
|
|
||||||
color: 'rgb(135, 0, 157)'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
},
|
},
|
||||||
emphasis: {
|
],
|
||||||
focus: 'series'
|
series: [
|
||||||
|
{
|
||||||
|
name: '罗伊',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
showSymbol: false,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.8,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgb(128, 255, 165)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgb(1, 191, 236)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [140, 232, 101, 264, 90, 340, 250],
|
||||||
},
|
},
|
||||||
data: [220, 402, 231, 134, 190, 230, 120]
|
{
|
||||||
},
|
name: '佩弦',
|
||||||
{
|
type: 'line',
|
||||||
name: '飞叶',
|
stack: 'Total',
|
||||||
type: 'line',
|
smooth: true,
|
||||||
stack: 'Total',
|
lineStyle: {
|
||||||
smooth: true,
|
width: 0,
|
||||||
lineStyle: {
|
},
|
||||||
width: 0
|
showSymbol: false,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.8,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgb(0, 221, 255)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgb(77, 119, 255)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [120, 282, 111, 234, 220, 340, 310],
|
||||||
},
|
},
|
||||||
showSymbol: false,
|
{
|
||||||
label: {
|
name: '开云',
|
||||||
show: true,
|
type: 'line',
|
||||||
position: 'top'
|
stack: 'Total',
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
showSymbol: false,
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.8,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgb(55, 162, 255)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgb(116, 21, 219)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [320, 132, 201, 334, 190, 130, 220],
|
||||||
},
|
},
|
||||||
areaStyle: {
|
{
|
||||||
opacity: 0.8,
|
name: '清野',
|
||||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
type: 'line',
|
||||||
{
|
stack: 'Total',
|
||||||
offset: 0,
|
smooth: true,
|
||||||
color: 'rgb(255, 191, 0)'
|
lineStyle: {
|
||||||
},
|
width: 0,
|
||||||
{
|
},
|
||||||
offset: 1,
|
showSymbol: false,
|
||||||
color: 'rgb(224, 62, 76)'
|
areaStyle: {
|
||||||
}
|
opacity: 0.8,
|
||||||
])
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgb(255, 0, 135)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgb(135, 0, 157)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [220, 402, 231, 134, 190, 230, 120],
|
||||||
},
|
},
|
||||||
emphasis: {
|
{
|
||||||
focus: 'series'
|
name: '飞叶',
|
||||||
|
type: 'line',
|
||||||
|
stack: 'Total',
|
||||||
|
smooth: true,
|
||||||
|
lineStyle: {
|
||||||
|
width: 0,
|
||||||
|
},
|
||||||
|
showSymbol: false,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
position: 'top',
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.8,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgb(255, 191, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgb(224, 62, 76)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
data: [220, 302, 181, 234, 210, 290, 150],
|
||||||
},
|
},
|
||||||
data: [220, 302, 181, 234, 210, 290, 150]
|
],
|
||||||
}
|
};
|
||||||
]
|
let chartDom = document.getElementById('gradient-main');
|
||||||
};
|
if (chartDom) {
|
||||||
let chartDom = document.getElementById("gradient-main");
|
let myChart = echarts.init(chartDom);
|
||||||
if (chartDom) {
|
option && myChart.setOption(option);
|
||||||
let myChart = echarts.init(chartDom);
|
}
|
||||||
option && myChart.setOption(option);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang='less' scoped>
|
<style lang="less" scoped>
|
||||||
.echarts-box {
|
.echarts-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
.gradient-main {
|
.gradient-main {
|
||||||
width: 1200px;
|
width: 1200px;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,78 +1,78 @@
|
|||||||
<template>
|
<template>
|
||||||
<default-home-card icon="PieChartTwoTone" title="加班统计">
|
<default-home-card icon="PieChartOutlined" title="加班统计">
|
||||||
<div class="echarts-box">
|
<div class="echarts-box">
|
||||||
<div class="pie-main" id="pie-main"></div>
|
<div class="pie-main" id="pie-main"></div>
|
||||||
</div>
|
</div>
|
||||||
</default-home-card>
|
</default-home-card>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import DefaultHomeCard from "/@/views/system/home/components/default-home-card.vue";
|
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import {onMounted} from "vue";
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
init();
|
init();
|
||||||
});
|
});
|
||||||
|
|
||||||
function init(){
|
function init() {
|
||||||
let option = {
|
let option = {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'item'
|
trigger: 'item',
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
top: '5%',
|
top: '5%',
|
||||||
left: 'center'
|
left: 'center',
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
name: '加班次数',
|
name: '加班次数',
|
||||||
type: 'pie',
|
type: 'pie',
|
||||||
radius: ['40%', '70%'],
|
radius: ['40%', '70%'],
|
||||||
avoidLabelOverlap: false,
|
avoidLabelOverlap: false,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
borderColor: '#fff',
|
borderColor: '#fff',
|
||||||
borderWidth: 2
|
borderWidth: 2,
|
||||||
},
|
},
|
||||||
label: {
|
|
||||||
show: false,
|
|
||||||
position: 'center'
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
label: {
|
label: {
|
||||||
show: true,
|
show: false,
|
||||||
fontSize: '40',
|
position: 'center',
|
||||||
fontWeight: 'bold'
|
},
|
||||||
}
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: '40',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [
|
||||||
|
{ value: 10, name: '初晓' },
|
||||||
|
{ value: 8, name: '善逸' },
|
||||||
|
{ value: 3, name: '胡克' },
|
||||||
|
{ value: 1, name: '罗伊' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
labelLine: {
|
],
|
||||||
show: false
|
};
|
||||||
},
|
let chartDom = document.getElementById('pie-main');
|
||||||
data: [
|
if (chartDom) {
|
||||||
{ value: 10, name: '初晓' },
|
let myChart = echarts.init(chartDom);
|
||||||
{ value: 8, name: '善逸' },
|
option && myChart.setOption(option);
|
||||||
{ value: 3, name: '胡克' },
|
}
|
||||||
{ value: 1, name: '罗伊' },
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
let chartDom = document.getElementById("pie-main");
|
|
||||||
if (chartDom) {
|
|
||||||
let myChart = echarts.init(chartDom);
|
|
||||||
option && myChart.setOption(option);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang='less' scoped>
|
<style lang="less" scoped>
|
||||||
.echarts-box {
|
.echarts-box {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
.pie-main {
|
.pie-main {
|
||||||
width: 260px;
|
width: 260px;
|
||||||
height: 260px;
|
height: 260px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<default-home-card icon="SmileTwoTone" title="联系我们">
|
<default-home-card icon="SmileOutlined" title="联系我们">
|
||||||
<div class="app-qr-box">
|
<div class="app-qr-box">
|
||||||
<div class="app-qr">
|
<div class="app-qr">
|
||||||
<img :src="zhuoda" />
|
<img :src="zhuoda" />
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<default-home-card
|
<default-home-card :extra="`${editFlag ? '完成' : '编辑'}`" icon="ThunderboltTwoTone" title="快捷入口" @extraClick="editFlag = !editFlag">
|
||||||
:extra="`${editFlag ? '完成' : '编辑'}`"
|
|
||||||
icon="ThunderboltTwoTone"
|
|
||||||
title="快捷入口"
|
|
||||||
@extraClick="editFlag = !editFlag"
|
|
||||||
>
|
|
||||||
<div class="quick-entry-list">
|
<div class="quick-entry-list">
|
||||||
<a-row>
|
<a-row>
|
||||||
<a-col v-for="(item,index) in quickEntry" :key="index" span="4">
|
<a-col v-for="(item, index) in quickEntry" :key="index" span="4">
|
||||||
<div class="quick-entry" @click="turnToPage(item.path)">
|
<div class="quick-entry" @click="turnToPage(item.path)">
|
||||||
<div class="icon">
|
<div class="icon">
|
||||||
<component :is='$antIcons[item.icon]' :style="{ fontSize:'30px'}"/>
|
<component :is="$antIcons[item.icon]" :style="{ fontSize: '30px' }" />
|
||||||
<close-circle-outlined v-if="editFlag" class="delete-icon" @click="deleteQuickEntry(index)"/>
|
<close-circle-outlined v-if="editFlag" class="delete-icon" @click="deleteQuickEntry(index)" />
|
||||||
</div>
|
</div>
|
||||||
<span class="entry-title">{{ item.title }}</span>
|
<span class="entry-title">{{ item.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -19,131 +14,135 @@
|
|||||||
<a-col v-if="editFlag && quickEntry.length < maxCount" span="4">
|
<a-col v-if="editFlag && quickEntry.length < maxCount" span="4">
|
||||||
<div class="add-quick-entry" @click="addHomeQuickEntry">
|
<div class="add-quick-entry" @click="addHomeQuickEntry">
|
||||||
<div class="add-icon">
|
<div class="add-icon">
|
||||||
<plus-outlined :style="{ fontSize:'30px'}"/>
|
<plus-outlined :style="{ fontSize: '30px' }" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</div>
|
</div>
|
||||||
</default-home-card>
|
</default-home-card>
|
||||||
<HomeQuickEntryModal ref="homeQuickEntryModal" @addQuickEntry="addQuickEntry"/>
|
<HomeQuickEntryModal ref="homeQuickEntryModal" @addQuickEntry="addQuickEntry" />
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import {onMounted, ref} from "vue";
|
import { onMounted, ref } from 'vue';
|
||||||
import {router} from "/@/router";
|
import { router } from '/@/router';
|
||||||
import HomeQuickEntryModal from './home-quick-entry-modal.vue'
|
import HomeQuickEntryModal from './home-quick-entry-modal.vue';
|
||||||
import localKey from '/@/constants/local-storage-key-const';
|
import localKey from '/@/constants/local-storage-key-const';
|
||||||
import {localRead, localSave} from '/@/utils/local-util';
|
import { localRead, localSave } from '/@/utils/local-util';
|
||||||
import _ from "lodash";
|
import _ from 'lodash';
|
||||||
import InitQuickEntryList from './init-quick-entry-list';
|
import InitQuickEntryList from './init-quick-entry-list';
|
||||||
import DefaultHomeCard from "/@/views/system/home/components/default-home-card.vue";
|
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
||||||
|
import { theme } from 'ant-design-vue';
|
||||||
|
|
||||||
//---------------- 初始化展示 --------------------
|
//---------------- 初始化展示 --------------------
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
initQuickEntry();
|
initQuickEntry();
|
||||||
})
|
});
|
||||||
let quickEntry = ref([])
|
let quickEntry = ref([]);
|
||||||
|
|
||||||
function initQuickEntry() {
|
function initQuickEntry() {
|
||||||
let quickEntryJson = localRead(localKey.HOME_QUICK_ENTRY);
|
let quickEntryJson = localRead(localKey.HOME_QUICK_ENTRY);
|
||||||
if (!quickEntryJson) {
|
if (!quickEntryJson) {
|
||||||
quickEntry.value = _.cloneDeep(InitQuickEntryList);
|
quickEntry.value = _.cloneDeep(InitQuickEntryList);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
let quickEntryList = JSON.parse(quickEntryJson);
|
||||||
|
if (_.isEmpty(quickEntryList)) {
|
||||||
|
quickEntry.value = _.cloneDeep(InitQuickEntryList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
quickEntry.value = quickEntryList;
|
||||||
}
|
}
|
||||||
let quickEntryList = JSON.parse(quickEntryJson);
|
|
||||||
if (_.isEmpty(quickEntryList)) {
|
// 页面跳转
|
||||||
quickEntry.value = _.cloneDeep(InitQuickEntryList);
|
function turnToPage(path) {
|
||||||
return;
|
if (editFlag.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
router.push({ path });
|
||||||
}
|
}
|
||||||
quickEntry.value = quickEntryList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 页面跳转
|
//---------------- 编辑快捷入口 --------------------
|
||||||
function turnToPage(path) {
|
let editFlag = ref(false);
|
||||||
if (editFlag.value) {
|
let maxCount = ref(6);
|
||||||
return;
|
|
||||||
|
// 快捷入口删除
|
||||||
|
function deleteQuickEntry(index) {
|
||||||
|
quickEntry.value.splice(index, 1);
|
||||||
|
localSave(localKey.HOME_QUICK_ENTRY, JSON.stringify(quickEntry.value));
|
||||||
}
|
}
|
||||||
router.push({path});
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------- 编辑快捷入口 --------------------
|
// 添加快捷入口
|
||||||
let editFlag = ref(false);
|
let homeQuickEntryModal = ref();
|
||||||
let maxCount = ref(6);
|
|
||||||
|
|
||||||
// 快捷入口删除
|
function addHomeQuickEntry() {
|
||||||
function deleteQuickEntry(index) {
|
homeQuickEntryModal.value.showModal();
|
||||||
quickEntry.value.splice(index, 1)
|
}
|
||||||
localSave(localKey.HOME_QUICK_ENTRY, JSON.stringify(quickEntry.value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加快捷入口
|
function addQuickEntry(row) {
|
||||||
let homeQuickEntryModal = ref();
|
quickEntry.value.push(row);
|
||||||
|
localSave(localKey.HOME_QUICK_ENTRY, JSON.stringify(quickEntry.value));
|
||||||
|
}
|
||||||
|
|
||||||
function addHomeQuickEntry() {
|
const { useToken } = theme;
|
||||||
homeQuickEntryModal.value.showModal();
|
const { token } = useToken();
|
||||||
}
|
|
||||||
|
|
||||||
function addQuickEntry(row) {
|
|
||||||
quickEntry.value.push(row);
|
|
||||||
localSave(localKey.HOME_QUICK_ENTRY, JSON.stringify(quickEntry.value));
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
<style lang='less' scoped>
|
<style lang="less" scoped>
|
||||||
.quick-entry-list {
|
.quick-entry-list {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.quick-entry {
|
.quick-entry {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
.entry-title {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #F0FFFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
.delete-icon {
|
|
||||||
position: absolute;
|
|
||||||
color: #F08080;
|
|
||||||
top: -5px;
|
|
||||||
right: -5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-quick-entry {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
.add-icon {
|
|
||||||
width: 70px;
|
|
||||||
height: 70px;
|
|
||||||
background-color: #fafafa;
|
|
||||||
border: 1px dashed #d9d9d9;
|
|
||||||
border-radius: 2px;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: border-color .3s;
|
border-radius: 4px;
|
||||||
|
|
||||||
|
.entry-title {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f0ffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-icon {
|
||||||
|
position: absolute;
|
||||||
|
color: #f08080;
|
||||||
|
top: -5px;
|
||||||
|
right: -5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-quick-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #A9A9A9;
|
|
||||||
|
|
||||||
&:hover {
|
.add-icon {
|
||||||
border-color: @primary-color;
|
width: 70px;
|
||||||
color: @primary-color;
|
height: 70px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
border: 1px dashed #d9d9d9;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.3s;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #a9a9a9;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-color: v-bind('token.colorPrimary');
|
||||||
|
color: v-bind('token.colorPrimary');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
*
|
*
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<default-home-card icon="StarTwoTone" title="已办待办">
|
<default-home-card icon="Star" title="已办待办">
|
||||||
<div style="height: 280px;">
|
<div style="height: 280px">
|
||||||
<div class="center column">
|
<div class="center column">
|
||||||
<a-space direction="vertical" style="width: 100%">
|
<a-space direction="vertical" style="width: 100%">
|
||||||
<div v-for="(item, index) in toDoList" :key="index" :class="['to-do', { done: item.doneFlag }]">
|
<div v-for="(item, index) in toDoList" :key="index" :class="['to-do', { done: item.doneFlag }]">
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<default-home-card extra="更多" icon="SoundTwoTone" title="通知公告" @extraClick="onMore">
|
<default-home-card extra="更多" icon="SoundOutlined" title="通知公告" @extraClick="onMore">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
<a-empty v-if="$lodash.isEmpty(data)" />
|
<a-empty v-if="$lodash.isEmpty(data)" />
|
||||||
@@ -37,10 +37,6 @@
|
|||||||
import { noticeApi } from '/@/api/business/oa/notice-api';
|
import { noticeApi } from '/@/api/business/oa/notice-api';
|
||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
import DefaultHomeCard from '/@/views/system/home/components/default-home-card.vue';
|
||||||
import { theme } from 'ant-design-vue';
|
|
||||||
const { useToken } = theme;
|
|
||||||
const { token } = useToken();
|
|
||||||
const colorPrimary = token.value.colorPrimary;
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
noticeTypeId: {
|
noticeTypeId: {
|
||||||
@@ -110,7 +106,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
color: v-bind(colorPrimary);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
.login-container {
|
.login-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: url(/@/assets/images/login/login-bg.jpg) no-repeat center;
|
background: url(/@/assets/images/login/login-bg.png) no-repeat center;
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
border-radius: 0px 12px 12px 0px;
|
border-radius: 0px 12px 12px 0px;
|
||||||
padding: 34px 42px;
|
padding: 34px 42px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border: solid 1px #efefef;
|
||||||
}
|
}
|
||||||
.login-qr {
|
.login-qr {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@@ -160,7 +161,7 @@
|
|||||||
.btn {
|
.btn {
|
||||||
width: 350px;
|
width: 350px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background: #1890ff;
|
background: #1748FD;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
@@ -193,14 +194,14 @@
|
|||||||
margin: 0 19px;
|
margin: 0 19px;
|
||||||
}
|
}
|
||||||
.login-type {
|
.login-type {
|
||||||
padding: 0 50px;
|
padding: 0 5px;
|
||||||
margin-top: 25px;
|
margin-top: 25px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
> img {
|
> img {
|
||||||
width: 22px;
|
width: 30px;
|
||||||
height: 22px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,38 +86,37 @@
|
|||||||
<p class="line"></p>
|
<p class="line"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-type">
|
<div class="login-type">
|
||||||
<img :src="aliLogin" />
|
<img src="/@/assets/images/login/wechat-icon.png" />
|
||||||
<img :src="qqLogin" />
|
<img src="/@/assets/images/login/ali-icon.png" />
|
||||||
<img :src="googleLogin" />
|
<img src="/@/assets/images/login/douyin-icon.png" />
|
||||||
<img :src="weiboLogin" />
|
<img src="/@/assets/images/login/qq-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/weibo-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/feishu-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/google-icon.png" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { message } from 'ant-design-vue';
|
import { message, notification, Button } from 'ant-design-vue';
|
||||||
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { loginApi } from '/@/api/system/login-api';
|
import { loginApi } from '/@/api/system/login-api';
|
||||||
import { SmartLoading } from '/@/components/framework/smart-loading';
|
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||||
import { LOGIN_DEVICE_ENUM } from '/@/constants/system/login-device-const';
|
import { LOGIN_DEVICE_ENUM } from '/@/constants/system/login-device-const';
|
||||||
import { useUserStore } from '/@/store/modules/system/user';
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
import { saveTokenToCookie } from '/@/utils/cookie-util';
|
|
||||||
|
|
||||||
import gongzhonghao from '/@/assets/images/1024lab/1024lab-gzh.jpg';
|
import gongzhonghao from '/@/assets/images/1024lab/1024lab-gzh.jpg';
|
||||||
import zhuoda from '/@/assets/images/1024lab/zhuoda-wechat.jpg';
|
import zhuoda from '/@/assets/images/1024lab/zhuoda-wechat.jpg';
|
||||||
import loginQR from '/@/assets/images/login/login-qr.png';
|
import loginQR from '/@/assets/images/login/login-qr.png';
|
||||||
import gzh from '/@/assets/images/1024lab/gzh.jpg';
|
import gzh from '/@/assets/images/1024lab/gzh.jpg';
|
||||||
|
|
||||||
import aliLogin from '/@/assets/images/login/ali-icon.png';
|
|
||||||
import googleLogin from '/@/assets/images/login/google-icon.png';
|
|
||||||
import qqLogin from '/@/assets/images/login/qq-icon.png';
|
|
||||||
import weiboLogin from '/@/assets/images/login/weibo-icon.png';
|
|
||||||
|
|
||||||
import { buildRoutes } from '/@/router/index';
|
import { buildRoutes } from '/@/router/index';
|
||||||
import { smartSentry } from '/@/lib/smart-sentry';
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
import { encryptData } from '/@/lib/encrypt';
|
import { encryptData } from '/@/lib/encrypt';
|
||||||
|
import { h } from 'vue';
|
||||||
|
import { localSave } from '/@/utils/local-util.js';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
//--------------------- 登录表单 ---------------------------------
|
//--------------------- 登录表单 ---------------------------------
|
||||||
|
|
||||||
@@ -145,6 +144,25 @@
|
|||||||
onLogin();
|
onLogin();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
notification['success']({
|
||||||
|
message: '温馨提示',
|
||||||
|
description: 'SmartAdmin 提供 9种 登录背景风格哦!',
|
||||||
|
duration: null,
|
||||||
|
onClick: () => {},
|
||||||
|
btn: () =>
|
||||||
|
h(
|
||||||
|
Button,
|
||||||
|
{
|
||||||
|
type: 'primary',
|
||||||
|
target: '_blank',
|
||||||
|
size: 'small',
|
||||||
|
href: 'https://smartadmin.vip/views/v3/front/Login.html',
|
||||||
|
onClick: () => {},
|
||||||
|
},
|
||||||
|
{ default: () => '去看看' }
|
||||||
|
),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
@@ -162,7 +180,7 @@
|
|||||||
});
|
});
|
||||||
const res = await loginApi.login(encryptPasswordForm);
|
const res = await loginApi.login(encryptPasswordForm);
|
||||||
stopRefrestCaptchaInterval();
|
stopRefrestCaptchaInterval();
|
||||||
saveTokenToCookie(res.data.token ? res.data.token : '');
|
localSave(LocalStorageKeyConst.USER_TOKEN, res.data.token ? res.data.token : '');
|
||||||
message.success('登录成功');
|
message.success('登录成功');
|
||||||
//更新用户信息到pinia
|
//更新用户信息到pinia
|
||||||
useUserStore().setUserLoginInfo(res.data);
|
useUserStore().setUserLoginInfo(res.data);
|
||||||
|
|||||||
@@ -0,0 +1,196 @@
|
|||||||
|
.login-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url(/@/assets/images/login/login-bg.png) no-repeat center;
|
||||||
|
background-size: cover;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.box-item {
|
||||||
|
width: 444px;
|
||||||
|
height: 570px;
|
||||||
|
&.desc {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 12px 0px 0px 12px;
|
||||||
|
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
|
||||||
|
padding: 23px 25px;
|
||||||
|
}
|
||||||
|
&.login {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 0px 12px 12px 0px;
|
||||||
|
padding: 34px 42px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.login-qr {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 66px;
|
||||||
|
height: 66px;
|
||||||
|
}
|
||||||
|
.welcome {
|
||||||
|
background: url(/@/assets/images/login/left-bg1.png) no-repeat center;
|
||||||
|
background-size: cover;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 6px 20px 0px rgba(33,47,117,0.10);
|
||||||
|
padding-top: 35px;
|
||||||
|
p{
|
||||||
|
color: #333333;
|
||||||
|
line-height: 25px;
|
||||||
|
letter-spacing: 0.26px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
.sub-welcome{
|
||||||
|
color: #333333;
|
||||||
|
line-height: 25px;
|
||||||
|
letter-spacing: 0.26px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.app-qr-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 20px;
|
||||||
|
.app-qr {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 50%;
|
||||||
|
> img {
|
||||||
|
width: 112px;
|
||||||
|
height: 112px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-desc {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 11px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
> img {
|
||||||
|
width: 15px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-desc-marquee {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 11px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
@keyframes marquee {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-30%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.marquee {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
animation: marquee 5s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-title {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #1e1e1e;
|
||||||
|
margin-bottom: 35px;
|
||||||
|
}
|
||||||
|
.login-form {
|
||||||
|
.captcha-input {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-img {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-input,
|
||||||
|
.ant-input-affix-wrapper {
|
||||||
|
height: 44px;
|
||||||
|
border: 1px solid #ededed;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eye-box {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 10px;
|
||||||
|
.eye-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
width: 350px;
|
||||||
|
height: 50px;
|
||||||
|
background: #1748FD;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.more {
|
||||||
|
margin-top: 30px;
|
||||||
|
.title-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
> p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
width: 114px;
|
||||||
|
height: 1px;
|
||||||
|
background: #e6e6e6;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #a1aebe;
|
||||||
|
margin: 0 19px;
|
||||||
|
}
|
||||||
|
.login-type {
|
||||||
|
padding: 0 5px;
|
||||||
|
margin-top: 25px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
> img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
<!--
|
||||||
|
* 登录
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-12 22:34:00
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="box-item desc">
|
||||||
|
<div class="welcome">
|
||||||
|
<p>欢迎登录 SmartAdmin V3</p>
|
||||||
|
<p class="sub-welcome">高质量代码的快速开发平台</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="box-item login">
|
||||||
|
<img class="login-qr" :src="loginQR" />
|
||||||
|
<div class="login-title">账号登录</div>
|
||||||
|
<a-form ref="formRef" class="login-form" :model="loginForm" :rules="rules">
|
||||||
|
<a-form-item name="loginName">
|
||||||
|
<a-input v-model:value.trim="loginForm.loginName" placeholder="请输入用户名" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="password">
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="loginForm.password"
|
||||||
|
autocomplete="on"
|
||||||
|
:type="showPassword ? 'text' : 'password'"
|
||||||
|
placeholder="请输入密码:至少三种字符,最小 8 位"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="captchaCode">
|
||||||
|
<a-input class="captcha-input" v-model:value.trim="loginForm.captchaCode" placeholder="请输入验证码" />
|
||||||
|
<img class="captcha-img" :src="captchaBase64Image" @click="getCaptcha" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-checkbox v-model:checked="rememberPwd">记住密码</a-checkbox>
|
||||||
|
<span> ( 账号:admin, 密码:123456)</span>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<div class="btn" @click="onLogin">登录</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<div class="more">
|
||||||
|
<div class="title-box">
|
||||||
|
<p class="line"></p>
|
||||||
|
<p class="title">其他方式登录</p>
|
||||||
|
<p class="line"></p>
|
||||||
|
</div>
|
||||||
|
<div class="login-type">
|
||||||
|
<img src="/@/assets/images/login/wechat-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/ali-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/douyin-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/qq-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/weibo-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/feishu-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/google-icon.png" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { loginApi } from '/@/api/system/login-api';
|
||||||
|
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||||
|
import { LOGIN_DEVICE_ENUM } from '/@/constants/system/login-device-const';
|
||||||
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
|
import loginQR from '/@/assets/images/login/login-qr.png';
|
||||||
|
|
||||||
|
import { buildRoutes } from '/@/router/index';
|
||||||
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
|
import { encryptData } from '/@/lib/encrypt';
|
||||||
|
import { localSave } from '/@/utils/local-util.js';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
|
//--------------------- 登录表单 ---------------------------------
|
||||||
|
|
||||||
|
const loginForm = reactive({
|
||||||
|
loginName: 'admin',
|
||||||
|
password: '',
|
||||||
|
captchaCode: '',
|
||||||
|
captchaUuid: '',
|
||||||
|
loginDevice: LOGIN_DEVICE_ENUM.PC.value,
|
||||||
|
});
|
||||||
|
const rules = {
|
||||||
|
loginName: [{ required: true, message: '用户名不能为空' }],
|
||||||
|
password: [{ required: true, message: '密码不能为空' }],
|
||||||
|
captchaCode: [{ required: true, message: '验证码不能为空' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPassword = ref(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const formRef = ref();
|
||||||
|
const rememberPwd = ref(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.onkeyup = (e) => {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
onLogin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.onkeyup = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
//登录
|
||||||
|
async function onLogin() {
|
||||||
|
formRef.value.validate().then(async () => {
|
||||||
|
try {
|
||||||
|
SmartLoading.show();
|
||||||
|
// 密码加密
|
||||||
|
let encryptPasswordForm = Object.assign({}, loginForm, {
|
||||||
|
password: encryptData(loginForm.password),
|
||||||
|
});
|
||||||
|
const res = await loginApi.login(encryptPasswordForm);
|
||||||
|
stopRefrestCaptchaInterval();
|
||||||
|
localSave(LocalStorageKeyConst.USER_TOKEN, res.data.token ? res.data.token : '');
|
||||||
|
message.success('登录成功');
|
||||||
|
//更新用户信息到pinia
|
||||||
|
useUserStore().setUserLoginInfo(res.data);
|
||||||
|
//构建系统的路由
|
||||||
|
buildRoutes();
|
||||||
|
router.push('/home');
|
||||||
|
} catch (e) {
|
||||||
|
if (e.data && e.data.code !== 0) {
|
||||||
|
loginForm.captchaCode = '';
|
||||||
|
getCaptcha();
|
||||||
|
}
|
||||||
|
smartSentry.captureError(e);
|
||||||
|
} finally {
|
||||||
|
SmartLoading.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------- 验证码 ---------------------------------
|
||||||
|
|
||||||
|
const captchaBase64Image = ref('');
|
||||||
|
async function getCaptcha() {
|
||||||
|
try {
|
||||||
|
let captchaResult = await loginApi.getCaptcha();
|
||||||
|
captchaBase64Image.value = captchaResult.data.captchaBase64Image;
|
||||||
|
loginForm.captchaUuid = captchaResult.data.captchaUuid;
|
||||||
|
beginRefrestCaptchaInterval(captchaResult.data.expireSeconds);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let refrestCaptchaInterval = null;
|
||||||
|
function beginRefrestCaptchaInterval(expireSeconds) {
|
||||||
|
if (refrestCaptchaInterval === null) {
|
||||||
|
refrestCaptchaInterval = setInterval(getCaptcha, (expireSeconds - 5) * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopRefrestCaptchaInterval() {
|
||||||
|
if (refrestCaptchaInterval != null) {
|
||||||
|
clearInterval(refrestCaptchaInterval);
|
||||||
|
refrestCaptchaInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(getCaptcha);
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './login.less';
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
.login-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: url(/@/assets/images/login/login-bg.png) no-repeat center;
|
||||||
|
background-size: cover;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.box-item {
|
||||||
|
width: 444px;
|
||||||
|
height: 570px;
|
||||||
|
&.desc {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 12px 0px 0px 12px;
|
||||||
|
box-shadow: 0px 16px 73px 8px rgba(203, 203, 203, 0.2);
|
||||||
|
padding: 23px 25px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
&.login {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 0px 12px 12px 0px;
|
||||||
|
padding: 34px 42px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.login-qr {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 66px;
|
||||||
|
height: 66px;
|
||||||
|
}
|
||||||
|
.welcome {
|
||||||
|
padding-top: 35px;
|
||||||
|
p{
|
||||||
|
color: #333333;
|
||||||
|
line-height: 25px;
|
||||||
|
letter-spacing: 0.26px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
.sub-welcome{
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 25px;
|
||||||
|
letter-spacing: 0.26px;
|
||||||
|
opacity: 0.96;
|
||||||
|
background: #1748fd;
|
||||||
|
border-radius: 22px 22px 2px 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.welcome-img{
|
||||||
|
width: 350px;
|
||||||
|
height: 350px;
|
||||||
|
}
|
||||||
|
.app-qr-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
margin-top: 20px;
|
||||||
|
.app-qr {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 50%;
|
||||||
|
> img {
|
||||||
|
width: 112px;
|
||||||
|
height: 112px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-desc {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 11px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
> img {
|
||||||
|
width: 15px;
|
||||||
|
height: 18px;
|
||||||
|
margin-right: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-desc-marquee {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 11px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
|
||||||
|
@keyframes marquee {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-30%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.marquee {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
animation: marquee 5s linear infinite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.login-title {
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
color: #1e1e1e;
|
||||||
|
margin-bottom: 35px;
|
||||||
|
}
|
||||||
|
.login-form {
|
||||||
|
.captcha-input {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captcha-img {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ant-input,
|
||||||
|
.ant-input-affix-wrapper {
|
||||||
|
height: 44px;
|
||||||
|
border: 1px solid #ededed;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eye-box {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 10px;
|
||||||
|
.eye-icon {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
width: 350px;
|
||||||
|
height: 50px;
|
||||||
|
background: #1748FD;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-align: center;
|
||||||
|
color: #ffffff;
|
||||||
|
line-height: 50px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.more {
|
||||||
|
margin-top: 30px;
|
||||||
|
.title-box {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
> p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
width: 114px;
|
||||||
|
height: 1px;
|
||||||
|
background: #e6e6e6;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #a1aebe;
|
||||||
|
margin: 0 19px;
|
||||||
|
}
|
||||||
|
.login-type {
|
||||||
|
padding: 0 5px;
|
||||||
|
margin-top: 25px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
> img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
<!--
|
||||||
|
* 登录
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-12 22:34:00
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div class="login-container">
|
||||||
|
<div class="box-item desc">
|
||||||
|
<div class="welcome">
|
||||||
|
<p>欢迎登录 SmartAdmin V3</p>
|
||||||
|
<p class="sub-welcome">高质量代码的快速开发平台</p>
|
||||||
|
</div>
|
||||||
|
<img class="welcome-img" src="/@/assets/images/login/left-bg2.png" />
|
||||||
|
</div>
|
||||||
|
<div class="box-item login">
|
||||||
|
<img class="login-qr" :src="loginQR" />
|
||||||
|
<div class="login-title">账号登录</div>
|
||||||
|
<a-form ref="formRef" class="login-form" :model="loginForm" :rules="rules">
|
||||||
|
<a-form-item name="loginName">
|
||||||
|
<a-input v-model:value.trim="loginForm.loginName" placeholder="请输入用户名" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="password">
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="loginForm.password"
|
||||||
|
autocomplete="on"
|
||||||
|
:type="showPassword ? 'text' : 'password'"
|
||||||
|
placeholder="请输入密码:至少三种字符,最小 8 位"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item name="captchaCode">
|
||||||
|
<a-input class="captcha-input" v-model:value.trim="loginForm.captchaCode" placeholder="请输入验证码" />
|
||||||
|
<img class="captcha-img" :src="captchaBase64Image" @click="getCaptcha" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<a-checkbox v-model:checked="rememberPwd">记住密码</a-checkbox>
|
||||||
|
<span> ( 账号:admin, 密码:123456)</span>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<div class="btn" @click="onLogin">登录</div>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
<div class="more">
|
||||||
|
<div class="title-box">
|
||||||
|
<p class="line"></p>
|
||||||
|
<p class="title">其他方式登录</p>
|
||||||
|
<p class="line"></p>
|
||||||
|
</div>
|
||||||
|
<div class="login-type">
|
||||||
|
<img src="/@/assets/images/login/wechat-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/ali-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/douyin-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/qq-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/weibo-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/feishu-icon.png" />
|
||||||
|
<img src="/@/assets/images/login/google-icon.png" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import { onMounted, onUnmounted, reactive, ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { loginApi } from '/@/api/system/login-api';
|
||||||
|
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||||
|
import { LOGIN_DEVICE_ENUM } from '/@/constants/system/login-device-const';
|
||||||
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
|
import loginQR from '/@/assets/images/login/login-qr.png';
|
||||||
|
|
||||||
|
import { buildRoutes } from '/@/router/index';
|
||||||
|
import { smartSentry } from '/@/lib/smart-sentry';
|
||||||
|
import { encryptData } from '/@/lib/encrypt';
|
||||||
|
import { localSave } from '/@/utils/local-util.js';
|
||||||
|
import LocalStorageKeyConst from '/@/constants/local-storage-key-const.js';
|
||||||
|
|
||||||
|
//--------------------- 登录表单 ---------------------------------
|
||||||
|
|
||||||
|
const loginForm = reactive({
|
||||||
|
loginName: 'admin',
|
||||||
|
password: '',
|
||||||
|
captchaCode: '',
|
||||||
|
captchaUuid: '',
|
||||||
|
loginDevice: LOGIN_DEVICE_ENUM.PC.value,
|
||||||
|
});
|
||||||
|
const rules = {
|
||||||
|
loginName: [{ required: true, message: '用户名不能为空' }],
|
||||||
|
password: [{ required: true, message: '密码不能为空' }],
|
||||||
|
captchaCode: [{ required: true, message: '验证码不能为空' }],
|
||||||
|
};
|
||||||
|
|
||||||
|
const showPassword = ref(false);
|
||||||
|
const router = useRouter();
|
||||||
|
const formRef = ref();
|
||||||
|
const rememberPwd = ref(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
document.onkeyup = (e) => {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
onLogin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.onkeyup = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
//登录
|
||||||
|
async function onLogin() {
|
||||||
|
formRef.value.validate().then(async () => {
|
||||||
|
try {
|
||||||
|
SmartLoading.show();
|
||||||
|
// 密码加密
|
||||||
|
let encryptPasswordForm = Object.assign({}, loginForm, {
|
||||||
|
password: encryptData(loginForm.password),
|
||||||
|
});
|
||||||
|
const res = await loginApi.login(encryptPasswordForm);
|
||||||
|
stopRefrestCaptchaInterval();
|
||||||
|
localSave(LocalStorageKeyConst.USER_TOKEN, res.data.token ? res.data.token : '');
|
||||||
|
message.success('登录成功');
|
||||||
|
//更新用户信息到pinia
|
||||||
|
useUserStore().setUserLoginInfo(res.data);
|
||||||
|
//构建系统的路由
|
||||||
|
buildRoutes();
|
||||||
|
router.push('/home');
|
||||||
|
} catch (e) {
|
||||||
|
if (e.data && e.data.code !== 0) {
|
||||||
|
loginForm.captchaCode = '';
|
||||||
|
getCaptcha();
|
||||||
|
}
|
||||||
|
smartSentry.captureError(e);
|
||||||
|
} finally {
|
||||||
|
SmartLoading.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------- 验证码 ---------------------------------
|
||||||
|
|
||||||
|
const captchaBase64Image = ref('');
|
||||||
|
async function getCaptcha() {
|
||||||
|
try {
|
||||||
|
let captchaResult = await loginApi.getCaptcha();
|
||||||
|
captchaBase64Image.value = captchaResult.data.captchaBase64Image;
|
||||||
|
loginForm.captchaUuid = captchaResult.data.captchaUuid;
|
||||||
|
beginRefrestCaptchaInterval(captchaResult.data.expireSeconds);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let refrestCaptchaInterval = null;
|
||||||
|
function beginRefrestCaptchaInterval(expireSeconds) {
|
||||||
|
if (refrestCaptchaInterval === null) {
|
||||||
|
refrestCaptchaInterval = setInterval(getCaptcha, (expireSeconds - 5) * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopRefrestCaptchaInterval() {
|
||||||
|
if (refrestCaptchaInterval != null) {
|
||||||
|
clearInterval(refrestCaptchaInterval);
|
||||||
|
refrestCaptchaInterval = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(getCaptcha);
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './login.less';
|
||||||
|
</style>
|
||||||
3
smart-app/.env.development
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=development
|
||||||
|
VITE_APP_TITLE='SmartAdmin 开发环境(Dev)'
|
||||||
|
VITE_APP_API_URL='http://127.0.0.1:1024'
|
||||||
3
smart-app/.env.localhost
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=development
|
||||||
|
VITE_APP_TITLE='SmartH5 本地环境(Local)'
|
||||||
|
VITE_APP_API_URL='http://127.0.0.1:1024'
|
||||||
3
smart-app/.env.pre
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=production
|
||||||
|
VITE_APP_TITLE='SmartH5 预发布环境(Pre)'
|
||||||
|
VITE_APP_API_URL='https://app.smartadmin.vip/smart-app-api'
|
||||||
3
smart-app/.env.production
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=production
|
||||||
|
VITE_APP_TITLE='Smart App V3.X'
|
||||||
|
VITE_APP_API_URL='https://app.smartadmin.vip/smart-app-api'
|
||||||
3
smart-app/.env.test
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
NODE_ENV=production
|
||||||
|
VITE_APP_TITLE='SmartH5 测试环境(Test)'
|
||||||
|
VITE_APP_API_URL='http://127.0.0.1:1024'
|
||||||
18
smart-app/.eslintignore
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
*.sh
|
||||||
|
node_modules
|
||||||
|
lib
|
||||||
|
*.md
|
||||||
|
*.woff
|
||||||
|
*.ttf
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
dist
|
||||||
|
public
|
||||||
|
/docs
|
||||||
|
.husky
|
||||||
|
.local
|
||||||
|
.localhost
|
||||||
|
/bin
|
||||||
|
Dockerfile
|
||||||
|
src/assets
|
||||||
66
smart-app/.eslintrc.cjs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: zhuoda
|
||||||
|
* @Date: 2021-11-05
|
||||||
|
* @LastEditTime: 2022-07-05
|
||||||
|
* @LastEditors: zhuoda
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
root: true, //此项是用来告诉eslint找当前配置文件不能往父级查找
|
||||||
|
env: {
|
||||||
|
browser: true,
|
||||||
|
es2021: true,
|
||||||
|
node: true,
|
||||||
|
},
|
||||||
|
parser: 'vue-eslint-parser', //使用vue-eslint-parser 来解析vue文件中的 template和script
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 12, // 默认情况下,ESLint使用的是ECMAScript5语法,此处我们设置的选项是 es12
|
||||||
|
sourceType: 'module', // 指定js导入的方式
|
||||||
|
},
|
||||||
|
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', 'plugin:vue/base'],
|
||||||
|
globals: {
|
||||||
|
defineProps: 'readonly',
|
||||||
|
defineEmits: 'readonly',
|
||||||
|
defineExpose: 'readonly',
|
||||||
|
withDefaults: 'readonly',
|
||||||
|
},
|
||||||
|
plugins: ['vue'],
|
||||||
|
rules: {
|
||||||
|
'no-unused-vars': [
|
||||||
|
'error',
|
||||||
|
// we are only using this rule to check for unused arguments since TS
|
||||||
|
// catches unused variables but not args.
|
||||||
|
{ varsIgnorePattern: '.*', args: 'none' },
|
||||||
|
],
|
||||||
|
'space-before-function-paren': 'off',
|
||||||
|
|
||||||
|
'vue/attributes-order': 'off',
|
||||||
|
'vue/one-component-per-file': 'off',
|
||||||
|
'vue/html-closing-bracket-newline': 'off',
|
||||||
|
'vue/max-attributes-per-line': 'off',
|
||||||
|
'vue/multiline-html-element-content-newline': 'off',
|
||||||
|
'vue/singleline-html-element-content-newline': 'off',
|
||||||
|
'vue/attribute-hyphenation': 'off',
|
||||||
|
'vue/require-default-prop': 'off',
|
||||||
|
'vue/multi-word-component-names': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
ignores: ['index'], //需要忽略的组件名
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'vue/html-self-closing': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
html: {
|
||||||
|
void: 'always',
|
||||||
|
normal: 'never',
|
||||||
|
component: 'always',
|
||||||
|
},
|
||||||
|
svg: 'always',
|
||||||
|
math: 'always',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Enable vue/script-setup-uses-vars rule
|
||||||
|
'vue/script-setup-uses-vars': 'error',
|
||||||
|
},
|
||||||
|
};
|
||||||
21
smart-app/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
30
smart-app/.prettierrc.cjs
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 代码格式化配置
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-12 14:44:18
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
module.exports = {
|
||||||
|
printWidth: 150, // 每行代码长度(默认80)
|
||||||
|
tabWidth: 2, // 缩进空格数
|
||||||
|
useTabs: false, //不用tab缩进
|
||||||
|
semi: true, //// 在语句末尾打印分号
|
||||||
|
singleQuote: true, // 使用单引号而不是双引号
|
||||||
|
vueIndentScriptAndStyle: true, //Vue文件脚本和样式标签缩进
|
||||||
|
quoteProps: 'as-needed', // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
|
||||||
|
jsxSingleQuote: true, // 在JSX中使用单引号而不是双引号
|
||||||
|
trailingComma: 'es5', //多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
|
||||||
|
bracketSpacing: true, // 在对象文字中的括号之间打印空格
|
||||||
|
jsxBracketSameLine: false, //jsx 标签的反尖括号需要换行
|
||||||
|
arrowParens: 'always', // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
|
||||||
|
rangeStart: 0, // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
|
||||||
|
rangeEnd: Infinity,
|
||||||
|
requirePragma: false, // 指定要使用的解析器,不需要写文件开头的 @prettier
|
||||||
|
insertPragma: false, // 不需要自动在文件开头插入 @prettier
|
||||||
|
proseWrap: 'preserve', // 使用默认的折行标准 always\never\preserve
|
||||||
|
htmlWhitespaceSensitivity: 'css', // 指定HTML文件的全局空格敏感度 css\strict\ignore
|
||||||
|
endOfLine: 'auto', // 因为prettier的规范和eslint的换行规则不同,所以这个必须配置。要不然每次打开文件都会有一堆的警告;换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr
|
||||||
|
};
|
||||||
20
smart-app/index.html
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<script>
|
||||||
|
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||||
|
CSS.supports('top: constant(a)'))
|
||||||
|
document.write(
|
||||||
|
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||||
|
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||||
|
</script>
|
||||||
|
<title></title>
|
||||||
|
<!--preload-links-->
|
||||||
|
<!--app-context-->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"><!--app-html--></div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
89
smart-app/package.json
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
"name": "smart-app",
|
||||||
|
"version": "3.0.0",
|
||||||
|
"author": {
|
||||||
|
"name": "1024创新实验室(1024lab)",
|
||||||
|
"email": "lab1024@163.com",
|
||||||
|
"url": "https://www.1024lab.net"
|
||||||
|
},
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://smartadmin.1024lab.net",
|
||||||
|
"scripts": {
|
||||||
|
"dev:app": "uni -p app",
|
||||||
|
"dev:app-android": "uni -p app-android",
|
||||||
|
"dev:app-ios": "uni -p app-ios",
|
||||||
|
"dev:custom": "uni -p",
|
||||||
|
"dev:h5": "uni",
|
||||||
|
"dev:h5:ssr": "uni --ssr",
|
||||||
|
"dev:mp-alipay": "uni -p mp-alipay",
|
||||||
|
"dev:mp-baidu": "uni -p mp-baidu",
|
||||||
|
"dev:mp-jd": "uni -p mp-jd",
|
||||||
|
"dev:mp-kuaishou": "uni -p mp-kuaishou",
|
||||||
|
"dev:mp-lark": "uni -p mp-lark",
|
||||||
|
"dev:mp-qq": "uni -p mp-qq",
|
||||||
|
"dev:mp-toutiao": "uni -p mp-toutiao",
|
||||||
|
"dev:mp-weixin": "uni -p mp-weixin",
|
||||||
|
"dev:mp-xhs": "uni -p mp-xhs",
|
||||||
|
"dev:quickapp-webview": "uni -p quickapp-webview",
|
||||||
|
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
|
||||||
|
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
|
||||||
|
"build:app": "uni build -p app",
|
||||||
|
"build:app-android": "uni build -p app-android",
|
||||||
|
"build:app-ios": "uni build -p app-ios",
|
||||||
|
"build:custom": "uni build -p",
|
||||||
|
"build:h5": "uni build",
|
||||||
|
"build:h5:ssr": "uni build --ssr",
|
||||||
|
"build:mp-alipay": "uni build -p mp-alipay",
|
||||||
|
"build:mp-baidu": "uni build -p mp-baidu",
|
||||||
|
"build:mp-jd": "uni build -p mp-jd",
|
||||||
|
"build:mp-kuaishou": "uni build -p mp-kuaishou",
|
||||||
|
"build:mp-lark": "uni build -p mp-lark",
|
||||||
|
"build:mp-qq": "uni build -p mp-qq",
|
||||||
|
"build:mp-toutiao": "uni build -p mp-toutiao",
|
||||||
|
"build:mp-weixin": "uni build -p mp-weixin",
|
||||||
|
"build:mp-xhs": "uni build -p mp-xhs",
|
||||||
|
"build:quickapp-webview": "uni build -p quickapp-webview",
|
||||||
|
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
|
||||||
|
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@dcloudio/uni-app": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-app-plus": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-components": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-h5": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-alipay": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-baidu": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-jd": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-kuaishou": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-lark": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-qq": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-toutiao": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-weixin": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-mp-xhs": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-quickapp-webview": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-ui": "1.5.0",
|
||||||
|
"crypto-js": "4.1.1",
|
||||||
|
"dayjs": "1.11.10",
|
||||||
|
"lodash": "4.17.21",
|
||||||
|
"pinia": "2.0.36",
|
||||||
|
"sm-crypto": "0.3.13",
|
||||||
|
"vue": "3.2.47",
|
||||||
|
"vue-i18n": "9.1.9"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@dcloudio/types": "3.3.2",
|
||||||
|
"@dcloudio/uni-automator": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-cli-shared": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/uni-stacktracey": "3.0.0-3090920231225001",
|
||||||
|
"@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001",
|
||||||
|
"@vue/runtime-core": "3.2.45",
|
||||||
|
"eslint": "8.16.0",
|
||||||
|
"eslint-config-prettier": "9.0.0",
|
||||||
|
"eslint-plugin-prettier": "5.0.0",
|
||||||
|
"eslint-plugin-vue": "9.17.0",
|
||||||
|
"prettier": "3.0.2",
|
||||||
|
"sass": "1.69.7",
|
||||||
|
"sass-loader": "10.1.1",
|
||||||
|
"vite": "4.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
10
smart-app/shims-uni.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
/// <reference types='@dcloudio/types' />
|
||||||
|
import 'vue'
|
||||||
|
|
||||||
|
declare module '@vue/runtime-core' {
|
||||||
|
type Hooks = App.AppInstance & Page.PageInstance;
|
||||||
|
|
||||||
|
interface ComponentCustomOptions extends Hooks {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
22
smart-app/src/App.vue
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<script>
|
||||||
|
import { useUserStore } from '/@/store/modules/system/user';
|
||||||
|
export default {
|
||||||
|
onLaunch: function () {
|
||||||
|
useUserStore().getLoginInfo();
|
||||||
|
},
|
||||||
|
onShow: function () {
|
||||||
|
console.log('App Show');
|
||||||
|
},
|
||||||
|
onHide: function () {
|
||||||
|
console.log('App Hide');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import '@/uni_modules/uni-scss/index.scss';
|
||||||
|
/* 设置基准字体大小为16px */
|
||||||
|
body {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
31
smart-app/src/api/business/goods/goods-api.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* @Description:
|
||||||
|
* @Author: zhuoda
|
||||||
|
* @Date: 2021-11-05
|
||||||
|
* @LastEditTime: 2022-06-23
|
||||||
|
* @LastEditors: zhuoda
|
||||||
|
*/
|
||||||
|
import { postRequest, getRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const goodsApi = {
|
||||||
|
// 添加商品 @author zhuoda
|
||||||
|
addGoods: (param) => {
|
||||||
|
return postRequest('/goods/add', param);
|
||||||
|
},
|
||||||
|
// 删除 @author zhuoda
|
||||||
|
deleteGoods: (goodsId) => {
|
||||||
|
return getRequest(`/goods/delete/${goodsId}`);
|
||||||
|
},
|
||||||
|
// 批量 @author zhuoda
|
||||||
|
batchDelete: (goodsIdList) => {
|
||||||
|
return postRequest('/goods/batchDelete', goodsIdList);
|
||||||
|
},
|
||||||
|
// 分页查询 @author zhuoda
|
||||||
|
queryGoodsList: (param) => {
|
||||||
|
return postRequest('/goods/query', param);
|
||||||
|
},
|
||||||
|
// 更新商品 @author zhuoda
|
||||||
|
updateGoods: (param) => {
|
||||||
|
return postRequest('/goods/update', param);
|
||||||
|
},
|
||||||
|
};
|
||||||
37
smart-app/src/api/business/oa/enterprise-api.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 企业信息
|
||||||
|
*
|
||||||
|
* @Author: 开云
|
||||||
|
* @Date: 2023-09-03 21:47:28
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
import { postRequest, getRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const enterpriseApi = {
|
||||||
|
// 新建企业 @author 开云
|
||||||
|
create: (param) => {
|
||||||
|
return postRequest('/oa/enterprise/create', param);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询企业详情 @author 开云
|
||||||
|
detail: (enterpriseId) => {
|
||||||
|
return getRequest(`/oa/enterprise/get/${enterpriseId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 分页查询企业模块 @author 开云
|
||||||
|
pageQuery: (param) => {
|
||||||
|
return postRequest('/oa/enterprise/page/query', param);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 编辑企业 @author 开云
|
||||||
|
update: (param) => {
|
||||||
|
return postRequest('/oa/enterprise/update', param);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除企业 @author 开云
|
||||||
|
delete: (enterpriseId) => {
|
||||||
|
return getRequest(`/oa/enterprise/delete/${enterpriseId}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
33
smart-app/src/api/business/oa/notice-api.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* @Description: 公告信息、企业动态
|
||||||
|
* @version:
|
||||||
|
* @Author: zhuoda
|
||||||
|
* @Date: 2022-08-16 20:34:36
|
||||||
|
*/
|
||||||
|
import { postRequest, getRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const noticeApi = {
|
||||||
|
// ---------------- 通知公告类型 -----------------------
|
||||||
|
|
||||||
|
// 通知公告类型-获取全部 @author zhuoda
|
||||||
|
getAllNoticeTypeList() {
|
||||||
|
return getRequest('/oa/noticeType/getAll');
|
||||||
|
},
|
||||||
|
|
||||||
|
// --------------------- 【员工】查看 通知公告 -------------------------
|
||||||
|
|
||||||
|
// 通知公告-员工-查看详情 @author zhuoda
|
||||||
|
view(noticeId) {
|
||||||
|
return getRequest(`/oa/notice/employee/view/${noticeId}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 通知公告-员工-查询 @author zhuoda
|
||||||
|
queryEmployeeNotice(param) {
|
||||||
|
return postRequest('/oa/notice/employee/query', param);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 【员工】通知公告-查询 查看记录 @author zhuoda
|
||||||
|
queryViewRecord(param) {
|
||||||
|
return postRequest('/oa/notice/employee/queryViewRecord', param);
|
||||||
|
},
|
||||||
|
};
|
||||||
24
smart-app/src/api/support/change-log-api.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* 系统更新日志 api 封装
|
||||||
|
*
|
||||||
|
* @Author: 卓大
|
||||||
|
* @Date: 2022-09-26 14:53:50
|
||||||
|
* @Copyright 1024创新实验室
|
||||||
|
*/
|
||||||
|
import { postRequest, getRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const changeLogApi = {
|
||||||
|
/**
|
||||||
|
* 分页查询 @author 卓大
|
||||||
|
*/
|
||||||
|
queryPage: (param) => {
|
||||||
|
return postRequest('/support/changeLog/queryPage', param);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 详情 @author 卓大
|
||||||
|
*/
|
||||||
|
getDetail: (changeLogId) => {
|
||||||
|
return getRequest(`/support/changeLog/getDetail/${changeLogId}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
59
smart-app/src/api/support/dict-api.js
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 字典
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-03 21:55:25
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
import { postRequest, getRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const dictApi = {
|
||||||
|
// 分页查询数据字典KEY - @author 卓大
|
||||||
|
keyQuery: (param) => {
|
||||||
|
return postRequest('/support/dict/key/query', param);
|
||||||
|
},
|
||||||
|
// 查询全部字典key - @author 卓大
|
||||||
|
queryAllKey: () => {
|
||||||
|
return getRequest('/support/dict/key/queryAll');
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 分页查询数据字典value - @author 卓大
|
||||||
|
*/
|
||||||
|
valueQuery: (param) => {
|
||||||
|
return postRequest('/support/dict/value/query', param);
|
||||||
|
},
|
||||||
|
// 数据字典KEY-添加- @author 卓大
|
||||||
|
keyAdd: (param) => {
|
||||||
|
return postRequest('/support/dict/key/add', param);
|
||||||
|
},
|
||||||
|
// 分页查询数据字典value - @author 卓大
|
||||||
|
valueAdd: (param) => {
|
||||||
|
return postRequest('/support/dict/value/add', param);
|
||||||
|
},
|
||||||
|
// 数据字典key-更新- @author 卓大
|
||||||
|
keyEdit: (param) => {
|
||||||
|
return postRequest('/support/dict/key/edit', param);
|
||||||
|
},
|
||||||
|
// 数据字典Value-更新- @author 卓大
|
||||||
|
valueEdit: (param) => {
|
||||||
|
return postRequest('/support/dict/value/edit', param);
|
||||||
|
},
|
||||||
|
// 数据字典key-删除- @author 卓大
|
||||||
|
keyDelete: (keyIdList) => {
|
||||||
|
return postRequest('/support/dict/key/delete', keyIdList);
|
||||||
|
},
|
||||||
|
// 数据字典Value-删除- @author 卓大
|
||||||
|
valueDelete: (valueIdList) => {
|
||||||
|
return postRequest('/support/dict/value/delete', valueIdList);
|
||||||
|
},
|
||||||
|
// 缓存刷新- @author 卓大
|
||||||
|
cacheRefresh: () => {
|
||||||
|
return getRequest('/support/dict/cache/refresh');
|
||||||
|
},
|
||||||
|
// 数据字典-值列表- @author 卓大
|
||||||
|
valueList: (keyCode) => {
|
||||||
|
return getRequest(`/support/dict/value/list/${keyCode}`);
|
||||||
|
},
|
||||||
|
};
|
||||||
17
smart-app/src/api/support/feedback-api.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* 意见反馈
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室:开云
|
||||||
|
* @Date: 2022-09-03 21:56:31
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
import { postRequest } from '/src/lib/smart-request';
|
||||||
|
|
||||||
|
export const feedbackApi = {
|
||||||
|
// 意见反馈-新增
|
||||||
|
addFeedback: (params) => {
|
||||||
|
return postRequest('/support/feedback/add', params);
|
||||||
|
},
|
||||||
|
};
|
||||||
14
smart-app/src/api/support/file-api.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 系统更新日志 api 封装
|
||||||
|
*
|
||||||
|
* @Author: 卓大
|
||||||
|
* @Date: 2022-09-26 14:53:50
|
||||||
|
* @Copyright 1024创新实验室
|
||||||
|
*/
|
||||||
|
import { uploadRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const fileApi = {
|
||||||
|
upload: (file, folder) => {
|
||||||
|
return uploadRequest(file, folder);
|
||||||
|
},
|
||||||
|
};
|
||||||
40
smart-app/src/api/system/login-api.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 登录
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-03 21:59:58
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
import { getRequest, postRequest } from '/@/lib/smart-request';
|
||||||
|
|
||||||
|
export const loginApi = {
|
||||||
|
/**
|
||||||
|
* 登录 @author 卓大
|
||||||
|
*/
|
||||||
|
login: (param) => {
|
||||||
|
return postRequest('/login', param);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退出登录 @author 卓大
|
||||||
|
*/
|
||||||
|
logout: () => {
|
||||||
|
return getRequest('/login/logout');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取验证码 @author 卓大
|
||||||
|
*/
|
||||||
|
getCaptcha: () => {
|
||||||
|
return getRequest('/login/getCaptcha');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录信息 @author 卓大
|
||||||
|
*/
|
||||||
|
getLoginInfo: () => {
|
||||||
|
return getRequest('/login/getLoginInfo');
|
||||||
|
},
|
||||||
|
};
|
||||||
55
smart-app/src/components/dict-select/index.vue
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
<!---
|
||||||
|
* 字段 下拉选择框
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室:罗伊
|
||||||
|
* @Date: 2022-09-12 22:06:45
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<uni-data-select :localdata="dictValueList" v-model="selectValue" :placeholder="props.placeholder" :clear="true" @change="onChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, watch } from 'vue';
|
||||||
|
import { dictApi } from '/@/api/support/dict-api';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
keyCode: String,
|
||||||
|
modelValue: [String],
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------- 查询 字典数据 --------------------------
|
||||||
|
|
||||||
|
const dictValueList = ref([]);
|
||||||
|
async function queryDict() {
|
||||||
|
let res = await dictApi.valueList(props.keyCode);
|
||||||
|
dictValueList.value = res.data.map((e) => Object.assign({}, { text: e.valueName, value: e.valueCode }));
|
||||||
|
}
|
||||||
|
onMounted(queryDict);
|
||||||
|
|
||||||
|
// -------------------------- 选中 相关、事件 --------------------------
|
||||||
|
|
||||||
|
const selectValue = ref(props.modelValue);
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value) => {
|
||||||
|
selectValue.value = value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
function onChange(selectValue) {
|
||||||
|
let find = dictValueList.value.filter((e) => e.value === selectValue)[0];
|
||||||
|
emit('update:modelValue', find.value);
|
||||||
|
emit('change', find.value, find.text);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
70
smart-app/src/components/smart-card/index.vue
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<template>
|
||||||
|
<view class="card">
|
||||||
|
<view class="card-header">
|
||||||
|
<slot name="title">
|
||||||
|
<view class="card-title">
|
||||||
|
{{ title }}
|
||||||
|
</view>
|
||||||
|
</slot>
|
||||||
|
<slot v-if="isExtra" name="extra" @click="extra">
|
||||||
|
<view class="card-extra">
|
||||||
|
查看更多>
|
||||||
|
</view>
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
<view class="card-content">
|
||||||
|
<slot name="content"></slot>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
//标题
|
||||||
|
title:{
|
||||||
|
type:String,
|
||||||
|
default:''
|
||||||
|
},
|
||||||
|
//是否展示Extra
|
||||||
|
isExtra:{
|
||||||
|
type:Boolean,
|
||||||
|
default:true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const emits = defineEmits(['extra'])
|
||||||
|
const extra = ()=>{
|
||||||
|
emits('extra')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.card{
|
||||||
|
width: 700rpx;
|
||||||
|
margin: 0 auto 20rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
.card-header{
|
||||||
|
height: 76rpx;
|
||||||
|
background: linear-gradient(180deg,#e8f4ff, #f8fcff);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
align-items: center;
|
||||||
|
.card-title{
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-family: PingFang SC, PingFang SC-Semibold;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: left;
|
||||||
|
color: #323333;
|
||||||
|
}
|
||||||
|
.card-extra{
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-family: PingFang SC, PingFang SC-Regular;
|
||||||
|
font-weight: 400;
|
||||||
|
text-align: center;
|
||||||
|
color: #1a9aff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
73
smart-app/src/components/smart-detail-tabs/index.vue
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<template>
|
||||||
|
<scroll-view class="scroll-view" scroll-x="true" :show-scrollbar="false">
|
||||||
|
<view class="item" :class="active === item.value ? 'active' : ''" v-for="item in tabsList" :key="item.value" @click="change(item.value)">
|
||||||
|
{{ item.label }}
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
type: String,
|
||||||
|
default: 'fixed',
|
||||||
|
},
|
||||||
|
tabsList: {
|
||||||
|
type: Array,
|
||||||
|
default: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const position = ref(props.position);
|
||||||
|
const active = ref(props.modelValue);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newValue) => {
|
||||||
|
active.value = newValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
|
||||||
|
const change = (value) => {
|
||||||
|
active.value = value;
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
emit('change', value);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.scroll-view {
|
||||||
|
position: v-bind(position);
|
||||||
|
background-color: #f4f4f4;
|
||||||
|
z-index: 998;
|
||||||
|
|
||||||
|
:deep(::-webkit-scrollbar) {
|
||||||
|
height: 0 !important;
|
||||||
|
width: 0 !important;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
height: 72rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #777;
|
||||||
|
background: #fff;
|
||||||
|
display: inline-block;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
line-height: 72rpx;
|
||||||
|
margin: 24rpx 0 24rpx 24rpx;
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
color: #ffffff;
|
||||||
|
background: #1a9aff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
42
smart-app/src/components/smart-enum-radio/index.vue
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<!--
|
||||||
|
* 枚举 radio
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-08-08 20:32:30
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<radio-group @change="handleChange">
|
||||||
|
<label v-for="item in $smartEnumPlugin.getValueDescList(props.enumName)" :key="item.value" class="smart-margin-right10">
|
||||||
|
<radio :value="item.value + ''" :checked="item.value === modelValue">{{ item.desc }}</radio>
|
||||||
|
</label>
|
||||||
|
</radio-group>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
enumName: String,
|
||||||
|
modelValue: [Number, String],
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
|
||||||
|
const selectValue = ref(props.modelValue);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(newValue) => {
|
||||||
|
selectValue.value = newValue;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleChange(e) {
|
||||||
|
emit('update:modelValue', e.detail.value);
|
||||||
|
emit('change', e.detail.value);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
49
smart-app/src/components/smart-enum-select/index.vue
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<!---
|
||||||
|
* 字段 下拉选择框
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室:罗伊
|
||||||
|
* @Date: 2022-09-12 22:06:45
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*
|
||||||
|
-->
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<uni-data-select :localdata="dataList" v-model="selectValue" :clear="true" @change="onChange" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { inject, onMounted, ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const smartEnumPlugin = inject('smartEnumPlugin');
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
enumName: String,
|
||||||
|
modelValue: [Number, String],
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------- 枚举数据列表 --------------------------
|
||||||
|
const dataList = ref([]);
|
||||||
|
function getEnumData() {
|
||||||
|
dataList.value = smartEnumPlugin.getValueDescList(props.enumName).map((e) => Object.assign({}, { text: e.desc, value: e.value }));
|
||||||
|
}
|
||||||
|
onMounted(getEnumData);
|
||||||
|
|
||||||
|
// -------------------------- 选中 相关、事件 --------------------------
|
||||||
|
|
||||||
|
const selectValue = ref(props.modelValue);
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(value) => {
|
||||||
|
selectValue.value = value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue', 'change']);
|
||||||
|
function onChange(value) {
|
||||||
|
emit('update:modelValue', value);
|
||||||
|
emit('change', value, smartEnumPlugin.getDescByValue(props.enumName, value));
|
||||||
|
}
|
||||||
|
</script>
|
||||||
26
smart-app/src/constants/business/erp/goods-const.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 商品
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2022-09-03 22:08:10
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
export const GOODS_STATUS_ENUM = {
|
||||||
|
APPOINTMENT: {
|
||||||
|
value: 1,
|
||||||
|
desc: '预约中',
|
||||||
|
},
|
||||||
|
SELL: {
|
||||||
|
value: 2,
|
||||||
|
desc: '售卖中',
|
||||||
|
},
|
||||||
|
SELL_OUT: {
|
||||||
|
value: 3,
|
||||||
|
desc: '售罄',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default {
|
||||||
|
GOODS_STATUS_ENUM,
|
||||||
|
};
|
||||||
24
smart-app/src/constants/business/oa/enterprise-const.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 企业
|
||||||
|
*
|
||||||
|
* @Author: 1024创新实验室-主任:卓大
|
||||||
|
* @Date: 2023-09-03 22:07:27
|
||||||
|
* @Wechat: zhuda1024
|
||||||
|
* @Email: lab1024@163.com
|
||||||
|
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const ENTERPRISE_TYPE_ENUM = {
|
||||||
|
NORMAL: {
|
||||||
|
value: 1,
|
||||||
|
desc: '有限企业',
|
||||||
|
},
|
||||||
|
FOREIGN: {
|
||||||
|
value: 2,
|
||||||
|
desc: '外资企业',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
ENTERPRISE_TYPE_ENUM,
|
||||||
|
};
|
||||||