index.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import React, { useEffect, useState } from "react";
  2. import http from '@src/http';
  3. import { Button, Table, Input, message, ConfigProvider } from "antd";
  4. import zhCN from 'antd/locale/zh_CN';
  5. import { UserAddOutlined } from '@ant-design/icons'
  6. import ChatRoomConfig from '../dialog/config'
  7. import QrCodeModal from '../dialog/qrcode'
  8. import { wechatUserInfoPage, qwQuiteQwLogin } from '@src/http/api';
  9. import type { TableProps, TablePaginationConfig } from 'antd';
  10. interface DataType {
  11. id: number;
  12. uuid: string;
  13. vid: string;
  14. name: string;
  15. avatar: string;
  16. phone: string;
  17. corpId: number;
  18. corpName: string;
  19. unionId: string;
  20. loginStatus: number;
  21. }
  22. interface PaginationParams {
  23. currentPage: number;
  24. pageSize: number;
  25. phone?: string;
  26. }
  27. interface WechatUserResponse {
  28. currentPage: number;
  29. totalSize: number;
  30. pageSize: number;
  31. objs: DataType[];
  32. totalPage: number;
  33. nextPage: number;
  34. prePage: number;
  35. curPageFirstRecNum: number;
  36. curPageLastRecNum: number;
  37. offset: number;
  38. }
  39. const Gzh: React.FC = () => {
  40. const [loading, setLoading] = useState(false)
  41. const [tableData, setTableData] = useState<DataType[]>([])
  42. const [pagination, setPagination] = useState<{
  43. current: number;
  44. pageSize: number;
  45. total: number;
  46. }>({
  47. current: 1,
  48. pageSize: 10,
  49. total: 0,
  50. })
  51. const [searchPhone, setSearchPhone] = useState<string>('')
  52. const [configVisible, setConfigVisible] = useState(false)
  53. const [currentUuid, setCurrentUuid] = useState('')
  54. const [qrModalVisible, setQrModalVisible] = useState(false)
  55. const [qrModalTitle, setQrModalTitle] = useState('')
  56. const [currentVid, setCurrentVid] = useState<string | undefined>(undefined)
  57. const columns: TableProps<DataType>['columns'] = [
  58. {
  59. title: '企微实名',
  60. dataIndex: 'name',
  61. key: 'name',
  62. },
  63. {
  64. title: '企微主体',
  65. dataIndex: 'corpName',
  66. key: 'corpName',
  67. },
  68. {
  69. title: '手机号',
  70. dataIndex: 'phone',
  71. key: 'phone',
  72. },
  73. {
  74. title: '账号状态',
  75. dataIndex: 'loginStatus',
  76. key: 'loginStatus',
  77. render: (status) => {
  78. const statusMap: Record<number, string> = {
  79. 0: '离线',
  80. 1: '在线',
  81. 2: '注销'
  82. }
  83. return <span style={{
  84. color: status === 1 ? '#52c41a' : status === 0 ? '#f5222d' : '#999999'
  85. }}>{statusMap[status]}</span>
  86. }
  87. },
  88. {
  89. title: '操作',
  90. key: 'action',
  91. render: (_, record) => (
  92. <>
  93. {record.loginStatus === 0 && <a onClick={() => handleLogin(record.vid)}>登录</a>}
  94. {record.loginStatus === 1 && <>
  95. <a onClick={() => handleLogout(record.uuid)}>登出</a>&emsp;
  96. <a onClick={() => handleConfigClick(record.uuid)}>配置</a>
  97. </>}
  98. </>
  99. ),
  100. }
  101. ]
  102. const fetchUserList = async (params: PaginationParams) => {
  103. try {
  104. setLoading(true)
  105. const res = await http.get<WechatUserResponse>(wechatUserInfoPage, { params })
  106. if (res.success) {
  107. setTableData(res.data.objs || [])
  108. setPagination({
  109. current: res.data.currentPage,
  110. pageSize: res.data.pageSize,
  111. total: res.data.totalSize
  112. })
  113. } else {
  114. message.error(res.msg || '获取用户列表失败')
  115. }
  116. } catch {
  117. message.error('获取用户列表失败')
  118. } finally {
  119. setLoading(false)
  120. }
  121. }
  122. const handleTableChange = (pagination: TablePaginationConfig) => {
  123. fetchUserList({
  124. currentPage: pagination.current as number,
  125. pageSize: pagination.pageSize as number,
  126. phone: searchPhone
  127. })
  128. }
  129. const handleSearch = () => {
  130. setPagination(prev => ({ ...prev, current: 1 }))
  131. fetchUserList({
  132. currentPage: 1,
  133. pageSize: pagination.pageSize,
  134. phone: searchPhone
  135. })
  136. }
  137. const handleConfigClick = (uuid: string) => {
  138. setCurrentUuid(uuid)
  139. setConfigVisible(true)
  140. }
  141. const handleConfigClose = () => {
  142. setConfigVisible(false)
  143. }
  144. const handleAddAccount = () => {
  145. setQrModalTitle('添加账号')
  146. setCurrentVid('') // 添加账号不需要 vid
  147. setQrModalVisible(true)
  148. }
  149. const handleLogin = (vid: string) => {
  150. setQrModalTitle('账号登录')
  151. setCurrentVid(vid) // 设置当前 vid
  152. setQrModalVisible(true)
  153. }
  154. const handleQrModalClose = () => {
  155. setQrModalVisible(false)
  156. }
  157. const handleLogout = async (uuid: string) => {
  158. try {
  159. setLoading(true)
  160. const res = await http.post(qwQuiteQwLogin, { uuid })
  161. if (res.success) {
  162. message.success('登出成功')
  163. // 刷新列表
  164. fetchUserList({
  165. currentPage: pagination.current,
  166. pageSize: pagination.pageSize,
  167. phone: searchPhone
  168. })
  169. } else {
  170. message.error(res.msg || '登出失败')
  171. }
  172. } catch {
  173. message.error('登出失败')
  174. } finally {
  175. setLoading(false)
  176. }
  177. }
  178. useEffect(() => {
  179. fetchUserList({
  180. currentPage: pagination.current,
  181. pageSize: pagination.pageSize
  182. })
  183. }, [])
  184. return (
  185. <>
  186. <div className={"text-[20px] font-medium mb-[10px]"}>
  187. 账号管理
  188. </div>
  189. <div className={"flex mb-[10px]"}>
  190. <div className={"flex-1 flex gap-[10px]"}>
  191. <Input
  192. className={"!w-[200px]"}
  193. allowClear
  194. placeholder="请输入手机号"
  195. value={searchPhone}
  196. onChange={(e) => {
  197. const value = e.target.value.replace(/[^\d]/g, '');
  198. setSearchPhone(value);
  199. }}
  200. onPressEnter={handleSearch}
  201. maxLength={11}
  202. />
  203. <Button type="primary" onClick={handleSearch}>搜索</Button>
  204. </div>
  205. <div>
  206. <Button type="primary" className={"mr-[10px]"} onClick={handleAddAccount}><UserAddOutlined />添加账号</Button>
  207. </div>
  208. </div>
  209. <ConfigProvider locale={zhCN}>
  210. <Table
  211. columns={columns}
  212. dataSource={tableData}
  213. rowKey="uuid"
  214. loading={loading}
  215. pagination={{
  216. current: pagination.current,
  217. pageSize: pagination.pageSize,
  218. total: pagination.total,
  219. showSizeChanger: true,
  220. showQuickJumper: true,
  221. showTotal: (total) => `共 ${total} 条记录`
  222. }}
  223. onChange={handleTableChange}
  224. />
  225. </ConfigProvider>
  226. <ChatRoomConfig
  227. visible={configVisible}
  228. onClose={handleConfigClose}
  229. uuid={currentUuid}
  230. />
  231. <QrCodeModal
  232. visible={qrModalVisible}
  233. onClose={handleQrModalClose}
  234. title={qrModalTitle}
  235. vid={currentVid}
  236. onSuccess={(data) => {
  237. // 获取二维码成功后的回调,可以在这里处理后续逻辑
  238. console.log('获取二维码成功:', data);
  239. }}
  240. onLoginSuccess={() => {
  241. // 登录成功后刷新列表
  242. fetchUserList({
  243. currentPage: pagination.current,
  244. pageSize: pagination.pageSize,
  245. phone: searchPhone
  246. });
  247. }}
  248. />
  249. </>
  250. )
  251. }
  252. export default Gzh;