Browse Source

add:钉钉、企业微信扫码、工作台登录,用户注册

master
秋田弘 6 months ago
parent
commit
43423f7f86
  1. 3
      frontend/components.d.ts
  2. 245
      frontend/src/components/desktop/LockDesktop.vue
  3. 13
      frontend/src/stores/login.ts
  4. 7
      frontend/src/system/dinglogin.ts
  5. 34
      frontend/src/system/index.ts
  6. 42
      frontend/src/system/qiyeweixinlogin.ts

3
frontend/components.d.ts

@ -67,6 +67,7 @@ declare module 'vue' {
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBadge: typeof import('element-plus/es')['ElBadge']
ElBu: typeof import('element-plus/es')['ElBu']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCarousel: typeof import('element-plus/es')['ElCarousel']
@ -84,10 +85,12 @@ declare module 'vue' {
ElFooter: typeof import('element-plus/es')['ElFooter']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElFormItm: typeof import('element-plus/es')['ElFormItm']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElImage: typeof import('element-plus/es')['ElImage']
ElInput: typeof import('element-plus/es')['ElInput']
ElItem: typeof import('element-plus/es')['ElItem']
ElMain: typeof import('element-plus/es')['ElMain']
ElOption: typeof import('element-plus/es')['ElOption']
ElPagination: typeof import('element-plus/es')['ElPagination']

245
frontend/src/components/desktop/LockDesktop.vue

@ -16,7 +16,7 @@
</el-avatar>
</div>
<el-form
v-if="ThirdPartyLoginMethod === 'login'"
v-if="store.ThirdPartyLoginMethod === 'login'"
label-position="left"
label-width="0px"
>
@ -62,11 +62,13 @@
/>
</div>
<div class="actions">
<!-- <a
<a
href="#"
@click.prevent="toggleRegister"
@click.prevent="
store.ThirdPartyLoginMethod = 'register'
"
>注册新用户</a
> -->
>
<a
href="#"
@click.prevent="toggleUserSwitch"
@ -75,44 +77,44 @@
</div>
</el-form>
<el-form
v-else-if="ThirdPartyLoginMethod === 'register'"
v-else-if="store.ThirdPartyLoginMethod === 'register'"
label-position="left"
label-width="0px"
:model="regForm"
:model="store.registerInfo"
ref="regFormRef"
:rules="rules"
>
<el-form-item prop="username">
<el-input
v-model="regForm.username"
v-model="store.registerInfo.username"
placeholder="请输入用户名"
prefix-icon="UserFilled"
></el-input>
</el-form-item>
<el-form-item prop="nickname">
<el-input
v-model="regForm.nickname"
v-model="store.registerInfo.nickname"
placeholder="请输入真实姓名"
prefix-icon="Avatar"
></el-input>
</el-form-item>
<el-form-item prop="email">
<el-input
v-model="regForm.email"
v-model="store.registerInfo.email"
placeholder="请输入邮箱"
prefix-icon="Message"
></el-input>
</el-form-item>
<el-form-item prop="phone">
<el-input
v-model="regForm.phone"
v-model="store.registerInfo.phone"
placeholder="请输入手机号"
prefix-icon="Iphone"
></el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="regForm.password"
v-model="store.registerInfo.password"
type="password"
placeholder="请输入密码"
show-password
@ -121,21 +123,31 @@
</el-form-item>
<el-form-item prop="confirmPassword">
<el-input
v-model="regForm.confirmPassword"
v-model="store.registerInfo.confirmPassword"
type="password"
placeholder="请再次输入密码"
show-password
prefix-icon="Lock"
></el-input>
</el-form-item>
<el-button
type="primary"
@click="onRegister"
>注册</el-button
>
<el-form-item>
<el-button
type="primary"
@click="onRegister"
>注册</el-button
>
</el-form-item>
<el-form-item>
<el-button
style="background-color: gray; color: white"
@click="store.ThirdPartyLoginMethod = 'login'"
>返回</el-button
>
</el-form-item>
</el-form>
<div v-else-if="ThirdPartyLoginMethod === 'dingding'">
<div v-else-if="store.ThirdPartyLoginMethod === 'dingding'">
<el-row>
<el-col :span="24">
<div class="qr-code">
@ -145,31 +157,33 @@
</el-row>
<el-row>
<el-col :span="24">
<el-button @click="ThirdPartyLoginMethod = 'login'"
<el-button
@click="store.ThirdPartyLoginMethod = 'login'"
>返回</el-button
>
</el-col>
</el-row>
</div>
<!-- 企业微信 -->
<div v-else-if="ThirdPartyLoginMethod === 'qywechat'">
<div v-else-if="store.ThirdPartyLoginMethod === 'qyweixin'">
<el-row>
<el-col :span="24">
<div class="qr-code">
<div class="qywechat">
<div id="qywechat-qr-code"></div>
</div>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-button @click="ThirdPartyLoginMethod = 'login'"
<el-button
@click="store.ThirdPartyLoginMethod = 'login'"
>返回</el-button
>
</el-col>
</el-row>
</div>
<!-- 手机登录 -->
<div v-else-if="ThirdPartyLoginMethod === 'phone'">
<div v-else-if="store.ThirdPartyLoginMethod === 'phone'">
<el-row>
<el-col :span="24">
<el-form>
@ -180,33 +194,45 @@
/>
</el-form-item>
<!-- 验证码 -->
<el-form-item>
<el-row :gutter="10">
<el-col :span="16">
<el-row :gutter="24">
<el-col :span="17">
<el-form-item>
<el-input
v-model="phoneForm.code"
placeholder="请输入验证码"
/>
</el-col>
<el-col :span="8">
</el-form-item>
</el-col>
<el-col :span="7">
<el-form-item>
<button
class="send-code-btn"
@click.prevent="onSendCode"
:disabled="isSendCodeButtonDisabled"
>
发送验证码
{{ sendCodeButtonText }}
</button>
</el-col>
</el-row>
</el-form-item>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-col>
</el-row>
<el-row>
<el-col :span="24">
<el-button @click="ThirdPartyLoginMethod = 'login'"
<el-row :gutter="24">
<el-col :span="12">
<el-button
style="background-color: gray; color: white"
@click="store.ThirdPartyLoginMethod = 'login'"
>返回</el-button
>
</el-col>
<el-col :span="12">
<el-button
@click="loginByPhone"
>登录</el-button
>
</el-col>
</el-row>
</div>
</el-card>
@ -220,7 +246,6 @@
import router from "@/system/router";
import { RestartApp } from "@/util/goutil";
import { notifyError } from "@/util/msg";
import QRCode from "qrcode";
import { onMounted, ref, watchEffect } from "vue";
import { useRoute } from "vue-router";
const route = useRoute();
@ -229,12 +254,13 @@
const loginCallback = sys._options.loginCallback;
const config = getSystemConfig();
const lockClassName = ref("screen-show");
const ThirdPartyLoginMethod = ref("login");
const phoneForm = ref({
phone: "",
code: "",
});
// DTFrameLogin
// DTFrameLogin WwLogin
declare global {
interface Window {
DTFrameLogin: (
@ -243,6 +269,13 @@
successCbk: (result: IDTLoginSuccess) => void,
errorCbk?: (error: any) => void
) => void;
WwLogin: (options: {
id: string; // ID
appid: string; // ID
agentid: string; // ID
redirect_uri: string; //
state: string; // CSRF
}) => void;
}
}
// 使css
@ -283,8 +316,9 @@
}
};
const onDingDingScan = () => {
ThirdPartyLoginMethod.value = "dingding";
store.ThirdPartyLoginMethod = "dingding";
initDingDingScan();
};
@ -338,13 +372,34 @@
});
}
const sendCodeButtonText = ref("发送验证码");
const isSendCodeButtonDisabled = ref(false);
const onSendCode = () => {
console.log("发送验证码");
startCountdown();
};
const startCountdown = () => {
let countdown = 2;
isSendCodeButtonDisabled.value = true;
sendCodeButtonText.value = `${countdown}秒后重试`;
const interval = setInterval(() => {
countdown--;
if (countdown > 0) {
sendCodeButtonText.value = `${countdown}秒后重试`;
} else {
clearInterval(interval);
sendCodeButtonText.value = "发送验证码";
isSendCodeButtonDisabled.value = false;
}
}, 1000);
};
const thirdPartyPlatforms = [
{
name: "qywechat",
name: "qyweixin",
icon: new URL("@/assets/login/qywechat.png", import.meta.url).href,
},
{
@ -404,20 +459,28 @@
const platformCodeMap: Record<string, string> = {
github: "github",
gitee: "gitee",
qywechat: "qywechat",
qyweixin: "qyweixin_scan",
qq: "qq",
dingding: "dingtalk_scan",
phone: "sms_code",
};
//
const codeParam = platform ? platformCodeMap[platform] : null;
//
const login_type = codeParam ? codeParam : "password";
let param;
if (codeParam) {
if (platform === "phone") {
param = { phone: phoneForm.value.phone, sms_code: code };
} else {
param = { code: code };
}
} else {
param = { username: userName.value, password: userPassword.value };
}
const param = codeParam
? { code: code }
: { username: userName.value, password: userPassword.value };
const login_type = codeParam ? codeParam : "password";
const res = await loginCallback(login_type, param);
@ -436,14 +499,6 @@
RestartApp();
};
const regForm = ref({
username: "",
password: "",
confirmPassword: "",
email: "",
phone: "",
nickname: "",
});
const regFormRef: any = ref(null);
const rules = {
@ -467,7 +522,7 @@
console.log(rule);
if (value === "") {
callback(new Error("请再次输入密码"));
} else if (value !== regForm.value.password) {
} else if (value !== store.registerInfo.password) {
callback(new Error("两次输入的密码不一致"));
} else {
callback();
@ -506,11 +561,10 @@
const onRegister = async () => {
try {
await regFormRef.value.validate();
const save = toRaw(regForm.value);
const userInfo = config.userInfo;
const comp = await fetch(userInfo.url + "/member/register", {
const comp = await fetch(userInfo.url + "/user/register", {
method: "POST",
body: JSON.stringify(save),
body: JSON.stringify(store.registerInfo),
});
if (!comp.ok) {
notifyError("网络错误,注册失败");
@ -519,7 +573,12 @@
const res = await comp.json();
if (res.success) {
notifyError("注册成功");
// toggleRegister();
loginSuccess();
window.location.href = "/";
//
config.userInfo.username = store.registerInfo.username;
config.userInfo.password = store.registerInfo.password;
setSystemConfig(config);
} else {
notifyError(res.message);
return;
@ -566,7 +625,7 @@
return await authWithDingDing();
};
break;
case "qywechat":
case "qyweixin":
loginFunction = async function () {
return await authWithWechat();
};
@ -590,10 +649,14 @@
};
const authWithPhone = async (): Promise<boolean> => {
ThirdPartyLoginMethod.value = "phone";
store.ThirdPartyLoginMethod = "phone";
return true;
};
const loginByPhone = async () => {
await onLogin();
};
const authWithDingDing = async (): Promise<boolean> => {
onDingDingScan();
return true;
@ -619,7 +682,6 @@
}
const data = await res.json();
if (data && data.data && data.data.url) {
console.log(data.data.url, "---");
// 使URL
const urlPattern = /client_id=[^&]+/;
if (urlPattern.test(data.data.url)) {
@ -636,29 +698,32 @@
};
const authWithWechat = async (): Promise<boolean> => {
ThirdPartyLoginMethod.value = "qywechat";
await loadScript(
"http://rescdn.qqmail.com/node/ww/wwopenmng/js/sso/wwLogin-1.0.0.js"
);
store.ThirdPartyLoginMethod = "qyweixin";
const res = await fetch(
"http://server001.godoos.com/user/qyweixin/qrcode"
"http://server001.godoos.com/user/qyweixin/conf"
);
if (res.ok) {
const data = await res.json();
if (data.success && data.data.url) {
//
QRCode.toCanvas(
document.getElementById("qywechat-qr-code"),
data.data.url,
function (error: any) {
if (error) console.error(error);
console.log("二维码生成成功");
}
);
} else {
notifyError("获取授权URL失败");
if (data.success) {
console.log(data.data);
window.WwLogin({
id: "qywechat-qr-code",
appid: data.data.corp_id,
agentid: data.data.agent_id,
redirect_uri: data.data.redirect,
state: "WWLogin",
});
return true;
}
return false;
} else {
notifyError("网络错误,无法获取二维码");
return false;
}
return true;
};
const authWithGitee = async (): Promise<boolean> => {
@ -716,7 +781,7 @@
backdrop-filter: blur(7px);
.login-box {
width: 300px;
width: 400px;
padding: 20px;
text-align: center;
background: #ffffff;
@ -863,5 +928,31 @@
border-radius: 4px;
line-height: 40px;
cursor: pointer;
transition: background 0.3s ease;
&:disabled {
background: #c0c4cc; //
cursor: not-allowed; //
}
}
.qywechat {
width: 300px;
height: 400px;
padding: 10px;
margin: 0 auto;
overflow: hidden;
border: 1px solid rgb(203, 203, 203);
border-radius: 10px;
// 使 :deep
:deep(#qywechat-qr-code) {
width: 100%;
height: 100%;
iframe {
width: 100%;
height: 100%;
border: none; //
}
}
}
</style>

13
frontend/src/stores/login.ts

@ -10,11 +10,24 @@ export const useLoginStore = defineStore("login", () => {
// 临时clientId
const tempClientId = ref<string>("");
const ThirdPartyLoginMethod = ref("login");
const registerInfo = ref({
"username": "",
"nickname": "",
"password": "",
"email": "",
"phone": "",
"third_user_id": "",
"union_id": "",
"patform": "",
"confirmPassword": "",
});
return {
ThirdPartyPlatform,
State,
ThirdPartyLoginMethod,
tempToken,
tempClientId,
registerInfo,
};
},);

7
frontend/src/system/dinglogin.ts

@ -4,7 +4,7 @@ import { getSystemConfig, setSystemConfig } from "./config";
const config = getSystemConfig();
// 函数用于动态加载外部JS文件
function loadScript(url: string): Promise<void> {
export function loadScript(url: string): Promise<void> {
return new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
@ -16,14 +16,13 @@ function loadScript(url: string): Promise<void> {
const currentUrl = window.location.origin
export async function getCropId(): Promise<boolean> {
export async function authWithDing(): Promise<boolean> {
try {
const res = await fetch(currentUrl + "/user/ding/conf");
const data = await res.json();
console.log(data)
if (data.success) {
console.log(data.data.id)
alert(data.data.host);
return await getCode(data.data.id);
}
return false;
@ -54,7 +53,7 @@ async function getCode(corpId: string): Promise<boolean> {
// 登录接口
async function toLogin(this: any, code: string): Promise<boolean> {
async function toLogin(code: string): Promise<boolean> {
const data = {
login_type: "dingtalk_workbench",
client_id: GetClientId(),

34
frontend/src/system/index.ts

@ -26,11 +26,12 @@ import { pick } from '../util/modash';
import { clearSystemConfig, fetchGet, getFileUrl, getSystemConfig, getSystemKey, setSystemConfig, setSystemKey } from './config';
import { OsFileInterface } from './core/FIleInterface';
import { extname } from './core/Path';
import { getCropId } from './dinglogin.ts';
import { authWithDing } from './dinglogin.ts';
import { initBuiltinApp, initBuiltinFileOpener } from './initBuiltin';
import { defaultConfig } from './initConfig';
import { Tray, TrayOptions } from './menu/Tary';
import { Notify, NotifyConstructorOptions } from './notification/Notification';
import { authWithWechat } from './qiyeweixinlogin.ts';
import { Dialog } from './window/Dialog';
export type OsPlugin = (system: System) => void;
@ -102,16 +103,26 @@ export class System {
initBuiltinApp(this);
initBuiltinFileOpener(this); // 注册内建文件打开器
await this.initSavedConfig(); // 初始化保存的配置
// 判断是否登录
// 判断是否为钉钉工作台登录
const login_type = new URLSearchParams(window.location.search).get("login_type");
if (login_type === "dingding") {
let islogin = await getCropId();
let islogin = await authWithDing();
alert(islogin);
if (!islogin) {
console.log("登录失败,无法继续操作");
alert("登录失败,无法继续操作");
return;
}
}
if (login_type === "qiyeweixin") {
let islogin = await authWithWechat();
if (!islogin) {
alert("登录失败,无法继续操作");
return;
}
}
// 判断是否登录
await this.isLogin();
initEventListener(); // 初始化事件侦听
@ -235,9 +246,20 @@ export class System {
if (jsondata.code == 10000) {
const loginStore = useLoginStore();
loginStore.tempToken = jsondata.data.token;
loginStore.tempClientId = jsondata.data.client_id;
loginStore.ThirdPartyLoginMethod = "register";
// 注册信息
loginStore.registerInfo = {
username: jsondata.data.username,
nickname: jsondata.data.nickname,
password: jsondata.data.password,
email: jsondata.data.email,
phone: jsondata.data.phone,
third_user_id: jsondata.data.third_user_id,
union_id: jsondata.data.union_id,
patform: jsondata.data.patform,
confirmPassword: "",
}
return true;
}

42
frontend/src/system/qiyeweixinlogin.ts

@ -0,0 +1,42 @@
import { GetClientId } from "@/util/clientid";
import { getSystemConfig, setSystemConfig } from "./config";
const config = getSystemConfig();
export async function authWithWechat(): Promise<boolean> {
const queryParams = new URLSearchParams(window.location.search);
const code = queryParams.get("code");
if (!code) {
alert("登录失败,无法继续操作");
return false;
}
return await toLogin(code);
}
// 登录接口
async function toLogin(code: string): Promise<boolean> {
const data = {
login_type: "qyweixin",
client_id: GetClientId(),
param: {
code,
},
};
try {
const res = await fetch("http://server001.godoos.com/user/login", {
method: "POST",
body: JSON.stringify(data),
});
const jsondata = await res.json();
if (jsondata.success) {
jsondata.data.url = "http://server001.godoos.com";
config.userInfo = jsondata.data;
config.userType = "member";
setSystemConfig(config);
window.location.href = "/";
return true;
}
} catch (error) {
alert(error);
}
return false;
}
Loading…
Cancel
Save