完成钉钉扫码登陆

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

@ -1,24 +1,23 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client'
import { configure } from 'mobx'; import { configure } from 'mobx'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'; import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { AuthContext } from '@/stores/AuthContext'; import { AuthContext } from '@/stores/AuthContext'
import { ThemeContext } from '@/stores/ThemeContext'; import { ThemeContext } from '@/stores/ThemeContext'
import ConversationProvider from '@/views/Conversations/ConversationProvider'; import ConversationProvider from '@/views/Conversations/ConversationProvider'
import Auth from '@/stores/Auth'; import Auth from '@/stores/Auth'
import App from '@/views/App'; import App from '@/views/App'
import Standlone from '@/views/Standlone'; import Standlone from '@/views/Standlone'
import OrderFollow from '@/views/OrderFollow'; import OrderFollow from '@/views/OrderFollow'
import ChatHistory from '@/views/ChatHistory'; import ChatHistory from '@/views/ChatHistory'
import SalesManagement from '@/views/SalesManagement'; import SalesManagement from '@/views/SalesManagement'
import DingdingQRCode from '@/views/DingdingQRCode'; import DingdingQRCode from '@/views/DingdingQRCode'
import DingdingCallbak from '@/views/DingdingCallbak'; import AccountProfile from '@/views/AccountProfile'
import AccountProfile from '@/views/AccountProfile'; import ErrorPage from '@/views/ErrorPage'
import ErrorPage from '@/views/ErrorPage';
import Conversations from '@/views/Conversations/ChatWindow'; import Conversations from '@/views/Conversations/ChatWindow'
// import Conversations from '@/views/Conversations/ChatApp'; // import Conversations from '@/views/Conversations/ChatApp'
import '@/assets/index.css'; import '@/assets/index.css'
configure({ configure({
useProxies: 'ifavailable', useProxies: 'ifavailable',
@ -27,7 +26,7 @@ configure({
observableRequiresReaction: false, observableRequiresReaction: false,
reactionRequiresObservable: true, reactionRequiresObservable: true,
disableErrorBoundaries: process.env.NODE_ENV == 'production', disableErrorBoundaries: process.env.NODE_ENV == 'production',
}); })
const router = createBrowserRouter([ const router = createBrowserRouter([
{ {
@ -48,10 +47,9 @@ const router = createBrowserRouter([
element: <Standlone />, element: <Standlone />,
children: [ children: [
{ path: 'dingding/qrcode', element: <DingdingQRCode /> }, { path: 'dingding/qrcode', element: <DingdingQRCode /> },
{ path: 'dingding/callback', element: <DingdingCallbak /> },
], ],
}, },
]); ])
ReactDOM.createRoot(document.getElementById('root')).render( ReactDOM.createRoot(document.getElementById('root')).render(
// <React.StrictMode> // <React.StrictMode>
@ -63,4 +61,4 @@ ReactDOM.createRoot(document.getElementById('root')).render(
</AuthContext.Provider> </AuthContext.Provider>
</ThemeContext.Provider> </ThemeContext.Provider>
// </React.StrictMode> // </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.144:8888/whatever/';
const WS_URL = 'ws://202.103.68.157: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: URL, protocol: 'aaa' });
let realtimeAPI = new RealTimeAPI({ url: WS_URL, protocol: 'WhatsApp' }); // let realtimeAPI = new RealTimeAPI({ url: WS_URL, protocol: 'WhatsApp' });
export const useConversations = () => { export const useConversations = () => {
const [errors, setErrors] = useState([]); const [errors, setErrors] = useState([]);
@ -87,7 +87,7 @@ export const useConversations = () => {
type: 'message', type: 'message',
message: msg, message: msg,
}; };
realtimeAPI.sendMessage(msgObj); // realtimeAPI.sendMessage(msgObj);
addMessage(msgObj.message); addMessage(msgObj.message);
// debug: // debug:
// const msgObjR = { // const msgObjR = {
@ -97,10 +97,10 @@ export const useConversations = () => {
// addMessage({ ...msgObjR.message, sender: 'other', id: Date.now().toString(16) }); // addMessage({ ...msgObjR.message, sender: 'other', id: Date.now().toString(16) });
}; };
realtimeAPI.onError(addError.bind(null, 'Error')); // realtimeAPI.onError(addError.bind(null, 'Error'));
realtimeAPI.onMessage(handleMessage); // realtimeAPI.onMessage(handleMessage);
realtimeAPI.onCompletion(addError.bind(null, 'Not Connected to Server')); // realtimeAPI.onCompletion(addError.bind(null, 'Not Connected to Server'));
realtimeAPI.keepAlive(); // Ping Server // realtimeAPI.keepAlive(); // Ping Server
return { return {
errors, messages, conversationsList, currentConversation, sendMessage, errors, messages, conversationsList, currentConversation, sendMessage,

@ -22,10 +22,10 @@ function AccountProfile() {
<Row> <Row>
<Col span={12} offset={6}> <Col span={12} offset={6}>
<Descriptions title='个人信息' layout='vertical' column={2}> <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='名字'><Space size='middle'><Avatar src={loginUser.avatarUrl} />{loginUser.username}</Space></Descriptions.Item>
<Descriptions.Item label='手机'>+86-15878301602</Descriptions.Item> <Descriptions.Item label='HT 账号'>{loginUser.accountName}</Descriptions.Item>
<Descriptions.Item label='邮件'>christyluo@hainatravel.com</Descriptions.Item> <Descriptions.Item label='手机'>{loginUser.stateCode} {loginUser.mobile}</Descriptions.Item>
<Descriptions.Item label='部门'>海纳业务组-GH事业部群-GH销售</Descriptions.Item> <Descriptions.Item label='邮件'>{loginUser.email}</Descriptions.Item>
</Descriptions> </Descriptions>
</Col> </Col>
</Row> </Row>

@ -15,7 +15,7 @@ import { isEmpty } from '@/utils/commons';
const { Header, Footer, Content } = Layout const { Header, Footer, Content } = Layout
const { Title } = Typography const { Title } = Typography
// SecurityApp // AuthApp
function App() { function App() {
const { colorPrimary, borderRadius } = useThemeContext() 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 { Result, Spin, Flex, Typography } from 'antd'
import React, { useEffect } from 'react' import React, { useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuthContext } from '@/stores/AuthContext'
const { Title } = Typography const { Title } = Typography
const { Meta } = Card
// https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information#title-qpi-0qv-anm // https://open.dingtalk.com/document/orgapp/tutorial-obtaining-user-personal-information#title-qpi-0qv-anm
function DingdingQRCode() { function DingdingQRCode() {
const navigate = useNavigate()
const { loginUser } = useAuthContext()
const [loginStatus, setLoginStatus] = useState(0)
useEffect(() => { 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( window.DTFrameLogin(
{ {
id: 'qrCodeContainer', id: 'qrCodeContainer',
@ -17,34 +23,76 @@ function DingdingQRCode() {
height: 300, height: 300,
}, },
{ {
redirect_uri: encodeURIComponent('https://sales.mycht.cn/dingding/callback'), redirect_uri: encodeURIComponent('https://sales.mycht.cn/p/dingding/callback'),
client_id: 'dingwgdx6emlxr3fcrg8', client_id: 'dingwgdx6emlxr3fcrg8',
scope: 'openid', scope: 'openid',
response_type: 'code', response_type: 'code',
state: 'state1111', state: 'global-sales',
prompt: 'consent', prompt: 'consent',
}, },
(loginResult) => { (loginResult) => {
const { redirectUrl, authCode, state } = loginResult const { authCode } = loginResult
// console.log(loginResult)
window.location.href = redirectUrl setLoginStatus(200)
// 使code
console.log(authCode) 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) => { (errorMsg) => {
// setLoginStatus(403)
alert(`Login Error: ${errorMsg}`) console.error(`Login Error: ${errorMsg}`)
}, },
) )
}) })
}, []) }, [])
return ( if (loginStatus === 200) {
<Flex justify='center' align='center' gap='middle' vertical> return (
<Title level={4}>使用钉钉扫码</Title> <Flex justify='center' align='center' gap='middle' vertical>
<div id='qrCodeContainer' style={{ border: '1px solid rgba(5, 5, 5, 0.06)', borderRadius: '8px' }}></div> <Result
</Flex> 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 export default DingdingQRCode

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

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

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

Loading…
Cancel
Save