Prechádzať zdrojové kódy

vue3登录页面及扫码登录

lipenghui 5 mesiacov pred
rodič
commit
78843d29e2

+ 2 - 2
.env.dev

@@ -4,7 +4,7 @@ NODE_ENV=production
 VITE_DEV=true
 
 # 请求路径
-VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
+VITE_BASE_URL='http://127.0.0.1:48080'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
 VITE_UPLOAD_TYPE=server
@@ -34,4 +34,4 @@ VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
 VITE_APP_CAPTCHA_ENABLE=true
 
 # GoView域名
-VITE_GOVIEW_URL='http://127.0.0.1:3000'
+VITE_GOVIEW_URL='http://127.0.0.1:3000'

+ 1 - 266
README.md

@@ -1,266 +1 @@
-**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!**
-
-**「我喜欢写代码,乐此不疲」**  
-**「我喜欢做开源,以此为乐」**
-
-我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。
-
-如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。
-
-## 🐶 新手必读
-
-* nodejs > 16.18.0 && pnpm > 8.6.0 (强制使用pnpm)
-* 演示地址【Vue3 + element-plus】:<http://dashboard-vue3.yudao.iocoder.cn>
-* 演示地址【Vue3 + vben(ant-design-vue)】:<http://dashboard-vben.yudao.iocoder.cn>
-* 演示地址【Vue2 + element-ui】:<http://dashboard.yudao.iocoder.cn>
-* 启动文档:<https://doc.iocoder.cn/quick-start/>
-* 视频教程:<https://doc.iocoder.cn/video/>
-
-## 🐯 平台简介
-
-**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。
-
-* 采用 [vue-element-plus-admin](https://gitee.com/kailong110120130/vue-element-plus-admin) 实现
-* 改换 saas,自动引入等功能
-* 使用 Element Plus 免费开源的中后台模版,具备如下特性:
-
-![首页](public/home.png)
-
-* **最新技术栈**:使用 Vue3、Vite4 等前端前沿技术开发
-* **TypeScript**: 应用程序级 JavaScript 的语言
-* **主题**: 可配置的主题
-* **国际化**:内置完善的国际化方案
-* **权限**:内置完善的动态路由权限生成方案
-* **组件**:二次封装了多个常用的组件
-* **示例**:内置丰富的示例
-
-## 技术栈
-
-| 框架                                                                   | 说明               | 版本     |
-|----------------------------------------------------------------------|------------------|--------|
-| [Vue](https://staging-cn.vuejs.org/)                                 | Vue 框架           | 3.3.8 |
-| [Vite](https://cn.vitejs.dev//)                                      | 开发与构建工具          | 4.5.0  |
-| [Element Plus](https://element-plus.org/zh-CN/)                      | Element Plus     | 2.4.2 |
-| [TypeScript](https://www.typescriptlang.org/docs/)                   | JavaScript 的超集   | 5.2.2  |
-| [pinia](https://pinia.vuejs.org/)                                    | Vue 存储库 替代 vuex5 | 2.1.7 |
-| [vueuse](https://vueuse.org/)                                        | 常用工具集            | 10.6.1 |
-| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化              | 9.6.5  |
-| [vue-router](https://router.vuejs.org/)                              | Vue 路由           | 4.2.5  |
-| [unocss](https://uno.antfu.me/)                                      | 原子 css          | 0.57.4  |
-| [iconify](https://icon-sets.iconify.design/)                         | 在线图标库            | 3.1.1  |
-| [wangeditor](https://www.wangeditor.com/)                            | 富文本编辑器           | 5.1.23 |
-
-## 开发工具
-
-推荐 VS Code 开发,配合插件如下:
-
-| 插件名                           | 功能                  |
-|-------------------------------|---------------------|
-| Vue - Official                | Vue 与 TypeScript 支持 |
-| unocss                        | unocss for vscode   |
-| Iconify IntelliSense          | Iconify 预览和搜索       |
-| i18n Ally                     | 国际化智能提示             |
-| Stylelint                     | Css    格式化          |
-| Prettier                      | 代码格式化               |
-| ESLint                        | 脚本代码检查              |
-| DotENV                        | env 文件高亮            |
-
-## 🔥 后端架构
-
-支持 Spring Boot、Spring Cloud 两种架构:
-
-① Spring Boot 单体架构:<https://doc.iocoder.cn>
-
-![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
-
-② Spring Cloud 微服务架构:<https://cloud.iocoder.cn>
-
-![架构图](/.image/common/yudao-cloud-architecture.png)
-
-## 内置功能
-
-系统内置多种多种业务功能,可以用于快速你的业务系统:
-
-* 系统功能
-* 基础设施
-* 工作流程
-* 支付系统
-* 会员中心
-* 数据报表
-* 商城系统
-* 微信公众号
-* ERP 系统
-* CRM 系统
-
-### 系统功能
-
-|     | 功能    | 描述                              |
-|-----|-------|---------------------------------|
-|     | 用户管理  | 用户是系统操作者,该功能主要完成系统用户配置          |
-| ⭐️  | 在线用户  | 当前系统中活跃用户状态监控,支持手动踢下线           |
-|     | 角色管理  | 角色菜单权限分配、设置角色按机构进行数据范围权限划分      |
-|     | 菜单管理  | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能    |
-|     | 部门管理  | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限  |
-|     | 岗位管理  | 配置系统用户所属担任职务                    |
-| 🚀  | 租户管理  | 配置系统租户,支持 SaaS 场景下的多租户功能        |
-| 🚀  | 租户套餐  | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限       |
-|     | 字典管理  | 对系统中经常使用的一些较为固定的数据进行维护          |
-| 🚀  | 短信管理  | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 |
-| 🚀  | 邮件管理  | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台       |
-| 🚀  | 站内信   | 系统内的消息通知,提供站内信模版、站内信消息          |
-| 🚀  | 操作日志  | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 |
-| ⭐️  | 登录日志  | 系统登录日志记录查询,包含登录异常               |
-| 🚀  | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务     |
-|     | 通知公告  | 系统通知公告信息发布维护                    |
-| 🚀  | 敏感词   | 配置系统敏感词,支持标签分组                  |
-| 🚀  | 应用管理  | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 |
-| 🚀  | 地区管理  | 展示省份、城市、区镇等城市信息,支持 IP 对应城市      |
-
-![功能图](/.image/common/system-feature.png)
-
-### 工作流程
-
-|    | 功能    | 描述                                      |
-|----|-------|-----------------------------------------|
-| 🚀 | 流程模型  | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器           |
-| 🚀 | 流程表单  | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件  |
-| 🚀 | 用户分组  | 自定义用户分组,可用于工作流的审批分组                     |
-| 🚀 | 我的流程  | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线     |
-| 🚀 | 待办任务  | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
-| 🚀 | 已办任务  | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息         |
-| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批  |
-
-![功能图](/.image/common/bpm-feature.png)
-
-| BPMN 设计器                     | 钉钉/飞书设计器                       |
-|------------------------------|--------------------------------|
-| ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) |
-
-### 支付系统
-
-|     | 功能   | 描述                        |
-|-----|------|---------------------------|
-| 🚀  | 商户信息 | 管理商户信息,支持 Saas 场景下的多商户功能  |
-| 🚀  | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 |
-| 🚀  | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单     |
-| 🚀  | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单     |
-
-ps:核心功能已经实现,正在对接微信小程序中...
-
-### 基础设施
-
-|    | 功能       | 描述                                           |
-|----|----------|----------------------------------------------|
-| 🚀 | 代码生成     | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载       |
-| 🚀 | 系统接口     | 基于 Swagger 自动生成相关的 RESTful API 接口文档          |
-| 🚀 | 数据库文档    | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式      |
-|    | 表单构建     | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件         |
-| 🚀 | 配置管理     | 对系统动态配置常用参数,支持 SpringBoot 加载                 |
-| ⭐️ | 定时任务     | 在线(添加、修改、删除)任务调度包含执行结果日志                     |
-| 🚀 | 文件服务     | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等   |
-| 🚀 | API 日志   | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题   |
-|    | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈              |
-|    | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理           |
-| 🚀 | 消息队列     | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 |
-| 🚀 | Java 监控  | 基于 Spring Boot Admin 实现 Java 应用的监控           |
-| 🚀 | 链路追踪     | 接入 SkyWalking 组件,实现链路追踪                      |
-| 🚀 | 日志中心     | 接入 SkyWalking 组件,实现日志中心                      |
-| 🚀 | 服务保障     | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景              |
-| 🚀 | 日志服务     | 轻量级日志中心,查看远程服务器的日志                           |
-| 🚀 | 单元测试     | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等    |
-
-![功能图](/.image/common/infra-feature.png)
-
-### 数据报表
-
-|     | 功能    | 描述                 |
-|-----|-------|--------------------|
-| 🚀  | 报表设计器 | 支持数据报表、图形报表、打印设计等  |
-| 🚀  | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 |
-
-### 微信公众号
-
-|     | 功能     | 描述                            |
-|-----|--------|-------------------------------|
-| 🚀  | 账号管理   | 配置接入的微信公众号,可支持多个公众号           |
-| 🚀  | 数据统计   | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据  |
-| 🚀  | 粉丝管理   | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 |
-| 🚀  | 消息管理   | 查看粉丝发送的消息列表,可主动回复粉丝消息         |
-| 🚀  | 自动回复   | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 |
-| 🚀  | 标签管理   | 对公众号的标签进行创建、查询、修改、删除等操作       |
-| 🚀  | 菜单管理   | 自定义公众号的菜单,也可以从公众号同步菜单         |
-| 🚀  | 素材管理   | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 |
-| 🚀  | 图文草稿箱  | 新增常用的图文素材到草稿箱,可发布到公众号         |
-| 🚀  | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作           |
-
-### 商城系统
-
-演示地址:<https://doc.iocoder.cn/mall-preview/>
-
-![功能图](/.image/common/mall-feature.png)
-
-![功能图](/.image/common/mall-preview.png)
-
-### ERP 系统
-
-演示地址:<https://doc.iocoder.cn/erp-preview/>
-
-![功能图](/.image/common/erp-feature.png)
-
-### CRM 系统
-
-演示地址:<https://doc.iocoder.cn/crm-preview/>
-
-![功能图](/.image/common/crm-feature.png)
-
-## 🐷 演示图
-
-### 系统功能
-
-| 模块       | biu                         | biu                       | biu                      |
-|----------|-----------------------------|---------------------------|--------------------------|
-| 登录 & 首页  | ![登录](/.image/登录.jpg)       | ![首页](/.image/首页.jpg)     | ![个人中心](/.image/个人中心.jpg) |
-| 用户 & 应用  | ![用户管理](/.image/用户管理.jpg)   | ![令牌管理](/.image/令牌管理.jpg) | ![应用管理](/.image/应用管理.jpg) |
-| 租户 & 套餐  | ![租户管理](/.image/租户管理.jpg)   | ![租户套餐](/.image/租户套餐.png) | -                        |
-| 部门 & 岗位  | ![部门管理](/.image/部门管理.jpg)   | ![岗位管理](/.image/岗位管理.jpg) | -                        |
-| 菜单 & 角色  | ![菜单管理](/.image/菜单管理.jpg)   | ![角色管理](/.image/角色管理.jpg) | -                        |
-| 审计日志     | ![操作日志](/.image/操作日志.jpg)   | ![登录日志](/.image/登录日志.jpg) | -                        |
-| 短信       | ![短信渠道](/.image/短信渠道.jpg)   | ![短信模板](/.image/短信模板.jpg) | ![短信日志](/.image/短信日志.jpg) |
-| 字典 & 敏感词 | ![字典类型](/.image/字典类型.jpg)   | ![字典数据](/.image/字典数据.jpg) | ![敏感词](/.image/敏感词.jpg)  |
-| 错误码 & 通知 | ![错误码管理](/.image/错误码管理.jpg) | ![通知公告](/.image/通知公告.jpg) | -                        |
-
-### 工作流程
-
-| 模块      | biu                             | biu                             | biu                             |
-|---------|---------------------------------|---------------------------------|---------------------------------|
-| 流程模型    | ![流程模型-列表](/.image/流程模型-列表.jpg) | ![流程模型-设计](/.image/流程模型-设计.jpg) | ![流程模型-定义](/.image/流程模型-定义.jpg) |
-| 表单 & 分组 | ![流程表单](/.image/流程表单.jpg)       | ![用户分组](/.image/用户分组.jpg)       | -                               |
-| 我的流程    | ![我的流程-列表](/.image/我的流程-列表.jpg) | ![我的流程-发起](/.image/我的流程-发起.jpg) | ![我的流程-详情](/.image/我的流程-详情.jpg) |
-| 待办 & 已办 | ![任务列表-审批](/.image/任务列表-审批.jpg) | ![任务列表-待办](/.image/任务列表-待办.jpg) | ![任务列表-已办](/.image/任务列表-已办.jpg) |
-| OA 请假   | ![OA请假-列表](/.image/OA请假-列表.jpg) | ![OA请假-发起](/.image/OA请假-发起.jpg) | ![OA请假-详情](/.image/OA请假-详情.jpg) |
-
-### 基础设施
-
-| 模块            | biu                           | biu                         | biu                       |
-|---------------|-------------------------------|-----------------------------|---------------------------|
-| 代码生成          | ![代码生成](/.image/代码生成.jpg)     | ![生成效果](/.image/生成效果.jpg)   | -                         |
-| 文档            | ![系统接口](/.image/系统接口.jpg)     | ![数据库文档](/.image/数据库文档.jpg) | -                         |
-| 文件 & 配置       | ![文件配置](/.image/文件配置.jpg)     | ![文件管理](/.image/文件管理2.jpg)  | ![配置管理](/.image/配置管理.jpg) |
-| 定时任务          | ![定时任务](/.image/定时任务.jpg)     | ![任务日志](/.image/任务日志.jpg)   | -                         |
-| API 日志        | ![访问日志](/.image/访问日志.jpg)     | ![错误日志](/.image/错误日志.jpg)   | -                         |
-| MySQL & Redis | ![MySQL](/.image/MySQL.jpg)   | ![Redis](/.image/Redis.jpg) | -                         |
-| 监控平台          | ![Java监控](/.image/Java监控.jpg) | ![链路追踪](/.image/链路追踪.jpg)   | ![日志中心](/.image/日志中心.jpg) |
-
-### 支付系统
-
-| 模块      | biu                       | biu                             | biu                             |
-|---------|---------------------------|---------------------------------|---------------------------------|
-| 商家 & 应用 | ![商户信息](/.image/商户信息.jpg) | ![应用信息-列表](/.image/应用信息-列表.jpg) | ![应用信息-编辑](/.image/应用信息-编辑.jpg) |
-| 支付 & 退款 | ![支付订单](/.image/支付订单.jpg) | ![退款订单](/.image/退款订单.jpg)       | ---                             |
-
-### 数据报表
-
-| 模块    | biu                             | biu                             | biu                                   |
-|-------|---------------------------------|---------------------------------|---------------------------------------|
-| 报表设计器 | ![数据报表](/.image/报表设计器-数据报表.jpg) | ![图形报表](/.image/报表设计器-图形报表.jpg) | ![报表设计器-打印设计](/.image/报表设计器-打印设计.jpg) |
-| 大屏设计器 | ![大屏列表](/.image/大屏设计器-列表.jpg)   | ![大屏预览](/.image/大屏设计器-预览.jpg)   | ![大屏编辑](/.image/大屏设计器-编辑.jpg)         |
+DeepOil前端V3

+ 1 - 0
index.html

@@ -1,6 +1,7 @@
 <!DOCTYPE html>
 <html lang="en">
   <head>
+      <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
     <meta charset="UTF-8" />
     <link rel="icon" href="/favicon.ico" />
     <meta http-equiv="X-UA-Compatible" content="IE=edge" />

+ 0 - 1
src/api/login/index.ts

@@ -1,5 +1,4 @@
 import request from '@/config/axios'
-import { getRefreshToken } from '@/utils/auth'
 import type { RegisterVO, UserLoginVO } from './types'
 
 export interface SmsCodeVO {

BIN
src/assets/imgs/img.png


BIN
src/assets/imgs/login-box.png


BIN
src/assets/imgs/loginbg.png


BIN
src/assets/imgs/loginindex.jpg


BIN
src/assets/imgs/return.png


BIN
src/assets/imgs/saoma.png


+ 61 - 0
src/components/SvgIcon/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
+  <svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
+    <use :xlink:href="iconName" />
+  </svg>
+</template>
+
+<script>
+import { isExternal } from '@/utils/validate'
+
+export default {
+  name: 'SvgIcon',
+  props: {
+    iconClass: {
+      type: String,
+      required: true
+    },
+    className: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    isExternal() {
+      return isExternal(this.iconClass)
+    },
+    iconName() {
+      return `#icon-${this.iconClass}`
+    },
+    svgClass() {
+      if (this.className) {
+        return 'svg-icon ' + this.className
+      } else {
+        return 'svg-icon'
+      }
+    },
+    styleExternalIcon() {
+      return {
+        mask: `url(${this.iconClass}) no-repeat 50% 50%`,
+        '-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.svg-icon {
+  width: 1em;
+  height: 1em;
+  vertical-align: -0.15em;
+  fill: currentColor;
+  overflow: hidden;
+}
+
+.svg-external-icon {
+  background-color: currentColor;
+  mask-size: cover!important;
+  display: inline-block;
+}
+</style>

+ 13 - 2
src/permission.ts

@@ -1,13 +1,16 @@
 import router from './router'
 import type { RouteRecordRaw } from 'vue-router'
 import { isRelogin } from '@/config/axios/service'
-import { getAccessToken } from '@/utils/auth'
+import {getAccessToken} from '@/utils/auth'
 import { useTitle } from '@/hooks/web/useTitle'
 import { useNProgress } from '@/hooks/web/useNProgress'
 import { usePageLoading } from '@/hooks/web/usePageLoading'
 import { useDictStoreWithOut } from '@/store/modules/dict'
 import { useUserStoreWithOut } from '@/store/modules/user'
 import { usePermissionStoreWithOut } from '@/store/modules/permission'
+import * as LoginApi from "@/api/login";
+import * as authUtil from "@/utils/auth";
+
 
 const { start, done } = useNProgress()
 
@@ -92,7 +95,15 @@ router.beforeEach(async (to, from, next) => {
     }
   } else {
     if (whiteList.indexOf(to.path) !== -1) {
-      next()
+      const code = to.query.code;
+      if (code) {
+        const res = await LoginApi.socialLogin('20', typeof code === "string" ? code :"", '22')
+        authUtil.setToken(res)
+        next({ path: 'index' });
+      } else {
+        next(); // 正常导航
+      }
+      // next()
     } else {
       next(`/login?redirect=${to.fullPath}`) // 否则全部重定向到登录页
     }

+ 44 - 47
src/views/Login/Login.vue

@@ -3,30 +3,7 @@
     :class="prefixCls"
     class="relative h-[100%] lt-md:px-10px lt-sm:px-10px lt-xl:px-10px lt-xl:px-10px"
   >
-    <div class="relative mx-auto h-full flex">
-      <div
-        :class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px lt-xl:hidden overflow-x-hidden overflow-y-auto`"
-      >
-        <!-- 左上角的 logo + 系统标题 -->
-        <div class="relative flex items-center text-white">
-          <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
-          <span class="text-20px font-bold">{{ underlineToHump(appStore.getTitle) }}</span>
-        </div>
-        <!-- 左边的背景图 + 欢迎语 -->
-        <div class="h-[calc(100%-60px)] flex items-center justify-center">
-          <TransitionGroup
-            appear
-            enter-active-class="animate__animated animate__bounceInLeft"
-            tag="div"
-          >
-            <img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" />
-            <div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div>
-            <div key="3" class="mt-5 text-14px font-normal text-white">
-              {{ t('login.message') }}
-            </div>
-          </TransitionGroup>
-        </div>
-      </div>
+    <div class="relative mx-auto h-full flex" :style="{ backgroundImage: `url(${bgImage})` }">
       <div
         class="relative flex-1 p-30px dark:bg-[var(--login-bg-color)] lt-sm:p-10px overflow-x-hidden overflow-y-auto"
       >
@@ -35,30 +12,26 @@
           class="flex items-center justify-between at-2xl:justify-end at-xl:justify-end"
           style="color: var(--el-text-color-primary);"
         >
-          <div class="flex items-center at-2xl:hidden at-xl:hidden">
-            <img alt="" class="mr-10px h-48px w-48px" src="@/assets/imgs/logo.png" />
-            <span class="text-20px font-bold" >{{ underlineToHump(appStore.getTitle) }}</span>
-          </div>
           <div class="flex items-center justify-end space-x-10px h-48px">
-            <ThemeSwitch />
             <LocaleDropdown />
           </div>
         </div>
         <!-- 右边的登录界面 -->
         <Transition appear enter-active-class="animate__animated animate__bounceInRight">
           <div
-            class="m-auto h-[calc(100%-60px)] w-[100%] flex items-center at-2xl:max-w-500px at-lg:max-w-500px at-md:max-w-500px at-xl:max-w-500px"
+            class="m-auto h-[calc(70%)] w-[100%] flex items-center at-2xl:max-w-550px at-lg:max-w-550px at-md:max-w-550px at-xl:max-w-550px"
+            :style="{backgroundImage: `url(${loginBox})`,minHeight: '630px', minWidth: '300px',backgroundSize: 'cover',marginTop:'5%'}"
           >
             <!-- 账号登录 -->
-            <LoginForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
+            <LoginForm class="m-auto h-auto p-50px lt-xl:(rounded-3xl)" />
             <!-- 手机登录 -->
-            <MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 二维码登录 -->
-            <QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 注册 -->
-            <RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
-            <!-- 三方登录 -->
-            <SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
+<!--            <MobileForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
+<!--            &lt;!&ndash; 二维码登录 &ndash;&gt;-->
+<!--            <QrCodeForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
+<!--            &lt;!&ndash; 注册 &ndash;&gt;-->
+<!--            <RegisterForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
+<!--            &lt;!&ndash; 三方登录 &ndash;&gt;-->
+<!--            <SSOLoginVue class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />-->
             <!-- 忘记密码 -->
             <ForgetPasswordForm class="m-auto h-auto p-20px lt-xl:(rounded-3xl light:bg-white)" />
           </div>
@@ -68,21 +41,34 @@
   </div>
 </template>
 <script lang="ts" setup>
-import { underlineToHump } from '@/utils'
-
 import { useDesign } from '@/hooks/web/useDesign'
 import { useAppStore } from '@/store/modules/app'
-import { ThemeSwitch } from '@/layout/components/ThemeSwitch'
 import { LocaleDropdown } from '@/layout/components/LocaleDropdown'
-
-import { LoginForm, MobileForm, QrCodeForm, RegisterForm, SSOLoginVue, ForgetPasswordForm } from './components'
-
+import bgImage from '@/assets/imgs/loginbg.png'
+import loginBox from '@/assets/imgs/login-box.png'
+import { LoginForm, ForgetPasswordForm } from './components'
 defineOptions({ name: 'Login' })
-
 const { t } = useI18n()
 const appStore = useAppStore()
 const { getPrefixCls } = useDesign()
 const prefixCls = getPrefixCls('login')
+// const handleDingTalkLogin=async (code) => {
+//   try {
+//     const type = 20;
+//     const state = "22";
+//     return await request.post({
+//       url: '/system/auth/social-login',
+//       method: 'post',
+//       data: {
+//         type,
+//         code,
+//         state
+//       }
+//     });
+//   } catch (error) {
+//     throw error;
+//   }
+// }
 </script>
 
 <style lang="scss" scoped>
@@ -99,7 +85,7 @@ $prefix-cls: #{$namespace}-login;
       z-index: -1;
       width: 100%;
       height: 100%;
-      background-image: url('@/assets/svgs/login-bg.svg');
+      background-image: url('@/assets/imgs/loginbg.png');
       background-position: center;
       background-repeat: no-repeat;
       content: '';
@@ -118,4 +104,15 @@ $prefix-cls: #{$namespace}-login;
     background-color: var(--login-bg-color);
   }
 }
-</style>
+.el-input__wrapper {
+  background-color: transparent !important;
+  height: 3.5em;
+  color: white;
+  box-shadow: none !important;
+  border-radius: 0;
+  border: none !important;
+  border-bottom: 1px solid #bacde0 !important;
+  outline: none;
+}
+
+</style>

+ 121 - 124
src/views/Login/components/LoginForm.vue

@@ -9,33 +9,17 @@
     label-width="120px"
     size="large"
   >
-    <el-row style="margin-right: -10px; margin-left: -10px">
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item>
-          <LoginFormTitle style="width: 100%" />
-        </el-form-item>
-      </el-col>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantName">
-          <el-input
-            v-model="loginData.loginForm.tenantName"
-            :placeholder="t('login.tenantNamePlaceholder')"
-            :prefix-icon="iconHouse"
-            link
-            type="primary"
-          />
+    <el-row style="display: flex;justify-content: center">
+      <el-image v-if="loginType === 'uname'" :src="imgsaoma" class="clickable-image" alt="扫码登录" title="扫码登录" @click="changeLogin('dingding')"/>
+      <el-image v-if="loginType === 'dingding'" :src="imgreturn" class="clickable-image" alt="返回" title="返回" @click="changeLogin('uname')"/>
+        <el-image :src="img" class=" h-100px" style="margin-top: 16%"/>
+      <div v-if="loginType === 'uname'" style="width: 90%">
+      <el-col :span="24" style="padding-right: 10px; padding-left: 10px; margin-top: 85px">
+        <el-form-item prop="username" >
+          <el-input  v-model="loginData.loginForm.username" :placeholder="t('login.usernamePlaceholder')" type="text" auto-complete="off"  :prefix-icon="iconAvatar"/>
         </el-form-item>
       </el-col>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item prop="username">
-          <el-input
-            v-model="loginData.loginForm.username"
-            :placeholder="t('login.usernamePlaceholder')"
-            :prefix-icon="iconAvatar"
-          />
-        </el-form-item>
-      </el-col>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
+      <el-col :span="24" style="padding-right: 10px; padding-left: 10px;margin-top: 15px">
         <el-form-item prop="password">
           <el-input
             v-model="loginData.loginForm.password"
@@ -49,10 +33,10 @@
       </el-col>
       <el-col
         :span="24"
-        style="padding-right: 10px; padding-left: 10px; margin-top: -20px; margin-bottom: -20px"
+        style="padding-right: 10px; padding-left: 10px; margin-top: 20px;"
       >
         <el-form-item>
-          <el-row justify="space-between" style="width: 100%">
+          <el-row justify="space-between" style="width: 100%;">
             <el-col :span="6">
               <el-checkbox v-model="loginData.loginForm.rememberMe">
                 {{ t('login.remember') }}
@@ -70,9 +54,10 @@
           </el-row>
         </el-form-item>
       </el-col>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
+      <el-col :span="24" style="padding-right: 10px; padding-left: 10px;margin-bottom: 10px">
         <el-form-item>
           <XButton
+            style="height: 4em;background-color: rgb(11, 26, 50);border: none;opacity: 1;color: rgb(186, 205, 224)"
             :loading="loginLoading"
             :title="t('login.login')"
             class="w-[100%]"
@@ -89,72 +74,18 @@
         mode="pop"
         @success="handleLogin"
       />
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item>
-          <el-row :gutter="5" justify="space-between" style="width: 100%">
-            <el-col :span="8">
-              <XButton
-                :title="t('login.btnMobile')"
-                class="w-[100%]"
-                @click="setLoginState(LoginStateEnum.MOBILE)"
-              />
-            </el-col>
-            <el-col :span="8">
-              <XButton
-                :title="t('login.btnQRCode')"
-                class="w-[100%]"
-                @click="setLoginState(LoginStateEnum.QR_CODE)"
-              />
-            </el-col>
-            <el-col :span="8">
-              <XButton
-                :title="t('login.btnRegister')"
-                class="w-[100%]"
-                @click="setLoginState(LoginStateEnum.REGISTER)"
-              />
-            </el-col>
-          </el-row>
-        </el-form-item>
-      </el-col>
-      <el-divider content-position="center">{{ t('login.otherLogin') }}</el-divider>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item>
-          <div class="w-[100%] flex justify-between">
-            <Icon
-              v-for="(item, key) in socialList"
-              :key="key"
-              :icon="item.icon"
-              :size="30"
-              class="anticon cursor-pointer"
-              color="#999"
-              @click="doSocialLogin(item.type)"
-            />
-          </div>
-        </el-form-item>
-      </el-col>
-      <el-divider content-position="center">萌新必读</el-divider>
-      <el-col :span="24" style="padding-right: 10px; padding-left: 10px">
-        <el-form-item>
-          <div class="w-[100%] flex justify-between">
-            <el-link href="https://doc.iocoder.cn/" target="_blank">📚开发指南</el-link>
-            <el-link href="https://doc.iocoder.cn/video/" target="_blank">🔥视频教程</el-link>
-            <el-link href="https://www.iocoder.cn/Interview/good-collection/" target="_blank">
-              ⚡面试手册
-            </el-link>
-            <el-link href="http://static.yudao.iocoder.cn/mp/Aix9975.jpeg" target="_blank">
-              🤝外包咨询
-            </el-link>
-          </div>
-        </el-form-item>
-      </el-col>
+      </div>
+      <div v-if="loginType === 'dingding'" id="login_container"  style="margin-left: 8%;margin-top: 18%">
+      </div>
     </el-row>
   </el-form>
 </template>
 <script lang="ts" setup>
+import img from '@/assets/imgs/img.png'
+import imgsaoma from '@/assets/imgs/saoma.png'
+import imgreturn from '@/assets/imgs/return.png'
 import { ElLoading } from 'element-plus'
-import LoginFormTitle from './LoginFormTitle.vue'
 import type { RouteLocationNormalizedLoaded } from 'vue-router'
-
 import { useIcon } from '@/hooks/web/useIcon'
 
 import * as authUtil from '@/utils/auth'
@@ -162,13 +93,94 @@ import { usePermissionStore } from '@/store/modules/permission'
 import * as LoginApi from '@/api/login'
 import { LoginStateEnum, useFormValid, useLoginState } from './useLogin'
 
+import { ref,nextTick } from 'vue';
+const appId = 'dingik345qmyhtysvs2x'
+const dingRedirectUrl = 'http://1.94.244.160:81/login?loginType=dingding&type=20'
+
+const loginType = ref('uname');
+const changeLogin = (type) => {
+  loginType.value = type;
+  if (type === 'dingding') {
+    initDingLogin();
+  }
+};
+
+
+const initDingLogin=()=> {
+  // if (window.DDLogin) {
+  var url = dingRedirectUrl
+  var state = _getRandomString(10);
+  var gotoUrl = encodeURIComponent(
+    "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?" +
+    "appid=" + appId +
+    "&response_type=code" +
+    "&scope=snsapi_login" +
+    "&state=" + state +
+    "&redirect_uri=" + url
+  );
+
+  nextTick(() => {
+    var obj = window.DDLogin({
+      id: "login_container",
+      goto: gotoUrl,
+      style: "border:none;background-color:#FFFFFF;",
+      width: '90%', // 二维码的宽度
+      height: "290", // 二维码的高度
+    })
+    // 重置扫码登录框的样式,让登录框居中
+    let box = document.getElementById('login_container')
+    box.querySelector('iframe').style.top = '0'
+    box.querySelector('iframe').style.bottom = '0'
+    box.querySelector('iframe').style.left = '0'
+    box.querySelector('iframe').style.right = '0'
+    box.querySelector('iframe').style.margin = 'auto'
+  })
+
+  /* new window.DDLogin({
+    id: "login_container",
+    goto: gotoUrl,
+    style: "border:none;background-color:#FFFFFF;",
+    width: "350",
+    height: "350",
+  }); */
+  var handleMessage = function (event) {
+    var origin = event.origin;
+    if (origin === "https://login.dingtalk.com") {
+      var loginTmpCode = event.data;
+      console.log(loginTmpCode)
+      window.location.href =
+        "https://oapi.dingtalk.com/connect/oauth2/sns_authorize?appid=dingik345qmyhtysvs2x&response_type=code&scope=snsapi_login&state=" +
+        state +
+        "&redirect_uri=" +
+        url +
+        "&loginTmpCode=" +
+        loginTmpCode;
+    }
+  };
+  if (typeof window.addEventListener != "undefined") {
+    window.addEventListener("message", handleMessage, false);
+  } else if (typeof window.attachEvent != "undefined") {
+    window.attachEvent("onmessage", handleMessage);
+  }
+  // }
+}
+const _getRandomString=(len)=> {
+  len = len || 10;
+  let $chars = "ABCDEFGHIJKMNOPQRSTUVWXYZ"; // 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1
+  let maxPos = $chars.length;
+  let pwd = "";
+  for (var i = 0; i < len; i++) {
+    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
+  }
+  return pwd;
+}
 defineOptions({ name: 'LoginForm' })
 
 const { t } = useI18n()
 const message = useMessage()
 const iconHouse = useIcon({ icon: 'ep:house' })
-const iconAvatar = useIcon({ icon: 'ep:avatar' })
-const iconLock = useIcon({ icon: 'ep:lock' })
+const iconAvatar = useIcon({ icon: 'ep:avatar',size:26 })
+const iconLock = useIcon({ icon: 'ep:lock',size:26 })
 const formLogin = ref()
 const { validForm } = useFormValid(formLogin)
 const { setLoginState, getLoginState } = useLoginState()
@@ -289,40 +301,6 @@ const handleLogin = async (params: any) => {
 }
 
 // 社交登录
-const doSocialLogin = async (type: number) => {
-  if (type === 0) {
-    message.error('此方式未配置')
-  } else {
-    loginLoading.value = true
-    if (loginData.tenantEnable === 'true') {
-      // 尝试先通过 tenantName 获取租户
-      await getTenantId()
-      // 如果获取不到,则需要弹出提示,进行处理
-      if (!authUtil.getTenantId()) {
-        try {
-          const data = await message.prompt('请输入租户名称', t('common.reminder'))
-          if (data?.action !== 'confirm') throw 'cancel'
-          const res = await LoginApi.getTenantIdByName(data.value)
-          authUtil.setTenantId(res)
-        } catch (error) {
-          if (error === 'cancel') return
-        } finally {
-          loginLoading.value = false
-        }
-      }
-    }
-    // 计算 redirectUri
-    // tricky: type、redirect需要先encode一次,否则钉钉回调会丢失。
-    // 配合 Login/SocialLogin.vue#getUrlValue() 使用
-    const redirectUri =
-      location.origin +
-      '/social-login?' +
-      encodeURIComponent(`type=${type}&redirect=${redirect.value || '/'}`)
-
-    // 进行跳转
-    window.location.href = await LoginApi.socialAuthRedirect(type, encodeURIComponent(redirectUri))
-  }
-}
 watch(
   () => currentRoute.value,
   (route: RouteLocationNormalizedLoaded) => {
@@ -358,4 +336,23 @@ onMounted(() => {
     cursor: pointer;
   }
 }
+::v-deep .rqinput .el-input__inner  {
+  background-color: transparent !important;
+  border: none !important;
+  border-bottom: 1px solid rgb(186, 205, 224) !important;
+  border-radius: 0;
+  height: 2.8em;
+  //left: 18%;
+  padding-left: 3em;
+  color: white;
+}
+.clickable-image {
+  cursor: pointer;
+  position:absolute;
+  //top: 2%;
+  //right: 2%;
+  width: 6%;
+  margin-top: -9%;
+  margin-left: 111%;
+}
 </style>