index.vue 30 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="100px">
  4. <el-form-item label="企微主体" prop="corpId">
  5. <el-select v-model="queryParams.corpId" placeholder="企微主体" size="small" @change="updateCorpId()">
  6. <el-option
  7. v-for="dict in myQwCompanyList"
  8. :key="dict.dictValue"
  9. :label="dict.dictLabel"
  10. :value="dict.dictValue"
  11. />
  12. </el-select>
  13. </el-form-item>
  14. <el-form-item label="企微账号" prop="qwUserId">
  15. <el-input
  16. v-model="queryParams.qwUserId"
  17. placeholder="请输入企微账号"
  18. clearable
  19. size="small"
  20. @keyup.enter.native="handleQuery"
  21. />
  22. </el-form-item>
  23. <el-form-item label="企微昵称" prop="qwUserName">
  24. <el-input
  25. v-model="queryParams.qwUserName"
  26. placeholder="请输入企微昵称"
  27. clearable
  28. size="small"
  29. @keyup.enter.native="handleQuery"
  30. />
  31. </el-form-item>
  32. <el-form-item label="授权码" prop="appKey">
  33. <el-input
  34. v-model="queryParams.appKey"
  35. placeholder="请输入授权码"
  36. clearable
  37. size="small"
  38. @keyup.enter.native="handleQuery"
  39. />
  40. </el-form-item>
  41. <el-form-item>
  42. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  43. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  44. </el-form-item>
  45. </el-form>
  46. <el-row :gutter="10" class="mb8">
  47. <el-col :span="1.5">
  48. <el-button
  49. type="warning"
  50. plain
  51. icon="el-icon-download"
  52. size="mini"
  53. :loading="exportLoading"
  54. @click="handleExport"
  55. v-hasPermi="['qw:user:export']"
  56. >导出</el-button>
  57. </el-col>
  58. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  59. </el-row>
  60. <el-table border v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
  61. <el-table-column label="企微成员ID" align="center" prop="id" />
  62. <el-table-column label="企微账号" align="center" prop="qwUserId" />
  63. <el-table-column label="企微昵称" align="center" prop="qwUserName" />
  64. <el-table-column label="员工称呼" align="center" prop="welcomeText" />
  65. <!-- <el-table-column label="所属部门" align="center" prop="departmentName" />-->
  66. <el-table-column label="联系我二维码" align="center" prop="contactWay" >
  67. <template slot-scope="scope">
  68. <el-image
  69. v-if="scope.row.contactWay!=null"
  70. style="width: 100px; height: 100px"
  71. :src="scope.row.contactWay"
  72. fit="contain"
  73. @click="openImageViewer(scope.row.contactWay)"/>
  74. </template>
  75. </el-table-column>
  76. <el-table-column label="绑定的AI客服" align="center" prop="fastGptRoleName" />
  77. <el-table-column label="授权码" align="center" prop="appKey" />
  78. <!-- <el-table-column label="企微状态" align="center" prop="loginStatus">-->
  79. <!-- <template slot-scope="scope">-->
  80. <!-- <el-tag v-if="scope.row.loginStatus == 1 && scope.row.toolStatus==1" type="success">在线</el-tag>-->
  81. <!-- <el-tag v-else type="danger">离线</el-tag>-->
  82. <!-- </template>-->
  83. <!-- </el-table-column>-->
  84. <!-- <el-table-column label="插件状态" align="center" prop="toolStatus">-->
  85. <!-- <template slot-scope="scope">-->
  86. <!-- <el-tag v-if="scope.row.toolStatus == 1" type="success">在线</el-tag>-->
  87. <!-- <el-tag v-else type="danger">离线</el-tag>-->
  88. <!-- </template>-->
  89. <!-- </el-table-column>-->
  90. <!-- <el-table-column label="插件版本" align="center" prop="version"/>-->
  91. <el-table-column label="服务器地址" align="center" prop="loginCodeUrl">
  92. <template slot-scope="scope">
  93. <el-tooltip class="item" effect="dark" :content="scope.row.loginCodeUrl" placement="top">
  94. <div style="display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; text-overflow: ellipsis;">
  95. <span>{{ scope.row.loginCodeUrl }}</span>
  96. </div>
  97. </el-tooltip>
  98. </template>
  99. </el-table-column>
  100. <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="120px" fixed="right">
  101. <template slot-scope="scope">
  102. <el-button
  103. size="mini"
  104. type="text"
  105. icon="el-icon-user-solid"
  106. plain
  107. @click="handleAppellation(scope.row)"
  108. >
  109. 修改员工称呼
  110. </el-button>
  111. <!-- <el-button-->
  112. <!-- v-if="scope.row.toolStatus==1"-->
  113. <!-- size="mini"-->
  114. <!-- type="text"-->
  115. <!-- icon="el-icon-sunny"-->
  116. <!-- plain-->
  117. <!-- @click="handleLoginQwCode(scope.row)"-->
  118. <!-- v-hasPermi="['qw:user:login']"-->
  119. <!-- >-->
  120. <!-- 登录企微-->
  121. <!-- </el-button>-->
  122. <!-- <el-button-->
  123. <!-- v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 1"-->
  124. <!-- size="mini"-->
  125. <!-- type="text"-->
  126. <!-- icon="el-icon-moon"-->
  127. <!-- plain-->
  128. <!-- @click="handleLoginOutQwStatus(scope.row)"-->
  129. <!-- v-hasPermi="['qw:user:login']"-->
  130. <!-- >-->
  131. <!-- 退出企微-->
  132. <!-- </el-button>-->
  133. </template>
  134. </el-table-column>
  135. <el-table-column label="主机" align="center" class-name="small-padding fixed-width" width="110px" fixed="right">
  136. <template slot-scope="scope">
  137. <el-button
  138. v-if="scope.row.appKey==null"
  139. size="mini"
  140. type="text"
  141. icon="el-icon-s-check"
  142. plain
  143. v-hasPermi="['qw:user:authAppKey']"
  144. @click="uploadAuthorizeKey2(scope.row)"
  145. >授权key
  146. </el-button>
  147. <el-button
  148. v-if="scope.row.loginCodeUrl==null && scope.row.appKey !=null"
  149. size="mini"
  150. type="text"
  151. icon="el-icon-sunny"
  152. plain
  153. @click="handleBindCloudHost(scope.row)"
  154. v-hasPermi="['qw:user:loginIp']"
  155. >
  156. 绑定主机
  157. </el-button>
  158. <el-button
  159. v-if="scope.row.loginCodeUrl!=null"
  160. size="mini"
  161. type="text"
  162. icon="el-icon-video-camera-solid"
  163. plain
  164. @click="handleCloudAP(scope.row.loginCodeUrl)"
  165. v-hasPermi="['qw:user:cloudAP']"
  166. >
  167. 获取主机帐密
  168. </el-button>
  169. <el-button
  170. v-if="scope.row.loginCodeUrl!=null"
  171. size="mini"
  172. type="text"
  173. icon="el-icon-moon"
  174. plain
  175. @click="handleUnbindCloudHost(scope.row)"
  176. v-hasPermi="['qw:user:loginIpOut']"
  177. >
  178. 解除主机
  179. </el-button>
  180. </template>
  181. </el-table-column>
  182. <el-table-column label="AI客服" align="center" class-name="small-padding fixed-width" width="100px" fixed="right">
  183. <template slot-scope="scope">
  184. <el-button
  185. size="mini"
  186. type="text"
  187. icon="el-icon-connection"
  188. plain
  189. v-if="scope.row.fastGptRoleName!=null"
  190. @click="bindFastGptRole(scope.row)"
  191. >换绑AI客服</el-button>
  192. <el-button
  193. size="mini"
  194. type="text"
  195. plain
  196. icon="el-icon-link"
  197. v-else
  198. @click="bindFastGptRole(scope.row)"
  199. >绑定AI客服</el-button>
  200. <el-button
  201. size="mini"
  202. type="text"
  203. icon="el-icon-unlock"
  204. plain
  205. v-if="scope.row.fastGptRoleName!=null"
  206. @click="relieveFastGptRole(scope.row)"
  207. >解绑AI客服</el-button>
  208. </template>
  209. </el-table-column>
  210. </el-table>
  211. <pagination
  212. v-show="total>0"
  213. :total="total"
  214. :page.sync="queryParams.pageNum"
  215. :limit.sync="queryParams.pageSize"
  216. @pagination="getList"
  217. />
  218. <!-- 绑定AI客服-->
  219. <el-dialog :title="bindAiTitle" :visible.sync="bindAiOpen" width="1200px" append-to-body>
  220. <fast-gpt-role ref="fastGptRole" @refreshFastGptList="refreshFastGptList" ></fast-gpt-role>
  221. </el-dialog>
  222. <!-- <el-dialog :visible.sync="updateIp.open" width="600px" append-to-body>-->
  223. <!-- <el-form ref="updateIpForm" :model="updateIpForm" :rules="updateIpRule" label-width="100px">-->
  224. <!-- <el-form-item label="新云主机IP" prop="Ip">-->
  225. <!-- <el-input v-model="updateIpForm.newIp" placeholder="请输入新IP" />-->
  226. <!-- </el-form-item>-->
  227. <!-- </el-form>-->
  228. <!-- <div slot="footer" class="dialog-footer" >-->
  229. <!-- <el-button type="primary" @click="submitUpdateIpForm">确 定</el-button>-->
  230. <!-- </div>-->
  231. <!-- </el-dialog>-->
  232. <el-dialog title="云主机信息" :visible.sync="cloudAPOpen.open" append-to-body>
  233. <el-card class="box-card">
  234. <div slot="header" class="clearfix">
  235. <span>账号:{{cloudAPOpen.admin}}</span>
  236. </div>
  237. <div slot="header" class="clearfix">
  238. <span>密码:{{cloudAPOpen.passWord}}</span>
  239. </div>
  240. </el-card>
  241. </el-dialog>
  242. <el-dialog :title="callOpen.title" :visible.sync="callOpen.open" width="500px" append-to-body>
  243. <el-form ref="callOpenFrom" :model="callOpenFrom" :rules="callOpenRule" label-width="110px">
  244. <el-form-item label="员工称呼" prop="welcomeText">
  245. <el-input v-model="callOpenFrom.welcomeText" placeholder="请输入员工称呼" />
  246. </el-form-item>
  247. </el-form>
  248. <div slot="footer" class="dialog-footer" >
  249. <el-button type="primary" @click="submitCallOpenFrom">确 定</el-button>
  250. </div>
  251. </el-dialog>
  252. <el-dialog title="授权key" :visible.sync="authorizeKeyOpen" width="500px" append-to-body>
  253. <el-form ref="authorizeKeyFrom" :model="authorizeKeyFrom" :rules="authorizeKeyRule" label-width="110px">
  254. <el-form-item label="授权的key值" prop="appKey">
  255. <el-input v-model="authorizeKeyFrom.appKey" placeholder="请输入授权key" type="Number"/>
  256. </el-form-item>
  257. </el-form>
  258. <div slot="footer" class="dialog-footer" >
  259. <el-button type="primary" @click="submitAuthorizeKeyForm">确 定</el-button>
  260. </div>
  261. </el-dialog>
  262. <!--二维码 -->
  263. <el-dialog
  264. :title="qwLogin.title"
  265. :visible.sync="qwLogin.open"
  266. width="600px"
  267. append-to-body
  268. custom-class="qr-login-dialog"
  269. >
  270. <div class="qr-login-container">
  271. <!-- 包裹 el-image 的 div,控制加载状态 -->
  272. <div class="image-wrapper" v-loading="imageLoading" element-loading-text="加载中..." element-loading-spinner="el-icon-loading">
  273. <!-- 如果图片未加载或加载失败,显示一个占位图 -->
  274. <el-image
  275. :src="'data:image/png;base64,' + qwLogin.codeUrl"
  276. style="display: block; margin: 0 auto; width: 300px; height: 300px;"
  277. @click="handleQrCodeClick"
  278. />
  279. </div>
  280. <p class="qr-login-instructions">使用企业微信扫码授权登录</p>
  281. <el-input
  282. v-model="qwLogin.code"
  283. placeholder="请输入六位验证码 按回车"
  284. clearable
  285. size="small"
  286. type="text"
  287. maxlength="6"
  288. class="verification-code-input"
  289. @input="validateCode"
  290. @keyup.native.enter="handleLoginQwCodeMsg"
  291. />
  292. </div>
  293. </el-dialog>
  294. <!-- 大图预览对话框 -->
  295. <el-dialog
  296. :visible.sync="dialogVisible"
  297. :modal="false"
  298. width="1200"
  299. append-to-body>
  300. <img
  301. :src="this.dialogImageUrl"
  302. style="display: block; max-width: 100%; margin: 0 auto"
  303. />
  304. </el-dialog>
  305. </div>
  306. </template>
  307. <script>
  308. import {
  309. listUser,
  310. getUser,
  311. delUser,
  312. addUser,
  313. updateUser,
  314. exportUser,
  315. updateUserWeclome,
  316. getMyQwCompanyList,
  317. relieveFastGptRoleById,
  318. staffListUser,
  319. loginQwCode,
  320. modifyLoginQwStatus,
  321. loginQwCodeMsg,
  322. getQwCodeUrl,
  323. logoutQwLogout,
  324. loginQwCodeUrl,
  325. getLoginQwStatus,
  326. qwBindCloudHost, qwUnbindCloudHost, handleAuthAppKey, handleInputAuthAppKey, selectCloudAP
  327. } from '@/api/qw/user'
  328. import fastGptRole from "@/views/fastGpt/fastGptRole/fastGptRole";
  329. import {updateSop, updateSopQwUser} from "@/api/qw/sop";
  330. export default {
  331. name: "User",
  332. components: { fastGptRole},
  333. data() {
  334. return {
  335. updateIp:{
  336. open:false,
  337. title: "修改云主机IP"
  338. },
  339. updateIpForm:{
  340. id:null,
  341. newIp:null,
  342. },
  343. authorizeKeyOpen:false,
  344. authorizeKeyFrom:{
  345. id:null,
  346. appKey:null,
  347. qwUserId:null,
  348. qwUserName:null
  349. },
  350. updateIpRule:{},
  351. newIp:null,
  352. //放大图片
  353. dialogImageUrl:null,
  354. dialogVisible:false,
  355. // 遮罩层
  356. loading: true,
  357. // 导出遮罩层
  358. exportLoading: false,
  359. // 选中数组
  360. ids: [],
  361. // 非单个禁用
  362. single: true,
  363. // 非多个禁用
  364. multiple: true,
  365. // 显示搜索条件
  366. showSearch: true,
  367. // 总条数
  368. total: 0,
  369. //公司列表
  370. myQwCompanyList:[],
  371. // 企微用户表格数据
  372. userList: [],
  373. allowSelectOptions:[],
  374. // 弹出层标题
  375. bindAiTitle: "",
  376. bindAiOpen: false,
  377. qwLogin:{
  378. title:"",
  379. open:false,
  380. codeUrl:null,
  381. code:null,
  382. appKey:null,
  383. },
  384. cloudAPOpen:{
  385. open:false,
  386. admin:null,
  387. passWord:null,
  388. },
  389. callOpen:{
  390. open:false,
  391. title: '修改员工称呼',
  392. },
  393. callOpenFrom:{
  394. id:null,
  395. welcomeText:null,
  396. },
  397. qrCodeInterval:null,
  398. loginQwInterval:null,
  399. loginOutQwInterval:null,
  400. imageLoading: true, // 控制加载状态
  401. // 查询参数
  402. queryParams: {
  403. pageNum: 1,
  404. pageSize: 10,
  405. qwUserId: null,
  406. corpId: null,
  407. qwUserName: null,
  408. },
  409. companyUserList:[],
  410. // 表单参数
  411. form: {
  412. isSendMsg: '2',
  413. },
  414. authorizeKeyRule:{
  415. appKey:[{required:true,message:"授权码不能为空",trigger:"blur"}]
  416. },
  417. callOpenRule:{
  418. welcomeText:[{required:true,message:"员工称呼不能为空",trigger:"blur"}]
  419. },
  420. // 表单校验
  421. rules: {
  422. },
  423. //欢迎语表单校验
  424. weclomeRules:{
  425. welcomeText:[{required:true,message:"消息文本不能为空",trigger:"blur"}]
  426. },
  427. };
  428. },
  429. created() {
  430. getMyQwCompanyList().then(response => {
  431. this.myQwCompanyList = response.data;
  432. if(this.myQwCompanyList!=null){
  433. this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
  434. this.getList();
  435. }
  436. });
  437. },
  438. watch: {
  439. // 监听弹窗的可见性变化
  440. 'qwLogin.open'(newVal) {
  441. if (!newVal) {
  442. // 如果弹窗关闭,清除定时器
  443. this.beforeDestroy();
  444. }
  445. },
  446. },
  447. methods: {
  448. // handleChangeIP(row){
  449. // this.updateIp.open = true
  450. // this.updateIpForm.id = row.id
  451. // this.updateIpForm.newIp = null;
  452. // },
  453. // submitUpdateIpForm(){
  454. // if (this.updateIpForm.newIp==null){
  455. // this.$message.warning("请填写IP")
  456. // return
  457. // }
  458. // const data = {
  459. // id : this.updateIpForm.id,
  460. // loginCodeUrl : this.updateIpForm.newIp
  461. // }
  462. //
  463. // updateUser(data).then(response => {
  464. // this.msgSuccess("绑定成功");
  465. // this.updateIp.open = false;
  466. // this.getList();
  467. // });
  468. // },
  469. /** 查询企微用户列表 */
  470. getList() {
  471. this.loading = true;
  472. staffListUser(this.queryParams).then(response => {
  473. this.userList = response.rows;
  474. this.total = response.total;
  475. this.loading = false;
  476. });
  477. },
  478. updateCorpId(){
  479. this.reset();
  480. this.getList();
  481. },
  482. //绑定AI客服
  483. bindFastGptRole(row){
  484. this.bindAiTitle="绑定AI客服";
  485. this.bindAiOpen=true;
  486. setTimeout(() => {
  487. this.$refs.fastGptRole.handleBindAiData(row)
  488. }, 200);
  489. },
  490. handleAppellation(val){
  491. this.callOpen.open=true;
  492. this.callOpenFrom.welcomeText=val.welcomeText;
  493. this.callOpenFrom.id=val.id;
  494. },
  495. //登录
  496. handleLoginQwCode(val){
  497. // v-if="scope.row.appKey!=null && scope.row.toolStatus === 1 && scope.row.loginStatus === 0"
  498. if (val.appKey==null || val.appKey===''){
  499. return this.$message.warning("没有授权码,无法登录企业微信,请授权");
  500. }
  501. if (val.toolStatus===0 || val.toolStatus==null ){
  502. return this.$message.warning("插件离线了,无法登录企业微信,请联系管理员~");
  503. }
  504. if (val.loginStatus===1 && val.toolStatus===1){
  505. return this.$message.success("已经登录了,无需重复登录");
  506. }
  507. loginQwCode({appKey:val.appKey}).then(res => {
  508. this.qwLogin.appKey=val.appKey;
  509. this.qwLogin.open=true;
  510. this.imageLoading=true;
  511. //定时器 获取登录状态(有自动登录的)
  512. this.loginQwPolling();
  513. // 调用后台接口获取新的二维码URL
  514. loginQwCodeUrl({ appKey: this.qwLogin.appKey }).then(res => {
  515. getQwCodeUrl({appKey:this.qwLogin.appKey}).then(res=>{
  516. this.qwLogin.codeUrl=res.base64 // 更新二维码URL
  517. }).catch(()=>{
  518. this.resetQwLogin();
  519. }).finally(()=>{
  520. this.imageLoading=false;
  521. })
  522. });
  523. // setTimeout(()=>{
  524. //
  525. // getQwCodeUrl({appKey:val.appKey}).then(res=>{
  526. // this.qwLogin.codeUrl=res.base64
  527. // }).catch(()=>{
  528. // this.resetQwLogin();
  529. // }).finally(()=>{
  530. // this.imageLoading=false;
  531. // })
  532. //
  533. // },3000)
  534. // 启动定时器,每隔45秒更新一次二维码
  535. this.startQrCodePolling();
  536. })
  537. },
  538. // 每隔45秒请求一次二维码
  539. startQrCodePolling() {
  540. this.qrCodeInterval = setInterval(() => {
  541. this.refreshQrCode();
  542. }, 45000); // 45秒更新一次
  543. },
  544. loginQwPolling() {
  545. let elapsedTime = 0; // 记录已执行的时间(单位:秒)
  546. this.loginQwInterval = setInterval(() => {
  547. // 每次轮询后增加已执行的时间
  548. elapsedTime += 5; // 因为每 3 秒执行一次
  549. // 请求登录状态
  550. getLoginQwStatus({ appKey: this.qwLogin.appKey }).then(res => {
  551. if (res.status==1) {
  552. this.$message.success('登录成功');
  553. clearInterval(this.loginQwInterval); // 登录成功,停止轮询
  554. this.resetQwLogin();
  555. }
  556. // 判断是否超过最大轮询时间(60秒)
  557. if (elapsedTime >= 90) {
  558. clearInterval(this.loginQwInterval); // 超过90秒停止轮询
  559. this.$message.warning('登录超时,请稍后再试!')
  560. this.resetQwLogin();
  561. }
  562. });
  563. }, 3000); // 每 3 秒更新一次
  564. },
  565. // 退出时清除定时器
  566. beforeDestroy() {
  567. if (this.qrCodeInterval) {
  568. clearInterval(this.qrCodeInterval); // 清除定时器
  569. }
  570. if (this.loginQwInterval) {
  571. clearInterval(this.loginQwInterval); // 清除定时器
  572. }
  573. if (this.loginOutQwInterval){
  574. clearInterval(this.loginOutQwInterval); // 清除定时器
  575. }
  576. this.resetQwLogin();
  577. },
  578. // 刷新二维码
  579. refreshQrCode() {
  580. // 调用后台接口获取新的二维码URL
  581. loginQwCodeUrl({ appKey: this.qwLogin.appKey }).then(res => {
  582. getQwCodeUrl({appKey:this.qwLogin.appKey}).then(res=>{
  583. this.qwLogin.codeUrl=res.base64 // 更新二维码URL
  584. }).catch(()=>{
  585. this.resetQwLogin();
  586. }).finally(()=>{
  587. this.imageLoading=false;
  588. })
  589. });
  590. },
  591. handleQrCodeClick(){
  592. this.imageLoading=true;
  593. // 调用后台接口获取新的二维码URL
  594. loginQwCodeUrl({ appKey: this.qwLogin.appKey }).then(res => {
  595. setTimeout(()=>{
  596. getQwCodeUrl({appKey:this.qwLogin.appKey}).then(res=>{
  597. //确保万一再调用一下刷新二维码
  598. this.qwLogin.codeUrl=res.base64 // 更新二维码URL
  599. }).catch(()=>{
  600. this.resetQwLogin();
  601. }).finally(()=>{
  602. this.imageLoading=false;
  603. })
  604. },3000)
  605. });
  606. },
  607. //退出
  608. handleLoginOutQwStatus(val){
  609. this.loading=true;
  610. logoutQwLogout({appKey:val.appKey,loginStatus:0}).then(res => {
  611. let elapsedTime = 0; // 记录已执行的时间(单位:秒)
  612. this.loginOutQwInterval = setInterval(() => {
  613. // 每次轮询后增加已执行的时间
  614. elapsedTime += 3; // 因为每 3 秒执行一次
  615. // 请求登录状态
  616. getLoginQwStatus({ appKey: val.appKey }).then(res => {
  617. if (res.status==0) {
  618. this.$message.success('退出成功');
  619. clearInterval(this.loginOutQwInterval); // 登录成功,停止轮询
  620. this.resetQwLogin();
  621. }
  622. // 判断是否超过最大轮询时间(60秒)
  623. if (elapsedTime >= 90) {
  624. clearInterval(this.loginOutQwInterval); // 超过90秒停止轮询
  625. this.$message.warning('退出超时,请多等待后刷新页面!')
  626. this.resetQwLogin();
  627. }
  628. });
  629. }, 3000); // 每 3 秒更新一次
  630. })
  631. },
  632. //传验证码
  633. handleLoginQwCodeMsg(){
  634. loginQwCodeMsg({appKey: this.qwLogin.appKey,code:this.qwLogin.code}).then(res => {
  635. this.qwLogin.open=false;
  636. this.$message.success("登录成功");
  637. })
  638. },
  639. validateCode() {
  640. // 只允许输入数字并限制长度为6
  641. this.qwLogin.code = this.qwLogin.code.replace(/\D/g, "").slice(0, 6);
  642. },
  643. handleCloudAP(urlAP){
  644. selectCloudAP({ipAddress:urlAP}).then(res => {
  645. this.cloudAPOpen.open=true
  646. this.cloudAPOpen.admin=res.data.apAdmin;
  647. this.cloudAPOpen.passWord=res.data.apPassword;
  648. })
  649. },
  650. handleUnbindCloudHost(val){
  651. const appKey=val.appKey;
  652. this.$confirm(
  653. '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
  654. '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
  655. '</span><br><span style="color: orange;">解绑【Ps:解绑后此云主机可能会分配给他人】</span></span>',
  656. "警告",
  657. {
  658. confirmButtonText: "确定",
  659. cancelButtonText: "取消",
  660. type: "warning",
  661. dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
  662. }
  663. ).then(() => {
  664. return qwUnbindCloudHost(appKey);
  665. }).then(response => {
  666. this.$message.success('解绑成功');
  667. }).finally(res=>{
  668. this.getList();
  669. })
  670. },
  671. handleAuthorizeKey(val){
  672. this.authorizeKeyFrom.id=val.id;
  673. this.authorizeKeyFrom.qwUserId=val.qwUserId;
  674. this.authorizeKeyFrom.qwUserName=val.qwUserName;
  675. this.authorizeKeyOpen=true;
  676. },
  677. submitAuthorizeKeyForm(){
  678. this.$refs["authorizeKeyFrom"].validate(valid => {
  679. if (valid) {
  680. if (this.authorizeKeyFrom.id != null && this.authorizeKeyFrom.appKey != null) {
  681. this.uploadAuthorizeKey();
  682. }
  683. }
  684. });
  685. },
  686. submitCallOpenFrom(){
  687. this.$refs["callOpenFrom"].validate(valid => {
  688. if (valid) {
  689. if (this.callOpenFrom.id != null && this.callOpenFrom.welcomeText != null) {
  690. updateUser(this.callOpenFrom).then(res=>{
  691. this.$message.success('修改成功');
  692. this.callOpen.open=false;
  693. this.getList();
  694. });
  695. }
  696. }
  697. });
  698. },
  699. uploadAuthorizeKey(){
  700. this.$confirm(
  701. '确定要给企微账号:<span style="color: green;">' + this.authorizeKeyFrom.qwUserId + '' +
  702. '</span><br>企微昵称:<span style="color: red;">【' + this.authorizeKeyFrom.qwUserName + '】</span>' +
  703. '</span><br>授权key:<span style="color: #04adf6;">【' + this.authorizeKeyFrom.appKey + '】</span>?',
  704. "警告",
  705. {
  706. confirmButtonText: "确定",
  707. cancelButtonText: "取消",
  708. type: "warning",
  709. dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
  710. }
  711. ).then(() => {
  712. this.authorizeKeyOpen=false;
  713. return handleInputAuthAppKey(this.authorizeKeyFrom);
  714. }).then(response => {
  715. this.msgSuccess("授权key完成");
  716. }).finally(res=>{
  717. this.resetAuthorizeKeyFrom();
  718. this.getList();
  719. })
  720. },
  721. uploadAuthorizeKey2(val){
  722. const id=val.id;
  723. this.$confirm(
  724. '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
  725. '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
  726. '</span><br>授权key</span>?',
  727. "警告",
  728. {
  729. confirmButtonText: "确定",
  730. cancelButtonText: "取消",
  731. type: "warning",
  732. dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
  733. }
  734. ).then(() => {
  735. return handleAuthAppKey({id:id});
  736. }).then(response => {
  737. this.msgSuccess("授权key完成");
  738. }).finally(res=>{
  739. this.resetAuthorizeKeyFrom();
  740. this.getList();
  741. })
  742. },
  743. handleBindCloudHost(val){
  744. if (val.appKey == null || val.appKey == '') {
  745. return this.$message.warning('没有授权码,无法绑定主机,请联系管理员');
  746. }
  747. const appKey=val.appKey;
  748. this.$confirm(
  749. '确定要给企微账号:<span style="color: green;">' +val.qwUserId + '' +
  750. '</span><br>企微昵称:<span style="color: red;">【' + val.qwUserName + '】</span>' +
  751. '</span><br><span style="color: dodgerblue;">绑定云主机?</span></span>',
  752. "警告",
  753. {
  754. confirmButtonText: "确定",
  755. cancelButtonText: "取消",
  756. type: "warning",
  757. dangerouslyUseHTMLString: true // 允许使用 HTML 字符串
  758. }
  759. ).then(() => {
  760. return qwBindCloudHost(appKey);
  761. }).then(response => {
  762. this.$message.success('绑定成功,请登录云主机进行配置~~');
  763. }).finally(res=>{
  764. this.getList();
  765. })
  766. },
  767. openImageViewer(url) {
  768. // 打开大图预览对话框
  769. this.dialogImageUrl=url
  770. this.dialogVisible = true;
  771. },
  772. //刷新页面
  773. refreshFastGptList(){
  774. this.bindAiOpen=false;
  775. this.getList();
  776. },
  777. // 取消按钮
  778. cancel() {
  779. this.open = false;
  780. this.reset();
  781. },
  782. // 表单重置
  783. reset() {
  784. this.form = {
  785. id: null,
  786. qwUserId: null,
  787. corpId: null,
  788. qwUserName: null,
  789. };
  790. this.resetForm("form");
  791. },
  792. //重置授权
  793. resetAuthorizeKeyFrom(){
  794. this.authorizeKeyFrom={
  795. id:null,
  796. appKey:null,
  797. qwUserId:null,
  798. qwUserName:null
  799. };
  800. },
  801. //重置登录
  802. resetQwLogin(){
  803. this.qwLogin={
  804. title:"",
  805. open:false,
  806. codeUrl:null,
  807. code:null,
  808. corpId:null,
  809. qwUserId:null,
  810. }
  811. this.qwLogin.open=false;
  812. this.loading=false;
  813. this.getList();
  814. },
  815. //解绑AI客服
  816. relieveFastGptRole(row){
  817. this.$confirm('是否确认解绑AI客服?', "警告", {
  818. confirmButtonText: "确定",
  819. cancelButtonText: "取消",
  820. type: "warning"
  821. }).then(function() {
  822. return relieveFastGptRoleById(row.id);
  823. }).then(() => {
  824. this.getList();
  825. this.msgSuccess("解绑成功");
  826. })
  827. },
  828. /** 搜索按钮操作 */
  829. handleQuery() {
  830. this.queryParams.pageNum = 1;
  831. this.getList();
  832. },
  833. /** 重置按钮操作 */
  834. resetQuery() {
  835. this.resetForm("queryForm");
  836. this.queryParams.corpId=this.myQwCompanyList[0].dictValue;
  837. this.handleQuery();
  838. },
  839. // 多选框选中数据
  840. handleSelectionChange(selection) {
  841. this.ids = selection.map(item => item.id)
  842. this.single = selection.length!==1
  843. this.multiple = !selection.length
  844. },
  845. /** 提交按钮 */
  846. submitForm() {
  847. this.$refs["form"].validate(valid => {
  848. if (valid) {
  849. if (this.form.id != null) {
  850. // updateUser(this.form).then(response => {
  851. // this.msgSuccess("绑定成功");
  852. // this.open = false;
  853. // this.getList();
  854. //
  855. // });
  856. } else {
  857. // addUser(this.form).then(response => {
  858. // this.msgSuccess("新增成功");
  859. // this.open = false;
  860. // this.getList();
  861. // });
  862. }
  863. }
  864. });
  865. },
  866. /** 导出按钮操作 */
  867. handleExport() {
  868. const queryParams = this.queryParams;
  869. this.$confirm('是否确认导出所有企微用户数据项?', "警告", {
  870. confirmButtonText: "确定",
  871. cancelButtonText: "取消",
  872. type: "warning"
  873. }).then(() => {
  874. this.exportLoading = true;
  875. return exportUser(queryParams);
  876. }).then(response => {
  877. this.download(response.msg);
  878. this.exportLoading = false;
  879. }).catch(() => {});
  880. }
  881. }
  882. };
  883. </script>
  884. <style>
  885. .text-container {
  886. max-height: 7.5em; /* 设置最大高度为6行,根据字体大小调整 */
  887. overflow-y: auto; /* 内容超出时显示滚动条 */
  888. line-height: 1.5em; /* 行高设置,确保每行高度一致 */
  889. }
  890. .qr-login-dialog .el-dialog__body {
  891. display: flex;
  892. flex-direction: column;
  893. align-items: center;
  894. text-align: center;
  895. padding: 20px;
  896. }
  897. .qr-login-container {
  898. width: 100%;
  899. }
  900. .qr-login-instructions {
  901. font-size: 14px;
  902. color: #666;
  903. margin: 10px 0;
  904. }
  905. .verification-code-input {
  906. margin-top: 15px;
  907. width: 80%;
  908. }
  909. </style>