完成钉钉扫码登陆

dev/mobile
Jimmy Liow 1 year ago
parent 865ff8c9cc
commit 41280bcd6c

@ -1,24 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { configure } from 'mobx';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { AuthContext } from '@/stores/AuthContext';
import { ThemeContext } from '@/stores/ThemeContext';
import ConversationProvider from '@/views/Conversations/ConversationProvider';
import Auth from '@/stores/Auth';
import App from '@/views/App';
import Standlone from '@/views/Standlone';
import OrderFollow from '@/views/OrderFollow';
import ChatHistory from '@/views/ChatHistory';
import SalesManagement from '@/views/SalesManagement';
import DingdingQRCode from '@/views/DingdingQRCode';
import DingdingCallbak from '@/views/DingdingCallbak';
import AccountProfile from '@/views/AccountProfile';
import ErrorPage from '@/views/ErrorPage';
import React from 'react'
import ReactDOM from 'react-dom/client'
import { configure } from 'mobx'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { AuthContext } from '@/stores/AuthContext'
import { ThemeContext } from '@/stores/ThemeContext'
import ConversationProvider from '@/views/Conversations/ConversationProvider'
import Auth from '@/stores/Auth'
import App from '@/views/App'
import Standlone from '@/views/Standlone'
import OrderFollow from '@/views/OrderFollow'
import ChatHistory from '@/views/ChatHistory'
import SalesManagement from '@/views/SalesManagement'
import DingdingQRCode from '@/views/DingdingQRCode'
import AccountProfile from '@/views/AccountProfile'
import ErrorPage from '@/views/ErrorPage'
import Conversations from '@/views/Conversations/ChatWindow';
// import Conversations from '@/views/Conversations/ChatApp';
import '@/assets/index.css';
import Conversations from '@/views/Conversations/ChatWindow'
// import Conversations from '@/views/Conversations/ChatApp'
import '@/assets/index.css'
configure({
useProxies: 'ifavailable',
@ -27,7 +26,7 @@ configure({
observableRequiresReaction: false,
reactionRequiresObservable: true,
disableErrorBoundaries: process.env.NODE_ENV == 'production',
});
})
const router = createBrowserRouter([
{
@ -48,10 +47,9 @@ const router = createBrowserRouter([
element: <Standlone />,
children: [
{ path: 'dingding/qrcode', element: <DingdingQRCode /> },
{ path: 'dingding/callback', element: <DingdingCallbak /> },
],
},
]);
])
ReactDOM.createRoot(document.getElementById('root')).render(
// <React.StrictMode>
@ -63,4 +61,4 @@ ReactDOM.createRoot(document.getElementById('root')).render(
</AuthContext.Provider>
</ThemeContext.Provider>
// </React.StrictMode>
);
)

@ -12,7 +12,7 @@ const URL = {
// const WS_URL = 'ws://202.103.68.144:8888/whatever/';
const WS_URL = 'ws://202.103.68.157:8888/whatever/';
// let realtimeAPI = new RealTimeAPI({ url: URL, protocol: 'aaa' });
let realtimeAPI = new RealTimeAPI({ url: WS_URL, protocol: 'WhatsApp' });
// let realtimeAPI = new RealTimeAPI({ url: WS_URL, protocol: 'WhatsApp' });
export const useConversations = () => {
const [errors, setErrors] = useState([]);
@ -87,7 +87,7 @@ export const useConversations = () => {
type: 'message',
message: msg,
};
realtimeAPI.sendMessage(msgObj);
// realtimeAPI.sendMessage(msgObj);
addMessage(msgObj.message);
// debug:
// const msgObjR = {
@ -97,10 +97,10 @@ export const useConversations = () => {
// addMessage({ ...msgObjR.message, sender: 'other', id: Date.now().toString(16) });
};
realtimeAPI.onError(addError.bind(null, 'Error'));
realtimeAPI.onMessage(handleMessage);
realtimeAPI.onCompletion(addError.bind(null, 'Not Connected to Server'));
realtimeAPI.keepAlive(); // Ping Server
// realtimeAPI.onError(addError.bind(null, 'Error'));
// realtimeAPI.onMessage(handleMessage);
// realtimeAPI.onCompletion(addError.bind(null, 'Not Connected to Server'));
// realtimeAPI.keepAlive(); // Ping Server
return {
errors, messages, conversationsList, currentConversation, sendMessage,

@ -22,10 +22,10 @@ function AccountProfile() {
<Row>
<Col span={12} offset={6}>
<Descriptions title='个人信息' layout='vertical' column={2}>
<Descriptions.Item label='名字'><Space size='middle'><Avatar src={`https://api.dicebear.com/7.x/miniavs/svg?seed=${new Date().toString()}`} />骆梅玉</Space></Descriptions.Item>
<Descriptions.Item label='手机'>+86-15878301602</Descriptions.Item>
<Descriptions.Item label='邮件'>christyluo@hainatravel.com</Descriptions.Item>
<Descriptions.Item label='部门'>海纳业务组-GH事业部群-GH销售</Descriptions.Item>
<Descriptions.Item label='名字'><Space size='middle'><Avatar src={loginUser.avatarUrl} />{loginUser.username}</Space></Descriptions.Item>
<Descriptions.Item label='HT 账号'>{loginUser.accountName}</Descriptions.Item>
<Descriptions.Item label='手机'>{loginUser.stateCode} {loginUser.mobile}</Descriptions.Item>
<Descriptions.Item label='邮件'>{loginUser.email}</Descriptions.Item>
</Descriptions>
</Col>
</Row>

@ -15,7 +15,7 @@ import { isEmpty } from '@/utils/commons';
const { Header, Footer, Content } = Layout
const { Title } = Typography
// SecurityApp
// AuthApp
function App() {
const { colorPrimary, borderRadius } = useThemeContext()

@ -1,56 +0,0 @@
import React, { useEffect } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { Card, Typography, Flex, Result, Button } from 'antd'
import { useRequest } from 'ahooks'
const { Title } = Typography
const { Meta } = Card
import { useAuthContext } from '@/stores/AuthContext'
function DingdingCallbak() {
const navigate = useNavigate()
const params = new URLSearchParams(window.location.search)
const code = params.get('code')
const state = params.get('state')
console.info('code: ' + code)
console.info('state: ' + state)
const { loginUser, permissionList } = useAuthContext()
console.info('loginUser: ')
console.info(loginUser)
console.info('permissionList: ')
console.info(permissionList)
// const { data, error, loading } = useRequest(() => {
// return new Promise((resolve) => {
// setTimeout(() => {
// resolve('use Request data...');
// }, 1000);
// });
// });
setTimeout(() => {
loginUser.userId = 999
loginUser.openId = '987654321'
navigate('/s/account/profile')
}, 1000);
useEffect(() => {
}, [])
return (
<Flex justify='center' align='center' gap='middle' vertical>
<Title level={4}>正在获取你的权限请稍等</Title>
<Result
status='403'
title='403'
subTitle='你没有绑定钉钉账号,无法登陆。'
/>
</Flex>
)
}
export default DingdingCallbak;

@ -1,15 +1,21 @@
import { Card, Flex, Typography } from 'antd'
import React, { useEffect } from 'react'
import { Result, Spin, Flex, Typography } from 'antd'
import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuthContext } from '@/stores/AuthContext'
const { Title } = Typography
const { Meta } = Card
// https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information#title-qpi-0qv-anm
function DingdingQRCode() {
const navigate = useNavigate()
const { loginUser } = useAuthContext()
const [loginStatus, setLoginStatus] = useState(0)
useEffect(() => {
import('https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js').then((module) => {
import('https://g.alicdn.com/dingding/h5-dingtalk-login/0.21.0/ddlogin.js').then(() => {
window.DTFrameLogin(
{
id: 'qrCodeContainer',
@ -17,34 +23,76 @@ function DingdingQRCode() {
height: 300,
},
{
redirect_uri: encodeURIComponent('https://sales.mycht.cn/dingding/callback'),
redirect_uri: encodeURIComponent('https://sales.mycht.cn/p/dingding/callback'),
client_id: 'dingwgdx6emlxr3fcrg8',
scope: 'openid',
response_type: 'code',
state: 'state1111',
state: 'global-sales',
prompt: 'consent',
},
(loginResult) => {
const { redirectUrl, authCode, state } = loginResult
//
window.location.href = redirectUrl
// 使code
console.log(authCode)
const { authCode } = loginResult
console.log(loginResult)
setLoginStatus(200)
fetch(`https://p9axztuwd7x8a7.mycht.cn/dingtalk/dingtalkwork/WhatsAppAuth?authCode=${authCode}`)
.then(response => response.json())
.then(json => {
// {"errcode":0,"errmsg":"ok","result":{"nick":"","unionId":"j7cuUCplZe1ZiiqjQNUUmFwiEiE","avatarUrl":"https://static-legacy.dingtalk.com/media/lALPBDDrhXr716HNAoDNAoA_640_640.png","openId":"iioljiPmZ4RPoOYpkFiSn7IKAiEiE","mobile":"18777396951","stateCode":"86","email":"lyj@hainatravel.com","opisn":"383"}}
if (json.errcode === 0) {
loginUser.userId = json.result.opisn
loginUser.accountName = json.result.opisn
loginUser.username = json.result.nick
loginUser.avatarUrl = json.result.avatarUrl
loginUser.stateCode = json.result.stateCode
loginUser.mobile = json.result.mobile
loginUser.email = json.result.email
loginUser.openId = json.result.openId
navigate('/account/profile')
} else {
setLoginStatus(403)
}
})
},
(errorMsg) => {
//
alert(`Login Error: ${errorMsg}`)
setLoginStatus(403)
console.error(`Login Error: ${errorMsg}`)
},
)
})
}, [])
return (
<Flex justify='center' align='center' gap='middle' vertical>
<Title level={4}>使用钉钉扫码</Title>
<div id='qrCodeContainer' style={{ border: '1px solid rgba(5, 5, 5, 0.06)', borderRadius: '8px' }}></div>
</Flex>
)
if (loginStatus === 200) {
return (
<Flex justify='center' align='center' gap='middle' vertical>
<Result
status='success'
title='扫码成功'
subTitle='正在获取你的权限'
extra={[
<Spin size='small' />
]}
/>
</Flex>
)
} else if (loginStatus === 403) {
return (
<Flex justify='center' align='center' gap='middle' vertical>
<Result
status='403'
title='403'
subTitle='你没有绑定钉钉账号,无法登陆。'
/>
</Flex>
)
} else {
return (
<Flex justify='center' align='center' gap='middle' vertical>
<Title level={4}>使用钉钉扫码</Title>
<div id='qrCodeContainer' style={{ border: '12px solid rgba(5, 5, 5, 0.06)', borderRadius: '8px' }}></div>
</Flex>
)
}
}
export default DingdingQRCode

@ -1,8 +1,8 @@
import { useNavigate } from 'react-router-dom'
import { memo, useMemo, useCallback, useEffect, useState } from 'react'
import {
Row, Col, Divider, Table, Card, Button, Input,
Space, Segmented, Radio, Select, Flex, Spin, Form, Switch, DatePicker, List, Avatar
Row, Badge, Divider, Table, Card, Button, Input,
Space, Tag, Radio, Select, Flex, Spin, Form, Switch, DatePicker, List, Avatar
} from 'antd'
import {
StarFilled, ZoomInOutlined, StarOutlined, BarsOutlined, AppstoreOutlined, SearchOutlined
@ -83,29 +83,11 @@ const columns = [
title: '订单号',
dataIndex: 'orderNumber',
key: 'orderNumber',
width: 300,
render: (text, record, inde) => {
return (
<Space size='middle'>
{text}
<Segmented
options={[
{
label: '潜力',
value: '潜力',
},
{
label: '重点',
value: '重点',
},
{
label: '休眠',
value: '休眠',
},
]}
/>
</Space>
)
width: 222,
render: (text, record, index) => {
if (index === 1) return <Space size='middle'>{text}<Tag color='red'>重点</Tag></Space>
else if (index === 2) return <Space size='middle'>{text}<Tag color='green'>潜力</Tag></Space>
else return <Space size='middle'>{text}<Tag color='blue'>休眠</Tag></Space>
}
},
{
@ -116,6 +98,13 @@ const columns = [
return (
<Space size='middle'>
<a>{text}</a>
<Badge
className="site-badge-count-109"
count={Math.floor(Math.random() * (100 - 2 + 1) + 2)}
style={{
backgroundColor: '#52c41a',
}}
/>
</Space>
)
}
@ -125,42 +114,11 @@ const columns = [
dataIndex: 'orderStatus',
key: 'orderStatus',
width: 120,
render: (text, record, inde) => {
return (
<Select
defaultValue='新订单'
style={{
width: 100,
}}
bordered={false}
options={[
{
value: '新订单',
label: '新订单',
},
{
value: '报价中',
label: '报价中',
},
{
value: '丢失',
label: '丢失',
},
{
value: '一催',
label: '一催',
},
{
value: '二催',
label: '二催',
},
{
value: '三催',
label: '三催',
},
]}
/>
)
render: (text, record, index) => {
if (index === 1) return text + '(一催)'
else if (index === 2) return text + '(二催)'
else if (index === 3) return text + '(三催)'
else return text
}
},
{
@ -184,7 +142,7 @@ const columns = [
const AdvanceSearchForm = memo(function ({ onSubmit }) {
const [form] = Form.useForm()
function handleSubmit(values) {
onSubmit(values)
onSubmit?.(values)
}
return (
<Form
@ -319,8 +277,7 @@ function OrderFollow() {
/>
</Space>
<Divider plain orientation='left'></Divider>
{/* {orderListMemo} */}
<OrderList searchCriteria={searchCriteria} />
{orderListMemo}
</>
)

@ -1,5 +1,5 @@
import { useNavigate } from 'react-router-dom'
import { memo, useMemo, useCallback, useEffect, useState } from 'react'
import { memo, useRef, useCallback, useEffect, useState } from 'react'
import {
Row, Col, Divider, Table, Card, Button, Input,
Space, Segmented, Radio, Select, Flex, Spin, Form, Switch, DatePicker, List, Avatar
@ -75,7 +75,7 @@ const AdvanceSearchForm = memo(function ({ onSubmit }) {
console.info('AdvanceSearchForm')
const [form] = Form.useForm()
function handleSubmit(values) {
if (onSubmit) onSubmit(values)
onSubmit?.(values)
}
return (
<Form
@ -158,22 +158,25 @@ const SalesTable = function({formValues}) {
console.info('SalesTable')
console.info(formValues)
const isMounted = useRef(false)
const [salesData, setSalesData] = useState([])
const [loading, setLoading] = useState(false)
useEffect(() => {
console.info('SalesTable.useEffect')
console.info(isMounted)
setLoading(true)
// setSalesData([]) //
fetch('https://p9axztuwd7x8a7.mycht.cn/service-InfoSys/InfoSys/GetCountryList')
.then(response => response.json())
.then(json => {
if (salesData.length > 0) {
const timeString = new Date().toISOString()
salesData[0].travelAdvisor = timeString;
setSalesData(salesData)
} else {
setSalesData([...dataSource])
}
setTimeout(() => {
const randomData = dataSource.map(data => {
data.important = Math.floor(Math.random() * (100 - 2 + 1) + 2)
return data
})
setSalesData([...randomData])
setLoading(false)
}, 1000)
isMounted.current = true
}, [formValues])
const columns = [
@ -182,11 +185,6 @@ const SalesTable = function({formValues}) {
dataIndex: 'travelAdvisor',
key: 'travelAdvisor',
},
{
title: '客人名单',
dataIndex: 'contactList',
key: 'contactList',
},
{
title: '重点订单',
dataIndex: 'important',
@ -204,7 +202,7 @@ const SalesTable = function({formValues}) {
},
]
return (
<Table dataSource={salesData} columns={columns} />
<Table dataSource={salesData} loading={loading} columns={columns} />
)
}

@ -17,29 +17,18 @@ export default defineConfig({
sourcemap: true,
manifest: true,
chunkSizeWarningLimit: 555,
rollupOptions: {
output: {
manualChunks(id) {
// if (id.includes('antd')) {
// console.info('chunk: ' + id)
// return 'antd-component'
// } else if (id.includes('rc-')) {
// return 'rc-component'
// } else if (id.includes('ant-design')) {
// return 'ant-design'
// } else {
// // console.info('chunk: ' + id)
// }
if (id.includes('node_modules')) {
return id.toString().split('node_modules/')[1].split('/')[0].toString();
}
},
chunkFileNames: (chunkInfo) => {
const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/') : [];
// const fileName = facadeModuleId[facadeModuleId.length - 2] || '[name]';
return `assets/[name].[hash].js`;
}
}
}
// rollupOptions: {
// output: {
// manualChunks(id) {
// if (id.includes('node_modules')) {
// return id.toString().split('node_modules/')[1].split('/')[0].toString();
// }
// },
// chunkFileNames: (chunkInfo) => {
// const facadeModuleId = chunkInfo.facadeModuleId ? chunkInfo.facadeModuleId.split('/') : [];
// return `assets/[name].[hash].js`;
// }
// }
// }
}
})

Loading…
Cancel
Save